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