Home | History | Annotate | Download | only in idl_parser
      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