| OLD | NEW | 
|---|
|  | (Empty) | 
| 1 # Copyright (C) 2013 Google Inc. All rights reserved. |  | 
| 2 # |  | 
| 3 # Redistribution and use in source and binary forms, with or without |  | 
| 4 # modification, are permitted provided that the following conditions are |  | 
| 5 # met: |  | 
| 6 # |  | 
| 7 #     * Redistributions of source code must retain the above copyright |  | 
| 8 # notice, this list of conditions and the following disclaimer. |  | 
| 9 #     * Redistributions in binary form must reproduce the above |  | 
| 10 # copyright notice, this list of conditions and the following disclaimer |  | 
| 11 # in the documentation and/or other materials provided with the |  | 
| 12 # distribution. |  | 
| 13 #     * Neither the name of Google Inc. nor the names of its |  | 
| 14 # contributors may be used to endorse or promote products derived from |  | 
| 15 # this software without specific prior written permission. |  | 
| 16 # |  | 
| 17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |  | 
| 18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |  | 
| 19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |  | 
| 20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |  | 
| 21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |  | 
| 22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |  | 
| 23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |  | 
| 24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |  | 
| 25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |  | 
| 26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |  | 
| 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |  | 
| 28 |  | 
| 29 """Parser for Blink IDL. |  | 
| 30 |  | 
| 31 The parser uses the PLY (Python Lex-Yacc) library to build a set of parsing |  | 
| 32 rules which understand the Blink dialect of Web IDL. |  | 
| 33 It derives from a standard Web IDL parser, overriding rules where Blink IDL |  | 
| 34 differs syntactically or semantically from the base parser, or where the base |  | 
| 35 parser diverges from the Web IDL standard. |  | 
| 36 |  | 
| 37 Web IDL: |  | 
| 38     http://www.w3.org/TR/WebIDL/ |  | 
| 39 Web IDL Grammar: |  | 
| 40     http://www.w3.org/TR/WebIDL/#idl-grammar |  | 
| 41 PLY: |  | 
| 42     http://www.dabeaz.com/ply/ |  | 
| 43 """ |  | 
| 44 |  | 
| 45 # Disable check for line length and Member as Function due to how grammar rules |  | 
| 46 # are defined with PLY |  | 
| 47 # |  | 
| 48 # pylint: disable=R0201 |  | 
| 49 # pylint: disable=C0301 |  | 
| 50 # |  | 
| 51 # Disable attribute validation, as lint can't import parent class to check |  | 
| 52 # pylint: disable=E1101 |  | 
| 53 |  | 
| 54 import os.path |  | 
| 55 import sys |  | 
| 56 |  | 
| 57 # PLY is in Chromium src/third_party/ply |  | 
| 58 module_path, module_name = os.path.split(__file__) |  | 
| 59 third_party = os.path.join(module_path, os.pardir, os.pardir, os.pardir, os.pard
     ir) |  | 
| 60 # Insert at front to override system libraries, and after path[0] == script dir |  | 
| 61 sys.path.insert(1, third_party) |  | 
| 62 from ply import yacc |  | 
| 63 |  | 
| 64 # Base parser is in Chromium src/tools/idl_parser |  | 
| 65 tools_dir = os.path.join(module_path, os.pardir, os.pardir, os.pardir, os.pardir
     , os.pardir, 'tools') |  | 
