Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(309)

Side by Side Diff: mojo/public/tools/bindings/pylib/mojom/parse/parser.py

Issue 814543006: Move //mojo/{public, edk} underneath //third_party (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW
« no previous file with comments | « mojo/public/tools/bindings/pylib/mojom/parse/lexer.py ('k') | mojo/public/tools/bindings/pylib/mojom/parse/translate.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698