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 """
11 # The parser is uses the PLY yacc library to build a set of parsing rules based
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
36 from idl_lexer
import IDLLexer
37 from idl_node
import IDLAttribute
, IDLNode
45 # Maps the standard error formula into a more friendly error message.
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.',
60 """Convert to strict boolean type."""
66 def ListFromConcat(*items
):
67 """Generate list by concatenating inputs"""
72 if type(item
) is not type([]):
79 def ExpandProduction(p
):
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
)
88 return '%s:%s' % (p
.__class
__.__name
__, str(p
))
92 # Generate a string which has the type and value of the token.
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' :
103 if t
.type == t
.value
:
104 return '"%s"' % t
.value
107 if t
.type == 'identifier':
108 return 'identifier "%s"' % t
.value
109 return 'keyword "%s"' % t
.value
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> ....
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
149 # [0] Insert a TOP definition for Copyright and Comments
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"""
162 # [0.2] Produce a COMMENT and aggregate sibling comments
163 def p_CommentsRest(self
, p
):
164 """CommentsRest : COMMENT CommentsRest
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
175 def p_Definitions(self
, p
):
176 """Definitions : ExtendedAttributeList Definition Definitions
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
190 | ImplementsStatement"""
193 # [2.1] Error recovery for definition
194 def p_DefinitionError(self
, p
):
195 """Definition : error ';'"""
196 p
[0] = self
.BuildError(p
, 'Definition')
199 def p_CallbackOrInterface(self
, p
):
200 """CallbackOrInterface : CALLBACK CallbackRestOrInterface
208 def p_CallbackRestOrInterface(self
, p
):
209 """CallbackRestOrInterface : CallbackRest
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'))
224 # [6.1] Error recovery for Enums
225 def p_PartialError(self
, p
):
226 """Partial : PARTIAL error"""
227 p
[0] = self
.BuildError(p
, 'Partial')
230 def p_PartialDefinition(self
, p
):
231 """PartialDefinition : PartialDictionary
232 | PartialInterface"""
236 def p_PartialInterface(self
, p
):
237 """PartialInterface : INTERFACE identifier '{' InterfaceMembers '}' ';'"""
238 p
[0] = self
.BuildNamed('Interface', p
, 2, p
[4])
241 def p_InterfaceMembers(self
, p
):
242 """InterfaceMembers : ExtendedAttributeList InterfaceMember InterfaceMembers
245 p
[2].AddChildren(p
[1])
246 p
[0] = ListFromConcat(p
[2], p
[3])
249 def p_InterfaceMember(self
, p
):
250 """InterfaceMember : Const
251 | AttributeOrOperation"""
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')
265 def p_DictionaryMembers(self
, p
):
266 """DictionaryMembers : ExtendedAttributeList DictionaryMember DictionaryMembers
269 p
[2].AddChildren(p
[1])
270 p
[0] = ListFromConcat(p
[2], p
[3])
273 def p_DictionaryMember(self
, p
):
274 """DictionaryMember : Type identifier Default ';'"""
275 p
[0] = self
.BuildNamed('Key', p
, 2, ListFromConcat(p
[1], p
[3]))
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')
289 def p_Default(self
, p
):
290 """Default : '=' DefaultValue
293 p
[0] = self
.BuildProduction('Default', p
, 2, p
[2])
296 def p_DefaultValue(self
, p
):
297 """DefaultValue : ConstValue
299 if type(p
[1]) == str:
300 p
[0] = ListFromConcat(self
.BuildAttribute('TYPE', 'DOMString'),
301 self
.BuildAttribute('NAME', p
[1]))
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]))
311 def p_ExceptionMembers(self
, p
):
312 """ExceptionMembers : ExtendedAttributeList ExceptionMember ExceptionMembers
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')
324 def p_Inheritance(self
, p
):
325 """Inheritance : ':' identifier
328 p
[0] = self
.BuildNamed('Inherit', p
, 2)
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')
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])
347 def p_EnumValues(self
, p
):
348 """EnumValues : ',' ExtendedAttributeList string EnumValues
351 enum
= self
.BuildNamed('EnumItem', p
, 3, p
[2])
352 p
[0] = ListFromConcat(enum
, p
[4])
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
))
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')
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
)
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
))
383 def p_ConstValue(self
, p
):
384 """ConstValue : BooleanLiteral
388 if type(p
[1]) == str:
389 p
[0] = ListFromConcat(self
.BuildAttribute('TYPE', 'integer'),
390 self
.BuildAttribute('NAME', p
[1]))
394 # [27.1] Add definition for NULL
397 p
[0] = ListFromConcat(self
.BuildAttribute('TYPE', 'NULL'),
398 self
.BuildAttribute('NAME', 'NULL'))
401 def p_BooleanLiteral(self
, p
):
402 """BooleanLiteral : TRUE
404 value
= self
.BuildAttribute('NAME', Boolean(p
[1] == 'true'))
405 p
[0] = ListFromConcat(self
.BuildAttribute('TYPE', 'boolean'), value
)
408 def p_FloatLiteral(self
, p
):
409 """FloatLiteral : float
417 p
[0] = ListFromConcat(self
.BuildAttribute('TYPE', 'float'),
418 self
.BuildAttribute('VALUE', val
))
421 def p_AttributeOrOperation(self
, p
):
422 """AttributeOrOperation : STRINGIFIER StringifierAttributeOrOperation
431 def p_StringifierAttributeOrOperation(self
, p
):
432 """StringifierAttributeOrOperation : Attribute
436 p
[0] = self
.BuildAttribute('STRINGIFIER', Boolean(True))
438 p
[0] = ListFromConcat(self
.BuildAttribute('STRINGIFIER', p
[1]), p
[1])
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]))
447 def p_Inherit(self
, p
):
451 p
[0] = self
.BuildTrue('INHERIT')
454 def p_ReadOnly(self
, p
):
455 """ReadOnly : READONLY
458 p
[0] = self
.BuildTrue('READONLY')
461 def p_Operation(self
, p
):
462 """Operation : Qualifiers OperationRest"""
463 p
[2].AddChildren(p
[1])
467 def p_Qualifiers(self
, p
):
468 """Qualifiers : STATIC
471 p
[0] = self
.BuildTrue('STATIC')
476 def p_Specials(self
, p
):
477 """Specials : Special Specials
480 p
[0] = ListFromConcat(p
[1], p
[2])
483 def p_Special(self
, p
):
489 p
[0] = self
.BuildTrue(p
[1].upper())
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
))
499 def p_OptionalIdentifier(self
, p
):
500 """OptionalIdentifier : identifier
508 def p_ArgumentList(self
, p
):
509 """ArgumentList : Argument Arguments
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')
520 def p_Arguments(self
, p
):
521 """Arguments : ',' Argument Arguments
524 p
[0] = ListFromConcat(p
[2], p
[3])
527 def p_Argument(self
, p
):
528 """Argument : ExtendedAttributeList OptionalOrRequiredArgument"""
529 p
[2].AddChildren(p
[1])
534 def p_OptionalOrRequiredArgument(self
, p
):
535 """OptionalOrRequiredArgument : OPTIONAL Type ArgumentName Default
536 | Type Ellipsis ArgumentName"""
538 arg
= self
.BuildNamed('Argument', p
, 3, ListFromConcat(p
[2], p
[4]))
539 arg
.AddChildren(self
.BuildTrue('OPTIONAL'))
541 arg
= self
.BuildNamed('Argument', p
, 3, ListFromConcat(p
[1], p
[2]))
545 def p_ArgumentName(self
, p
):
546 """ArgumentName : ArgumentNameKeyword
551 def p_Ellipsis(self
, p
):
552 """Ellipsis : ELLIPSIS
555 p
[0] = self
.BuildNamed('Argument', p
, 1)
556 p
[0].AddChildren(self
.BuildTrue('ELLIPSIS'))
559 def p_ExceptionMember(self
, p
):
560 """ExceptionMember : Const
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 ']'
579 items
= ListFromConcat(p
[3], p
[4])
580 attribs
= self
.BuildProduction('ExtAttributes', p
, 2, items
)
581 p
[0] = ListFromConcat(p
[1], attribs
)
586 def p_ExtendedAttributes(self
, p
):
587 """ExtendedAttributes : ',' ExtendedAttribute ExtendedAttributes
590 p
[0] = ListFromConcat(p
[2], p
[3])
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"""
607 def p_ArgumentNameKeyword(self
, p
):
608 """ArgumentNameKeyword : ATTRIBUTE
631 | UnionType TypeSuffix"""
633 p
[0] = self
.BuildProduction('Type', p
, 1, p
[1])
635 p
[0] = self
.BuildProduction('Type', p
, 1, ListFromConcat(p
[1], p
[2]))
638 def p_SingleType(self
, p
):
639 """SingleType : NonAnyType
640 | ANY TypeSuffixStartingWithArray"""
644 p
[0] = ListFromConcat(self
.BuildProduction('Any', p
, 1), p
[2])
647 def p_UnionType(self
, p
):
648 """UnionType : '(' UnionMemberType OR UnionMemberType UnionMemberTypes ')'"""
651 def p_UnionMemberType(self
, p
):
652 """UnionMemberType : NonAnyType
653 | UnionType TypeSuffix
654 | ANY '[' ']' TypeSuffix"""
656 def p_UnionMemberTypes(self
, p
):
657 """UnionMemberTypes : OR UnionMemberType UnionMemberTypes
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"""
666 if type(p
[1]) == str:
667 typeref
= self
.BuildNamed('Typeref', p
, 1)
670 p
[0] = ListFromConcat(typeref
, p
[2])
673 p
[0] = self
.BuildProduction('Sequence', p
, 1, ListFromConcat(p
[3], p
[5]))
677 def p_ConstType(self
, p
):
678 """ConstType : PrimitiveType Null
680 if type(p
[1]) == str:
681 p
[0] = self
.BuildNamed('Typeref', p
, 1, p
[2])
683 p
[1].AddChildren(p
[2])
688 def p_PrimitiveType(self
, p
):
689 """PrimitiveType : UnsignedIntegerType
690 | UnrestrictedFloatType
697 if type(p
[1]) == str:
698 p
[0] = self
.BuildNamed('PrimitiveType', p
, 1)
704 def p_UnrestrictedFloatType(self
, p
):
705 """UnrestrictedFloatType : UNRESTRICTED FloatType
708 typeref
= self
.BuildNamed('PrimitiveType', p
, 1)
710 typeref
= self
.BuildNamed('PrimitiveType', p
, 2)
711 typeref
.AddChildren(self
.BuildTrue('UNRESTRICTED'))
716 def p_FloatType(self
, p
):
722 def p_UnsignedIntegerType(self
, p
):
723 """UnsignedIntegerType : UNSIGNED IntegerType
728 p
[0] = 'unsigned ' + p
[2]
731 def p_IntegerType(self
, p
):
732 """IntegerType : SHORT
733 | LONG OptionalLong"""
740 def p_OptionalLong(self
, p
):
741 """OptionalLong : LONG
749 # [70] Add support for sized array
750 def p_TypeSuffix(self
, p
):
751 """TypeSuffix : '[' integer ']' TypeSuffix
753 | '?' TypeSuffixStartingWithArray
756 p
[0] = self
.BuildNamed('Array', p
, 2, p
[4])
759 p
[0] = self
.BuildProduction('Array', p
, 1, p
[3])
762 p
[0] = ListFromConcat(self
.BuildTrue('NULLABLE'), p
[2])
766 def p_TypeSuffixStartingWithArray(self
, p
):
767 """TypeSuffixStartingWithArray : '[' ']' TypeSuffix
770 p
[0] = self
.BuildProduction('Array', p
, 0, p
[3])
777 p
[0] = self
.BuildTrue('NULLABLE')
780 def p_ReturnType(self
, p
):
784 p
[0] = self
.BuildProduction('Type', p
, 1)
785 p
[0].AddChildren(self
.BuildNamed('PrimitiveType', p
, 1))
790 def p_ExtendedAttributeNoArgs(self
, p
):
791 """ExtendedAttributeNoArgs : identifier"""
792 p
[0] = self
.BuildNamed('ExtAttribute', p
, 1)
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
)
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
)
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
)
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
):
826 prev
= self
.yaccobj
.symstack
[-1]
827 if type(prev
) == lex
.LexToken
:
828 msg
= "Unexpected %s after %s." % (
829 TokenTypeName(t
), TokenTypeName(prev
))
831 msg
= "Unexpected %s." % (t
.value
)
833 last
= self
.LastToken()
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
852 return self
.lexer
.last
854 def __init__(self
, lexer
, verbose
=False, debug
=False, mute_error
=False):
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
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):
884 filename
= self
.lexer
.Lexer().filename
885 lineno
= p
.lineno(index
)
886 pos
= p
.lexpos(index
)
887 out
= IDLNode(cls
, filename
, lineno
, pos
, childlist
)
890 print 'Exception while parsing:'
891 for num
, item
in enumerate(p
):
892 print ' [%d] %s' % (num
, ExpandProduction(item
))
894 print 'Last token: %s' % str(self
.LastToken())
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
):
905 # Remove comment markers
908 # For C++ style, remove any leading whitespace and the '//' marker from
911 for line
in name
.split('\n'):
912 start
= line
.find('//')
913 lines
.append(line
[start
+2:])
915 # For C style, remove ending '*/''
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('*')
922 line
= line
[offs
+ 1:].rstrip()
926 name
= '\n'.join(lines
)
927 childlist
= [self
.BuildAttribute('NAME', name
),
928 self
.BuildAttribute('FORM', form
)]
929 return self
.BuildProduction(cls
, p
, index
, childlist
)
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
)
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))
968 return self
._parse
_errors
+ self
.lexer
._lex
_errors
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
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
))
994 def ParseFile(parser
, filename
):
995 """Parse a file and return a File type of node."""
996 with
open(filename
) as fileobject
:
998 out
= parser
.ParseText(filename
, fileobject
.read())
999 out
.SetProperty('DATETIME', time
.ctime(os
.path
.getmtime(filename
)))
1000 out
.SetProperty('ERRORS', parser
.GetErrors())
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
)))
1011 parser
= IDLParser(IDLLexer())
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']))
1022 print '\nFound %d errors.\n' % errors
1028 if __name__
== '__main__':
1029 sys
.exit(main(sys
.argv
[1:]))