| #!/usr/bin/env python3 |
| # Copyright 2013 The Chromium Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Parser for Web IDL.""" |
| |
| # |
| # IDL Parser |
| # |
| # The parser uses the PLY yacc library to build a set of parsing rules based |
| # on Web IDL. |
| # |
| # Web IDL, and Web IDL grammar can be found at: |
| # http://webidl.spec.whatwg.org/ |
| # PLY can be found at: |
| # http://www.dabeaz.com/ply/ |
| # |
| # The parser generates a tree by recursively matching sets of items against |
| # defined patterns. When a match is made, that set of items is reduced |
| # to a new item. The new item can provide a match for parent patterns. |
| # In this way an AST is built (reduced) depth first. |
| # |
| |
| # |
| # Disable check for line length and Member as Function due to how grammar rules |
| # are defined with PLY |
| # |
| # pylint: disable=R0201 |
| # pylint: disable=C0301 |
| |
| import os.path |
| import sys |
| |
| # Can't use relative imports if we don't have a parent package. |
| if __package__: |
| from .idl_lexer import IDLLexer |
| from .idl_node import IDLAttribute, IDLNode |
| else: |
| from idl_lexer import IDLLexer |
| from idl_node import IDLAttribute, IDLNode |
| |
| SRC_DIR = os.path.abspath(os.path.dirname(__file__)) |
| # Preserve sys.path[0] as is. |
| # https://docs.python.org/3/library/sys.html?highlight=path[0]#sys.path |
| sys.path.insert(1, os.path.join(SRC_DIR, os.pardir, os.pardir, 'third_party')) |
| from ply import lex |
| from ply import yacc |
| |
| |
| # |
| # ERROR_REMAP |
| # |
| # Maps the standard error formula into a more friendly error message. |
| # |
| ERROR_REMAP = { |
| 'Unexpected ")" after "(".' : 'Empty argument list.', |
| 'Unexpected ")" after ",".' : 'Missing argument.', |
| 'Unexpected "}" after ",".' : 'Trailing comma in block.', |
| 'Unexpected "}" after "{".' : 'Unexpected empty block.', |
| 'Unexpected comment after "}".' : 'Unexpected trailing comment.', |
| 'Unexpected "{" after keyword "enum".' : 'Enum missing name.', |
| 'Unexpected "{" after keyword "struct".' : 'Struct missing name.', |
| 'Unexpected "{" after keyword "interface".' : 'Interface missing name.', |
| } |
| |
| _EXTENDED_ATTRIBUTES_APPLICABLE_TO_TYPES = [ |
| 'Clamp', 'EnforceRange', 'StringContext', 'TreatNullAs'] |
| |
| |
| def Boolean(val): |
| """Convert to strict boolean type.""" |
| if val: |
| return True |
| return False |
| |
| |
| def ListFromConcat(*items): |
| """Generate list by concatenating inputs""" |
| itemsout = [] |
| for item in items: |
| if item is None: |
| continue |
| if type(item) is not type([]): |
| itemsout.append(item) |
| else: |
| itemsout.extend(item) |
| |
| return itemsout |
| |
| def ExpandProduction(p): |
| if type(p) == list: |
| return '[' + ', '.join([ExpandProduction(x) for x in p]) + ']' |
| if type(p) == IDLNode: |
| return 'Node:' + str(p) |
| if type(p) == IDLAttribute: |
| return 'Attr:' + str(p) |
| if type(p) == str: |
| return 'str:' + p |
| return '%s:%s' % (p.__class__.__name__, str(p)) |
| |
| # TokenTypeName |
| # |
| # Generate a string which has the type and value of the token. |
| # |
| def TokenTypeName(t): |
| if t.type == 'SYMBOL': |
| return 'symbol %s' % t.value |
| if t.type in ['HEX', 'INT', 'OCT', 'FLOAT']: |
| return 'value %s' % t.value |
| if t.type == 'string' : |
| return 'string "%s"' % t.value |
| if t.type == 'SPECIAL_COMMENT': |
| return 'comment' |
| if t.type == t.value: |
| return '"%s"' % t.value |
| if t.type == ',': |
| return 'Comma' |
| if t.type == 'identifier': |
| return 'identifier "%s"' % t.value |
| return 'keyword "%s"' % t.value |
| |
| |
| # TODO(bashi): Consider moving this out of idl_parser. |
| def ExtractSpecialComment(comment): |
| if not comment.startswith('/**'): |
| raise ValueError('Special comment must start with /**') |
| if not comment.endswith('*/'): |
| raise ValueError('Special comment must end with */') |
| |
| # Remove comment markers |
| lines = [] |
| for line in comment[2:-2].split('\n'): |
| # Remove characters until start marker for this line '*' if found |
| # otherwise it will be blank. |
| offs = line.find('*') |
| if offs >= 0: |
| line = line[offs + 1:].rstrip() |
| else: |
| # TODO(bashi): We may want to keep |line| as is. |
| line = '' |
| lines.append(line) |
| return '\n'.join(lines) |
| |
| |
| # There are two groups of ExtendedAttributes. |
| # One group can apply to types (It is said "applicable to types"), |
| # but the other cannot apply to types. |
| # This function is intended to divide ExtendedAttributes into those 2 groups. |
| # For more details at |
| # https://webidl.spec.whatwg.org/#extended-attributes-applicable-to-types |
| def DivideExtAttrsIntoApplicableAndNonApplicable(extended_attribute_list): |
| if not extended_attribute_list: |
| return [[], []] |
| else: |
| applicable_to_types = [] |
| non_applicable_to_types = [] |
| for ext_attribute in extended_attribute_list.GetChildren(): |
| if ext_attribute.GetName() in _EXTENDED_ATTRIBUTES_APPLICABLE_TO_TYPES: |
| applicable_to_types.append(ext_attribute) |
| else: |
| non_applicable_to_types.append(ext_attribute) |
| return [applicable_to_types, non_applicable_to_types] |
| |
| # |
| # IDL Parser |
| # |
| # The Parser inherits the from the Lexer to provide PLY with the tokenizing |
| # definitions. Parsing patterns are encoded as functions where p_<name> is |
| # is called any time a patern matching the function documentation is found. |
| # Paterns are expressed in the form of: |
| # """ <new item> : <item> .... |
| # | <item> ....""" |
| # |
| # Where new item is the result of a match against one or more sets of items |
| # separated by the "|". |
| # |
| # The function is called with an object 'p' where p[0] is the output object |
| # and p[n] is the set of inputs for positive values of 'n'. Len(p) can be |
| # used to distinguish between multiple item sets in the pattern. |
| # |
| # The rules can look cryptic at first, but there are a few standard |
| # transforms from the CST to AST. With these in mind, the actions should |
| # be reasonably legible. |
| # |
| # * Ignore production |
| # Discard this branch. Primarily used when one alternative is empty. |
| # |
| # Sample code: |
| # if len(p) > 1: |
| # p[0] = ... |
| # # Note no assignment if len(p) == 1 |
| # |
| # * Eliminate singleton production |
| # Discard this node in the CST, pass the next level down up the tree. |
| # Used to ignore productions only necessary for parsing, but not needed |
| # in the AST. |
| # |
| # Sample code: |
| # p[0] = p[1] |
| # |
| # * Build node |
| # The key type of rule. In this parser, produces object of class IDLNode. |
| # There are several helper functions: |
| # * BuildProduction: actually builds an IDLNode, based on a production. |
| # * BuildAttribute: builds an IDLAttribute, which is a temporary |
| # object to hold a name-value pair, which is then |
| # set as a Property of the IDLNode when the IDLNode |
| # is built. |
| # * BuildNamed: Same as BuildProduction, and sets the 'NAME' property. |
| # * BuildTrue: BuildAttribute with value True, for flags. |
| # |
| # Sample code: |
| # # Build node of type NodeType, with value p[1], and children. |
| # p[0] = self.BuildProduction('NodeType', p, 1, children) |
| # |
| # # Build named node of type NodeType, with name and value p[1]. |
| # # (children optional) |
| # p[0] = self.BuildNamed('NodeType', p, 1) |
| # |
| # # Make a list |
| # # Used if one node has several children. |
| # children = ListFromConcat(p[2], p[3]) |
| # p[0] = self.BuildProduction('NodeType', p, 1, children) |
| # |
| # # Also used to collapse the right-associative tree |
| # # produced by parsing a list back into a single list. |
| # """Foos : Foo Foos |
| # |""" |
| # if len(p) > 1: |
| # p[0] = ListFromConcat(p[1], p[2]) |
| # |
| # # Add children. |
| # # Primarily used to add attributes, produced via BuildTrue. |
| # # p_StaticAttribute |
| # """StaticAttribute : STATIC Attribute""" |
| # p[2].AddChildren(self.BuildTrue('STATIC')) |
| # p[0] = p[2] |
| # |
| # For more details on parsing refer to the PLY documentation at |
| # http://www.dabeaz.com/ply/ |
| # |
| # The parser is based on the Web IDL standard. See: |
| # http://webidl.spec.whatwg.org/#idl-grammar |
| # |
| # Productions with a fractional component in the comment denote additions to |
| # the Web IDL spec, such as allowing string list in extended attributes. |
| class IDLParser(object): |
| def p_Definitions(self, p): |
| """Definitions : SpecialComments ExtendedAttributeList Definition Definitions |
| | ExtendedAttributeList Definition Definitions |
| | """ |
| if len(p) > 4: |
| special_comments_and_attribs = ListFromConcat(p[1], p[2]) |
| p[3].AddChildren(special_comments_and_attribs) |
| p[0] = ListFromConcat(p[3], p[4]) |
| elif len(p) > 1: |
| p[2].AddChildren(p[1]) |
| p[0] = ListFromConcat(p[2], p[3]) |
| |
| def p_Definition(self, p): |
| """Definition : CallbackOrInterfaceOrMixin |
| | Namespace |
| | Partial |
| | Dictionary |
| | Enum |
| | Typedef |
| | IncludesStatement""" |
| p[0] = p[1] |
| |
| # Error recovery for definition |
| def p_DefinitionError(self, p): |
| """Definition : error ';'""" |
| p[0] = self.BuildError(p, 'Definition') |
| |
| def p_ArgumentNameKeyword(self, p): |
| """ArgumentNameKeyword : ASYNC |
| | ATTRIBUTE |
| | CALLBACK |
| | CONST |
| | CONSTRUCTOR |
| | DELETER |
| | DICTIONARY |
| | ENUM |
| | GETTER |
| | INCLUDES |
| | INHERIT |
| | INTERFACE |
| | ITERABLE |
| | MAPLIKE |
| | NAMESPACE |
| | PARTIAL |
| | REQUIRED |
| | SETLIKE |
| | SETTER |
| | STATIC |
| | STRINGIFIER |
| | TYPEDEF |
| | UNRESTRICTED""" |
| p[0] = p[1] |
| |
| def p_CallbackOrInterfaceOrMixin(self, p): |
| """CallbackOrInterfaceOrMixin : CALLBACK CallbackRestOrInterface |
| | INTERFACE InterfaceOrMixin""" |
| p[0] = p[2] |
| |
| def p_InterfaceOrMixin(self, p): |
| """InterfaceOrMixin : InterfaceRest |
| | MixinRest""" |
| p[0] = p[1] |
| |
| def p_InterfaceRest(self, p): |
| """InterfaceRest : identifier Inheritance '{' InterfaceMembers '}' ';'""" |
| p[0] = self.BuildNamed('Interface', p, 1, ListFromConcat(p[2], p[4])) |
| |
| # Error recovery for interface. |
| def p_InterfaceRestError(self, p): |
| """InterfaceRest : identifier Inheritance '{' error""" |
| p[0] = self.BuildError(p, 'Interface') |
| |
| def p_Partial(self, p): |
| """Partial : PARTIAL PartialDefinition""" |
| p[2].AddChildren(self.BuildTrue('PARTIAL')) |
| p[0] = p[2] |
| |
| # Error recovery for Partial |
| def p_PartialError(self, p): |
| """Partial : PARTIAL error""" |
| p[0] = self.BuildError(p, 'Partial') |
| |
| def p_PartialDefinition(self, p): |
| """PartialDefinition : INTERFACE PartialInterfaceOrPartialMixin |
| | PartialDictionary |
| | Namespace""" |
| if len(p) > 2: |
| p[0] = p[2] |
| else: |
| p[0] = p[1] |
| |
| def p_PartialInterfaceOrPartialMixin(self, p): |
| """PartialInterfaceOrPartialMixin : PartialInterfaceRest |
| | MixinRest""" |
| p[0] = p[1] |
| |
| def p_PartialInterfaceRest(self, p): |
| """PartialInterfaceRest : identifier '{' PartialInterfaceMembers '}' ';'""" |
| p[0] = self.BuildNamed('Interface', p, 1, p[3]) |
| |
| def p_InterfaceMembers(self, p): |
| """InterfaceMembers : ExtendedAttributeList InterfaceMember InterfaceMembers |
| |""" |
| if len(p) > 1: |
| p[2].AddChildren(p[1]) |
| p[0] = ListFromConcat(p[2], p[3]) |
| |
| # Error recovery for InterfaceMembers |
| def p_InterfaceMembersError(self, p): |
| """InterfaceMembers : error""" |
| p[0] = self.BuildError(p, 'InterfaceMembers') |
| |
| def p_InterfaceMember(self, p): |
| """InterfaceMember : PartialInterfaceMember |
| | Constructor""" |
| p[0] = p[1] |
| |
| def p_PartialInterfaceMembers(self, p): |
| """PartialInterfaceMembers : ExtendedAttributeList PartialInterfaceMember PartialInterfaceMembers |
| |""" |
| if len(p) > 1: |
| p[2].AddChildren(p[1]) |
| p[0] = ListFromConcat(p[2], p[3]) |
| |
| # Error recovery for InterfaceMembers |
| def p_PartialInterfaceMembersError(self, p): |
| """PartialInterfaceMembers : error""" |
| p[0] = self.BuildError(p, 'PartialInterfaceMembers') |
| |
| def p_PartialInterfaceMember(self, p): |
| """PartialInterfaceMember : Const |
| | Operation |
| | Stringifier |
| | StaticMember |
| | Iterable |
| | AsyncIterable |
| | ReadonlyMember |
| | ReadWriteAttribute |
| | ReadWriteMaplike |
| | ReadWriteSetlike""" |
| p[0] = p[1] |
| |
| def p_Inheritance(self, p): |
| """Inheritance : ':' identifier |
| |""" |
| if len(p) > 1: |
| p[0] = self.BuildNamed('Inherit', p, 2) |
| |
| def p_MixinRest(self, p): |
| """MixinRest : MIXIN identifier '{' MixinMembers '}' ';'""" |
| p[0] = self.BuildNamed('Interface', p, 2, p[4]) |
| p[0].AddChildren(self.BuildTrue('MIXIN')) |
| |
| def p_MixinMembers(self, p): |
| """MixinMembers : ExtendedAttributeList MixinMember MixinMembers |
| |""" |
| if len(p) > 1: |
| p[2].AddChildren(p[1]) |
| p[0] = ListFromConcat(p[2], p[3]) |
| |
| # Error recovery for InterfaceMembers |
| def p_MixinMembersError(self, p): |
| """MixinMembers : error""" |
| p[0] = self.BuildError(p, 'MixinMembers') |
| |
| def p_MixinMember(self, p): |
| """MixinMember : Const |
| | Operation |
| | Stringifier |
| | ReadOnly AttributeRest""" |
| if len(p) == 2: |
| p[0] = p[1] |
| else: |
| p[2].AddChildren(p[1]) |
| p[0] = p[2] |
| |
| def p_IncludesStatement(self, p): |
| """IncludesStatement : identifier INCLUDES identifier ';'""" |
| name = self.BuildAttribute('REFERENCE', p[3]) |
| p[0] = self.BuildNamed('Includes', p, 1, name) |
| |
| def p_CallbackRestOrInterface(self, p): |
| """CallbackRestOrInterface : CallbackRest |
| | INTERFACE InterfaceRest""" |
| if len(p) < 3: |
| p[0] = p[1] |
| else: |
| p[2].AddChildren(self.BuildTrue('CALLBACK')) |
| p[0] = p[2] |
| |
| def p_Const(self, p): |
| """Const : CONST ConstType identifier '=' ConstValue ';'""" |
| value = self.BuildProduction('Value', p, 5, p[5]) |
| p[0] = self.BuildNamed('Const', p, 3, ListFromConcat(p[2], value)) |
| |
| def p_ConstValue(self, p): |
| """ConstValue : BooleanLiteral |
| | FloatLiteral |
| | integer""" |
| if type(p[1]) == str: |
| p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'integer'), |
| self.BuildAttribute('VALUE', p[1])) |
| else: |
| p[0] = p[1] |
| |
| def p_BooleanLiteral(self, p): |
| """BooleanLiteral : TRUE |
| | FALSE""" |
| value = self.BuildAttribute('VALUE', Boolean(p[1] == 'true')) |
| p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'boolean'), value) |
| |
| def p_FloatLiteral(self, p): |
| """FloatLiteral : float |
| | '-' INFINITY |
| | INFINITY |
| | NAN """ |
| if len(p) > 2: |
| val = '-Infinity' |
| else: |
| val = p[1] |
| p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'float'), |
| self.BuildAttribute('VALUE', val)) |
| |
| def p_ConstType(self, p): |
| """ConstType : PrimitiveType Null |
| | identifier Null""" |
| if type(p[1]) == str: |
| p[0] = self.BuildNamed('Typeref', p, 1, p[2]) |
| else: |
| p[1].AddChildren(p[2]) |
| p[0] = p[1] |
| |
| def p_ReadonlyMember(self, p): |
| """ReadonlyMember : READONLY ReadonlyMemberRest""" |
| p[2].AddChildren(self.BuildTrue('READONLY')) |
| p[0] = p[2] |
| |
| def p_ReadonlyMemberRest(self, p): |
| """ReadonlyMemberRest : AttributeRest |
| | MaplikeRest |
| | SetlikeRest""" |
| p[0] = p[1] |
| |
| def p_ReadWriteAttribute(self, p): |
| """ReadWriteAttribute : INHERIT ReadOnly AttributeRest |
| | AttributeRest""" |
| if len(p) > 2: |
| inherit = self.BuildTrue('INHERIT') |
| p[3].AddChildren(ListFromConcat(inherit, p[2])) |
| p[0] = p[3] |
| else: |
| p[0] = p[1] |
| |
| def p_AttributeRest(self, p): |
| """AttributeRest : ATTRIBUTE TypeWithExtendedAttributes AttributeName ';'""" |
| p[0] = self.BuildNamed('Attribute', p, 3, p[2]) |
| |
| def p_AttributeName(self, p): |
| """AttributeName : AttributeNameKeyword |
| | identifier""" |
| p[0] = p[1] |
| |
| def p_AttributeNameKeyword(self, p): |
| """AttributeNameKeyword : ASYNC |
| | REQUIRED""" |
| p[0] = p[1] |
| |
| def p_ReadOnly(self, p): |
| """ReadOnly : READONLY |
| |""" |
| if len(p) > 1: |
| p[0] = self.BuildTrue('READONLY') |
| |
| def p_DefaultValue(self, p): |
| """DefaultValue : ConstValue |
| | string |
| | '[' ']' |
| | '{' '}' |
| | null""" |
| if len(p) == 3: |
| if p[1] == '[': |
| p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'sequence'), |
| self.BuildAttribute('VALUE', '[]')) |
| else: |
| p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'dictionary'), |
| self.BuildAttribute('VALUE', '{}')) |
| elif type(p[1]) == str: |
| p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'DOMString'), |
| self.BuildAttribute('VALUE', p[1])) |
| else: |
| p[0] = p[1] |
| |
| def p_Operation(self, p): |
| """Operation : RegularOperation |
| | SpecialOperation""" |
| p[0] = p[1] |
| |
| def p_RegularOperation(self, p): |
| """RegularOperation : ReturnType OperationRest""" |
| p[2].AddChildren(p[1]) |
| p[0] = p[2] |
| |
| def p_SpecialOperation(self, p): |
| """SpecialOperation : Special RegularOperation""" |
| p[2].AddChildren(p[1]) |
| p[0] = p[2] |
| |
| def p_Special(self, p): |
| """Special : GETTER |
| | SETTER |
| | DELETER""" |
| p[0] = self.BuildTrue(p[1].upper()) |
| |
| def p_OperationRest(self, p): |
| """OperationRest : OptionalOperationName '(' ArgumentList ')' ';'""" |
| arguments = self.BuildProduction('Arguments', p, 2, p[3]) |
| p[0] = self.BuildNamed('Operation', p, 1, arguments) |
| |
| def p_OptionalOperationName(self, p): |
| """OptionalOperationName : OperationName |
| |""" |
| if len(p) > 1: |
| p[0] = p[1] |
| else: |
| p[0] = '' |
| |
| def p_OperationName(self, p): |
| """OperationName : OperationNameKeyword |
| | identifier""" |
| p[0] = p[1] |
| |
| def p_OperationNameKeyword(self, p): |
| """OperationNameKeyword : INCLUDES""" |
| p[0] = p[1] |
| |
| def p_ArgumentList(self, p): |
| """ArgumentList : Argument Arguments |
| |""" |
| if len(p) > 1: |
| p[0] = ListFromConcat(p[1], p[2]) |
| |
| # ArgumentList error recovery |
| def p_ArgumentListError(self, p): |
| """ArgumentList : error """ |
| p[0] = self.BuildError(p, 'ArgumentList') |
| |
| def p_Arguments(self, p): |
| """Arguments : ',' Argument Arguments |
| |""" |
| if len(p) > 1: |
| p[0] = ListFromConcat(p[2], p[3]) |
| |
| # Arguments error recovery |
| def p_ArgumentsError(self, p): |
| """Arguments : ',' error""" |
| p[0] = self.BuildError(p, 'Arguments') |
| |
| def p_Argument(self, p): |
| """Argument : ExtendedAttributeList OPTIONAL TypeWithExtendedAttributes ArgumentName Default |
| | ExtendedAttributeList Type Ellipsis ArgumentName""" |
| if len(p) > 5: |
| p[0] = self.BuildNamed('Argument', p, 4, ListFromConcat(p[3], p[5])) |
| p[0].AddChildren(self.BuildTrue('OPTIONAL')) |
| p[0].AddChildren(p[1]) |
| else: |
| applicable_to_types, non_applicable_to_types = \ |
| DivideExtAttrsIntoApplicableAndNonApplicable(p[1]) |
| if applicable_to_types: |
| attributes = self.BuildProduction('ExtAttributes', p, 1, |
| applicable_to_types) |
| p[2].AddChildren(attributes) |
| p[0] = self.BuildNamed('Argument', p, 4, ListFromConcat(p[2], p[3])) |
| if non_applicable_to_types: |
| attributes = self.BuildProduction('ExtAttributes', p, 1, |
| non_applicable_to_types) |
| p[0].AddChildren(attributes) |
| |
| def p_ArgumentName(self, p): |
| """ArgumentName : ArgumentNameKeyword |
| | identifier""" |
| p[0] = p[1] |
| |
| def p_Ellipsis(self, p): |
| """Ellipsis : ELLIPSIS |
| |""" |
| if len(p) > 1: |
| p[0] = self.BuildNamed('Argument', p, 1) |
| p[0].AddChildren(self.BuildTrue('ELLIPSIS')) |
| |
| def p_ReturnType(self, p): |
| """ReturnType : Type |
| | VOID""" |
| if p[1] == 'void': |
| p[0] = self.BuildProduction('Type', p, 1) |
| p[0].AddChildren(self.BuildNamed('PrimitiveType', p, 1)) |
| else: |
| p[0] = p[1] |
| |
| def p_Constructor(self, p): |
| """Constructor : CONSTRUCTOR '(' ArgumentList ')' ';'""" |
| arguments = self.BuildProduction('Arguments', p, 1, p[3]) |
| p[0] = self.BuildProduction('Constructor', p, 1, arguments) |
| |
| def p_Stringifier(self, p): |
| """Stringifier : STRINGIFIER StringifierRest""" |
| p[0] = self.BuildProduction('Stringifier', p, 1, p[2]) |
| |
| def p_StringifierRest(self, p): |
| """StringifierRest : ReadOnly AttributeRest |
| | ReturnType OperationRest |
| | ';'""" |
| if len(p) == 3: |
| p[2].AddChildren(p[1]) |
| p[0] = p[2] |
| |
| def p_StaticMember(self, p): |
| """StaticMember : STATIC StaticMemberRest""" |
| p[2].AddChildren(self.BuildTrue('STATIC')) |
| p[0] = p[2] |
| |
| def p_StaticMemberRest(self, p): |
| """StaticMemberRest : ReadOnly AttributeRest |
| | ReturnType OperationRest""" |
| if len(p) == 2: |
| p[0] = p[1] |
| else: |
| p[2].AddChildren(p[1]) |
| p[0] = p[2] |
| |
| def p_Iterable(self, p): |
| """Iterable : ITERABLE '<' TypeWithExtendedAttributes OptionalType '>' ';'""" |
| childlist = ListFromConcat(p[3], p[4]) |
| p[0] = self.BuildProduction('Iterable', p, 2, childlist) |
| |
| def p_AsyncIterable(self, p): |
| """AsyncIterable : ASYNC ITERABLE '<' TypeWithExtendedAttributes OptionalType '>' OptionalArgumentList ';'""" |
| childlist = ListFromConcat(p[4], p[5], p[7]) |
| p[0] = self.BuildProduction('AsyncIterable', p, 2, childlist) |
| |
| def p_OptionalType(self, p): |
| """OptionalType : ',' TypeWithExtendedAttributes |
| |""" |
| if len(p) > 1: |
| p[0] = p[2] |
| |
| def p_OptionalArgumentList(self, p): |
| """OptionalArgumentList : '(' ArgumentList ')' |
| |""" |
| if len(p) > 1: |
| p[0] = self.BuildProduction('Arguments', p, 1, p[2]) |
| |
| def p_ReadWriteMaplike(self, p): |
| """ReadWriteMaplike : MaplikeRest""" |
| p[0] = p[1] |
| |
| def p_MaplikeRest(self, p): |
| """MaplikeRest : MAPLIKE '<' TypeWithExtendedAttributes ',' TypeWithExtendedAttributes '>' ';'""" |
| childlist = ListFromConcat(p[3], p[5]) |
| p[0] = self.BuildProduction('Maplike', p, 2, childlist) |
| |
| def p_ReadWriteSetlike(self, p): |
| """ReadWriteSetlike : SetlikeRest""" |
| p[0] = p[1] |
| |
| def p_SetlikeRest(self, p): |
| """SetlikeRest : SETLIKE '<' TypeWithExtendedAttributes '>' ';'""" |
| p[0] = self.BuildProduction('Setlike', p, 2, p[3]) |
| |
| def p_Namespace(self, p): |
| """Namespace : NAMESPACE identifier '{' NamespaceMembers '}' ';'""" |
| p[0] = self.BuildNamed('Namespace', p, 2, p[4]) |
| |
| # Error recovery for namespace. |
| def p_NamespaceError(self, p): |
| """Namespace : NAMESPACE identifier '{' error""" |
| p[0] = self.BuildError(p, 'Namespace') |
| |
| def p_NamespaceMembers(self, p): |
| """NamespaceMembers : NamespaceMember NamespaceMembers |
| | """ |
| if len(p) > 1: |
| p[0] = ListFromConcat(p[1], p[2]) |
| |
| # Error recovery for NamespaceMembers |
| def p_NamespaceMembersError(self, p): |
| """NamespaceMembers : ExtendedAttributeList error""" |
| p[0] = self.BuildError(p, 'NamespaceMembers') |
| |
| def p_NamespaceMember(self, p): |
| """NamespaceMember : Const |
| | ExtendedAttributeList READONLY AttributeRest |
| | ExtendedAttributeList ReturnType OperationRest""" |
| if len(p) == 2: |
| p[0] = p[1] |
| elif p[2] != 'readonly': |
| applicable_to_types, non_applicable_to_types = \ |
| DivideExtAttrsIntoApplicableAndNonApplicable(p[1]) |
| if applicable_to_types: |
| attributes = self.BuildProduction('ExtAttributes', p, 1, |
| applicable_to_types) |
| p[2].AddChildren(attributes) |
| p[3].AddChildren(p[2]) |
| if non_applicable_to_types: |
| attributes = self.BuildProduction('ExtAttributes', p, 1, |
| non_applicable_to_types) |
| p[3].AddChildren(attributes) |
| p[0] = p[3] |
| else: |
| p[3].AddChildren(self.BuildTrue('READONLY')) |
| p[3].AddChildren(p[1]) |
| p[0] = p[3] |
| |
| def p_Dictionary(self, p): |
| """Dictionary : DICTIONARY identifier Inheritance '{' DictionaryMembers '}' ';'""" |
| p[0] = self.BuildNamed('Dictionary', p, 2, ListFromConcat(p[3], p[5])) |
| |
| # Error recovery for regular Dictionary |
| def p_DictionaryError(self, p): |
| """Dictionary : DICTIONARY error ';'""" |
| p[0] = self.BuildError(p, 'Dictionary') |
| |
| # Error recovery for regular Dictionary |
| # (for errors inside dictionary definition) |
| def p_DictionaryError2(self, p): |
| """Dictionary : DICTIONARY identifier Inheritance '{' error""" |
| p[0] = self.BuildError(p, 'Dictionary') |
| |
| def p_DictionaryMembers(self, p): |
| """DictionaryMembers : DictionaryMember DictionaryMembers |
| |""" |
| if len(p) > 1: |
| p[0] = ListFromConcat(p[1], p[2]) |
| |
| # Error recovery for DictionaryMembers |
| def p_DictionaryMembersError(self, p): |
| """DictionaryMembers : ExtendedAttributeList error""" |
| p[0] = self.BuildError(p, 'DictionaryMembers') |
| |
| def p_DictionaryMember(self, p): |
| """DictionaryMember : ExtendedAttributeList REQUIRED TypeWithExtendedAttributes identifier Default ';' |
| | ExtendedAttributeList Type identifier Default ';'""" |
| if len(p) > 6: |
| p[2] = self.BuildTrue('REQUIRED') |
| p[0] = self.BuildNamed('Key', p, 4, ListFromConcat(p[2], p[3], p[5])) |
| p[0].AddChildren(p[1]) |
| else: |
| applicable_to_types, non_applicable_to_types = \ |
| DivideExtAttrsIntoApplicableAndNonApplicable(p[1]) |
| if applicable_to_types: |
| attributes = self.BuildProduction('ExtAttributes', p, 1, |
| applicable_to_types) |
| p[2].AddChildren(attributes) |
| p[0] = self.BuildNamed('Key', p, 3, ListFromConcat(p[2], p[4])) |
| if non_applicable_to_types: |
| attributes = self.BuildProduction('ExtAttributes', p, 1, |
| non_applicable_to_types) |
| p[0].AddChildren(attributes) |
| |
| def p_PartialDictionary(self, p): |
| """PartialDictionary : DICTIONARY identifier '{' DictionaryMembers '}' ';'""" |
| p[0] = self.BuildNamed('Dictionary', p, 2, p[4]) |
| |
| # Error recovery for Partial Dictionary |
| def p_PartialDictionaryError(self, p): |
| """PartialDictionary : DICTIONARY error ';'""" |
| p[0] = self.BuildError(p, 'PartialDictionary') |
| |
| def p_Default(self, p): |
| """Default : '=' DefaultValue |
| |""" |
| if len(p) > 1: |
| p[0] = self.BuildProduction('Default', p, 2, p[2]) |
| |
| def p_Enum(self, p): |
| """Enum : ENUM identifier '{' EnumValueList '}' ';'""" |
| p[0] = self.BuildNamed('Enum', p, 2, p[4]) |
| |
| # Error recovery for Enums |
| def p_EnumError(self, p): |
| """Enum : ENUM error ';'""" |
| p[0] = self.BuildError(p, 'Enum') |
| |
| def p_EnumValueList(self, p): |
| """EnumValueList : string EnumValueListComma""" |
| enum = self.BuildNamed('EnumItem', p, 1) |
| p[0] = ListFromConcat(enum, p[2]) |
| |
| def p_EnumValueListComma(self, p): |
| """EnumValueListComma : ',' EnumValueListString |
| |""" |
| if len(p) > 1: |
| p[0] = p[2] |
| |
| def p_EnumValueListString(self, p): |
| """EnumValueListString : string EnumValueListComma |
| |""" |
| if len(p) > 1: |
| enum = self.BuildNamed('EnumItem', p, 1) |
| p[0] = ListFromConcat(enum, p[2]) |
| |
| def p_CallbackRest(self, p): |
| """CallbackRest : identifier '=' ReturnType '(' ArgumentList ')' ';'""" |
| arguments = self.BuildProduction('Arguments', p, 4, p[5]) |
| p[0] = self.BuildNamed('Callback', p, 1, ListFromConcat(p[3], arguments)) |
| |
| def p_Typedef(self, p): |
| """Typedef : TYPEDEF TypeWithExtendedAttributes identifier ';'""" |
| p[0] = self.BuildNamed('Typedef', p, 3, p[2]) |
| |
| # Error recovery for Typedefs |
| def p_TypedefError(self, p): |
| """Typedef : TYPEDEF error ';'""" |
| p[0] = self.BuildError(p, 'Typedef') |
| |
| def p_Type(self, p): |
| """Type : SingleType |
| | UnionType Null""" |
| if len(p) == 2: |
| p[0] = self.BuildProduction('Type', p, 1, p[1]) |
| else: |
| p[0] = self.BuildProduction('Type', p, 1, ListFromConcat(p[1], p[2])) |
| |
| def p_TypeWithExtendedAttributes(self, p): |
| """ TypeWithExtendedAttributes : ExtendedAttributeList SingleType |
| | ExtendedAttributeList UnionType Null""" |
| if len(p) < 4: |
| p[0] = self.BuildProduction('Type', p, 2, p[2]) |
| else: |
| p[0] = self.BuildProduction('Type', p, 2, ListFromConcat(p[2], p[3])) |
| p[0].AddChildren(p[1]) |
| |
| def p_SingleType(self, p): |
| """SingleType : DistinguishableType |
| | ANY |
| | PromiseType""" |
| if p[1] != 'any': |
| p[0] = p[1] |
| else: |
| p[0] = self.BuildProduction('Any', p, 1) |
| |
| def p_UnionType(self, p): |
| """UnionType : '(' UnionMemberType OR UnionMemberType UnionMemberTypes ')'""" |
| members = ListFromConcat(p[2], p[4], p[5]) |
| p[0] = self.BuildProduction('UnionType', p, 1, members) |
| |
| def p_UnionMemberType(self, p): |
| """UnionMemberType : ExtendedAttributeList DistinguishableType |
| | UnionType Null""" |
| if p[1] is None: |
| p[0] = self.BuildProduction('Type', p, 1, p[2]) |
| elif p[1].GetClass() == 'ExtAttributes': |
| p[0] = self.BuildProduction('Type', p, 1, ListFromConcat(p[2], p[1])) |
| else: |
| p[0] = self.BuildProduction('Type', p, 1, ListFromConcat(p[1], p[2])) |
| |
| def p_UnionMemberTypes(self, p): |
| """UnionMemberTypes : OR UnionMemberType UnionMemberTypes |
| |""" |
| if len(p) > 2: |
| p[0] = ListFromConcat(p[2], p[3]) |
| |
| # Moved BYTESTRING, DOMSTRING, OBJECT to PrimitiveType |
| # Moving all built-in types into PrimitiveType makes it easier to |
| # differentiate between them and 'identifier', since p[1] would be a string in |
| # both cases. |
| def p_DistinguishableType(self, p): |
| """DistinguishableType : PrimitiveType Null |
| | identifier Null |
| | SEQUENCE '<' TypeWithExtendedAttributes '>' Null |
| | FROZENARRAY '<' TypeWithExtendedAttributes '>' Null |
| | OBSERVABLEARRAY '<' TypeWithExtendedAttributes '>' Null |
| | RecordType Null |
| | UNDEFINED Null""" |
| if len(p) == 3: |
| if p[1] == 'undefined': |
| typeref = self.BuildProduction('Undefined', p, 1) |
| elif type(p[1]) == str: |
| typeref = self.BuildNamed('Typeref', p, 1) |
| else: |
| typeref = p[1] |
| p[0] = ListFromConcat(typeref, p[2]) |
| |
| if len(p) == 6: |
| if p[1] == 'sequence': |
| cls = 'Sequence' |
| elif p[1] == 'FrozenArray': |
| cls = 'FrozenArray' |
| elif p[1] == 'ObservableArray': |
| cls = 'ObservableArray' |
| else: |
| assert False |
| p[0] = self.BuildProduction(cls, p, 1, p[3]) |
| p[0] = ListFromConcat(p[0], p[5]) |
| |
| # Added StringType, OBJECT |
| def p_PrimitiveType(self, p): |
| """PrimitiveType : UnsignedIntegerType |
| | UnrestrictedFloatType |
| | StringType |
| | BIGINT |
| | BOOLEAN |
| | BYTE |
| | OCTET |
| | OBJECT""" |
| if type(p[1]) == str: |
| p[0] = self.BuildNamed('PrimitiveType', p, 1) |
| else: |
| p[0] = p[1] |
| |
| def p_UnrestrictedFloatType(self, p): |
| """UnrestrictedFloatType : UNRESTRICTED FloatType |
| | FloatType""" |
| if len(p) == 2: |
| typeref = self.BuildNamed('PrimitiveType', p, 1) |
| else: |
| typeref = self.BuildNamed('PrimitiveType', p, 2) |
| typeref.AddChildren(self.BuildTrue('UNRESTRICTED')) |
| p[0] = typeref |
| |
| def p_FloatType(self, p): |
| """FloatType : FLOAT |
| | DOUBLE""" |
| p[0] = p[1] |
| |
| def p_UnsignedIntegerType(self, p): |
| """UnsignedIntegerType : UNSIGNED IntegerType |
| | IntegerType""" |
| if len(p) == 2: |
| p[0] = p[1] |
| else: |
| p[0] = 'unsigned ' + p[2] |
| |
| def p_IntegerType(self, p): |
| """IntegerType : SHORT |
| | LONG OptionalLong""" |
| if len(p) == 2: |
| p[0] = p[1] |
| else: |
| p[0] = p[1] + p[2] |
| |
| def p_OptionalLong(self, p): |
| """OptionalLong : LONG |
| | """ |
| if len(p) > 1: |
| p[0] = ' ' + p[1] |
| else: |
| p[0] = '' |
| |
| def p_StringType(self, p): |
| """StringType : BYTESTRING |
| | DOMSTRING |
| | USVSTRING""" |
| p[0] = self.BuildNamed('StringType', p, 1) |
| |
| def p_PromiseType(self, p): |
| """PromiseType : PROMISE '<' ReturnType '>'""" |
| p[0] = self.BuildNamed('Promise', p, 1, p[3]) |
| |
| def p_RecordType(self, p): |
| """RecordType : RECORD '<' StringType ',' TypeWithExtendedAttributes '>'""" |
| p[0] = self.BuildProduction('Record', p, 2, ListFromConcat(p[3], p[5])) |
| |
| # Error recovery for RecordType. |
| def p_RecordTypeError(self, p): |
| """RecordType : RECORD '<' error ',' Type '>'""" |
| p[0] = self.BuildError(p, 'RecordType') |
| |
| def p_Null(self, p): |
| """Null : '?' |
| |""" |
| if len(p) > 1: |
| p[0] = self.BuildTrue('NULLABLE') |
| |
| # This rule has custom additions (i.e. SpecialComments). |
| def p_ExtendedAttributeList(self, p): |
| """ExtendedAttributeList : '[' ExtendedAttribute ExtendedAttributes ']' |
| | """ |
| if len(p) > 4: |
| items = ListFromConcat(p[2], p[3]) |
| p[0] = self.BuildProduction('ExtAttributes', p, 1, items) |
| |
| # Error recovery for ExtendedAttributeList |
| def p_ExtendedAttributeListError(self, p): |
| """ExtendedAttributeList : '[' ExtendedAttribute ',' error""" |
| p[0] = self.BuildError(p, 'ExtendedAttributeList') |
| |
| def p_ExtendedAttributes(self, p): |
| """ExtendedAttributes : ',' ExtendedAttribute ExtendedAttributes |
| |""" |
| if len(p) > 1: |
| p[0] = ListFromConcat(p[2], p[3]) |
| |
| # https://webidl.spec.whatwg.org/#idl-extended-attributes |
| # The ExtendedAttribute symbol in Web IDL grammar is very flexible but we |
| # only support following patterns: |
| # [ identifier ] |
| # [ identifier ( ArgumentList ) ] |
| # [ identifier = identifier ] |
| # [ identifier = ( IdentifierList ) ] |
| # [ identifier = identifier ( ArgumentList ) ] |
| # [ identifier = StringLiteral ] |
| # [ identifier = ( StringList ) ] |
| # The first five patterns are specified in the Web IDL spec and the last two |
| # patterns are Blink's custom extension to support [ReflectOnly]. |
| def p_ExtendedAttribute(self, p): |
| """ExtendedAttribute : ExtendedAttributeNoArgs |
| | ExtendedAttributeArgList |
| | ExtendedAttributeIdent |
| | ExtendedAttributeWildcard |
| | ExtendedAttributeIdentList |
| | ExtendedAttributeNamedArgList |
| | ExtendedAttributeStringLiteral |
| | ExtendedAttributeStringLiteralList""" |
| p[0] = p[1] |
| |
| # Add definition for NULL |
| def p_null(self, p): |
| """null : NULL""" |
| p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'NULL'), |
| self.BuildAttribute('VALUE', 'NULL')) |
| |
| def p_IdentifierList(self, p): |
| """IdentifierList : identifier Identifiers""" |
| p[0] = ListFromConcat(p[1], p[2]) |
| |
| def p_Identifiers(self, p): |
| """Identifiers : ',' identifier Identifiers |
| |""" |
| if len(p) > 1: |
| p[0] = ListFromConcat(p[2], p[3]) |
| |
| def p_ExtendedAttributeNoArgs(self, p): |
| """ExtendedAttributeNoArgs : identifier""" |
| p[0] = self.BuildNamed('ExtAttribute', p, 1) |
| |
| def p_ExtendedAttributeArgList(self, p): |
| """ExtendedAttributeArgList : identifier '(' ArgumentList ')'""" |
| arguments = self.BuildProduction('Arguments', p, 2, p[3]) |
| p[0] = self.BuildNamed('ExtAttribute', p, 1, arguments) |
| |
| def p_ExtendedAttributeIdent(self, p): |
| """ExtendedAttributeIdent : identifier '=' identifier""" |
| value = self.BuildAttribute('VALUE', p[3]) |
| p[0] = self.BuildNamed('ExtAttribute', p, 1, value) |
| |
| def p_ExtendedAttributeWildcard(self, p): |
| """ExtendedAttributeWildcard : identifier '=' '*'""" |
| value = self.BuildAttribute('VALUE', p[3]) |
| p[0] = self.BuildNamed('ExtAttribute', p, 1, value) |
| |
| def p_ExtendedAttributeIdentList(self, p): |
| """ExtendedAttributeIdentList : identifier '=' '(' IdentifierList ')'""" |
| value = self.BuildAttribute('VALUE', p[4]) |
| p[0] = self.BuildNamed('ExtAttribute', p, 1, value) |
| |
| def p_ExtendedAttributeNamedArgList(self, p): |
| """ExtendedAttributeNamedArgList : identifier '=' identifier '(' ArgumentList ')'""" |
| args = self.BuildProduction('Arguments', p, 4, p[5]) |
| value = self.BuildNamed('Call', p, 3, args) |
| p[0] = self.BuildNamed('ExtAttribute', p, 1, value) |
| |
| # Blink extension: Add support for string literal Extended Attribute values |
| def p_ExtendedAttributeStringLiteral(self, p): |
| """ExtendedAttributeStringLiteral : identifier '=' StringLiteral""" |
| def UnwrapString(ls): |
| """Reach in and grab the string literal's "NAME".""" |
| return ls[1].value |
| |
| value = self.BuildAttribute('VALUE', UnwrapString(p[3])) |
| p[0] = self.BuildNamed('ExtAttribute', p, 1, value) |
| |
| # Blink extension: Add support for compound Extended Attribute values over |
| # string literals ("A","B") |
| def p_ExtendedAttributeStringLiteralList(self, p): |
| """ExtendedAttributeStringLiteralList : identifier '=' '(' StringLiteralList ')'""" |
| value = self.BuildAttribute('VALUE', p[4]) |
| p[0] = self.BuildNamed('ExtAttribute', p, 1, value) |
| |
| # Blink extension: One or more string literals. The values aren't propagated |
| # as literals, but their by their value only. |
| def p_StringLiteralList(self, p): |
| """StringLiteralList : StringLiteral ',' StringLiteralList |
| | StringLiteral""" |
| def UnwrapString(ls): |
| """Reach in and grab the string literal's "NAME".""" |
| return ls[1].value |
| |
| if len(p) > 3: |
| p[0] = ListFromConcat(UnwrapString(p[1]), p[3]) |
| else: |
| p[0] = ListFromConcat(UnwrapString(p[1])) |
| |
| # Blink extension: Wrap string literal. |
| def p_StringLiteral(self, p): |
| """StringLiteral : string""" |
| p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'DOMString'), |
| self.BuildAttribute('NAME', p[1])) |
| |
| # Blink extension: Treat special comments (/** ... */) as AST nodes to |
| # annotate other nodes. Currently they are used for testing. |
| def p_SpecialComments(self, p): |
| """SpecialComments : SPECIAL_COMMENT SpecialComments |
| | """ |
| if len(p) > 1: |
| p[0] = ListFromConcat(self.BuildSpecialComment(p, 1), p[2]) |
| |
| # |
| # Parser Errors |
| # |
| # p_error is called whenever the parser can not find a pattern match for |
| # a set of items from the current state. The p_error function defined here |
| # is triggered logging an error, and parsing recovery happens as the |
| # p_<type>_error functions defined above are called. This allows the parser |
| # to continue so as to capture more than one error per file. |
| # |
| def p_error(self, t): |
| if t: |
| lineno = t.lineno |
| pos = t.lexpos |
| prev = self.yaccobj.symstack[-1] |
| if type(prev) == lex.LexToken: |
| msg = "Unexpected %s after %s." % ( |
| TokenTypeName(t), TokenTypeName(prev)) |
| else: |
| msg = "Unexpected %s." % (t.value) |
| else: |
| last = self.LastToken() |
| lineno = last.lineno |
| pos = last.lexpos |
| msg = "Unexpected end of file after %s." % TokenTypeName(last) |
| self.yaccobj.restart() |
| |
| # Attempt to remap the error to a friendlier form |
| if msg in ERROR_REMAP: |
| msg = ERROR_REMAP[msg] |
| |
| self._last_error_msg = msg |
| self._last_error_lineno = lineno |
| self._last_error_pos = pos |
| |
| def Warn(self, node, msg): |
| sys.stdout.write(node.GetLogLine(msg)) |
| self.parse_warnings += 1 |
| |
| def LastToken(self): |
| return self.lexer.last |
| |
| def __init__(self, lexer, verbose=False, debug=False, mute_error=False): |
| self.lexer = lexer |
| self.tokens = lexer.KnownTokens() |
| self.yaccobj = yacc.yacc(module=self, tabmodule=None, debug=debug, |
| optimize=0, write_tables=0) |
| # TODO: Make our code compatible with defaulted_states. Currently disabled |
| # for compatibility. |
| self.yaccobj.defaulted_states = {} |
| self.parse_debug = debug |
| self.verbose = verbose |
| self.mute_error = mute_error |
| self._parse_errors = 0 |
| self._parse_warnings = 0 |
| self._last_error_msg = None |
| self._last_error_lineno = 0 |
| self._last_error_pos = 0 |
| |
| |
| # |
| # BuildProduction |
| # |
| # Production is the set of items sent to a grammar rule resulting in a new |
| # item being returned. |
| # |
| # cls - The type of item being producted |
| # p - Is the Yacc production object containing the stack of items |
| # index - Index into the production of the name for the item being produced. |
| # childlist - The children of the new item |
| def BuildProduction(self, cls, p, index, childlist=None): |
| try: |
| if not childlist: |
| childlist = [] |
| |
| filename = self.lexer.Lexer().filename |
| lineno = p.lineno(index) |
| pos = p.lexpos(index) |
| out = IDLNode(cls, filename, lineno, pos, childlist) |
| return out |
| except: |
| print('Exception while parsing:') |
| for num, item in enumerate(p): |
| print(' [%d] %s' % (num, ExpandProduction(item))) |
| if self.LastToken(): |
| print('Last token: %s' % str(self.LastToken())) |
| raise |
| |
| def BuildNamed(self, cls, p, index, childlist=None): |
| childlist = ListFromConcat(childlist) |
| childlist.append(self.BuildAttribute('NAME', p[index])) |
| return self.BuildProduction(cls, p, index, childlist) |
| |
| def BuildSpecialComment(self, p, index): |
| name = ExtractSpecialComment(p[index]) |
| childlist = [self.BuildAttribute('NAME', name)] |
| return self.BuildProduction('SpecialComment', p, index, childlist) |
| |
| # |
| # BuildError |
| # |
| # Build and Error node as part of the recovery process. |
| # |
| # |
| def BuildError(self, p, prod): |
| self._parse_errors += 1 |
| name = self.BuildAttribute('NAME', self._last_error_msg) |
| line = self.BuildAttribute('LINENO', self._last_error_lineno) |
| pos = self.BuildAttribute('POSITION', self._last_error_pos) |
| prod = self.BuildAttribute('PROD', prod) |
| |
| node = self.BuildProduction('Error', p, 1, |
| ListFromConcat(name, line, pos, prod)) |
| if not self.mute_error: |
| node.Error(self._last_error_msg) |
| |
| return node |
| |
| # |
| # BuildAttribute |
| # |
| # An ExtendedAttribute is a special production that results in a property |
| # which is applied to the adjacent item. Attributes have no children and |
| # instead represent key/value pairs. |
| # |
| def BuildAttribute(self, key, val): |
| return IDLAttribute(key, val) |
| |
| def BuildFalse(self, key): |
| return IDLAttribute(key, Boolean(False)) |
| |
| def BuildTrue(self, key): |
| return IDLAttribute(key, Boolean(True)) |
| |
| def GetErrors(self): |
| # Access lexer errors, despite being private |
| # pylint: disable=W0212 |
| return self._parse_errors + self.lexer._lex_errors |
| |
| # |
| # ParseData |
| # |
| # Attempts to parse the current data loaded in the lexer. |
| # |
| def ParseText(self, filename, data): |
| self._parse_errors = 0 |
| self._parse_warnings = 0 |
| self._last_error_msg = None |
| self._last_error_lineno = 0 |
| self._last_error_pos = 0 |
| |
| try: |
| self.lexer.Tokenize(data, filename) |
| nodes = self.yaccobj.parse(lexer=self.lexer) or [] |
| name = self.BuildAttribute('NAME', filename) |
| return IDLNode('File', filename, 0, 0, nodes + [name]) |
| |
| except lex.LexError as lexError: |
| sys.stderr.write('Error in token: %s\n' % str(lexError)) |
| return None |
| |
| |
| |
| def ParseFile(parser, filename): |
| """Parse a file and return a File type of node.""" |
| with open(filename, encoding='utf-8') as fileobject: |
| try: |
| out = parser.ParseText(filename, fileobject.read()) |
| out.SetProperty('ERRORS', parser.GetErrors()) |
| return out |
| |
| except Exception as e: |
| last = parser.LastToken() |
| sys.stderr.write('%s(%d) : Internal parsing error\n\t%s.\n' % ( |
| filename, last.lineno, str(e))) |
| |
| |
| def main(argv): |
| nodes = [] |
| parser = IDLParser(IDLLexer()) |
| errors = 0 |
| for filename in argv: |
| filenode = ParseFile(parser, filename) |
| if (filenode): |
| errors += filenode.GetProperty('ERRORS') |
| nodes.append(filenode) |
| |
| ast = IDLNode('AST', '__AST__', 0, 0, nodes) |
| |
| print('\n'.join(ast.Tree())) |
| if errors: |
| print('\nFound %d errors.\n' % errors) |
| |
| return errors |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main(sys.argv[1:])) |