diff options
author | noelallen@google.com <noelallen@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-12 18:48:47 +0000 |
---|---|---|
committer | noelallen@google.com <noelallen@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-12 18:48:47 +0000 |
commit | ac7b49d82dee3e690968139b610553386f6f1f3c (patch) | |
tree | 3701977bff91385289785992d6f016aa2ed66ee9 /tools/idl_parser | |
parent | 7a54a4316797f7afebeb013b20631f90bba9eecb (diff) | |
download | chromium_src-ac7b49d82dee3e690968139b610553386f6f1f3c.zip chromium_src-ac7b49d82dee3e690968139b610553386f6f1f3c.tar.gz chromium_src-ac7b49d82dee3e690968139b610553386f6f1f3c.tar.bz2 |
Add WebIDL and Pepper compliant lexer/parser tool.
This CL is the first step towards supporting automatic doc
generation. This lexer more closely follows the WebIDL
spec.
Please review idl_node, idl_parser, idl_node
idl_log.py is unchanged from the original
This is a complete re-write of the original lexer/parser, as well as simplifications to IDLNode.
All *.in and *.idl files are for test purposes only.
To test: python idl_lexer.py OR python idl_parser.py
NOTRY=true
R=sehr@chromium.org
BUG=224150
Review URL: https://codereview.chromium.org/13498002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@193973 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/idl_parser')
-rwxr-xr-x | tools/idl_parser/idl_lexer.py | 88 | ||||
-rwxr-xr-x | tools/idl_parser/idl_node.py | 217 | ||||
-rwxr-xr-x | tools/idl_parser/idl_parser.py | 1029 | ||||
-rwxr-xr-x | tools/idl_parser/idl_parser_test.py | 61 | ||||
-rw-r--r-- | tools/idl_parser/idl_ppapi_lexer.py | 41 | ||||
-rwxr-xr-x | tools/idl_parser/run_tests.py | 13 | ||||
-rw-r--r-- | tools/idl_parser/test_lexer/keywords.in | 3 | ||||
-rw-r--r-- | tools/idl_parser/test_parser/callback.idl | 116 | ||||
-rw-r--r-- | tools/idl_parser/test_parser/dictionary.idl | 95 | ||||
-rw-r--r-- | tools/idl_parser/test_parser/enum.idl | 119 | ||||
-rw-r--r-- | tools/idl_parser/test_parser/exception.idl | 87 | ||||
-rw-r--r-- | tools/idl_parser/test_parser/implements.idl | 52 | ||||
-rw-r--r-- | tools/idl_parser/test_parser/interface.idl | 127 | ||||
-rw-r--r-- | tools/idl_parser/test_parser/typedef.idl | 190 |
14 files changed, 2181 insertions, 57 deletions
diff --git a/tools/idl_parser/idl_lexer.py b/tools/idl_parser/idl_lexer.py index 9a07ac6..aa0da06 100755 --- a/tools/idl_parser/idl_lexer.py +++ b/tools/idl_parser/idl_lexer.py @@ -100,31 +100,24 @@ class IDLLexer(object): 'void' : 'VOID' } - # Add keywords - for key in keywords: - tokens.append(keywords[key]) - - # '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 = '"*.(){}[],;:=+-/~|&^?<>' - # 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. + def t_ELLIPSIS(self, t): + r'\.\.\.' + return t - # 't_ignore' is a special match of items to ignore - t_ignore = ' \t' + def t_float(self, t): + r'-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+)' + return t - # Ellipsis operator - t_ELLIPSIS = r'\.\.\.' + def t_integer(self, t): + r'-?(0([0-7]*|[Xx][0-9A-Fa-f]+)|[1-9][0-9]*)' + return t - # Constant values - t_integer = r'-?(0([0-7]*|[Xx][0-9A-Fa-f]+)|[1-9][0-9]*)' - t_float = r'-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)' - t_float += r'([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+)' # A line ending '\n', we use this to increment the line number def t_LINE_END(self, t): @@ -160,7 +153,7 @@ class IDLLexer(object): def t_ANY_error(self, t): msg = 'Unrecognized input' - line = self.lexobj.lineno + 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. @@ -169,10 +162,10 @@ class IDLLexer(object): word = t.value.split()[0] offs = self.lines[line - 1].find(word) # Add the computed line's starting position - self.index.append(self.lexobj.lexpos - offs) + self.index.append(self.Lexer().lexpos - offs) msg = 'Unexpected EoF reached after' - pos = self.lexobj.lexpos - self.index[line] + pos = self.Lexer().lexpos - self.index[line] out = self.ErrorMessage(line, pos, msg) sys.stderr.write(out + '\n') self._lex_errors += 1 @@ -183,13 +176,13 @@ class IDLLexer(object): # 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.lexobj.lineno += count + self.Lexer().lineno += count for _ in range(count): - self.index.append(self.lexobj.lexpos) + 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.lexobj.filename + filename = self.Lexer().filename if filename: return "%s(%d) : %s" % (filename, line + 1, msg) return "<BuiltIn> : %s" % msg @@ -213,7 +206,7 @@ class IDLLexer(object): # against the leaf paterns. # def token(self): - tok = self.lexobj.token() + tok = self.Lexer().token() if tok: self.last = tok return tok @@ -222,21 +215,60 @@ class IDLLexer(object): def GetTokens(self): outlist = [] while True: - t = self.lexobj.token() + t = self.Lexer().token() if not t: break outlist.append(t) return outlist def Tokenize(self, data, filename='__no_file__'): - self.lexobj.filename = filename - self.lexobj.input(data) + 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 _AddConstDefs(self): + # 'literals' is a value expected by lex which specifies a list of valid + # literal tokens, meaning the token type and token value are identical. + self.literals = r'"*.(){}[],;:=+-/~|&^?<>' + self.t_ignore = ' \t' + + 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 __init__(self): self.index = [0] self._lex_errors = 0 self.linex = [] self.filename = None - self.lexobj = lex.lex(object=self, lextab=None, optimize=0) - + self.keywords = {} + self.tokens = [] + self._AddConstDefs() + self._AddTokens(IDLLexer.tokens) + self._AddKeywords(IDLLexer.keywords) + self._lexobj = None + +# If run by itself, attempt to build the lexer +if __name__ == '__main__': + lexer = IDLLexer() diff --git a/tools/idl_parser/idl_node.py b/tools/idl_parser/idl_node.py new file mode 100755 index 0000000..2e31d9a --- /dev/null +++ b/tools/idl_parser/idl_node.py @@ -0,0 +1,217 @@ +#!/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): + def __init__(self, cls, filename, lineno, pos, children=None): + self._cls = cls + self._properties = { + 'ERRORS' : [], + 'WARNINGS': [], + 'FILENAME': filename, + 'LINENO' : lineno, + 'POSSITION' : pos, + } + + self._children = [] + self._parent = None + self.AddChildren(children) + +# +# +# + # Return a string representation of this node + def __str__(self): + name = self.GetProperty('NAME','') + 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, accept_props=None): + class DumpTreeSearch(IDLSearch): + def __init__(self, props): + IDLSearch.__init__(self) + self.out = [] + self.props = props + + def Enter(self, node): + tab = ''.rjust(self.depth * 2) + self.out.append(tab + str(node)) + if self.props: + for key, value in node.GetProperties().iteritems(): + proplist = [] + if key in self.props: + proplist.append(tab + ' %s: %s' % (key, str(value))) + if proplist: + self.out.append(tab + ' PROPERTIES') + self.out.extend(proplist) + + if filter_nodes == None: + filter_nodes = ['Comment', 'Copyright'] + + search = DumpTreeSearch(accept_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 .\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/tools/idl_parser/idl_parser.py b/tools/idl_parser/idl_parser.py new file mode 100755 index 0000000..3e57295 --- /dev/null +++ b/tools/idl_parser/idl_parser.py @@ -0,0 +1,1029 @@ +#!/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 PPAPI IDL """ + +# +# IDL Parser +# +# The parser is uses the PLY yacc library to build a set of parsing rules based +# on WebIDL. +# +# WebIDL, and WebIDL grammar can be found at: +# http://dev.w3.org/2006/webapi/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, IDLNode + +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.', +} + + +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 == '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 + + +# +# 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. +# +# For more details on parsing refer to the PLY documentation at +# http://www.dabeaz.com/ply/ +# +# The parser is based on the WebIDL standard. See: +# http://www.w3.org/TR/WebIDL/#idl-grammar +# +# The various productions are annotated so that the WHOLE number greater than +# zero in the comment denotes the matching WebIDL grammar definition. +# +# Productions with a fractional component in the comment denote additions to +# the WebIDL spec, such as comments. +# + + +class IDLParser(object): +# +# We force all input files to start with two comments. The first comment is a +# Copyright notice followed by a file comment and finally by file level +# productions. +# + # [0] Insert a TOP definition for Copyright and Comments + def p_Top(self, p): + """Top : COMMENT COMMENT Definitions""" + Copyright = self.BuildComment('Copyright', p, 1) + Filedoc = self.BuildComment('Comment', p, 2) + p[0] = ListFromConcat(Copyright, Filedoc, p[3]) + + # [0.1] Add support for Multiple COMMENTS + def p_Comments(self, p): + """Comments : CommentsRest""" + if len(p) > 1: + p[0] = p[1] + + # [0.2] Produce a COMMENT and aggregate sibling comments + def p_CommentsRest(self, p): + """CommentsRest : COMMENT CommentsRest + | """ + if len(p) > 1: + p[0] = ListFromConcat(self.BuildComment('Comment', p, 1), p[2]) + + +# +#The parser is based on the WebIDL standard. See: +# http://www.w3.org/TR/WebIDL/#idl-grammar +# + # [1] + def p_Definitions(self, p): + """Definitions : ExtendedAttributeList Definition Definitions + | """ + if len(p) > 1: + p[2].AddChildren(p[1]) + p[0] = ListFromConcat(p[2], p[3]) + + # [2] Add INLINE definition + def p_Definition(self, p): + """Definition : CallbackOrInterface + | Partial + | Dictionary + | Exception + | Enum + | Typedef + | ImplementsStatement""" + p[0] = p[1] + + # [2.1] Error recovery for definition + def p_DefinitionError(self, p): + """Definition : error ';'""" + p[0] = self.BuildError(p, 'Definition') + + # [3] + def p_CallbackOrInterface(self, p): + """CallbackOrInterface : CALLBACK CallbackRestOrInterface + | Interface""" + if len(p) > 2: + p[0] = p[2] + else: + p[0] = p[1] + + # [4] + def p_CallbackRestOrInterface(self, p): + """CallbackRestOrInterface : CallbackRest + | Interface""" + p[0] = p[1] + + # [5] + def p_Interface(self, p): + """Interface : INTERFACE identifier Inheritance '{' InterfaceMembers '}' ';'""" + p[0] = self.BuildNamed('Interface', p, 2, ListFromConcat(p[3], p[5])) + + # [6] Error recovery for PARTIAL + def p_Partial(self, p): + """Partial : PARTIAL PartialDefinition""" + p[2].AddChildren(self.BuildTrue('Partial')) + p[0] = p[2] + + # [6.1] Error recovery for Enums + def p_PartialError(self, p): + """Partial : PARTIAL error""" + p[0] = self.BuildError(p, 'Partial') + + # [7] + def p_PartialDefinition(self, p): + """PartialDefinition : PartialDictionary + | PartialInterface""" + p[0] = p[1] + + # [8] + def p_PartialInterface(self, p): + """PartialInterface : INTERFACE identifier '{' InterfaceMembers '}' ';'""" + p[0] = self.BuildNamed('Interface', p, 2, p[4]) + + # [9] + 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]) + + # [10] + def p_InterfaceMember(self, p): + """InterfaceMember : Const + | AttributeOrOperation""" + p[0] = p[1] + + # [11] + def p_Dictionary(self, p): + """Dictionary : DICTIONARY identifier Inheritance '{' DictionaryMembers '}' ';'""" + p[0] = self.BuildNamed('Dictionary', p, 2, ListFromConcat(p[3], p[5])) + + # [11.1] Error recovery for regular Dictionary + def p_DictionaryError(self, p): + """Dictionary : DICTIONARY error ';'""" + p[0] = self.BuildError(p, 'Dictionary') + + # [12] + def p_DictionaryMembers(self, p): + """DictionaryMembers : ExtendedAttributeList DictionaryMember DictionaryMembers + |""" + if len(p) > 1: + p[2].AddChildren(p[1]) + p[0] = ListFromConcat(p[2], p[3]) + + # [13] + def p_DictionaryMember(self, p): + """DictionaryMember : Type identifier Default ';'""" + p[0] = self.BuildNamed('Key', p, 2, ListFromConcat(p[1], p[3])) + + # [14] + def p_PartialDictionary(self, p): + """PartialDictionary : DICTIONARY identifier '{' DictionaryMembers '}' ';'""" + partial = self.BuildTrue('Partial') + p[0] = self.BuildNamed('Dictionary', p, 2, ListFromConcat(p[4], partial)) + + # [14.1] Error recovery for Partial Dictionary + def p_PartialDictionaryError(self, p): + """PartialDictionary : DICTIONARY error ';'""" + p[0] = self.BuildError(p, 'PartialDictionary') + + # [15] + def p_Default(self, p): + """Default : '=' DefaultValue + |""" + if len(p) > 1: + p[0] = self.BuildProduction('Default', p, 2, p[2]) + + # [16] + def p_DefaultValue(self, p): + """DefaultValue : ConstValue + | string""" + if type(p[1]) == str: + p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'DOMString'), + self.BuildAttribute('NAME', p[1])) + else: + p[0] = p[1] + + # [17] + def p_Exception(self, p): + """Exception : EXCEPTION identifier Inheritance '{' ExceptionMembers '}' ';'""" + p[0] = self.BuildNamed('Exception', p, 2, ListFromConcat(p[3], p[5])) + + # [18] + def p_ExceptionMembers(self, p): + """ExceptionMembers : ExtendedAttributeList ExceptionMember ExceptionMembers + |""" + if len(p) > 1: + p[2].AddChildren(p[1]) + p[0] = ListFromConcat(p[2], p[3]) + + # [18.1] Error recovery for ExceptionMembers + def p_ExceptionMembersError(self, p): + """ExceptionMembers : error""" + p[0] = self.BuildError(p, 'ExceptionMembers') + + # [19] + def p_Inheritance(self, p): + """Inheritance : ':' identifier + |""" + if len(p) > 1: + p[0] = self.BuildNamed('Inherit', p, 2) + + # [20] + def p_Enum(self, p): + """Enum : ENUM identifier '{' EnumValueList '}' ';'""" + p[0] = self.BuildNamed('Enum', p, 2, p[4]) + + # [20.1] Error recovery for Enums + def p_EnumError(self, p): + """Enum : ENUM error ';'""" + p[0] = self.BuildError(p, 'Enum') + + # [21] + def p_EnumValueList(self, p): + """EnumValueList : ExtendedAttributeList string EnumValues""" + enum = self.BuildNamed('EnumItem', p, 2, p[1]) + p[0] = ListFromConcat(enum, p[3]) + + # [22] + def p_EnumValues(self, p): + """EnumValues : ',' ExtendedAttributeList string EnumValues + |""" + if len(p) > 1: + enum = self.BuildNamed('EnumItem', p, 3, p[2]) + p[0] = ListFromConcat(enum, p[4]) + + # [23] + 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)) + + # [24] + def p_Typedef(self, p): + """Typedef : TYPEDEF ExtendedAttributeList Type identifier ';'""" + p[0] = self.BuildNamed('Typedef', p, 4, ListFromConcat(p[2], p[3])) + + # [24.1] Error recovery for Typedefs + def p_TypedefError(self, p): + """Typedef : TYPEDEF error ';'""" + p[0] = self.BuildError(p, 'Typedef') + + # [25] + def p_ImplementsStatement(self, p): + """ImplementsStatement : identifier IMPLEMENTS identifier ';'""" + name = self.BuildAttribute('REFERENCE', p[3]) + p[0] = self.BuildNamed('Implements', p, 1, name) + + # [26] + 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)) + + # [27] + def p_ConstValue(self, p): + """ConstValue : BooleanLiteral + | FloatLiteral + | integer + | null""" + if type(p[1]) == str: + p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'integer'), + self.BuildAttribute('NAME', p[1])) + else: + p[0] = p[1] + + # [27.1] Add definition for NULL + def p_null(self, p): + """null : NULL""" + p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'NULL'), + self.BuildAttribute('NAME', 'NULL')) + + # [28] + def p_BooleanLiteral(self, p): + """BooleanLiteral : TRUE + | FALSE""" + value = self.BuildAttribute('NAME', Boolean(p[1] == 'true')) + p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'boolean'), value) + + # [29] + 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)) + + # [30] + def p_AttributeOrOperation(self, p): + """AttributeOrOperation : STRINGIFIER StringifierAttributeOrOperation + | Attribute + | Operation""" + if len(p) > 2: + p[0] = p[2] + else: + p[0] = p[1] + + # [31] + def p_StringifierAttributeOrOperation(self, p): + """StringifierAttributeOrOperation : Attribute + | OperationRest + | ';'""" + if p[1] == ';': + p[0] = self.BuildAttribute('STRINGIFIER', Boolean(True)) + else: + p[0] = ListFromConcat(self.BuildAttribute('STRINGIFIER', p[1]), p[1]) + + # [32] + def p_Attribute(self, p): + """Attribute : Inherit ReadOnly ATTRIBUTE Type identifier ';'""" + p[0] = self.BuildNamed('Attribute', p, 5, + ListFromConcat(p[1], p[2], p[4])) + + # [33] + def p_Inherit(self, p): + """Inherit : INHERIT + |""" + if len(p) > 1: + p[0] = self.BuildTrue('INHERIT') + + # [34] + def p_ReadOnly(self, p): + """ReadOnly : READONLY + |""" + if len(p) > 1: + p[0] = self.BuildTrue('READONLY') + + # [35] + def p_Operation(self, p): + """Operation : Qualifiers OperationRest""" + p[2].AddChildren(p[1]) + p[0] = p[2] + + # [36] + def p_Qualifiers(self, p): + """Qualifiers : STATIC + | Specials""" + if p[1] == 'static': + p[0] = self.BuildTrue('STATIC') + else: + p[0] = p[1] + + # [37] + def p_Specials(self, p): + """Specials : Special Specials + | """ + if len(p) > 1: + p[0] = ListFromConcat(p[1], p[2]) + + # [38] + def p_Special(self, p): + """Special : GETTER + | SETTER + | CREATOR + | DELETER + | LEGACYCALLER""" + p[0] = self.BuildTrue(p[1].upper()) + + + # [39] + def p_OperationRest(self, p): + """OperationRest : ReturnType OptionalIdentifier '(' ArgumentList ')' ';'""" + arguments = self.BuildProduction('Arguments', p, 3, p[4]) + p[0] = self.BuildNamed('Operation', p, 2, ListFromConcat(p[1], arguments)) + + # [40] + def p_OptionalIdentifier(self, p): + """OptionalIdentifier : identifier + |""" + if len(p) > 1: + p[0] = p[1] + else: + p[0] = '_unnamed_' + + # [41] + def p_ArgumentList(self, p): + """ArgumentList : Argument Arguments + |""" + if len(p) > 1: + p[0] = ListFromConcat(p[1], p[2]) + + # [41.1] ArgumentList error recovery + def p_ArgumentListError(self, p): + """ArgumentList : error """ + p[0] = self.BuildError(p, 'ArgumentList') + + # [42] + def p_Arguments(self, p): + """Arguments : ',' Argument Arguments + |""" + if len(p) > 1: + p[0] = ListFromConcat(p[2], p[3]) + + # [43] + def p_Argument(self, p): + """Argument : ExtendedAttributeList OptionalOrRequiredArgument""" + p[2].AddChildren(p[1]) + p[0] = p[2] + + + # [44] + def p_OptionalOrRequiredArgument(self, p): + """OptionalOrRequiredArgument : OPTIONAL Type ArgumentName Default + | Type Ellipsis ArgumentName""" + if len(p) > 4: + arg = self.BuildNamed('Argument', p, 3, ListFromConcat(p[2], p[4])) + arg.AddChildren(self.BuildTrue('OPTIONAL')) + else: + arg = self.BuildNamed('Argument', p, 3, ListFromConcat(p[1], p[2])) + p[0] = arg + + # [45] + def p_ArgumentName(self, p): + """ArgumentName : ArgumentNameKeyword + | identifier""" + p[0] = p[1] + + # [46] + def p_Ellipsis(self, p): + """Ellipsis : ELLIPSIS + |""" + if len(p) > 1: + p[0] = self.BuildNamed('Argument', p, 1) + p[0].AddChildren(self.BuildTrue('ELLIPSIS')) + + # [47] + def p_ExceptionMember(self, p): + """ExceptionMember : Const + | ExceptionField""" + p[0] = p[1] + + # [48] + def p_ExceptionField(self, p): + """ExceptionField : Type identifier ';'""" + p[0] = self.BuildNamed('ExceptionField', p, 2, p[1]) + + # [48.1] Error recovery for ExceptionMembers + def p_ExceptionFieldError(self, p): + """ExceptionField : error""" + p[0] = self.BuildError(p, 'ExceptionField') + + # [49] Add optional comment field + def p_ExtendedAttributeList(self, p): + """ExtendedAttributeList : Comments '[' ExtendedAttribute ExtendedAttributes ']' + | Comments """ + if len(p) > 2: + items = ListFromConcat(p[3], p[4]) + attribs = self.BuildProduction('ExtAttributes', p, 2, items) + p[0] = ListFromConcat(p[1], attribs) + else: + p[0] = p[1] + + # [50] + def p_ExtendedAttributes(self, p): + """ExtendedAttributes : ',' ExtendedAttribute ExtendedAttributes + |""" + if len(p) > 1: + p[0] = ListFromConcat(p[2], p[3]) + + # We only support: + # [ identifier ] + # [ identifier = identifier ] + # [ identifier ( ArgumentList )] + # [ identifier = identifier ( ArgumentList )] + # [51] map directly to 74-77 + # [52-54, 56] are unsupported + def p_ExtendedAttribute(self, p): + """ExtendedAttribute : ExtendedAttributeNoArgs + | ExtendedAttributeArgList + | ExtendedAttributeIdent + | ExtendedAttributeNamedArgList""" + p[0] = p[1] + + # [55] + def p_ArgumentNameKeyword(self, p): + """ArgumentNameKeyword : ATTRIBUTE + | CALLBACK + | CONST + | CREATOR + | DELETER + | DICTIONARY + | ENUM + | EXCEPTION + | GETTER + | IMPLEMENTS + | INHERIT + | LEGACYCALLER + | PARTIAL + | SETTER + | STATIC + | STRINGIFIER + | TYPEDEF + | UNRESTRICTED""" + p[0] = p[1] + + # [57] + def p_Type(self, p): + """Type : SingleType + | UnionType TypeSuffix""" + 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])) + + # [58] + def p_SingleType(self, p): + """SingleType : NonAnyType + | ANY TypeSuffixStartingWithArray""" + if len(p) == 2: + p[0] = p[1] + else: + p[0] = ListFromConcat(self.BuildProduction('Any', p, 1), p[2]) + + # [59] + def p_UnionType(self, p): + """UnionType : '(' UnionMemberType OR UnionMemberType UnionMemberTypes ')'""" + + # [60] + def p_UnionMemberType(self, p): + """UnionMemberType : NonAnyType + | UnionType TypeSuffix + | ANY '[' ']' TypeSuffix""" + # [61] + def p_UnionMemberTypes(self, p): + """UnionMemberTypes : OR UnionMemberType UnionMemberTypes + |""" + + # [62] Moved DATE, DOMSTRING, OBJECT to PrimitiveType + def p_NonAnyType(self, p): + """NonAnyType : PrimitiveType TypeSuffix + | identifier TypeSuffix + | SEQUENCE '<' Type '>' 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: + p[0] = self.BuildProduction('Sequence', p, 1, ListFromConcat(p[3], p[5])) + + + # [63] + 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] + + + # [64] + def p_PrimitiveType(self, p): + """PrimitiveType : UnsignedIntegerType + | UnrestrictedFloatType + | BOOLEAN + | BYTE + | OCTET + | DOMSTRING + | DATE + | OBJECT""" + if type(p[1]) == str: + p[0] = self.BuildNamed('PrimitiveType', p, 1) + else: + p[0] = p[1] + + + # [65] + 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 + + + # [66] + def p_FloatType(self, p): + """FloatType : FLOAT + | DOUBLE""" + p[0] = p[1] + + # [67] + def p_UnsignedIntegerType(self, p): + """UnsignedIntegerType : UNSIGNED IntegerType + | IntegerType""" + if len(p) == 2: + p[0] = p[1] + else: + p[0] = 'unsigned ' + p[2] + + # [68] + def p_IntegerType(self, p): + """IntegerType : SHORT + | LONG OptionalLong""" + if len(p) == 2: + p[0] = p[1] + else: + p[0] = p[1] + p[2] + + # [69] + def p_OptionalLong(self, p): + """OptionalLong : LONG + | """ + if len(p) > 1: + p[0] = ' ' + p[1] + else: + p[0] = '' + + + # [70] Add support for sized array + def p_TypeSuffix(self, p): + """TypeSuffix : '[' integer ']' TypeSuffix + | '[' ']' TypeSuffix + | '?' TypeSuffixStartingWithArray + |""" + if len(p) == 5: + p[0] = self.BuildNamed('Array', p, 2, p[4]) + + if len(p) == 4: + p[0] = self.BuildProduction('Array', p, 1, p[3]) + + if len(p) == 3: + p[0] = ListFromConcat(self.BuildTrue('NULLABLE'), p[2]) + + + # [71] + def p_TypeSuffixStartingWithArray(self, p): + """TypeSuffixStartingWithArray : '[' ']' TypeSuffix + | """ + if len(p) > 1: + p[0] = self.BuildProduction('Array', p, 0, p[3]) + + # [72] + def p_Null(self, p): + """Null : '?' + |""" + if len(p) > 1: + p[0] = self.BuildTrue('NULLABLE') + + # [73] + 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] + + # [74] + def p_ExtendedAttributeNoArgs(self, p): + """ExtendedAttributeNoArgs : identifier""" + p[0] = self.BuildNamed('ExtAttribute', p, 1) + + # [75] + def p_ExtendedAttributeArgList(self, p): + """ExtendedAttributeArgList : identifier '(' ArgumentList ')'""" + arguments = self.BuildProduction('Arguments', p, 2, p[3]) + p[0] = self.BuildNamed('ExtAttribute', p, 1, arguments) + + # [76] + def p_ExtendedAttributeIdent(self, p): + """ExtendedAttributeIdent : identifier '=' identifier""" + value = self.BuildAttribute('VALUE', p[3]) + p[0] = self.BuildNamed('ExtAttribute', p, 1, value) + + # [77] + 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) + +# +# 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=False, + 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. +# +# p - Is the Yacc production object containing the stack of items +# index - Index into the production of the name for the item being produced. +# cls - The type of item being producted +# 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 BuildComment(self, cls, p, index): + name = p[index] + + # Remove comment markers + lines = [] + if name[:2] == '//': + # For C++ style, remove any leading whitespace and the '//' marker from + # each line. + form = 'cc' + for line in name.split('\n'): + start = line.find('//') + lines.append(line[start+2:]) + else: + # For C style, remove ending '*/'' + form = 'c' + for line in name[:-2].split('\n'): + # Remove characters until start marker for this line '*' if found + # otherwise it should be blank. + offs = line.find('*') + if offs >= 0: + line = line[offs + 1:].rstrip() + else: + line = '' + lines.append(line) + name = '\n'.join(lines) + childlist = [self.BuildAttribute('NAME', name), + self.BuildAttribute('FORM', form)] + return self.BuildProduction(cls, 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('LINE', self._last_error_lineno) + pos = self.BuildAttribute('POS', 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): + 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) + 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 - %s.' % ( + filename, last.lineno, str(e))) + + +def main(argv): + nodes = [] + parser = IDLParser(IDLLexer()) + errors = 0 + for filename in argv: + filenode = ParseFile(parser, filename) + errors += filenode.GetProperty('ERRORS') + nodes.append(filenode) + + ast = IDLNode('AST', '__AST__', 0, 0, nodes) + + print '\n'.join(ast.Tree(accept_props=['PROD'])) + if errors: + print '\nFound %d errors.\n' % errors + + + return errors + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:]))
\ No newline at end of file diff --git a/tools/idl_parser/idl_parser_test.py b/tools/idl_parser/idl_parser_test.py new file mode 100755 index 0000000..5b7669e --- /dev/null +++ b/tools/idl_parser/idl_parser_test.py @@ -0,0 +1,61 @@ +#!/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 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) + self.filenames = glob.glob('test_parser/*.idl') + + def _TestNode(self, node): + comments = node.GetListOf('Comment') + for comment in comments: + check, value = ParseCommentTest(comment.GetName()) + if check == 'BUILD': + msg = 'Expecting %s, but found %s.\n' % (value, str(node)) + self.assertEqual(value, str(node), msg) + + 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 == 'PROP': + key, expect = value.split('=') + actual = str(node.GetProperty(key)) + msg = 'Mismatched property %s: %s vs %s.\n' % (key, expect, actual) + self.assertEqual(expect, actual, msg) + + if check == 'TREE': + quick = '\n'.join(node.Tree()) + lineno = node.GetProperty('LINENO') + msg = 'Mismatched tree at line %d:\n%sVS\n%s' % (lineno, 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()[2:]: + self._TestNode(node) + + +if __name__ == '__main__': + unittest.main(verbosity=2) + diff --git a/tools/idl_parser/idl_ppapi_lexer.py b/tools/idl_parser/idl_ppapi_lexer.py index 3accd2d..f615c77 100644 --- a/tools/idl_parser/idl_ppapi_lexer.py +++ b/tools/idl_parser/idl_ppapi_lexer.py @@ -23,32 +23,27 @@ import sys # IDL PPAPI Lexer # class IDLPPAPILexer(IDLLexer): - # 'tokens' is a value required by lex which specifies the complete list - # of valid token types. To WebIDL we add the following token types - IDLLexer.tokens += [ - # Operators - 'LSHIFT', - 'RSHIFT', - - # Pepper Extras - 'INLINE', - ] - - # '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. Add the new keywords to the - # dictionary and set of tokens - ppapi_keywords = ['LABEL', 'NAMESPACE', 'STRUCT'] - for keyword in ppapi_keywords: - IDLLexer.keywords[ keyword.lower() ] = keyword - IDLLexer.tokens.append(keyword) - # Special multi-character operators - t_LSHIFT = r'<<' - t_RSHIFT = r'>>' + def t_LSHIFT(self, t): + r'<<' + return t; + + def t_RSHIFT(self, t): + r'>>' + return t; - # Return a "preprocessor" inline block def t_INLINE(self, t): r'\#inline (.|\n)*?\#endinl.*' self.AddLines(t.value.count('\n')) return t + + # Return a "preprocessor" inline block + def __init__(self): + IDLLexer.__init__(self) + self._AddTokens(['LSHIFT', 'RSHIFT', 'INLINE']) + self._AddKeywords(['label', 'namespace', 'struct']) + + +# If run by itself, attempt to build the lexer +if __name__ == '__main__': + lexer = IDLPPAPILexer() diff --git a/tools/idl_parser/run_tests.py b/tools/idl_parser/run_tests.py index 9bb1356..cf26759 100755 --- a/tools/idl_parser/run_tests.py +++ b/tools/idl_parser/run_tests.py @@ -8,6 +8,13 @@ import sys import unittest if __name__ == '__main__': - testlist = glob.glob('*_test.py') - for testname in testlist: - unittest.main(verbosity=2, module=testname[:-3])
\ No newline at end of file + suite = unittest.TestSuite() + for testname in glob.glob('*_test.py'): + print 'Adding Test: ' + testname + module = __import__(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/tools/idl_parser/test_lexer/keywords.in b/tools/idl_parser/test_lexer/keywords.in index e155753..11baff9 100644 --- a/tools/idl_parser/test_lexer/keywords.in +++ b/tools/idl_parser/test_lexer/keywords.in @@ -17,10 +17,8 @@ GETTER getter IMPLEMENTS implements INFINITY Infinity INTERFACE interface -LABEL label LEGACYCALLER legacycaller LONG long -NAMESPACE namespace NAN Nan NULL null OBJECT object @@ -32,7 +30,6 @@ SETTER setter SHORT short STATIC static STRINGIFIER stringifier -STRUCT struct TYPEDEF typedef TRUE true UNSIGNED unsigned diff --git a/tools/idl_parser/test_parser/callback.idl b/tools/idl_parser/test_parser/callback.idl new file mode 100644 index 0000000..b16b6b5 --- /dev/null +++ b/tools/idl_parser/test_parser/callback.idl @@ -0,0 +1,116 @@ +/* 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. + +BUILD Type(Name) +This comment signals that a node of type <Type> is created with the +name <Name>. + +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. + +PROP Key=Value +This comment signals that a property has been set on the Node such that +<Key> = <Value>. + +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(VoidFuncLongArray) + * Type() + * PrimitiveType(void) + * Arguments() + * Argument(L1) + * Type() + * PrimitiveType(long) + * Array() + */ +callback VoidFuncLongArray = void ( long[] L1 ); + +/* TREE + *Callback(VoidFuncLongArray5) + * Type() + * PrimitiveType(void) + * Arguments() + * Argument(L1) + * Type() + * PrimitiveType(long) + * Array(5) + */ +callback VoidFuncLongArray5 = void ( long[5] L1 ); + + +/* TREE + *Callback(VoidFuncLongArray54) + * Type() + * PrimitiveType(void) + * Arguments() + * Argument(L1) + * Type() + * PrimitiveType(long) + * Array(5) + * Argument(L2) + * Type() + * PrimitiveType(long long) + * Array(4) + */ +callback VoidFuncLongArray54 = void ( long[5] L1, long long [4] L2 ); + + +/* TREE + *Callback(VoidFuncLongIdent) + * Type() + * PrimitiveType(void) + * Arguments() + * Argument(L1) + * Type() + * PrimitiveType(long) + * Array(5) + * Argument(L2) + * Type() + * Typeref(VoidFuncLongArray) + */ +callback VoidFuncLongIdent = void ( long[5] L1, VoidFuncLongArray L2 ); diff --git a/tools/idl_parser/test_parser/dictionary.idl b/tools/idl_parser/test_parser/dictionary.idl new file mode 100644 index 0000000..5030686 --- /dev/null +++ b/tools/idl_parser/test_parser/dictionary.idl @@ -0,0 +1,95 @@ +/* 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. + +BUILD Type(Name) +This comment signals that a node of type <Type> is created with the +name <Name>. + +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. + +PROP Key=Value +This comment signals that a property has been set on the Node such that +<Key> = <Value>. + +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 dictionary MyDictPartial { }; + +/* ERROR Unexpected ":" after identifier "MyDictInherit". */ +partial dictionary MyDictInherit : Foo {}; + +/* TREE + *Dictionary(MyDictBig) + * Key(setString) + * Type() + * PrimitiveType(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; +}; + + +/* ERROR Unexpected "{" after keyword "dictionary". */ +dictionary { + DOMString? setString = null; +}; + + +/* ERROR Unexpected identifier "NoColon" after identifier "ForParent". */ +dictionary ForParent NoColon { + DOMString? setString = null; +}; + +/* TREE + *Dictionary(MyDictNull) + * Key(setString) + * Type() + * PrimitiveType(DOMString) + * Default(NULL) + */ +dictionary MyDictNull { + DOMString? setString = null; +}; + + diff --git a/tools/idl_parser/test_parser/enum.idl b/tools/idl_parser/test_parser/enum.idl new file mode 100644 index 0000000..233410c --- /dev/null +++ b/tools/idl_parser/test_parser/enum.idl @@ -0,0 +1,119 @@ +/* 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 Enum productions + +Run with --test to generate an AST and verify that all comments accurately +reflect the state of the Nodes. + +BUILD Type(Name) +This comment signals that a node of type <Type> is created with the +name <Name>. + +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. + +PROP Key=Value +This comment signals that a property has been set on the Node such that +<Key> = <Value>. + +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 + *Enum(MealType1) + * EnumItem(rice) + * EnumItem(noodles) + * EnumItem(other) +*/ +enum MealType1 { + /* BUILD EnumItem (rice) */ + "rice", + /* BUILD EnumItem (noodles) */ + "noodles", + /* BUILD EnumItem(other) */ + "other" +}; + +/* BUILD Error(Enum missing name.) */ +/* ERROR Enum missing name. */ +enum { + "rice", + "noodles", + "other" +}; + +/* TREE + *Enum(MealType2) + * EnumItem(rice) + * EnumItem(noodles) + * EnumItem(other) +*/ +enum MealType2 { + /* BUILD EnumItem(rice) */ + "rice", + /* BUILD EnumItem(noodles) */ + "noodles", + /* BUILD EnumItem(other) */ + "other" +}; + +/* BUILD Error(Unexpected string "noodles" after string "rice".) */ +/* ERROR Unexpected string "noodles" after string "rice". */ +enum MissingComma { + "rice" + "noodles", + "other" +}; + +/* BUILD Error(Trailing comma in block.) */ +/* ERROR Trailing comma in block. */ +enum TrailingComma { + "rice", + "noodles", + "other", +}; + +/* BUILD Error(Unexpected "," after ",".) */ +/* ERROR Unexpected "," after ",". */ +enum ExtraComma { + "rice", + "noodles", + ,"other", +}; + +/* BUILD Error(Unexpected keyword "interface" after "{".) */ +/* ERROR Unexpected keyword "interface" after "{". */ +enum ExtraComma { + interface, + "noodles", + ,"other", +}; + +/* BUILD Error(Unexpected identifier "somename" after "{".) */ +/* ERROR Unexpected identifier "somename" after "{". */ +enum ExtraComma { + somename, + "noodles", + ,"other", +}; + +/* BUILD Enum(MealType3) */ +enum MealType3 { + /* BUILD EnumItem(rice) */ + "rice", + /* BUILD EnumItem(noodles) */ + "noodles", + /* BUILD EnumItem(other) */ + "other" +}; + diff --git a/tools/idl_parser/test_parser/exception.idl b/tools/idl_parser/test_parser/exception.idl new file mode 100644 index 0000000..3801a4a --- /dev/null +++ b/tools/idl_parser/test_parser/exception.idl @@ -0,0 +1,87 @@ +/* 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 Exception productions + +Run with --test to generate an AST and verify that all comments accurately +reflect the state of the Nodes. + +BUILD Type(Name) +This comment signals that a node of type <Type> is created with the +name <Name>. + +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. + +PROP Key=Value +This comment signals that a property has been set on the Node such that +<Key> = <Value>. + +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 + *Exception(MyExc) + */ +exception MyExc { }; + +/* TREE + *Exception(MyExcInherit) + * Inherit(Foo) + */ +exception MyExcInherit : Foo {}; + +/* ERROR Unexpected keyword "exception" after keyword "partial". */ +partial exception MyExcPartial { }; + +/* TREE + *Exception(MyExcBig) + * ExceptionField(MyString) + * Type() + * PrimitiveType(DOMString) + * Error(Unexpected "=" after identifier "ErrorSetLong".) + * ExceptionField(MyLong) + * Type() + * PrimitiveType(long) + */ +exception MyExcBig { + DOMString MyString; + unsigned long long ErrorSetLong = 123; + long MyLong; +}; + + +/* ERROR Unexpected "{" after keyword "exception". */ +exception { + DOMString? setString = null; +}; + + +/* ERROR Unexpected identifier "NoColon" after identifier "ForParent". */ +exception ForParent NoColon { + DOMString? setString = null; +}; + +/* TREE + *Exception(MyExcConst) + * Const(setString) + * PrimitiveType(DOMString) + * Value(NULL) + */ +exception MyExcConst { + const DOMString? setString = null; +}; + + + + diff --git a/tools/idl_parser/test_parser/implements.idl b/tools/idl_parser/test_parser/implements.idl new file mode 100644 index 0000000..252dd4b --- /dev/null +++ b/tools/idl_parser/test_parser/implements.idl @@ -0,0 +1,52 @@ +/* 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 Implements productions + +Run with --test to generate an AST and verify that all comments accurately +reflect the state of the Nodes. + +BUILD Type(Name) +This comment signals that a node of type <Type> is created with the +name <Name>. + +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. + +PROP Key=Value +This comment signals that a property has been set on the Node such that +<Key> = <Value>. + +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. +*/ + +/* BUILD Implements(A) */ +/* PROP REFERENCE=B */ +A implements B; + +/* ERROR Unexpected ";" after keyword "implements". */ +A implements; + +/* BUILD Implements(B) */ +/* PROP REFERENCE=C */ +B implements C; + +/* ERROR Unexpected keyword "implements" after "]". */ +[foo] implements B; + +/* BUILD Implements(D) */ +/* PROP REFERENCE=E */ +D implements E; + +/* ERROR Unexpected keyword "implements" after comment. */ +implements C; + diff --git a/tools/idl_parser/test_parser/interface.idl b/tools/idl_parser/test_parser/interface.idl new file mode 100644 index 0000000..a8a4135 --- /dev/null +++ b/tools/idl_parser/test_parser/interface.idl @@ -0,0 +1,127 @@ +/* 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. + +BUILD Type(Name) +This comment signals that a node of type <Type> is created with the +name <Name>. + +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. + +PROP Key=Value +This comment signals that a property has been set on the Node such that +<Key> = <Value>. + +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 interface MyIFacePartial { }; + +/* ERROR Unexpected ":" after identifier "MyIFaceInherit". */ +partial interface MyIFaceInherit : Foo {}; + +/* TREE + *Interface(MyIFaceBig) + * Const(setString) + * PrimitiveType(DOMString) + * Value(NULL) + */ +interface MyIFaceBig { + const DOMString? setString = null; +}; + +/* TREE + *Interface(MyIFaceBig2) + * Const(nullValue) + * PrimitiveType(DOMString) + * Value(NULL) + * Const(longValue) + * PrimitiveType(long) + * Value(123) + * Const(longValue2) + * PrimitiveType(long long) + * Value(123) + * Attribute(myString) + * Type() + * PrimitiveType(DOMString) + * Attribute(readOnlyString) + * Type() + * PrimitiveType(DOMString) + * Operation(myFunction) + * Type() + * PrimitiveType(void) + * Arguments() + * Argument(myLong) + * Type() + * PrimitiveType(long long) + */ +interface MyIFaceBig2 { + const DOMString? nullValue = null; + const long longValue = 123; + const long long longValue2 = 123; + attribute DOMString myString; + readonly attribute DOMString readOnlyString; + void myFunction(long long myLong); +}; + + +/* TREE + *Interface(MyIFaceSpecials) + * Operation(set) + * Type() + * PrimitiveType(void) + * Arguments() + * Argument(property) + * Type() + * PrimitiveType(DOMString) + * Operation(_unnamed_) + * Type() + * PrimitiveType(double) + * Arguments() + * Argument(property) + * Type() + * PrimitiveType(DOMString) + * Operation(GetFiveSix) + * Type() + * PrimitiveType(long long) + * Array(5) + * Array(6) + * Arguments() + * Argument(arg) + * Type() + * Typeref(SomeType) + */ +interface MyIFaceSpecials { + setter creator void set(DOMString property); + getter double (DOMString property); + long long [5][6] GetFiveSix(SomeType arg); +}; diff --git a/tools/idl_parser/test_parser/typedef.idl b/tools/idl_parser/test_parser/typedef.idl new file mode 100644 index 0000000..ba95db7c --- /dev/null +++ b/tools/idl_parser/test_parser/typedef.idl @@ -0,0 +1,190 @@ +/* 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. + +BUILD Type(Name) +This comment signals that a node of type <Type> is created with the +name <Name>. + +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. + +PROP Key=Value +This comment signals that a property has been set on the Node such that +<Key> = <Value>. + +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) + * ExtAttributes() + * ExtAttribute(foo) + * Type() + * PrimitiveType(long) + */ +typedef [foo] long MyLong; + +/* TREE + *Typedef(MyLongArray) + * Type() + * PrimitiveType(long) + * Array() + */ +typedef long[] MyLongArray; + +/* TREE + *Typedef(MyLongSizedArray) + * Type() + * PrimitiveType(long) + * Array(4) + */ +typedef long[4] MyLongSizedArray; + +/* TREE + *Typedef(MyLongSizedArrayArray) + * Type() + * PrimitiveType(long) + * Array(4) + * Array(5) + */ +typedef long[4][5] MyLongSizedArrayArray; + +/* TREE + *Typedef(MyLongArraySizedArray) + * Type() + * PrimitiveType(long) + * Array() + * Array(5) + */ +typedef long[][5] MyLongArraySizedArray; + +/* TREE + *Typedef(MyTypeFive) + * Type() + * Typeref(MyType) + * Array(5) + */ +typedef MyType[5] MyTypeFive; + +/* TREE + *Typedef(MyTypeUnsizedFive) + * Type() + * Typeref(MyType) + * Array() + * Array(5) + */ +typedef MyType[][5] MyTypeUnsizedFive; + +/* 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() + * PrimitiveType(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) + */ +typedef unrestricted float MyUFloat; + +/* TREE + *Typedef(MyDouble) + * Type() + * PrimitiveType(double) + */ +typedef double MyDouble; + +/* TREE + *Typedef(MyUDouble) + * Type() + * PrimitiveType(double) + */ +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; + |