OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 2 #Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. |
| 5 |
| 6 """ Parser for PPAPI IDL """ |
| 7 |
| 8 # |
| 9 # IDL Parser |
| 10 # |
| 11 # The parser is uses the PLY yacc library to build a set of parsing rules based |
| 12 # on WebIDL. |
| 13 # |
| 14 # WebIDL, and WebIDL grammar can be found at: |
| 15 # http://dev.w3.org/2006/webapi/WebIDL/ |
| 16 # PLY can be found at: |
| 17 # http://www.dabeaz.com/ply/ |
| 18 # |
| 19 # The parser generates a tree by recursively matching sets of items against |
| 20 # defined patterns. When a match is made, that set of items is reduced |
| 21 # to a new item. The new item can provide a match for parent patterns. |
| 22 # In this way an AST is built (reduced) depth first. |
| 23 # |
| 24 |
| 25 # |
| 26 # Disable check for line length and Member as Function due to how grammar rules |
| 27 # are defined with PLY |
| 28 # |
| 29 # pylint: disable=R0201 |
| 30 # pylint: disable=C0301 |
| 31 |
| 32 import glob |
| 33 import optparse |
| 34 import os.path |
| 35 import sys |
| 36 import time |
| 37 |
| 38 from idl_lexer import IDLLexer |
| 39 from idl_node import IDLAttribute, IDLNode |
| 40 |
| 41 from ply import lex |
| 42 from ply import yacc |
| 43 |
| 44 # |
| 45 # ERROR_REMAP |
| 46 # |
| 47 # Maps the standard error formula into a more friendly error message. |
| 48 # |
| 49 ERROR_REMAP = { |
| 50 'Unexpected ")" after "(".' : 'Empty argument list.', |
| 51 'Unexpected ")" after ",".' : 'Missing argument.', |
| 52 'Unexpected "}" after ",".' : 'Trailing comma in block.', |
| 53 'Unexpected "}" after "{".' : 'Unexpected empty block.', |
| 54 'Unexpected comment after "}".' : 'Unexpected trailing comment.', |
| 55 'Unexpected "{" after keyword "enum".' : 'Enum missing name.', |
| 56 'Unexpected "{" after keyword "struct".' : 'Struct missing name.', |
| 57 'Unexpected "{" after keyword "interface".' : 'Interface missing name.', |
| 58 } |
| 59 |
| 60 |
| 61 def Boolean(val): |
| 62 """Convert to strict boolean type.""" |
| 63 if val: |
| 64 return True |
| 65 return False |
| 66 |
| 67 |
| 68 def ListFromConcat(*items): |
| 69 """Generate list by concatenating inputs""" |
| 70 itemsout = [] |
| 71 for item in items: |
| 72 if item is None: |
| 73 continue |
| 74 if type(item) is not type([]): |
| 75 itemsout.append(item) |
| 76 else: |
| 77 itemsout.extend(item) |
| 78 |
| 79 return itemsout |
| 80 |
| 81 def ExpandProduction(p): |
| 82 if type(p) == list: |
| 83 return '[' + ', '.join([ExpandProduction(x) for x in p]) + ']' |
| 84 if type(p) == IDLNode: |
| 85 return 'Node:' + str(p) |
| 86 if type(p) == IDLAttribute: |
| 87 return 'Attr:' + str(p) |
| 88 if type(p) == str: |
| 89 return 'str:' + p |
| 90 return '%s:%s' % (p.__class__.__name__, str(p)) |
| 91 |
| 92 # TokenTypeName |
| 93 # |
| 94 # Generate a string which has the type and value of the token. |
| 95 # |
| 96 def TokenTypeName(t): |
| 97 if t.type == 'SYMBOL': |
| 98 return 'symbol %s' % t.value |
| 99 if t.type in ['HEX', 'INT', 'OCT', 'FLOAT']: |
| 100 return 'value %s' % t.value |
| 101 if t.type == 'string' : |
| 102 return 'string "%s"' % t.value |
| 103 if t.type == 'COMMENT' : |
| 104 return 'comment' |
| 105 if t.type == t.value: |
| 106 return '"%s"' % t.value |
| 107 if t.type == ',': |
| 108 return 'Comma' |
| 109 if t.type == 'identifier': |
| 110 return 'identifier "%s"' % t.value |
| 111 return 'keyword "%s"' % t.value |
| 112 |
| 113 |
| 114 # |
| 115 # IDL Parser |
| 116 # |
| 117 # The Parser inherits the from the Lexer to provide PLY with the tokenizing |
| 118 # definitions. Parsing patterns are encoded as functions where p_<name> is |
| 119 # is called any time a patern matching the function documentation is found. |
| 120 # Paterns are expressed in the form of: |
| 121 # """ <new item> : <item> .... |
| 122 # | <item> ....""" |
| 123 # |
| 124 # Where new item is the result of a match against one or more sets of items |
| 125 # separated by the "|". |
| 126 # |
| 127 # The function is called with an object 'p' where p[0] is the output object |
| 128 # and p[n] is the set of inputs for positive values of 'n'. Len(p) can be |
| 129 # used to distinguish between multiple item sets in the pattern. |
| 130 # |
| 131 # For more details on parsing refer to the PLY documentation at |
| 132 # http://www.dabeaz.com/ply/ |
| 133 # |
| 134 # The parser is based on the WebIDL standard. See: |
| 135 # http://www.w3.org/TR/WebIDL/#idl-grammar |
| 136 # |
| 137 # The various productions are annotated so that the WHOLE number greater than |
| 138 # zero in the comment denotes the matching WebIDL grammar definition. |
| 139 # |
| 140 # Productions with a fractional component in the comment denote additions to |
| 141 # the WebIDL spec, such as comments. |
| 142 # |
| 143 |
| 144 |
| 145 class IDLParser(IDLLexer): |
| 146 # |
| 147 # We force all input files to start with two comments. The first comment is a |
| 148 # Copyright notice followed by a file comment and finally by file level |
| 149 # productions. |
| 150 # |
| 151 # [0] Insert a TOP definition for Copyright and Comments |
| 152 def p_Top(self, p): |
| 153 """Top : COMMENT COMMENT Definitions""" |
| 154 Copyright = self.BuildComment('Copyright', p, 1) |
| 155 Filedoc = self.BuildComment('Comment', p, 2) |
| 156 p[0] = ListFromConcat(Copyright, Filedoc, p[3]) |
| 157 |
| 158 # [0.1] Add support for Multiple COMMENTS |
| 159 def p_Comments(self, p): |
| 160 """Comments : CommentsRest""" |
| 161 if len(p) > 1: |
| 162 p[0] = p[1] |
| 163 |
| 164 # [0.2] Produce a COMMENT and aggregate sibling comments |
| 165 def p_CommentsRest(self, p): |
| 166 """CommentsRest : COMMENT CommentsRest |
| 167 | """ |
| 168 if len(p) > 1: |
| 169 p[0] = ListFromConcat(self.BuildComment('Comment', p, 1), p[2]) |
| 170 |
| 171 |
| 172 # |
| 173 #The parser is based on the WebIDL standard. See: |
| 174 # http://www.w3.org/TR/WebIDL/#idl-grammar |
| 175 # |
| 176 # [1] |
| 177 def p_Definitions(self, p): |
| 178 """Definitions : ExtendedAttributeList Definition Definitions |
| 179 | """ |
| 180 if len(p) > 1: |
| 181 p[2].AddChildren(p[1]) |
| 182 p[0] = ListFromConcat(p[2], p[3]) |
| 183 |
| 184 # [2] Add INLINE definition |
| 185 def p_Definition(self, p): |
| 186 """Definition : CallbackOrInterface |
| 187 | Partial |
| 188 | Dictionary |
| 189 | Exception |
| 190 | Enum |
| 191 | Typedef |
| 192 | ImplementsStatement""" |
| 193 p[0] = p[1] |
| 194 |
| 195 # [2.1] Error recovery for definition |
| 196 def p_DefinitionError(self, p): |
| 197 """Definition : error ';'""" |
| 198 p[0] = self.BuildError(p, 'Definition') |
| 199 |
| 200 # [3] |
| 201 def p_CallbackOrInterface(self, p): |
| 202 """CallbackOrInterface : CALLBACK CallbackRestOrInterface |
| 203 | Interface""" |
| 204 if len(p) > 2: |
| 205 p[0] = p[2] |
| 206 else: |
| 207 p[0] = p[1] |
| 208 |
| 209 # [4] |
| 210 def p_CallbackRestOrInterface(self, p): |
| 211 """CallbackRestOrInterface : CallbackRest |
| 212 | Interface""" |
| 213 p[0] = p[1] |
| 214 |
| 215 # [5] |
| 216 def p_Interface(self, p): |
| 217 """Interface : INTERFACE identifier Inheritance '{' InterfaceMembers '}' ';'
""" |
| 218 p[0] = self.BuildNamed('Interface', p, 2, ListFromConcat(p[3], p[5])) |
| 219 |
| 220 # [6] Error recovery for PARTIAL |
| 221 def p_Partial(self, p): |
| 222 """Partial : PARTIAL PartialDefinition""" |
| 223 p[2].AddChildren(self.BuildTrue('Partial')) |
| 224 p[0] = p[2] |
| 225 |
| 226 # [6.1] Error recovery for Enums |
| 227 def p_PartialError(self, p): |
| 228 """Partial : PARTIAL error""" |
| 229 p[0] = self.BuildError(p, 'Partial') |
| 230 |
| 231 # [7] |
| 232 def p_PartialDefinition(self, p): |
| 233 """PartialDefinition : PartialDictionary |
| 234 | PartialInterface""" |
| 235 p[0] = p[1] |
| 236 |
| 237 # [8] |
| 238 def p_PartialInterface(self, p): |
| 239 """PartialInterface : INTERFACE identifier '{' InterfaceMembers '}' ';'""" |
| 240 p[0] = self.BuildNamed('Interface', p, 2, p[4]) |
| 241 |
| 242 # [9] |
| 243 def p_InterfaceMembers(self, p): |
| 244 """InterfaceMembers : ExtendedAttributeList InterfaceMember InterfaceMembers |
| 245 |""" |
| 246 if len(p) > 1: |
| 247 p[2].AddChildren(p[1]) |
| 248 p[0] = ListFromConcat(p[2], p[3]) |
| 249 |
| 250 # [10] |
| 251 def p_InterfaceMember(self, p): |
| 252 """InterfaceMember : Const |
| 253 | AttributeOrOperation""" |
| 254 p[0] = p[1] |
| 255 |
| 256 # [11] |
| 257 def p_Dictionary(self, p): |
| 258 """Dictionary : DICTIONARY identifier Inheritance '{' DictionaryMembers '}'
';'""" |
| 259 p[0] = self.BuildNamed('Dictionary', p, 2, ListFromConcat(p[3], p[5])) |
| 260 |
| 261 # [11.1] Error recovery for regular Dictionary |
| 262 def p_DictionaryError(self, p): |
| 263 """Dictionary : DICTIONARY error ';'""" |
| 264 p[0] = self.BuildError(p, 'Dictionary') |
| 265 |
| 266 # [12] |
| 267 def p_DictionaryMembers(self, p): |
| 268 """DictionaryMembers : ExtendedAttributeList DictionaryMember DictionaryMemb
ers |
| 269 |""" |
| 270 if len(p) > 1: |
| 271 p[2].AddChildren(p[1]) |
| 272 p[0] = ListFromConcat(p[2], p[3]) |
| 273 |
| 274 # [13] |
| 275 def p_DictionaryMember(self, p): |
| 276 """DictionaryMember : Type identifier Default ';'""" |
| 277 p[0] = self.BuildNamed('Key', p, 2, ListFromConcat(p[1], p[3])) |
| 278 |
| 279 # [14] |
| 280 def p_PartialDictionary(self, p): |
| 281 """PartialDictionary : DICTIONARY identifier '{' DictionaryMembers '}' ';'""
" |
| 282 partial = self.BuildTrue('Partial') |
| 283 p[0] = self.BuildNamed('Dictionary', p, 2, ListFromConcat(p[4], partial)) |
| 284 |
| 285 # [14.1] Error recovery for Partial Dictionary |
| 286 def p_PartialDictionaryError(self, p): |
| 287 """PartialDictionary : DICTIONARY error ';'""" |
| 288 p[0] = self.BuildError(p, 'PartialDictionary') |
| 289 |
| 290 # [15] |
| 291 def p_Default(self, p): |
| 292 """Default : '=' DefaultValue |
| 293 |""" |
| 294 if len(p) > 1: |
| 295 p[0] = self.BuildProduction('Default', p, 2, p[2]) |
| 296 |
| 297 # [16] |
| 298 def p_DefaultValue(self, p): |
| 299 """DefaultValue : ConstValue |
| 300 | string""" |
| 301 if type(p[1]) == str: |
| 302 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'DOMString'), |
| 303 self.BuildAttribute('NAME', p[1])) |
| 304 else: |
| 305 p[0] = p[1] |
| 306 |
| 307 # [17] |
| 308 def p_Exception(self, p): |
| 309 """Exception : EXCEPTION identifier Inheritance '{' ExceptionMembers '}' ';'
""" |
| 310 p[0] = self.BuildNamed('Exception', p, 2, ListFromConcat(p[3], p[5])) |
| 311 |
| 312 # [18] |
| 313 def p_ExceptionMembers(self, p): |
| 314 """ExceptionMembers : ExtendedAttributeList ExceptionMember ExceptionMembers |
| 315 |""" |
| 316 if len(p) > 1: |
| 317 p[2].AddChildren(p[1]) |
| 318 p[0] = ListFromConcat(p[2], p[3]) |
| 319 |
| 320 # [18.1] Error recovery for ExceptionMembers |
| 321 def p_ExceptionMembersError(self, p): |
| 322 """ExceptionMembers : error""" |
| 323 p[0] = self.BuildError(p, 'ExceptionMembers') |
| 324 |
| 325 # [19] |
| 326 def p_Inheritance(self, p): |
| 327 """Inheritance : ':' identifier |
| 328 |""" |
| 329 if len(p) > 1: |
| 330 p[0] = self.BuildNamed('Inherit', p, 2) |
| 331 |
| 332 # [20] |
| 333 def p_Enum(self, p): |
| 334 """Enum : ENUM identifier '{' EnumValueList '}' ';'""" |
| 335 p[0] = self.BuildNamed('Enum', p, 2, p[4]) |
| 336 |
| 337 # [20.1] Error recovery for Enums |
| 338 def p_EnumError(self, p): |
| 339 """Enum : ENUM error ';'""" |
| 340 p[0] = self.BuildError(p, 'Enum') |
| 341 |
| 342 # [21] |
| 343 def p_EnumValueList(self, p): |
| 344 """EnumValueList : ExtendedAttributeList string EnumValues""" |
| 345 enum = self.BuildNamed('EnumItem', p, 2, p[1]) |
| 346 p[0] = ListFromConcat(enum, p[3]) |
| 347 |
| 348 # [22] |
| 349 def p_EnumValues(self, p): |
| 350 """EnumValues : ',' ExtendedAttributeList string EnumValues |
| 351 |""" |
| 352 if len(p) > 1: |
| 353 enum = self.BuildNamed('EnumItem', p, 3, p[2]) |
| 354 p[0] = ListFromConcat(enum, p[4]) |
| 355 |
| 356 # [23] |
| 357 def p_CallbackRest(self, p): |
| 358 """CallbackRest : identifier '=' ReturnType '(' ArgumentList ')' ';'""" |
| 359 arguments = self.BuildProduction('Arguments', p, 4, p[5]) |
| 360 p[0] = self.BuildNamed('Callback', p, 1, ListFromConcat(p[3], arguments)) |
| 361 |
| 362 # [24] |
| 363 def p_Typedef(self, p): |
| 364 """Typedef : TYPEDEF ExtendedAttributeList Type identifier ';'""" |
| 365 p[0] = self.BuildNamed('Typedef', p, 4, ListFromConcat(p[2], p[3])) |
| 366 |
| 367 # [24.1] Error recovery for Typedefs |
| 368 def p_TypedefError(self, p): |
| 369 """Typedef : TYPEDEF error ';'""" |
| 370 p[0] = self.BuildError(p, 'Typedef') |
| 371 |
| 372 # [25] |
| 373 def p_ImplementsStatement(self, p): |
| 374 """ImplementsStatement : identifier IMPLEMENTS identifier ';'""" |
| 375 name = self.BuildAttribute('REFERENCE', p[3]) |
| 376 p[0] = self.BuildNamed('Implements', p, 1, name) |
| 377 |
| 378 # [26] |
| 379 def p_Const(self, p): |
| 380 """Const : CONST ConstType identifier '=' ConstValue ';'""" |
| 381 value = self.BuildProduction('Value', p, 5, p[5]) |
| 382 p[0] = self.BuildNamed('Const', p, 3, ListFromConcat(p[2], value)) |
| 383 |
| 384 # [27] |
| 385 def p_ConstValue(self, p): |
| 386 """ConstValue : BooleanLiteral |
| 387 | FloatLiteral |
| 388 | integer |
| 389 | null""" |
| 390 if type(p[1]) == str: |
| 391 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'integer'), |
| 392 self.BuildAttribute('NAME', p[1])) |
| 393 else: |
| 394 p[0] = p[1] |
| 395 |
| 396 # [27.1] Add definition for NULL |
| 397 def p_null(self, p): |
| 398 """null : NULL""" |
| 399 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'NULL'), |
| 400 self.BuildAttribute('NAME', 'NULL')) |
| 401 |
| 402 # [28] |
| 403 def p_BooleanLiteral(self, p): |
| 404 """BooleanLiteral : TRUE |
| 405 | FALSE""" |
| 406 value = self.BuildAttribute('NAME', Boolean(p[1] == 'true')) |
| 407 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'boolean'), value) |
| 408 |
| 409 # [29] |
| 410 def p_FloatLiteral(self, p): |
| 411 """FloatLiteral : float |
| 412 | '-' INFINITY |
| 413 | INFINITY |
| 414 | NAN """ |
| 415 if len(p) > 2: |
| 416 val = '-Infinity' |
| 417 else: |
| 418 val = p[1] |
| 419 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'float'), |
| 420 self.BuildAttribute('VALUE', val)) |
| 421 |
| 422 # [30] |
| 423 def p_AttributeOrOperation(self, p): |
| 424 """AttributeOrOperation : STRINGIFIER StringifierAttributeOrOperation |
| 425 | Attribute |
| 426 | Operation""" |
| 427 if len(p) > 2: |
| 428 p[0] = p[2] |
| 429 else: |
| 430 p[0] = p[1] |
| 431 |
| 432 # [31] |
| 433 def p_StringifierAttributeOrOperation(self, p): |
| 434 """StringifierAttributeOrOperation : Attribute |
| 435 | OperationRest |
| 436 | ';'""" |
| 437 if p[1] == ';': |
| 438 p[0] = self.BuildAttribute('STRINGIFIER', Boolean(True)) |
| 439 else: |
| 440 p[0] = ListFromConcat(self.BuildAttribute('STRINGIFIER', p[1]), p[1]) |
| 441 |
| 442 # [32] |
| 443 def p_Attribute(self, p): |
| 444 """Attribute : Inherit ReadOnly ATTRIBUTE Type identifier ';'""" |
| 445 p[0] = self.BuildNamed('Attribute', p, 5, |
| 446 ListFromConcat(p[1], p[2], p[4])) |
| 447 |
| 448 # [33] |
| 449 def p_Inherit(self, p): |
| 450 """Inherit : INHERIT |
| 451 |""" |
| 452 if len(p) > 1: |
| 453 p[0] = self.BuildTrue('INHERIT') |
| 454 |
| 455 # [34] |
| 456 def p_ReadOnly(self, p): |
| 457 """ReadOnly : READONLY |
| 458 |""" |
| 459 if len(p) > 1: |
| 460 p[0] = self.BuildTrue('READONLY') |
| 461 |
| 462 # [35] |
| 463 def p_Operation(self, p): |
| 464 """Operation : Qualifiers OperationRest""" |
| 465 p[2].AddChildren(p[1]) |
| 466 p[0] = p[2] |
| 467 |
| 468 # [36] |
| 469 def p_Qualifiers(self, p): |
| 470 """Qualifiers : STATIC |
| 471 | Specials""" |
| 472 if p[1] == 'static': |
| 473 p[0] = self.BuildTrue('STATIC') |
| 474 else: |
| 475 p[0] = p[1] |
| 476 |
| 477 # [37] |
| 478 def p_Specials(self, p): |
| 479 """Specials : Special Specials |
| 480 | """ |
| 481 if len(p) > 1: |
| 482 p[0] = ListFromConcat(p[1], p[2]) |
| 483 |
| 484 # [38] |
| 485 def p_Special(self, p): |
| 486 """Special : GETTER |
| 487 | SETTER |
| 488 | CREATOR |
| 489 | DELETER |
| 490 | LEGACYCALLER""" |
| 491 p[0] = self.BuildTrue(p[1].upper()) |
| 492 |
| 493 |
| 494 # [39] |
| 495 def p_OperationRest(self, p): |
| 496 """OperationRest : ReturnType OptionalIdentifier '(' ArgumentList ')' ';'""" |
| 497 arguments = self.BuildProduction('Arguments', p, 3, p[4]) |
| 498 p[0] = self.BuildNamed('Operation', p, 2, ListFromConcat(p[1], arguments)) |
| 499 |
| 500 # [40] |
| 501 def p_OptionalIdentifier(self, p): |
| 502 """OptionalIdentifier : identifier |
| 503 |""" |
| 504 if len(p) > 1: |
| 505 p[0] = p[1] |
| 506 else: |
| 507 p[0] = '_unnamed_' |
| 508 |
| 509 # [41] |
| 510 def p_ArgumentList(self, p): |
| 511 """ArgumentList : Argument Arguments |
| 512 |""" |
| 513 if len(p) > 1: |
| 514 p[0] = ListFromConcat(p[1], p[2]) |
| 515 |
| 516 # [41.1] ArgumentList error recovery |
| 517 def p_ArgumentListError(self, p): |
| 518 """ArgumentList : error """ |
| 519 p[0] = self.BuildError(p, 'ArgumentList') |
| 520 |
| 521 # [42] |
| 522 def p_Arguments(self, p): |
| 523 """Arguments : ',' Argument Arguments |
| 524 |""" |
| 525 if len(p) > 1: |
| 526 p[0] = ListFromConcat(p[2], p[3]) |
| 527 |
| 528 # [43] |
| 529 def p_Argument(self, p): |
| 530 """Argument : ExtendedAttributeList OptionalOrRequiredArgument""" |
| 531 p[2].AddChildren(p[1]) |
| 532 p[0] = p[2] |
| 533 |
| 534 |
| 535 # [44] |
| 536 def p_OptionalOrRequiredArgument(self, p): |
| 537 """OptionalOrRequiredArgument : OPTIONAL Type ArgumentName Default |
| 538 | Type Ellipsis ArgumentName""" |
| 539 if len(p) > 4: |
| 540 arg = self.BuildNamed('Argument', p, 3, ListFromConcat(p[2], p[4])) |
| 541 arg.AddChildren(self.BuildTrue('OPTIONAL')) |
| 542 else: |
| 543 arg = self.BuildNamed('Argument', p, 3, ListFromConcat(p[1], p[2])) |
| 544 p[0] = arg |
| 545 |
| 546 # [45] |
| 547 def p_ArgumentName(self, p): |
| 548 """ArgumentName : ArgumentNameKeyword |
| 549 | identifier""" |
| 550 p[0] = p[1] |
| 551 |
| 552 # [46] |
| 553 def p_Ellipsis(self, p): |
| 554 """Ellipsis : ELLIPSIS |
| 555 |""" |
| 556 if len(p) > 1: |
| 557 p[0] = self.BuildNamed('Argument', p, 1) |
| 558 p[0].AddChildren(self.BuildTrue('ELLIPSIS')) |
| 559 |
| 560 # [47] |
| 561 def p_ExceptionMember(self, p): |
| 562 """ExceptionMember : Const |
| 563 | ExceptionField""" |
| 564 p[0] = p[1] |
| 565 |
| 566 # [48] |
| 567 def p_ExceptionField(self, p): |
| 568 """ExceptionField : Type identifier ';'""" |
| 569 p[0] = self.BuildNamed('ExceptionField', p, 2, p[1]) |
| 570 |
| 571 # [48.1] Error recovery for ExceptionMembers |
| 572 def p_ExceptionFieldError(self, p): |
| 573 """ExceptionField : error""" |
| 574 p[0] = self.BuildError(p, 'ExceptionField') |
| 575 |
| 576 # [49] Add optional comment field |
| 577 def p_ExtendedAttributeList(self, p): |
| 578 """ExtendedAttributeList : Comments '[' ExtendedAttribute ExtendedAttributes
']' |
| 579 | Comments """ |
| 580 if len(p) > 2: |
| 581 items = ListFromConcat(p[3], p[4]) |
| 582 attribs = self.BuildProduction('ExtAttributes', p, 2, items) |
| 583 p[0] = ListFromConcat(p[1], attribs) |
| 584 else: |
| 585 p[0] = p[1] |
| 586 |
| 587 # [50] |
| 588 def p_ExtendedAttributes(self, p): |
| 589 """ExtendedAttributes : ',' ExtendedAttribute ExtendedAttributes |
| 590 |""" |
| 591 if len(p) > 1: |
| 592 p[0] = ListFromConcat(p[2], p[3]) |
| 593 |
| 594 # We only support: |
| 595 # [ identifier ] |
| 596 # [ identifier = identifier ] |
| 597 # [ identifier ( ArgumentList )] |
| 598 # [ identifier = identifier ( ArgumentList )] |
| 599 # [51] map directly to 74-77 |
| 600 # [52-54, 56] are unsupported |
| 601 def p_ExtendedAttribute(self, p): |
| 602 """ExtendedAttribute : ExtendedAttributeNoArgs |
| 603 | ExtendedAttributeArgList |
| 604 | ExtendedAttributeIdent |
| 605 | ExtendedAttributeNamedArgList""" |
| 606 p[0] = p[1] |
| 607 |
| 608 # [55] |
| 609 def p_ArgumentNameKeyword(self, p): |
| 610 """ArgumentNameKeyword : ATTRIBUTE |
| 611 | CALLBACK |
| 612 | CONST |
| 613 | CREATOR |
| 614 | DELETER |
| 615 | DICTIONARY |
| 616 | ENUM |
| 617 | EXCEPTION |
| 618 | GETTER |
| 619 | IMPLEMENTS |
| 620 | INHERIT |
| 621 | LEGACYCALLER |
| 622 | PARTIAL |
| 623 | SETTER |
| 624 | STATIC |
| 625 | STRINGIFIER |
| 626 | TYPEDEF |
| 627 | UNRESTRICTED""" |
| 628 p[0] = p[1] |
| 629 |
| 630 # [57] |
| 631 def p_Type(self, p): |
| 632 """Type : SingleType |
| 633 | UnionType TypeSuffix""" |
| 634 if len(p) == 2: |
| 635 p[0] = self.BuildProduction('Type', p, 1, p[1]) |
| 636 else: |
| 637 p[0] = self.BuildProduction('Type', p, 1, ListFromConcat(p[1], p[2])) |
| 638 |
| 639 # [58] |
| 640 def p_SingleType(self, p): |
| 641 """SingleType : NonAnyType |
| 642 | ANY TypeSuffixStartingWithArray""" |
| 643 if len(p) == 2: |
| 644 p[0] = p[1] |
| 645 else: |
| 646 p[0] = ListFromConcat(self.BuildProduction('Any', p, 1), p[2]) |
| 647 |
| 648 # [59] |
| 649 def p_UnionType(self, p): |
| 650 """UnionType : '(' UnionMemberType OR UnionMemberType UnionMemberTypes ')'""
" |
| 651 |
| 652 # [60] |
| 653 def p_UnionMemberType(self, p): |
| 654 """UnionMemberType : NonAnyType |
| 655 | UnionType TypeSuffix |
| 656 | ANY '[' ']' TypeSuffix""" |
| 657 # [61] |
| 658 def p_UnionMemberTypes(self, p): |
| 659 """UnionMemberTypes : OR UnionMemberType UnionMemberTypes |
| 660 |""" |
| 661 |
| 662 # [62] Moved DATE, DOMSTRING, OBJECT to PrimitiveType |
| 663 def p_NonAnyType(self, p): |
| 664 """NonAnyType : PrimitiveType TypeSuffix |
| 665 | identifier TypeSuffix |
| 666 | SEQUENCE '<' Type '>' Null""" |
| 667 if len(p) == 3: |
| 668 if type(p[1]) == str: |
| 669 typeref = self.BuildNamed('Typeref', p, 1) |
| 670 else: |
| 671 typeref = p[1] |
| 672 p[0] = ListFromConcat(typeref, p[2]) |
| 673 |
| 674 if len(p) == 6: |
| 675 p[0] = self.BuildProduction('Sequence', p, 1, ListFromConcat(p[3], p[5])) |
| 676 |
| 677 |
| 678 # [63] |
| 679 def p_ConstType(self, p): |
| 680 """ConstType : PrimitiveType Null |
| 681 | identifier Null""" |
| 682 if type(p[1]) == str: |
| 683 p[0] = self.BuildNamed('Typeref', p, 1, p[2]) |
| 684 else: |
| 685 p[1].AddChildren(p[2]) |
| 686 p[0] = p[1] |
| 687 |
| 688 |
| 689 # [64] |
| 690 def p_PrimitiveType(self, p): |
| 691 """PrimitiveType : UnsignedIntegerType |
| 692 | UnrestrictedFloatType |
| 693 | BOOLEAN |
| 694 | BYTE |
| 695 | OCTET |
| 696 | DOMSTRING |
| 697 | DATE |
| 698 | OBJECT""" |
| 699 if type(p[1]) == str: |
| 700 p[0] = self.BuildNamed('PrimitiveType', p, 1) |
| 701 else: |
| 702 p[0] = p[1] |
| 703 |
| 704 |
| 705 # [65] |
| 706 def p_UnrestrictedFloatType(self, p): |
| 707 """UnrestrictedFloatType : UNRESTRICTED FloatType |
| 708 | FloatType""" |
| 709 if len(p) == 2: |
| 710 typeref = self.BuildNamed('PrimitiveType', p, 1) |
| 711 else: |
| 712 typeref = self.BuildNamed('PrimitiveType', p, 2) |
| 713 typeref.AddChildren(self.BuildTrue('UNRESTRICTED')) |
| 714 p[0] = typeref |
| 715 |
| 716 |
| 717 # [66] |
| 718 def p_FloatType(self, p): |
| 719 """FloatType : FLOAT |
| 720 | DOUBLE""" |
| 721 p[0] = p[1] |
| 722 |
| 723 # [67] |
| 724 def p_UnsignedIntegerType(self, p): |
| 725 """UnsignedIntegerType : UNSIGNED IntegerType |
| 726 | IntegerType""" |
| 727 if len(p) == 2: |
| 728 p[0] = p[1] |
| 729 else: |
| 730 p[0] = 'unsigned ' + p[2] |
| 731 |
| 732 # [68] |
| 733 def p_IntegerType(self, p): |
| 734 """IntegerType : SHORT |
| 735 | LONG OptionalLong""" |
| 736 if len(p) == 2: |
| 737 p[0] = p[1] |
| 738 else: |
| 739 p[0] = p[1] + p[2] |
| 740 |
| 741 # [69] |
| 742 def p_OptionalLong(self, p): |
| 743 """OptionalLong : LONG |
| 744 | """ |
| 745 if len(p) > 1: |
| 746 p[0] = ' ' + p[1] |
| 747 else: |
| 748 p[0] = '' |
| 749 |
| 750 |
| 751 # [70] Add support for sized array |
| 752 def p_TypeSuffix(self, p): |
| 753 """TypeSuffix : '[' integer ']' TypeSuffix |
| 754 | '[' ']' TypeSuffix |
| 755 | '?' TypeSuffixStartingWithArray |
| 756 |""" |
| 757 if len(p) == 5: |
| 758 p[0] = self.BuildNamed('Array', p, 2, p[4]) |
| 759 |
| 760 if len(p) == 4: |
| 761 p[0] = self.BuildProduction('Array', p, 1, p[3]) |
| 762 |
| 763 if len(p) == 3: |
| 764 p[0] = ListFromConcat(self.BuildTrue('NULLABLE'), p[2]) |
| 765 |
| 766 |
| 767 # [71] |
| 768 def p_TypeSuffixStartingWithArray(self, p): |
| 769 """TypeSuffixStartingWithArray : '[' ']' TypeSuffix |
| 770 | """ |
| 771 if len(p) > 1: |
| 772 p[0] = self.BuildProduction('Array', p, 0, p[3]) |
| 773 |
| 774 # [72] |
| 775 def p_Null(self, p): |
| 776 """Null : '?' |
| 777 |""" |
| 778 if len(p) > 1: |
| 779 p[0] = self.BuildTrue('NULLABLE') |
| 780 |
| 781 # [73] |
| 782 def p_ReturnType(self, p): |
| 783 """ReturnType : Type |
| 784 | VOID""" |
| 785 if p[1] == 'void': |
| 786 p[0] = self.BuildProduction('Type', p, 1) |
| 787 p[0].AddChildren(self.BuildNamed('PrimitiveType', p, 1)) |
| 788 else: |
| 789 p[0] = p[1] |
| 790 |
| 791 # [74] |
| 792 def p_ExtendedAttributeNoArgs(self, p): |
| 793 """ExtendedAttributeNoArgs : identifier""" |
| 794 p[0] = self.BuildNamed('ExtAttribute', p, 1) |
| 795 |
| 796 # [75] |
| 797 def p_ExtendedAttributeArgList(self, p): |
| 798 """ExtendedAttributeArgList : identifier '(' ArgumentList ')'""" |
| 799 arguments = self.BuildProduction('Arguments', p, 2, p[3]) |
| 800 p[0] = self.BuildNamed('ExtAttribute', p, 1, arguments) |
| 801 |
| 802 # [76] |
| 803 def p_ExtendedAttributeIdent(self, p): |
| 804 """ExtendedAttributeIdent : identifier '=' identifier""" |
| 805 value = self.BuildAttribute('VALUE', p[3]) |
| 806 p[0] = self.BuildNamed('ExtAttribute', p, 1, value) |
| 807 |
| 808 # [77] |
| 809 def p_ExtendedAttributeNamedArgList(self, p): |
| 810 """ExtendedAttributeNamedArgList : identifier '=' identifier '(' ArgumentLis
t ')'""" |
| 811 args = self.BuildProduction('Arguments', p, 4, p[5]) |
| 812 value = self.BuildNamed('Call', p, 3, args) |
| 813 p[0] = self.BuildNamed('ExtAttribute', p, 1, value) |
| 814 |
| 815 # |
| 816 # Parser Errors |
| 817 # |
| 818 # p_error is called whenever the parser can not find a pattern match for |
| 819 # a set of items from the current state. The p_error function defined here |
| 820 # is triggered logging an error, and parsing recovery happens as the |
| 821 # p_<type>_error functions defined above are called. This allows the parser |
| 822 # to continue so as to capture more than one error per file. |
| 823 # |
| 824 def p_error(self, t): |
| 825 filename = self.lexer.lexobj.filename |
| 826 if t: |
| 827 lineno = t.lineno |
| 828 pos = t.lexpos |
| 829 prev = self.yaccobj.symstack[-1] |
| 830 if type(prev) == lex.LexToken: |
| 831 msg = "Unexpected %s after %s." % ( |
| 832 TokenTypeName(t), TokenTypeName(prev)) |
| 833 else: |
| 834 msg = "Unexpected %s." % (t.value) |
| 835 else: |
| 836 lineno = self.last().lineno |
| 837 pos = self.last().lexpos |
| 838 msg = "Unexpected end of file after %s." % TokenTypeName(self.last()) |
| 839 self.yaccobj.restart() |
| 840 |
| 841 # Attempt to remap the error to a friendlier form |
| 842 if msg in ERROR_REMAP: |
| 843 msg = ERROR_REMAP[msg] |
| 844 |
| 845 self._last_error_msg = msg |
| 846 self._last_error_lineno = lineno |
| 847 self._last_error_pos = pos |
| 848 |
| 849 def Warn(self, node, msg): |
| 850 sys.stdout.write(node.GetLogLine(msg)) |
| 851 self.parse_warnings += 1 |
| 852 |
| 853 def last(self): |
| 854 return self.lexer.last |
| 855 |
| 856 def __init__(self, lexer, verbose=False, debug=False, mute_error=False): |
| 857 self.lexer = lexer |
| 858 self.yaccobj = yacc.yacc(module=self, tabmodule=None, debug=False, |
| 859 optimize=0, write_tables=0) |
| 860 self.parse_debug = debug |
| 861 self.verbose = verbose |
| 862 self.mute_error = mute_error |
| 863 self._parse_errors = 0 |
| 864 self._parse_warnings = 0 |
| 865 self._last_error_msg = None |
| 866 self._last_error_lineno = 0 |
| 867 self._last_error_pos = 0 |
| 868 |
| 869 |
| 870 # |
| 871 # BuildProduction |
| 872 # |
| 873 # Production is the set of items sent to a grammar rule resulting in a new |
| 874 # item being returned. |
| 875 # |
| 876 # p - Is the Yacc production object containing the stack of items |
| 877 # index - Index into the production of the name for the item being produced. |
| 878 # cls - The type of item being producted |
| 879 # childlist - The children of the new item |
| 880 def BuildProduction(self, cls, p, index, childlist=None): |
| 881 try: |
| 882 if not childlist: |
| 883 childlist = [] |
| 884 |
| 885 filename = self.lexer.lexobj.filename |
| 886 lineno = p.lineno(index) |
| 887 pos = p.lexpos(index) |
| 888 out = IDLNode(cls, filename, lineno, pos, childlist) |
| 889 return out |
| 890 except: |
| 891 print 'Exception while parsing:' |
| 892 for num, item in enumerate(p): |
| 893 print ' [%d] %s' % (num, ExpandProduction(item)) |
| 894 if self.last(): |
| 895 print 'Last token: %s' % str(self.last()) |
| 896 raise |
| 897 |
| 898 def BuildNamed(self, cls, p, index, childlist=None): |
| 899 childlist = ListFromConcat(childlist) |
| 900 childlist.append(self.BuildAttribute('NAME', p[index])) |
| 901 return self.BuildProduction(cls, p, index, childlist) |
| 902 |
| 903 def BuildComment(self, cls, p, index): |
| 904 name = p[index] |
| 905 |
| 906 # Remove comment markers |
| 907 lines = [] |
| 908 if name[:2] == '//': |
| 909 # For C++ style, remove any leading whitespace and the '//' marker from |
| 910 # each line. |
| 911 form = 'cc' |
| 912 for line in name.split('\n'): |
| 913 start = line.find('//') |
| 914 lines.append(line[start+2:]) |
| 915 else: |
| 916 # For C style, remove ending '*/'' |
| 917 form = 'c' |
| 918 for line in name[:-2].split('\n'): |
| 919 # Remove characters until start marker for this line '*' if found |
| 920 # otherwise it should be blank. |
| 921 offs = line.find('*') |
| 922 if offs >= 0: |
| 923 line = line[offs + 1:].rstrip() |
| 924 else: |
| 925 line = '' |
| 926 lines.append(line) |
| 927 name = '\n'.join(lines) |
| 928 childlist = [self.BuildAttribute('NAME', name), |
| 929 self.BuildAttribute('FORM', form)] |
| 930 return self.BuildProduction(cls, p, index, childlist) |
| 931 |
| 932 # |
| 933 # BuildError |
| 934 # |
| 935 # Build and Errror node as part of the recovery process. |
| 936 # |
| 937 # |
| 938 def BuildError(self, p, prod): |
| 939 self._parse_errors += 1 |
| 940 name = self.BuildAttribute('NAME', self._last_error_msg) |
| 941 line = self.BuildAttribute('LINE', self._last_error_lineno) |
| 942 pos = self.BuildAttribute('POS', self._last_error_pos) |
| 943 prod = self.BuildAttribute('PROD', prod) |
| 944 |
| 945 node = self.BuildProduction('Error', p, 1, |
| 946 ListFromConcat(name, line, pos, prod)) |
| 947 if not self.mute_error: |
| 948 sys.stderr.write(node.GetLogLine('error: ' + self._last_error_msg)) |
| 949 return node |
| 950 |
| 951 # |
| 952 # BuildAttribute |
| 953 # |
| 954 # An ExtendedAttribute is a special production that results in a property |
| 955 # which is applied to the adjacent item. Attributes have no children and |
| 956 # instead represent key/value pairs. |
| 957 # |
| 958 def BuildAttribute(self, key, val): |
| 959 return IDLAttribute(key, val) |
| 960 |
| 961 def BuildFalse(self, key): |
| 962 return IDLAttribute(key, Boolean(False)) |
| 963 |
| 964 def BuildTrue(self, key): |
| 965 return IDLAttribute(key, Boolean(True)) |
| 966 |
| 967 def GetErrors(self): |
| 968 return self._parse_errors + self.lexer._lex_errors |
| 969 |
| 970 # |
| 971 # ParseData |
| 972 # |
| 973 # Attempts to parse the current data loaded in the lexer. |
| 974 # |
| 975 def ParseText(self, filename, data): |
| 976 self._parse_errors = 0 |
| 977 self._parse_warnings = 0 |
| 978 self._last_error_msg = None |
| 979 self._last_error_lineno = 0 |
| 980 self._last_error_pos = 0 |
| 981 |
| 982 try: |
| 983 self.lexer.Tokenize(data, filename) |
| 984 nodes = self.yaccobj.parse(lexer=self.lexer) |
| 985 name = self.BuildAttribute('NAME', filename) |
| 986 return IDLNode('File', filename, 0, 0, nodes + [name]) |
| 987 |
| 988 except lex.LexError as lexError: |
| 989 sys.stderr.write('Error in token: %s\n' % str(lexError)) |
| 990 return None |
| 991 |
| 992 |
| 993 |
| 994 def ParseFile(parser, filename): |
| 995 """Parse a file and return a File type of node.""" |
| 996 with open(filename) as fileobject: |
| 997 try: |
| 998 out = parser.ParseText(filename, fileobject.read()) |
| 999 out.SetProperty('DATETIME', time.ctime(os.path.getmtime(filename))) |
| 1000 out.SetProperty('ERRORS', parser.GetErrors()) |
| 1001 return out |
| 1002 |
| 1003 except Exception as e: |
| 1004 sys.stderr.write('%s(%d) : Internal parsing error - %s.' % ( |
| 1005 filename, parser.last().lineno, str(e))) |
| 1006 |
| 1007 |
| 1008 def main(argv): |
| 1009 nodes = [] |
| 1010 parser = IDLParser(IDLLexer()) |
| 1011 errors = 0 |
| 1012 for filename in argv: |
| 1013 filenode = ParseFile(parser, filename) |
| 1014 errors += filenode.GetProperty('ERRORS') |
| 1015 nodes.append(filenode) |
| 1016 |
| 1017 ast = IDLNode('AST', '__AST__', 0, 0, nodes) |
| 1018 |
| 1019 print '\n'.join(ast.Tree(accept_props=['PROD'])) |
| 1020 if errors: |
| 1021 print '\nFound %d errors.\n' % errors |
| 1022 |
| 1023 |
| 1024 return errors |
| 1025 |
| 1026 |
| 1027 if __name__ == '__main__': |
| 1028 sys.exit(main(sys.argv[1:])) |
OLD | NEW |