OLD | NEW |
| (Empty) |
1 # Copyright 2014 The Chromium Authors. All rights reserved. | |
2 # Use of this source code is governed by a BSD-style license that can be | |
3 # found in the LICENSE file. | |
4 | |
5 """Generates a syntax tree from a Mojo IDL file.""" | |
6 | |
7 import imp | |
8 import os.path | |
9 import sys | |
10 | |
11 def _GetDirAbove(dirname): | |
12 """Returns the directory "above" this file containing |dirname| (which must | |
13 also be "above" this file).""" | |
14 path = os.path.abspath(__file__) | |
15 while True: | |
16 path, tail = os.path.split(path) | |
17 assert tail | |
18 if tail == dirname: | |
19 return path | |
20 | |
21 try: | |
22 imp.find_module("ply") | |
23 except ImportError: | |
24 sys.path.append(os.path.join(_GetDirAbove("public"), "public/third_party")) | |
25 from ply import lex | |
26 from ply import yacc | |
27 | |
28 from ..error import Error | |
29 from . import ast | |
30 from .lexer import Lexer | |
31 | |
32 | |
33 _MAX_ORDINAL_VALUE = 0xffffffff | |
34 _MAX_ARRAY_SIZE = 0xffffffff | |
35 | |
36 | |
37 class ParseError(Error): | |
38 """Class for errors from the parser.""" | |
39 | |
40 def __init__(self, filename, message, lineno=None, snippet=None): | |
41 Error.__init__(self, filename, message, lineno=lineno, | |
42 addenda=([snippet] if snippet else None)) | |
43 | |
44 | |
45 # We have methods which look like they could be functions: | |
46 # pylint: disable=R0201 | |
47 class Parser(object): | |
48 | |
49 def __init__(self, lexer, source, filename): | |
50 self.tokens = lexer.tokens | |
51 self.source = source | |
52 self.filename = filename | |
53 | |
54 # Names of functions | |
55 # | |
56 # In general, we name functions after the left-hand-side of the rule(s) that | |
57 # they handle. E.g., |p_foo_bar| for a rule |foo_bar : ...|. | |
58 # | |
59 # There may be multiple functions handling rules for the same left-hand-side; | |
60 # then we name the functions |p_foo_bar_N| (for left-hand-side |foo_bar|), | |
61 # where N is a number (numbered starting from 1). Note that using multiple | |
62 # functions is actually more efficient than having single functions handle | |
63 # multiple rules (and, e.g., distinguishing them by examining |len(p)|). | |
64 # | |
65 # It's also possible to have a function handling multiple rules with different | |
66 # left-hand-sides. We do not do this. | |
67 # | |
68 # See http://www.dabeaz.com/ply/ply.html#ply_nn25 for more details. | |
69 | |
70 # TODO(vtl): Get rid of the braces in the module "statement". (Consider | |
71 # renaming "module" -> "package".) Then we'll be able to have a single rule | |
72 # for root (by making module "optional"). | |
73 def p_root_1(self, p): | |
74 """root : """ | |
75 p[0] = ast.Mojom(None, ast.ImportList(), []) | |
76 | |
77 def p_root_2(self, p): | |
78 """root : root module""" | |
79 if p[1].module is not None: | |
80 raise ParseError(self.filename, | |
81 "Multiple \"module\" statements not allowed:", | |
82 p[2].lineno, snippet=self._GetSnippet(p[2].lineno)) | |
83 if p[1].import_list.items or p[1].definition_list: | |
84 raise ParseError( | |
85 self.filename, | |
86 "\"module\" statements must precede imports and definitions:", | |
87 p[2].lineno, snippet=self._GetSnippet(p[2].lineno)) | |
88 p[0] = p[1] | |
89 p[0].module = p[2] | |
90 | |
91 def p_root_3(self, p): | |
92 """root : root import""" | |
93 if p[1].definition_list: | |
94 raise ParseError(self.filename, | |
95 "\"import\" statements must precede definitions:", | |
96 p[2].lineno, snippet=self._GetSnippet(p[2].lineno)) | |
97 p[0] = p[1] | |
98 p[0].import_list.Append(p[2]) | |
99 | |
100 def p_root_4(self, p): | |
101 """root : root definition""" | |
102 p[0] = p[1] | |
103 p[0].definition_list.append(p[2]) | |
104 | |
105 def p_import(self, p): | |
106 """import : IMPORT STRING_LITERAL SEMI""" | |
107 # 'eval' the literal to strip the quotes. | |
108 # TODO(vtl): This eval is dubious. We should unquote/unescape ourselves. | |
109 p[0] = ast.Import(eval(p[2]), filename=self.filename, lineno=p.lineno(2)) | |
110 | |
111 def p_module(self, p): | |
112 """module : attribute_section MODULE identifier_wrapped SEMI""" | |
113 p[0] = ast.Module(p[3], p[1], filename=self.filename, lineno=p.lineno(2)) | |
114 | |
115 def p_definition(self, p): | |
116 """definition : struct | |
117 | union | |
118 | interface | |
119 | enum | |
120 | const""" | |
121 p[0] = p[1] | |
122 | |
123 def p_attribute_section_1(self, p): | |
124 """attribute_section : """ | |
125 p[0] = None | |
126 | |
127 def p_attribute_section_2(self, p): | |
128 """attribute_section : LBRACKET attribute_list RBRACKET""" | |
129 p[0] = p[2] | |
130 | |
131 def p_attribute_list_1(self, p): | |
132 """attribute_list : """ | |
133 p[0] = ast.AttributeList() | |
134 | |
135 def p_attribute_list_2(self, p): | |
136 """attribute_list : nonempty_attribute_list""" | |
137 p[0] = p[1] | |
138 | |
139 def p_nonempty_attribute_list_1(self, p): | |
140 """nonempty_attribute_list : attribute""" | |
141 p[0] = ast.AttributeList(p[1]) | |
142 | |
143 def p_nonempty_attribute_list_2(self, p): | |
144 """nonempty_attribute_list : nonempty_attribute_list COMMA attribute""" | |
145 p[0] = p[1] | |
146 p[0].Append(p[3]) | |
147 | |
148 def p_attribute(self, p): | |
149 """attribute : NAME EQUALS evaled_literal | |
150 | NAME EQUALS NAME""" | |
151 p[0] = ast.Attribute(p[1], p[3], filename=self.filename, lineno=p.lineno(1)) | |
152 | |
153 def p_evaled_literal(self, p): | |
154 """evaled_literal : literal""" | |
155 # 'eval' the literal to strip the quotes. | |
156 p[0] = eval(p[1]) | |
157 | |
158 def p_struct(self, p): | |
159 """struct : attribute_section STRUCT NAME LBRACE struct_body RBRACE SEMI""" | |
160 p[0] = ast.Struct(p[3], p[1], p[5]) | |
161 | |
162 def p_struct_body_1(self, p): | |
163 """struct_body : """ | |
164 p[0] = ast.StructBody() | |
165 | |
166 def p_struct_body_2(self, p): | |
167 """struct_body : struct_body const | |
168 | struct_body enum | |
169 | struct_body struct_field""" | |
170 p[0] = p[1] | |
171 p[0].Append(p[2]) | |
172 | |
173 def p_struct_field(self, p): | |
174 """struct_field : typename NAME ordinal default SEMI""" | |
175 p[0] = ast.StructField(p[2], p[3], p[1], p[4]) | |
176 | |
177 def p_union(self, p): | |
178 """union : UNION NAME LBRACE union_body RBRACE SEMI""" | |
179 p[0] = ast.Union(p[2], p[4]) | |
180 | |
181 def p_union_body_1(self, p): | |
182 """union_body : """ | |
183 p[0] = ast.UnionBody() | |
184 | |
185 def p_union_body_2(self, p): | |
186 """union_body : union_body union_field""" | |
187 p[0] = p[1] | |
188 p[1].Append(p[2]) | |
189 | |
190 def p_union_field(self, p): | |
191 """union_field : typename NAME ordinal SEMI""" | |
192 p[0] = ast.UnionField(p[2], p[3], p[1]) | |
193 | |
194 def p_default_1(self, p): | |
195 """default : """ | |
196 p[0] = None | |
197 | |
198 def p_default_2(self, p): | |
199 """default : EQUALS constant""" | |
200 p[0] = p[2] | |
201 | |
202 def p_interface(self, p): | |
203 """interface : attribute_section INTERFACE NAME LBRACE interface_body \ | |
204 RBRACE SEMI""" | |
205 p[0] = ast.Interface(p[3], p[1], p[5]) | |
206 | |
207 def p_interface_body_1(self, p): | |
208 """interface_body : """ | |
209 p[0] = ast.InterfaceBody() | |
210 | |
211 def p_interface_body_2(self, p): | |
212 """interface_body : interface_body const | |
213 | interface_body enum | |
214 | interface_body method""" | |
215 p[0] = p[1] | |
216 p[0].Append(p[2]) | |
217 | |
218 def p_response_1(self, p): | |
219 """response : """ | |
220 p[0] = None | |
221 | |
222 def p_response_2(self, p): | |
223 """response : RESPONSE LPAREN parameter_list RPAREN""" | |
224 p[0] = p[3] | |
225 | |
226 def p_method(self, p): | |
227 """method : NAME ordinal LPAREN parameter_list RPAREN response SEMI""" | |
228 p[0] = ast.Method(p[1], p[2], p[4], p[6]) | |
229 | |
230 def p_parameter_list_1(self, p): | |
231 """parameter_list : """ | |
232 p[0] = ast.ParameterList() | |
233 | |
234 def p_parameter_list_2(self, p): | |
235 """parameter_list : nonempty_parameter_list""" | |
236 p[0] = p[1] | |
237 | |
238 def p_nonempty_parameter_list_1(self, p): | |
239 """nonempty_parameter_list : parameter""" | |
240 p[0] = ast.ParameterList(p[1]) | |
241 | |
242 def p_nonempty_parameter_list_2(self, p): | |
243 """nonempty_parameter_list : nonempty_parameter_list COMMA parameter""" | |
244 p[0] = p[1] | |
245 p[0].Append(p[3]) | |
246 | |
247 def p_parameter(self, p): | |
248 """parameter : typename NAME ordinal""" | |
249 p[0] = ast.Parameter(p[2], p[3], p[1], | |
250 filename=self.filename, lineno=p.lineno(2)) | |
251 | |
252 def p_typename(self, p): | |
253 """typename : nonnullable_typename QSTN | |
254 | nonnullable_typename""" | |
255 if len(p) == 2: | |
256 p[0] = p[1] | |
257 else: | |
258 p[0] = p[1] + "?" | |
259 | |
260 def p_nonnullable_typename(self, p): | |
261 """nonnullable_typename : basictypename | |
262 | array | |
263 | fixed_array | |
264 | associative_array | |
265 | interfacerequest""" | |
266 p[0] = p[1] | |
267 | |
268 def p_basictypename(self, p): | |
269 """basictypename : identifier | |
270 | handletype""" | |
271 p[0] = p[1] | |
272 | |
273 def p_handletype(self, p): | |
274 """handletype : HANDLE | |
275 | HANDLE LANGLE NAME RANGLE""" | |
276 if len(p) == 2: | |
277 p[0] = p[1] | |
278 else: | |
279 if p[3] not in ('data_pipe_consumer', | |
280 'data_pipe_producer', | |
281 'message_pipe', | |
282 'shared_buffer'): | |
283 # Note: We don't enable tracking of line numbers for everything, so we | |
284 # can't use |p.lineno(3)|. | |
285 raise ParseError(self.filename, "Invalid handle type %r:" % p[3], | |
286 lineno=p.lineno(1), | |
287 snippet=self._GetSnippet(p.lineno(1))) | |
288 p[0] = "handle<" + p[3] + ">" | |
289 | |
290 def p_array(self, p): | |
291 """array : ARRAY LANGLE typename RANGLE""" | |
292 p[0] = p[3] + "[]" | |
293 | |
294 def p_fixed_array(self, p): | |
295 """fixed_array : ARRAY LANGLE typename COMMA INT_CONST_DEC RANGLE""" | |
296 value = int(p[5]) | |
297 if value == 0 or value > _MAX_ARRAY_SIZE: | |
298 raise ParseError(self.filename, "Fixed array size %d invalid:" % value, | |
299 lineno=p.lineno(5), | |
300 snippet=self._GetSnippet(p.lineno(5))) | |
301 p[0] = p[3] + "[" + p[5] + "]" | |
302 | |
303 def p_associative_array(self, p): | |
304 """associative_array : MAP LANGLE identifier COMMA typename RANGLE""" | |
305 p[0] = p[5] + "{" + p[3] + "}" | |
306 | |
307 def p_interfacerequest(self, p): | |
308 """interfacerequest : identifier AMP""" | |
309 p[0] = p[1] + "&" | |
310 | |
311 def p_ordinal_1(self, p): | |
312 """ordinal : """ | |
313 p[0] = None | |
314 | |
315 def p_ordinal_2(self, p): | |
316 """ordinal : ORDINAL""" | |
317 value = int(p[1][1:]) | |
318 if value > _MAX_ORDINAL_VALUE: | |
319 raise ParseError(self.filename, "Ordinal value %d too large:" % value, | |
320 lineno=p.lineno(1), | |
321 snippet=self._GetSnippet(p.lineno(1))) | |
322 p[0] = ast.Ordinal(value, filename=self.filename, lineno=p.lineno(1)) | |
323 | |
324 def p_enum(self, p): | |
325 """enum : ENUM NAME LBRACE nonempty_enum_value_list RBRACE SEMI | |
326 | ENUM NAME LBRACE nonempty_enum_value_list COMMA RBRACE SEMI""" | |
327 p[0] = ast.Enum(p[2], p[4], filename=self.filename, lineno=p.lineno(1)) | |
328 | |
329 def p_nonempty_enum_value_list_1(self, p): | |
330 """nonempty_enum_value_list : enum_value""" | |
331 p[0] = ast.EnumValueList(p[1]) | |
332 | |
333 def p_nonempty_enum_value_list_2(self, p): | |
334 """nonempty_enum_value_list : nonempty_enum_value_list COMMA enum_value""" | |
335 p[0] = p[1] | |
336 p[0].Append(p[3]) | |
337 | |
338 def p_enum_value(self, p): | |
339 """enum_value : NAME | |
340 | NAME EQUALS int | |
341 | NAME EQUALS identifier_wrapped""" | |
342 p[0] = ast.EnumValue(p[1], p[3] if len(p) == 4 else None, | |
343 filename=self.filename, lineno=p.lineno(1)) | |
344 | |
345 def p_const(self, p): | |
346 """const : CONST typename NAME EQUALS constant SEMI""" | |
347 p[0] = ast.Const(p[3], p[2], p[5]) | |
348 | |
349 def p_constant(self, p): | |
350 """constant : literal | |
351 | identifier_wrapped""" | |
352 p[0] = p[1] | |
353 | |
354 def p_identifier_wrapped(self, p): | |
355 """identifier_wrapped : identifier""" | |
356 p[0] = ('IDENTIFIER', p[1]) | |
357 | |
358 # TODO(vtl): Make this produce a "wrapped" identifier (probably as an | |
359 # |ast.Identifier|, to be added) and get rid of identifier_wrapped. | |
360 def p_identifier(self, p): | |
361 """identifier : NAME | |
362 | NAME DOT identifier""" | |
363 p[0] = ''.join(p[1:]) | |
364 | |
365 def p_literal(self, p): | |
366 """literal : int | |
367 | float | |
368 | TRUE | |
369 | FALSE | |
370 | DEFAULT | |
371 | STRING_LITERAL""" | |
372 p[0] = p[1] | |
373 | |
374 def p_int(self, p): | |
375 """int : int_const | |
376 | PLUS int_const | |
377 | MINUS int_const""" | |
378 p[0] = ''.join(p[1:]) | |
379 | |
380 def p_int_const(self, p): | |
381 """int_const : INT_CONST_DEC | |
382 | INT_CONST_HEX""" | |
383 p[0] = p[1] | |
384 | |
385 def p_float(self, p): | |
386 """float : FLOAT_CONST | |
387 | PLUS FLOAT_CONST | |
388 | MINUS FLOAT_CONST""" | |
389 p[0] = ''.join(p[1:]) | |
390 | |
391 def p_error(self, e): | |
392 if e is None: | |
393 # Unexpected EOF. | |
394 # TODO(vtl): Can we figure out what's missing? | |
395 raise ParseError(self.filename, "Unexpected end of file") | |
396 | |
397 raise ParseError(self.filename, "Unexpected %r:" % e.value, lineno=e.lineno, | |
398 snippet=self._GetSnippet(e.lineno)) | |
399 | |
400 def _GetSnippet(self, lineno): | |
401 return self.source.split('\n')[lineno - 1] | |
402 | |
403 | |
404 def Parse(source, filename): | |
405 lexer = Lexer(filename) | |
406 parser = Parser(lexer, source, filename) | |
407 | |
408 lex.lex(object=lexer) | |
409 yacc.yacc(module=parser, debug=0, write_tables=0) | |
410 | |
411 tree = yacc.parse(source) | |
412 return tree | |
OLD | NEW |