• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
7import imp
8import os.path
9import sys
10
11# Disable lint check for finding modules:
12# pylint: disable=F0401
13
14def _GetDirAbove(dirname):
15  """Returns the directory "above" this file containing |dirname| (which must
16  also be "above" this file)."""
17  path = os.path.abspath(__file__)
18  while True:
19    path, tail = os.path.split(path)
20    assert tail
21    if tail == dirname:
22      return path
23
24try:
25  imp.find_module("ply")
26except ImportError:
27  sys.path.append(os.path.join(_GetDirAbove("mojo"), "third_party"))
28from ply import lex
29from ply import yacc
30
31from ..error import Error
32import ast
33from lexer import Lexer
34
35
36_MAX_ORDINAL_VALUE = 0xffffffff
37
38
39def _ListFromConcat(*items):
40  """Generate list by concatenating inputs (note: only concatenates lists, not
41  tuples or other iterables)."""
42  itemsout = []
43  for item in items:
44    if item is None:
45      continue
46    if type(item) is not type([]):
47      itemsout.append(item)
48    else:
49      itemsout.extend(item)
50  return itemsout
51
52
53# Disable lint check for exceptions deriving from Exception:
54# pylint: disable=W0710
55class ParseError(Error):
56  """Class for errors from the parser."""
57
58  def __init__(self, filename, message, lineno=None, snippet=None):
59    Error.__init__(self, filename, message, lineno=lineno,
60                   addenda=([snippet] if snippet else None))
61
62
63# We have methods which look like they could be functions:
64# pylint: disable=R0201
65class Parser(object):
66
67  def __init__(self, lexer, source, filename):
68    self.tokens = lexer.tokens
69    self.source = source
70    self.filename = filename
71
72  def p_root(self, p):
73    """root : import root
74            | module
75            | definitions"""
76    if len(p) > 2:
77      p[0] = _ListFromConcat(p[1], p[2])
78    else:
79      # Generator expects a module. If one wasn't specified insert one with an
80      # empty name.
81      if p[1][0] != 'MODULE':
82        p[0] = [('MODULE', '', None, p[1])]
83      else:
84        p[0] = [p[1]]
85
86  def p_import(self, p):
87    """import : IMPORT STRING_LITERAL"""
88    # 'eval' the literal to strip the quotes.
89    p[0] = ('IMPORT', eval(p[2]))
90
91  def p_module(self, p):
92    """module : attribute_section MODULE identifier LBRACE definitions RBRACE"""
93    p[0] = ('MODULE', p[3], p[1], p[5])
94
95  def p_definitions(self, p):
96    """definitions : definition definitions
97                   | """
98    if len(p) > 1:
99      p[0] = _ListFromConcat(p[1], p[2])
100
101  def p_definition(self, p):
102    """definition : struct
103                  | interface
104                  | enum
105                  | const"""
106    p[0] = p[1]
107
108  def p_attribute_section(self, p):
109    """attribute_section : LBRACKET attributes RBRACKET
110                         | """
111    if len(p) > 3:
112      p[0] = p[2]
113
114  def p_attributes(self, p):
115    """attributes : attribute
116                  | attribute COMMA attributes
117                  | """
118    if len(p) == 2:
119      p[0] = _ListFromConcat(p[1])
120    elif len(p) > 3:
121      p[0] = _ListFromConcat(p[1], p[3])
122
123  def p_attribute(self, p):
124    """attribute : NAME EQUALS evaled_literal
125                 | NAME EQUALS NAME"""
126    p[0] = ('ATTRIBUTE', p[1], p[3])
127
128  def p_evaled_literal(self, p):
129    """evaled_literal : literal"""
130    # 'eval' the literal to strip the quotes.
131    p[0] = eval(p[1])
132
133  def p_struct(self, p):
134    """struct : attribute_section STRUCT NAME LBRACE struct_body RBRACE SEMI"""
135    p[0] = ('STRUCT', p[3], p[1], p[5])
136
137  def p_struct_body(self, p):
138    """struct_body : field struct_body
139                   | enum struct_body
140                   | const struct_body
141                   | """
142    if len(p) > 1:
143      p[0] = _ListFromConcat(p[1], p[2])
144
145  def p_field(self, p):
146    """field : typename NAME ordinal default SEMI"""
147    p[0] = ('FIELD', p[1], p[2], p[3], p[4])
148
149  def p_default(self, p):
150    """default : EQUALS constant
151               | """
152    if len(p) > 2:
153      p[0] = p[2]
154
155  def p_interface(self, p):
156    """interface : attribute_section INTERFACE NAME LBRACE interface_body \
157                       RBRACE SEMI"""
158    p[0] = ('INTERFACE', p[3], p[1], p[5])
159
160  def p_interface_body(self, p):
161    """interface_body : method interface_body
162                      | enum interface_body
163                      | const interface_body
164                      | """
165    if len(p) > 1:
166      p[0] = _ListFromConcat(p[1], p[2])
167
168  def p_response(self, p):
169    """response : RESPONSE LPAREN parameters RPAREN
170                | """
171    if len(p) > 3:
172      p[0] = p[3]
173
174  def p_method(self, p):
175    """method : NAME ordinal LPAREN parameters RPAREN response SEMI"""
176    p[0] = ('METHOD', p[1], p[4], p[2], p[6])
177
178  def p_parameters(self, p):
179    """parameters : parameter
180                  | parameter COMMA parameters
181                  | """
182    if len(p) == 1:
183      p[0] = []
184    elif len(p) == 2:
185      p[0] = _ListFromConcat(p[1])
186    elif len(p) > 3:
187      p[0] = _ListFromConcat(p[1], p[3])
188
189  def p_parameter(self, p):
190    """parameter : typename NAME ordinal"""
191    p[0] = ('PARAM', p[1], p[2], p[3])
192
193  def p_typename(self, p):
194    """typename : basictypename
195                | array
196                | interfacerequest"""
197    p[0] = p[1]
198
199  def p_basictypename(self, p):
200    """basictypename : identifier
201                     | handletype"""
202    p[0] = p[1]
203
204  def p_handletype(self, p):
205    """handletype : HANDLE
206                  | HANDLE LANGLE NAME RANGLE"""
207    if len(p) == 2:
208      p[0] = p[1]
209    else:
210      if p[3] not in ('data_pipe_consumer',
211                      'data_pipe_producer',
212                      'message_pipe',
213                      'shared_buffer'):
214        # Note: We don't enable tracking of line numbers for everything, so we
215        # can't use |p.lineno(3)|.
216        raise ParseError(self.filename, "Invalid handle type %r:" % p[3],
217                         lineno=p.lineno(1),
218                         snippet=self._GetSnippet(p.lineno(1)))
219      p[0] = "handle<" + p[3] + ">"
220
221  def p_array(self, p):
222    """array : typename LBRACKET RBRACKET"""
223    p[0] = p[1] + "[]"
224
225  def p_interfacerequest(self, p):
226    """interfacerequest : identifier AMP"""
227    p[0] = p[1] + "&"
228
229  def p_ordinal(self, p):
230    """ordinal : ORDINAL
231               | """
232    if len(p) > 1:
233      value = int(p[1][1:])
234      if value > _MAX_ORDINAL_VALUE:
235        raise ParseError(self.filename, "Ordinal value %d too large:" % value,
236                         lineno=p.lineno(1),
237                         snippet=self._GetSnippet(p.lineno(1)))
238      p[0] = ast.Ordinal(value, filename=self.filename, lineno=p.lineno(1))
239    else:
240      p[0] = ast.Ordinal(None)
241
242  def p_enum(self, p):
243    """enum : ENUM NAME LBRACE enum_fields RBRACE SEMI"""
244    p[0] = ('ENUM', p[2], p[4])
245
246  def p_enum_fields(self, p):
247    """enum_fields : enum_field
248                   | enum_field COMMA enum_fields
249                   | """
250    if len(p) == 2:
251      p[0] = _ListFromConcat(p[1])
252    elif len(p) > 3:
253      p[0] = _ListFromConcat(p[1], p[3])
254
255  def p_enum_field(self, p):
256    """enum_field : NAME
257                  | NAME EQUALS constant"""
258    if len(p) == 2:
259      p[0] = ('ENUM_FIELD', p[1], None)
260    else:
261      p[0] = ('ENUM_FIELD', p[1], p[3])
262
263  def p_const(self, p):
264    """const : CONST typename NAME EQUALS constant SEMI"""
265    p[0] = ('CONST', p[2], p[3], p[5])
266
267  def p_constant(self, p):
268    """constant : literal
269                | identifier_wrapped"""
270    p[0] = p[1]
271
272  def p_identifier_wrapped(self, p):
273    """identifier_wrapped : identifier"""
274    p[0] = ('IDENTIFIER', p[1])
275
276  def p_identifier(self, p):
277    """identifier : NAME
278                  | NAME DOT identifier"""
279    p[0] = ''.join(p[1:])
280
281  def p_literal(self, p):
282    """literal : number
283               | CHAR_CONST
284               | TRUE
285               | FALSE
286               | DEFAULT
287               | STRING_LITERAL"""
288    p[0] = p[1]
289
290  def p_number(self, p):
291    """number : digits
292              | PLUS digits
293              | MINUS digits"""
294    p[0] = ''.join(p[1:])
295
296  def p_digits(self, p):
297    """digits : INT_CONST_DEC
298              | INT_CONST_HEX
299              | FLOAT_CONST"""
300    p[0] = p[1]
301
302  def p_error(self, e):
303    if e is None:
304      # Unexpected EOF.
305      # TODO(vtl): Can we figure out what's missing?
306      raise ParseError(self.filename, "Unexpected end of file")
307
308    raise ParseError(self.filename, "Unexpected %r:" % e.value, lineno=e.lineno,
309                     snippet=self._GetSnippet(e.lineno))
310
311  def _GetSnippet(self, lineno):
312    return self.source.split('\n')[lineno - 1]
313
314
315def Parse(source, filename):
316  lexer = Lexer(filename)
317  parser = Parser(lexer, source, filename)
318
319  lex.lex(object=lexer)
320  yacc.yacc(module=parser, debug=0, write_tables=0)
321
322  tree = yacc.parse(source)
323  return tree
324