Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(270)

Unified Diff: Source/bindings/scripts/blink_idl_parser.py

Issue 15801003: IDL parser rewrite in Python (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: [WIP] Full parser Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: Source/bindings/scripts/blink_idl_parser.py
diff --git a/Source/bindings/scripts/blink_idl_parser.py b/Source/bindings/scripts/blink_idl_parser.py
new file mode 100644
index 0000000000000000000000000000000000000000..3122a25de8eff25b3d492f5eee65ada685e10a7d
--- /dev/null
+++ b/Source/bindings/scripts/blink_idl_parser.py
@@ -0,0 +1,533 @@
+# Copyright (C) 2013 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Parser for Blink IDL
+
+The parser uses the PLY (Python Lex-Yacc) library to build a set of parsing
+rules which understand the Blink dialect of Web IDL.
+It derives from a standard Web IDL parser, overriding the rules where Blink
+IDL differs.
+
+Web IDL:
+ http://www.w3.org/TR/WebIDL/
+Web IDL Grammar:
+ http://www.w3.org/TR/WebIDL/#idl-grammar
+PLY:
+ http://www.dabeaz.com/ply/
+"""
+
+# Disable check for line length and Member as Function due to how grammar rules
+# are defined with PLY
+#
+# pylint: disable=R0201
+# pylint: disable=C0301
+#
+# Disable attribute validation, as lint can't import parent class to check
+# pylint: disable=E1101
+
+import os.path
+import sys
+
+# PLY is in Chromium src/third_party/ply
+module_path, module_name = os.path.split(__file__)
+third_party = os.path.join(module_path, os.pardir, os.pardir, os.pardir, os.pardir)
+sys.path.append(third_party)
+from ply import yacc
+
+# Base parser is in Chromium src/tools/idl_parser
+tools_dir = os.path.join(module_path, os.pardir, os.pardir, os.pardir, os.pardir, os.pardir, 'tools')
+sys.path.append(tools_dir)
+from idl_parser.idl_parser import IDLParser, ListFromConcat
+
+from blink_idl_lexer import BlinkIDLLexer
+
+
+REMOVED_RULES = ['Comments', # [0.1]
+ 'CommentsRest', # [0.2]
+ 'AttributeOrOperation', # [30]
+ 'StringifierAttributeOrOperation', # [31]
+ 'Operation', # [35]
+ 'Qualifiers', # [36]
+ ]
+
+
+class BlinkIDLParser(IDLParser):
+ # Numbering scheme is:
+ # [1] for Web IDL spec (or base Pepper IDL)
+ # [b1] for Blink IDL (overrides Web IDL or inserts rule)
+ # [s1] is for numbering Sakamoto
+
+ # [0] Override Pepper definition, since we strip comments
+ def p_Top(self, p):
+ """Top : Definitions"""
+ p[0] = p[1]
+
+ # [3] Override Pepper action, since we distinguish callbacks
+ def p_CallbackOrInterface(self, p):
+ """CallbackOrInterface : CALLBACK CallbackRestOrInterface
+ | Interface"""
+ if len(p) > 2:
+ p[2].AddChildren(self.BuildTrue('Callback'))
+ p[0] = p[2]
+ else:
+ p[0] = p[1]
+
+ # [b10]
+ def p_InterfaceMember(self, p):
+ """InterfaceMember : Const
+ | AttributeOrOperationOrIterator"""
+ # Standard is (no 'OrIterator'):
+ # InterfaceMember : Const
+ # | AttributeOrOperation
+ p[0] = p[1]
+
+ # [b19]
+ # List needed for multiple inheritance in SVG
+ # FIXME: remove when multiple inheritance (which is deprecated) is removed
+ def p_Inheritance(self, p):
+ """Inheritance : ':' ScopedNameList
+ |"""
+ # Standard is (single identifier, not list):
+ # """Inheritance : ':' identifier
+ # |"""
+ if len(p) > 1:
+ p[0] = self.BuildProduction('MultipleInherit', p, 2, p[2])
+
+ # [b27]
+ def p_ConstValue(self, p):
+ """ConstValue : BooleanLiteral
+ | FloatLiteral
+ | IntegerLiteral
+ | StringLiteral
+ | null"""
+ # Standard is (no 'string', fewer 'Literals'):
+ # ConstValue : BooleanLiteral
+ # | FloatLiteral
+ # | integer
+ # | NULL
+ p[0] = p[1]
+
+ # [b27.1]
+ def p_IntegerLiteral(self, p):
+ """IntegerLiteral : integer"""
+ p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'integer'),
+ self.BuildAttribute('NAME', p[1]))
+
+ # [b27.2]
+ def p_StringLiteral(self, p):
+ """StringLiteral : string"""
+ p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'DOMString'),
+ self.BuildAttribute('NAME', p[1]))
+
+ # [b30]
+ # Replaces [30] AttributeOrOperation
+ def p_AttributeOrOperationOrIterator(self, p):
+ """AttributeOrOperationOrIterator : Serializer
+ | Qualifier AttributeOrOperationRest
+ | Attribute
+ | OperationOrIterator"""
+ # Standard is:
+ # AttributeOrOperation : "stringifier" StringifierAttributeOrOperation
+ # | Attribute
+ # | Operation
+ if len(p) > 2:
+ p[2].AddChildren(p[1])
+ p[0] = p[2]
+ else:
+ p[0] = p[1]
+
+ # Serialization
+ # FIXME: can we remove??
+ # [s31]
+ def p_Serializer(self, p):
+ """Serializer : SERIALIZER SerializerRest"""
+ p[0] = p[1]
+
+ # [s32]
+ def p_SerializerRest(self, p):
+ """SerializerRest : OperationRest
+ | '=' SerializationPattern
+ |"""
+ if len(p) > 2:
+ p[0] = p[2]
+ elif len(p) == 2:
+ p[0] = p[1]
+
+ # [s33]
+ def p_SerializationPattern(self, p):
+ """SerializationPattern : '{' SerializationPatternMap '}'
+ | '[' SerializationPatternList ']'
+ | identifier"""
+ if len(p) == 4:
+ p[0] = p[2]
+ else:
+ p[0] = p[1]
+
+ # [s34]
+ def p_SerializationPatternMap(self, p):
+ """SerializationPatternMap : GETTER
+ | INHERIT Identifiers
+ | identifier Identifiers
+ |"""
+ if len(p) > 1:
+ p[0] = p[1]
+
+ # [s35]
+ def p_SerializationPatternList(self, p):
+ """SerializationPatternList : GETTER
+ | identifier Identifiers
+ |"""
+ if len(p) > 1:
+ p[0] = p[1]
+
+ # [s36]
+ def p_Identifiers(self, p):
+ """Identifiers : ',' identifier Identifiers
+ |"""
+ if len(p) > 1:
+ p[0] = p[2]
+
+ # Iterator and SpecialOperation
+ # Follows [35] Operation
+ # [b35.1]
+ def p_OperationOrIterator(self, p):
+ """OperationOrIterator : ReturnType OperationOrIteratorRest
+ | SpecialOperation"""
+ if len(p) > 2:
+ p[0] = self.BuildProduction('OperationOrIterator', p, 2, ListFromConcat(p[1], p[2]))
+ else:
+ p[0] = p[1]
+
+ # [b35.2]
+ def p_OperationOrIteratorRest(self, p):
+ """OperationOrIteratorRest : IteratorRest
+ | OperationRest"""
+ p[0] = p[1]
+
+ # [b35.3]
+ def p_IteratorRest(self, p):
+ """IteratorRest : ITERATOR OptionalIteratorInterfaceOrObject ';'"""
+ p[0] = p[2]
+
+ # [b35.4]
+ def p_OptionalIteratorInterfaceOrObject(self, p):
+ """OptionalIteratorInterfaceOrObject : OptionalIteratorInterface
+ | OBJECT"""
+ p[0] = p[1]
+
+ # [b35.5]
+ def p_OptionalIteratorInterface(self, p):
+ """OptionalIteratorInterface : '=' identifier
+ |"""
+ if len(p) > 2:
+ p[0] = p[2]
+
+ # [b35.6]
+ def p_SpecialOperation(self, p):
+ """SpecialOperation : Special Specials ReturnType OperationRest"""
+ specials = ListFromConcat(p[1], p[2])
+ p[4].AddChildren(p[3])
+ p[0] = p[4]
+ p[0].AddChildren(specials)
+
+ # Follows [36] Qualifiers
+ # [b36.1] [s37]
+ def p_Qualifier(self, p):
+ """Qualifier : STATIC
+ | STRINGIFIER"""
+ if p[1] == 'static':
+ p[0] = self.BuildTrue('STATIC')
+ elif p[1] == 'stringifier':
+ p[0] = self.BuildTrue('STRINGIFIER')
+
+ # [b39]
+ def p_OperationRest(self, p):
+ """OperationRest : OptionalIdentifier '(' ArgumentList ')' Raises ';'"""
+ # Standard is:
+ # OperationRest : ReturnType OptionalIdentifier '(' ArgumentList ')' ';'
+ arguments = self.BuildProduction('Arguments', p, 2, p[3])
+ p[0] = self.BuildNamed('Operation', p, 1, arguments)
+
+ # [b39.1]
+ def p_Raises(self, p):
+ """Raises : RAISES ExceptionList
+ |"""
+ if len(p) > 2:
+ p[0] = p[2]
+
+ # [s39.2]
+ def p_ExceptionList(self, p):
+ """ExceptionList : '(' ScopedNameList ')'"""
+ p[0] = p[2]
+
+ # [b39.3]
+ def p_AttributeOrOperationRest(self, p):
+ """AttributeOrOperationRest : AttributeRest
+ | ReturnType OperationRest
+ | ';'"""
+ if len(p) > 2:
+ p[2].AddChildren(p[1])
+ p[0] = p[2]
+ elif len(p) == 2 and p[1] != ';':
+ p[0] = p[1]
+
+ # [b39.2]
+ def p_AttributeRest(self, p):
+ """AttributeRest : ReadOnly ATTRIBUTE Type identifier Get ';'"""
+ p[0] = self.BuildNamed('Attribute', p, 4, ListFromConcat(p[1], p[3], p[5]))
+
+ # [b39.3]
+ def p_Get(self, p):
+ """Get : InheritsGetter SetRaises
+ | SetGetRaises
+ |"""
+ if len(p) > 2:
+ p[0] = p[2]
+ elif len(p) == 2:
+ p[0] = p[1]
+
+ # [s39.4]
+ def p_InheritsGetter(self, p):
+ """InheritsGetter : INHERITS GETTER"""
+ pass
+
+ # [b47]
+ def p_ExceptionMember(self, p):
+ """ExceptionMember : Const
+ | ExceptionField
+ | Attribute
+ | ExceptionFieldToString"""
+ # Standard is:
+ # ExceptionMember : Const
+ # | ExceptionField
+ p[0] = p[1]
+
+ # [b47.1]
+ def p_ExceptionFieldToString(self, p):
+ """ExceptionFieldToString : Type identifier '(' ')' ';'"""
+ # Needed to handle:
+ # // Override in a Mozilla compatible format
+ # [NotEnumerable] DOMString toString();
+ p[0] = self.BuildNamed('ExceptionFieldToString', p, 2, p[1])
+
+ # Extended attributes
+ # [b49] Override Pepper: remove comment field, since comments stripped
+ # FIXME: Upstream
+ def p_ExtendedAttributeList(self, p):
+ """ExtendedAttributeList : '[' ExtendedAttribute ExtendedAttributes ']'
+ | '[' ']'
+ | """
+ if len(p) > 3:
+ items = ListFromConcat(p[2], p[3])
+ attribs = self.BuildProduction('ExtAttributes', p, 1, items)
+ p[0] = ListFromConcat(p[0], attribs)
+
+ # [b50] Allow optional trailing comma
+ def p_ExtendedAttributes(self, p):
+ """ExtendedAttributes : ',' ExtendedAttribute ExtendedAttributes
+ | ','
+ |"""
+ if len(p) > 2:
+ p[0] = ListFromConcat(p[2], p[3])
+
+ # [b51] Add ExtendedAttributeIdentOrIdent and ExtendedAttributeIdentAndIdent
+ def p_ExtendedAttribute(self, p):
+ """ExtendedAttribute : ExtendedAttributeNoArgs
+ | ExtendedAttributeArgList
+ | ExtendedAttributeIdent
+ | ExtendedAttributeIdentOrIdent
+ | ExtendedAttributeIdentAndIdent
+ | ExtendedAttributeNamedArgList"""
+ p[0] = p[1]
+
+ # FIXME: Upstream UnionType
+ # [59]
+ 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)
+
+ # [60]
+ def p_UnionMemberType(self, p):
+ """UnionMemberType : NonAnyType
+ | UnionType TypeSuffix
+ | ANY '[' ']' TypeSuffix"""
+ if len(p) == 2:
+ p[0] = p[1]
+ elif len(p) == 3:
+ p[0] = ListFromConcat(p[1], p[2])
+ else:
+ p[0] = ListFromConcat(self.BuildProduction('Any', p, 1), p[4])
+
+ # [61]
+ def p_UnionMemberTypes(self, p):
+ """UnionMemberTypes : OR UnionMemberType UnionMemberTypes
+ |"""
+ if len(p) > 1:
+ p[0] = ListFromConcat(p[2], p[3])
+
+ # [70] Override Pepper to remove non-standard sized array
+ # FIXME: upstream should remove sized array from base
+ def p_TypeSuffix(self, p):
+ """TypeSuffix : '[' ']' TypeSuffix
+ | '?' TypeSuffixStartingWithArray
+ |"""
+ if len(p) == 4:
+ p[0] = self.BuildProduction('Array', p, 1, p[3])
+
+ if len(p) == 3:
+ p[0] = ListFromConcat(self.BuildTrue('NULLABLE'), p[2])
+
+ # [b76.1]
+ def p_ExtendedAttributeIdentOrIdent(self, p):
+ """ExtendedAttributeIdentOrIdent : identifier '=' identifier '|' identifier"""
+ # Used in a handful of files
+ value = self.BuildAttribute('VALUE', p[3] + '|' + p[5])
+ p[0] = self.BuildNamed('ExtAttribute', p, 1, value)
+
+ # [b76.2]
+ def p_ExtendedAttributeIdentAndIdent(self, p):
+ """ExtendedAttributeIdentAndIdent : identifier '=' identifier '&' identifier"""
+ # FIXME: Used only in NavigatorContentUtils.idl,
+ # which doesn't appear to be parsed - remove?
+ value = self.BuildAttribute('VALUE', p[3] + '&' + p[5])
+ p[0] = self.BuildNamed('ExtAttribute', p, 1, value)
+
+ # Exceptions
+ # [s25]
+ def p_SetGetRaises(self, p):
+ """SetGetRaises : SetRaises GetRaises2
+ | GetRaises
+ | SetGetRaises2"""
+ p[0] = p[1]
+
+ # [s27]
+ def p_GetRaises(self, p):
+ """GetRaises : GETTER RAISES ExceptionList"""
+ p[0] = p[3]
+
+ # [s27.5]
+ def p_GetRaises2(self, p):
+ """GetRaises2 : ',' GETTER RAISES ExceptionList
+ |"""
+ p[0] = p[4]
+
+ # [s28]
+ def p_SetRaises(self, p):
+ """SetRaises : SETTER RAISES ExceptionList"""
+ p[0] = p[3]
+
+ # [s29]
+ def p_SetGetRaises2(self, p):
+ """SetGetRaises2 : GetRaises3 SetRaises3"""
+ # FIXME
+ p[0] = p[1]
+
+ # [s29a]
+ def p_GetRaises3(self, p):
+ """GetRaises3 : GETRAISES ExceptionList
+ |"""
+ p[0] = p[2]
+
+ # [s29b]
+ def p_SetRaises3(self, p):
+ """SetRaises3 : SETRAISES ExceptionList
+ |"""
+ p[0] = p[2]
+
+ # Scoped names
+ # FIXME: remove??
+ # [s63]
+ def p_ScopedNameList(self, p):
+ """ScopedNameList : ScopedName ScopedNames"""
+ scoped_name = self.BuildNamed('ScopedName', p, 1)
+ p[0] = ListFromConcat(scoped_name, p[2])
+
+ # [s64]
+ def p_ScopedNames(self, p):
+ """ScopedNames : ',' ScopedName ScopedNames
+ |"""
+ if len(p) > 1:
+ scoped_name = self.BuildNamed('ScopedName', p, 2)
+ p[0] = ListFromConcat(scoped_name, p[3])
+
+ # [s65]
+ def p_ScopedName(self, p):
+ """ScopedName : AbsoluteScopedName
+ | RelativeScopedName"""
+ p[0] = p[1]
+
+ # [s66]
+ def p_AbsoluteScopedName(self, p):
+ """AbsoluteScopedName : SCOPE_RESOLUTION identifier ScopedNameParts"""
+ p[0] = p[2]
+
+ # [s67]
+ def p_RelativeScopedName(self, p):
+ """RelativeScopedName : identifier ScopedNameParts"""
+ p[0] = p[1]
+
+ # [s68]
+ def p_ScopedNameParts(self, p):
+ """ScopedNameParts : SCOPE_RESOLUTION identifier ScopedNameParts
+ |"""
+ # if len(p) > 1:
+ # p[0] = p[2]
+ pass
+
+ def __dir__(self):
+ # Remove rules from listing so yacc doesn't parse them
+ keys = set(self.__dict__.keys() + dir(self.__class__))
+ for rule in REMOVED_RULES:
+ keys.remove('p_' + rule)
+ return list(keys)
+
+ def __init__(self, lexer, verbose=False, debug=False, mute_error=False):
+ self.lexer = lexer
+ self.tokens = lexer.KnownTokens()
+ # SLR yields faster table generation (same output), ok b/c LL(1) grammar
+ self.yaccobj = yacc.yacc(module=self, debug=debug, method='SLR')
+ # Optimized mode, improves startup time of yacc compiler
+ # optimize=1
+ # Turn on table generation with:
+ # write_tables=1
+ # Turn off table generation with:
+ # tabmodule=None, write_tables=0
+ 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
+
+
+# If run by itself, attempt to build the parser
+if __name__ == '__main__':
+ parser = BlinkIDLParser(BlinkIDLLexer())

Powered by Google App Engine
This is Rietveld 408576698