Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import io
2import keyword
4from ply import lex
5from ply import yacc
7from logging import getLogger, basicConfig, DEBUG, INFO
9log = getLogger(__name__)
11reserved = """
12CONST ENUM STRUCT OPAQUE UNSIGNED STRING TYPEDEF CASE DEFAULT VOID
13UNION SWITCH BOOL HYPER LONG INT NETOBJ TRUE FALSE
14PROGRAM VERSION
15""".strip().split()
17tokens = reserved + """
18ID TYPEID ICONST
19EQ LT GT MINUS PLUS TIMES
20SEMI COLON COMMA
21LPAREN RPAREN
22LBRACKET RBRACKET
23LBRACE RBRACE
24""".strip().split()
26t_ignore = " \t\x0c"
28ngname = keyword.kwlist + dir(__builtins__)
31def t_NEWLINE(t):
32 r'\n+'
35t_TIMES = r'\*'
36t_MINUS = r'\-'
37t_PLUS = r'\+'
38t_LT = r'<'
39t_GT = r'>'
40t_EQ = r'='
41t_LPAREN = r'\('
42t_RPAREN = r'\)'
43t_LBRACKET = r'\['
44t_RBRACKET = r'\]'
45t_LBRACE = r'\{'
46t_RBRACE = r'\}'
47t_COMMA = r','
48t_SEMI = r';'
49t_COLON = r':'
50t_ICONST = r'[-+]?(0x?)?\d+'
51t_ignore_COMMENT = r'(/\*(.|\n)*?\*/|//[^\n]*\n$)'
52t_ignore_PP = r'\#(.)*?\n'
53t_ignore_PX = r'\%(.)*?\n'
55reserved_map = {
56 "TRUE": "ICONST",
57 "FALSE": "ICONST",
58}
59for r in reserved:
60 reserved_map[r.lower()] = r
62constmap = {}
65def t_error(t):
66 log.error("error: %s", t)
69def t_ID(t):
70 r'[A-Za-z_][\w_]*'
71 t.type = reserved_map.get(t.value, "ID")
72 while t.value in ngname:
73 t.value = t.value + "_"
74 return t
77def sequence(t, first, second):
78 if len(t) == first + 1:
79 t[0] = [t[first]]
80 elif len(t) == second + 1:
81 if t[0] is None:
82 t[0] = [t[first]]
83 if t[second] is not None:
84 t[0].extend(t[second])
85 else:
86 t[0].append(t[first])
89def valmap(s):
90 return dict(s)
93def p_statements_1(t):
94 """statements : statement statements
95 | statement"""
96 log.debug("p_statements_1: %s", t)
97 sequence(t, 1, 2)
100def p_statement(t):
101 """statement : defconst SEMI
102 | defenum SEMI
103 | defstruct SEMI
104 | typedef SEMI
105 | union SEMI
106 | program SEMI"""
107 log.debug("p_statement_1: %s", t)
108 t[0] = t[1]
111def p_defconst(t):
112 """defconst : CONST ID EQ ICONST"""
113 log.debug("defconst: %s %s", t[2], t[4])
114 reserved_map[t[2]] = "ICONST"
115 constmap[t[2]] = t[4]
116 t[0] = {"const": t[2], "value": t[4]}
119def p_defenum(t):
120 """defenum : ENUM ID LBRACE enuments RBRACE"""
121 log.debug("p_defenum: %s %s", t[2], t[4])
122 reserved_map[t[2]] = "TYPEID"
123 t[0] = {"enum": t[2], "values": valmap(t[4])}
126def p_enuments(t):
127 """enuments : enument
128 | enument COMMA enuments"""
129 log.debug("p_enuments_1: %s", list(t))
130 sequence(t, 1, 3)
133def p_enument(t):
134 """enument : ID EQ ICONST"""
135 log.debug("p_enuments_2: %s %s", t[1], t[3])
136 reserved_map[t[1]] = "ICONST"
137 t[0] = (t[1], t[3])
140def p_struct(t):
141 """defstruct : STRUCT ID LBRACE structents RBRACE
142 | STRUCT TYPEID LBRACE structents RBRACE"""
143 log.debug("p_struct: %s", t[2])
144 reserved_map[t[2]] = "TYPEID"
145 t[0] = {"struct": t[2], "entries": t[4]}
148def p_structents(t):
149 """structents : structent structents
150 | structent"""
151 log.debug("p_structents: %s", list(t))
152 sequence(t, 1, 2)
155def p_structent_1(t):
156 """structent : typeid ID SEMI
157 | typeid TYPEID SEMI
158 | ID ID SEMI"""
159 log.debug("p_structent_1: %s %s", t[1], t[2])
160 t[0] = {"name": t[2], "type": t[1], "note": "raw"}
163def p_structent_2(t):
164 """structent : typeid ID LT ICONST GT SEMI
165 | typeid ID LT GT SEMI
166 | typeid ID LBRACKET ICONST RBRACKET SEMI
167 | typeid ID LBRACKET RBRACKET SEMI"""
168 log.debug("p_structent_2: %s", list(t))
169 t[0] = {"name": t[2], "type": t[1], "note": "array"}
170 if len(t) == 7:
171 t[0]["length"] = t[4]
172 if t[3] == "[":
173 t[0]["fixed"] = True
176def p_structent_3(t):
177 """structent : ID TIMES ID SEMI
178 | typeid TIMES ID SEMI"""
179 log.debug("p_structent_3: %s", list(t))
180 t[0] = {"name": t[3], "type": t[1], "note": "pointer"}
183def p_typeid(t):
184 """typeid : TYPEID
185 | OPAQUE
186 | UNSIGNED
187 | UNSIGNED HYPER
188 | UNSIGNED INT
189 | UNSIGNED LONG
190 | STRING
191 | NETOBJ
192 | BOOL
193 | HYPER
194 | LONG
195 | INT
196 | VOID
197 | STRUCT TYPEID"""
198 log.debug("p_typeid: %s", t[1])
199 t[0] = t[1]
202def p_typedef_1(t):
203 """typedef : TYPEDEF typeid ID
204 | TYPEDEF typeid TYPEID
205 | TYPEDEF typeid ID LT ICONST GT
206 | TYPEDEF typeid ID LT GT
207 | TYPEDEF typeid ID LBRACKET ICONST RBRACKET"""
208 log.debug("p_typedef: %s", t[3])
209 reserved_map[t[3]] = "TYPEID"
210 t[0] = {"typedef": t[3], "type": t[2]}
211 if len(t) == 4:
212 t[0]["note"] = "raw"
213 else:
214 t[0]["note"] = "array"
215 if len(t) == 7:
216 t[0]["length"] = t[5]
217 if len(t) > 5 and t[4] == "[":
218 t[0]["fixed"] = True
221def p_typedef_2(t):
222 """typedef : TYPEDEF STRUCT ID TIMES ID"""
223 log.debug("p_typedef_2: %s %s", t[3], t[5])
224 reserved_map[t[5]] = "TYPEID"
225 t[0] = {"typedef": t[3], "type": t[5], "note": "pointer"}
228def p_union(t):
229 """union : UNION ID SWITCH LPAREN typeid ID RPAREN LBRACE cases RBRACE"""
230 log.debug("p_union: %s %s", t[2], t[5])
231 reserved_map[t[2]] = "TYPEID"
232 t[0] = {"union": t[2], "cond": {"type": t[5], "name": t[6]}, "cases": t[9]}
235def p_cases(t):
236 """cases : case cases
237 | case"""
238 log.debug("p_cases: %s", list(t))
239 sequence(t, 1, 2)
242def p_case(t):
243 """case : caselabel typeid SEMI
244 | caselabel
245 | caselabel ID SEMI
246 | caselabel ID ID SEMI
247 | caselabel typeid ID SEMI"""
248 log.debug("p_case: %s", t[1])
249 t[0] = {"label": t[1]}
250 if len(t) != 2:
251 t[0]["type"] = t[2]
252 if len(t) == 5:
253 t[0]["name"] = t[3]
256def p_caselabel_1(t):
257 """caselabel : CASE ICONST COLON"""
258 log.debug("p_caselabel: %s", t[2])
259 t[0] = t[2]
262def p_caselabel_2(t):
263 """caselabel : DEFAULT COLON"""
264 log.debug("p_caselabel: %s", t[1])
265 t[0] = t[1]
268def p_program(t):
269 """program : PROGRAM ID LBRACE versions RBRACE EQ ICONST"""
270 log.debug("p_program: %s %s %s", t[2], t[4], t[7])
271 t[0] = {"program": t[2], "num": t[7], "versions": t[4]}
274def p_versions(t):
275 """versions : version versions
276 | version"""
277 log.debug("p_versions: %s", list(t))
278 sequence(t, 1, 2)
281def p_version(t):
282 """version : VERSION ID LBRACE procs RBRACE EQ ICONST SEMI"""
283 log.debug("p_version: %s id=%s procs=%s", t[2], t[7], t[4])
284 t[0] = {"version": t[2], "num": t[7], "procs": t[4]}
287def p_procs(t):
288 """procs : proc procs
289 | proc"""
290 log.debug("p_procs: %s", list(t))
291 sequence(t, 1, 2)
294def p_proc(t):
295 """proc : typeid ID LPAREN typeid RPAREN EQ ICONST SEMI"""
296 log.debug("p_proc: %s id=%s arg=%s res=%s", t[2], t[7], t[4], t[1])
297 t[0] = {"id": t[7], "name": t[2], "arg": t[4], "res": t[1]}
300def p_error(t):
301 log.error("error: %s", t)
304def parse_file(fp, debug=False, defines={}):
305 log.debug("defines: %s", defines)
306 for k, v in defines.items():
307 log.info("const: %s=%s", k, v)
308 constmap[k] = str(v)
309 if isinstance(v, int):
310 reserved_map[k] = "ICONST"
311 log.debug("reserved: %s=%s", k, v)
312 lexer = lex.lex()
313 parser = yacc.yacc(debug=debug)
314 if not hasattr(fp, "encoding"):
315 fp = io.TextIOWrapper(fp)
316 return yacc.parse(fp.read(), debug=debug)
319def get_lexer(fp):
320 lx = lex.lex()
321 lx.input(fp.read())
322 return lx
325if __name__ == "__main__":
326 import sys
327 import yaml
328 basicConfig(level=DEBUG)
329 mode = "lex"
330 defs = {"LM_MAXSTRLEN": 1024, "MAXNAMELEN": 1025, "MAXNETNAMELEN": 255}
331 # defs = {}
332 if len(sys.argv) >= 2:
333 mode = sys.argv[1]
334 if mode == "lex":
335 for token in get_lexer(sys.stdin):
336 log.info("token %s", token)
337 elif mode == "yacc":
338 result = parse_file(sys.stdin, debug=True, defines=defs)
339 log.debug("parsed %s", result)
340 log.info("const %s", constmap)
341 sys.stdout.write(yaml.dump(result))
342 elif mode == "yacc_cpp":
343 import subprocess
344 with subprocess.Popen(["cpp"], stdin=subprocess.PIPE,
345 stdout=subprocess.PIPE) as p:
346 p.stdin.write(sys.stdin.read().encode('utf-8'))
347 p.stdin.close()
348 result = parse_file(p.stdout, debug=False, defines=defs)
349 log.debug("parsed %s", result)
350 log.info("const %s", constmap)
351 sys.stdout.write(yaml.dump(result))