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