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 32import os.path 33import sys 34import time 35 36from idl_lexer import IDLLexer 37from 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# 43try: 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 48except 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# 61ERROR_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 73def Boolean(val): 74 """Convert to strict boolean type.""" 75 if val: 76 return True 77 return False 78 79 80def 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 93def 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# 108def 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://www.w3.org/TR/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 157class 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://www.w3.org/TR/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] Add INLINE definition 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] Error recovery for PARTIAL 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 Enums 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 | AttributeOrOperation""" 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 EnumValues""" 357 enum = self.BuildNamed('EnumItem', p, 2, p[1]) 358 p[0] = ListFromConcat(enum, p[3]) 359 360 # [22] 361 def p_EnumValues(self, p): 362 """EnumValues : ',' ExtendedAttributeList string EnumValues 363 |""" 364 if len(p) > 1: 365 enum = self.BuildNamed('EnumItem', p, 3, p[2]) 366 p[0] = ListFromConcat(enum, p[4]) 367 368 # [23] 369 def p_CallbackRest(self, p): 370 """CallbackRest : identifier '=' ReturnType '(' ArgumentList ')' ';'""" 371 arguments = self.BuildProduction('Arguments', p, 4, p[5]) 372 p[0] = self.BuildNamed('Callback', p, 1, ListFromConcat(p[3], arguments)) 373 374 # [24] 375 def p_Typedef(self, p): 376 """Typedef : TYPEDEF ExtendedAttributeListNoComments Type identifier ';'""" 377 p[0] = self.BuildNamed('Typedef', p, 4, ListFromConcat(p[2], p[3])) 378 379 # [24.1] Error recovery for Typedefs 380 def p_TypedefError(self, p): 381 """Typedef : TYPEDEF error ';'""" 382 p[0] = self.BuildError(p, 'Typedef') 383 384 # [25] 385 def p_ImplementsStatement(self, p): 386 """ImplementsStatement : identifier IMPLEMENTS identifier ';'""" 387 name = self.BuildAttribute('REFERENCE', p[3]) 388 p[0] = self.BuildNamed('Implements', p, 1, name) 389 390 # [26] 391 def p_Const(self, p): 392 """Const : CONST ConstType identifier '=' ConstValue ';'""" 393 value = self.BuildProduction('Value', p, 5, p[5]) 394 p[0] = self.BuildNamed('Const', p, 3, ListFromConcat(p[2], value)) 395 396 # [27] 397 def p_ConstValue(self, p): 398 """ConstValue : BooleanLiteral 399 | FloatLiteral 400 | integer 401 | null""" 402 if type(p[1]) == str: 403 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'integer'), 404 self.BuildAttribute('NAME', p[1])) 405 else: 406 p[0] = p[1] 407 408 # [27.1] Add definition for NULL 409 def p_null(self, p): 410 """null : NULL""" 411 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'NULL'), 412 self.BuildAttribute('NAME', 'NULL')) 413 414 # [28] 415 def p_BooleanLiteral(self, p): 416 """BooleanLiteral : TRUE 417 | FALSE""" 418 value = self.BuildAttribute('VALUE', Boolean(p[1] == 'true')) 419 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'boolean'), value) 420 421 # [29] 422 def p_FloatLiteral(self, p): 423 """FloatLiteral : float 424 | '-' INFINITY 425 | INFINITY 426 | NAN """ 427 if len(p) > 2: 428 val = '-Infinity' 429 else: 430 val = p[1] 431 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'float'), 432 self.BuildAttribute('VALUE', val)) 433 434 # [30] 435 def p_AttributeOrOperation(self, p): 436 """AttributeOrOperation : STRINGIFIER StringifierAttributeOrOperation 437 | Attribute 438 | Operation""" 439 if len(p) > 2: 440 p[0] = p[2] 441 else: 442 p[0] = p[1] 443 444 # [31] 445 def p_StringifierAttributeOrOperation(self, p): 446 """StringifierAttributeOrOperation : Attribute 447 | OperationRest 448 | ';'""" 449 if p[1] == ';': 450 p[0] = self.BuildAttribute('STRINGIFIER', Boolean(True)) 451 else: 452 p[0] = ListFromConcat(self.BuildAttribute('STRINGIFIER', p[1]), p[1]) 453 454 # [32] 455 def p_Attribute(self, p): 456 """Attribute : Inherit ReadOnly ATTRIBUTE Type identifier ';'""" 457 p[0] = self.BuildNamed('Attribute', p, 5, 458 ListFromConcat(p[1], p[2], p[4])) 459 460 # [33] 461 def p_Inherit(self, p): 462 """Inherit : INHERIT 463 |""" 464 if len(p) > 1: 465 p[0] = self.BuildTrue('INHERIT') 466 467 # [34] 468 def p_ReadOnly(self, p): 469 """ReadOnly : READONLY 470 |""" 471 if len(p) > 1: 472 p[0] = self.BuildTrue('READONLY') 473 474 # [35] 475 def p_Operation(self, p): 476 """Operation : Qualifiers OperationRest""" 477 p[2].AddChildren(p[1]) 478 p[0] = p[2] 479 480 # [36] 481 def p_Qualifiers(self, p): 482 """Qualifiers : STATIC 483 | Specials""" 484 if p[1] == 'static': 485 p[0] = self.BuildTrue('STATIC') 486 else: 487 p[0] = p[1] 488 489 # [37] 490 def p_Specials(self, p): 491 """Specials : Special Specials 492 | """ 493 if len(p) > 1: 494 p[0] = ListFromConcat(p[1], p[2]) 495 496 # [38] 497 def p_Special(self, p): 498 """Special : GETTER 499 | SETTER 500 | CREATOR 501 | DELETER 502 | LEGACYCALLER""" 503 p[0] = self.BuildTrue(p[1].upper()) 504 505 506 # [39] 507 def p_OperationRest(self, p): 508 """OperationRest : ReturnType OptionalIdentifier '(' ArgumentList ')' ';'""" 509 arguments = self.BuildProduction('Arguments', p, 3, p[4]) 510 p[0] = self.BuildNamed('Operation', p, 2, ListFromConcat(p[1], arguments)) 511 512 # [40] 513 def p_OptionalIdentifier(self, p): 514 """OptionalIdentifier : identifier 515 |""" 516 if len(p) > 1: 517 p[0] = p[1] 518 else: 519 p[0] = '_unnamed_' 520 521 # [41] 522 def p_ArgumentList(self, p): 523 """ArgumentList : Argument Arguments 524 |""" 525 if len(p) > 1: 526 p[0] = ListFromConcat(p[1], p[2]) 527 528 # [41.1] ArgumentList error recovery 529 def p_ArgumentListError(self, p): 530 """ArgumentList : error """ 531 p[0] = self.BuildError(p, 'ArgumentList') 532 533 # [42] 534 def p_Arguments(self, p): 535 """Arguments : ',' Argument Arguments 536 |""" 537 if len(p) > 1: 538 p[0] = ListFromConcat(p[2], p[3]) 539 540 # [43] 541 def p_Argument(self, p): 542 """Argument : ExtendedAttributeList OptionalOrRequiredArgument""" 543 p[2].AddChildren(p[1]) 544 p[0] = p[2] 545 546 547 # [44] 548 def p_OptionalOrRequiredArgument(self, p): 549 """OptionalOrRequiredArgument : OPTIONAL Type ArgumentName Default 550 | Type Ellipsis ArgumentName""" 551 if len(p) > 4: 552 arg = self.BuildNamed('Argument', p, 3, ListFromConcat(p[2], p[4])) 553 arg.AddChildren(self.BuildTrue('OPTIONAL')) 554 else: 555 arg = self.BuildNamed('Argument', p, 3, ListFromConcat(p[1], p[2])) 556 p[0] = arg 557 558 # [45] 559 def p_ArgumentName(self, p): 560 """ArgumentName : ArgumentNameKeyword 561 | identifier""" 562 p[0] = p[1] 563 564 # [46] 565 def p_Ellipsis(self, p): 566 """Ellipsis : ELLIPSIS 567 |""" 568 if len(p) > 1: 569 p[0] = self.BuildNamed('Argument', p, 1) 570 p[0].AddChildren(self.BuildTrue('ELLIPSIS')) 571 572 # [47] 573 def p_ExceptionMember(self, p): 574 """ExceptionMember : Const 575 | ExceptionField""" 576 p[0] = p[1] 577 578 # [48] 579 def p_ExceptionField(self, p): 580 """ExceptionField : Type identifier ';'""" 581 p[0] = self.BuildNamed('ExceptionField', p, 2, p[1]) 582 583 # [48.1] Error recovery for ExceptionMembers 584 def p_ExceptionFieldError(self, p): 585 """ExceptionField : error""" 586 p[0] = self.BuildError(p, 'ExceptionField') 587 588 # [49] No comment version for mid statement attributes. 589 def p_ExtendedAttributeListNoComments(self, p): 590 """ExtendedAttributeListNoComments : '[' ExtendedAttribute ExtendedAttributes ']' 591 | """ 592 if len(p) > 2: 593 items = ListFromConcat(p[2], p[3]) 594 p[0] = self.BuildProduction('ExtAttributes', p, 1, items) 595 596 # [49.1] Add optional comment field for start of statements. 597 def p_ExtendedAttributeList(self, p): 598 """ExtendedAttributeList : Comments '[' ExtendedAttribute ExtendedAttributes ']' 599 | Comments """ 600 if len(p) > 2: 601 items = ListFromConcat(p[3], p[4]) 602 attribs = self.BuildProduction('ExtAttributes', p, 2, items) 603 p[0] = ListFromConcat(p[1], attribs) 604 else: 605 p[0] = p[1] 606 607 # [50] 608 def p_ExtendedAttributes(self, p): 609 """ExtendedAttributes : ',' ExtendedAttribute ExtendedAttributes 610 |""" 611 if len(p) > 1: 612 p[0] = ListFromConcat(p[2], p[3]) 613 614 # We only support: 615 # [ identifier ] 616 # [ identifier = identifier ] 617 # [ identifier ( ArgumentList )] 618 # [ identifier = identifier ( ArgumentList )] 619 # [51] map directly to 74-77 620 # [52-54, 56] are unsupported 621 def p_ExtendedAttribute(self, p): 622 """ExtendedAttribute : ExtendedAttributeNoArgs 623 | ExtendedAttributeArgList 624 | ExtendedAttributeIdent 625 | ExtendedAttributeNamedArgList""" 626 p[0] = p[1] 627 628 # [55] 629 def p_ArgumentNameKeyword(self, p): 630 """ArgumentNameKeyword : ATTRIBUTE 631 | CALLBACK 632 | CONST 633 | CREATOR 634 | DELETER 635 | DICTIONARY 636 | ENUM 637 | EXCEPTION 638 | GETTER 639 | IMPLEMENTS 640 | INHERIT 641 | LEGACYCALLER 642 | PARTIAL 643 | SETTER 644 | STATIC 645 | STRINGIFIER 646 | TYPEDEF 647 | UNRESTRICTED""" 648 p[0] = p[1] 649 650 # [57] 651 def p_Type(self, p): 652 """Type : SingleType 653 | UnionType TypeSuffix""" 654 if len(p) == 2: 655 p[0] = self.BuildProduction('Type', p, 1, p[1]) 656 else: 657 p[0] = self.BuildProduction('Type', p, 1, ListFromConcat(p[1], p[2])) 658 659 # [58] 660 def p_SingleType(self, p): 661 """SingleType : NonAnyType 662 | ANY TypeSuffixStartingWithArray""" 663 if len(p) == 2: 664 p[0] = p[1] 665 else: 666 p[0] = ListFromConcat(self.BuildProduction('Any', p, 1), p[2]) 667 668 # [59] 669 def p_UnionType(self, p): 670 """UnionType : '(' UnionMemberType OR UnionMemberType UnionMemberTypes ')'""" 671 672 # [60] 673 def p_UnionMemberType(self, p): 674 """UnionMemberType : NonAnyType 675 | UnionType TypeSuffix 676 | ANY '[' ']' TypeSuffix""" 677 # [61] 678 def p_UnionMemberTypes(self, p): 679 """UnionMemberTypes : OR UnionMemberType UnionMemberTypes 680 |""" 681 682 # [62] Moved DATE, DOMSTRING, OBJECT to PrimitiveType 683 def p_NonAnyType(self, p): 684 """NonAnyType : PrimitiveType TypeSuffix 685 | identifier TypeSuffix 686 | SEQUENCE '<' Type '>' Null""" 687 if len(p) == 3: 688 if type(p[1]) == str: 689 typeref = self.BuildNamed('Typeref', p, 1) 690 else: 691 typeref = p[1] 692 p[0] = ListFromConcat(typeref, p[2]) 693 694 if len(p) == 6: 695 p[0] = self.BuildProduction('Sequence', p, 1, ListFromConcat(p[3], p[5])) 696 697 698 # [63] 699 def p_ConstType(self, p): 700 """ConstType : PrimitiveType Null 701 | identifier Null""" 702 if type(p[1]) == str: 703 p[0] = self.BuildNamed('Typeref', p, 1, p[2]) 704 else: 705 p[1].AddChildren(p[2]) 706 p[0] = p[1] 707 708 709 # [64] 710 def p_PrimitiveType(self, p): 711 """PrimitiveType : UnsignedIntegerType 712 | UnrestrictedFloatType 713 | BOOLEAN 714 | BYTE 715 | OCTET 716 | DOMSTRING 717 | DATE 718 | OBJECT""" 719 if type(p[1]) == str: 720 p[0] = self.BuildNamed('PrimitiveType', p, 1) 721 else: 722 p[0] = p[1] 723 724 725 # [65] 726 def p_UnrestrictedFloatType(self, p): 727 """UnrestrictedFloatType : UNRESTRICTED FloatType 728 | FloatType""" 729 if len(p) == 2: 730 typeref = self.BuildNamed('PrimitiveType', p, 1) 731 else: 732 typeref = self.BuildNamed('PrimitiveType', p, 2) 733 typeref.AddChildren(self.BuildTrue('UNRESTRICTED')) 734 p[0] = typeref 735 736 737 # [66] 738 def p_FloatType(self, p): 739 """FloatType : FLOAT 740 | DOUBLE""" 741 p[0] = p[1] 742 743 # [67] 744 def p_UnsignedIntegerType(self, p): 745 """UnsignedIntegerType : UNSIGNED IntegerType 746 | IntegerType""" 747 if len(p) == 2: 748 p[0] = p[1] 749 else: 750 p[0] = 'unsigned ' + p[2] 751 752 # [68] 753 def p_IntegerType(self, p): 754 """IntegerType : SHORT 755 | LONG OptionalLong""" 756 if len(p) == 2: 757 p[0] = p[1] 758 else: 759 p[0] = p[1] + p[2] 760 761 # [69] 762 def p_OptionalLong(self, p): 763 """OptionalLong : LONG 764 | """ 765 if len(p) > 1: 766 p[0] = ' ' + p[1] 767 else: 768 p[0] = '' 769 770 771 # [70] Add support for sized array 772 def p_TypeSuffix(self, p): 773 """TypeSuffix : '[' integer ']' TypeSuffix 774 | '[' ']' TypeSuffix 775 | '?' TypeSuffixStartingWithArray 776 | """ 777 if len(p) == 5: 778 p[0] = self.BuildNamed('Array', p, 2, p[4]) 779 780 if len(p) == 4: 781 p[0] = self.BuildProduction('Array', p, 1, p[3]) 782 783 if len(p) == 3: 784 p[0] = ListFromConcat(self.BuildTrue('NULLABLE'), p[2]) 785 786 787 # [71] 788 def p_TypeSuffixStartingWithArray(self, p): 789 """TypeSuffixStartingWithArray : '[' ']' TypeSuffix 790 | """ 791 if len(p) > 1: 792 p[0] = self.BuildProduction('Array', p, 0, p[3]) 793 794 # [72] 795 def p_Null(self, p): 796 """Null : '?' 797 |""" 798 if len(p) > 1: 799 p[0] = self.BuildTrue('NULLABLE') 800 801 # [73] 802 def p_ReturnType(self, p): 803 """ReturnType : Type 804 | VOID""" 805 if p[1] == 'void': 806 p[0] = self.BuildProduction('Type', p, 1) 807 p[0].AddChildren(self.BuildNamed('PrimitiveType', p, 1)) 808 else: 809 p[0] = p[1] 810 811 # [74] 812 def p_ExtendedAttributeNoArgs(self, p): 813 """ExtendedAttributeNoArgs : identifier""" 814 p[0] = self.BuildNamed('ExtAttribute', p, 1) 815 816 # [75] 817 def p_ExtendedAttributeArgList(self, p): 818 """ExtendedAttributeArgList : identifier '(' ArgumentList ')'""" 819 arguments = self.BuildProduction('Arguments', p, 2, p[3]) 820 p[0] = self.BuildNamed('ExtAttribute', p, 1, arguments) 821 822 # [76] 823 def p_ExtendedAttributeIdent(self, p): 824 """ExtendedAttributeIdent : identifier '=' identifier""" 825 value = self.BuildAttribute('VALUE', p[3]) 826 p[0] = self.BuildNamed('ExtAttribute', p, 1, value) 827 828 # [77] 829 def p_ExtendedAttributeNamedArgList(self, p): 830 """ExtendedAttributeNamedArgList : identifier '=' identifier '(' ArgumentList ')'""" 831 args = self.BuildProduction('Arguments', p, 4, p[5]) 832 value = self.BuildNamed('Call', p, 3, args) 833 p[0] = self.BuildNamed('ExtAttribute', p, 1, value) 834 835# 836# Parser Errors 837# 838# p_error is called whenever the parser can not find a pattern match for 839# a set of items from the current state. The p_error function defined here 840# is triggered logging an error, and parsing recovery happens as the 841# p_<type>_error functions defined above are called. This allows the parser 842# to continue so as to capture more than one error per file. 843# 844 def p_error(self, t): 845 if t: 846 lineno = t.lineno 847 pos = t.lexpos 848 prev = self.yaccobj.symstack[-1] 849 if type(prev) == lex.LexToken: 850 msg = "Unexpected %s after %s." % ( 851 TokenTypeName(t), TokenTypeName(prev)) 852 else: 853 msg = "Unexpected %s." % (t.value) 854 else: 855 last = self.LastToken() 856 lineno = last.lineno 857 pos = last.lexpos 858 msg = "Unexpected end of file after %s." % TokenTypeName(last) 859 self.yaccobj.restart() 860 861 # Attempt to remap the error to a friendlier form 862 if msg in ERROR_REMAP: 863 msg = ERROR_REMAP[msg] 864 865 self._last_error_msg = msg 866 self._last_error_lineno = lineno 867 self._last_error_pos = pos 868 869 def Warn(self, node, msg): 870 sys.stdout.write(node.GetLogLine(msg)) 871 self.parse_warnings += 1 872 873 def LastToken(self): 874 return self.lexer.last 875 876 def __init__(self, lexer, verbose=False, debug=False, mute_error=False): 877 self.lexer = lexer 878 self.tokens = lexer.KnownTokens() 879 self.yaccobj = yacc.yacc(module=self, tabmodule=None, debug=debug, 880 optimize=0, write_tables=0) 881 self.parse_debug = debug 882 self.verbose = verbose 883 self.mute_error = mute_error 884 self._parse_errors = 0 885 self._parse_warnings = 0 886 self._last_error_msg = None 887 self._last_error_lineno = 0 888 self._last_error_pos = 0 889 890 891# 892# BuildProduction 893# 894# Production is the set of items sent to a grammar rule resulting in a new 895# item being returned. 896# 897# p - Is the Yacc production object containing the stack of items 898# index - Index into the production of the name for the item being produced. 899# cls - The type of item being producted 900# childlist - The children of the new item 901 def BuildProduction(self, cls, p, index, childlist=None): 902 try: 903 if not childlist: 904 childlist = [] 905 906 filename = self.lexer.Lexer().filename 907 lineno = p.lineno(index) 908 pos = p.lexpos(index) 909 out = IDLNode(cls, filename, lineno, pos, childlist) 910 return out 911 except: 912 print 'Exception while parsing:' 913 for num, item in enumerate(p): 914 print ' [%d] %s' % (num, ExpandProduction(item)) 915 if self.LastToken(): 916 print 'Last token: %s' % str(self.LastToken()) 917 raise 918 919 def BuildNamed(self, cls, p, index, childlist=None): 920 childlist = ListFromConcat(childlist) 921 childlist.append(self.BuildAttribute('NAME', p[index])) 922 return self.BuildProduction(cls, p, index, childlist) 923 924 def BuildComment(self, cls, p, index): 925 name = p[index] 926 927 # Remove comment markers 928 lines = [] 929 if name[:2] == '//': 930 # For C++ style, remove any leading whitespace and the '//' marker from 931 # each line. 932 form = 'cc' 933 for line in name.split('\n'): 934 start = line.find('//') 935 lines.append(line[start+2:]) 936 else: 937 # For C style, remove ending '*/'' 938 form = 'c' 939 for line in name[:-2].split('\n'): 940 # Remove characters until start marker for this line '*' if found 941 # otherwise it should be blank. 942 offs = line.find('*') 943 if offs >= 0: 944 line = line[offs + 1:].rstrip() 945 else: 946 line = '' 947 lines.append(line) 948 name = '\n'.join(lines) 949 childlist = [self.BuildAttribute('NAME', name), 950 self.BuildAttribute('FORM', form)] 951 return self.BuildProduction(cls, p, index, childlist) 952 953# 954# BuildError 955# 956# Build and Errror node as part of the recovery process. 957# 958# 959 def BuildError(self, p, prod): 960 self._parse_errors += 1 961 name = self.BuildAttribute('NAME', self._last_error_msg) 962 line = self.BuildAttribute('LINE', self._last_error_lineno) 963 pos = self.BuildAttribute('POS', self._last_error_pos) 964 prod = self.BuildAttribute('PROD', prod) 965 966 node = self.BuildProduction('Error', p, 1, 967 ListFromConcat(name, line, pos, prod)) 968 if not self.mute_error: 969 node.Error(self._last_error_msg) 970 971 return node 972 973# 974# BuildAttribute 975# 976# An ExtendedAttribute is a special production that results in a property 977# which is applied to the adjacent item. Attributes have no children and 978# instead represent key/value pairs. 979# 980 def BuildAttribute(self, key, val): 981 return IDLAttribute(key, val) 982 983 def BuildFalse(self, key): 984 return IDLAttribute(key, Boolean(False)) 985 986 def BuildTrue(self, key): 987 return IDLAttribute(key, Boolean(True)) 988 989 def GetErrors(self): 990 # Access lexer errors, despite being private 991 # pylint: disable=W0212 992 return self._parse_errors + self.lexer._lex_errors 993 994# 995# ParseData 996# 997# Attempts to parse the current data loaded in the lexer. 998# 999 def ParseText(self, filename, data): 1000 self._parse_errors = 0 1001 self._parse_warnings = 0 1002 self._last_error_msg = None 1003 self._last_error_lineno = 0 1004 self._last_error_pos = 0 1005 1006 try: 1007 self.lexer.Tokenize(data, filename) 1008 nodes = self.yaccobj.parse(lexer=self.lexer) or [] 1009 name = self.BuildAttribute('NAME', filename) 1010 return IDLNode('File', filename, 0, 0, nodes + [name]) 1011 1012 except lex.LexError as lexError: 1013 sys.stderr.write('Error in token: %s\n' % str(lexError)) 1014 return None 1015 1016 1017 1018def ParseFile(parser, filename): 1019 """Parse a file and return a File type of node.""" 1020 with open(filename) as fileobject: 1021 try: 1022 out = parser.ParseText(filename, fileobject.read()) 1023 out.SetProperty('DATETIME', time.ctime(os.path.getmtime(filename))) 1024 out.SetProperty('ERRORS', parser.GetErrors()) 1025 return out 1026 1027 except Exception as e: 1028 last = parser.LastToken() 1029 sys.stderr.write('%s(%d) : Internal parsing error\n\t%s.\n' % ( 1030 filename, last.lineno, str(e))) 1031 1032 1033def main(argv): 1034 nodes = [] 1035 parser = IDLParser(IDLLexer()) 1036 errors = 0 1037 for filename in argv: 1038 filenode = ParseFile(parser, filename) 1039 if (filenode): 1040 errors += filenode.GetProperty('ERRORS') 1041 nodes.append(filenode) 1042 1043 ast = IDLNode('AST', '__AST__', 0, 0, nodes) 1044 1045 print '\n'.join(ast.Tree(accept_props=['PROD'])) 1046 if errors: 1047 print '\nFound %d errors.\n' % errors 1048 1049 return errors 1050 1051 1052if __name__ == '__main__': 1053 sys.exit(main(sys.argv[1:])) 1054