Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 #!/usr/bin/env python | |
| 2 # Copyright (c) 2012 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 glob | |
| 33 import optparse | |
| 34 import os.path | |
| 35 import sys | |
| 36 import time | |
| 37 | |
| 38 from idl_lexer import IDLLexer | |
| 39 from idl_node import IDLAttribute, IDLNode | |
| 40 from idl_log import ErrOut, InfoOut, WarnOut | |
| 41 | |
| 42 from ply import lex | |
| 43 from ply import yacc | |
| 44 | |
| 45 # | |
| 46 # ERROR_REMAP | |
| 47 # | |
| 48 # Maps the standard error formula into a more friendly error message. | |
| 49 # | |
| 50 ERROR_REMAP = { | |
| 51 'Unexpected ")" after "(".' : 'Empty argument list.', | |
| 52 'Unexpected ")" after ",".' : 'Missing argument.', | |
| 53 'Unexpected "}" after ",".' : 'Trailing comma in block.', | |
| 54 'Unexpected "}" after "{".' : 'Unexpected empty block.', | |
| 55 'Unexpected comment after "}".' : 'Unexpected trailing comment.', | |
| 56 'Unexpected "{" after keyword "enum".' : 'Enum missing name.', | |
| 57 'Unexpected "{" after keyword "struct".' : 'Struct missing name.', | |
| 58 'Unexpected "{" after keyword "interface".' : 'Interface missing name.', | |
| 59 } | |
| 60 | |
| 61 | |
| 62 def Boolean(val): | |
| 63 """Convert to strict boolean type.""" | |
| 64 if val: | |
| 65 return True | |
| 66 return False | |
| 67 | |
| 68 | |
| 69 def ListFromConcat(*items): | |
| 70 """Generate list by concatenating inputs""" | |
| 71 itemsout = [] | |
| 72 for item in items: | |
| 73 if item is None: | |
| 74 continue | |
| 75 if type(item) is not type([]): | |
| 76 itemsout.append(item) | |
| 77 else: | |
| 78 itemsout.extend(item) | |
| 79 | |
| 80 return itemsout | |
| 81 | |
| 82 def ExpandProduction(p): | |
| 83 if type(p) == list: | |
| 84 return '[' + ', '.join([ExpandProduction(x) for x in p]) + ']' | |
| 85 if type(p) == IDLNode: | |
| 86 return 'Node:' + str(p) | |
| 87 if type(p) == IDLAttribute: | |
| 88 return 'Attr:' + str(p) | |
| 89 if type(p) == str: | |
| 90 return 'str:' + p | |
| 91 return '%s:%s' % (p.__class__.__name__, str(p)) | |
| 92 | |
| 93 # TokenTypeName | |
| 94 # | |
| 95 # Generate a string which has the type and value of the token. | |
| 96 # | |
| 97 def TokenTypeName(t): | |
| 98 if t.type == 'SYMBOL': | |
| 99 return 'symbol %s' % t.value | |
| 100 if t.type in ['HEX', 'INT', 'OCT', 'FLOAT']: | |
| 101 return 'value %s' % t.value | |
| 102 if t.type == 'string' : | |
| 103 return 'string "%s"' % t.value | |
| 104 if t.type == 'COMMENT' : | |
| 105 return 'comment' | |
| 106 if t.type == t.value: | |
| 107 return '"%s"' % t.value | |
| 108 if t.type == ',': | |
| 109 return 'Comma' | |
| 110 if t.type == 'identifier': | |
| 111 return 'identifier "%s"' % t.value | |
| 112 return 'keyword "%s"' % t.value | |
| 113 | |
| 114 | |
| 115 # | |
| 116 # IDL Parser | |
| 117 # | |
| 118 # The Parser inherits the from the Lexer to provide PLY with the tokenizing | |
| 119 # definitions. Parsing patterns are encoded as functions where p_<name> is | |
| 120 # is called any time a patern matching the function documentation is found. | |
| 121 # Paterns are expressed in the form of: | |
| 122 # """ <new item> : <item> .... | |
| 123 # | <item> ....""" | |
| 124 # | |
| 125 # Where new item is the result of a match against one or more sets of items | |
| 126 # separated by the "|". | |
| 127 # | |
| 128 # The function is called with an object 'p' where p[0] is the output object | |
| 129 # and p[n] is the set of inputs for positive values of 'n'. Len(p) can be | |
| 130 # used to distinguish between multiple item sets in the pattern. | |
| 131 # | |
| 132 # For more details on parsing refer to the PLY documentation at | |
| 133 # http://www.dabeaz.com/ply/ | |
| 134 # | |
| 135 # The parser is based on the WebIDL standard. See: | |
| 136 # http://www.w3.org/TR/WebIDL/#idl-grammar | |
| 137 # | |
| 138 # The various productions are anotated so that the WHOLE number greater than | |
|
sehr
2013/04/08 18:37:07
annotated
noelallen1
2013/04/11 21:52:09
Done.
| |
| 139 # zero in the comment denote the matching WebIDL grammer definition. | |
|
sehr
2013/04/08 18:37:07
denotes ... grammar
noelallen1
2013/04/11 21:52:09
Done.
| |
| 140 # | |
| 141 # Productions with a factional component in the comment denote additions to | |
|
sehr
2013/04/08 18:37:07
fractional
noelallen1
2013/04/11 21:52:09
Done.
| |
| 142 # the WebIDL spec, such as comments. | |
| 143 # | |
| 144 | |
| 145 | |
| 146 class IDLParser(IDLLexer): | |
| 147 # | |
| 148 # We force all input files to start with two comments. The first comment is a | |
| 149 # Copyright notice followed by a file comment and finally by file level | |
| 150 # productions. | |
| 151 # | |
| 152 # [0] Insert a TOP definition for Copyright and Comments | |
| 153 def p_Top(self, p): | |
| 154 """Top : COMMENT COMMENT Definitions""" | |
| 155 Copyright = self.BuildComment('Copyright', p, 1) | |
| 156 Filedoc = self.BuildComment('Comment', p, 2) | |
| 157 p[0] = ListFromConcat(Copyright, Filedoc, p[3]) | |
| 158 | |
| 159 # [0.1] Add support for Multiple COMMENTS | |
| 160 def p_Comments(self, p): | |
| 161 """Comments : CommentsRest""" | |
| 162 if len(p) > 1: | |
| 163 p[0] = p[1] | |
| 164 | |
| 165 # [0.2] Produce a COMMENT and agregate sibling comments | |
|
sehr
2013/04/08 18:37:07
aggregate
noelallen1
2013/04/11 21:52:09
Done.
| |
| 166 def p_CommentsRest(self, p): | |
| 167 """CommentsRest : COMMENT CommentsRest | |
| 168 | """ | |
| 169 if len(p) > 1: | |
| 170 p[0] = ListFromConcat(self.BuildComment('Comment', p, 1), p[2]) | |
| 171 | |
| 172 # [0.3] Add Inline block, and bogus production to removing unused | |
| 173 # warning for Pepper Only RSHIFT, LSHIFT, LABEL, and STRUCT | |
| 174 # TODO(noelallen) remove bogus definition. | |
| 175 def p_InlineBlock(self, p): | |
| 176 """InlineBlock : INLINE | |
| 177 | INLINE RSHIFT LABEL LSHIFT NAMESPACE STRUCT""" | |
| 178 if len(p) == 2: | |
| 179 p[0] = self.BuildProduction("Inline", p, 1) | |
| 180 | |
| 181 # | |
| 182 #The parser is based on the WebIDL standard. See: | |
| 183 # http://www.w3.org/TR/WebIDL/#idl-grammar | |
| 184 # | |
| 185 # [1] | |
| 186 def p_Definitions(self, p): | |
| 187 """Definitions : ExtendedAttributeList Definition Definitions | |
| 188 | """ | |
| 189 if len(p) > 1: | |
| 190 p[2].AddChildren(p[1]) | |
| 191 p[0] = ListFromConcat(p[2], p[3]) | |
| 192 | |
| 193 # [2] Add INLINE definition | |
| 194 def p_Definition(self, p): | |
| 195 """Definition : CallbackOrInterface | |
| 196 | Partial | |
| 197 | Dictionary | |
| 198 | Exception | |
| 199 | Enum | |
| 200 | Typedef | |
| 201 | ImplementsStatement | |
| 202 | InlineBlock""" | |
| 203 p[0] = p[1] | |
| 204 | |
| 205 # [2.1] Error recovery for definition | |
| 206 def p_DefinitionError(self, p): | |
| 207 """Definition : error ';'""" | |
| 208 p[0] = self.BuildError(p, 'Definition') | |
| 209 | |
| 210 # [3] | |
| 211 def p_CallbackOrInterface(self, p): | |
| 212 """CallbackOrInterface : CALLBACK CallbackRestOrInterface | |
| 213 | Interface""" | |
| 214 if len(p) > 2: | |
| 215 p[0] = p[2] | |
| 216 else: | |
| 217 p[0] = p[1] | |
| 218 | |
| 219 # [4] | |
| 220 def p_CallbackRestOrInterface(self, p): | |
| 221 """CallbackRestOrInterface : CallbackRest | |
| 222 | Interface""" | |
| 223 p[0] = p[1] | |
| 224 | |
| 225 # [5] | |
| 226 def p_Interface(self, p): | |
| 227 """Interface : INTERFACE identifier Inheritance '{' InterfaceMembers '}' ';' """ | |
| 228 p[0] = self.BuildNamed('Interface', p, 2, ListFromConcat(p[3], p[5])) | |
| 229 | |
| 230 # [6] Error recovery for PARTIAL | |
| 231 def p_Partial(self, p): | |
| 232 """Partial : PARTIAL PartialDefinition""" | |
| 233 p[2].AddChildren(self.BuildTrue('Partial')) | |
| 234 p[0] = p[2] | |
| 235 | |
| 236 # [6.1] Error recovery for Enums | |
| 237 def p_PartialError(self, p): | |
| 238 """Partial : PARTIAL error""" | |
| 239 p[0] = self.BuildError(p, 'Partial') | |
| 240 | |
| 241 # [7] | |
| 242 def p_PartialDefinition(self, p): | |
| 243 """PartialDefinition : PartialDictionary | |
| 244 | PartialInterface""" | |
| 245 p[0] = p[1] | |
| 246 | |
| 247 # [8] | |
| 248 def p_PartialInterface(self, p): | |
| 249 """PartialInterface : INTERFACE identifier '{' InterfaceMembers '}' ';'""" | |
| 250 p[0] = self.BuildNamed('Interface', p, 2, p[4]) | |
| 251 | |
| 252 # [9] | |
| 253 def p_InterfaceMembers(self, p): | |
| 254 """InterfaceMembers : ExtendedAttributeList InterfaceMember InterfaceMembers | |
| 255 |""" | |
| 256 if len(p) > 1: | |
| 257 p[2].AddChildren(p[1]) | |
| 258 p[0] = ListFromConcat(p[2], p[3]) | |
| 259 | |
| 260 # [10] | |
| 261 def p_InterfaceMember(self, p): | |
| 262 """InterfaceMember : Const | |
| 263 | AttributeOrOperation""" | |
| 264 p[0] = p[1] | |
| 265 | |
| 266 # [11] | |
| 267 def p_Dictionary(self, p): | |
| 268 """Dictionary : DICTIONARY identifier Inheritance '{' DictionaryMembers '}' ';'""" | |
| 269 p[0] = self.BuildNamed('Dictionary', p, 2, ListFromConcat(p[3], p[5])) | |
| 270 | |
| 271 # [11.1] Error recovery for regular Dictionary | |
| 272 def p_DictionaryError(self, p): | |
| 273 """Dictionary : DICTIONARY error ';'""" | |
| 274 p[0] = self.BuildError(p, 'Dictionary') | |
| 275 | |
| 276 # [12] | |
| 277 def p_DictionaryMembers(self, p): | |
| 278 """DictionaryMembers : ExtendedAttributeList DictionaryMember DictionaryMemb ers | |
| 279 |""" | |
| 280 if len(p) > 1: | |
| 281 p[2].AddChildren(p[1]) | |
| 282 p[0] = ListFromConcat(p[2], p[3]) | |
| 283 | |
| 284 # [13] | |
| 285 def p_DictionaryMember(self, p): | |
| 286 """DictionaryMember : Type identifier Default ';'""" | |
| 287 p[0] = self.BuildNamed('Key', p, 2, ListFromConcat(p[1], p[3])) | |
| 288 | |
| 289 # [14] | |
| 290 def p_PartialDictionary(self, p): | |
| 291 """PartialDictionary : DICTIONARY identifier '{' DictionaryMembers '}' ';'"" " | |
| 292 partial = self.BuildTrue('Partial') | |
| 293 p[0] = self.BuildNamed('Dictionary', p, 2, ListFromConcat(p[4], partial)) | |
| 294 | |
| 295 # [14.1] Error recovery for Partial Dictionary | |
| 296 def p_PartialDictionaryError(self, p): | |
| 297 """PartialDictionary : DICTIONARY error ';'""" | |
| 298 p[0] = self.BuildError(p, 'PartialDictionary') | |
| 299 | |
| 300 # [15] | |
| 301 def p_Default(self, p): | |
| 302 """Default : '=' DefaultValue | |
| 303 |""" | |
| 304 if len(p) > 1: | |
| 305 p[0] = self.BuildProduction('Default', p, 2, p[2]) | |
| 306 | |
| 307 # [16] | |
| 308 def p_DefaultValue(self, p): | |
| 309 """DefaultValue : ConstValue | |
| 310 | string""" | |
| 311 if type(p[1]) == str: | |
| 312 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'DOMString'), | |
| 313 self.BuildAttribute('NAME', p[1])) | |
| 314 else: | |
| 315 p[0] = p[1] | |
| 316 | |
| 317 # [17] | |
| 318 def p_Exception(self, p): | |
| 319 """Exception : EXCEPTION identifier Inheritance '{' ExceptionMembers '}' ';' """ | |
| 320 p[0] = self.BuildNamed('Exception', p, 2, ListFromConcat(p[3], p[5])) | |
| 321 | |
| 322 # [18] | |
| 323 def p_ExceptionMembers(self, p): | |
| 324 """ExceptionMembers : ExtendedAttributeList ExceptionMember ExceptionMembers | |
| 325 |""" | |
| 326 if len(p) > 1: | |
| 327 p[2].AddChildren(p[1]) | |
| 328 p[0] = ListFromConcat(p[2], p[3]) | |
| 329 | |
| 330 # [18.1] Error recovery for ExceptionMembers | |
| 331 def p_ExceptionMembersError(self, p): | |
| 332 """ExceptionMembers : error""" | |
| 333 p[0] = self.BuildError(p, 'ExceptionMembers') | |
| 334 | |
| 335 # [19] | |
| 336 def p_Inheritance(self, p): | |
| 337 """Inheritance : ':' identifier | |
| 338 |""" | |
| 339 if len(p) > 1: | |
| 340 p[0] = self.BuildNamed('Inherit', p, 2) | |
| 341 | |
| 342 # [20] | |
| 343 def p_Enum(self, p): | |
| 344 """Enum : ENUM identifier '{' EnumValueList '}' ';'""" | |
| 345 p[0] = self.BuildNamed('Enum', p, 2, p[4]) | |
| 346 | |
| 347 # [20.1] Error recovery for Enums | |
| 348 def p_EnumError(self, p): | |
| 349 """Enum : ENUM error ';'""" | |
| 350 p[0] = self.BuildError(p, 'Enum') | |
| 351 | |
| 352 # [21] | |
| 353 def p_EnumValueList(self, p): | |
| 354 """EnumValueList : ExtendedAttributeList string EnumValues""" | |
| 355 enum = self.BuildNamed('EnumItem', p, 2, p[1]) | |
| 356 p[0] = ListFromConcat(enum, p[3]) | |
| 357 | |
| 358 # [22] | |
| 359 def p_EnumValues(self, p): | |
| 360 """EnumValues : ',' ExtendedAttributeList string EnumValues | |
| 361 |""" | |
| 362 if len(p) > 1: | |
| 363 enum = self.BuildNamed('EnumItem', p, 3, p[2]) | |
| 364 p[0] = ListFromConcat(enum, p[4]) | |
| 365 | |
| 366 # [23] | |
| 367 def p_CallbackRest(self, p): | |
| 368 """CallbackRest : identifier '=' ReturnType '(' ArgumentList ')' ';'""" | |
| 369 arguments = self.BuildProduction('Arguments', p, 4, p[5]) | |
| 370 p[0] = self.BuildNamed('Callback', p, 1, ListFromConcat(p[3], arguments)) | |
| 371 | |
| 372 # [24] | |
| 373 def p_Typedef(self, p): | |
| 374 """Typedef : TYPEDEF ExtendedAttributeList Type identifier ';'""" | |
| 375 p[0] = self.BuildNamed('Typedef', p, 4, ListFromConcat(p[2], p[3])) | |
| 376 | |
| 377 # [24.1] Error recovery for Typedefs | |
| 378 def p_TypedefError(self, p): | |
| 379 """Typedef : TYPEDEF error ';'""" | |
| 380 p[0] = self.BuildError(p, 'Typedef') | |
| 381 | |
| 382 # [25] | |
| 383 def p_ImplementsStatement(self, p): | |
| 384 """ImplementsStatement : identifier IMPLEMENTS identifier ';'""" | |
| 385 name = self.BuildAttribute('REFERENCE', p[3]) | |
| 386 p[0] = self.BuildNamed('Implements', p, 1, name) | |
| 387 | |
| 388 # [26] | |
| 389 def p_Const(self, p): | |
| 390 """Const : CONST ConstType identifier '=' ConstValue ';'""" | |
| 391 value = self.BuildProduction('Value', p, 5, p[5]) | |
| 392 p[0] = self.BuildNamed('Const', p, 3, ListFromConcat(p[2], value)) | |
| 393 | |
| 394 # [27] | |
| 395 def p_ConstValue(self, p): | |
| 396 """ConstValue : BooleanLiteral | |
| 397 | FloatLiteral | |
| 398 | integer | |
| 399 | null""" | |
| 400 if type(p[1]) == str: | |
| 401 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'integer'), | |
| 402 self.BuildAttribute('NAME', p[1])) | |
| 403 else: | |
| 404 p[0] = p[1] | |
| 405 | |
| 406 # [27.1] Add definition for NULL | |
| 407 def p_null(self, p): | |
| 408 """null : NULL""" | |
| 409 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'NULL'), | |
| 410 self.BuildAttribute('NAME', 'NULL')) | |
| 411 | |
| 412 # [28] | |
| 413 def p_BooleanLiteral(self, p): | |
| 414 """BooleanLiteral : TRUE | |
| 415 | FALSE""" | |
| 416 value = self.BuildAttribute('NAME', Boolean(p[1] == 'true')) | |
| 417 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'boolean'), value) | |
| 418 | |
| 419 # [29] | |
| 420 def p_FloatLiteral(self, p): | |
| 421 """FloatLiteral : float | |
| 422 | '-' INFINITY | |
| 423 | INFINITY | |
| 424 | NAN """ | |
| 425 if len(p) > 2: | |
| 426 val = '-Infinitiy' | |
|
sehr
2013/04/08 18:37:07
Infinity
noelallen1
2013/04/11 21:52:09
Done.
| |
| 427 else: | |
| 428 val = p[1] | |
| 429 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'float'), | |
| 430 self.BuildAttribute('VALUE', val)) | |
| 431 | |
| 432 # [30] | |
| 433 def p_AttributeOrOperation(self, p): | |
| 434 """AttributeOrOperation : STRINGIFIER StringifierAttributeOrOperation | |
| 435 | Attribute | |
| 436 | Operation""" | |
| 437 if len(p) > 2: | |
| 438 p[0] = p[2] | |
| 439 else: | |
| 440 p[0] = p[1] | |
| 441 | |
| 442 # [31] | |
| 443 def p_StringifierAttributeOrOperation(self, p): | |
| 444 """StringifierAttributeOrOperation : Attribute | |
| 445 | OperationRest | |
| 446 | ';'""" | |
| 447 if p[1] == ';': | |
| 448 p[0] = self.BuildAttribute('STRINGIFIER', Boolean(True)) | |
| 449 else: | |
| 450 p[0] = ListFromConcat(self.BuildAttribute('STRINGIFIER', p[1]), p[1]) | |
| 451 | |
| 452 # [32] | |
| 453 def p_Attribute(self, p): | |
| 454 """Attribute : Inherit ReadOnly ATTRIBUTE Type identifier ';'""" | |
| 455 p[0] = self.BuildNamed('Attribute', p, 5, | |
| 456 ListFromConcat(p[1], p[2], p[4])) | |
| 457 | |
| 458 # [33] | |
| 459 def p_Inherit(self, p): | |
| 460 """Inherit : INHERIT | |
| 461 |""" | |
| 462 if len(p) > 1: | |
| 463 p[0] = self.BuildTrue('INHERIT') | |
| 464 | |
| 465 # [34] | |
| 466 def p_ReadOnly(self, p): | |
| 467 """ReadOnly : READONLY | |
| 468 |""" | |
| 469 if len(p) > 1: | |
| 470 p[0] = self.BuildTrue('READONLY') | |
| 471 | |
| 472 # [35] | |
| 473 def p_Operation(self, p): | |
| 474 """Operation : Qualifiers OperationRest""" | |
| 475 p[2].AddChildren(p[1]) | |
| 476 p[0] = p[2] | |
| 477 | |
| 478 # [36] | |
| 479 def p_Qualifiers(self, p): | |
| 480 """Qualifiers : STATIC | |
| 481 | Specials""" | |
| 482 if p[1] == 'static': | |
| 483 p[0] = self.BuildTrue('STATIC') | |
| 484 else: | |
| 485 p[0] = p[1] | |
| 486 | |
| 487 # [37] | |
| 488 def p_Specials(self, p): | |
| 489 """Specials : Special Specials | |
| 490 | """ | |
| 491 if len(p) > 1: | |
| 492 p[0] = ListFromConcat(p[1], p[2]) | |
| 493 | |
| 494 # [38] | |
| 495 def p_Special(self, p): | |
| 496 """Special : GETTER | |
| 497 | SETTER | |
| 498 | CREATOR | |
| 499 | DELETER | |
| 500 | LEGACYCALLER""" | |
| 501 p[0] = self.BuildTrue(p[1].upper()) | |
| 502 | |
| 503 | |
| 504 # [39] | |
| 505 def p_OperationRest(self, p): | |
| 506 """OperationRest : ReturnType OptionalIdentifier '(' ArgumentList ')' ';'""" | |
| 507 arguments = self.BuildProduction('Arguments', p, 3, p[4]) | |
| 508 p[0] = self.BuildNamed('Operation', p, 2, ListFromConcat(p[1], arguments)) | |
| 509 | |
| 510 # [40] | |
| 511 def p_OptionalIdentifier(self, p): | |
| 512 """OptionalIdentifier : identifier | |
| 513 |""" | |
| 514 if len(p) > 1: | |
| 515 p[0] = p[1] | |
| 516 else: | |
| 517 p[0] = '_unnamed_' | |
| 518 | |
| 519 # [41] | |
| 520 def p_ArgumentList(self, p): | |
| 521 """ArgumentList : Argument Arguments | |
| 522 |""" | |
| 523 if len(p) > 1: | |
| 524 p[0] = ListFromConcat(p[1], p[2]) | |
| 525 | |
| 526 # [41.1] ArgumentList error recovery | |
| 527 def p_ArgumentListError(self, p): | |
| 528 """ArgumentList : error """ | |
| 529 p[0] = self.BuildError(p, 'ArgumentList') | |
| 530 | |
| 531 # [42] | |
| 532 def p_Arguments(self, p): | |
| 533 """Arguments : ',' Argument Arguments | |
| 534 |""" | |
| 535 if len(p) > 1: | |
| 536 p[0] = ListFromConcat(p[2], p[3]) | |
| 537 | |
| 538 # [43] | |
| 539 def p_Argument(self, p): | |
| 540 """Argument : ExtendedAttributeList OptionalOrRequiredArgument""" | |
| 541 p[2].AddChildren(p[1]) | |
| 542 p[0] = p[2] | |
| 543 | |
| 544 | |
| 545 # [44] | |
| 546 def p_OptionalOrRequiredArgument(self, p): | |
| 547 """OptionalOrRequiredArgument : OPTIONAL Type ArgumentName Default | |
| 548 | Type Ellipsis ArgumentName""" | |
| 549 if len(p) > 4: | |
| 550 arg = self.BuildNamed('Argument', p, 3, ListFromConcat(p[2], p[4])) | |
| 551 arg.AddChildren(self.BuildTrue('OPTIONAL')) | |
| 552 else: | |
| 553 arg = self.BuildNamed('Argument', p, 3, ListFromConcat(p[1], p[2])) | |
| 554 p[0] = arg | |
| 555 | |
| 556 # [45] | |
| 557 def p_ArgumentName(self, p): | |
| 558 """ArgumentName : ArgumentNameKeyword | |
| 559 | identifier""" | |
| 560 p[0] = p[1] | |
| 561 | |
| 562 # [46] | |
| 563 def p_Ellipsis(self, p): | |
| 564 """Ellipsis : ELLIPSIS | |
| 565 |""" | |
| 566 if len(p) > 1: | |
| 567 p[0] = self.BuildNamed('Argument', p, 1) | |
| 568 p[0].AddChildren(self.BuildTrue('ELLIPSIS')) | |
| 569 | |
| 570 # [47] | |
| 571 def p_ExceptionMember(self, p): | |
| 572 """ExceptionMember : Const | |
| 573 | ExceptionField""" | |
| 574 p[0] = p[1] | |
| 575 | |
| 576 # [48] | |
| 577 def p_ExceptionField(self, p): | |
| 578 """ExceptionField : Type identifier ';'""" | |
| 579 p[0] = self.BuildNamed('ExceptionField', p, 2, p[1]) | |
| 580 | |
| 581 # [48.1] Error recovery for ExceptionMembers | |
| 582 def p_ExceptionFieldError(self, p): | |
| 583 """ExceptionField : error""" | |
| 584 p[0] = self.BuildError(p, 'ExceptionField') | |
| 585 | |
| 586 # [49] Add optional comment field | |
| 587 def p_ExtendedAttributeList(self, p): | |
| 588 """ExtendedAttributeList : Comments '[' ExtendedAttribute ExtendedAttributes ']' | |
| 589 | Comments """ | |
| 590 if len(p) > 2: | |
| 591 items = ListFromConcat(p[3], p[4]) | |
| 592 attribs = self.BuildProduction('ExtAttributes', p, 2, items) | |
| 593 p[0] = ListFromConcat(p[1], attribs) | |
| 594 else: | |
| 595 p[0] = p[1] | |
| 596 | |
| 597 # [50] | |
| 598 def p_ExtendedAttributes(self, p): | |
| 599 """ExtendedAttributes : ',' ExtendedAttribute ExtendedAttributes | |
| 600 |""" | |
| 601 if len(p) > 1: | |
| 602 p[0] = ListFromConcat(p[2], p[3]) | |
| 603 | |
| 604 # We only support: | |
| 605 # [ identifier ] | |
| 606 # [ identifier = identifier ] | |
| 607 # [ identifier ( ArgumentList )] | |
| 608 # [ identifier = identifier ( ArgumentList )] | |
| 609 # [51] map directly to 74-77 | |
| 610 # [52-54, 56] are unsupported | |
| 611 def p_ExtendedAttribute(self, p): | |
| 612 """ExtendedAttribute : ExtendedAttributeNoArgs | |
| 613 | ExtendedAttributeArgList | |
| 614 | ExtendedAttributeIdent | |
| 615 | ExtendedAttributeNamedArgList""" | |
| 616 p[0] = p[1] | |
| 617 | |
| 618 # [55] | |
| 619 def p_ArgumentNameKeyword(self, p): | |
| 620 """ArgumentNameKeyword : ATTRIBUTE | |
| 621 | CALLBACK | |
| 622 | CONST | |
| 623 | CREATOR | |
| 624 | DELETER | |
| 625 | DICTIONARY | |
| 626 | ENUM | |
| 627 | EXCEPTION | |
| 628 | GETTER | |
| 629 | IMPLEMENTS | |
| 630 | INHERIT | |
| 631 | LEGACYCALLER | |
| 632 | PARTIAL | |
| 633 | SETTER | |
| 634 | STATIC | |
| 635 | STRINGIFIER | |
| 636 | TYPEDEF | |
| 637 | UNRESTRICTED""" | |
| 638 p[0] = p[1] | |
| 639 | |
| 640 # [57] | |
| 641 def p_Type(self, p): | |
| 642 """Type : SingleType | |
| 643 | UnionType TypeSuffix""" | |
| 644 if len(p) == 2: | |
| 645 p[0] = self.BuildProduction('Type', p, 1, p[1]) | |
| 646 else: | |
| 647 p[0] = self.BuildProduction('Type', p, 1, ListFromConcat(p[1], p[2])) | |
| 648 | |
| 649 # [58] | |
| 650 def p_SingleType(self, p): | |
| 651 """SingleType : NonAnyType | |
| 652 | ANY TypeSuffixStartingWithArray""" | |
| 653 if len(p) == 2: | |
| 654 p[0] = p[1] | |
| 655 else: | |
| 656 p[0] = ListFromConcat(self.BuildProduction('Any', p, 1), p[2]) | |
| 657 | |
| 658 # [59] | |
| 659 def p_UnionType(self, p): | |
| 660 """UnionType : '(' UnionMemberType OR UnionMemberType UnionMemberTypes ')'"" " | |
| 661 | |
| 662 # [60] | |
| 663 def p_UnionMemberType(self, p): | |
| 664 """UnionMemberType : NonAnyType | |
| 665 | UnionType TypeSuffix | |
| 666 | ANY '[' ']' TypeSuffix""" | |
| 667 # [61] | |
| 668 def p_UnionMemberTypes(self, p): | |
| 669 """UnionMemberTypes : OR UnionMemberType UnionMemberTypes | |
| 670 |""" | |
| 671 | |
| 672 # [62] Moved DATE, DOMSTRING, OBJECT to PrimitiveType | |
| 673 def p_NonAnyType(self, p): | |
| 674 """NonAnyType : PrimitiveType TypeSuffix | |
| 675 | identifier TypeSuffix | |
| 676 | SEQUENCE '<' Type '>' Null""" | |
| 677 if len(p) == 3: | |
| 678 if type(p[1]) == str: | |
| 679 typeref = self.BuildNamed('Typeref', p, 1) | |
| 680 else: | |
| 681 typeref = p[1] | |
| 682 p[0] = ListFromConcat(typeref, p[2]) | |
| 683 | |
| 684 if len(p) == 6: | |
| 685 p[0] = self.BuildProduction('Sequence', p, 1, ListFromConcat(p[3], p[5])) | |
| 686 | |
| 687 | |
| 688 # [63] | |
| 689 def p_ConstType(self, p): | |
| 690 """ConstType : PrimitiveType Null | |
| 691 | identifier Null""" | |
| 692 if type(p[1]) == str: | |
| 693 p[0] = self.BuildNamed('Typeref', p, 1, p[2]) | |
| 694 else: | |
| 695 p[1].AddChildren(p[2]) | |
| 696 p[0] = p[1] | |
| 697 | |
| 698 | |
| 699 # [64] | |
| 700 def p_PrimitiveType(self, p): | |
| 701 """PrimitiveType : UnsignedIntegerType | |
| 702 | UnrestrictedFloatType | |
| 703 | BOOLEAN | |
| 704 | BYTE | |
| 705 | OCTET | |
| 706 | DOMSTRING | |
| 707 | DATE | |
| 708 | OBJECT""" | |
| 709 if type(p[1]) == str: | |
| 710 p[0] = self.BuildNamed('PrimitiveType', p, 1) | |
| 711 else: | |
| 712 p[0] = p[1] | |
| 713 | |
| 714 | |
| 715 # [65] | |
| 716 def p_UnrestrictedFloatType(self, p): | |
| 717 """UnrestrictedFloatType : UNRESTRICTED FloatType | |
| 718 | FloatType""" | |
| 719 if len(p) == 2: | |
| 720 typeref = self.BuildNamed('PrimitiveType', p, 1) | |
| 721 else: | |
| 722 typeref = self.BuildNamed('PrimitiveType', p, 2) | |
| 723 typeref.AddChildren(self.BuildTrue('UNRESTRICTED')) | |
| 724 p[0] = typeref | |
| 725 | |
| 726 | |
| 727 # [66] | |
| 728 def p_FloatType(self, p): | |
| 729 """FloatType : FLOAT | |
| 730 | DOUBLE""" | |
| 731 p[0] = p[1] | |
| 732 | |
| 733 # [67] | |
| 734 def p_UnsignedIntegerType(self, p): | |
| 735 """UnsignedIntegerType : UNSIGNED IntegerType | |
| 736 | IntegerType""" | |
| 737 if len(p) == 2: | |
| 738 p[0] = p[1] | |
| 739 else: | |
| 740 p[0] = 'unsigned ' + p[2] | |
| 741 | |
| 742 # [68] | |
| 743 def p_IntegerType(self, p): | |
| 744 """IntegerType : SHORT | |
| 745 | LONG OptionalLong""" | |
| 746 if len(p) == 2: | |
| 747 p[0] = p[1] | |
| 748 else: | |
| 749 p[0] = p[1] + p[2] | |
| 750 | |
| 751 # [69] | |
| 752 def p_OptionalLong(self, p): | |
| 753 """OptionalLong : LONG | |
| 754 | """ | |
| 755 if len(p) > 1: | |
| 756 p[0] = ' ' + p[1] | |
| 757 else: | |
| 758 p[0] = '' | |
| 759 | |
| 760 | |
| 761 # [70] Add support for sized array | |
| 762 def p_TypeSuffix(self, p): | |
| 763 """TypeSuffix : '[' integer ']' TypeSuffix | |
| 764 | '[' ']' TypeSuffix | |
| 765 | '?' TypeSuffixStartingWithArray | |
| 766 |""" | |
| 767 if len(p) == 5: | |
| 768 p[0] = self.BuildNamed('Array', p, 2, p[4]) | |
| 769 | |
| 770 if len(p) == 4: | |
| 771 p[0] = self.BuildProduction('Array', p, 1, p[3]) | |
| 772 | |
| 773 if len(p) == 3: | |
| 774 p[0] = ListFromConcat(self.BuildTrue('NULLABLE'), p[2]) | |
| 775 | |
| 776 | |
| 777 # [71] | |
| 778 def p_TypeSuffixStartingWithArray(self, p): | |
| 779 """TypeSuffixStartingWithArray : '[' ']' TypeSuffix | |
| 780 | """ | |
| 781 if len(p) > 1: | |
| 782 p[0] = self.BuildProduction('Array', p, 0, p[3]) | |
| 783 | |
| 784 # [72] | |
| 785 def p_Null(self, p): | |
| 786 """Null : '?' | |
| 787 |""" | |
| 788 if len(p) > 1: | |
| 789 p[0] = self.BuildTrue('NULLABLE') | |
| 790 | |
| 791 # [73] | |
| 792 def p_ReturnType(self, p): | |
| 793 """ReturnType : Type | |
| 794 | VOID""" | |
| 795 if p[1] == 'void': | |
| 796 p[0] = self.BuildProduction('Type', p, 1) | |
| 797 p[0].AddChildren(self.BuildNamed('PrimitiveType', p, 1)) | |
| 798 else: | |
| 799 p[0] = p[1] | |
| 800 | |
| 801 # [74] | |
| 802 def p_ExtendedAttributeNoArgs(self, p): | |
| 803 """ExtendedAttributeNoArgs : identifier""" | |
| 804 p[0] = self.BuildNamed('ExtAttribute', p, 1) | |
| 805 | |
| 806 # [75] | |
| 807 def p_ExtendedAttributeArgList(self, p): | |
| 808 """ExtendedAttributeArgList : identifier '(' ArgumentList ')'""" | |
| 809 arguments = self.BuildProduction('Arguments', p, 2, p[3]) | |
| 810 p[0] = self.BuildNamed('ExtAttribute', p, 1, arguments) | |
| 811 | |
| 812 # [76] | |
| 813 def p_ExtendedAttributeIdent(self, p): | |
| 814 """ExtendedAttributeIdent : identifier '=' identifier""" | |
| 815 value = self.BuildAttribute('VALUE', p[3]) | |
| 816 p[0] = self.BuildNamed('ExtAttribute', p, 1, value) | |
| 817 | |
| 818 # [77] | |
| 819 def p_ExtendedAttributeNamedArgList(self, p): | |
| 820 """ExtendedAttributeNamedArgList : identifier '=' identifier '(' ArgumentLis t ')'""" | |
| 821 args = self.BuildProduction('Arguments', p, 4, p[5]) | |
| 822 value = self.BuildNamed('Call', p, 3, args) | |
| 823 p[0] = self.BuildNamed('ExtAttribute', p, 1, value) | |
| 824 | |
| 825 # | |
| 826 # Parser Errors | |
| 827 # | |
| 828 # p_error is called whenever the parser can not find a pattern match for | |
| 829 # a set of items from the current state. The p_error function defined here | |
| 830 # is triggered logging an error, and parsing recover happens as the | |
|
sehr
2013/04/08 18:37:07
recovery
noelallen1
2013/04/11 21:52:09
Done.
| |
| 831 # p_<type>_error functions defined above are called. This allows the parser | |
| 832 # to continue so as to capture more than one error per file. | |
| 833 # | |
| 834 def p_error(self, t): | |
| 835 filename = self.lexobj.filename | |
| 836 self.parse_errors += 1 | |
| 837 if t: | |
| 838 lineno = t.lineno | |
| 839 pos = t.lexpos | |
| 840 prev = self.yaccobj.symstack[-1] | |
| 841 if type(prev) == lex.LexToken: | |
| 842 msg = "Unexpected %s after %s." % ( | |
| 843 TokenTypeName(t), TokenTypeName(prev)) | |
| 844 else: | |
| 845 msg = "Unexpected %s." % (t.value) | |
| 846 else: | |
| 847 lineno = self.last.lineno | |
| 848 pos = self.last.lexpos | |
| 849 msg = "Unexpected end of file after %s." % TokenTypeName(self.last) | |
| 850 self.yaccobj.restart() | |
| 851 | |
| 852 # Attempt to remap the error to a friendlier form | |
| 853 if msg in ERROR_REMAP: | |
| 854 msg = ERROR_REMAP[msg] | |
| 855 | |
| 856 self.last_error_msg = msg | |
| 857 self.last_error_lineno = lineno | |
| 858 self.last_error_pos = pos | |
| 859 | |
| 860 # Log the error | |
| 861 ErrOut.LogLine(filename, lineno, pos, msg) | |
| 862 | |
| 863 def Warn(self, node, msg): | |
| 864 WarnOut.LogLine(node.filename, node.lineno, node.pos, msg) | |
| 865 self.parse_warnings += 1 | |
| 866 | |
| 867 def __init__(self, filename, data, options): | |
| 868 IDLLexer.__init__(self, filename, data) | |
| 869 self.yaccobj = yacc.yacc(module=self, tabmodule=None, debug=False, | |
| 870 optimize=0, write_tables=0) | |
| 871 self.parse_debug = options.debug | |
| 872 self.verbose = options.verbose | |
| 873 self.parse_errors = 0 | |
| 874 self.parse_warnings = 0 | |
| 875 self.ast = None | |
| 876 self.last = None | |
| 877 self.last_error_msg = None | |
| 878 self.last_error_lineno = 0 | |
| 879 self.last_error_pos = 0 | |
| 880 | |
| 881 # | |
| 882 # Tokenizer | |
| 883 # | |
| 884 # The token function returns the next token provided by IDLLexer for matching | |
| 885 # against the leaf paterns. | |
| 886 # | |
| 887 def token(self): | |
| 888 tok = self.lexobj.token() | |
| 889 if tok: | |
| 890 self.last = tok | |
| 891 return tok | |
| 892 | |
| 893 # | |
| 894 # BuildProduction | |
| 895 # | |
| 896 # Production is the set of items sent to a grammar rule resulting in a new | |
| 897 # item being returned. | |
| 898 # | |
| 899 # p - Is the Yacc production object containing the stack of items | |
| 900 # index - Index into the production of the name for the item being produced. | |
| 901 # cls - The type of item being producted | |
| 902 # childlist - The children of the new item | |
| 903 def BuildProduction(self, cls, p, index, childlist=None): | |
| 904 try: | |
| 905 if not childlist: | |
| 906 childlist = [] | |
| 907 | |
| 908 filename = self.lexobj.filename | |
| 909 lineno = p.lineno(index) | |
| 910 pos = p.lexpos(index) | |
| 911 out = IDLNode(cls, filename, lineno, pos, childlist) | |
| 912 return out | |
| 913 except: | |
| 914 print 'Exception while parsing:' | |
| 915 for num, item in enumerate(p): | |
| 916 print ' [%d] %s' % (num, ExpandProduction(item)) | |
| 917 if self.last: | |
| 918 print 'Last token: %s' % str(self.last) | |
| 919 raise | |
| 920 | |
| 921 def BuildNamed(self, cls, p, index, childlist=None): | |
| 922 childlist = ListFromConcat(childlist) | |
| 923 childlist.append(self.BuildAttribute('NAME', p[index])) | |
| 924 return self.BuildProduction(cls, p, index, childlist) | |
| 925 | |
| 926 def BuildComment(self, cls, p, index): | |
| 927 name = p[index] | |
| 928 | |
| 929 # Remove comment markers | |
| 930 lines = [] | |
| 931 if name[:2] == '//': | |
| 932 # For C++ style, remove any leading whitespace and the '//' marker from | |
| 933 # each line. | |
| 934 form = 'cc' | |
| 935 for line in name.split('\n'): | |
| 936 start = line.find('//') | |
| 937 lines.append(line[start+2:]) | |
| 938 else: | |
| 939 # For C style, remove ending '*/'' | |
| 940 form = 'c' | |
| 941 for line in name[:-2].split('\n'): | |
| 942 # Remove characters until start marker for this line '*' if found | |
| 943 # otherwise it should be blank. | |
| 944 offs = line.find('*') | |
| 945 if offs >= 0: | |
| 946 line = line[offs + 1:].rstrip() | |
| 947 else: | |
| 948 line = '' | |
| 949 lines.append(line) | |
| 950 name = '\n'.join(lines) | |
| 951 childlist = [self.BuildAttribute('NAME', name), | |
| 952 self.BuildAttribute('FORM', form)] | |
| 953 return self.BuildProduction(cls, p, index, childlist) | |
| 954 | |
| 955 # | |
| 956 # BuildError | |
| 957 # | |
| 958 # Build and Errror node as part of the recovery process. | |
| 959 # | |
| 960 # | |
| 961 def BuildError(self, p, prod): | |
| 962 name = self.BuildAttribute('NAME', self.last_error_msg) | |
| 963 line = self.BuildAttribute('LINE', self.last_error_lineno) | |
| 964 pos = self.BuildAttribute('POS', self.last_error_pos) | |
| 965 prod = self.BuildAttribute('PROD', prod) | |
| 966 | |
| 967 return self.BuildProduction('Error', p, 1, | |
| 968 ListFromConcat(name, line, pos, prod)) | |
| 969 | |
| 970 # | |
| 971 # BuildAttribute | |
| 972 # | |
| 973 # An ExtendedAttribute is a special production that results in a property | |
| 974 # which is applied to the adjacent item. Attributes have no children and | |
| 975 # instead represent key/value pairs. | |
| 976 # | |
| 977 def BuildAttribute(self, key, val): | |
| 978 return IDLAttribute(key, val) | |
| 979 | |
| 980 def BuildFalse(self, key): | |
| 981 return IDLAttribute(key, Boolean(False)) | |
| 982 | |
| 983 def BuildTrue(self, key): | |
| 984 return IDLAttribute(key, Boolean(True)) | |
| 985 | |
| 986 # | |
| 987 # ParseData | |
| 988 # | |
| 989 # Attempts to parse the current data loaded in the lexer. | |
| 990 # | |
| 991 def GetAST(self): | |
| 992 if not self.ast: | |
| 993 try: | |
| 994 self.ast = IDLNode('File', 0, 0, 0, self.yaccobj.parse(lexer=self)) | |
| 995 except lex.LexError as lexError: | |
| 996 ErrOut.Log(str(lexError)) | |
| 997 return None | |
| 998 return self.ast | |
| 999 | |
| 1000 | |
| 1001 def ParseFile(filename, options): | |
| 1002 """Parse a file and return a File type of node.""" | |
| 1003 with open(filename) as fileobject: | |
| 1004 parser = IDLParser(filename, fileobject.read(), options) | |
| 1005 if options.verbose: | |
| 1006 InfoOut.Log("Parsing %s" % filename) | |
| 1007 try: | |
| 1008 out = parser.GetAST() | |
| 1009 out.SetProperty('DATETIME', time.ctime(os.path.getmtime(filename))) | |
| 1010 out.SetProperty('ERRORS', parser.parse_errors + parser.lex_errors) | |
| 1011 return out | |
| 1012 | |
| 1013 except Exception as e: | |
| 1014 ErrOut.LogLine(filename, parser.last.lineno, parser.last.lexpos, | |
| 1015 'Internal parsing error - %s.' % str(e)) | |
| 1016 | |
| 1017 | |
| 1018 def ParseCommentTest(comment): | |
| 1019 comment = comment.strip() | |
| 1020 comments = comment.split(None, 1) | |
| 1021 return comments[0], comments[1] | |
| 1022 | |
| 1023 | |
| 1024 def TestNode(node, expected_errors): | |
| 1025 comments = node.GetListOf('Comment') | |
| 1026 errors = 0 | |
| 1027 | |
| 1028 for comment in comments: | |
| 1029 check, value = ParseCommentTest(comment.GetName()) | |
| 1030 if check == 'BUILD': | |
| 1031 if value != str(node): | |
| 1032 ErrOut.Log('Expecting %s, but found %s.\n' % (value, str(node))) | |
| 1033 errors = errors + 1 | |
| 1034 | |
| 1035 if check == 'ERROR': | |
| 1036 if expected_errors: | |
| 1037 err = expected_errors.pop(0) | |
| 1038 if err == value: | |
| 1039 continue | |
| 1040 ErrOut.Log('Mismatched error:\n %s\n %s\n' % (value, err)) | |
| 1041 else: | |
| 1042 ErrOut.Log('Unexpected error: %s.\n' % value) | |
| 1043 | |
| 1044 if check == 'PROP': | |
| 1045 key, expect = value.split('=') | |
| 1046 actual = str(node.GetProperty(key)) | |
| 1047 if expect != actual: | |
| 1048 ErrOut.Log('Mismatch property %s: %s vs %s.\n' % | |
| 1049 (key, expect, actual)) | |
| 1050 | |
| 1051 if check == 'TREE': | |
| 1052 quick = '\n'.join(node.Tree()) | |
| 1053 if value != quick: | |
| 1054 lineno = node.GetProperty('LINENO') | |
| 1055 ErrOut.Log('Misatched tree at line %d:\n%sVS\n%s' % | |
| 1056 (lineno, value, quick)) | |
| 1057 | |
| 1058 quick_lines = quick.split('\n') | |
| 1059 value_lines = value.split('\n') | |
| 1060 if len(quick_lines) != len(value_lines): | |
| 1061 continue | |
| 1062 | |
| 1063 # If the number of nodes match, the error is possible a subtle typo | |
| 1064 # so show the actual line that mismatched | |
| 1065 for x in range(len(quick_lines)): | |
| 1066 if quick_lines[x] != value_lines[x]: | |
| 1067 ErrOut.Log('AT node %d:\n>>%s<<\n>>%s<<' % | |
| 1068 (x, quick_lines[x], value_lines[x])) | |
| 1069 break | |
| 1070 | |
| 1071 return errors | |
| 1072 | |
| 1073 | |
| 1074 def TestFile(filename, options): | |
| 1075 errors = 0 | |
| 1076 | |
| 1077 # Capture errors instead of reporting them so we can compare them | |
| 1078 # with the expected errors. | |
| 1079 ErrOut.SetConsole(False) | |
| 1080 ErrOut.SetCapture(True) | |
| 1081 | |
| 1082 filenode = ParseFile(filename, options) | |
| 1083 | |
| 1084 # Renable output | |
|
sehr
2013/04/08 18:37:07
Re-enable
noelallen1
2013/04/11 21:52:09
Done.
| |
| 1085 ErrOut.SetConsole(True) | |
| 1086 ErrOut.SetCapture(False) | |
| 1087 | |
| 1088 # Fetch captured errors | |
| 1089 err_list = ErrOut.DrainLog() | |
| 1090 for node in filenode.GetChildren()[2:]: | |
| 1091 errors += TestNode(node, err_list) | |
| 1092 | |
| 1093 if err_list: | |
| 1094 ErrOut.Log('Unmatched errors:\n ' + '\n '.join(err_list)) | |
| 1095 | |
| 1096 if errors: | |
| 1097 ErrOut.Log('%d errors in %s.\n' % (errors, filename)) | |
| 1098 | |
| 1099 return errors | |
| 1100 | |
| 1101 | |
| 1102 def TestErrorFiles(filenames, options): | |
| 1103 filenames = ExpandGlobs(filenames) | |
| 1104 total_errs = 0 | |
| 1105 | |
| 1106 for filename in filenames: | |
| 1107 print 'Testing ' + filename | |
| 1108 errs = TestFile(filename, options) | |
| 1109 if errs: | |
| 1110 ErrOut.Log("%s test failed with %d error(s)." % (filename, errs)) | |
| 1111 total_errs += errs | |
| 1112 | |
| 1113 if total_errs: | |
| 1114 ErrOut.Log("Failed parsing test.") | |
| 1115 else: | |
| 1116 InfoOut.Log("Passed parsing test.") | |
| 1117 return total_errs | |
| 1118 | |
| 1119 | |
| 1120 def ExpandGlobs(inpaths): | |
| 1121 filenames = [] | |
| 1122 for fileorglob in inpaths: | |
| 1123 expanded_names = sorted(glob.glob(fileorglob)) | |
| 1124 for srcpath in expanded_names: | |
| 1125 srcpath = os.path.normpath(srcpath) | |
| 1126 filenames.append(srcpath) | |
| 1127 return filenames | |
| 1128 | |
| 1129 | |
| 1130 def ParseFiles(filenames, options): | |
| 1131 filenames = ExpandGlobs(filenames) | |
| 1132 filenodes = [ParseFile(name, options) for name in filenames] | |
| 1133 return IDLNode('AST', 'AST', 0, 0, filenodes) | |
| 1134 | |
| 1135 | |
| 1136 def AddOptions(parser): | |
| 1137 parser.add_option('--debug', help='Enable debugging output for productions', | |
| 1138 action='store_true', default=False) | |
| 1139 parser.add_option('--dump', help='Dump the tree when done.', | |
| 1140 action='store_true', default=False) | |
| 1141 parser.add_option('--test', help='Run all built in tests.', | |
| 1142 action='store_true', default=False) | |
| 1143 parser.add_option('--verbose', help='Set verbose output.', | |
| 1144 action='store_true', default=False) | |
| 1145 | |
| 1146 | |
| 1147 def Main(args): | |
| 1148 parser = optparse.OptionParser() | |
| 1149 AddOptions(parser) | |
| 1150 | |
| 1151 # If no arguments are provided, run tests. | |
| 1152 if len(args) == 0: | |
| 1153 args = ['--test', 'test_parser/*.idl'] | |
|
sehr
2013/04/08 18:37:07
Again, should we be encoding paths here?
| |
| 1154 | |
| 1155 options, filenames = parser.parse_args(args) | |
| 1156 if options.test: | |
| 1157 errs = TestErrorFiles(filenames, options) | |
| 1158 if errs: | |
| 1159 ErrOut.Log("Parser failed with %d errors." % errs) | |
| 1160 return -1 | |
| 1161 return 0 | |
| 1162 | |
| 1163 ast = ParseFiles(filenames, options) | |
| 1164 if options.dump: | |
| 1165 print ast.Tree() | |
| 1166 | |
| 1167 errs = ast.GetProperty('ERRORS') | |
| 1168 if errs: | |
| 1169 ErrOut.Log('Found %d error(s).' % errs) | |
| 1170 | |
| 1171 InfoOut.Log("%d files processed." % len(filenames)) | |
| 1172 return errs | |
| 1173 | |
| 1174 | |
| 1175 if __name__ == '__main__': | |
| 1176 sys.exit(Main(sys.argv[1:])) | |
| 1177 | |
| OLD | NEW |