| OLD | NEW |
| (Empty) |
| 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://heycam.github.io/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 # | |
| 40 # Try to load the ply module, if not, then assume it is in the third_party | |
| 41 # directory. | |
| 42 # | |
| 43 try: | |
| 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 | |
| 48 except 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 # | |
| 61 ERROR_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 | |
| 73 def Boolean(val): | |
| 74 """Convert to strict boolean type.""" | |
| 75 if val: | |
| 76 return True | |
| 77 return False | |
| 78 | |
| 79 | |
| 80 def 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 | |
| 93 def 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 # | |
| 108 def 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://heycam.github.io/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 | |
| 157 class 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://heycam.github.io/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] | |
| 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 # [5.1] Error recovery for interface. | |
| 233 def p_InterfaceError(self, p): | |
| 234 """Interface : INTERFACE identifier Inheritance '{' error""" | |
| 235 p[0] = self.BuildError(p, 'Interface') | |
| 236 | |
| 237 # [6] | |
| 238 def p_Partial(self, p): | |
| 239 """Partial : PARTIAL PartialDefinition""" | |
| 240 p[2].AddChildren(self.BuildTrue('Partial')) | |
| 241 p[0] = p[2] | |
| 242 | |
| 243 # [6.1] Error recovery for Partial | |
| 244 def p_PartialError(self, p): | |
| 245 """Partial : PARTIAL error""" | |
| 246 p[0] = self.BuildError(p, 'Partial') | |
| 247 | |
| 248 # [7] | |
| 249 def p_PartialDefinition(self, p): | |
| 250 """PartialDefinition : PartialDictionary | |
| 251 | PartialInterface""" | |
| 252 p[0] = p[1] | |
| 253 | |
| 254 # [8] | |
| 255 def p_PartialInterface(self, p): | |
| 256 """PartialInterface : INTERFACE identifier '{' InterfaceMembers '}' ';'""" | |
| 257 p[0] = self.BuildNamed('Interface', p, 2, p[4]) | |
| 258 | |
| 259 # [9] | |
| 260 def p_InterfaceMembers(self, p): | |
| 261 """InterfaceMembers : ExtendedAttributeList InterfaceMember InterfaceMembers | |
| 262 |""" | |
| 263 if len(p) > 1: | |
| 264 p[2].AddChildren(p[1]) | |
| 265 p[0] = ListFromConcat(p[2], p[3]) | |
| 266 | |
| 267 # [9.1] Error recovery for InterfaceMembers | |
| 268 def p_InterfaceMembersError(self, p): | |
| 269 """InterfaceMembers : error""" | |
| 270 p[0] = self.BuildError(p, 'InterfaceMembers') | |
| 271 | |
| 272 # [10] Removed unsupported: Serializer | |
| 273 def p_InterfaceMember(self, p): | |
| 274 """InterfaceMember : Const | |
| 275 | Operation | |
| 276 | Serializer | |
| 277 | Stringifier | |
| 278 | StaticMember | |
| 279 | Iterable | |
| 280 | ReadonlyMember | |
| 281 | ReadWriteAttribute | |
| 282 | ReadWriteMaplike | |
| 283 | ReadWriteSetlike""" | |
| 284 p[0] = p[1] | |
| 285 | |
| 286 # [11] | |
| 287 def p_Dictionary(self, p): | |
| 288 """Dictionary : DICTIONARY identifier Inheritance '{' DictionaryMembers '}'
';'""" | |
| 289 p[0] = self.BuildNamed('Dictionary', p, 2, ListFromConcat(p[3], p[5])) | |
| 290 | |
| 291 # [11.1] Error recovery for regular Dictionary | |
| 292 def p_DictionaryError(self, p): | |
| 293 """Dictionary : DICTIONARY error ';'""" | |
| 294 p[0] = self.BuildError(p, 'Dictionary') | |
| 295 | |
| 296 # [11.2] Error recovery for regular Dictionary | |
| 297 # (for errors inside dictionary definition) | |
| 298 def p_DictionaryError2(self, p): | |
| 299 """Dictionary : DICTIONARY identifier Inheritance '{' error""" | |
| 300 p[0] = self.BuildError(p, 'Dictionary') | |
| 301 | |
| 302 # [12] | |
| 303 def p_DictionaryMembers(self, p): | |
| 304 """DictionaryMembers : ExtendedAttributeList DictionaryMember DictionaryMemb
ers | |
| 305 |""" | |
| 306 if len(p) > 1: | |
| 307 p[2].AddChildren(p[1]) | |
| 308 p[0] = ListFromConcat(p[2], p[3]) | |
| 309 | |
| 310 # [13] | |
| 311 def p_DictionaryMember(self, p): | |
| 312 """DictionaryMember : Type identifier Default ';'""" | |
| 313 p[0] = self.BuildNamed('Key', p, 2, ListFromConcat(p[1], p[3])) | |
| 314 | |
| 315 # [14] NOT IMPLEMENTED (Required) | |
| 316 | |
| 317 # [15] | |
| 318 def p_PartialDictionary(self, p): | |
| 319 """PartialDictionary : DICTIONARY identifier '{' DictionaryMembers '}' ';'""
" | |
| 320 partial = self.BuildTrue('Partial') | |
| 321 p[0] = self.BuildNamed('Dictionary', p, 2, ListFromConcat(p[4], partial)) | |
| 322 | |
| 323 # [15.1] Error recovery for Partial Dictionary | |
| 324 def p_PartialDictionaryError(self, p): | |
| 325 """PartialDictionary : DICTIONARY error ';'""" | |
| 326 p[0] = self.BuildError(p, 'PartialDictionary') | |
| 327 | |
| 328 # [16] | |
| 329 def p_Default(self, p): | |
| 330 """Default : '=' DefaultValue | |
| 331 |""" | |
| 332 if len(p) > 1: | |
| 333 p[0] = self.BuildProduction('Default', p, 2, p[2]) | |
| 334 | |
| 335 # [17] | |
| 336 def p_DefaultValue(self, p): | |
| 337 """DefaultValue : ConstValue | |
| 338 | string | |
| 339 | '[' ']'""" | |
| 340 if len(p) == 3: | |
| 341 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'sequence'), | |
| 342 self.BuildAttribute('VALUE', '[]')) | |
| 343 elif type(p[1]) == str: | |
| 344 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'DOMString'), | |
| 345 self.BuildAttribute('NAME', p[1])) | |
| 346 else: | |
| 347 p[0] = p[1] | |
| 348 | |
| 349 # [] - Not specified | |
| 350 def p_Exception(self, p): | |
| 351 """Exception : EXCEPTION identifier Inheritance '{' ExceptionMembers '}' ';'
""" | |
| 352 p[0] = self.BuildNamed('Exception', p, 2, ListFromConcat(p[3], p[5])) | |
| 353 | |
| 354 # [] - Not specified | |
| 355 def p_ExceptionMembers(self, p): | |
| 356 """ExceptionMembers : ExtendedAttributeList ExceptionMember ExceptionMembers | |
| 357 |""" | |
| 358 if len(p) > 1: | |
| 359 p[2].AddChildren(p[1]) | |
| 360 p[0] = ListFromConcat(p[2], p[3]) | |
| 361 | |
| 362 # [.1] Error recovery for ExceptionMembers - Not specified | |
| 363 def p_ExceptionMembersError(self, p): | |
| 364 """ExceptionMembers : error""" | |
| 365 p[0] = self.BuildError(p, 'ExceptionMembers') | |
| 366 | |
| 367 # [18] | |
| 368 def p_Inheritance(self, p): | |
| 369 """Inheritance : ':' identifier | |
| 370 |""" | |
| 371 if len(p) > 1: | |
| 372 p[0] = self.BuildNamed('Inherit', p, 2) | |
| 373 | |
| 374 # [19] | |
| 375 def p_Enum(self, p): | |
| 376 """Enum : ENUM identifier '{' EnumValueList '}' ';'""" | |
| 377 p[0] = self.BuildNamed('Enum', p, 2, p[4]) | |
| 378 | |
| 379 # [19.1] Error recovery for Enums | |
| 380 def p_EnumError(self, p): | |
| 381 """Enum : ENUM error ';'""" | |
| 382 p[0] = self.BuildError(p, 'Enum') | |
| 383 | |
| 384 # [20] | |
| 385 def p_EnumValueList(self, p): | |
| 386 """EnumValueList : ExtendedAttributeList string EnumValueListComma""" | |
| 387 enum = self.BuildNamed('EnumItem', p, 2, p[1]) | |
| 388 p[0] = ListFromConcat(enum, p[3]) | |
| 389 | |
| 390 # [21] | |
| 391 def p_EnumValueListComma(self, p): | |
| 392 """EnumValueListComma : ',' EnumValueListString | |
| 393 |""" | |
| 394 if len(p) > 1: | |
| 395 p[0] = p[2] | |
| 396 | |
| 397 # [22] | |
| 398 def p_EnumValueListString(self, p): | |
| 399 """EnumValueListString : ExtendedAttributeList string EnumValueListComma | |
| 400 |""" | |
| 401 if len(p) > 1: | |
| 402 enum = self.BuildNamed('EnumItem', p, 2, p[1]) | |
| 403 p[0] = ListFromConcat(enum, p[3]) | |
| 404 | |
| 405 # [23] | |
| 406 def p_CallbackRest(self, p): | |
| 407 """CallbackRest : identifier '=' ReturnType '(' ArgumentList ')' ';'""" | |
| 408 arguments = self.BuildProduction('Arguments', p, 4, p[5]) | |
| 409 p[0] = self.BuildNamed('Callback', p, 1, ListFromConcat(p[3], arguments)) | |
| 410 | |
| 411 # [24] | |
| 412 def p_Typedef(self, p): | |
| 413 """Typedef : TYPEDEF ExtendedAttributeListNoComments Type identifier ';'""" | |
| 414 p[0] = self.BuildNamed('Typedef', p, 4, ListFromConcat(p[2], p[3])) | |
| 415 | |
| 416 # [24.1] Error recovery for Typedefs | |
| 417 def p_TypedefError(self, p): | |
| 418 """Typedef : TYPEDEF error ';'""" | |
| 419 p[0] = self.BuildError(p, 'Typedef') | |
| 420 | |
| 421 # [25] | |
| 422 def p_ImplementsStatement(self, p): | |
| 423 """ImplementsStatement : identifier IMPLEMENTS identifier ';'""" | |
| 424 name = self.BuildAttribute('REFERENCE', p[3]) | |
| 425 p[0] = self.BuildNamed('Implements', p, 1, name) | |
| 426 | |
| 427 # [26] | |
| 428 def p_Const(self, p): | |
| 429 """Const : CONST ConstType identifier '=' ConstValue ';'""" | |
| 430 value = self.BuildProduction('Value', p, 5, p[5]) | |
| 431 p[0] = self.BuildNamed('Const', p, 3, ListFromConcat(p[2], value)) | |
| 432 | |
| 433 # [27] | |
| 434 def p_ConstValue(self, p): | |
| 435 """ConstValue : BooleanLiteral | |
| 436 | FloatLiteral | |
| 437 | integer | |
| 438 | null""" | |
| 439 if type(p[1]) == str: | |
| 440 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'integer'), | |
| 441 self.BuildAttribute('NAME', p[1])) | |
| 442 else: | |
| 443 p[0] = p[1] | |
| 444 | |
| 445 # [27.1] Add definition for NULL | |
| 446 def p_null(self, p): | |
| 447 """null : NULL""" | |
| 448 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'NULL'), | |
| 449 self.BuildAttribute('NAME', 'NULL')) | |
| 450 | |
| 451 # [28] | |
| 452 def p_BooleanLiteral(self, p): | |
| 453 """BooleanLiteral : TRUE | |
| 454 | FALSE""" | |
| 455 value = self.BuildAttribute('VALUE', Boolean(p[1] == 'true')) | |
| 456 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'boolean'), value) | |
| 457 | |
| 458 # [29] | |
| 459 def p_FloatLiteral(self, p): | |
| 460 """FloatLiteral : float | |
| 461 | '-' INFINITY | |
| 462 | INFINITY | |
| 463 | NAN """ | |
| 464 if len(p) > 2: | |
| 465 val = '-Infinity' | |
| 466 else: | |
| 467 val = p[1] | |
| 468 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'float'), | |
| 469 self.BuildAttribute('VALUE', val)) | |
| 470 | |
| 471 # [30] | |
| 472 def p_Serializer(self, p): | |
| 473 """Serializer : SERIALIZER SerializerRest""" | |
| 474 p[0] = self.BuildProduction('Serializer', p, 1, p[2]) | |
| 475 | |
| 476 # [31] | |
| 477 # TODO(jl): This adds ReturnType and ';', missing from the spec's grammar. | |
| 478 # https://www.w3.org/Bugs/Public/show_bug.cgi?id=20361 | |
| 479 def p_SerializerRest(self, p): | |
| 480 """SerializerRest : ReturnType OperationRest | |
| 481 | '=' SerializationPattern ';' | |
| 482 | ';'""" | |
| 483 if len(p) == 3: | |
| 484 p[2].AddChildren(p[1]) | |
| 485 p[0] = p[2] | |
| 486 elif len(p) == 4: | |
| 487 p[0] = p[2] | |
| 488 | |
| 489 # [32] | |
| 490 def p_SerializationPattern(self, p): | |
| 491 """SerializationPattern : '{' SerializationPatternMap '}' | |
| 492 | '[' SerializationPatternList ']' | |
| 493 | identifier""" | |
| 494 if len(p) > 2: | |
| 495 p[0] = p[2] | |
| 496 else: | |
| 497 p[0] = self.BuildAttribute('ATTRIBUTE', p[1]) | |
| 498 | |
| 499 # [33] | |
| 500 # TODO(jl): This adds the "ATTRIBUTE" and "INHERIT ',' ATTRIBUTE" variants, | |
| 501 # missing from the spec's grammar. | |
| 502 # https://www.w3.org/Bugs/Public/show_bug.cgi?id=20361 | |
| 503 def p_SerializationPatternMap(self, p): | |
| 504 """SerializationPatternMap : GETTER | |
| 505 | ATTRIBUTE | |
| 506 | INHERIT ',' ATTRIBUTE | |
| 507 | INHERIT Identifiers | |
| 508 | identifier Identifiers | |
| 509 |""" | |
| 510 p[0] = self.BuildProduction('Map', p, 0) | |
| 511 if len(p) == 4: | |
| 512 p[0].AddChildren(self.BuildTrue('INHERIT')) | |
| 513 p[0].AddChildren(self.BuildTrue('ATTRIBUTE')) | |
| 514 elif len(p) > 1: | |
| 515 if p[1] == 'getter': | |
| 516 p[0].AddChildren(self.BuildTrue('GETTER')) | |
| 517 elif p[1] == 'attribute': | |
| 518 p[0].AddChildren(self.BuildTrue('ATTRIBUTE')) | |
| 519 else: | |
| 520 if p[1] == 'inherit': | |
| 521 p[0].AddChildren(self.BuildTrue('INHERIT')) | |
| 522 attributes = p[2] | |
| 523 else: | |
| 524 attributes = ListFromConcat(p[1], p[2]) | |
| 525 p[0].AddChildren(self.BuildAttribute('ATTRIBUTES', attributes)) | |
| 526 | |
| 527 # [34] | |
| 528 def p_SerializationPatternList(self, p): | |
| 529 """SerializationPatternList : GETTER | |
| 530 | identifier Identifiers | |
| 531 |""" | |
| 532 p[0] = self.BuildProduction('List', p, 0) | |
| 533 if len(p) > 1: | |
| 534 if p[1] == 'getter': | |
| 535 p[0].AddChildren(self.BuildTrue('GETTER')) | |
| 536 else: | |
| 537 attributes = ListFromConcat(p[1], p[2]) | |
| 538 p[0].AddChildren(self.BuildAttribute('ATTRIBUTES', attributes)) | |
| 539 | |
| 540 # [35] | |
| 541 def p_Stringifier(self, p): | |
| 542 """Stringifier : STRINGIFIER StringifierRest""" | |
| 543 p[0] = self.BuildProduction('Stringifier', p, 1, p[2]) | |
| 544 | |
| 545 # [36] | |
| 546 def p_StringifierRest(self, p): | |
| 547 """StringifierRest : AttributeRest | |
| 548 | ReturnType OperationRest | |
| 549 | ';'""" | |
| 550 if len(p) == 3: | |
| 551 p[2].AddChildren(p[1]) | |
| 552 p[0] = p[2] | |
| 553 elif p[1] != ';': | |
| 554 p[0] = p[1] | |
| 555 | |
| 556 # [37] | |
| 557 def p_StaticMember(self, p): | |
| 558 """StaticMember : STATIC StaticMemberRest""" | |
| 559 p[2].AddChildren(self.BuildTrue('STATIC')) | |
| 560 p[0] = p[2] | |
| 561 | |
| 562 # [38] | |
| 563 def p_StaticMemberRest(self, p): | |
| 564 """StaticMemberRest : ReadOnly AttributeRest | |
| 565 | ReturnType OperationRest""" | |
| 566 if len(p) == 2: | |
| 567 p[0] = p[1] | |
| 568 else: | |
| 569 p[2].AddChildren(p[1]) | |
| 570 p[0] = p[2] | |
| 571 | |
| 572 # [39] | |
| 573 def p_ReadonlyMember(self, p): | |
| 574 """ReadonlyMember : READONLY ReadonlyMemberRest""" | |
| 575 p[2].AddChildren(self.BuildTrue('READONLY')) | |
| 576 p[0] = p[2] | |
| 577 | |
| 578 # [40] | |
| 579 def p_ReadonlyMemberRest(self, p): | |
| 580 """ReadonlyMemberRest : AttributeRest | |
| 581 | MaplikeRest | |
| 582 | SetlikeRest""" | |
| 583 p[0] = p[1] | |
| 584 | |
| 585 # [41] | |
| 586 def p_ReadWriteAttribute(self, p): | |
| 587 """ReadWriteAttribute : INHERIT ReadOnly AttributeRest | |
| 588 | AttributeRest""" | |
| 589 if len(p) > 2: | |
| 590 inherit = self.BuildTrue('INHERIT') | |
| 591 p[3].AddChildren(ListFromConcat(inherit, p[2])) | |
| 592 p[0] = p[3] | |
| 593 else: | |
| 594 p[0] = p[1] | |
| 595 | |
| 596 # [42] | |
| 597 def p_AttributeRest(self, p): | |
| 598 """AttributeRest : ATTRIBUTE Type AttributeName ';'""" | |
| 599 p[0] = self.BuildNamed('Attribute', p, 3, p[2]) | |
| 600 | |
| 601 # [43] | |
| 602 def p_AttributeName(self, p): | |
| 603 """AttributeName : AttributeNameKeyword | |
| 604 | identifier""" | |
| 605 p[0] = p[1] | |
| 606 | |
| 607 # [44] | |
| 608 def p_AttributeNameKeyword(self, p): | |
| 609 """AttributeNameKeyword : REQUIRED""" | |
| 610 p[0] = p[1] | |
| 611 | |
| 612 # [45] Unreferenced in the specification | |
| 613 | |
| 614 # [46] | |
| 615 def p_ReadOnly(self, p): | |
| 616 """ReadOnly : READONLY | |
| 617 |""" | |
| 618 if len(p) > 1: | |
| 619 p[0] = self.BuildTrue('READONLY') | |
| 620 | |
| 621 # [47] | |
| 622 def p_Operation(self, p): | |
| 623 """Operation : ReturnType OperationRest | |
| 624 | SpecialOperation""" | |
| 625 if len(p) == 3: | |
| 626 p[2].AddChildren(p[1]) | |
| 627 p[0] = p[2] | |
| 628 else: | |
| 629 p[0] = p[1] | |
| 630 | |
| 631 # [48] | |
| 632 def p_SpecialOperation(self, p): | |
| 633 """SpecialOperation : Special Specials ReturnType OperationRest""" | |
| 634 p[4].AddChildren(ListFromConcat(p[1], p[2], p[3])) | |
| 635 p[0] = p[4] | |
| 636 | |
| 637 # [49] | |
| 638 def p_Specials(self, p): | |
| 639 """Specials : Special Specials | |
| 640 | """ | |
| 641 if len(p) > 1: | |
| 642 p[0] = ListFromConcat(p[1], p[2]) | |
| 643 | |
| 644 # [50] | |
| 645 def p_Special(self, p): | |
| 646 """Special : GETTER | |
| 647 | SETTER | |
| 648 | CREATOR | |
| 649 | DELETER | |
| 650 | LEGACYCALLER""" | |
| 651 p[0] = self.BuildTrue(p[1].upper()) | |
| 652 | |
| 653 # [51] | |
| 654 def p_OperationRest(self, p): | |
| 655 """OperationRest : OptionalIdentifier '(' ArgumentList ')' ';'""" | |
| 656 arguments = self.BuildProduction('Arguments', p, 2, p[3]) | |
| 657 p[0] = self.BuildNamed('Operation', p, 1, arguments) | |
| 658 | |
| 659 # [52] | |
| 660 def p_OptionalIdentifier(self, p): | |
| 661 """OptionalIdentifier : identifier | |
| 662 |""" | |
| 663 if len(p) > 1: | |
| 664 p[0] = p[1] | |
| 665 else: | |
| 666 p[0] = '_unnamed_' | |
| 667 | |
| 668 # [53] | |
| 669 def p_ArgumentList(self, p): | |
| 670 """ArgumentList : Argument Arguments | |
| 671 |""" | |
| 672 if len(p) > 1: | |
| 673 p[0] = ListFromConcat(p[1], p[2]) | |
| 674 | |
| 675 # [53.1] ArgumentList error recovery | |
| 676 def p_ArgumentListError(self, p): | |
| 677 """ArgumentList : error """ | |
| 678 p[0] = self.BuildError(p, 'ArgumentList') | |
| 679 | |
| 680 # [54] | |
| 681 def p_Arguments(self, p): | |
| 682 """Arguments : ',' Argument Arguments | |
| 683 |""" | |
| 684 if len(p) > 1: | |
| 685 p[0] = ListFromConcat(p[2], p[3]) | |
| 686 | |
| 687 # [54.1] Arguments error recovery | |
| 688 def p_ArgumentsError(self, p): | |
| 689 """Arguments : ',' error""" | |
| 690 p[0] = self.BuildError(p, 'Arguments') | |
| 691 | |
| 692 # [55] | |
| 693 def p_Argument(self, p): | |
| 694 """Argument : ExtendedAttributeList OptionalOrRequiredArgument""" | |
| 695 p[2].AddChildren(p[1]) | |
| 696 p[0] = p[2] | |
| 697 | |
| 698 # [56] | |
| 699 def p_OptionalOrRequiredArgument(self, p): | |
| 700 """OptionalOrRequiredArgument : OPTIONAL Type ArgumentName Default | |
| 701 | Type Ellipsis ArgumentName""" | |
| 702 if len(p) > 4: | |
| 703 arg = self.BuildNamed('Argument', p, 3, ListFromConcat(p[2], p[4])) | |
| 704 arg.AddChildren(self.BuildTrue('OPTIONAL')) | |
| 705 else: | |
| 706 arg = self.BuildNamed('Argument', p, 3, ListFromConcat(p[1], p[2])) | |
| 707 p[0] = arg | |
| 708 | |
| 709 # [57] | |
| 710 def p_ArgumentName(self, p): | |
| 711 """ArgumentName : ArgumentNameKeyword | |
| 712 | identifier""" | |
| 713 p[0] = p[1] | |
| 714 | |
| 715 # [58] | |
| 716 def p_Ellipsis(self, p): | |
| 717 """Ellipsis : ELLIPSIS | |
| 718 |""" | |
| 719 if len(p) > 1: | |
| 720 p[0] = self.BuildNamed('Argument', p, 1) | |
| 721 p[0].AddChildren(self.BuildTrue('ELLIPSIS')) | |
| 722 | |
| 723 # [] Unspecified | |
| 724 def p_ExceptionMember(self, p): | |
| 725 """ExceptionMember : Const | |
| 726 | ExceptionField""" | |
| 727 p[0] = p[1] | |
| 728 | |
| 729 # [] Unspecified | |
| 730 def p_ExceptionField(self, p): | |
| 731 """ExceptionField : Type identifier ';'""" | |
| 732 p[0] = self.BuildNamed('ExceptionField', p, 2, p[1]) | |
| 733 | |
| 734 # [] Error recovery for ExceptionMembers - Unspecified | |
| 735 def p_ExceptionFieldError(self, p): | |
| 736 """ExceptionField : error""" | |
| 737 p[0] = self.BuildError(p, 'ExceptionField') | |
| 738 | |
| 739 # [59] | |
| 740 def p_Iterable(self, p): | |
| 741 """Iterable : ITERABLE '<' Type OptionalType '>' ';' | |
| 742 | LEGACYITERABLE '<' Type '>' ';'""" | |
| 743 if len(p) > 6: | |
| 744 childlist = ListFromConcat(p[3], p[4]) | |
| 745 p[0] = self.BuildProduction('Iterable', p, 2, childlist) | |
| 746 else: | |
| 747 p[0] = self.BuildProduction('LegacyIterable', p, 2, p[3]) | |
| 748 | |
| 749 # [60] | |
| 750 def p_OptionalType(self, p): | |
| 751 """OptionalType : ',' Type | |
| 752 |""" | |
| 753 if len(p) > 1: | |
| 754 p[0] = p[2] | |
| 755 | |
| 756 # [61] | |
| 757 def p_ReadWriteMaplike(self, p): | |
| 758 """ReadWriteMaplike : MaplikeRest""" | |
| 759 p[0] = p[1] | |
| 760 | |
| 761 # [62] | |
| 762 def p_ReadWriteSetlike(self, p): | |
| 763 """ReadWriteSetlike : SetlikeRest""" | |
| 764 p[0] = p[1] | |
| 765 | |
| 766 # [63] | |
| 767 def p_MaplikeRest(self, p): | |
| 768 """MaplikeRest : MAPLIKE '<' Type ',' Type '>' ';'""" | |
| 769 childlist = ListFromConcat(p[3], p[5]) | |
| 770 p[0] = self.BuildProduction('Maplike', p, 2, childlist) | |
| 771 | |
| 772 # [64] | |
| 773 def p_SetlikeRest(self, p): | |
| 774 """SetlikeRest : SETLIKE '<' Type '>' ';'""" | |
| 775 p[0] = self.BuildProduction('Setlike', p, 2, p[3]) | |
| 776 | |
| 777 # [65] No comment version for mid statement attributes. | |
| 778 def p_ExtendedAttributeListNoComments(self, p): | |
| 779 """ExtendedAttributeListNoComments : '[' ExtendedAttribute ExtendedAttribute
s ']' | |
| 780 | """ | |
| 781 if len(p) > 2: | |
| 782 items = ListFromConcat(p[2], p[3]) | |
| 783 p[0] = self.BuildProduction('ExtAttributes', p, 1, items) | |
| 784 | |
| 785 # [65.1] Add optional comment field for start of statements. | |
| 786 def p_ExtendedAttributeList(self, p): | |
| 787 """ExtendedAttributeList : Comments '[' ExtendedAttribute ExtendedAttributes
']' | |
| 788 | Comments """ | |
| 789 if len(p) > 2: | |
| 790 items = ListFromConcat(p[3], p[4]) | |
| 791 attribs = self.BuildProduction('ExtAttributes', p, 2, items) | |
| 792 p[0] = ListFromConcat(p[1], attribs) | |
| 793 else: | |
| 794 p[0] = p[1] | |
| 795 | |
| 796 # [66] | |
| 797 def p_ExtendedAttributes(self, p): | |
| 798 """ExtendedAttributes : ',' ExtendedAttribute ExtendedAttributes | |
| 799 |""" | |
| 800 if len(p) > 1: | |
| 801 p[0] = ListFromConcat(p[2], p[3]) | |
| 802 | |
| 803 # We only support: | |
| 804 # [ identifier ] | |
| 805 # [ identifier ( ArgumentList ) ] | |
| 806 # [ identifier = identifier ] | |
| 807 # [ identifier = ( IdentifierList ) ] | |
| 808 # [ identifier = identifier ( ArgumentList ) ] | |
| 809 # [66] map directly to [91-93, 95] | |
| 810 # [67-69, 71] are unsupported | |
| 811 def p_ExtendedAttribute(self, p): | |
| 812 """ExtendedAttribute : ExtendedAttributeNoArgs | |
| 813 | ExtendedAttributeArgList | |
| 814 | ExtendedAttributeIdent | |
| 815 | ExtendedAttributeIdentList | |
| 816 | ExtendedAttributeNamedArgList""" | |
| 817 p[0] = p[1] | |
| 818 | |
| 819 # [71] | |
| 820 def p_ArgumentNameKeyword(self, p): | |
| 821 """ArgumentNameKeyword : ATTRIBUTE | |
| 822 | CALLBACK | |
| 823 | CONST | |
| 824 | CREATOR | |
| 825 | DELETER | |
| 826 | DICTIONARY | |
| 827 | ENUM | |
| 828 | EXCEPTION | |
| 829 | GETTER | |
| 830 | IMPLEMENTS | |
| 831 | INHERIT | |
| 832 | LEGACYCALLER | |
| 833 | PARTIAL | |
| 834 | SERIALIZER | |
| 835 | SETTER | |
| 836 | STATIC | |
| 837 | STRINGIFIER | |
| 838 | TYPEDEF | |
| 839 | UNRESTRICTED""" | |
| 840 p[0] = p[1] | |
| 841 | |
| 842 # [72] NOT IMPLEMENTED (OtherOrComma) | |
| 843 | |
| 844 # [73] | |
| 845 def p_Type(self, p): | |
| 846 """Type : SingleType | |
| 847 | UnionType TypeSuffix""" | |
| 848 if len(p) == 2: | |
| 849 p[0] = self.BuildProduction('Type', p, 1, p[1]) | |
| 850 else: | |
| 851 p[0] = self.BuildProduction('Type', p, 1, ListFromConcat(p[1], p[2])) | |
| 852 | |
| 853 # [74] | |
| 854 def p_SingleType(self, p): | |
| 855 """SingleType : NonAnyType | |
| 856 | ANY TypeSuffixStartingWithArray""" | |
| 857 if len(p) == 2: | |
| 858 p[0] = p[1] | |
| 859 else: | |
| 860 p[0] = ListFromConcat(self.BuildProduction('Any', p, 1), p[2]) | |
| 861 | |
| 862 # [75] | |
| 863 def p_UnionType(self, p): | |
| 864 """UnionType : '(' UnionMemberType OR UnionMemberType UnionMemberTypes ')'""
" | |
| 865 | |
| 866 # [76] | |
| 867 def p_UnionMemberType(self, p): | |
| 868 """UnionMemberType : NonAnyType | |
| 869 | UnionType TypeSuffix | |
| 870 | ANY '[' ']' TypeSuffix""" | |
| 871 # [77] | |
| 872 def p_UnionMemberTypes(self, p): | |
| 873 """UnionMemberTypes : OR UnionMemberType UnionMemberTypes | |
| 874 |""" | |
| 875 | |
| 876 # [78] Moved BYTESTRING, DOMSTRING, OBJECT, DATE, REGEXP to PrimitiveType | |
| 877 # Moving all built-in types into PrimitiveType makes it easier to | |
| 878 # differentiate between them and 'identifier', since p[1] would be a string in | |
| 879 # both cases. | |
| 880 def p_NonAnyType(self, p): | |
| 881 """NonAnyType : PrimitiveType TypeSuffix | |
| 882 | PromiseType Null | |
| 883 | identifier TypeSuffix | |
| 884 | SEQUENCE '<' Type '>' Null""" | |
| 885 if len(p) == 3: | |
| 886 if type(p[1]) == str: | |
| 887 typeref = self.BuildNamed('Typeref', p, 1) | |
| 888 else: | |
| 889 typeref = p[1] | |
| 890 p[0] = ListFromConcat(typeref, p[2]) | |
| 891 | |
| 892 if len(p) == 6: | |
| 893 p[0] = self.BuildProduction('Sequence', p, 1, ListFromConcat(p[3], p[5])) | |
| 894 | |
| 895 # [79] NOT IMPLEMENTED (BufferRelatedType) | |
| 896 | |
| 897 # [80] | |
| 898 def p_ConstType(self, p): | |
| 899 """ConstType : PrimitiveType Null | |
| 900 | identifier Null""" | |
| 901 if type(p[1]) == str: | |
| 902 p[0] = self.BuildNamed('Typeref', p, 1, p[2]) | |
| 903 else: | |
| 904 p[1].AddChildren(p[2]) | |
| 905 p[0] = p[1] | |
| 906 | |
| 907 | |
| 908 # [81] Added BYTESTRING, DOMSTRING, OBJECT, DATE, REGEXP | |
| 909 def p_PrimitiveType(self, p): | |
| 910 """PrimitiveType : UnsignedIntegerType | |
| 911 | UnrestrictedFloatType | |
| 912 | BOOLEAN | |
| 913 | BYTE | |
| 914 | OCTET | |
| 915 | BYTESTRING | |
| 916 | DOMSTRING | |
| 917 | OBJECT | |
| 918 | DATE | |
| 919 | REGEXP""" | |
| 920 if type(p[1]) == str: | |
| 921 p[0] = self.BuildNamed('PrimitiveType', p, 1) | |
| 922 else: | |
| 923 p[0] = p[1] | |
| 924 | |
| 925 | |
| 926 # [82] | |
| 927 def p_UnrestrictedFloatType(self, p): | |
| 928 """UnrestrictedFloatType : UNRESTRICTED FloatType | |
| 929 | FloatType""" | |
| 930 if len(p) == 2: | |
| 931 typeref = self.BuildNamed('PrimitiveType', p, 1) | |
| 932 else: | |
| 933 typeref = self.BuildNamed('PrimitiveType', p, 2) | |
| 934 typeref.AddChildren(self.BuildTrue('UNRESTRICTED')) | |
| 935 p[0] = typeref | |
| 936 | |
| 937 | |
| 938 # [83] | |
| 939 def p_FloatType(self, p): | |
| 940 """FloatType : FLOAT | |
| 941 | DOUBLE""" | |
| 942 p[0] = p[1] | |
| 943 | |
| 944 # [84] | |
| 945 def p_UnsignedIntegerType(self, p): | |
| 946 """UnsignedIntegerType : UNSIGNED IntegerType | |
| 947 | IntegerType""" | |
| 948 if len(p) == 2: | |
| 949 p[0] = p[1] | |
| 950 else: | |
| 951 p[0] = 'unsigned ' + p[2] | |
| 952 | |
| 953 # [85] | |
| 954 def p_IntegerType(self, p): | |
| 955 """IntegerType : SHORT | |
| 956 | LONG OptionalLong""" | |
| 957 if len(p) == 2: | |
| 958 p[0] = p[1] | |
| 959 else: | |
| 960 p[0] = p[1] + p[2] | |
| 961 | |
| 962 # [86] | |
| 963 def p_OptionalLong(self, p): | |
| 964 """OptionalLong : LONG | |
| 965 | """ | |
| 966 if len(p) > 1: | |
| 967 p[0] = ' ' + p[1] | |
| 968 else: | |
| 969 p[0] = '' | |
| 970 | |
| 971 # [87] Add unqualified Promise | |
| 972 def p_PromiseType(self, p): | |
| 973 """PromiseType : PROMISE '<' ReturnType '>' | |
| 974 | PROMISE""" | |
| 975 if len(p) == 2: | |
| 976 # Promise without resolution type is not specified in the Web IDL spec. | |
| 977 # As it is used in some specs and in the blink implementation, | |
| 978 # we allow that here. | |
| 979 resolution_type = self.BuildProduction('Type', p, 1, | |
| 980 self.BuildProduction('Any', p, 1)) | |
| 981 p[0] = self.BuildNamed('Promise', p, 1, resolution_type) | |
| 982 else: | |
| 983 p[0] = self.BuildNamed('Promise', p, 1, p[3]) | |
| 984 | |
| 985 # [88] Add support for sized array | |
| 986 def p_TypeSuffix(self, p): | |
| 987 """TypeSuffix : '[' integer ']' TypeSuffix | |
| 988 | '[' ']' TypeSuffix | |
| 989 | '?' TypeSuffixStartingWithArray | |
| 990 | """ | |
| 991 if len(p) == 5: | |
| 992 p[0] = self.BuildNamed('Array', p, 2, p[4]) | |
| 993 | |
| 994 if len(p) == 4: | |
| 995 p[0] = self.BuildProduction('Array', p, 1, p[3]) | |
| 996 | |
| 997 if len(p) == 3: | |
| 998 p[0] = ListFromConcat(self.BuildTrue('NULLABLE'), p[2]) | |
| 999 | |
| 1000 | |
| 1001 # [89] | |
| 1002 def p_TypeSuffixStartingWithArray(self, p): | |
| 1003 """TypeSuffixStartingWithArray : '[' ']' TypeSuffix | |
| 1004 | """ | |
| 1005 if len(p) > 1: | |
| 1006 p[0] = self.BuildProduction('Array', p, 0, p[3]) | |
| 1007 | |
| 1008 # [90] | |
| 1009 def p_Null(self, p): | |
| 1010 """Null : '?' | |
| 1011 |""" | |
| 1012 if len(p) > 1: | |
| 1013 p[0] = self.BuildTrue('NULLABLE') | |
| 1014 | |
| 1015 # [91] | |
| 1016 def p_ReturnType(self, p): | |
| 1017 """ReturnType : Type | |
| 1018 | VOID""" | |
| 1019 if p[1] == 'void': | |
| 1020 p[0] = self.BuildProduction('Type', p, 1) | |
| 1021 p[0].AddChildren(self.BuildNamed('PrimitiveType', p, 1)) | |
| 1022 else: | |
| 1023 p[0] = p[1] | |
| 1024 | |
| 1025 # [92] | |
| 1026 def p_IdentifierList(self, p): | |
| 1027 """IdentifierList : identifier Identifiers""" | |
| 1028 p[0] = ListFromConcat(p[1], p[2]) | |
| 1029 | |
| 1030 # [93] | |
| 1031 def p_Identifiers(self, p): | |
| 1032 """Identifiers : ',' identifier Identifiers | |
| 1033 |""" | |
| 1034 if len(p) > 1: | |
| 1035 p[0] = ListFromConcat(p[2], p[3]) | |
| 1036 | |
| 1037 # [94] | |
| 1038 def p_ExtendedAttributeNoArgs(self, p): | |
| 1039 """ExtendedAttributeNoArgs : identifier""" | |
| 1040 p[0] = self.BuildNamed('ExtAttribute', p, 1) | |
| 1041 | |
| 1042 # [95] | |
| 1043 def p_ExtendedAttributeArgList(self, p): | |
| 1044 """ExtendedAttributeArgList : identifier '(' ArgumentList ')'""" | |
| 1045 arguments = self.BuildProduction('Arguments', p, 2, p[3]) | |
| 1046 p[0] = self.BuildNamed('ExtAttribute', p, 1, arguments) | |
| 1047 | |
| 1048 # [96] | |
| 1049 def p_ExtendedAttributeIdent(self, p): | |
| 1050 """ExtendedAttributeIdent : identifier '=' identifier""" | |
| 1051 value = self.BuildAttribute('VALUE', p[3]) | |
| 1052 p[0] = self.BuildNamed('ExtAttribute', p, 1, value) | |
| 1053 | |
| 1054 # [97] | |
| 1055 def p_ExtendedAttributeIdentList(self, p): | |
| 1056 """ExtendedAttributeIdentList : identifier '=' '(' IdentifierList ')'""" | |
| 1057 value = self.BuildAttribute('VALUE', p[4]) | |
| 1058 p[0] = self.BuildNamed('ExtAttribute', p, 1, value) | |
| 1059 | |
| 1060 # [98] | |
| 1061 def p_ExtendedAttributeNamedArgList(self, p): | |
| 1062 """ExtendedAttributeNamedArgList : identifier '=' identifier '(' ArgumentLis
t ')'""" | |
| 1063 args = self.BuildProduction('Arguments', p, 4, p[5]) | |
| 1064 value = self.BuildNamed('Call', p, 3, args) | |
| 1065 p[0] = self.BuildNamed('ExtAttribute', p, 1, value) | |
| 1066 | |
| 1067 # | |
| 1068 # Parser Errors | |
| 1069 # | |
| 1070 # p_error is called whenever the parser can not find a pattern match for | |
| 1071 # a set of items from the current state. The p_error function defined here | |
| 1072 # is triggered logging an error, and parsing recovery happens as the | |
| 1073 # p_<type>_error functions defined above are called. This allows the parser | |
| 1074 # to continue so as to capture more than one error per file. | |
| 1075 # | |
| 1076 def p_error(self, t): | |
| 1077 if t: | |
| 1078 lineno = t.lineno | |
| 1079 pos = t.lexpos | |
| 1080 prev = self.yaccobj.symstack[-1] | |
| 1081 if type(prev) == lex.LexToken: | |
| 1082 msg = "Unexpected %s after %s." % ( | |
| 1083 TokenTypeName(t), TokenTypeName(prev)) | |
| 1084 else: | |
| 1085 msg = "Unexpected %s." % (t.value) | |
| 1086 else: | |
| 1087 last = self.LastToken() | |
| 1088 lineno = last.lineno | |
| 1089 pos = last.lexpos | |
| 1090 msg = "Unexpected end of file after %s." % TokenTypeName(last) | |
| 1091 self.yaccobj.restart() | |
| 1092 | |
| 1093 # Attempt to remap the error to a friendlier form | |
| 1094 if msg in ERROR_REMAP: | |
| 1095 msg = ERROR_REMAP[msg] | |
| 1096 | |
| 1097 self._last_error_msg = msg | |
| 1098 self._last_error_lineno = lineno | |
| 1099 self._last_error_pos = pos | |
| 1100 | |
| 1101 def Warn(self, node, msg): | |
| 1102 sys.stdout.write(node.GetLogLine(msg)) | |
| 1103 self.parse_warnings += 1 | |
| 1104 | |
| 1105 def LastToken(self): | |
| 1106 return self.lexer.last | |
| 1107 | |
| 1108 def __init__(self, lexer, verbose=False, debug=False, mute_error=False): | |
| 1109 self.lexer = lexer | |
| 1110 self.tokens = lexer.KnownTokens() | |
| 1111 self.yaccobj = yacc.yacc(module=self, tabmodule=None, debug=debug, | |
| 1112 optimize=0, write_tables=0) | |
| 1113 self.parse_debug = debug | |
| 1114 self.verbose = verbose | |
| 1115 self.mute_error = mute_error | |
| 1116 self._parse_errors = 0 | |
| 1117 self._parse_warnings = 0 | |
| 1118 self._last_error_msg = None | |
| 1119 self._last_error_lineno = 0 | |
| 1120 self._last_error_pos = 0 | |
| 1121 | |
| 1122 | |
| 1123 # | |
| 1124 # BuildProduction | |
| 1125 # | |
| 1126 # Production is the set of items sent to a grammar rule resulting in a new | |
| 1127 # item being returned. | |
| 1128 # | |
| 1129 # p - Is the Yacc production object containing the stack of items | |
| 1130 # index - Index into the production of the name for the item being produced. | |
| 1131 # cls - The type of item being producted | |
| 1132 # childlist - The children of the new item | |
| 1133 def BuildProduction(self, cls, p, index, childlist=None): | |
| 1134 try: | |
| 1135 if not childlist: | |
| 1136 childlist = [] | |
| 1137 | |
| 1138 filename = self.lexer.Lexer().filename | |
| 1139 lineno = p.lineno(index) | |
| 1140 pos = p.lexpos(index) | |
| 1141 out = IDLNode(cls, filename, lineno, pos, childlist) | |
| 1142 return out | |
| 1143 except: | |
| 1144 print 'Exception while parsing:' | |
| 1145 for num, item in enumerate(p): | |
| 1146 print ' [%d] %s' % (num, ExpandProduction(item)) | |
| 1147 if self.LastToken(): | |
| 1148 print 'Last token: %s' % str(self.LastToken()) | |
| 1149 raise | |
| 1150 | |
| 1151 def BuildNamed(self, cls, p, index, childlist=None): | |
| 1152 childlist = ListFromConcat(childlist) | |
| 1153 childlist.append(self.BuildAttribute('NAME', p[index])) | |
| 1154 return self.BuildProduction(cls, p, index, childlist) | |
| 1155 | |
| 1156 def BuildComment(self, cls, p, index): | |
| 1157 name = p[index] | |
| 1158 | |
| 1159 # Remove comment markers | |
| 1160 lines = [] | |
| 1161 if name[:2] == '//': | |
| 1162 # For C++ style, remove any leading whitespace and the '//' marker from | |
| 1163 # each line. | |
| 1164 form = 'cc' | |
| 1165 for line in name.split('\n'): | |
| 1166 start = line.find('//') | |
| 1167 lines.append(line[start+2:]) | |
| 1168 else: | |
| 1169 # For C style, remove ending '*/'' | |
| 1170 form = 'c' | |
| 1171 for line in name[:-2].split('\n'): | |
| 1172 # Remove characters until start marker for this line '*' if found | |
| 1173 # otherwise it should be blank. | |
| 1174 offs = line.find('*') | |
| 1175 if offs >= 0: | |
| 1176 line = line[offs + 1:].rstrip() | |
| 1177 else: | |
| 1178 line = '' | |
| 1179 lines.append(line) | |
| 1180 name = '\n'.join(lines) | |
| 1181 childlist = [self.BuildAttribute('NAME', name), | |
| 1182 self.BuildAttribute('FORM', form)] | |
| 1183 return self.BuildProduction(cls, p, index, childlist) | |
| 1184 | |
| 1185 # | |
| 1186 # BuildError | |
| 1187 # | |
| 1188 # Build and Errror node as part of the recovery process. | |
| 1189 # | |
| 1190 # | |
| 1191 def BuildError(self, p, prod): | |
| 1192 self._parse_errors += 1 | |
| 1193 name = self.BuildAttribute('NAME', self._last_error_msg) | |
| 1194 line = self.BuildAttribute('LINE', self._last_error_lineno) | |
| 1195 pos = self.BuildAttribute('POS', self._last_error_pos) | |
| 1196 prod = self.BuildAttribute('PROD', prod) | |
| 1197 | |
| 1198 node = self.BuildProduction('Error', p, 1, | |
| 1199 ListFromConcat(name, line, pos, prod)) | |
| 1200 if not self.mute_error: | |
| 1201 node.Error(self._last_error_msg) | |
| 1202 | |
| 1203 return node | |
| 1204 | |
| 1205 # | |
| 1206 # BuildAttribute | |
| 1207 # | |
| 1208 # An ExtendedAttribute is a special production that results in a property | |
| 1209 # which is applied to the adjacent item. Attributes have no children and | |
| 1210 # instead represent key/value pairs. | |
| 1211 # | |
| 1212 def BuildAttribute(self, key, val): | |
| 1213 return IDLAttribute(key, val) | |
| 1214 | |
| 1215 def BuildFalse(self, key): | |
| 1216 return IDLAttribute(key, Boolean(False)) | |
| 1217 | |
| 1218 def BuildTrue(self, key): | |
| 1219 return IDLAttribute(key, Boolean(True)) | |
| 1220 | |
| 1221 def GetErrors(self): | |
| 1222 # Access lexer errors, despite being private | |
| 1223 # pylint: disable=W0212 | |
| 1224 return self._parse_errors + self.lexer._lex_errors | |
| 1225 | |
| 1226 # | |
| 1227 # ParseData | |
| 1228 # | |
| 1229 # Attempts to parse the current data loaded in the lexer. | |
| 1230 # | |
| 1231 def ParseText(self, filename, data): | |
| 1232 self._parse_errors = 0 | |
| 1233 self._parse_warnings = 0 | |
| 1234 self._last_error_msg = None | |
| 1235 self._last_error_lineno = 0 | |
| 1236 self._last_error_pos = 0 | |
| 1237 | |
| 1238 try: | |
| 1239 self.lexer.Tokenize(data, filename) | |
| 1240 nodes = self.yaccobj.parse(lexer=self.lexer) or [] | |
| 1241 name = self.BuildAttribute('NAME', filename) | |
| 1242 return IDLNode('File', filename, 0, 0, nodes + [name]) | |
| 1243 | |
| 1244 except lex.LexError as lexError: | |
| 1245 sys.stderr.write('Error in token: %s\n' % str(lexError)) | |
| 1246 return None | |
| 1247 | |
| 1248 | |
| 1249 | |
| 1250 def ParseFile(parser, filename): | |
| 1251 """Parse a file and return a File type of node.""" | |
| 1252 with open(filename) as fileobject: | |
| 1253 try: | |
| 1254 out = parser.ParseText(filename, fileobject.read()) | |
| 1255 out.SetProperty('DATETIME', time.ctime(os.path.getmtime(filename))) | |
| 1256 out.SetProperty('ERRORS', parser.GetErrors()) | |
| 1257 return out | |
| 1258 | |
| 1259 except Exception as e: | |
| 1260 last = parser.LastToken() | |
| 1261 sys.stderr.write('%s(%d) : Internal parsing error\n\t%s.\n' % ( | |
| 1262 filename, last.lineno, str(e))) | |
| 1263 | |
| 1264 | |
| 1265 def main(argv): | |
| 1266 nodes = [] | |
| 1267 parser = IDLParser(IDLLexer()) | |
| 1268 errors = 0 | |
| 1269 for filename in argv: | |
| 1270 filenode = ParseFile(parser, filename) | |
| 1271 if (filenode): | |
| 1272 errors += filenode.GetProperty('ERRORS') | |
| 1273 nodes.append(filenode) | |
| 1274 | |
| 1275 ast = IDLNode('AST', '__AST__', 0, 0, nodes) | |
| 1276 | |
| 1277 print '\n'.join(ast.Tree(accept_props=['PROD'])) | |
| 1278 if errors: | |
| 1279 print '\nFound %d errors.\n' % errors | |
| 1280 | |
| 1281 return errors | |
| 1282 | |
| 1283 | |
| 1284 if __name__ == '__main__': | |
| 1285 sys.exit(main(sys.argv[1:])) | |
| OLD | NEW |