Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / mojo / public / tools / bindings / pylib / mojom / parse / parser.py
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("mojo"), "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                   | interface
118                   | enum
119                   | const"""
120     p[0] = p[1]
121
122   def p_attribute_section_1(self, p):
123     """attribute_section : """
124     p[0] = None
125
126   def p_attribute_section_2(self, p):
127     """attribute_section : LBRACKET attribute_list RBRACKET"""
128     p[0] = p[2]
129
130   def p_attribute_list_1(self, p):
131     """attribute_list : """
132     p[0] = ast.AttributeList()
133
134   def p_attribute_list_2(self, p):
135     """attribute_list : nonempty_attribute_list"""
136     p[0] = p[1]
137
138   def p_nonempty_attribute_list_1(self, p):
139     """nonempty_attribute_list : attribute"""
140     p[0] = ast.AttributeList(p[1])
141
142   def p_nonempty_attribute_list_2(self, p):
143     """nonempty_attribute_list : nonempty_attribute_list COMMA attribute"""
144     p[0] = p[1]
145     p[0].Append(p[3])
146
147   def p_attribute(self, p):
148     """attribute : NAME EQUALS evaled_literal
149                  | NAME EQUALS NAME"""
150     p[0] = ast.Attribute(p[1], p[3], filename=self.filename, lineno=p.lineno(1))
151
152   def p_evaled_literal(self, p):
153     """evaled_literal : literal"""
154     # 'eval' the literal to strip the quotes.
155     p[0] = eval(p[1])
156
157   def p_struct(self, p):
158     """struct : attribute_section STRUCT NAME LBRACE struct_body RBRACE SEMI"""
159     p[0] = ast.Struct(p[3], p[1], p[5])
160
161   def p_struct_body_1(self, p):
162     """struct_body : """
163     p[0] = ast.StructBody()
164
165   def p_struct_body_2(self, p):
166     """struct_body : struct_body const
167                    | struct_body enum
168                    | struct_body struct_field"""
169     p[0] = p[1]
170     p[0].Append(p[2])
171
172   def p_struct_field(self, p):
173     """struct_field : typename NAME ordinal default SEMI"""
174     p[0] = ast.StructField(p[2], p[3], p[1], p[4])
175
176   def p_default_1(self, p):
177     """default : """
178     p[0] = None
179
180   def p_default_2(self, p):
181     """default : EQUALS constant"""
182     p[0] = p[2]
183
184   def p_interface(self, p):
185     """interface : attribute_section INTERFACE NAME LBRACE interface_body \
186                        RBRACE SEMI"""
187     p[0] = ast.Interface(p[3], p[1], p[5])
188
189   def p_interface_body_1(self, p):
190     """interface_body : """
191     p[0] = ast.InterfaceBody()
192
193   def p_interface_body_2(self, p):
194     """interface_body : interface_body const
195                       | interface_body enum
196                       | interface_body method"""
197     p[0] = p[1]
198     p[0].Append(p[2])
199
200   def p_response_1(self, p):
201     """response : """
202     p[0] = None
203
204   def p_response_2(self, p):
205     """response : RESPONSE LPAREN parameter_list RPAREN"""
206     p[0] = p[3]
207
208   def p_method(self, p):
209     """method : NAME ordinal LPAREN parameter_list RPAREN response SEMI"""
210     p[0] = ast.Method(p[1], p[2], p[4], p[6])
211
212   def p_parameter_list_1(self, p):
213     """parameter_list : """
214     p[0] = ast.ParameterList()
215
216   def p_parameter_list_2(self, p):
217     """parameter_list : nonempty_parameter_list"""
218     p[0] = p[1]
219
220   def p_nonempty_parameter_list_1(self, p):
221     """nonempty_parameter_list : parameter"""
222     p[0] = ast.ParameterList(p[1])
223
224   def p_nonempty_parameter_list_2(self, p):
225     """nonempty_parameter_list : nonempty_parameter_list COMMA parameter"""
226     p[0] = p[1]
227     p[0].Append(p[3])
228
229   def p_parameter(self, p):
230     """parameter : typename NAME ordinal"""
231     p[0] = ast.Parameter(p[2], p[3], p[1],
232                          filename=self.filename, lineno=p.lineno(2))
233
234   def p_typename(self, p):
235     """typename : nonnullable_typename QSTN
236                 | nonnullable_typename"""
237     if len(p) == 2:
238       p[0] = p[1]
239     else:
240       p[0] = p[1] + "?"
241
242   def p_nonnullable_typename(self, p):
243     """nonnullable_typename : basictypename
244                             | array
245                             | fixed_array
246                             | associative_array
247                             | interfacerequest"""
248     p[0] = p[1]
249
250   def p_basictypename(self, p):
251     """basictypename : identifier
252                      | handletype"""
253     p[0] = p[1]
254
255   def p_handletype(self, p):
256     """handletype : HANDLE
257                   | HANDLE LANGLE NAME RANGLE"""
258     if len(p) == 2:
259       p[0] = p[1]
260     else:
261       if p[3] not in ('data_pipe_consumer',
262                       'data_pipe_producer',
263                       'message_pipe',
264                       'shared_buffer'):
265         # Note: We don't enable tracking of line numbers for everything, so we
266         # can't use |p.lineno(3)|.
267         raise ParseError(self.filename, "Invalid handle type %r:" % p[3],
268                          lineno=p.lineno(1),
269                          snippet=self._GetSnippet(p.lineno(1)))
270       p[0] = "handle<" + p[3] + ">"
271
272   def p_array(self, p):
273     """array : ARRAY LANGLE typename RANGLE"""
274     p[0] = p[3] + "[]"
275
276   def p_fixed_array(self, p):
277     """fixed_array : ARRAY LANGLE typename COMMA INT_CONST_DEC RANGLE"""
278     value = int(p[5])
279     if value == 0 or value > _MAX_ARRAY_SIZE:
280       raise ParseError(self.filename, "Fixed array size %d invalid:" % value,
281                        lineno=p.lineno(5),
282                        snippet=self._GetSnippet(p.lineno(5)))
283     p[0] = p[3] + "[" + p[5] + "]"
284
285   def p_associative_array(self, p):
286     """associative_array : MAP LANGLE identifier COMMA typename RANGLE"""
287     p[0] = p[5] + "{" + p[3] + "}"
288
289   def p_interfacerequest(self, p):
290     """interfacerequest : identifier AMP"""
291     p[0] = p[1] + "&"
292
293   def p_ordinal_1(self, p):
294     """ordinal : """
295     p[0] = None
296
297   def p_ordinal_2(self, p):
298     """ordinal : ORDINAL"""
299     value = int(p[1][1:])
300     if value > _MAX_ORDINAL_VALUE:
301       raise ParseError(self.filename, "Ordinal value %d too large:" % value,
302                        lineno=p.lineno(1),
303                        snippet=self._GetSnippet(p.lineno(1)))
304     p[0] = ast.Ordinal(value, filename=self.filename, lineno=p.lineno(1))
305
306   def p_enum(self, p):
307     """enum : ENUM NAME LBRACE nonempty_enum_value_list RBRACE SEMI
308             | ENUM NAME LBRACE nonempty_enum_value_list COMMA RBRACE SEMI"""
309     p[0] = ast.Enum(p[2], p[4], filename=self.filename, lineno=p.lineno(1))
310
311   def p_nonempty_enum_value_list_1(self, p):
312     """nonempty_enum_value_list : enum_value"""
313     p[0] = ast.EnumValueList(p[1])
314
315   def p_nonempty_enum_value_list_2(self, p):
316     """nonempty_enum_value_list : nonempty_enum_value_list COMMA enum_value"""
317     p[0] = p[1]
318     p[0].Append(p[3])
319
320   def p_enum_value(self, p):
321     """enum_value : NAME
322                   | NAME EQUALS int
323                   | NAME EQUALS identifier_wrapped"""
324     p[0] = ast.EnumValue(p[1], p[3] if len(p) == 4 else None,
325                          filename=self.filename, lineno=p.lineno(1))
326
327   def p_const(self, p):
328     """const : CONST typename NAME EQUALS constant SEMI"""
329     p[0] = ast.Const(p[3], p[2], p[5])
330
331   def p_constant(self, p):
332     """constant : literal
333                 | identifier_wrapped"""
334     p[0] = p[1]
335
336   def p_identifier_wrapped(self, p):
337     """identifier_wrapped : identifier"""
338     p[0] = ('IDENTIFIER', p[1])
339
340   # TODO(vtl): Make this produce a "wrapped" identifier (probably as an
341   # |ast.Identifier|, to be added) and get rid of identifier_wrapped.
342   def p_identifier(self, p):
343     """identifier : NAME
344                   | NAME DOT identifier"""
345     p[0] = ''.join(p[1:])
346
347   def p_literal(self, p):
348     """literal : int
349                | float
350                | TRUE
351                | FALSE
352                | DEFAULT
353                | STRING_LITERAL"""
354     p[0] = p[1]
355
356   def p_int(self, p):
357     """int : int_const
358            | PLUS int_const
359            | MINUS int_const"""
360     p[0] = ''.join(p[1:])
361
362   def p_int_const(self, p):
363     """int_const : INT_CONST_DEC
364                  | INT_CONST_HEX"""
365     p[0] = p[1]
366
367   def p_float(self, p):
368     """float : FLOAT_CONST
369              | PLUS FLOAT_CONST
370              | MINUS FLOAT_CONST"""
371     p[0] = ''.join(p[1:])
372
373   def p_error(self, e):
374     if e is None:
375       # Unexpected EOF.
376       # TODO(vtl): Can we figure out what's missing?
377       raise ParseError(self.filename, "Unexpected end of file")
378
379     raise ParseError(self.filename, "Unexpected %r:" % e.value, lineno=e.lineno,
380                      snippet=self._GetSnippet(e.lineno))
381
382   def _GetSnippet(self, lineno):
383     return self.source.split('\n')[lineno - 1]
384
385
386 def Parse(source, filename):
387   lexer = Lexer(filename)
388   parser = Parser(lexer, source, filename)
389
390   lex.lex(object=lexer)
391   yacc.yacc(module=parser, debug=0, write_tables=0)
392
393   tree = yacc.parse(source)
394   return tree