Pass CreateDirectory errors up to IndexedDB.
[chromium-blink-merge.git] / tools / idl_parser / idl_parser.py
blob3e57295949ddfc6bdd91154817c9bc1ec4accbf6
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.
6 """ Parser for PPAPI IDL """
9 # IDL Parser
11 # The parser is uses the PLY yacc library to build a set of parsing rules based
12 # on WebIDL.
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/
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.
26 # Disable check for line length and Member as Function due to how grammar rules
27 # are defined with PLY
29 # pylint: disable=R0201
30 # pylint: disable=C0301
32 import os.path
33 import sys
34 import time
36 from idl_lexer import IDLLexer
37 from idl_node import IDLAttribute, IDLNode
39 from ply import lex
40 from ply import yacc
43 # ERROR_REMAP
45 # Maps the standard error formula into a more friendly error message.
47 ERROR_REMAP = {
48 'Unexpected ")" after "(".' : 'Empty argument list.',
49 'Unexpected ")" after ",".' : 'Missing argument.',
50 'Unexpected "}" after ",".' : 'Trailing comma in block.',
51 'Unexpected "}" after "{".' : 'Unexpected empty block.',
52 'Unexpected comment after "}".' : 'Unexpected trailing comment.',
53 'Unexpected "{" after keyword "enum".' : 'Enum missing name.',
54 'Unexpected "{" after keyword "struct".' : 'Struct missing name.',
55 'Unexpected "{" after keyword "interface".' : 'Interface missing name.',
59 def Boolean(val):
60 """Convert to strict boolean type."""
61 if val:
62 return True
63 return False
66 def ListFromConcat(*items):
67 """Generate list by concatenating inputs"""
68 itemsout = []
69 for item in items:
70 if item is None:
71 continue
72 if type(item) is not type([]):
73 itemsout.append(item)
74 else:
75 itemsout.extend(item)
77 return itemsout
79 def ExpandProduction(p):
80 if type(p) == list:
81 return '[' + ', '.join([ExpandProduction(x) for x in p]) + ']'
82 if type(p) == IDLNode:
83 return 'Node:' + str(p)
84 if type(p) == IDLAttribute:
85 return 'Attr:' + str(p)
86 if type(p) == str:
87 return 'str:' + p
88 return '%s:%s' % (p.__class__.__name__, str(p))
90 # TokenTypeName
92 # Generate a string which has the type and value of the token.
94 def TokenTypeName(t):
95 if t.type == 'SYMBOL':
96 return 'symbol %s' % t.value
97 if t.type in ['HEX', 'INT', 'OCT', 'FLOAT']:
98 return 'value %s' % t.value
99 if t.type == 'string' :
100 return 'string "%s"' % t.value
101 if t.type == 'COMMENT' :
102 return 'comment'
103 if t.type == t.value:
104 return '"%s"' % t.value
105 if t.type == ',':
106 return 'Comma'
107 if t.type == 'identifier':
108 return 'identifier "%s"' % t.value
109 return 'keyword "%s"' % t.value
113 # IDL Parser
115 # The Parser inherits the from the Lexer to provide PLY with the tokenizing
116 # definitions. Parsing patterns are encoded as functions where p_<name> is
117 # is called any time a patern matching the function documentation is found.
118 # Paterns are expressed in the form of:
119 # """ <new item> : <item> ....
120 # | <item> ...."""
122 # Where new item is the result of a match against one or more sets of items
123 # separated by the "|".
125 # The function is called with an object 'p' where p[0] is the output object
126 # and p[n] is the set of inputs for positive values of 'n'. Len(p) can be
127 # used to distinguish between multiple item sets in the pattern.
129 # For more details on parsing refer to the PLY documentation at
130 # http://www.dabeaz.com/ply/
132 # The parser is based on the WebIDL standard. See:
133 # http://www.w3.org/TR/WebIDL/#idl-grammar
135 # The various productions are annotated so that the WHOLE number greater than
136 # zero in the comment denotes the matching WebIDL grammar definition.
138 # Productions with a fractional component in the comment denote additions to
139 # the WebIDL spec, such as comments.
143 class IDLParser(object):
145 # We force all input files to start with two comments. The first comment is a
146 # Copyright notice followed by a file comment and finally by file level
147 # productions.
149 # [0] Insert a TOP definition for Copyright and Comments
150 def p_Top(self, p):
151 """Top : COMMENT COMMENT Definitions"""
152 Copyright = self.BuildComment('Copyright', p, 1)
153 Filedoc = self.BuildComment('Comment', p, 2)
154 p[0] = ListFromConcat(Copyright, Filedoc, p[3])
156 # [0.1] Add support for Multiple COMMENTS
157 def p_Comments(self, p):
158 """Comments : CommentsRest"""
159 if len(p) > 1:
160 p[0] = p[1]
162 # [0.2] Produce a COMMENT and aggregate sibling comments
163 def p_CommentsRest(self, p):
164 """CommentsRest : COMMENT CommentsRest
165 | """
166 if len(p) > 1:
167 p[0] = ListFromConcat(self.BuildComment('Comment', p, 1), p[2])
171 #The parser is based on the WebIDL standard. See:
172 # http://www.w3.org/TR/WebIDL/#idl-grammar
174 # [1]
175 def p_Definitions(self, p):
176 """Definitions : ExtendedAttributeList Definition Definitions
177 | """
178 if len(p) > 1:
179 p[2].AddChildren(p[1])
180 p[0] = ListFromConcat(p[2], p[3])
182 # [2] Add INLINE definition
183 def p_Definition(self, p):
184 """Definition : CallbackOrInterface
185 | Partial
186 | Dictionary
187 | Exception
188 | Enum
189 | Typedef
190 | ImplementsStatement"""
191 p[0] = p[1]
193 # [2.1] Error recovery for definition
194 def p_DefinitionError(self, p):
195 """Definition : error ';'"""
196 p[0] = self.BuildError(p, 'Definition')
198 # [3]
199 def p_CallbackOrInterface(self, p):
200 """CallbackOrInterface : CALLBACK CallbackRestOrInterface
201 | Interface"""
202 if len(p) > 2:
203 p[0] = p[2]
204 else:
205 p[0] = p[1]
207 # [4]
208 def p_CallbackRestOrInterface(self, p):
209 """CallbackRestOrInterface : CallbackRest
210 | Interface"""
211 p[0] = p[1]
213 # [5]
214 def p_Interface(self, p):
215 """Interface : INTERFACE identifier Inheritance '{' InterfaceMembers '}' ';'"""
216 p[0] = self.BuildNamed('Interface', p, 2, ListFromConcat(p[3], p[5]))
218 # [6] Error recovery for PARTIAL
219 def p_Partial(self, p):
220 """Partial : PARTIAL PartialDefinition"""
221 p[2].AddChildren(self.BuildTrue('Partial'))
222 p[0] = p[2]
224 # [6.1] Error recovery for Enums
225 def p_PartialError(self, p):
226 """Partial : PARTIAL error"""
227 p[0] = self.BuildError(p, 'Partial')
229 # [7]
230 def p_PartialDefinition(self, p):
231 """PartialDefinition : PartialDictionary
232 | PartialInterface"""
233 p[0] = p[1]
235 # [8]
236 def p_PartialInterface(self, p):
237 """PartialInterface : INTERFACE identifier '{' InterfaceMembers '}' ';'"""
238 p[0] = self.BuildNamed('Interface', p, 2, p[4])
240 # [9]
241 def p_InterfaceMembers(self, p):
242 """InterfaceMembers : ExtendedAttributeList InterfaceMember InterfaceMembers
243 |"""
244 if len(p) > 1:
245 p[2].AddChildren(p[1])
246 p[0] = ListFromConcat(p[2], p[3])
248 # [10]
249 def p_InterfaceMember(self, p):
250 """InterfaceMember : Const
251 | AttributeOrOperation"""
252 p[0] = p[1]
254 # [11]
255 def p_Dictionary(self, p):
256 """Dictionary : DICTIONARY identifier Inheritance '{' DictionaryMembers '}' ';'"""
257 p[0] = self.BuildNamed('Dictionary', p, 2, ListFromConcat(p[3], p[5]))
259 # [11.1] Error recovery for regular Dictionary
260 def p_DictionaryError(self, p):
261 """Dictionary : DICTIONARY error ';'"""
262 p[0] = self.BuildError(p, 'Dictionary')
264 # [12]
265 def p_DictionaryMembers(self, p):
266 """DictionaryMembers : ExtendedAttributeList DictionaryMember DictionaryMembers
267 |"""
268 if len(p) > 1:
269 p[2].AddChildren(p[1])
270 p[0] = ListFromConcat(p[2], p[3])
272 # [13]
273 def p_DictionaryMember(self, p):
274 """DictionaryMember : Type identifier Default ';'"""
275 p[0] = self.BuildNamed('Key', p, 2, ListFromConcat(p[1], p[3]))
277 # [14]
278 def p_PartialDictionary(self, p):
279 """PartialDictionary : DICTIONARY identifier '{' DictionaryMembers '}' ';'"""
280 partial = self.BuildTrue('Partial')
281 p[0] = self.BuildNamed('Dictionary', p, 2, ListFromConcat(p[4], partial))
283 # [14.1] Error recovery for Partial Dictionary
284 def p_PartialDictionaryError(self, p):
285 """PartialDictionary : DICTIONARY error ';'"""
286 p[0] = self.BuildError(p, 'PartialDictionary')
288 # [15]
289 def p_Default(self, p):
290 """Default : '=' DefaultValue
291 |"""
292 if len(p) > 1:
293 p[0] = self.BuildProduction('Default', p, 2, p[2])
295 # [16]
296 def p_DefaultValue(self, p):
297 """DefaultValue : ConstValue
298 | string"""
299 if type(p[1]) == str:
300 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'DOMString'),
301 self.BuildAttribute('NAME', p[1]))
302 else:
303 p[0] = p[1]
305 # [17]
306 def p_Exception(self, p):
307 """Exception : EXCEPTION identifier Inheritance '{' ExceptionMembers '}' ';'"""
308 p[0] = self.BuildNamed('Exception', p, 2, ListFromConcat(p[3], p[5]))
310 # [18]
311 def p_ExceptionMembers(self, p):
312 """ExceptionMembers : ExtendedAttributeList ExceptionMember ExceptionMembers
313 |"""
314 if len(p) > 1:
315 p[2].AddChildren(p[1])
316 p[0] = ListFromConcat(p[2], p[3])
318 # [18.1] Error recovery for ExceptionMembers
319 def p_ExceptionMembersError(self, p):
320 """ExceptionMembers : error"""
321 p[0] = self.BuildError(p, 'ExceptionMembers')
323 # [19]
324 def p_Inheritance(self, p):
325 """Inheritance : ':' identifier
326 |"""
327 if len(p) > 1:
328 p[0] = self.BuildNamed('Inherit', p, 2)
330 # [20]
331 def p_Enum(self, p):
332 """Enum : ENUM identifier '{' EnumValueList '}' ';'"""
333 p[0] = self.BuildNamed('Enum', p, 2, p[4])
335 # [20.1] Error recovery for Enums
336 def p_EnumError(self, p):
337 """Enum : ENUM error ';'"""
338 p[0] = self.BuildError(p, 'Enum')
340 # [21]
341 def p_EnumValueList(self, p):
342 """EnumValueList : ExtendedAttributeList string EnumValues"""
343 enum = self.BuildNamed('EnumItem', p, 2, p[1])
344 p[0] = ListFromConcat(enum, p[3])
346 # [22]
347 def p_EnumValues(self, p):
348 """EnumValues : ',' ExtendedAttributeList string EnumValues
349 |"""
350 if len(p) > 1:
351 enum = self.BuildNamed('EnumItem', p, 3, p[2])
352 p[0] = ListFromConcat(enum, p[4])
354 # [23]
355 def p_CallbackRest(self, p):
356 """CallbackRest : identifier '=' ReturnType '(' ArgumentList ')' ';'"""
357 arguments = self.BuildProduction('Arguments', p, 4, p[5])
358 p[0] = self.BuildNamed('Callback', p, 1, ListFromConcat(p[3], arguments))
360 # [24]
361 def p_Typedef(self, p):
362 """Typedef : TYPEDEF ExtendedAttributeList Type identifier ';'"""
363 p[0] = self.BuildNamed('Typedef', p, 4, ListFromConcat(p[2], p[3]))
365 # [24.1] Error recovery for Typedefs
366 def p_TypedefError(self, p):
367 """Typedef : TYPEDEF error ';'"""
368 p[0] = self.BuildError(p, 'Typedef')
370 # [25]
371 def p_ImplementsStatement(self, p):
372 """ImplementsStatement : identifier IMPLEMENTS identifier ';'"""
373 name = self.BuildAttribute('REFERENCE', p[3])
374 p[0] = self.BuildNamed('Implements', p, 1, name)
376 # [26]
377 def p_Const(self, p):
378 """Const : CONST ConstType identifier '=' ConstValue ';'"""
379 value = self.BuildProduction('Value', p, 5, p[5])
380 p[0] = self.BuildNamed('Const', p, 3, ListFromConcat(p[2], value))
382 # [27]
383 def p_ConstValue(self, p):
384 """ConstValue : BooleanLiteral
385 | FloatLiteral
386 | integer
387 | null"""
388 if type(p[1]) == str:
389 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'integer'),
390 self.BuildAttribute('NAME', p[1]))
391 else:
392 p[0] = p[1]
394 # [27.1] Add definition for NULL
395 def p_null(self, p):
396 """null : NULL"""
397 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'NULL'),
398 self.BuildAttribute('NAME', 'NULL'))
400 # [28]
401 def p_BooleanLiteral(self, p):
402 """BooleanLiteral : TRUE
403 | FALSE"""
404 value = self.BuildAttribute('NAME', Boolean(p[1] == 'true'))
405 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'boolean'), value)
407 # [29]
408 def p_FloatLiteral(self, p):
409 """FloatLiteral : float
410 | '-' INFINITY
411 | INFINITY
412 | NAN """
413 if len(p) > 2:
414 val = '-Infinity'
415 else:
416 val = p[1]
417 p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'float'),
418 self.BuildAttribute('VALUE', val))
420 # [30]
421 def p_AttributeOrOperation(self, p):
422 """AttributeOrOperation : STRINGIFIER StringifierAttributeOrOperation
423 | Attribute
424 | Operation"""
425 if len(p) > 2:
426 p[0] = p[2]
427 else:
428 p[0] = p[1]
430 # [31]
431 def p_StringifierAttributeOrOperation(self, p):
432 """StringifierAttributeOrOperation : Attribute
433 | OperationRest
434 | ';'"""
435 if p[1] == ';':
436 p[0] = self.BuildAttribute('STRINGIFIER', Boolean(True))
437 else:
438 p[0] = ListFromConcat(self.BuildAttribute('STRINGIFIER', p[1]), p[1])
440 # [32]
441 def p_Attribute(self, p):
442 """Attribute : Inherit ReadOnly ATTRIBUTE Type identifier ';'"""
443 p[0] = self.BuildNamed('Attribute', p, 5,
444 ListFromConcat(p[1], p[2], p[4]))
446 # [33]
447 def p_Inherit(self, p):
448 """Inherit : INHERIT
449 |"""
450 if len(p) > 1:
451 p[0] = self.BuildTrue('INHERIT')
453 # [34]
454 def p_ReadOnly(self, p):
455 """ReadOnly : READONLY
456 |"""
457 if len(p) > 1:
458 p[0] = self.BuildTrue('READONLY')
460 # [35]
461 def p_Operation(self, p):
462 """Operation : Qualifiers OperationRest"""
463 p[2].AddChildren(p[1])
464 p[0] = p[2]
466 # [36]
467 def p_Qualifiers(self, p):
468 """Qualifiers : STATIC
469 | Specials"""
470 if p[1] == 'static':
471 p[0] = self.BuildTrue('STATIC')
472 else:
473 p[0] = p[1]
475 # [37]
476 def p_Specials(self, p):
477 """Specials : Special Specials
478 | """
479 if len(p) > 1:
480 p[0] = ListFromConcat(p[1], p[2])
482 # [38]
483 def p_Special(self, p):
484 """Special : GETTER
485 | SETTER
486 | CREATOR
487 | DELETER
488 | LEGACYCALLER"""
489 p[0] = self.BuildTrue(p[1].upper())
492 # [39]
493 def p_OperationRest(self, p):
494 """OperationRest : ReturnType OptionalIdentifier '(' ArgumentList ')' ';'"""
495 arguments = self.BuildProduction('Arguments', p, 3, p[4])
496 p[0] = self.BuildNamed('Operation', p, 2, ListFromConcat(p[1], arguments))
498 # [40]
499 def p_OptionalIdentifier(self, p):
500 """OptionalIdentifier : identifier
501 |"""
502 if len(p) > 1:
503 p[0] = p[1]
504 else:
505 p[0] = '_unnamed_'
507 # [41]
508 def p_ArgumentList(self, p):
509 """ArgumentList : Argument Arguments
510 |"""
511 if len(p) > 1:
512 p[0] = ListFromConcat(p[1], p[2])
514 # [41.1] ArgumentList error recovery
515 def p_ArgumentListError(self, p):
516 """ArgumentList : error """
517 p[0] = self.BuildError(p, 'ArgumentList')
519 # [42]
520 def p_Arguments(self, p):
521 """Arguments : ',' Argument Arguments
522 |"""
523 if len(p) > 1:
524 p[0] = ListFromConcat(p[2], p[3])
526 # [43]
527 def p_Argument(self, p):
528 """Argument : ExtendedAttributeList OptionalOrRequiredArgument"""
529 p[2].AddChildren(p[1])
530 p[0] = p[2]
533 # [44]
534 def p_OptionalOrRequiredArgument(self, p):
535 """OptionalOrRequiredArgument : OPTIONAL Type ArgumentName Default
536 | Type Ellipsis ArgumentName"""
537 if len(p) > 4:
538 arg = self.BuildNamed('Argument', p, 3, ListFromConcat(p[2], p[4]))
539 arg.AddChildren(self.BuildTrue('OPTIONAL'))
540 else:
541 arg = self.BuildNamed('Argument', p, 3, ListFromConcat(p[1], p[2]))
542 p[0] = arg
544 # [45]
545 def p_ArgumentName(self, p):
546 """ArgumentName : ArgumentNameKeyword
547 | identifier"""
548 p[0] = p[1]
550 # [46]
551 def p_Ellipsis(self, p):
552 """Ellipsis : ELLIPSIS
553 |"""
554 if len(p) > 1:
555 p[0] = self.BuildNamed('Argument', p, 1)
556 p[0].AddChildren(self.BuildTrue('ELLIPSIS'))
558 # [47]
559 def p_ExceptionMember(self, p):
560 """ExceptionMember : Const
561 | ExceptionField"""
562 p[0] = p[1]
564 # [48]
565 def p_ExceptionField(self, p):
566 """ExceptionField : Type identifier ';'"""
567 p[0] = self.BuildNamed('ExceptionField', p, 2, p[1])
569 # [48.1] Error recovery for ExceptionMembers
570 def p_ExceptionFieldError(self, p):
571 """ExceptionField : error"""
572 p[0] = self.BuildError(p, 'ExceptionField')
574 # [49] Add optional comment field
575 def p_ExtendedAttributeList(self, p):
576 """ExtendedAttributeList : Comments '[' ExtendedAttribute ExtendedAttributes ']'
577 | Comments """
578 if len(p) > 2:
579 items = ListFromConcat(p[3], p[4])
580 attribs = self.BuildProduction('ExtAttributes', p, 2, items)
581 p[0] = ListFromConcat(p[1], attribs)
582 else:
583 p[0] = p[1]
585 # [50]
586 def p_ExtendedAttributes(self, p):
587 """ExtendedAttributes : ',' ExtendedAttribute ExtendedAttributes
588 |"""
589 if len(p) > 1:
590 p[0] = ListFromConcat(p[2], p[3])
592 # We only support:
593 # [ identifier ]
594 # [ identifier = identifier ]
595 # [ identifier ( ArgumentList )]
596 # [ identifier = identifier ( ArgumentList )]
597 # [51] map directly to 74-77
598 # [52-54, 56] are unsupported
599 def p_ExtendedAttribute(self, p):
600 """ExtendedAttribute : ExtendedAttributeNoArgs
601 | ExtendedAttributeArgList
602 | ExtendedAttributeIdent
603 | ExtendedAttributeNamedArgList"""
604 p[0] = p[1]
606 # [55]
607 def p_ArgumentNameKeyword(self, p):
608 """ArgumentNameKeyword : ATTRIBUTE
609 | CALLBACK
610 | CONST
611 | CREATOR
612 | DELETER
613 | DICTIONARY
614 | ENUM
615 | EXCEPTION
616 | GETTER
617 | IMPLEMENTS
618 | INHERIT
619 | LEGACYCALLER
620 | PARTIAL
621 | SETTER
622 | STATIC
623 | STRINGIFIER
624 | TYPEDEF
625 | UNRESTRICTED"""
626 p[0] = p[1]
628 # [57]
629 def p_Type(self, p):
630 """Type : SingleType
631 | UnionType TypeSuffix"""
632 if len(p) == 2:
633 p[0] = self.BuildProduction('Type', p, 1, p[1])
634 else:
635 p[0] = self.BuildProduction('Type', p, 1, ListFromConcat(p[1], p[2]))
637 # [58]
638 def p_SingleType(self, p):
639 """SingleType : NonAnyType
640 | ANY TypeSuffixStartingWithArray"""
641 if len(p) == 2:
642 p[0] = p[1]
643 else:
644 p[0] = ListFromConcat(self.BuildProduction('Any', p, 1), p[2])
646 # [59]
647 def p_UnionType(self, p):
648 """UnionType : '(' UnionMemberType OR UnionMemberType UnionMemberTypes ')'"""
650 # [60]
651 def p_UnionMemberType(self, p):
652 """UnionMemberType : NonAnyType
653 | UnionType TypeSuffix
654 | ANY '[' ']' TypeSuffix"""
655 # [61]
656 def p_UnionMemberTypes(self, p):
657 """UnionMemberTypes : OR UnionMemberType UnionMemberTypes
658 |"""
660 # [62] Moved DATE, DOMSTRING, OBJECT to PrimitiveType
661 def p_NonAnyType(self, p):
662 """NonAnyType : PrimitiveType TypeSuffix
663 | identifier TypeSuffix
664 | SEQUENCE '<' Type '>' Null"""
665 if len(p) == 3:
666 if type(p[1]) == str:
667 typeref = self.BuildNamed('Typeref', p, 1)
668 else:
669 typeref = p[1]
670 p[0] = ListFromConcat(typeref, p[2])
672 if len(p) == 6:
673 p[0] = self.BuildProduction('Sequence', p, 1, ListFromConcat(p[3], p[5]))
676 # [63]
677 def p_ConstType(self, p):
678 """ConstType : PrimitiveType Null
679 | identifier Null"""
680 if type(p[1]) == str:
681 p[0] = self.BuildNamed('Typeref', p, 1, p[2])
682 else:
683 p[1].AddChildren(p[2])
684 p[0] = p[1]
687 # [64]
688 def p_PrimitiveType(self, p):
689 """PrimitiveType : UnsignedIntegerType
690 | UnrestrictedFloatType
691 | BOOLEAN
692 | BYTE
693 | OCTET
694 | DOMSTRING
695 | DATE
696 | OBJECT"""
697 if type(p[1]) == str:
698 p[0] = self.BuildNamed('PrimitiveType', p, 1)
699 else:
700 p[0] = p[1]
703 # [65]
704 def p_UnrestrictedFloatType(self, p):
705 """UnrestrictedFloatType : UNRESTRICTED FloatType
706 | FloatType"""
707 if len(p) == 2:
708 typeref = self.BuildNamed('PrimitiveType', p, 1)
709 else:
710 typeref = self.BuildNamed('PrimitiveType', p, 2)
711 typeref.AddChildren(self.BuildTrue('UNRESTRICTED'))
712 p[0] = typeref
715 # [66]
716 def p_FloatType(self, p):
717 """FloatType : FLOAT
718 | DOUBLE"""
719 p[0] = p[1]
721 # [67]
722 def p_UnsignedIntegerType(self, p):
723 """UnsignedIntegerType : UNSIGNED IntegerType
724 | IntegerType"""
725 if len(p) == 2:
726 p[0] = p[1]
727 else:
728 p[0] = 'unsigned ' + p[2]
730 # [68]
731 def p_IntegerType(self, p):
732 """IntegerType : SHORT
733 | LONG OptionalLong"""
734 if len(p) == 2:
735 p[0] = p[1]
736 else:
737 p[0] = p[1] + p[2]
739 # [69]
740 def p_OptionalLong(self, p):
741 """OptionalLong : LONG
742 | """
743 if len(p) > 1:
744 p[0] = ' ' + p[1]
745 else:
746 p[0] = ''
749 # [70] Add support for sized array
750 def p_TypeSuffix(self, p):
751 """TypeSuffix : '[' integer ']' TypeSuffix
752 | '[' ']' TypeSuffix
753 | '?' TypeSuffixStartingWithArray
754 |"""
755 if len(p) == 5:
756 p[0] = self.BuildNamed('Array', p, 2, p[4])
758 if len(p) == 4:
759 p[0] = self.BuildProduction('Array', p, 1, p[3])
761 if len(p) == 3:
762 p[0] = ListFromConcat(self.BuildTrue('NULLABLE'), p[2])
765 # [71]
766 def p_TypeSuffixStartingWithArray(self, p):
767 """TypeSuffixStartingWithArray : '[' ']' TypeSuffix
768 | """
769 if len(p) > 1:
770 p[0] = self.BuildProduction('Array', p, 0, p[3])
772 # [72]
773 def p_Null(self, p):
774 """Null : '?'
775 |"""
776 if len(p) > 1:
777 p[0] = self.BuildTrue('NULLABLE')
779 # [73]
780 def p_ReturnType(self, p):
781 """ReturnType : Type
782 | VOID"""
783 if p[1] == 'void':
784 p[0] = self.BuildProduction('Type', p, 1)
785 p[0].AddChildren(self.BuildNamed('PrimitiveType', p, 1))
786 else:
787 p[0] = p[1]
789 # [74]
790 def p_ExtendedAttributeNoArgs(self, p):
791 """ExtendedAttributeNoArgs : identifier"""
792 p[0] = self.BuildNamed('ExtAttribute', p, 1)
794 # [75]
795 def p_ExtendedAttributeArgList(self, p):
796 """ExtendedAttributeArgList : identifier '(' ArgumentList ')'"""
797 arguments = self.BuildProduction('Arguments', p, 2, p[3])
798 p[0] = self.BuildNamed('ExtAttribute', p, 1, arguments)
800 # [76]
801 def p_ExtendedAttributeIdent(self, p):
802 """ExtendedAttributeIdent : identifier '=' identifier"""
803 value = self.BuildAttribute('VALUE', p[3])
804 p[0] = self.BuildNamed('ExtAttribute', p, 1, value)
806 # [77]
807 def p_ExtendedAttributeNamedArgList(self, p):
808 """ExtendedAttributeNamedArgList : identifier '=' identifier '(' ArgumentList ')'"""
809 args = self.BuildProduction('Arguments', p, 4, p[5])
810 value = self.BuildNamed('Call', p, 3, args)
811 p[0] = self.BuildNamed('ExtAttribute', p, 1, value)
814 # Parser Errors
816 # p_error is called whenever the parser can not find a pattern match for
817 # a set of items from the current state. The p_error function defined here
818 # is triggered logging an error, and parsing recovery happens as the
819 # p_<type>_error functions defined above are called. This allows the parser
820 # to continue so as to capture more than one error per file.
822 def p_error(self, t):
823 if t:
824 lineno = t.lineno
825 pos = t.lexpos
826 prev = self.yaccobj.symstack[-1]
827 if type(prev) == lex.LexToken:
828 msg = "Unexpected %s after %s." % (
829 TokenTypeName(t), TokenTypeName(prev))
830 else:
831 msg = "Unexpected %s." % (t.value)
832 else:
833 last = self.LastToken()
834 lineno = last.lineno
835 pos = last.lexpos
836 msg = "Unexpected end of file after %s." % TokenTypeName(last)
837 self.yaccobj.restart()
839 # Attempt to remap the error to a friendlier form
840 if msg in ERROR_REMAP:
841 msg = ERROR_REMAP[msg]
843 self._last_error_msg = msg
844 self._last_error_lineno = lineno
845 self._last_error_pos = pos
847 def Warn(self, node, msg):
848 sys.stdout.write(node.GetLogLine(msg))
849 self.parse_warnings += 1
851 def LastToken(self):
852 return self.lexer.last
854 def __init__(self, lexer, verbose=False, debug=False, mute_error=False):
855 self.lexer = lexer
856 self.tokens = lexer.KnownTokens()
857 self.yaccobj = yacc.yacc(module=self, tabmodule=None, debug=False,
858 optimize=0, write_tables=0)
859 self.parse_debug = debug
860 self.verbose = verbose
861 self.mute_error = mute_error
862 self._parse_errors = 0
863 self._parse_warnings = 0
864 self._last_error_msg = None
865 self._last_error_lineno = 0
866 self._last_error_pos = 0
870 # BuildProduction
872 # Production is the set of items sent to a grammar rule resulting in a new
873 # item being returned.
875 # p - Is the Yacc production object containing the stack of items
876 # index - Index into the production of the name for the item being produced.
877 # cls - The type of item being producted
878 # childlist - The children of the new item
879 def BuildProduction(self, cls, p, index, childlist=None):
880 try:
881 if not childlist:
882 childlist = []
884 filename = self.lexer.Lexer().filename
885 lineno = p.lineno(index)
886 pos = p.lexpos(index)
887 out = IDLNode(cls, filename, lineno, pos, childlist)
888 return out
889 except:
890 print 'Exception while parsing:'
891 for num, item in enumerate(p):
892 print ' [%d] %s' % (num, ExpandProduction(item))
893 if self.LastToken():
894 print 'Last token: %s' % str(self.LastToken())
895 raise
897 def BuildNamed(self, cls, p, index, childlist=None):
898 childlist = ListFromConcat(childlist)
899 childlist.append(self.BuildAttribute('NAME', p[index]))
900 return self.BuildProduction(cls, p, index, childlist)
902 def BuildComment(self, cls, p, index):
903 name = p[index]
905 # Remove comment markers
906 lines = []
907 if name[:2] == '//':
908 # For C++ style, remove any leading whitespace and the '//' marker from
909 # each line.
910 form = 'cc'
911 for line in name.split('\n'):
912 start = line.find('//')
913 lines.append(line[start+2:])
914 else:
915 # For C style, remove ending '*/''
916 form = 'c'
917 for line in name[:-2].split('\n'):
918 # Remove characters until start marker for this line '*' if found
919 # otherwise it should be blank.
920 offs = line.find('*')
921 if offs >= 0:
922 line = line[offs + 1:].rstrip()
923 else:
924 line = ''
925 lines.append(line)
926 name = '\n'.join(lines)
927 childlist = [self.BuildAttribute('NAME', name),
928 self.BuildAttribute('FORM', form)]
929 return self.BuildProduction(cls, p, index, childlist)
932 # BuildError
934 # Build and Errror node as part of the recovery process.
937 def BuildError(self, p, prod):
938 self._parse_errors += 1
939 name = self.BuildAttribute('NAME', self._last_error_msg)
940 line = self.BuildAttribute('LINE', self._last_error_lineno)
941 pos = self.BuildAttribute('POS', self._last_error_pos)
942 prod = self.BuildAttribute('PROD', prod)
944 node = self.BuildProduction('Error', p, 1,
945 ListFromConcat(name, line, pos, prod))
946 if not self.mute_error:
947 node.Error(self._last_error_msg)
949 return node
952 # BuildAttribute
954 # An ExtendedAttribute is a special production that results in a property
955 # which is applied to the adjacent item. Attributes have no children and
956 # instead represent key/value pairs.
958 def BuildAttribute(self, key, val):
959 return IDLAttribute(key, val)
961 def BuildFalse(self, key):
962 return IDLAttribute(key, Boolean(False))
964 def BuildTrue(self, key):
965 return IDLAttribute(key, Boolean(True))
967 def GetErrors(self):
968 return self._parse_errors + self.lexer._lex_errors
971 # ParseData
973 # Attempts to parse the current data loaded in the lexer.
975 def ParseText(self, filename, data):
976 self._parse_errors = 0
977 self._parse_warnings = 0
978 self._last_error_msg = None
979 self._last_error_lineno = 0
980 self._last_error_pos = 0
982 try:
983 self.lexer.Tokenize(data, filename)
984 nodes = self.yaccobj.parse(lexer=self.lexer)
985 name = self.BuildAttribute('NAME', filename)
986 return IDLNode('File', filename, 0, 0, nodes + [name])
988 except lex.LexError as lexError:
989 sys.stderr.write('Error in token: %s\n' % str(lexError))
990 return None
994 def ParseFile(parser, filename):
995 """Parse a file and return a File type of node."""
996 with open(filename) as fileobject:
997 try:
998 out = parser.ParseText(filename, fileobject.read())
999 out.SetProperty('DATETIME', time.ctime(os.path.getmtime(filename)))
1000 out.SetProperty('ERRORS', parser.GetErrors())
1001 return out
1003 except Exception as e:
1004 last = parser.LastToken()
1005 sys.stderr.write('%s(%d) : Internal parsing error - %s.' % (
1006 filename, last.lineno, str(e)))
1009 def main(argv):
1010 nodes = []
1011 parser = IDLParser(IDLLexer())
1012 errors = 0
1013 for filename in argv:
1014 filenode = ParseFile(parser, filename)
1015 errors += filenode.GetProperty('ERRORS')
1016 nodes.append(filenode)
1018 ast = IDLNode('AST', '__AST__', 0, 0, nodes)
1020 print '\n'.join(ast.Tree(accept_props=['PROD']))
1021 if errors:
1022 print '\nFound %d errors.\n' % errors
1025 return errors
1028 if __name__ == '__main__':
1029 sys.exit(main(sys.argv[1:]))