meta-flow: Correctly set destination MAC in mf_set_flow_value().
[sliver-openvswitch.git] / build-aux / extract-ofp-errors
1 #! /usr/bin/python
2
3 import sys
4 import os.path
5 import re
6
7 macros = {}
8
9 token = None
10 line = ""
11 idRe = "[a-zA-Z_][a-zA-Z_0-9]*"
12 tokenRe = "#?" + idRe + "|[0-9]+|."
13 inComment = False
14 inDirective = False
15 def getToken():
16     global token
17     global line
18     global inComment
19     global inDirective
20     while True:
21         line = line.lstrip()
22         if line != "":
23             if line.startswith("/*"):
24                 inComment = True
25                 line = line[2:]
26             elif inComment:
27                 commentEnd = line.find("*/")
28                 if commentEnd < 0:
29                     line = ""
30                 else:
31                     inComment = False
32                     line = line[commentEnd + 2:]
33             else:
34                 match = re.match(tokenRe, line)
35                 token = match.group(0)
36                 line = line[len(token):]
37                 if token.startswith('#'):
38                     inDirective = True
39                 elif token in macros and not inDirective:
40                     line = macros[token] + line
41                     continue
42                 return True
43         elif inDirective:
44             token = "$"
45             inDirective = False
46             return True
47         else:
48             global lineNumber
49             line = inputFile.readline()
50             lineNumber += 1
51             while line.endswith("\\\n"):
52                 line = line[:-2] + inputFile.readline()
53                 lineNumber += 1
54             if line == "":
55                 if token == None:
56                     fatal("unexpected end of input")
57                 token = None
58                 return False
59
60 def fatal(msg):
61     sys.stderr.write("%s:%d: error at \"%s\": %s\n" % (fileName, lineNumber, token, msg))
62     sys.exit(1)
63
64 def skipDirective():
65     getToken()
66     while token != '$':
67         getToken()
68
69 def isId(s):
70     return re.match(idRe + "$", s) != None
71
72 def forceId():
73     if not isId(token):
74         fatal("identifier expected")
75
76 def forceInteger():
77     if not re.match('[0-9]+$', token):
78         fatal("integer expected")
79
80 def match(t):
81     if token == t:
82         getToken()
83         return True
84     else:
85         return False
86
87 def forceMatch(t):
88     if not match(t):
89         fatal("%s expected" % t)
90
91 def parseTaggedName():
92     assert token in ('struct', 'union')
93     name = token
94     getToken()
95     forceId()
96     name = "%s %s" % (name, token)
97     getToken()
98     return name
99
100 def print_enum(tag, constants, storage_class):
101     print """
102 %(storage_class)sconst char *
103 %(tag)s_to_string(uint16_t value)
104 {
105     switch (value) {\
106 """ % {"tag": tag,
107        "bufferlen": len(tag) + 32,
108        "storage_class": storage_class}
109     for constant in constants:
110         print "    case %s: return \"%s\";" % (constant, constant)
111     print """\
112     }
113     return NULL;
114 }\
115 """ % {"tag": tag}
116
117 def usage():
118     argv0 = os.path.basename(sys.argv[0])
119     print '''\
120 %(argv0)s, for extracting OpenFlow error codes from header files
121 usage: %(argv0)s FILE [FILE...]
122
123 This program reads the header files specified on the command line and
124 outputs a C source file for translating OpenFlow error codes into
125 strings, for use as lib/ofp-errors.c in the Open vSwitch source tree.
126
127 This program is specialized for reading include/openflow/openflow.h
128 and include/openflow/nicira-ext.h.  It will not work on arbitrary
129 header files without extensions.''' % {"argv0": argv0}
130     sys.exit(0)
131
132 def extract_ofp_errors(filenames):
133     error_types = {}
134
135     global fileName
136     for fileName in filenames:
137         global inputFile
138         global lineNumber
139         inputFile = open(fileName)
140         lineNumber = 0
141         while getToken():
142             if token in ("#ifdef", "#ifndef", "#include",
143                          "#endif", "#elif", "#else", '#define'):
144                 skipDirective()
145             elif match('enum'):
146                 forceId()
147                 enum_tag = token
148                 getToken()
149
150                 forceMatch("{")
151
152                 constants = []
153                 while isId(token):
154                     constants.append(token)
155                     getToken()
156                     if match('='):
157                         while token != ',' and token != '}':
158                             getToken()
159                     match(',')
160
161                 forceMatch('}')
162
163                 if enum_tag == "ofp_error_type":
164                     error_types = {}
165                     for error_type in constants:
166                         error_types[error_type] = []
167                 elif enum_tag == 'nx_vendor_code':
168                     pass
169                 elif enum_tag.endswith('_code'):
170                     error_type = 'OFPET_%s' % '_'.join(enum_tag.split('_')[1:-1]).upper()
171                     if error_type not in error_types:
172                         fatal("enum %s looks like an error code enumeration but %s is unknown" % (enum_tag, error_type))
173                     error_types[error_type] += constants
174             elif token in ('struct', 'union'):
175                 getToken()
176                 forceId()
177                 getToken()
178                 forceMatch('{')
179                 while not match('}'):
180                     getToken()
181             elif match('OFP_ASSERT') or match('BOOST_STATIC_ASSERT'):
182                 while token != ';':
183                     getToken()
184             else:
185                 fatal("parse error")
186         inputFile.close()
187
188     print "/* -*- buffer-read-only: t -*- */"
189     print "#include <config.h>"
190     print '#include "ofp-errors.h"'
191     print "#include <inttypes.h>"
192     print "#include <stdio.h>"
193     for fileName in sys.argv[1:]:
194         print '#include "%s"' % fileName
195     print '#include "type-props.h"'
196
197     for error_type, constants in sorted(error_types.items()):
198         tag = 'ofp_%s_code' % re.sub('^OFPET_', '', error_type).lower()
199         print_enum(tag, constants, "static ")
200     print_enum("ofp_error_type", error_types.keys(), "")
201     print """
202 const char *
203 ofp_error_code_to_string(uint16_t type, uint16_t code)
204 {
205     switch (type) {\
206 """
207     for error_type in error_types:
208         tag = 'ofp_%s_code' % re.sub('^OFPET_', '', error_type).lower()
209         print "    case %s:" % error_type
210         print "        return %s_to_string(code);" % tag
211     print """\
212     }
213     return NULL;
214 }\
215 """
216
217 if __name__ == '__main__':
218     if '--help' in sys.argv:
219         usage()
220     elif len(sys.argv) < 2:
221         sys.stderr.write("at least one non-option argument required; "
222                          "use --help for help\n")
223         sys.exit(1)
224     else:
225         extract_ofp_errors(sys.argv[1:])