| 66 sys.path.append(tools_dir) |  | 
| 67 from idl_parser.idl_parser import IDLParser, ListFromConcat |  | 
| 68 from idl_parser.idl_parser import ParseFile as parse_file |  | 
| 69 |  | 
| 70 from blink_idl_lexer import BlinkIDLLexer |  | 
| 71 |  | 
| 72 |  | 
| 73 # Explicitly set starting symbol to rule defined only in base parser. |  | 
| 74 # BEWARE that the starting symbol should NOT be defined in both the base parser |  | 
| 75 # and the derived one, as otherwise which is used depends on which line number |  | 
| 76 # is lower, which is fragile. Instead, either use one in base parser or |  | 
| 77 # create a new symbol, so that this is unambiguous. |  | 
| 78 # FIXME: unfortunately, this doesn't work in PLY 3.4, so need to duplicate the |  | 
| 79 # rule below. |  | 
| 80 STARTING_SYMBOL = 'Definitions' |  | 
| 81 |  | 
| 82 # We ignore comments (and hence don't need 'Top') but base parser preserves them |  | 
| 83 # FIXME: Upstream: comments should be removed in base parser |  | 
| 84 REMOVED_RULES = ['Top',  # [0] |  | 
| 85                  'Comments',  # [0.1] |  | 
| 86                  'CommentsRest',  # [0.2] |  | 
| 87                 ] |  | 
| 88 |  | 
| 89 |  | 
| 90 class BlinkIDLParser(IDLParser): |  | 
| 91     # [1] |  | 
| 92     # FIXME: Need to duplicate rule for starting symbol here, with line number |  | 
| 93     # *lower* than in the base parser (idl_parser.py). |  | 
| 94     # This is a bug in PLY: it determines starting symbol by lowest line number. |  | 
| 95     # This can be overridden by the 'start' parameter, but as of PLY 3.4 this |  | 
| 96     # doesn't work correctly. |  | 
| 97     def p_Definitions(self, p): |  | 
| 98         """Definitions : ExtendedAttributeList Definition Definitions |  | 
| 99                        | """ |  | 
| 100         if len(p) > 1: |  | 
| 101             p[2].AddChildren(p[1]) |  | 
| 102             p[0] = ListFromConcat(p[2], p[3]) |  | 
| 103 |  | 
| 104     # Below are grammar rules used by yacc, given by functions named p_<RULE>. |  | 
| 105     # * The docstring is the production rule in BNF (grammar). |  | 
| 106     # * The body is the yacc action (semantics). |  | 
| 107     # |  | 
| 108     # The PLY framework builds the actual low-level parser by introspecting this |  | 
| 109     # parser object, selecting all attributes named p_<RULE> as grammar rules. |  | 
| 110     # It extracts the docstrings and uses them as the production rules, building |  | 
| 111     # the table of a LALR parser, and uses the body of the functions as actions. |  | 
| 112     # |  | 
| 113     # Reference: |  | 
| 114     # http://www.dabeaz.com/ply/ply.html#ply_nn23 |  | 
| 115     # |  | 
| 116     # Review of yacc: |  | 
| 117     # Yacc parses a token stream, internally producing a Concrete Syntax Tree |  | 
| 118     # (CST), where each node corresponds to a production rule in the grammar. |  | 
| 119     # At each node, it runs an action, which is usually "produce a node in the |  | 
| 120     # Abstract Syntax Tree (AST)" or "ignore this node" (for nodes in the CST |  | 
| 121     # that aren't included in the AST, since only needed for parsing). |  | 
| 122     # |  | 
| 123     # The rules use pseudo-variables; in PLY syntax: |  | 
| 124     # p[0] is the left side: assign return value to p[0] instead of returning, |  | 
| 125     # p[1] ... p[n] are the right side: the values can be accessed, and they |  | 
| 126     # can be modified. |  | 
| 127     # (In yacc these are $$ and $1 ... $n.) |  | 
| 128     # |  | 
| 129     # The rules can look cryptic at first, but there are a few standard |  | 
| 130     # transforms from the CST to AST. With these in mind, the actions should |  | 
| 131     # be reasonably legible. |  | 
| 132     # |  | 
| 133     # * Ignore production |  | 
| 134     #   Discard this branch. Primarily used when one alternative is empty. |  | 
| 135     # |  | 
| 136     #   Sample code: |  | 
| 137     #   if len(p) > 1: |  | 
| 138     #       p[0] = ... |  | 
| 139     #   # Note no assignment if len(p) == 1 |  | 
| 140     # |  | 
| 141     # * Eliminate singleton production |  | 
| 142     #   Discard this node in the CST, pass the next level down up the tree. |  | 
| 143     #   Used to ignore productions only necessary for parsing, but not needed |  | 
| 144     #   in the AST. |  | 
| 145     # |  | 
| 146     #   Sample code: |  | 
| 147     #   p[0] = p[1] |  | 
| 148     # |  | 
| 149     # * Build node |  | 
| 150     #   The key type of rule. In this parser, produces object of class IDLNode. |  | 
| 151     #   There are several helper functions: |  | 
| 152     #   * BuildProduction: actually builds an IDLNode, based on a production. |  | 
| 153     #   * BuildAttribute: builds an IDLAttribute, which is a temporary |  | 
| 154     #                     object to hold a name-value pair, which is then |  | 
| 155     #                     set as a Property of the IDLNode when the IDLNode |  | 
| 156     #                     is built. |  | 
| 157     #   * BuildNamed: Same as BuildProduction, and sets the 'NAME' property. |  | 
| 158     #   * BuildTrue: BuildAttribute with value True, for flags. |  | 
| 159     #   See base idl_parser.py for definitions and more examples of use. |  | 
| 160     # |  | 
| 161     #   Sample code: |  | 
| 162     #   # Build node of type NodeType, with value p[1], and children. |  | 
| 163     #   p[0] = self.BuildProduction('NodeType', p, 1, children) |  | 
| 164     # |  | 
| 165     #   # Build named node of type NodeType, with name and value p[1]. |  | 
| 166     #   # (children optional) |  | 
| 167     #   p[0] = self.BuildNamed('NodeType', p, 1) |  | 
| 168     # |  | 
| 169     #   # Make a list |  | 
| 170     #   # Used if one node has several children. |  | 
| 171     #   children = ListFromConcat(p[2], p[3]) |  | 
| 172     #   p[0] = self.BuildProduction('NodeType', p, 1, children) |  | 
| 173     # |  | 
| 174     #   # Also used to collapse the right-associative tree |  | 
| 175     #   # produced by parsing a list back into a single list. |  | 
| 176     #   """Foos : Foo Foos |  | 
| 177     #           |""" |  | 
| 178     #   if len(p) > 1: |  | 
| 179     #       p[0] = ListFromConcat(p[1], p[2]) |  | 
| 180     # |  | 
| 181     #   # Add children. |  | 
| 182     #   # Primarily used to add attributes, produced via BuildTrue. |  | 
| 183     #   # p_StaticAttribute |  | 
| 184     #   """StaticAttribute : STATIC Attribute""" |  | 
| 185     #   p[2].AddChildren(self.BuildTrue('STATIC')) |  | 
| 186     #   p[0] = p[2] |  | 
| 187     # |  | 
| 188     # Numbering scheme for the rules is: |  | 
| 189     # [1] for Web IDL spec (or additions in base parser) |  | 
| 190     #     These should all be upstreamed to the base parser. |  | 
| 191     # [b1] for Blink IDL changes (overrides Web IDL) |  | 
| 192     # [b1.1] for Blink IDL additions, auxiliary rules for [b1] |  | 
| 193     # Numbers are as per Candidate Recommendation 19 April 2012: |  | 
| 194     # http://www.w3.org/TR/2012/CR-WebIDL-20120419/ |  | 
| 195 |  | 
| 196     # [3] Override action, since we distinguish callbacks |  | 
| 197     # FIXME: Upstream |  | 
| 198     def p_CallbackOrInterface(self, p): |  | 
| 199         """CallbackOrInterface : CALLBACK CallbackRestOrInterface |  | 
| 200                                | Interface""" |  | 
| 201         if len(p) > 2: |  | 
| 202             p[2].AddChildren(self.BuildTrue('CALLBACK')) |  | 
| 203             p[0] = p[2] |  | 
| 204         else: |  | 
| 205             p[0] = p[1] |  | 
| 206 |  | 
| 207     # [b27] Add strings, more 'Literal' productions |  | 
| 208     # 'Literal's needed because integers and strings are both internally strings |  | 
| 209     def p_ConstValue(self, p): |  | 
| 210         """ConstValue : BooleanLiteral |  | 
| 211                       | FloatLiteral |  | 
| 212                       | IntegerLiteral |  | 
| 213                       | StringLiteral |  | 
| 214                       | null""" |  | 
| 215         # Standard is (no 'string', fewer 'Literal's): |  | 
| 216         # ConstValue : BooleanLiteral |  | 
| 217         #            | FloatLiteral |  | 
| 218         #            | integer |  | 
| 219         #            | NULL |  | 
| 220         p[0] = p[1] |  | 
| 221 |  | 
| 222     # [b27.1] |  | 
| 223     def p_IntegerLiteral(self, p): |  | 
| 224         """IntegerLiteral : integer""" |  | 
| 225         p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'integer'), |  | 
| 226                               self.BuildAttribute('NAME', p[1])) |  | 
| 227 |  | 
| 228     # [b27.2] |  | 
| 229     def p_StringLiteral(self, p): |  | 
| 230         """StringLiteral : string""" |  | 
| 231         p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'DOMString'), |  | 
| 232                               self.BuildAttribute('NAME', p[1])) |  | 
| 233 |  | 
| 234     # [b30] Add StaticAttribute |  | 
| 235     def p_AttributeOrOperation(self, p): |  | 
| 236         """AttributeOrOperation : STRINGIFIER StringifierAttributeOrOperation |  | 
| 237                                 | Attribute |  | 
| 238                                 | StaticAttribute |  | 
| 239                                 | Operation""" |  | 
| 240         # Standard is (no StaticAttribute): |  | 
| 241         # AttributeOrOperation : STRINGIFIER StringifierAttributeOrOperation |  | 
| 242         #                      | Attribute |  | 
| 243         #                      | Operation |  | 
| 244         if len(p) > 2: |  | 
| 245             # FIXME: Clearer to add stringifier property here, as: |  | 
| 246             # p[2].AddChildren(self.BuildTrue('STRINGIFIER')) |  | 
| 247             # Fix when actually implementing stringifiers. |  | 
| 248             p[0] = p[2] |  | 
| 249         else: |  | 
| 250             p[0] = p[1] |  | 
| 251 |  | 
| 252     # [b30.1] |  | 
| 253     def p_StaticAttribute(self, p): |  | 
| 254         """StaticAttribute : STATIC Attribute""" |  | 
| 255         p[2].AddChildren(self.BuildTrue('STATIC')) |  | 
| 256         p[0] = p[2] |  | 
| 257 |  | 
| 258     # [b47] |  | 
| 259     def p_ExceptionMember(self, p): |  | 
| 260         """ExceptionMember : Const |  | 
| 261                            | ExceptionField |  | 
| 262                            | Attribute |  | 
| 263                            | ExceptionOperation""" |  | 
| 264         # Standard is (no Attribute, no ExceptionOperation): |  | 
| 265         # ExceptionMember : Const |  | 
| 266         #                 | ExceptionField |  | 
| 267         # FIXME: In DOMException.idl, Attributes should be changed to |  | 
| 268         # ExceptionFields, and Attribute removed from this rule. |  | 
| 269         p[0] = p[1] |  | 
| 270 |  | 
| 271     # [b47.1] |  | 
| 272     def p_ExceptionOperation(self, p): |  | 
| 273         """ExceptionOperation : Type identifier '(' ')' ';'""" |  | 
| 274         # Needed to handle one case in DOMException.idl: |  | 
| 275         # // Override in a Mozilla compatible format |  | 
| 276         # [NotEnumerable] DOMString toString(); |  | 
| 277         # Limited form of Operation to prevent others from being added. |  | 
| 278         # FIXME: Should be a stringifier instead. |  | 
| 279         p[0] = self.BuildNamed('ExceptionOperation', p, 2, p[1]) |  | 
| 280 |  | 
| 281     # Extended attributes |  | 
| 282     # [b49] Override base parser: remove comment field, since comments stripped |  | 
| 283     # FIXME: Upstream |  | 
| 284     def p_ExtendedAttributeList(self, p): |  | 
| 285         """ExtendedAttributeList : '[' ExtendedAttribute ExtendedAttributes ']' |  | 
| 286                                  | '[' ']' |  | 
| 287                                  | """ |  | 
| 288         if len(p) > 3: |  | 
| 289             items = ListFromConcat(p[2], p[3]) |  | 
| 290             p[0] = self.BuildProduction('ExtAttributes', p, 1, items) |  | 
| 291 |  | 
| 292     # [b50] Allow optional trailing comma |  | 
| 293     # Blink-only, marked as WONTFIX in Web IDL spec: |  | 
| 294     # https://www.w3.org/Bugs/Public/show_bug.cgi?id=22156 |  | 
| 295     def p_ExtendedAttributes(self, p): |  | 
| 296         """ExtendedAttributes : ',' ExtendedAttribute ExtendedAttributes |  | 
| 297                               | ',' |  | 
| 298                               |""" |  | 
| 299         if len(p) > 3: |  | 
| 300             p[0] = ListFromConcat(p[2], p[3]) |  | 
| 301 |  | 
| 302     # [b51] Add ExtendedAttributeIdentAndOrIdent |  | 
| 303     def p_ExtendedAttribute(self, p): |  | 
| 304         """ExtendedAttribute : ExtendedAttributeNoArgs |  | 
| 305                              | ExtendedAttributeArgList |  | 
| 306                              | ExtendedAttributeIdent |  | 
| 307                              | ExtendedAttributeIdentList |  | 
| 308                              | ExtendedAttributeNamedArgList""" |  | 
| 309         p[0] = p[1] |  | 
| 310 |  | 
| 311     # [59] |  | 
| 312     # FIXME: Upstream UnionType |  | 
| 313     def p_UnionType(self, p): |  | 
| 314         """UnionType : '(' UnionMemberType OR UnionMemberType UnionMemberTypes '
     )'""" |  | 
