| 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 |