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;