| 315         members = ListFromConcat(p[2], p[4], p[5]) |  | 
| 316         p[0] = self.BuildProduction('UnionType', p, 1, members) |  | 
| 317 |  | 
| 318     # [60] |  | 
| 319     def p_UnionMemberType(self, p): |  | 
| 320         """UnionMemberType : NonAnyType |  | 
| 321                            | UnionType TypeSuffix |  | 
| 322                            | ANY '[' ']' TypeSuffix""" |  | 
| 323         if len(p) == 2: |  | 
| 324             p[0] = self.BuildProduction('Type', p, 1, p[1]) |  | 
| 325         elif len(p) == 3: |  | 
| 326             p[0] = self.BuildProduction('Type', p, 1, ListFromConcat(p[1], p[2])
     ) |  | 
| 327         else: |  | 
| 328             any_node = ListFromConcat(self.BuildProduction('Any', p, 1), p[4]) |  | 
| 329             p[0] = self.BuildProduction('Type', p, 1, any_node) |  | 
| 330 |  | 
| 331     # [61] |  | 
| 332     def p_UnionMemberTypes(self, p): |  | 
| 333         """UnionMemberTypes : OR UnionMemberType UnionMemberTypes |  | 
| 334                             |""" |  | 
| 335         if len(p) > 2: |  | 
| 336             p[0] = ListFromConcat(p[2], p[3]) |  | 
| 337 |  | 
| 338     # [70] Override base parser to remove non-standard sized array |  | 
| 339     # FIXME: Upstream |  | 
| 340     def p_TypeSuffix(self, p): |  | 
| 341         """TypeSuffix : '[' ']' TypeSuffix |  | 
| 342                       | '?' TypeSuffixStartingWithArray |  | 
| 343                       |""" |  | 
| 344         if len(p) == 4: |  | 
| 345             p[0] = self.BuildProduction('Array', p, 1, p[3]) |  | 
| 346         elif len(p) == 3: |  | 
| 347             p[0] = ListFromConcat(self.BuildTrue('NULLABLE'), p[2]) |  | 
| 348 |  | 
| 349     # [b76.1] Add support for compound Extended Attribute values (A&B and A|B) |  | 
| 350     def p_ExtendedAttributeIdentList(self, p): |  | 
| 351         """ExtendedAttributeIdentList : identifier '=' identifier '&' IdentAndLi
     st |  | 
| 352                                       | identifier '=' identifier '|' IdentOrLis
     t""" |  | 
