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://heycam.github.io/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
40 # Try to load the ply module, if not, then assume it is in the third_party
44 # Disable lint check which fails to find the ply module.
45 # pylint: disable=F0401
49 module_path
, module_name
= os
.path
.split(__file__
)
50 third_party
= os
.path
.join(module_path
, os
.par
, os
.par
, 'third_party')
51 sys
.path
.append(third_party
)
52 # pylint: disable=F0401
59 # Maps the standard error formula into a more friendly error message.
62 'Unexpected ")" after "(".' : 'Empty argument list.',
63 'Unexpected ")" after ",".' : 'Missing argument.',
64 'Unexpected "}" after ",".' : 'Trailing comma in block.',
65 'Unexpected "}" after "{".' : 'Unexpected empty block.',
66 'Unexpected comment after "}".' : 'Unexpected trailing comment.',
67 'Unexpected "{" after keyword "enum".' : 'Enum missing name.',
68 'Unexpected "{" after keyword "struct".' : 'Struct missing name.',
69 'Unexpected "{" after keyword "interface".' : 'Interface missing name.',
74 """Convert to strict boolean type."""
80 def ListFromConcat(*items
):
81 """Generate list by concatenating inputs"""
86 if type(item
) is not type([]):
93 def ExpandProduction(p
):
95 return '[' + ', '.join([ExpandProduction(x
) for x
in p
]) + ']'
96 if type(p
) == IDLNode
:
97 return 'Node:' + str(p
)
98 if type(p
) == IDLAttribute
:
99 return 'Attr:' + str(p
)
102 return '%s:%s' % (p
.__class
__.__name
__, str(p
))
106 # Generate a string which has the type and value of the token.
108 def TokenTypeName(t
):
109 if t
.type == 'SYMBOL':
110 return 'symbol %s' % t
.value
111 if t
.type in ['HEX', 'INT', 'OCT', 'FLOAT']:
112 return 'value %s' % t
.value
113 if t
.type == 'string' :
114 return 'string "%s"' % t
.value
115 if t
.type == 'COMMENT' :
117 if t
.type == t
.value
:
118 return '"%s"' % t
.value
121 if t
.type == 'identifier':
122 return 'identifier "%s"' % t
.value
123 return 'keyword "%s"' % t
.value
129 # The Parser inherits the from the Lexer to provide PLY with the tokenizing
130 # definitions. Parsing patterns are encoded as functions where p_<name> is
131 # is called any time a patern matching the function documentation is found.
132 # Paterns are expressed in the form of:
133 # """ <new item> : <item> ....
136 # Where new item is the result of a match against one or more sets of items
137 # separated by the "|".
139 # The function is called with an object 'p' where p[0] is the output object
140 # and p[n] is the set of inputs for positive values of 'n'. Len(p) can be
141 # used to distinguish between multiple item sets in the pattern.
143 # For more details on parsing refer to the PLY documentation at
144 # http://www.dabeaz.com/ply/
146 # The parser is based on the WebIDL standard. See:
147 # http://heycam.github.io/webidl/#idl-grammar
149 # The various productions are annotated so that the WHOLE number greater than
150 # zero in the comment denotes the matching WebIDL grammar definition.
152 # Productions with a fractional component in the comment denote additions to
153 # the WebIDL spec, such as comments.
157 class IDLParser(object):
159 # We force all input files to start with two comments. The first comment is a
160 # Copyright notice followed by a file comment and finally by file level
163 # [0] Insert a TOP definition for Copyright and Comments
165 """Top : COMMENT COMMENT Definitions"""
166 Copyright
= self
.BuildComment('Copyright', p
, 1)
167 Filedoc
= self
.BuildComment('Comment', p
, 2)
168 p
[0] = ListFromConcat(Copyright
, Filedoc
, p
[3])
170 # [0.1] Add support for Multiple COMMENTS
171 def p_Comments(self
, p
):
172 """Comments : CommentsRest"""
176 # [0.2] Produce a COMMENT and aggregate sibling comments
177 def p_CommentsRest(self
, p
):
178 """CommentsRest : COMMENT CommentsRest
181 p
[0] = ListFromConcat(self
.BuildComment('Comment', p
, 1), p
[2])
185 #The parser is based on the WebIDL standard. See:
186 # http://heycam.github.io/webidl/#idl-grammar
189 def p_Definitions(self
, p
):
190 """Definitions : ExtendedAttributeList Definition Definitions
193 p
[2].AddChildren(p
[1])
194 p
[0] = ListFromConcat(p
[2], p
[3])
197 def p_Definition(self
, p
):
198 """Definition : CallbackOrInterface
204 | ImplementsStatement"""
207 # [2.1] Error recovery for definition
208 def p_DefinitionError(self
, p
):
209 """Definition : error ';'"""
210 p
[0] = self
.BuildError(p
, 'Definition')
213 def p_CallbackOrInterface(self
, p
):
214 """CallbackOrInterface : CALLBACK CallbackRestOrInterface
222 def p_CallbackRestOrInterface(self
, p
):
223 """CallbackRestOrInterface : CallbackRest
228 def p_Interface(self
, p
):
229 """Interface : INTERFACE identifier Inheritance '{' InterfaceMembers '}' ';'"""
230 p
[0] = self
.BuildNamed('Interface', p
, 2, ListFromConcat(p
[3], p
[5]))
232 # [5.1] Error recovery for interface.
233 def p_InterfaceError(self
, p
):
234 """Interface : INTERFACE identifier Inheritance '{' error"""
235 p
[0] = self
.BuildError(p
, 'Interface')
238 def p_Partial(self
, p
):
239 """Partial : PARTIAL PartialDefinition"""
240 p
[2].AddChildren(self
.BuildTrue('Partial'))
243 # [6.1] Error recovery for Partial
244 def p_PartialError(self
, p
):
245 """Partial : PARTIAL error"""
246 p
[0] = self
.BuildError(p
, 'Partial')
249 def p_PartialDefinition(self
, p
):
250 """PartialDefinition : PartialDictionary
251 | PartialInterface"""
255 def p_PartialInterface(self
, p
):
256 """PartialInterface : INTERFACE identifier '{' InterfaceMembers '}' ';'"""
257 p
[0] = self
.BuildNamed('Interface', p
, 2, p
[4])
260 def p_InterfaceMembers(self
, p
):
261 """InterfaceMembers : ExtendedAttributeList InterfaceMember InterfaceMembers
264 p
[2].AddChildren(p
[1])
265 p
[0] = ListFromConcat(p
[2], p
[3])
267 # [9.1] Error recovery for InterfaceMembers
268 def p_InterfaceMembersError(self
, p
):
269 """InterfaceMembers : error"""
270 p
[0] = self
.BuildError(p
, 'InterfaceMembers')
272 # [10] Removed unsupported: Serializer
273 def p_InterfaceMember(self
, p
):
274 """InterfaceMember : Const
283 | ReadWriteSetlike"""
287 def p_Dictionary(self
, p
):
288 """Dictionary : DICTIONARY identifier Inheritance '{' DictionaryMembers '}' ';'"""
289 p
[0] = self
.BuildNamed('Dictionary', p
, 2, ListFromConcat(p
[3], p
[5]))
291 # [11.1] Error recovery for regular Dictionary
292 def p_DictionaryError(self
, p
):
293 """Dictionary : DICTIONARY error ';'"""
294 p
[0] = self
.BuildError(p
, 'Dictionary')
296 # [11.2] Error recovery for regular Dictionary
297 # (for errors inside dictionary definition)
298 def p_DictionaryError2(self
, p
):
299 """Dictionary : DICTIONARY identifier Inheritance '{' error"""
300 p
[0] = self
.BuildError(p
, 'Dictionary')
303 def p_DictionaryMembers(self
, p
):
304 """DictionaryMembers : ExtendedAttributeList DictionaryMember DictionaryMembers
307 p
[2].AddChildren(p
[1])
308 p
[0] = ListFromConcat(p
[2], p
[3])
310 # [12.1] Error recovery for DictionaryMembers
311 def p_DictionaryMembersError(self
, p
):
312 """DictionaryMembers : ExtendedAttributeList error"""
313 p
[0] = self
.BuildError(p
, 'DictionaryMembers')
316 def p_DictionaryMember(self
, p
):
317 """DictionaryMember : Required Type identifier Default ';'"""
318 p
[0] = self
.BuildNamed('Key', p
, 3, ListFromConcat(p
[1], p
[2], p
[4]))
321 def p_Required(self
, p
):
322 """Required : REQUIRED
325 p
[0] = self
.BuildTrue('REQUIRED')
328 def p_PartialDictionary(self
, p
):
329 """PartialDictionary : DICTIONARY identifier '{' DictionaryMembers '}' ';'"""
330 partial
= self
.BuildTrue('Partial')
331 p
[0] = self
.BuildNamed('Dictionary', p
, 2, ListFromConcat(p
[4], partial
))
333 # [15.1] Error recovery for Partial Dictionary
334 def p_PartialDictionaryError(self
, p
):
335 """PartialDictionary : DICTIONARY error ';'"""
336 p
[0] = self
.BuildError(p
, 'PartialDictionary')
339 def p_Default(self
, p
):
340 """Default : '=' DefaultValue
343 p
[0] = self
.BuildProduction('Default', p
, 2, p
[2])
346 def p_DefaultValue(self
, p
):
347 """DefaultValue : ConstValue
351 p
[0] = ListFromConcat(self
.BuildAttribute('TYPE', 'sequence'),
352 self
.BuildAttribute('VALUE', '[]'))
353 elif type(p
[1]) == str:
354 p
[0] = ListFromConcat(self
.BuildAttribute('TYPE', 'DOMString'),
355 self
.BuildAttribute('NAME', p
[1]))
360 def p_Exception(self
, p
):
361 """Exception : EXCEPTION identifier Inheritance '{' ExceptionMembers '}' ';'"""
362 p
[0] = self
.BuildNamed('Exception', p
, 2, ListFromConcat(p
[3], p
[5]))
365 def p_ExceptionMembers(self
, p
):
366 """ExceptionMembers : ExtendedAttributeList ExceptionMember ExceptionMembers
369 p
[2].AddChildren(p
[1])
370 p
[0] = ListFromConcat(p
[2], p
[3])
372 # [.1] Error recovery for ExceptionMembers - Not specified
373 def p_ExceptionMembersError(self
, p
):
374 """ExceptionMembers : error"""
375 p
[0] = self
.BuildError(p
, 'ExceptionMembers')
378 def p_Inheritance(self
, p
):
379 """Inheritance : ':' identifier
382 p
[0] = self
.BuildNamed('Inherit', p
, 2)
386 """Enum : ENUM identifier '{' EnumValueList '}' ';'"""
387 p
[0] = self
.BuildNamed('Enum', p
, 2, p
[4])
389 # [19.1] Error recovery for Enums
390 def p_EnumError(self
, p
):
391 """Enum : ENUM error ';'"""
392 p
[0] = self
.BuildError(p
, 'Enum')
395 def p_EnumValueList(self
, p
):
396 """EnumValueList : ExtendedAttributeList string EnumValueListComma"""
397 enum
= self
.BuildNamed('EnumItem', p
, 2, p
[1])
398 p
[0] = ListFromConcat(enum
, p
[3])
401 def p_EnumValueListComma(self
, p
):
402 """EnumValueListComma : ',' EnumValueListString
408 def p_EnumValueListString(self
, p
):
409 """EnumValueListString : ExtendedAttributeList string EnumValueListComma
412 enum
= self
.BuildNamed('EnumItem', p
, 2, p
[1])
413 p
[0] = ListFromConcat(enum
, p
[3])
416 def p_CallbackRest(self
, p
):
417 """CallbackRest : identifier '=' ReturnType '(' ArgumentList ')' ';'"""
418 arguments
= self
.BuildProduction('Arguments', p
, 4, p
[5])
419 p
[0] = self
.BuildNamed('Callback', p
, 1, ListFromConcat(p
[3], arguments
))
422 def p_Typedef(self
, p
):
423 """Typedef : TYPEDEF ExtendedAttributeListNoComments Type identifier ';'"""
424 p
[0] = self
.BuildNamed('Typedef', p
, 4, ListFromConcat(p
[2], p
[3]))
426 # [24.1] Error recovery for Typedefs
427 def p_TypedefError(self
, p
):
428 """Typedef : TYPEDEF error ';'"""
429 p
[0] = self
.BuildError(p
, 'Typedef')
432 def p_ImplementsStatement(self
, p
):
433 """ImplementsStatement : identifier IMPLEMENTS identifier ';'"""
434 name
= self
.BuildAttribute('REFERENCE', p
[3])
435 p
[0] = self
.BuildNamed('Implements', p
, 1, name
)
438 def p_Const(self
, p
):
439 """Const : CONST ConstType identifier '=' ConstValue ';'"""
440 value
= self
.BuildProduction('Value', p
, 5, p
[5])
441 p
[0] = self
.BuildNamed('Const', p
, 3, ListFromConcat(p
[2], value
))
444 def p_ConstValue(self
, p
):
445 """ConstValue : BooleanLiteral
449 if type(p
[1]) == str:
450 p
[0] = ListFromConcat(self
.BuildAttribute('TYPE', 'integer'),
451 self
.BuildAttribute('NAME', p
[1]))
455 # [27.1] Add definition for NULL
458 p
[0] = ListFromConcat(self
.BuildAttribute('TYPE', 'NULL'),
459 self
.BuildAttribute('NAME', 'NULL'))
462 def p_BooleanLiteral(self
, p
):
463 """BooleanLiteral : TRUE
465 value
= self
.BuildAttribute('VALUE', Boolean(p
[1] == 'true'))
466 p
[0] = ListFromConcat(self
.BuildAttribute('TYPE', 'boolean'), value
)
469 def p_FloatLiteral(self
, p
):
470 """FloatLiteral : float
478 p
[0] = ListFromConcat(self
.BuildAttribute('TYPE', 'float'),
479 self
.BuildAttribute('VALUE', val
))
482 def p_Serializer(self
, p
):
483 """Serializer : SERIALIZER SerializerRest"""
484 p
[0] = self
.BuildProduction('Serializer', p
, 1, p
[2])
487 # TODO(jl): This adds ReturnType and ';', missing from the spec's grammar.
488 # https://www.w3.org/Bugs/Public/show_bug.cgi?id=20361
489 def p_SerializerRest(self
, p
):
490 """SerializerRest : ReturnType OperationRest
491 | '=' SerializationPattern ';'
494 p
[2].AddChildren(p
[1])
500 def p_SerializationPattern(self
, p
):
501 """SerializationPattern : '{' SerializationPatternMap '}'
502 | '[' SerializationPatternList ']'
507 p
[0] = self
.BuildAttribute('ATTRIBUTE', p
[1])
510 # TODO(jl): This adds the "ATTRIBUTE" and "INHERIT ',' ATTRIBUTE" variants,
511 # missing from the spec's grammar.
512 # https://www.w3.org/Bugs/Public/show_bug.cgi?id=20361
513 def p_SerializationPatternMap(self
, p
):
514 """SerializationPatternMap : GETTER
516 | INHERIT ',' ATTRIBUTE
517 | INHERIT Identifiers
518 | identifier Identifiers
520 p
[0] = self
.BuildProduction('Map', p
, 0)
522 p
[0].AddChildren(self
.BuildTrue('INHERIT'))
523 p
[0].AddChildren(self
.BuildTrue('ATTRIBUTE'))
526 p
[0].AddChildren(self
.BuildTrue('GETTER'))
527 elif p
[1] == 'attribute':
528 p
[0].AddChildren(self
.BuildTrue('ATTRIBUTE'))
530 if p
[1] == 'inherit':
531 p
[0].AddChildren(self
.BuildTrue('INHERIT'))
534 attributes
= ListFromConcat(p
[1], p
[2])
535 p
[0].AddChildren(self
.BuildAttribute('ATTRIBUTES', attributes
))
538 def p_SerializationPatternList(self
, p
):
539 """SerializationPatternList : GETTER
540 | identifier Identifiers
542 p
[0] = self
.BuildProduction('List', p
, 0)
545 p
[0].AddChildren(self
.BuildTrue('GETTER'))
547 attributes
= ListFromConcat(p
[1], p
[2])
548 p
[0].AddChildren(self
.BuildAttribute('ATTRIBUTES', attributes
))
551 def p_Stringifier(self
, p
):
552 """Stringifier : STRINGIFIER StringifierRest"""
553 p
[0] = self
.BuildProduction('Stringifier', p
, 1, p
[2])
556 def p_StringifierRest(self
, p
):
557 """StringifierRest : AttributeRest
558 | ReturnType OperationRest
561 p
[2].AddChildren(p
[1])
567 def p_StaticMember(self
, p
):
568 """StaticMember : STATIC StaticMemberRest"""
569 p
[2].AddChildren(self
.BuildTrue('STATIC'))
573 def p_StaticMemberRest(self
, p
):
574 """StaticMemberRest : ReadOnly AttributeRest
575 | ReturnType OperationRest"""
579 p
[2].AddChildren(p
[1])
583 def p_ReadonlyMember(self
, p
):
584 """ReadonlyMember : READONLY ReadonlyMemberRest"""
585 p
[2].AddChildren(self
.BuildTrue('READONLY'))
589 def p_ReadonlyMemberRest(self
, p
):
590 """ReadonlyMemberRest : AttributeRest
596 def p_ReadWriteAttribute(self
, p
):
597 """ReadWriteAttribute : INHERIT ReadOnly AttributeRest
600 inherit
= self
.BuildTrue('INHERIT')
601 p
[3].AddChildren(ListFromConcat(inherit
, p
[2]))
607 def p_AttributeRest(self
, p
):
608 """AttributeRest : ATTRIBUTE Type AttributeName ';'"""
609 p
[0] = self
.BuildNamed('Attribute', p
, 3, p
[2])
612 def p_AttributeName(self
, p
):
613 """AttributeName : AttributeNameKeyword
618 def p_AttributeNameKeyword(self
, p
):
619 """AttributeNameKeyword : REQUIRED"""
622 # [45] Unreferenced in the specification
625 def p_ReadOnly(self
, p
):
626 """ReadOnly : READONLY
629 p
[0] = self
.BuildTrue('READONLY')
632 def p_Operation(self
, p
):
633 """Operation : ReturnType OperationRest
634 | SpecialOperation"""
636 p
[2].AddChildren(p
[1])
642 def p_SpecialOperation(self
, p
):
643 """SpecialOperation : Special Specials ReturnType OperationRest"""
644 p
[4].AddChildren(ListFromConcat(p
[1], p
[2], p
[3]))
648 def p_Specials(self
, p
):
649 """Specials : Special Specials
652 p
[0] = ListFromConcat(p
[1], p
[2])
655 def p_Special(self
, p
):
661 p
[0] = self
.BuildTrue(p
[1].upper())
664 def p_OperationRest(self
, p
):
665 """OperationRest : OptionalIdentifier '(' ArgumentList ')' ';'"""
666 arguments
= self
.BuildProduction('Arguments', p
, 2, p
[3])
667 p
[0] = self
.BuildNamed('Operation', p
, 1, arguments
)
670 def p_OptionalIdentifier(self
, p
):
671 """OptionalIdentifier : identifier
679 def p_ArgumentList(self
, p
):
680 """ArgumentList : Argument Arguments
683 p
[0] = ListFromConcat(p
[1], p
[2])
685 # [53.1] ArgumentList error recovery
686 def p_ArgumentListError(self
, p
):
687 """ArgumentList : error """
688 p
[0] = self
.BuildError(p
, 'ArgumentList')
691 def p_Arguments(self
, p
):
692 """Arguments : ',' Argument Arguments
695 p
[0] = ListFromConcat(p
[2], p
[3])
697 # [54.1] Arguments error recovery
698 def p_ArgumentsError(self
, p
):
699 """Arguments : ',' error"""
700 p
[0] = self
.BuildError(p
, 'Arguments')
703 def p_Argument(self
, p
):
704 """Argument : ExtendedAttributeList OptionalOrRequiredArgument"""
705 p
[2].AddChildren(p
[1])
709 def p_OptionalOrRequiredArgument(self
, p
):
710 """OptionalOrRequiredArgument : OPTIONAL Type ArgumentName Default
711 | Type Ellipsis ArgumentName"""
713 arg
= self
.BuildNamed('Argument', p
, 3, ListFromConcat(p
[2], p
[4]))
714 arg
.AddChildren(self
.BuildTrue('OPTIONAL'))
716 arg
= self
.BuildNamed('Argument', p
, 3, ListFromConcat(p
[1], p
[2]))
720 def p_ArgumentName(self
, p
):
721 """ArgumentName : ArgumentNameKeyword
726 def p_Ellipsis(self
, p
):
727 """Ellipsis : ELLIPSIS
730 p
[0] = self
.BuildNamed('Argument', p
, 1)
731 p
[0].AddChildren(self
.BuildTrue('ELLIPSIS'))
734 def p_ExceptionMember(self
, p
):
735 """ExceptionMember : Const
740 def p_ExceptionField(self
, p
):
741 """ExceptionField : Type identifier ';'"""
742 p
[0] = self
.BuildNamed('ExceptionField', p
, 2, p
[1])
744 # [] Error recovery for ExceptionMembers - Unspecified
745 def p_ExceptionFieldError(self
, p
):
746 """ExceptionField : error"""
747 p
[0] = self
.BuildError(p
, 'ExceptionField')
750 def p_Iterable(self
, p
):
751 """Iterable : ITERABLE '<' Type OptionalType '>' ';'
752 | LEGACYITERABLE '<' Type '>' ';'"""
754 childlist
= ListFromConcat(p
[3], p
[4])
755 p
[0] = self
.BuildProduction('Iterable', p
, 2, childlist
)
757 p
[0] = self
.BuildProduction('LegacyIterable', p
, 2, p
[3])
760 def p_OptionalType(self
, p
):
761 """OptionalType : ',' Type
767 def p_ReadWriteMaplike(self
, p
):
768 """ReadWriteMaplike : MaplikeRest"""
772 def p_ReadWriteSetlike(self
, p
):
773 """ReadWriteSetlike : SetlikeRest"""
777 def p_MaplikeRest(self
, p
):
778 """MaplikeRest : MAPLIKE '<' Type ',' Type '>' ';'"""
779 childlist
= ListFromConcat(p
[3], p
[5])
780 p
[0] = self
.BuildProduction('Maplike', p
, 2, childlist
)
783 def p_SetlikeRest(self
, p
):
784 """SetlikeRest : SETLIKE '<' Type '>' ';'"""
785 p
[0] = self
.BuildProduction('Setlike', p
, 2, p
[3])
787 # [65] No comment version for mid statement attributes.
788 def p_ExtendedAttributeListNoComments(self
, p
):
789 """ExtendedAttributeListNoComments : '[' ExtendedAttribute ExtendedAttributes ']'
792 items
= ListFromConcat(p
[2], p
[3])
793 p
[0] = self
.BuildProduction('ExtAttributes', p
, 1, items
)
795 # [65.1] Add optional comment field for start of statements.
796 def p_ExtendedAttributeList(self
, p
):
797 """ExtendedAttributeList : Comments '[' ExtendedAttribute ExtendedAttributes ']'
800 items
= ListFromConcat(p
[3], p
[4])
801 attribs
= self
.BuildProduction('ExtAttributes', p
, 2, items
)
802 p
[0] = ListFromConcat(p
[1], attribs
)
807 def p_ExtendedAttributes(self
, p
):
808 """ExtendedAttributes : ',' ExtendedAttribute ExtendedAttributes
811 p
[0] = ListFromConcat(p
[2], p
[3])
815 # [ identifier ( ArgumentList ) ]
816 # [ identifier = identifier ]
817 # [ identifier = ( IdentifierList ) ]
818 # [ identifier = identifier ( ArgumentList ) ]
819 # [66] map directly to [91-93, 95]
820 # [67-69, 71] are unsupported
821 def p_ExtendedAttribute(self
, p
):
822 """ExtendedAttribute : ExtendedAttributeNoArgs
823 | ExtendedAttributeArgList
824 | ExtendedAttributeIdent
825 | ExtendedAttributeIdentList
826 | ExtendedAttributeNamedArgList"""
830 def p_ArgumentNameKeyword(self
, p
):
831 """ArgumentNameKeyword : ATTRIBUTE
852 # [72] NOT IMPLEMENTED (OtherOrComma)
857 | UnionType TypeSuffix"""
859 p
[0] = self
.BuildProduction('Type', p
, 1, p
[1])
861 p
[0] = self
.BuildProduction('Type', p
, 1, ListFromConcat(p
[1], p
[2]))
864 def p_SingleType(self
, p
):
865 """SingleType : NonAnyType
866 | ANY TypeSuffixStartingWithArray"""
870 p
[0] = ListFromConcat(self
.BuildProduction('Any', p
, 1), p
[2])
873 def p_UnionType(self
, p
):
874 """UnionType : '(' UnionMemberType OR UnionMemberType UnionMemberTypes ')'"""
877 def p_UnionMemberType(self
, p
):
878 """UnionMemberType : NonAnyType
879 | UnionType TypeSuffix
880 | ANY '[' ']' TypeSuffix"""
882 def p_UnionMemberTypes(self
, p
):
883 """UnionMemberTypes : OR UnionMemberType UnionMemberTypes
886 # [78] Moved BYTESTRING, DOMSTRING, OBJECT, DATE, REGEXP to PrimitiveType
887 # Moving all built-in types into PrimitiveType makes it easier to
888 # differentiate between them and 'identifier', since p[1] would be a string in
890 def p_NonAnyType(self
, p
):
891 """NonAnyType : PrimitiveType TypeSuffix
893 | identifier TypeSuffix
894 | SEQUENCE '<' Type '>' Null
895 | FROZENARRAY '<' Type '>' Null"""
897 if type(p
[1]) == str:
898 typeref
= self
.BuildNamed('Typeref', p
, 1)
901 p
[0] = ListFromConcat(typeref
, p
[2])
904 cls
= 'Sequence' if p
[1] == 'sequence' else 'FrozenArray'
905 p
[0] = self
.BuildProduction(cls
, p
, 1, ListFromConcat(p
[3], p
[5]))
907 # [79] NOT IMPLEMENTED (BufferRelatedType)
910 def p_ConstType(self
, p
):
911 """ConstType : PrimitiveType Null
913 if type(p
[1]) == str:
914 p
[0] = self
.BuildNamed('Typeref', p
, 1, p
[2])
916 p
[1].AddChildren(p
[2])
920 # [81] Added BYTESTRING, DOMSTRING, OBJECT, DATE, REGEXP
921 def p_PrimitiveType(self
, p
):
922 """PrimitiveType : UnsignedIntegerType
923 | UnrestrictedFloatType
932 if type(p
[1]) == str:
933 p
[0] = self
.BuildNamed('PrimitiveType', p
, 1)
939 def p_UnrestrictedFloatType(self
, p
):
940 """UnrestrictedFloatType : UNRESTRICTED FloatType
943 typeref
= self
.BuildNamed('PrimitiveType', p
, 1)
945 typeref
= self
.BuildNamed('PrimitiveType', p
, 2)
946 typeref
.AddChildren(self
.BuildTrue('UNRESTRICTED'))
951 def p_FloatType(self
, p
):
957 def p_UnsignedIntegerType(self
, p
):
958 """UnsignedIntegerType : UNSIGNED IntegerType
963 p
[0] = 'unsigned ' + p
[2]
966 def p_IntegerType(self
, p
):
967 """IntegerType : SHORT
968 | LONG OptionalLong"""
975 def p_OptionalLong(self
, p
):
976 """OptionalLong : LONG
983 # [87] Add unqualified Promise
984 def p_PromiseType(self
, p
):
985 """PromiseType : PROMISE '<' ReturnType '>'
988 # Promise without resolution type is not specified in the Web IDL spec.
989 # As it is used in some specs and in the blink implementation,
990 # we allow that here.
991 resolution_type
= self
.BuildProduction('Type', p
, 1,
992 self
.BuildProduction('Any', p
, 1))
993 p
[0] = self
.BuildNamed('Promise', p
, 1, resolution_type
)
995 p
[0] = self
.BuildNamed('Promise', p
, 1, p
[3])
997 # [88] Add support for sized array
998 def p_TypeSuffix(self
, p
):
999 """TypeSuffix : '[' integer ']' TypeSuffix
1000 | '[' ']' TypeSuffix
1001 | '?' TypeSuffixStartingWithArray
1004 p
[0] = self
.BuildNamed('Array', p
, 2, p
[4])
1007 p
[0] = self
.BuildProduction('Array', p
, 1, p
[3])
1010 p
[0] = ListFromConcat(self
.BuildTrue('NULLABLE'), p
[2])
1014 def p_TypeSuffixStartingWithArray(self
, p
):
1015 """TypeSuffixStartingWithArray : '[' ']' TypeSuffix
1018 p
[0] = self
.BuildProduction('Array', p
, 0, p
[3])
1021 def p_Null(self
, p
):
1025 p
[0] = self
.BuildTrue('NULLABLE')
1028 def p_ReturnType(self
, p
):
1029 """ReturnType : Type
1032 p
[0] = self
.BuildProduction('Type', p
, 1)
1033 p
[0].AddChildren(self
.BuildNamed('PrimitiveType', p
, 1))
1038 def p_IdentifierList(self
, p
):
1039 """IdentifierList : identifier Identifiers"""
1040 p
[0] = ListFromConcat(p
[1], p
[2])
1043 def p_Identifiers(self
, p
):
1044 """Identifiers : ',' identifier Identifiers
1047 p
[0] = ListFromConcat(p
[2], p
[3])
1050 def p_ExtendedAttributeNoArgs(self
, p
):
1051 """ExtendedAttributeNoArgs : identifier"""
1052 p
[0] = self
.BuildNamed('ExtAttribute', p
, 1)
1055 def p_ExtendedAttributeArgList(self
, p
):
1056 """ExtendedAttributeArgList : identifier '(' ArgumentList ')'"""
1057 arguments
= self
.BuildProduction('Arguments', p
, 2, p
[3])
1058 p
[0] = self
.BuildNamed('ExtAttribute', p
, 1, arguments
)
1061 def p_ExtendedAttributeIdent(self
, p
):
1062 """ExtendedAttributeIdent : identifier '=' identifier"""
1063 value
= self
.BuildAttribute('VALUE', p
[3])
1064 p
[0] = self
.BuildNamed('ExtAttribute', p
, 1, value
)
1067 def p_ExtendedAttributeIdentList(self
, p
):
1068 """ExtendedAttributeIdentList : identifier '=' '(' IdentifierList ')'"""
1069 value
= self
.BuildAttribute('VALUE', p
[4])
1070 p
[0] = self
.BuildNamed('ExtAttribute', p
, 1, value
)
1073 def p_ExtendedAttributeNamedArgList(self
, p
):
1074 """ExtendedAttributeNamedArgList : identifier '=' identifier '(' ArgumentList ')'"""
1075 args
= self
.BuildProduction('Arguments', p
, 4, p
[5])
1076 value
= self
.BuildNamed('Call', p
, 3, args
)
1077 p
[0] = self
.BuildNamed('ExtAttribute', p
, 1, value
)
1082 # p_error is called whenever the parser can not find a pattern match for
1083 # a set of items from the current state. The p_error function defined here
1084 # is triggered logging an error, and parsing recovery happens as the
1085 # p_<type>_error functions defined above are called. This allows the parser
1086 # to continue so as to capture more than one error per file.
1088 def p_error(self
, t
):
1092 prev
= self
.yaccobj
.symstack
[-1]
1093 if type(prev
) == lex
.LexToken
:
1094 msg
= "Unexpected %s after %s." % (
1095 TokenTypeName(t
), TokenTypeName(prev
))
1097 msg
= "Unexpected %s." % (t
.value
)
1099 last
= self
.LastToken()
1100 lineno
= last
.lineno
1102 msg
= "Unexpected end of file after %s." % TokenTypeName(last
)
1103 self
.yaccobj
.restart()
1105 # Attempt to remap the error to a friendlier form
1106 if msg
in ERROR_REMAP
:
1107 msg
= ERROR_REMAP
[msg
]
1109 self
._last
_error
_msg
= msg
1110 self
._last
_error
_lineno
= lineno
1111 self
._last
_error
_pos
= pos
1113 def Warn(self
, node
, msg
):
1114 sys
.stdout
.write(node
.GetLogLine(msg
))
1115 self
.parse_warnings
+= 1
1117 def LastToken(self
):
1118 return self
.lexer
.last
1120 def __init__(self
, lexer
, verbose
=False, debug
=False, mute_error
=False):
1122 self
.tokens
= lexer
.KnownTokens()
1123 self
.yaccobj
= yacc
.yacc(module
=self
, tabmodule
=None, debug
=debug
,
1124 optimize
=0, write_tables
=0)
1125 self
.parse_debug
= debug
1126 self
.verbose
= verbose
1127 self
.mute_error
= mute_error
1128 self
._parse
_errors
= 0
1129 self
._parse
_warnings
= 0
1130 self
._last
_error
_msg
= None
1131 self
._last
_error
_lineno
= 0
1132 self
._last
_error
_pos
= 0
1138 # Production is the set of items sent to a grammar rule resulting in a new
1139 # item being returned.
1141 # p - Is the Yacc production object containing the stack of items
1142 # index - Index into the production of the name for the item being produced.
1143 # cls - The type of item being producted
1144 # childlist - The children of the new item
1145 def BuildProduction(self
, cls
, p
, index
, childlist
=None):
1150 filename
= self
.lexer
.Lexer().filename
1151 lineno
= p
.lineno(index
)
1152 pos
= p
.lexpos(index
)
1153 out
= IDLNode(cls
, filename
, lineno
, pos
, childlist
)
1156 print 'Exception while parsing:'
1157 for num
, item
in enumerate(p
):
1158 print ' [%d] %s' % (num
, ExpandProduction(item
))
1159 if self
.LastToken():
1160 print 'Last token: %s' % str(self
.LastToken())
1163 def BuildNamed(self
, cls
, p
, index
, childlist
=None):
1164 childlist
= ListFromConcat(childlist
)
1165 childlist
.append(self
.BuildAttribute('NAME', p
[index
]))
1166 return self
.BuildProduction(cls
, p
, index
, childlist
)
1168 def BuildComment(self
, cls
, p
, index
):
1171 # Remove comment markers
1173 if name
[:2] == '//':
1174 # For C++ style, remove any leading whitespace and the '//' marker from
1177 for line
in name
.split('\n'):
1178 start
= line
.find('//')
1179 lines
.append(line
[start
+2:])
1181 # For C style, remove ending '*/''
1183 for line
in name
[:-2].split('\n'):
1184 # Remove characters until start marker for this line '*' if found
1185 # otherwise it should be blank.
1186 offs
= line
.find('*')
1188 line
= line
[offs
+ 1:].rstrip()
1192 name
= '\n'.join(lines
)
1193 childlist
= [self
.BuildAttribute('NAME', name
),
1194 self
.BuildAttribute('FORM', form
)]
1195 return self
.BuildProduction(cls
, p
, index
, childlist
)
1200 # Build and Errror node as part of the recovery process.
1203 def BuildError(self
, p
, prod
):
1204 self
._parse
_errors
+= 1
1205 name
= self
.BuildAttribute('NAME', self
._last
_error
_msg
)
1206 line
= self
.BuildAttribute('LINE', self
._last
_error
_lineno
)
1207 pos
= self
.BuildAttribute('POS', self
._last
_error
_pos
)
1208 prod
= self
.BuildAttribute('PROD', prod
)
1210 node
= self
.BuildProduction('Error', p
, 1,
1211 ListFromConcat(name
, line
, pos
, prod
))
1212 if not self
.mute_error
:
1213 node
.Error(self
._last
_error
_msg
)
1220 # An ExtendedAttribute is a special production that results in a property
1221 # which is applied to the adjacent item. Attributes have no children and
1222 # instead represent key/value pairs.
1224 def BuildAttribute(self
, key
, val
):
1225 return IDLAttribute(key
, val
)
1227 def BuildFalse(self
, key
):
1228 return IDLAttribute(key
, Boolean(False))
1230 def BuildTrue(self
, key
):
1231 return IDLAttribute(key
, Boolean(True))
1233 def GetErrors(self
):
1234 # Access lexer errors, despite being private
1235 # pylint: disable=W0212
1236 return self
._parse
_errors
+ self
.lexer
._lex
_errors
1241 # Attempts to parse the current data loaded in the lexer.
1243 def ParseText(self
, filename
, data
):
1244 self
._parse
_errors
= 0
1245 self
._parse
_warnings
= 0
1246 self
._last
_error
_msg
= None
1247 self
._last
_error
_lineno
= 0
1248 self
._last
_error
_pos
= 0
1251 self
.lexer
.Tokenize(data
, filename
)
1252 nodes
= self
.yaccobj
.parse(lexer
=self
.lexer
) or []
1253 name
= self
.BuildAttribute('NAME', filename
)
1254 return IDLNode('File', filename
, 0, 0, nodes
+ [name
])
1256 except lex
.LexError
as lexError
:
1257 sys
.stderr
.write('Error in token: %s\n' % str(lexError
))
1262 def ParseFile(parser
, filename
):
1263 """Parse a file and return a File type of node."""
1264 with
open(filename
) as fileobject
:
1266 out
= parser
.ParseText(filename
, fileobject
.read())
1267 out
.SetProperty('DATETIME', time
.ctime(os
.path
.getmtime(filename
)))
1268 out
.SetProperty('ERRORS', parser
.GetErrors())
1271 except Exception as e
:
1272 last
= parser
.LastToken()
1273 sys
.stderr
.write('%s(%d) : Internal parsing error\n\t%s.\n' % (
1274 filename
, last
.lineno
, str(e
)))
1279 parser
= IDLParser(IDLLexer())
1281 for filename
in argv
:
1282 filenode
= ParseFile(parser
, filename
)
1284 errors
+= filenode
.GetProperty('ERRORS')
1285 nodes
.append(filenode
)
1287 ast
= IDLNode('AST', '__AST__', 0, 0, nodes
)
1289 print '\n'.join(ast
.Tree(accept_props
=['PROD']))
1291 print '\nFound %d errors.\n' % errors
1296 if __name__
== '__main__':
1297 sys
.exit(main(sys
.argv
[1:]))