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