| 353         value = self.BuildAttribute('VALUE', p[3] + p[4] + p[5]) |  | 
| 354         p[0] = self.BuildNamed('ExtAttribute', p, 1, value) |  | 
| 355 |  | 
| 356     # [b76.2] A&B&C |  | 
| 357     def p_IdentAndList(self, p): |  | 
| 358         """IdentAndList : identifier '&' IdentAndList |  | 
| 359                         | identifier""" |  | 
| 360         if len(p) > 3: |  | 
| 361             p[0] = p[1] + p[2] + p[3] |  | 
| 362         else: |  | 
| 363             p[0] = p[1] |  | 
| 364 |  | 
| 365     # [b76.3] A|B|C |  | 
| 366     def p_IdentOrList(self, p): |  | 
| 367         """IdentOrList : identifier '|' IdentOrList |  | 
| 368                        | identifier""" |  | 
| 369         if len(p) > 3: |  | 
| 370             p[0] = p[1] + p[2] + p[3] |  | 
| 371         else: |  | 
| 372             p[0] = p[1] |  | 
| 373 |  | 
| 374     def __dir__(self): |  | 
| 375         # Remove REMOVED_RULES from listing so yacc doesn't parse them |  | 
| 376         # FIXME: Upstream |  | 
| 377         keys = set(self.__dict__.keys() + dir(self.__class__)) |  | 
| 378         for rule in REMOVED_RULES: |  | 
| 379             production_name = 'p_' + rule |  | 
| 380             if production_name in keys: |  | 
| 381                 keys.remove(production_name) |  | 
| 382         return list(keys) |  | 
| 383 |  | 
| 384     def __init__(self, lexer=None, verbose=False, debug=False, mute_error=False,
      outputdir=''): |  | 
| 385         lexer = lexer or BlinkIDLLexer() |  | 
| 386         self.lexer = lexer |  | 
| 387         self.tokens = lexer.KnownTokens() |  | 
| 388         # Using SLR (instead of LALR) generates the table faster, |  | 
| 389         # but produces the same output. This is ok b/c Web IDL (and Blink IDL) |  | 
| 390         # is an SLR grammar (as is often the case for simple LL(1) grammars). |  | 
| 391         self.yaccobj = yacc.yacc(module=self, start=STARTING_SYMBOL, method='SLR
     ', debug=debug, outputdir=outputdir) |  | 
| 392         self.parse_debug = debug |  | 
| 393         self.verbose = verbose |  | 
| 394         self.mute_error = mute_error |  | 
| 395         self._parse_errors = 0 |  | 
| 396         self._parse_warnings = 0 |  | 
| 397         self._last_error_msg = None |  | 
| 398         self._last_error_lineno = 0 |  | 
| 399         self._last_error_pos = 0 |  | 
| 400 |  | 
| 401 |  | 
| 402 # If run by itself, attempt to build the parser |  | 
| 403 if __name__ == '__main__': |  | 
| 404     parser = BlinkIDLParser() |  | 
| OLD | NEW | 
|---|