Copy idl_parser to WebCore
Moves idl_parser from a mirrored dependency within tools/.
Corresponds to commit id 5fb1ebf49d235b5a70c9f49047e83b0654031eb7.
Adds the appropriate LICENSEs and removes OWNERS and PRESUBMIT.py.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..70bcb8a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+//
+// The Chromium Authors can be found at
+// http://src.chromium.org/svn/trunk/src/AUTHORS
+//
+// 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.
diff --git a/idl_parser/LICENSE b/idl_parser/LICENSE
new file mode 100644
index 0000000..a32e00c
--- /dev/null
+++ b/idl_parser/LICENSE
@@ -0,0 +1,27 @@
+// Copyright 2015 The Chromium Authors. 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.
diff --git a/idl_parser/idl_lexer.py b/idl_parser/idl_lexer.py
new file mode 100644
index 0000000..60b8288
--- /dev/null
+++ b/idl_parser/idl_lexer.py
@@ -0,0 +1,294 @@
+#!/usr/bin/env python
+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+""" Lexer for Web IDL
+
+The lexer uses the PLY library to build a tokenizer which understands
+Web IDL tokens.
+
+Web IDL, and Web IDL regular expressions can be found at:
+ http://heycam.github.io/webidl/
+PLY can be found at:
+ http://www.dabeaz.com/ply/
+"""
+
+import os.path
+import sys
+
+SRC_DIR = os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)
+sys.path.insert(0, os.path.join(SRC_DIR, 'third_party'))
+from ply import lex
+
+
+#
+# IDL Lexer
+#
+class IDLLexer(object):
+ # 'literals' is a value expected by lex which specifies a list of valid
+ # literal tokens, meaning the token type and token value are identical.
+ literals = r'"*.(){}[],;:=+-/~|&^?<>'
+
+ # 't_ignore' contains ignored characters (spaces and tabs)
+ t_ignore = ' \t'
+
+ # 'tokens' is a value required by lex which specifies the complete list
+ # of valid token types.
+ tokens = [
+ # Data types
+ 'float',
+ 'integer',
+ 'string',
+
+ # Symbol and keywords types
+ 'SPECIAL_COMMENT',
+ 'identifier',
+
+ # MultiChar operators
+ 'ELLIPSIS',
+ ]
+
+ # 'keywords' is a map of string to token type. All tokens matching
+ # KEYWORD_OR_SYMBOL are matched against keywords dictionary, to determine
+ # if the token is actually a keyword.
+ keywords = {
+ 'any' : 'ANY',
+ 'attribute' : 'ATTRIBUTE',
+ 'boolean' : 'BOOLEAN',
+ 'byte' : 'BYTE',
+ 'ByteString' : 'BYTESTRING',
+ 'callback' : 'CALLBACK',
+ 'const' : 'CONST',
+ 'creator' : 'CREATOR',
+ 'Date' : 'DATE',
+ 'deleter' : 'DELETER',
+ 'dictionary' : 'DICTIONARY',
+ 'DOMString' : 'DOMSTRING',
+ 'double' : 'DOUBLE',
+ 'enum' : 'ENUM',
+ 'false' : 'FALSE',
+ 'float' : 'FLOAT',
+ 'FrozenArray' : 'FROZENARRAY',
+ 'getter': 'GETTER',
+ 'implements' : 'IMPLEMENTS',
+ 'Infinity' : 'INFINITY',
+ 'inherit' : 'INHERIT',
+ 'interface' : 'INTERFACE',
+ 'iterable': 'ITERABLE',
+ 'legacycaller' : 'LEGACYCALLER',
+ 'long' : 'LONG',
+ 'maplike': 'MAPLIKE',
+ 'namespace' : 'NAMESPACE',
+ 'Nan' : 'NAN',
+ 'null' : 'NULL',
+ 'object' : 'OBJECT',
+ 'octet' : 'OCTET',
+ 'optional' : 'OPTIONAL',
+ 'or' : 'OR',
+ 'partial' : 'PARTIAL',
+ 'Promise' : 'PROMISE',
+ 'readonly' : 'READONLY',
+ 'RegExp' : 'REGEXP',
+ 'record' : 'RECORD',
+ 'required' : 'REQUIRED',
+ 'sequence' : 'SEQUENCE',
+ 'serializer' : 'SERIALIZER',
+ 'setlike' : 'SETLIKE',
+ 'setter': 'SETTER',
+ 'short' : 'SHORT',
+ 'static' : 'STATIC',
+ 'stringifier' : 'STRINGIFIER',
+ 'typedef' : 'TYPEDEF',
+ 'true' : 'TRUE',
+ 'unsigned' : 'UNSIGNED',
+ 'unrestricted' : 'UNRESTRICTED',
+ 'USVString' : 'USVSTRING',
+ 'void' : 'VOID'
+ }
+
+ # Token definitions
+ #
+ # Lex assumes any value or function in the form of 't_<TYPE>' represents a
+ # regular expression where a match will emit a token of type <TYPE>. In the
+ # case of a function, the function is called when a match is made. These
+ # definitions come from WebIDL.
+ #
+ # These need to be methods for lexer construction, despite not using self.
+ # pylint: disable=R0201
+ def t_ELLIPSIS(self, t):
+ r'\.\.\.'
+ return t
+
+ # Regex needs to be in the docstring
+ # pylint: disable=C0301
+ def t_float(self, t):
+ r'-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+)'
+ return t
+
+ def t_integer(self, t):
+ r'-?([1-9][0-9]*|0[Xx][0-9A-Fa-f]+|0[0-7]*)'
+ return t
+
+
+ # A line ending '\n', we use this to increment the line number
+ def t_LINE_END(self, t):
+ r'\n+'
+ self.AddLines(len(t.value))
+
+ # We do not process escapes in the IDL strings. Strings are exclusively
+ # used for attributes and enums, and not used as typical 'C' constants.
+ def t_string(self, t):
+ r'"[^"]*"'
+ t.value = t.value[1:-1]
+ self.AddLines(t.value.count('\n'))
+ return t
+
+ # A Javadoc style comment: /** xxx */
+ # Unlike t_COMMENT, this is NOT ignored.
+ # Also note that this should be defined before t_COMMENT.
+ def t_SPECIAL_COMMENT(self, t):
+ r'/\*\*(.|\n)+?\*/'
+ self.AddLines(t.value.count('\n'))
+ return t
+
+ # A C or C++ style comment: /* xxx */ or //
+ # This token is ignored.
+ def t_COMMENT(self, t):
+ r'(/\*(.|\n)*?\*/)|(//.*(\n[ \t]*//.*)*)'
+ self.AddLines(t.value.count('\n'))
+
+ # A symbol or keyword.
+ def t_KEYWORD_OR_SYMBOL(self, t):
+ r'_?[A-Za-z][A-Za-z_0-9]*'
+
+ # All non-keywords are assumed to be symbols
+ t.type = self.keywords.get(t.value, 'identifier')
+
+ # We strip leading underscores so that you can specify symbols with the same
+ # value as a keywords (E.g. a dictionary named 'interface').
+ if t.value[0] == '_':
+ t.value = t.value[1:]
+ return t
+
+ def t_ANY_error(self, t):
+ msg = 'Unrecognized input'
+ line = self.Lexer().lineno
+
+ # If that line has not been accounted for, then we must have hit
+ # EoF, so compute the beginning of the line that caused the problem.
+ if line >= len(self.index):
+ # Find the offset in the line of the first word causing the issue
+ word = t.value.split()[0]
+ offs = self.lines[line - 1].find(word)
+ # Add the computed line's starting position
+ self.index.append(self.Lexer().lexpos - offs)
+ msg = 'Unexpected EoF reached after'
+
+ pos = self.Lexer().lexpos - self.index[line]
+ out = self.ErrorMessage(line, pos, msg)
+ sys.stderr.write(out + '\n')
+ self._lex_errors += 1
+
+
+ def AddLines(self, count):
+ # Set the lexer position for the beginning of the next line. In the case
+ # of multiple lines, tokens can not exist on any of the lines except the
+ # last one, so the recorded value for previous lines are unused. We still
+ # fill the array however, to make sure the line count is correct.
+ self.Lexer().lineno += count
+ for _ in range(count):
+ self.index.append(self.Lexer().lexpos)
+
+ def FileLineMsg(self, line, msg):
+ # Generate a message containing the file and line number of a token.
+ filename = self.Lexer().filename
+ if filename:
+ return "%s(%d) : %s" % (filename, line + 1, msg)
+ return "<BuiltIn> : %s" % msg
+
+ def SourceLine(self, line, pos):
+ # Create a source line marker
+ caret = ' ' * pos + '^'
+ # We decrement the line number since the array is 0 based while the
+ # line numbers are 1 based.
+ return "%s\n%s" % (self.lines[line - 1], caret)
+
+ def ErrorMessage(self, line, pos, msg):
+ return "\n%s\n%s" % (
+ self.FileLineMsg(line, msg),
+ self.SourceLine(line, pos))
+
+#
+# Tokenizer
+#
+# The token function returns the next token provided by IDLLexer for matching
+# against the leaf paterns.
+#
+ def token(self):
+ tok = self.Lexer().token()
+ if tok:
+ self.last = tok
+ return tok
+
+
+ def GetTokens(self):
+ outlist = []
+ while True:
+ t = self.Lexer().token()
+ if not t:
+ break
+ outlist.append(t)
+ return outlist
+
+ def Tokenize(self, data, filename='__no_file__'):
+ lexer = self.Lexer()
+ lexer.lineno = 1
+ lexer.filename = filename
+ lexer.input(data)
+ self.lines = data.split('\n')
+
+ def KnownTokens(self):
+ return self.tokens
+
+ def Lexer(self):
+ if not self._lexobj:
+ self._lexobj = lex.lex(object=self, lextab=None, optimize=0)
+ return self._lexobj
+
+ def _AddToken(self, token):
+ if token in self.tokens:
+ raise RuntimeError('Same token: ' + token)
+ self.tokens.append(token)
+
+ def _AddTokens(self, tokens):
+ for token in tokens:
+ self._AddToken(token)
+
+ def _AddKeywords(self, keywords):
+ for key in keywords:
+ value = key.upper()
+ self._AddToken(value)
+ self.keywords[key] = value
+
+ def _DelKeywords(self, keywords):
+ for key in keywords:
+ self.tokens.remove(key.upper())
+ del self.keywords[key]
+
+ def __init__(self):
+ self.index = [0]
+ self._lex_errors = 0
+ self.linex = []
+ self.filename = None
+ self.keywords = {}
+ self.tokens = []
+ self._AddTokens(IDLLexer.tokens)
+ self._AddKeywords(IDLLexer.keywords)
+ self._lexobj = None
+ self.last = None
+ self.lines = None
+
+# If run by itself, attempt to build the lexer
+if __name__ == '__main__':
+ lexer_object = IDLLexer()
diff --git a/idl_parser/idl_lexer_test.py b/idl_parser/idl_lexer_test.py
new file mode 100644
index 0000000..f9e8a36
--- /dev/null
+++ b/idl_parser/idl_lexer_test.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python
+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import unittest
+
+from idl_lexer import IDLLexer
+
+
+#
+# FileToTokens
+#
+# From a source file generate a list of tokens.
+#
+def FileToTokens(lexer, filename):
+ with open(filename, 'rb') as srcfile:
+ lexer.Tokenize(srcfile.read(), filename)
+ return lexer.GetTokens()
+
+
+#
+# TextToTokens
+#
+# From a source file generate a list of tokens.
+#
+def TextToTokens(lexer, text):
+ lexer.Tokenize(text)
+ return lexer.GetTokens()
+
+
+class WebIDLLexer(unittest.TestCase):
+ def setUp(self):
+ self.lexer = IDLLexer()
+ cur_dir = os.path.dirname(os.path.realpath(__file__))
+ self.filenames = [
+ os.path.join(cur_dir, 'test_lexer/values.in'),
+ os.path.join(cur_dir, 'test_lexer/keywords.in')
+ ]
+
+ #
+ # testRebuildText
+ #
+ # From a set of tokens, generate a new source text by joining with a
+ # single space. The new source is then tokenized and compared against the
+ # old set.
+ #
+ def testRebuildText(self):
+ for filename in self.filenames:
+ tokens1 = FileToTokens(self.lexer, filename)
+ to_text = '\n'.join(['%s' % t.value for t in tokens1])
+ tokens2 = TextToTokens(self.lexer, to_text)
+
+ count1 = len(tokens1)
+ count2 = len(tokens2)
+ self.assertEqual(count1, count2)
+
+ for i in range(count1):
+ msg = 'Value %s does not match original %s on line %d of %s.' % (
+ tokens2[i].value, tokens1[i].value, tokens1[i].lineno, filename)
+ self.assertEqual(tokens1[i].value, tokens2[i].value, msg)
+
+ #
+ # testExpectedType
+ #
+ # From a set of tokens pairs, verify the type field of the second matches
+ # the value of the first, so that:
+ # integer 123 float 1.1 ...
+ # will generate a passing test, when the first token has both the type and
+ # value of the keyword integer and the second has the type of integer and
+ # value of 123 and so on.
+ #
+ def testExpectedType(self):
+ for filename in self.filenames:
+ tokens = FileToTokens(self.lexer, filename)
+ count = len(tokens)
+ self.assertTrue(count > 0)
+ self.assertFalse(count & 1)
+
+ index = 0
+ while index < count:
+ expect_type = tokens[index].value
+ actual_type = tokens[index + 1].type
+ msg = 'Type %s does not match expected %s on line %d of %s.' % (
+ actual_type, expect_type, tokens[index].lineno, filename)
+ index += 2
+ self.assertEqual(expect_type, actual_type, msg)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/idl_parser/idl_node.py b/idl_parser/idl_node.py
new file mode 100644
index 0000000..266a526
--- /dev/null
+++ b/idl_parser/idl_node.py
@@ -0,0 +1,224 @@
+#!/usr/bin/env python
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import sys
+
+#
+# IDL Node
+#
+# IDL Node defines the IDLAttribute and IDLNode objects which are constructed
+# by the parser as it processes the various 'productions'. The IDLAttribute
+# objects are assigned to the IDLNode's property dictionary instead of being
+# applied as children of The IDLNodes, so they do not exist in the final tree.
+# The AST of IDLNodes is the output from the parsing state and will be used
+# as the source data by the various generators.
+#
+
+
+#
+# CopyToList
+#
+# Takes an input item, list, or None, and returns a new list of that set.
+def CopyToList(item):
+ # If the item is 'Empty' make it an empty list
+ if not item:
+ item = []
+
+ # If the item is not a list
+ if type(item) is not type([]):
+ item = [item]
+
+ # Make a copy we can modify
+ return list(item)
+
+
+# IDLSearch
+#
+# A temporary object used by the parsing process to hold an Extended Attribute
+# which will be passed as a child to a standard IDLNode.
+#
+class IDLSearch(object):
+ def __init__(self):
+ self.depth = 0
+
+ def Enter(self, node):
+ pass
+
+ def Exit(self, node):
+ pass
+
+
+# IDLAttribute
+#
+# A temporary object used by the parsing process to hold an Extended Attribute
+# which will be passed as a child to a standard IDLNode.
+#
+class IDLAttribute(object):
+ def __init__(self, name, value):
+ self._cls = 'Property'
+ self.name = name
+ self.value = value
+
+ def __str__(self):
+ return '%s=%s' % (self.name, self.value)
+
+ def GetClass(self):
+ return self._cls
+
+#
+# IDLNode
+#
+# This class implements the AST tree, providing the associations between
+# parents and children. It also contains a namepsace and propertynode to
+# allow for look-ups. IDLNode is derived from IDLRelease, so it is
+# version aware.
+#
+class IDLNode(object):
+ VERBOSE_PROPS = [
+ 'PROD', 'NAME', 'VALUE', 'TYPE',
+ 'ERRORS', 'WARNINGS', 'FILENAME', 'LINENO', 'POSITION', 'DATETIME',
+ ]
+
+ def __init__(self, cls, filename, lineno, pos, children=None):
+ self._cls = cls
+ self._properties = {
+ 'ERRORS' : [],
+ 'WARNINGS': [],
+ 'FILENAME': filename,
+ 'LINENO' : lineno,
+ 'POSITION' : pos,
+ }
+
+ self._children = []
+ self._parent = None
+ self.AddChildren(children)
+
+#
+#
+#
+ # Return a string representation of this node
+ def __str__(self):
+ name = self.GetProperty('NAME','')
+ value = self.GetProperty('VALUE')
+ if value or value == '':
+ return '%s(%s) = "%s"' % (self._cls, name, value)
+ return '%s(%s)' % (self._cls, name)
+
+ def GetLogLine(self, msg):
+ filename, lineno = self.GetFileAndLine()
+ return '%s(%d) : %s\n' % (filename, lineno, msg)
+
+ # Log an error for this object
+ def Error(self, msg):
+ self.GetProperty('ERRORS').append(msg)
+ sys.stderr.write(self.GetLogLine('error: ' + msg))
+
+ # Log a warning for this object
+ def Warning(self, msg):
+ self.GetProperty('WARNINGS').append(msg)
+ sys.stdout.write(self.GetLogLine('warning:' + msg))
+
+ # Return file and line number for where node was defined
+ def GetFileAndLine(self):
+ return self.GetProperty('FILENAME'), self.GetProperty('LINENO')
+
+ def GetClass(self):
+ return self._cls
+
+ def GetName(self):
+ return self.GetProperty('NAME')
+
+ def GetParent(self):
+ return self._parent
+
+ def Traverse(self, search, filter_nodes):
+ if self._cls in filter_nodes:
+ return ''
+
+ search.Enter(self)
+ search.depth += 1
+ for child in self._children:
+ child.Traverse(search, filter_nodes)
+ search.depth -= 1
+ search.Exit(self)
+
+
+ def Tree(self, filter_nodes=None, suppress_props=VERBOSE_PROPS):
+ class DumpTreeSearch(IDLSearch):
+ def __init__(self, props):
+ IDLSearch.__init__(self)
+ self.out = []
+ self.props = props or []
+
+ def Enter(self, node):
+ tab = ''.rjust(self.depth * 2)
+ self.out.append(tab + str(node))
+
+ proplist = []
+ for key, value in node.GetProperties().iteritems():
+ if key not in self.props:
+ proplist.append(tab + ' %s: %s' % (key, str(value)))
+ if proplist:
+ self.out.extend(proplist)
+
+ if filter_nodes == None:
+ filter_nodes = ['SpecialComment']
+
+ search = DumpTreeSearch(suppress_props)
+ self.Traverse(search, filter_nodes)
+ return search.out
+
+#
+# Search related functions
+#
+ # Check if node is of a given type
+ def IsA(self, *typelist):
+ if self._cls in typelist:
+ return True
+ return False
+
+ # Get a list of all children
+ def GetChildren(self):
+ return self._children
+
+ def GetListOf(self, *keys):
+ out = []
+ for child in self.GetChildren():
+ if child.GetClass() in keys:
+ out.append(child)
+ return out
+
+ def GetOneOf(self, *keys):
+ out = self.GetListOf(*keys)
+ if out:
+ return out[0]
+ return None
+
+ def AddChildren(self, children):
+ children = CopyToList(children)
+ for child in children:
+ if not child:
+ continue
+ if type(child) == IDLAttribute:
+ self.SetProperty(child.name, child.value)
+ continue
+ if type(child) == IDLNode:
+ child._parent = self
+ self._children.append(child)
+ continue
+ raise RuntimeError('Adding child of type %s.\n' % type(child).__name__)
+
+
+#
+# Property Functions
+#
+ def SetProperty(self, name, val):
+ self._properties[name] = val
+
+ def GetProperty(self, name, default=None):
+ return self._properties.get(name, default)
+
+ def GetProperties(self):
+ return self._properties
diff --git a/idl_parser/idl_parser.py b/idl_parser/idl_parser.py
new file mode 100644
index 0000000..d498a66
--- /dev/null
+++ b/idl_parser/idl_parser.py
@@ -0,0 +1,1310 @@
+#!/usr/bin/env python
+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# 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://heycam.github.io/webidl/
+# 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
+import time
+
+from idl_lexer import IDLLexer
+from idl_node import IDLAttribute
+from idl_node import IDLNode
+
+SRC_DIR = os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)
+sys.path.insert(0, os.path.join(SRC_DIR, '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', '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://heycam.github.io/webidl/#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://heycam.github.io/webidl/#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 : CallbackOrInterface
+ | Namespace
+ | Partial
+ | Dictionary
+ | Enum
+ | Typedef
+ | ImplementsStatement"""
+ p[0] = p[1]
+
+ # Error recovery for definition
+ def p_DefinitionError(self, p):
+ """Definition : error ';'"""
+ p[0] = self.BuildError(p, 'Definition')
+
+ def p_CallbackOrInterface(self, p):
+ """CallbackOrInterface : CALLBACK CallbackRestOrInterface
+ | Interface"""
+ if len(p) > 2:
+ if p[2].GetClass() != 'Callback':
+ p[2].AddChildren(self.BuildTrue('CALLBACK'))
+ p[0] = p[2]
+ else:
+ p[0] = p[1]
+
+ def p_CallbackRestOrInterface(self, p):
+ """CallbackRestOrInterface : CallbackRest
+ | Interface"""
+ p[0] = p[1]
+
+ def p_Interface(self, p):
+ """Interface : INTERFACE identifier Inheritance '{' InterfaceMembers '}' ';'"""
+ p[0] = self.BuildNamed('Interface', p, 2, ListFromConcat(p[3], p[5]))
+
+ # Error recovery for interface.
+ def p_InterfaceError(self, p):
+ """Interface : INTERFACE 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 : PartialDictionary
+ | PartialInterface
+ | Namespace"""
+ p[0] = p[1]
+
+ def p_PartialInterface(self, p):
+ """PartialInterface : INTERFACE identifier '{' InterfaceMembers '}' ';'"""
+ p[0] = self.BuildNamed('Interface', p, 2, p[4])
+
+ 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')
+
+ # Removed unsupported: Serializer
+ def p_InterfaceMember(self, p):
+ """InterfaceMember : Const
+ | Operation
+ | Serializer
+ | Stringifier
+ | StaticMember
+ | Iterable
+ | ReadonlyMember
+ | ReadWriteAttribute
+ | ReadWriteMaplike
+ | ReadWriteSetlike"""
+ p[0] = p[1]
+
+ 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_DefaultValue(self, p):
+ """DefaultValue : ConstValue
+ | string
+ | '[' ']'"""
+ if len(p) == 3:
+ p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'sequence'),
+ 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_Inheritance(self, p):
+ """Inheritance : ':' identifier
+ |"""
+ if len(p) > 1:
+ p[0] = self.BuildNamed('Inherit', 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 : ExtendedAttributeList string EnumValueListComma"""
+ enum = self.BuildNamed('EnumItem', p, 2, p[1])
+ p[0] = ListFromConcat(enum, p[3])
+
+ def p_EnumValueListComma(self, p):
+ """EnumValueListComma : ',' EnumValueListString
+ |"""
+ if len(p) > 1:
+ p[0] = p[2]
+
+ def p_EnumValueListString(self, p):
+ """EnumValueListString : ExtendedAttributeList string EnumValueListComma
+ |"""
+ if len(p) > 1:
+ enum = self.BuildNamed('EnumItem', p, 2, p[1])
+ p[0] = ListFromConcat(enum, p[3])
+
+ 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_ImplementsStatement(self, p):
+ """ImplementsStatement : identifier IMPLEMENTS identifier ';'"""
+ name = self.BuildAttribute('REFERENCE', p[3])
+ p[0] = self.BuildNamed('Implements', p, 1, name)
+
+ 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
+ | null"""
+ if type(p[1]) == str:
+ p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'integer'),
+ self.BuildAttribute('VALUE', p[1]))
+ else:
+ 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_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_Serializer(self, p):
+ """Serializer : SERIALIZER SerializerRest"""
+ p[0] = self.BuildProduction('Serializer', p, 1, p[2])
+
+ # TODO(jl): This adds ReturnType and ';', missing from the spec's grammar.
+ # https://www.w3.org/Bugs/Public/show_bug.cgi?id=20361
+ def p_SerializerRest(self, p):
+ """SerializerRest : ReturnType OperationRest
+ | '=' SerializationPattern ';'
+ | ';'"""
+ if len(p) == 3:
+ p[2].AddChildren(p[1])
+ p[0] = p[2]
+ elif len(p) == 4:
+ p[0] = p[2]
+
+ def p_SerializationPattern(self, p):
+ """SerializationPattern : '{' SerializationPatternMap '}'
+ | '[' SerializationPatternList ']'
+ | identifier"""
+ if len(p) > 2:
+ p[0] = p[2]
+ else:
+ p[0] = self.BuildAttribute('ATTRIBUTE', p[1])
+
+ # TODO(jl): This adds the "ATTRIBUTE" and "INHERIT ',' ATTRIBUTE" variants,
+ # missing from the spec's grammar.
+ # https://www.w3.org/Bugs/Public/show_bug.cgi?id=20361
+ def p_SerializationPatternMap(self, p):
+ """SerializationPatternMap : GETTER
+ | ATTRIBUTE
+ | INHERIT ',' ATTRIBUTE
+ | INHERIT Identifiers
+ | identifier Identifiers
+ |"""
+ p[0] = self.BuildProduction('Map', p, 0)
+ if len(p) == 4:
+ p[0].AddChildren(self.BuildTrue('INHERIT'))
+ p[0].AddChildren(self.BuildTrue('ATTRIBUTE'))
+ elif len(p) > 1:
+ if p[1] == 'getter':
+ p[0].AddChildren(self.BuildTrue('GETTER'))
+ elif p[1] == 'attribute':
+ p[0].AddChildren(self.BuildTrue('ATTRIBUTE'))
+ else:
+ if p[1] == 'inherit':
+ p[0].AddChildren(self.BuildTrue('INHERIT'))
+ attributes = p[2]
+ else:
+ attributes = ListFromConcat(p[1], p[2])
+ p[0].AddChildren(self.BuildAttribute('ATTRIBUTES', attributes))
+
+ def p_SerializationPatternList(self, p):
+ """SerializationPatternList : GETTER
+ | identifier Identifiers
+ |"""
+ p[0] = self.BuildProduction('List', p, 0)
+ if len(p) > 1:
+ if p[1] == 'getter':
+ p[0].AddChildren(self.BuildTrue('GETTER'))
+ else:
+ attributes = ListFromConcat(p[1], p[2])
+ p[0].AddChildren(self.BuildAttribute('ATTRIBUTES', attributes))
+
+ 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_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 : REQUIRED"""
+ p[0] = p[1]
+
+ def p_ReadOnly(self, p):
+ """ReadOnly : READONLY
+ |"""
+ if len(p) > 1:
+ p[0] = self.BuildTrue('READONLY')
+
+ def p_Operation(self, p):
+ """Operation : ReturnType OperationRest
+ | SpecialOperation"""
+ if len(p) == 3:
+ p[2].AddChildren(p[1])
+ p[0] = p[2]
+ else:
+ p[0] = p[1]
+
+ def p_SpecialOperation(self, p):
+ """SpecialOperation : Special Specials ReturnType OperationRest"""
+ p[4].AddChildren(ListFromConcat(p[1], p[2], p[3]))
+ p[0] = p[4]
+
+ def p_Specials(self, p):
+ """Specials : Special Specials
+ | """
+ if len(p) > 1:
+ p[0] = ListFromConcat(p[1], p[2])
+
+ def p_Special(self, p):
+ """Special : GETTER
+ | SETTER
+ | CREATOR
+ | DELETER
+ | LEGACYCALLER"""
+ p[0] = self.BuildTrue(p[1].upper())
+
+ def p_OperationRest(self, p):
+ """OperationRest : OptionalIdentifier '(' ArgumentList ')' ';'"""
+ arguments = self.BuildProduction('Arguments', p, 2, p[3])
+ p[0] = self.BuildNamed('Operation', p, 1, arguments)
+
+ def p_OptionalIdentifier(self, p):
+ """OptionalIdentifier : identifier
+ |"""
+ if len(p) > 1:
+ p[0] = p[1]
+ else:
+ p[0] = ''
+
+ 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_Iterable(self, p):
+ """Iterable : ITERABLE '<' TypeWithExtendedAttributes OptionalType '>' ';'"""
+ childlist = ListFromConcat(p[3], p[4])
+ p[0] = self.BuildProduction('Iterable', p, 2, childlist)
+
+ def p_OptionalType(self, p):
+ """OptionalType : ',' TypeWithExtendedAttributes
+ |"""
+ if len(p) > 1:
+ p[0] = p[2]
+
+ def p_ReadWriteMaplike(self, p):
+ """ReadWriteMaplike : MaplikeRest"""
+ p[0] = p[1]
+
+ def p_ReadWriteSetlike(self, p):
+ """ReadWriteSetlike : SetlikeRest"""
+ 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_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 : ExtendedAttributeList ReturnType OperationRest
+ | ExtendedAttributeList READONLY AttributeRest"""
+ if 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)
+ else:
+ p[3].AddChildren(self.BuildTrue('READONLY'))
+ p[3].AddChildren(p[1])
+ p[0] = p[3]
+
+ # 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://heycam.github.io/webidl/#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 = ( StringList ) ]
+ # The first five patterns are specified in the Web IDL spec and the last
+ # pattern is Blink's custom extension to support [ReflectOnly].
+ def p_ExtendedAttribute(self, p):
+ """ExtendedAttribute : ExtendedAttributeNoArgs
+ | ExtendedAttributeArgList
+ | ExtendedAttributeIdent
+ | ExtendedAttributeIdentList
+ | ExtendedAttributeNamedArgList
+ | ExtendedAttributeStringLiteral
+ | ExtendedAttributeStringLiteralList"""
+ p[0] = p[1]
+
+ def p_ArgumentNameKeyword(self, p):
+ """ArgumentNameKeyword : ATTRIBUTE
+ | CALLBACK
+ | CONST
+ | CREATOR
+ | DELETER
+ | DICTIONARY
+ | ENUM
+ | GETTER
+ | IMPLEMENTS
+ | INHERIT
+ | LEGACYCALLER
+ | NAMESPACE
+ | PARTIAL
+ | SERIALIZER
+ | SETTER
+ | STATIC
+ | STRINGIFIER
+ | TYPEDEF
+ | UNRESTRICTED"""
+ p[0] = p[1]
+
+ 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 : NonAnyType
+ | ANY"""
+ 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 : NonAnyType
+ | 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_UnionMemberTypes(self, p):
+ """UnionMemberTypes : OR UnionMemberType UnionMemberTypes
+ |"""
+ if len(p) > 2:
+ p[0] = ListFromConcat(p[2], p[3])
+
+ # Moved BYTESTRING, DOMSTRING, OBJECT, DATE, REGEXP 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_NonAnyType(self, p):
+ """NonAnyType : PrimitiveType Null
+ | PromiseType Null
+ | identifier Null
+ | SEQUENCE '<' TypeWithExtendedAttributes '>' Null
+ | FROZENARRAY '<' TypeWithExtendedAttributes '>' Null
+ | RecordType Null"""
+ if len(p) == 3:
+ if type(p[1]) == str:
+ typeref = self.BuildNamed('Typeref', p, 1)
+ else:
+ typeref = p[1]
+ p[0] = ListFromConcat(typeref, p[2])
+
+ if len(p) == 6:
+ cls = 'Sequence' if p[1] == 'sequence' else 'FrozenArray'
+ p[0] = self.BuildProduction(cls, p, 1, ListFromConcat(p[3], p[5]))
+
+ 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]
+
+
+ # Added StringType, OBJECT, DATE, REGEXP
+ def p_PrimitiveType(self, p):
+ """PrimitiveType : UnsignedIntegerType
+ | UnrestrictedFloatType
+ | StringType
+ | BOOLEAN
+ | BYTE
+ | OCTET
+ | OBJECT
+ | DATE
+ | REGEXP"""
+ 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] = ''
+
+ # Add unqualified Promise
+ def p_PromiseType(self, p):
+ """PromiseType : PROMISE '<' ReturnType '>'
+ | PROMISE"""
+ if len(p) == 2:
+ # Promise without resolution type is not specified in the Web IDL spec.
+ # As it is used in some specs and in the blink implementation,
+ # we allow that here.
+ resolution_type = self.BuildProduction('Type', p, 1,
+ self.BuildProduction('Any', p, 1))
+ p[0] = self.BuildNamed('Promise', p, 1, resolution_type)
+ else:
+ p[0] = self.BuildNamed('Promise', p, 1, p[3])
+
+ def p_Null(self, p):
+ """Null : '?'
+ |"""
+ if len(p) > 1:
+ p[0] = self.BuildTrue('NULLABLE')
+
+ 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_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_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]))
+
+ def p_StringType(self, p):
+ """StringType : BYTESTRING
+ | DOMSTRING
+ | USVSTRING"""
+ p[0] = self.BuildNamed('StringType', p, 1)
+
+ 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')
+
+ # 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)
+ 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 Errror 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) as fileobject:
+ try:
+ out = parser.ParseText(filename, fileobject.read())
+ out.SetProperty('DATETIME', time.ctime(os.path.getmtime(filename)))
+ 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:]))
diff --git a/idl_parser/idl_parser_test.py b/idl_parser/idl_parser_test.py
new file mode 100644
index 0000000..6aeabd4
--- /dev/null
+++ b/idl_parser/idl_parser_test.py
@@ -0,0 +1,403 @@
+#!/usr/bin/env python
+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import glob
+import os
+import unittest
+
+from idl_lexer import IDLLexer
+from idl_parser import IDLParser, ParseFile
+
+
+def ParseCommentTest(comment):
+ comment = comment.strip()
+ comments = comment.split(None, 1)
+ return comments[0], comments[1]
+
+
+class WebIDLParser(unittest.TestCase):
+
+ def setUp(self):
+ self.parser = IDLParser(IDLLexer(), mute_error=True)
+ test_dir = os.path.abspath(
+ os.path.join(os.path.dirname(__file__), 'test_parser'))
+ self.filenames = glob.glob('%s/*_web.idl' % test_dir)
+
+ def _TestNode(self, node):
+ comments = node.GetListOf('SpecialComment')
+ for comment in comments:
+ check, value = ParseCommentTest(comment.GetName())
+ if check == 'ERROR':
+ msg = node.GetLogLine('Expecting\n\t%s\nbut found \n\t%s\n' % (
+ value, str(node)))
+ self.assertEqual(value, node.GetName() ,msg)
+
+ if check == 'TREE':
+ quick = '\n'.join(node.Tree())
+ lineno = node.GetProperty('LINENO')
+ msg = 'Mismatched tree at line %d:' % lineno
+ msg += '\n\n[EXPECTED]\n%s\n\n[ACTUAL]\n%s\n' % (value, quick)
+ self.assertEqual(value, quick, msg)
+
+ def testExpectedNodes(self):
+ for filename in self.filenames:
+ filenode = ParseFile(self.parser, filename)
+ children = filenode.GetChildren()
+ self.assertTrue(len(children) > 2, 'Expecting children in %s.' %
+ filename)
+
+ for node in filenode.GetChildren():
+ self._TestNode(node)
+
+
+class TestImplements(unittest.TestCase):
+
+ def setUp(self):
+ self.parser = IDLParser(IDLLexer(), mute_error=True)
+
+ def _ParseImplements(self, idl_text):
+ filenode = self.parser.ParseText(filename='', data=idl_text)
+ self.assertEqual(1, len(filenode.GetChildren()))
+ return filenode.GetChildren()[0]
+
+ def testAImplementsB(self):
+ idl_text = 'A implements B;'
+ implements_node = self._ParseImplements(idl_text)
+ self.assertEqual('Implements(A)', str(implements_node))
+ reference_node = implements_node.GetProperty('REFERENCE')
+ self.assertEqual('B', str(reference_node))
+
+ def testBImplementsC(self):
+ idl_text = 'B implements C;'
+ implements_node = self._ParseImplements(idl_text)
+ self.assertEqual('Implements(B)', str(implements_node))
+ reference_node = implements_node.GetProperty('REFERENCE')
+ self.assertEqual('C', str(reference_node))
+
+ def testUnexpectedSemicolon(self):
+ idl_text = 'A implements;'
+ node = self._ParseImplements(idl_text)
+ self.assertEqual('Error', node.GetClass())
+ error_message = node.GetName()
+ self.assertEqual('Unexpected ";" after keyword "implements".',
+ error_message)
+
+ def testUnexpectedImplements(self):
+ idl_text = 'implements C;'
+ node = self._ParseImplements(idl_text)
+ self.assertEqual('Error', node.GetClass())
+ error_message = node.GetName()
+ self.assertEqual('Unexpected implements.',
+ error_message)
+
+ def testUnexpectedImplementsAfterBracket(self):
+ idl_text = '[foo] implements B;'
+ node = self._ParseImplements(idl_text)
+ self.assertEqual('Error', node.GetClass())
+ error_message = node.GetName()
+ self.assertEqual('Unexpected keyword "implements" after "]".',
+ error_message)
+
+
+class TestEnums(unittest.TestCase):
+
+ def setUp(self):
+ self.parser = IDLParser(IDLLexer(), mute_error=True)
+
+ def _ParseEnums(self, idl_text):
+ filenode = self.parser.ParseText(filename='', data=idl_text)
+ self.assertEqual(1, len(filenode.GetChildren()))
+ return filenode.GetChildren()[0]
+
+ def testBasic(self):
+ idl_text = 'enum MealType {"rice","noodles","other"};'
+ node = self._ParseEnums(idl_text)
+ children = node.GetChildren()
+ self.assertEqual('Enum', node.GetClass())
+ self.assertEqual(3, len(children))
+ self.assertEqual('EnumItem', children[0].GetClass())
+ self.assertEqual('rice', children[0].GetName())
+ self.assertEqual('EnumItem', children[1].GetClass())
+ self.assertEqual('noodles', children[1].GetName())
+ self.assertEqual('EnumItem', children[2].GetClass())
+ self.assertEqual('other', children[2].GetName())
+
+ def testErrorMissingName(self):
+ idl_text = 'enum {"rice","noodles","other"};'
+ node = self._ParseEnums(idl_text)
+ self.assertEqual('Error', node.GetClass())
+ error_message = node.GetName()
+ self.assertEqual('Enum missing name.', error_message)
+
+ def testTrailingCommaIsAllowed(self):
+ idl_text = 'enum TrailingComma {"rice","noodles","other",};'
+ node = self._ParseEnums(idl_text)
+ children = node.GetChildren()
+ self.assertEqual('Enum', node.GetClass())
+ self.assertEqual(3, len(children))
+ self.assertEqual('EnumItem', children[0].GetClass())
+ self.assertEqual('rice', children[0].GetName())
+ self.assertEqual('EnumItem', children[1].GetClass())
+ self.assertEqual('noodles', children[1].GetName())
+ self.assertEqual('EnumItem', children[2].GetClass())
+ self.assertEqual('other', children[2].GetName())
+
+ def testErrorMissingCommaBetweenIdentifiers(self):
+ idl_text = 'enum MissingComma {"rice" "noodles","other"};'
+ node = self._ParseEnums(idl_text)
+ self.assertEqual('Error', node.GetClass())
+ error_message = node.GetName()
+ self.assertEqual('Unexpected string "noodles" after string "rice".',
+ error_message)
+
+ def testErrorExtraCommaBetweenIdentifiers(self):
+ idl_text = 'enum ExtraComma {"rice","noodles",,"other"};'
+ node = self._ParseEnums(idl_text)
+ self.assertEqual('Error', node.GetClass())
+ error_message = node.GetName()
+ self.assertEqual('Unexpected "," after ",".', error_message)
+
+ def testErrorUnexpectedKeyword(self):
+ idl_text = 'enum TestEnum {interface,"noodles","other"};'
+ node = self._ParseEnums(idl_text)
+ self.assertEqual('Error', node.GetClass())
+ error_message = node.GetName()
+ self.assertEqual('Unexpected keyword "interface" after "{".',
+ error_message)
+
+ def testErrorUnexpectedIdentifier(self):
+ idl_text = 'enum TestEnum {somename,"noodles","other"};'
+ node = self._ParseEnums(idl_text)
+ self.assertEqual('Error', node.GetClass())
+ error_message = node.GetName()
+ self.assertEqual('Unexpected identifier "somename" after "{".',
+ error_message)
+
+class TestExtendedAttribute(unittest.TestCase):
+ def setUp(self):
+ self.parser = IDLParser(IDLLexer(), mute_error=True)
+
+ def _ParseIdlWithExtendedAttributes(self, extended_attribute_text):
+ idl_text = extended_attribute_text + ' enum MealType {"rice"};'
+ filenode = self.parser.ParseText(filename='', data=idl_text)
+ self.assertEqual(1, len(filenode.GetChildren()))
+ node = filenode.GetChildren()[0]
+ self.assertEqual('Enum', node.GetClass())
+ children = node.GetChildren()
+ self.assertEqual(2, len(children))
+ self.assertEqual('EnumItem', children[0].GetClass())
+ self.assertEqual('rice', children[0].GetName())
+ return children[1]
+
+ def testNoArguments(self):
+ extended_attribute_text = '[Replacable]'
+ attributes = self._ParseIdlWithExtendedAttributes(extended_attribute_text)
+ self.assertEqual('ExtAttributes', attributes.GetClass())
+ self.assertEqual(1, len(attributes.GetChildren()) )
+ attribute = attributes.GetChildren()[0]
+ self.assertEqual('ExtAttribute', attribute.GetClass())
+ self.assertEqual('Replacable', attribute.GetName())
+
+ def testArgumentList(self):
+ extended_attribute_text = '[Constructor(double x, double y)]'
+ attributes = self._ParseIdlWithExtendedAttributes(extended_attribute_text)
+ self.assertEqual('ExtAttributes', attributes.GetClass())
+ self.assertEqual(1, len(attributes.GetChildren()))
+ attribute = attributes.GetChildren()[0]
+ self.assertEqual('ExtAttribute', attribute.GetClass())
+ self.assertEqual('Constructor', attribute.GetName())
+ self.assertEqual('Arguments', attribute.GetChildren()[0].GetClass())
+ arguments = attributes.GetChildren()[0].GetChildren()[0]
+ self.assertEqual(2, len(arguments.GetChildren()))
+ self.assertEqual('Argument', arguments.GetChildren()[0].GetClass())
+ self.assertEqual('x', arguments.GetChildren()[0].GetName())
+ self.assertEqual('Argument', arguments.GetChildren()[1].GetClass())
+ self.assertEqual('y', arguments.GetChildren()[1].GetName())
+
+ def testNamedArgumentList(self):
+ extended_attribute_text = '[NamedConstructor=Image(DOMString src)]'
+ attributes = self._ParseIdlWithExtendedAttributes(extended_attribute_text)
+ self.assertEqual('ExtAttributes', attributes.GetClass())
+ self.assertEqual(1, len(attributes.GetChildren()))
+ attribute = attributes.GetChildren()[0]
+ self.assertEqual('ExtAttribute', attribute.GetClass())
+ self.assertEqual('NamedConstructor',attribute.GetName())
+ self.assertEqual(1, len(attribute.GetChildren()))
+ self.assertEqual('Call', attribute.GetChildren()[0].GetClass())
+ self.assertEqual('Image', attribute.GetChildren()[0].GetName())
+ arguments = attribute.GetChildren()[0].GetChildren()[0]
+ self.assertEqual('Arguments', arguments.GetClass())
+ self.assertEqual(1, len(arguments.GetChildren()))
+ self.assertEqual('Argument', arguments.GetChildren()[0].GetClass())
+ self.assertEqual('src', arguments.GetChildren()[0].GetName())
+ argument = arguments.GetChildren()[0]
+ self.assertEqual(1, len(argument.GetChildren()))
+ self.assertEqual('Type', argument.GetChildren()[0].GetClass())
+ arg = argument.GetChildren()[0]
+ self.assertEqual(1, len(arg.GetChildren()))
+ argType = arg.GetChildren()[0]
+ self.assertEqual('StringType', argType.GetClass())
+ self.assertEqual('DOMString', argType.GetName())
+
+ def testIdentifier(self):
+ extended_attribute_text = '[PutForwards=name]'
+ attributes = self._ParseIdlWithExtendedAttributes(extended_attribute_text)
+ self.assertEqual('ExtAttributes', attributes.GetClass())
+ self.assertEqual(1, len(attributes.GetChildren()))
+ attribute = attributes.GetChildren()[0]
+ self.assertEqual('ExtAttribute', attribute.GetClass())
+ self.assertEqual('PutForwards', attribute.GetName())
+ identifier = attribute.GetProperty('VALUE')
+ self.assertEqual('name', identifier)
+
+ def testIdentifierList(self):
+ extended_attribute_text = '[Exposed=(Window,Worker)]'
+ attributes = self._ParseIdlWithExtendedAttributes(extended_attribute_text)
+ self.assertEqual('ExtAttributes', attributes.GetClass())
+ self.assertEqual(1, len(attributes.GetChildren()))
+ attribute = attributes.GetChildren()[0]
+ self.assertEqual('ExtAttribute', attribute.GetClass())
+ self.assertEqual('Exposed', attribute.GetName())
+ identifierList = attribute.GetProperty('VALUE')
+ self.assertEqual(2, len(identifierList))
+ self.assertEqual('Window', identifierList[0])
+ self.assertEqual('Worker', identifierList[1])
+
+ def testCombinationOfExtendedAttributes(self):
+ extended_attribute_text = '[Replacable, Exposed=(Window,Worker)]'
+ attributes = self._ParseIdlWithExtendedAttributes(extended_attribute_text)
+ self.assertEqual('ExtAttributes', attributes.GetClass())
+ self.assertEqual(2, len(attributes.GetChildren()))
+ attribute0 = attributes.GetChildren()[0]
+ self.assertEqual('ExtAttribute', attribute0.GetClass())
+ self.assertEqual('Replacable', attribute0.GetName())
+ attribute1 = attributes.GetChildren()[1]
+ self.assertEqual('ExtAttribute', attribute1.GetClass())
+ self.assertEqual('Exposed', attribute1.GetName())
+ identifierList = attribute1.GetProperty('VALUE')
+ self.assertEqual(2, len(identifierList))
+ self.assertEqual('Window', identifierList[0])
+ self.assertEqual('Worker', identifierList[1])
+
+ def testErrorTrailingComma(self):
+ extended_attribute_text = '[Replacable, Exposed=(Window,Worker),]'
+ error = self._ParseIdlWithExtendedAttributes(extended_attribute_text)
+ self.assertEqual('Error', error.GetClass())
+ error_message = error.GetName()
+ self.assertEqual('Unexpected "]" after ",".', error_message)
+ self.assertEqual('ExtendedAttributeList', error.GetProperty('PROD'))
+
+ def testErrorMultipleExtendedAttributes(self):
+ extended_attribute_text = '[Attribute1][Attribute2]'
+ idl_text = extended_attribute_text + ' enum MealType {"rice"};'
+ filenode = self.parser.ParseText(filename='', data=idl_text)
+ self.assertEqual(1, len(filenode.GetChildren()))
+ node = filenode.GetChildren()[0]
+ self.assertEqual('Error', node.GetClass())
+ self.assertEqual('Unexpected "[" after "]".', node.GetName())
+ self.assertEqual('Definition', node.GetProperty('PROD'))
+ children = node.GetChildren()
+ self.assertEqual(1, len(children))
+ attributes = children[0]
+ self.assertEqual('ExtAttributes', attributes.GetClass())
+ self.assertEqual(1, len(attributes.GetChildren()))
+ attribute = attributes.GetChildren()[0]
+ self.assertEqual('ExtAttribute', attribute.GetClass())
+ self.assertEqual('Attribute1', attribute.GetName())
+
+
+class TestDefaultValue(unittest.TestCase):
+ def setUp(self):
+ self.parser = IDLParser(IDLLexer(), mute_error=True)
+
+ def _ParseDefaultValue(self, default_value_text):
+ idl_text = 'interface I { void hello(' + default_value_text + '); };'
+ filenode = self.parser.ParseText(filename='', data=idl_text)
+ self.assertEqual(1, len(filenode.GetChildren()))
+ node = filenode.GetChildren()[0]
+ self.assertEqual('Interface', node.GetClass())
+ self.assertEqual('I', node.GetName())
+ children = node.GetChildren()
+ self.assertEqual(1, len(children))
+ operation = children[0]
+ self.assertEqual('Operation', operation.GetClass())
+ self.assertEqual('hello', operation.GetName())
+ self.assertEqual(2, len(operation.GetChildren()))
+ arguments = operation.GetChildren()[0]
+ self.assertEqual('Arguments', arguments.GetClass())
+ self.assertEqual(1, len(arguments.GetChildren()))
+ argument = arguments.GetChildren()[0]
+ return_type = operation.GetChildren()[1]
+ self._CheckTypeNode(return_type, 'PrimitiveType', 'void')
+ return argument
+
+ def _CheckTypeNode(self, type_node, expected_class, expected_name):
+ self.assertEqual('Type', type_node.GetClass())
+ self.assertEqual(1, len(type_node.GetChildren()))
+ type_detail = type_node.GetChildren()[0]
+ class_name = type_detail.GetClass()
+ name = type_detail.GetName()
+ self.assertEqual(expected_class, class_name)
+ self.assertEqual(expected_name, name)
+
+ def _CheckArgumentNode(self, argument, expected_class, expected_name):
+ class_name = argument.GetClass()
+ name = argument.GetName()
+ self.assertEqual(expected_class, class_name)
+ self.assertEqual(expected_name, name)
+
+ def _CheckDefaultValue(self, default_value, expected_type, expected_value):
+ self.assertEqual('Default', default_value.GetClass())
+ self.assertEqual(expected_type, default_value.GetProperty('TYPE'))
+ self.assertEqual(expected_value, default_value.GetProperty('VALUE'))
+
+ def testDefaultValueDOMString(self):
+ default_value_text = 'optional DOMString arg = "foo"'
+ argument = self._ParseDefaultValue(default_value_text)
+ self._CheckArgumentNode(argument, 'Argument', 'arg')
+ argument_type = argument.GetChildren()[0]
+ self._CheckTypeNode(argument_type, 'StringType', 'DOMString')
+ default_value = argument.GetChildren()[1]
+ self._CheckDefaultValue(default_value, 'DOMString', 'foo')
+
+ def testDefaultValueInteger(self):
+ default_value_text = 'optional long arg = 10'
+ argument = self._ParseDefaultValue(default_value_text)
+ self._CheckArgumentNode(argument, 'Argument', 'arg')
+ argument_type = argument.GetChildren()[0]
+ self._CheckTypeNode(argument_type, 'PrimitiveType', 'long')
+ default_value = argument.GetChildren()[1]
+ self._CheckDefaultValue(default_value, 'integer', '10')
+
+ def testDefaultValueFloat(self):
+ default_value_text = 'optional float arg = 1.5'
+ argument = self._ParseDefaultValue(default_value_text)
+ self._CheckArgumentNode(argument, 'Argument', 'arg')
+ argument_type = argument.GetChildren()[0]
+ self._CheckTypeNode(argument_type, 'PrimitiveType', 'float')
+ default_value = argument.GetChildren()[1]
+ self._CheckDefaultValue(default_value, 'float', '1.5')
+
+ def testDefaultValueBoolean(self):
+ default_value_text = 'optional boolean arg = true'
+ argument = self._ParseDefaultValue(default_value_text)
+ self._CheckArgumentNode(argument, 'Argument', 'arg')
+ argument_type = argument.GetChildren()[0]
+ self._CheckTypeNode(argument_type, 'PrimitiveType', 'boolean')
+ default_value = argument.GetChildren()[1]
+ self._CheckDefaultValue(default_value, 'boolean', True)
+
+ def testDefaultValueNull(self):
+ # Node is a nullable type
+ default_value_text = 'optional Node arg = null'
+ argument = self._ParseDefaultValue(default_value_text)
+ self._CheckArgumentNode(argument, 'Argument', 'arg')
+ argument_type = argument.GetChildren()[0]
+ self._CheckTypeNode(argument_type, 'Typeref', 'Node')
+ default_value = argument.GetChildren()[1]
+ self._CheckDefaultValue(default_value, 'NULL', 'NULL')
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/idl_parser/run_tests.py b/idl_parser/run_tests.py
new file mode 100644
index 0000000..878f17e
--- /dev/null
+++ b/idl_parser/run_tests.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import glob
+import os
+import sys
+import unittest
+
+if __name__ == '__main__':
+ suite = unittest.TestSuite()
+ cur_dir = os.path.dirname(os.path.realpath(__file__))
+ for testname in glob.glob(os.path.join(cur_dir, '*_test.py')):
+ print 'Adding Test: ' + testname
+ module = __import__(os.path.basename(testname)[:-3])
+ suite.addTests(unittest.defaultTestLoader.loadTestsFromModule(module))
+ result = unittest.TextTestRunner(verbosity=2).run(suite)
+ if result.wasSuccessful():
+ sys.exit(0)
+ else:
+ sys.exit(1)
diff --git a/idl_parser/test_lexer/keywords.in b/idl_parser/test_lexer/keywords.in
new file mode 100644
index 0000000..b695a57
--- /dev/null
+++ b/idl_parser/test_lexer/keywords.in
@@ -0,0 +1,50 @@
+ANY any
+ATTRIBUTE attribute
+BOOLEAN boolean
+BYTESTRING ByteString
+BYTE byte
+CALLBACK callback
+CONST const
+CREATOR creator
+DATE Date
+DELETER deleter
+DICTIONARY dictionary
+DOMSTRING DOMString
+DOUBLE double
+ENUM enum
+FALSE false
+FLOAT float
+GETTER getter
+IMPLEMENTS implements
+INFINITY Infinity
+INHERIT inherit
+INTERFACE interface
+ITERABLE iterable
+LEGACYCALLER legacycaller
+LONG long
+MAPLIKE maplike
+NAN Nan
+NULL null
+OBJECT object
+OCTET octet
+OPTIONAL optional
+OR or
+PARTIAL partial
+PROMISE Promise
+READONLY readonly
+RECORD record
+REGEXP RegExp
+REQUIRED required
+SEQUENCE sequence
+SERIALIZER serializer
+SETLIKE setlike
+SETTER setter
+SHORT short
+STATIC static
+STRINGIFIER stringifier
+TYPEDEF typedef
+TRUE true
+UNSIGNED unsigned
+UNRESTRICTED unrestricted
+USVSTRING USVString
+VOID void
diff --git a/idl_parser/test_lexer/values.in b/idl_parser/test_lexer/values.in
new file mode 100644
index 0000000..bc37898
--- /dev/null
+++ b/idl_parser/test_lexer/values.in
@@ -0,0 +1,55 @@
+integer 1 integer 123 integer 12345
+identifier A123 identifier A_A
+
+/*XXXX*/
+//XXXX
+
+/*MULTI LINE*/
+
+[ [
+] ]
+* *
+. .
+( (
+) )
+{ {
+} }
+[ [
+] ]
+, ,
+; ;
+: :
+= =
++ +
+- -
+/ /
+~ ~
+| |
+& &
+^ ^
+> >
+< <
+
+ELLIPSIS ...
+
+float 1.1
+float 1e1
+float -1.1
+float -1e1
+float 1e-1
+float -1e-1
+float 1.0e1
+float -1.0e-1
+
+integer 00
+integer 01
+integer 0123
+integer 01234567
+integer 123
+integer 1234567890
+integer 0x123
+integer 0X123
+integer 0x1234567890AbCdEf
+integer 0X1234567890aBcDeF
+
+identifier blah
diff --git a/idl_parser/test_parser/callback_web.idl b/idl_parser/test_parser/callback_web.idl
new file mode 100644
index 0000000..6e879d5
--- /dev/null
+++ b/idl_parser/test_parser/callback_web.idl
@@ -0,0 +1,80 @@
+/* Copyright (c) 2013 The Chromium Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file. */
+
+/* Test Callback productions
+
+Run with --test to generate an AST and verify that all comments accurately
+reflect the state of the Nodes.
+
+ERROR Error String
+This comment signals that a error of <Error String> is generated. The error
+is not assigned to a node, but are expected in order.
+
+TREE
+Type(Name)
+ Type(Name)
+ Type(Name)
+ Type(Name)
+ ...
+This comment signals that a tree of nodes matching the BUILD comment
+symatics should exist. This is an exact match.
+*/
+
+
+/** TREE
+ *Callback(VoidFunc)
+ * Type()
+ * PrimitiveType(void)
+ * Arguments()
+ */
+callback VoidFunc = void();
+
+/** TREE
+ *Callback(VoidFuncLongErr)
+ * Type()
+ * PrimitiveType(void)
+ * Arguments()
+ * Error(Unexpected ).)
+ */
+callback VoidFuncLongErr = void ( long );
+
+/** TREE
+ *Callback(VoidFuncLong)
+ * Type()
+ * PrimitiveType(void)
+ * Arguments()
+ * Argument(L1)
+ * Type()
+ * PrimitiveType(long)
+ */
+callback VoidFuncLong = void ( long L1 );
+
+/** TREE
+ *Callback(VoidFuncLongSequence)
+ * Type()
+ * PrimitiveType(void)
+ * Arguments()
+ * Argument(L1)
+ * Type()
+ * Sequence()
+ * Type()
+ * PrimitiveType(long)
+ */
+callback VoidFuncLongSequence = void ( sequence<long> L1 );
+
+/** TREE
+ *Callback(VoidFuncLongIdent)
+ * Type()
+ * PrimitiveType(void)
+ * Arguments()
+ * Argument(L1)
+ * Type()
+ * Sequence()
+ * Type()
+ * PrimitiveType(long)
+ * Argument(L2)
+ * Type()
+ * Typeref(VoidFuncLongSequence)
+ */
+callback VoidFuncLongIdent = void ( sequence<long> L1, VoidFuncLongSequence L2 );
diff --git a/idl_parser/test_parser/dictionary_web.idl b/idl_parser/test_parser/dictionary_web.idl
new file mode 100644
index 0000000..bcf91e0
--- /dev/null
+++ b/idl_parser/test_parser/dictionary_web.idl
@@ -0,0 +1,307 @@
+/* Copyright (c) 2013 The Chromium Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file. */
+
+/* Test Dictionary productions
+
+Run with --test to generate an AST and verify that all comments accurately
+reflect the state of the Nodes.
+
+ERROR Error String
+This comment signals that a error of <Error String> is generated. The error
+is not assigned to a node, but are expected in order.
+
+TREE
+Type(Name)
+ Type(Name)
+ Type(Name)
+ Type(Name)
+ ...
+This comment signals that a tree of nodes matching the BUILD comment
+symatics should exist. This is an exact match.
+*/
+
+
+/** TREE
+ *Dictionary(MyDict)
+ */
+dictionary MyDict { };
+
+/** TREE
+ *Dictionary(MyDictInherit)
+ * Inherit(Foo)
+ */
+dictionary MyDictInherit : Foo {};
+
+/** TREE
+ *Dictionary(MyDictPartial)
+ * PARTIAL: True
+ */
+partial dictionary MyDictPartial { };
+
+/** ERROR Unexpected ":" after identifier "MyDictInherit". */
+partial dictionary MyDictInherit : Foo {};
+
+/** TREE
+ *Dictionary(MyDictBig)
+ * Key(setString)
+ * Type()
+ * StringType(DOMString)
+ * Default() = "Foo"
+ * Key(setLong)
+ * Type()
+ * PrimitiveType(unsigned long long)
+ * Default() = "123"
+ * Key(unsetLong)
+ * Type()
+ * PrimitiveType(long)
+ */
+dictionary MyDictBig {
+ DOMString setString = "Foo";
+ unsigned long long setLong = 123;
+ long unsetLong;
+};
+
+/** TREE
+ *Dictionary(MyDictRequired)
+ * Key(setLong)
+ * REQUIRED: True
+ * Type()
+ * PrimitiveType(long)
+ */
+dictionary MyDictRequired {
+ required long setLong;
+};
+
+/** ERROR Unexpected "{" after keyword "dictionary". */
+dictionary {
+ DOMString? setString = null;
+};
+
+/** TREE
+ *Dictionary(MyDictionaryInvalidOptional)
+ * Key(mandatory)
+ * Type()
+ * StringType(DOMString)
+ * Key(sequenceOfLongWithClamp)
+ * Type()
+ * Sequence()
+ * Type()
+ * PrimitiveType(long)
+ * ExtAttributes()
+ * ExtAttribute(Clamp)
+ * Error(Unexpected keyword "optional" after ">".)
+ */
+dictionary MyDictionaryInvalidOptional {
+ DOMString mandatory;
+ sequence<[Clamp] long> sequenceOfLongWithClamp;
+ sequence<DOMString> optional;
+};
+
+/** ERROR Unexpected identifier "NoColon" after identifier "ForParent". */
+dictionary ForParent NoColon {
+ DOMString? setString = null;
+};
+
+/** TREE
+ *Dictionary(MyDictNull)
+ * Key(setString)
+ * Type()
+ * NULLABLE: True
+ * StringType(DOMString)
+ * Default() = "NULL"
+ */
+dictionary MyDictNull {
+ DOMString? setString = null;
+};
+
+/** ERROR Unexpected keyword "attribute" after "{". */
+dictionary MyDictUnexpectedAttribute {
+ attribute DOMString foo = "";
+};
+
+/** TREE
+ *Dictionary(MyDictRequiredClampNotAppliedToType)
+ * Key(setLong)
+ * REQUIRED: True
+ * Type()
+ * PrimitiveType(long)
+ * ExtAttributes()
+ * ExtAttribute(Clamp)
+ */
+dictionary MyDictRequiredClampNotAppliedToType {
+ [Clamp] required long setLong;
+};
+
+/** TREE
+ *Dictionary(MyDictRequired)
+ * Key(setLong)
+ * REQUIRED: True
+ * Type()
+ * PrimitiveType(long)
+ * ExtAttributes()
+ * ExtAttribute(XAttr) = "foo"
+ */
+dictionary MyDictRequired {
+ [XAttr = foo] required long setLong;
+};
+
+/** TREE
+ *Dictionary(MyDictRequired)
+ * Key(setLong)
+ * REQUIRED: True
+ * Type()
+ * PrimitiveType(long)
+ * ExtAttributes()
+ * ExtAttribute(XAttr1)
+ * ExtAttribute(XAttr2)
+ */
+dictionary MyDictRequired {
+ [XAttr1, XAttr2] required long setLong;
+};
+
+/** TREE
+ *Dictionary(MyDictRequired)
+ * Key(setLong)
+ * REQUIRED: True
+ * Type()
+ * PrimitiveType(long)
+ * ExtAttributes()
+ * ExtAttribute(EnforceRange)
+ * ExtAttributes()
+ * ExtAttribute(Clamp)
+ */
+dictionary MyDictRequired {
+ [Clamp] required [EnforceRange] long setLong;
+};
+
+/** TREE
+ *Dictionary(MyDictRequired)
+ * Key(setLong)
+ * REQUIRED: True
+ * Type()
+ * PrimitiveType(long)
+ * ExtAttributes()
+ * ExtAttribute(XAttr) = "foo"
+ * ExtAttributes()
+ * ExtAttribute(Clamp)
+ */
+dictionary MyDictRequired {
+ [Clamp] required [XAttr = foo] long setLong;
+};
+
+/** TREE
+ *Dictionary(MyDictRequired)
+ * Key(setLong)
+ * REQUIRED: True
+ * Type()
+ * PrimitiveType(long)
+ * ExtAttributes()
+ * ExtAttribute(XAttr1)
+ * ExtAttribute(XAttr2)
+ * ExtAttributes()
+ * ExtAttribute(Clamp)
+ */
+dictionary MyDictRequired {
+ [Clamp] required [XAttr1, XAttr2] long setLong;
+};
+
+/** TREE
+ *Dictionary(MyDict)
+ * Key(setLong)
+ * Type()
+ * PrimitiveType(long)
+ * ExtAttributes()
+ * ExtAttribute(Clamp)
+ */
+dictionary MyDict {
+ [Clamp] long setLong;
+};
+
+/** TREE
+ *Dictionary(MyDict)
+ * Key(setLong)
+ * Type()
+ * PrimitiveType(long)
+ * ExtAttributes()
+ * ExtAttribute(XAttr)
+ */
+dictionary MyDict {
+ [XAttr] long setLong;
+};
+
+/** TREE
+ *Dictionary(MyDict)
+ * Key(setLong)
+ * Type()
+ * PrimitiveType(long)
+ * ExtAttributes()
+ * ExtAttribute(XAttr) = "foo"
+ */
+dictionary MyDict {
+ [XAttr = foo] long setLong;
+};
+
+/** TREE
+ *Dictionary(MyDict)
+ * Key(setLong)
+ * Type()
+ * PrimitiveType(long)
+ * ExtAttributes()
+ * ExtAttribute(Clamp)
+ * ExtAttributes()
+ * ExtAttribute(XAttr)
+ */
+dictionary MyDict {
+ [XAttr, Clamp] long setLong;
+};
+
+/** TREE
+ *Dictionary(MyDict)
+ * Key(foo)
+ * Type()
+ * PrimitiveType(long)
+ * ExtAttributes()
+ * ExtAttribute(Clamp)
+ * Default() = "2"
+ */
+dictionary MyDict {
+ [Clamp] long foo = 2;
+};
+
+/** TREE
+ *Dictionary(MyDict)
+ * Key(setString)
+ * Type()
+ * StringType(DOMString)
+ * ExtAttributes()
+ * ExtAttribute(XAttr)
+ */
+dictionary MyDict {
+ [XAttr] DOMString setString;
+};
+
+/** TREE
+ *Dictionary(MyDict)
+ * Key(setLong)
+ * Type()
+ * PrimitiveType(long)
+ * ExtAttributes()
+ * ExtAttribute(XAttr) = "bar"
+ */
+dictionary MyDict {
+ [XAttr = bar] long setLong;
+};
+
+/** TREE
+ *Dictionary(MyDict)
+ * Key(setLong)
+ * Type()
+ * PrimitiveType(long)
+ * ExtAttributes()
+ * ExtAttribute(XAttr1)
+ * ExtAttribute(XAttr2)
+ */
+dictionary MyDict {
+ [XAttr1, XAttr2] long setLong;
+};
diff --git a/idl_parser/test_parser/interface_web.idl b/idl_parser/test_parser/interface_web.idl
new file mode 100644
index 0000000..589a47c
--- /dev/null
+++ b/idl_parser/test_parser/interface_web.idl
@@ -0,0 +1,624 @@
+/* Copyright (c) 2013 The Chromium Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file. */
+
+/* Test Interface productions
+
+Run with --test to generate an AST and verify that all comments accurately
+reflect the state of the Nodes.
+
+ERROR Error String
+This comment signals that a error of <Error String> is generated. The error
+is not assigned to a node, but are expected in order.
+
+TREE
+Type(Name)
+ Type(Name)
+ Type(Name)
+ Type(Name)
+ ...
+This comment signals that a tree of nodes matching the BUILD comment
+symatics should exist. This is an exact match.
+*/
+
+
+/** TREE
+ *Interface(MyIFace)
+ */
+interface MyIFace { };
+
+/** TREE
+ *Interface(MyIFaceInherit)
+ * Inherit(Foo)
+ */
+interface MyIFaceInherit : Foo {};
+
+/** TREE
+ *Interface(MyIFacePartial)
+ * PARTIAL: True
+ */
+partial interface MyIFacePartial { };
+
+/** ERROR Unexpected ":" after identifier "MyIFaceInherit". */
+partial interface MyIFaceInherit : Foo {};
+
+/** TREE
+ *Interface(MyIFaceMissingArgument)
+ * Operation(foo)
+ * Arguments()
+ * Argument(arg)
+ * Type()
+ * StringType(DOMString)
+ * Error(Missing argument.)
+ * Type()
+ * PrimitiveType(void)
+ */
+interface MyIFaceMissingArgument {
+ void foo(DOMString arg, );
+};
+
+/** TREE
+ *Error(Unexpected keyword "double" after keyword "readonly".)
+ */
+interface MyIFaceMissingAttribute {
+ readonly double foo;
+};
+
+/** TREE
+ *Interface(MyIFaceContainsUnresolvedConflictDiff)
+ * Operation(foo)
+ * Arguments()
+ * Type()
+ * StringType(DOMString)
+ * Error(Unexpected "<" after ";".)
+ */
+interface MyIFaceContainsUnresolvedConflictDiff {
+ DOMString foo();
+<<<<<< ours
+ DOMString bar();
+ iterable<long>;
+======
+>>>>>> theirs
+};
+
+/** TREE
+ *Interface(MyIFaceWrongRecordKeyType)
+ * Operation(foo)
+ * Arguments()
+ * Argument(arg)
+ * Type()
+ * Error(Unexpected identifier "int" after "<".)
+ * Type()
+ * PrimitiveType(void)
+ */
+interface MyIFaceWrongRecordKeyType {
+ void foo(record<int, ByteString> arg);
+};
+
+/** TREE
+ *Interface(MyIFaceBig)
+ * Const(setString)
+ * StringType(DOMString)
+ * NULLABLE: True
+ * Value() = "NULL"
+ */
+interface MyIFaceBig {
+ const DOMString? setString = null;
+};
+
+/** TREE
+ *Interface(MyIfaceEmptySequenceDefalutValue)
+ * Operation(foo)
+ * Arguments()
+ * Argument(arg)
+ * OPTIONAL: True
+ * Type()
+ * Sequence()
+ * Type()
+ * StringType(DOMString)
+ * Default() = "[]"
+ * Type()
+ * PrimitiveType(void)
+ */
+interface MyIfaceEmptySequenceDefalutValue {
+ void foo(optional sequence<DOMString> arg = []);
+};
+
+/** TREE
+ *Interface(MyIfaceWithRecords)
+ * Operation(foo)
+ * Arguments()
+ * Argument(arg)
+ * Type()
+ * Record()
+ * StringType(DOMString)
+ * Type()
+ * PrimitiveType(long)
+ * ExtAttributes()
+ * ExtAttribute(EnforceRange)
+ * Type()
+ * PrimitiveType(void)
+ * Operation(bar)
+ * Arguments()
+ * Argument(arg1)
+ * Type()
+ * Typeref(int)
+ * Argument(arg2)
+ * Type()
+ * Record()
+ * StringType(ByteString)
+ * Type()
+ * PrimitiveType(float)
+ * Type()
+ * PrimitiveType(double)
+ */
+interface MyIfaceWithRecords {
+ void foo(record<DOMString, [EnforceRange] long> arg);
+ double bar(int arg1, record<ByteString, float> arg2);
+};
+
+/** TREE
+ *Interface(MyIFaceBig2)
+ * Const(nullValue)
+ * StringType(DOMString)
+ * NULLABLE: True
+ * Value() = "NULL"
+ * Const(longValue)
+ * PrimitiveType(long)
+ * Value() = "123"
+ * Const(longValue2)
+ * PrimitiveType(long long)
+ * Value() = "123"
+ * Attribute(myString)
+ * Type()
+ * StringType(DOMString)
+ * Attribute(readOnlyString)
+ * READONLY: True
+ * Type()
+ * StringType(DOMString)
+ * Attribute(staticString)
+ * STATIC: True
+ * Type()
+ * StringType(DOMString)
+ * Operation(myFunction)
+ * Arguments()
+ * Argument(myLong)
+ * Type()
+ * PrimitiveType(long long)
+ * Type()
+ * PrimitiveType(void)
+ * Operation(staticFunction)
+ * STATIC: True
+ * Arguments()
+ * Argument(myLong)
+ * Type()
+ * PrimitiveType(long long)
+ * Type()
+ * PrimitiveType(void)
+ */
+interface MyIFaceBig2 {
+ const DOMString? nullValue = null;
+ const long longValue = 123;
+ const long long longValue2 = 123;
+ attribute DOMString myString;
+ readonly attribute DOMString readOnlyString;
+ static attribute DOMString staticString;
+ void myFunction(long long myLong);
+ static void staticFunction(long long myLong);
+};
+
+/** TREE
+ *Interface(MyIFaceSpecials)
+ * Operation(set)
+ * CREATOR: True
+ * SETTER: True
+ * Arguments()
+ * Argument(property)
+ * Type()
+ * StringType(DOMString)
+ * Type()
+ * PrimitiveType(void)
+ * Operation()
+ * GETTER: True
+ * Arguments()
+ * Argument(property)
+ * Type()
+ * StringType(DOMString)
+ * Type()
+ * PrimitiveType(double)
+ */
+interface MyIFaceSpecials {
+ setter creator void set(DOMString property);
+ getter double (DOMString property);
+};
+
+/** TREE
+ *Interface(MyIFaceStringifiers)
+ * Stringifier()
+ * Stringifier()
+ * Operation()
+ * Arguments()
+ * Type()
+ * StringType(DOMString)
+ * Stringifier()
+ * Operation(namedStringifier)
+ * Arguments()
+ * Type()
+ * StringType(DOMString)
+ * Stringifier()
+ * Attribute(stringValue)
+ * Type()
+ * StringType(DOMString)
+ */
+interface MyIFaceStringifiers {
+ stringifier;
+ stringifier DOMString ();
+ stringifier DOMString namedStringifier();
+ stringifier attribute DOMString stringValue;
+};
+
+/** TREE
+ *Interface(MyExtendedAttributeInterface)
+ * Operation(method)
+ * Arguments()
+ * Type()
+ * PrimitiveType(void)
+ * ExtAttributes()
+ * ExtAttribute(Attr)
+ * ExtAttribute(MethodIdentList) = "['Foo', 'Bar']"
+ * ExtAttributes()
+ * ExtAttribute(MyExtendedAttribute)
+ * ExtAttribute(MyExtendedIdentListAttribute) = "['Foo', 'Bar', 'Baz']"
+ */
+[MyExtendedAttribute,
+ MyExtendedIdentListAttribute=(Foo, Bar, Baz)]
+interface MyExtendedAttributeInterface {
+ [Attr, MethodIdentList=(Foo, Bar)] void method();
+};
+
+/** TREE
+ *Interface(MyIfacePromise)
+ * Operation(method1)
+ * Arguments()
+ * Type()
+ * Promise(Promise)
+ * Type()
+ * PrimitiveType(void)
+ * Operation(method2)
+ * Arguments()
+ * Type()
+ * Promise(Promise)
+ * Type()
+ * PrimitiveType(long)
+ * Operation(method3)
+ * Arguments()
+ * Type()
+ * Promise(Promise)
+ * Type()
+ * Any()
+ * Operation(method4)
+ * Arguments()
+ * Type()
+ * Promise(Promise)
+ * Type()
+ * Any()
+ */
+interface MyIfacePromise {
+ Promise<void> method1();
+ Promise<long> method2();
+ Promise<any> method3();
+ Promise method4();
+};
+
+/** TREE
+ *Interface(MyIfaceIterable)
+ * Iterable()
+ * Type()
+ * PrimitiveType(long)
+ * Iterable()
+ * Type()
+ * PrimitiveType(double)
+ * Type()
+ * StringType(DOMString)
+ * Iterable()
+ * Type()
+ * PrimitiveType(long)
+ * ExtAttributes()
+ * ExtAttribute(Clamp)
+ * Iterable()
+ * Type()
+ * StringType(DOMString)
+ * ExtAttributes()
+ * ExtAttribute(TreatNullAs) = "EmptyString"
+ * Iterable()
+ * Type()
+ * PrimitiveType(long)
+ * ExtAttributes()
+ * ExtAttribute(Clamp)
+ * ExtAttribute(XAttr)
+ * Iterable()
+ * Type()
+ * PrimitiveType(long)
+ * ExtAttributes()
+ * ExtAttribute(Clamp)
+ * Type()
+ * PrimitiveType(long long)
+ * ExtAttributes()
+ * ExtAttribute(EnforceRange)
+ */
+interface MyIfaceIterable {
+ iterable<long>;
+ iterable<double, DOMString>;
+ iterable<[Clamp] long>;
+ iterable<[TreatNullAs=EmptyString] DOMString>;
+ iterable<[Clamp, XAttr] long>;
+ iterable<[Clamp] long, [EnforceRange] long long>;
+};
+
+/** TREE
+ *Interface(MyIfaceMaplike)
+ * Maplike()
+ * READONLY: True
+ * Type()
+ * PrimitiveType(long)
+ * Type()
+ * StringType(DOMString)
+ * Maplike()
+ * Type()
+ * PrimitiveType(double)
+ * Type()
+ * PrimitiveType(boolean)
+ * Maplike()
+ * Type()
+ * PrimitiveType(long)
+ * ExtAttributes()
+ * ExtAttribute(Clamp)
+ * Type()
+ * StringType(DOMString)
+ * ExtAttributes()
+ * ExtAttribute(XAttr)
+ */
+interface MyIfaceMaplike {
+ readonly maplike<long, DOMString>;
+ maplike<double, boolean>;
+ maplike<[Clamp] long, [XAttr] DOMString>;
+};
+
+/** TREE
+ *Interface(MyIfaceSetlike)
+ * Setlike()
+ * READONLY: True
+ * Type()
+ * PrimitiveType(long)
+ * Setlike()
+ * Type()
+ * PrimitiveType(double)
+ * Setlike()
+ * Type()
+ * PrimitiveType(long)
+ * ExtAttributes()
+ * ExtAttribute(EnforceRange)
+ */
+interface MyIfaceSetlike {
+ readonly setlike<long>;
+ setlike<double>;
+ setlike<[EnforceRange] long>;
+};
+
+/** TREE
+ *Interface(MyIfaceSerializer)
+ * Serializer()
+ * Serializer()
+ * Operation(toJSON)
+ * Arguments()
+ * Type()
+ * Any()
+ * Serializer()
+ * ATTRIBUTE: name
+ * Serializer()
+ * Map()
+ * Serializer()
+ * Map()
+ * GETTER: True
+ * Serializer()
+ * Map()
+ * ATTRIBUTE: True
+ * Serializer()
+ * Map()
+ * ATTRIBUTE: True
+ * INHERIT: True
+ * Serializer()
+ * Map()
+ * INHERIT: True
+ * ATTRIBUTES: None
+ * Serializer()
+ * Map()
+ * INHERIT: True
+ * ATTRIBUTES: ['name1', 'name2']
+ * Serializer()
+ * Map()
+ * ATTRIBUTES: ['name1', 'name2']
+ * Serializer()
+ * List()
+ * Serializer()
+ * List()
+ * GETTER: True
+ * Serializer()
+ * List()
+ * ATTRIBUTES: ['name1', 'name2']
+ */
+interface MyIfaceSerializer {
+ serializer;
+ serializer any toJSON();
+ serializer = name;
+ serializer = {};
+ serializer = { getter };
+ serializer = { attribute };
+ serializer = { inherit, attribute };
+ serializer = { inherit };
+ serializer = { inherit, name1, name2 };
+ serializer = { name1, name2 };
+ serializer = [];
+ serializer = [getter];
+ serializer = [name1, name2];
+};
+
+/** TREE
+ *Interface(MyIfaceFrozenArray)
+ * Attribute(foo)
+ * READONLY: True
+ * Type()
+ * FrozenArray()
+ * Type()
+ * StringType(DOMString)
+ * Attribute(bar)
+ * READONLY: True
+ * Type()
+ * FrozenArray()
+ * Type()
+ * StringType(DOMString)
+ * ExtAttributes()
+ * ExtAttribute(TreatNullAs) = "EmptyString"
+ */
+interface MyIfaceFrozenArray {
+ readonly attribute FrozenArray<DOMString> foo;
+ readonly attribute FrozenArray<[TreatNullAs=EmptyString] DOMString> bar;
+};
+
+/** TREE
+ *Interface(MyIfaceUnion)
+ * Attribute(foo)
+ * Type()
+ * UnionType()
+ * Type()
+ * StringType(DOMString)
+ * Type()
+ * PrimitiveType(long)
+ */
+interface MyIfaceUnion {
+ attribute (DOMString or long) foo;
+};
+
+/** TREE
+ *Interface(MyIfaceAttributeRestClamp)
+ * Attribute(myLong)
+ * Type()
+ * PrimitiveType(long)
+ * ExtAttributes()
+ * ExtAttribute(Clamp)
+ */
+interface MyIfaceAttributeRestClamp {
+ attribute [Clamp] long myLong;
+};
+
+/** TREE
+ *Interface(MyIFaceArgumentWithAnnotatedType1)
+ * Operation(myFunction)
+ * Arguments()
+ * Argument(myLong)
+ * OPTIONAL: True
+ * Type()
+ * PrimitiveType(long long)
+ * ExtAttributes()
+ * ExtAttribute(Clamp)
+ * Type()
+ * PrimitiveType(void)
+ */
+interface MyIFaceArgumentWithAnnotatedType1 {
+ void myFunction(optional [Clamp] long long myLong);
+};
+
+/** TREE
+ *Interface(MyIFaceArgumentWithAnnotatedType2)
+ * Operation(voidMethodTestArgumentWithExtAttribute1)
+ * Arguments()
+ * Argument(myLong)
+ * Type()
+ * PrimitiveType(long long)
+ * ExtAttributes()
+ * ExtAttribute(Clamp)
+ * ExtAttributes()
+ * ExtAttribute(XAttr)
+ * Type()
+ * PrimitiveType(void)
+ * Operation(voidMethodTestArgumentWithExtAttribute2)
+ * Arguments()
+ * Argument(longArg)
+ * Type()
+ * PrimitiveType(long)
+ * ExtAttributes()
+ * ExtAttribute(EnforceRange)
+ * Type()
+ * PrimitiveType(void)
+ */
+interface MyIFaceArgumentWithAnnotatedType2 {
+ void voidMethodTestArgumentWithExtAttribute1([Clamp, XAttr] long long myLong);
+ void voidMethodTestArgumentWithExtAttribute2([EnforceRange] long longArg);
+};
+
+/** TREE
+ *Interface(InterfaceConstructors)
+ * ExtAttributes()
+ * ExtAttribute(Constructor)
+ * ExtAttribute(Constructor)
+ * Arguments()
+ * Argument(doubleArg)
+ * Type()
+ * PrimitiveType(double)
+ * ExtAttribute(CustomConstructor)
+ * ExtAttribute(CustomConstructor)
+ * Arguments()
+ * Argument(doubleArg)
+ * Type()
+ * PrimitiveType(double)
+ * ExtAttribute(NamedConstructor) = "Audio"
+ * ExtAttribute(NamedConstructor)
+ * Call(Audio)
+ * Arguments()
+ * Argument(src)
+ * Type()
+ * StringType(DOMString)
+ */
+[
+ Constructor,
+ Constructor(double doubleArg),
+ CustomConstructor,
+ CustomConstructor(double doubleArg),
+ NamedConstructor=Audio,
+ NamedConstructor=Audio(DOMString src)
+] interface InterfaceConstructors { };
+
+/** TREE
+ *Interface(InterfaceExposed)
+ * ExtAttributes()
+ * ExtAttribute(Exposed) = "Window"
+ * ExtAttribute(Exposed) = "['Window', 'Worker']"
+ * ExtAttribute(Exposed)
+ * Arguments()
+ * Argument(Feature1)
+ * Type()
+ * Typeref(Window)
+ * ExtAttribute(Exposed)
+ * Arguments()
+ * Argument(Feature1)
+ * Type()
+ * Typeref(Window)
+ * Argument(Feature2)
+ * Type()
+ * Typeref(Worker)
+ */
+[
+ Exposed=Window,
+ Exposed=(Window, Worker),
+ Exposed(Window Feature1),
+ Exposed(Window Feature1, Worker Feature2)
+] interface InterfaceExposed { };
+
+/** TREE
+ *Interface(InterfaceExposedError)
+ * ExtAttributes()
+ * ExtAttribute(Exposed)
+ * Arguments()
+ * Error(Unexpected ,.)
+ */
+[ Exposed(Window, Worker) ] interface InterfaceExposedError { };
diff --git a/idl_parser/test_parser/namespace_web.idl b/idl_parser/test_parser/namespace_web.idl
new file mode 100644
index 0000000..06e42a4
--- /dev/null
+++ b/idl_parser/test_parser/namespace_web.idl
@@ -0,0 +1,150 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/* Test Namespace productions
+
+Run with --test to generate an AST and verify that all comments accurately
+reflect the state of the Nodes.
+
+TREE
+Type(Name)
+ Type(Name)
+ Type(Name)
+ Type(Name)
+ ...
+This comment signals that a tree of nodes matching the BUILD comment
+symatics should exist. This is an exact match.
+*/
+
+
+/** TREE
+ *Namespace(MyNamespace)
+ */
+namespace MyNamespace { };
+
+/** TREE
+ *Namespace(MyNamespace2)
+ * Operation(fooLong)
+ * Arguments()
+ * Type()
+ * PrimitiveType(long)
+ * Operation(voidArgLong)
+ * Arguments()
+ * Argument(arg)
+ * Type()
+ * PrimitiveType(long)
+ * Type()
+ * PrimitiveType(void)
+ */
+namespace MyNamespace2 {
+ long fooLong();
+ void voidArgLong(long arg);
+};
+
+/** TREE
+ *Namespace(MyNamespaceMissingArgument)
+ * Operation(foo)
+ * Arguments()
+ * Argument(arg)
+ * Type()
+ * StringType(DOMString)
+ * Error(Missing argument.)
+ * Type()
+ * PrimitiveType(void)
+ */
+namespace MyNamespaceMissingArgument {
+ void foo(DOMString arg, );
+};
+
+/** TREE
+ *Namespace(VectorUtils)
+ * Attribute(unit)
+ * READONLY: True
+ * Type()
+ * Typeref(Vector)
+ * Operation(dotProduct)
+ * Arguments()
+ * Argument(x)
+ * Type()
+ * Typeref(Vector)
+ * Argument(y)
+ * Type()
+ * Typeref(Vector)
+ * Type()
+ * PrimitiveType(double)
+ * Operation(crossProduct)
+ * Arguments()
+ * Argument(x)
+ * Type()
+ * Typeref(Vector)
+ * Argument(y)
+ * Type()
+ * Typeref(Vector)
+ * Type()
+ * Typeref(Vector)
+ */
+namespace VectorUtils {
+ readonly attribute Vector unit;
+ double dotProduct(Vector x, Vector y);
+ Vector crossProduct(Vector x, Vector y);
+};
+
+/**TREE
+ *Namespace(ErrorOnlyExtAttrs)
+ * Error(Unexpected ";" after "]".)
+ */
+namespace ErrorOnlyExtAttrs {
+ [Clamp];
+};
+
+/** TREE
+ *Error(Unexpected keyword "attribute" after "{".)
+ */
+namespace ErrorNonReadonly {
+ attribute Vector unit2;
+};
+
+/** TREE
+ *Error(Unexpected ";" after "{".)
+ */
+namespace NameSpaceError {
+ ;
+
+/**TREE
+ *Namespace(PartialNamespace)
+ * PARTIAL: True
+ * Operation(fooLong)
+ * Arguments()
+ * Type()
+ * PrimitiveType(long)
+ */
+partial namespace PartialNamespace {
+ long fooLong();
+};
+
+/** TREE
+ *Namespace(NamespaceWithExtAttrs)
+ * Operation(fooLong)
+ * Arguments()
+ * Type()
+ * PrimitiveType(long)
+ * ExtAttributes()
+ * ExtAttribute(Replaceable)
+ */
+[Replaceable] namespace NamespaceWithExtAttrs {
+ long fooLong();
+};
+
+/** TREE
+ *Namespace(NamespaceAnnotatedTypeMember)
+ * Operation(fooLong)
+ * Arguments()
+ * Type()
+ * PrimitiveType(long)
+ * ExtAttributes()
+ * ExtAttribute(Clamp)
+ */
+namespace NamespaceAnnotatedTypeMember {
+ [Clamp] long fooLong();
+};
diff --git a/idl_parser/test_parser/typedef_web.idl b/idl_parser/test_parser/typedef_web.idl
new file mode 100644
index 0000000..7ba169f
--- /dev/null
+++ b/idl_parser/test_parser/typedef_web.idl
@@ -0,0 +1,167 @@
+/* Copyright (c) 2013 The Chromium Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file. */
+
+/* Test Typedef productions
+
+Run with --test to generate an AST and verify that all comments accurately
+reflect the state of the Nodes.
+
+ERROR Error String
+This comment signals that a error of <Error String> is generated. The error
+is not assigned to a node, but are expected in order.
+
+TREE
+Type(Name)
+ Type(Name)
+ Type(Name)
+ Type(Name)
+ ...
+This comment signals that a tree of nodes matching the BUILD comment
+symatics should exist. This is an exact match.
+*/
+
+
+/** TREE
+ *Typedef(MyLong)
+ * Type()
+ * PrimitiveType(long)
+ */
+typedef long MyLong;
+
+/** TREE
+ *Typedef(MyLong)
+ * Type()
+ * PrimitiveType(long)
+ * ExtAttributes()
+ * ExtAttribute(foo)
+ */
+typedef [foo] long MyLong;
+
+/** TREE
+ *Typedef(MyLongSequence)
+ * Type()
+ * Sequence()
+ * Type()
+ * PrimitiveType(long)
+ */
+typedef sequence<long> MyLongSequence;
+
+/** TREE
+ *Typedef(MyTypeSequence)
+ * Type()
+ * Sequence()
+ * Type()
+ * Typeref(MyType)
+ */
+typedef sequence<MyType> MyTypeSequence;
+
+/** TREE
+ *Typedef(MyLongLong)
+ * Type()
+ * PrimitiveType(long long)
+ */
+typedef long long MyLongLong;
+
+/** TREE
+ *Typedef(MyULong)
+ * Type()
+ * PrimitiveType(unsigned long)
+ */
+typedef unsigned long MyULong;
+
+/** TREE
+ *Typedef(MyULongLong)
+ * Type()
+ * PrimitiveType(unsigned long long)
+ */
+typedef unsigned long long MyULongLong;
+
+/** TREE
+ *Typedef(MyString)
+ * Type()
+ * StringType(DOMString)
+ */
+typedef DOMString MyString;
+
+/** TREE
+ *Typedef(MyObject)
+ * Type()
+ * PrimitiveType(object)
+ */
+typedef object MyObject;
+
+/** TREE
+ *Typedef(MyDate)
+ * Type()
+ * PrimitiveType(Date)
+ */
+typedef Date MyDate;
+
+/** TREE
+ *Typedef(MyFloat)
+ * Type()
+ * PrimitiveType(float)
+ */
+typedef float MyFloat;
+
+/** TREE
+ *Typedef(MyUFloat)
+ * Type()
+ * PrimitiveType(float)
+ * UNRESTRICTED: True
+ */
+typedef unrestricted float MyUFloat;
+
+/** TREE
+ *Typedef(MyDouble)
+ * Type()
+ * PrimitiveType(double)
+ */
+typedef double MyDouble;
+
+/** TREE
+ *Typedef(MyUDouble)
+ * Type()
+ * PrimitiveType(double)
+ * UNRESTRICTED: True
+ */
+typedef unrestricted double MyUDouble;
+
+/** TREE
+ *Typedef(MyBool)
+ * Type()
+ * PrimitiveType(boolean)
+ */
+typedef boolean MyBool;
+
+/** TREE
+ *Typedef(MyByte)
+ * Type()
+ * PrimitiveType(byte)
+ */
+typedef byte MyByte;
+
+/** TREE
+ *Typedef(MyOctet)
+ * Type()
+ * PrimitiveType(octet)
+ */
+typedef octet MyOctet;
+
+/** TREE
+ *Typedef(MyRecord)
+ * Type()
+ * Record()
+ * StringType(ByteString)
+ * Type()
+ * Typeref(int)
+ */
+typedef record<ByteString, int> MyRecord;
+
+/** TREE
+ *Typedef(MyInvalidRecord)
+ * Type()
+ * Error(Unexpected keyword "double" after "<".)
+ */
+typedef record<double, ByteString> MyInvalidRecord;