Index: tools/nixysa/nixysa/idl_parser.py |
=================================================================== |
--- tools/nixysa/nixysa/idl_parser.py (revision 0) |
+++ tools/nixysa/nixysa/idl_parser.py (revision 0) |
@@ -0,0 +1,566 @@ |
+#!/usr/bin/python2.4 |
+# |
+# Copyright 2008 Google Inc. |
+# |
+# Licensed under the Apache License, Version 2.0 (the "License"); |
+# you may not use this file except in compliance with the License. |
+# You may obtain a copy of the License at |
+# |
+# http://www.apache.org/licenses/LICENSE-2.0 |
+# |
+# Unless required by applicable law or agreed to in writing, software |
+# distributed under the License is distributed on an "AS IS" BASIS, |
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
+# See the License for the specific language governing permissions and |
+# limitations under the License. |
+ |
+"""Parser module for IdlGlue-NG code generator. |
+ |
+This is the parser module for the IdlGlue-NG code generator. It is written using |
+ply (http://www.dabeaz.com/ply/). |
+""" |
+ |
+import sys |
+import os.path |
+from ply import lex |
+from ply import yacc |
+ |
+import syntax_tree |
+ |
+ |
+class File(object): |
+ """Simple class that stores filenames for each IDL source file. |
+ |
+ Various code generation back-ends can add fields, such as 'glue header' or |
+ 'glue cpp'. |
+ |
+ Attributes: |
+ source: the source IDL filename. |
+ header: C++ header file containing definitions of types declared in this |
+ IDL file. |
+ """ |
+ |
+ def __init__(self, filename): |
+ """Inits a File instance. |
+ |
+ Args: |
+ filename: the IDL filename. |
+ """ |
+ self.source = filename |
+ self.basename = os.path.basename(filename).split('.')[0] |
+ self.header = self.basename + '.h' |
+ self.documentation = self.basename + '.doc' |
+ |
+ def __str__(self): |
+ return self.source |
+ |
+ |
+class SourceLocation(object): |
+ """Simple class that stores the source location of IDL definitions. |
+ |
+ Attributes: |
+ file: the source IDL File object containing the definition. |
+ line: the source line of the definition. |
+ """ |
+ |
+ def __init__(self, source_file, line): |
+ """Inits a SourceLocation instance. |
+ |
+ Args: |
+ source_file: the source IDL File object containing the definition. |
+ line: the source line of the definition. |
+ """ |
+ self.file = source_file |
+ self.line = line |
+ |
+ def __str__(self): |
+ return '%s:%s' % (self.file, self.line) |
+ |
+ |
+class Parser(object): |
+ """IDL parser class. |
+ |
+ This class implements the IDL parser, that parses IDL files to produce syntax |
+ tree objects. It is written using PLY. t_* methods and members are used for |
+ the ply lexer to define the tokens and their regular expressions, p_* methods |
+ are used for the ply parser to define the grammar rules. |
+ """ |
+ |
+ def __init__(self, output_dir): |
+ self.output_dir = output_dir |
+ |
+ # remove gpylint warnings regarding docstrings and naming. |
+ # pylint: disable-msg=C6409,C6102,C6108,C6104,C6111,C6105,C6310 |
+ |
+ tokens = ('NAMESPACE', |
+ 'CLASS', |
+ 'ENUM', |
+ 'TYPEDEF', |
+ 'TYPENAME', |
+ 'CALLBACK', |
+ 'QUALIFIER', |
+ 'SIGNED', |
+ 'TEXT', |
+ 'VERBATIM_OPEN', |
+ 'VERBATIM_CLOSE', |
+ 'DOCUMENTATION_OPEN', |
+ 'DOCUMENTATION_CLOSE', |
+ 'COMMENT_OPEN', |
+ 'COMMENT_CLOSE', |
+ 'STRING_OPEN', |
+ 'STRING_CLOSE', |
+ 'ID', |
+ 'NUMBER') |
+ |
+ _reserved = {'namespace': 'NAMESPACE', |
+ 'class': 'CLASS', |
+ 'struct': 'CLASS', |
+ 'enum': 'ENUM', |
+ 'typename': 'TYPENAME', |
+ 'typedef': 'TYPEDEF', |
+ 'callback': 'CALLBACK', |
+ 'unsigned': 'SIGNED', |
+ 'signed': 'SIGNED', |
+ 'const': 'QUALIFIER', |
+ 'volatile': 'QUALIFIER', |
+ 'restrict': 'QUALIFIER'} |
+ |
+ states = (('verbatim', 'exclusive'), |
+ ('documentation', 'exclusive'), |
+ ('cppcomment', 'exclusive'), |
+ ('ccomment', 'exclusive'), |
+ ('string', 'exclusive')) |
+ |
+ literals = ['{', '}', '(', ')', '[', ']', ';', ':', ',', '=', '?'] |
+ |
+ # Tokens |
+ t_ignore = ' \t\r' |
+ t_verbatim_ignore = '\r' |
+ t_documentation_ignore = '\r' |
+ t_ccomment_ignore = '\r' |
+ t_cppcomment_ignore = '\r' |
+ t_string_ignore = '\r' |
+ t_NUMBER = r'0x[0-9A-Fa-f]+|0[0-7]*|[1-9][0-9]*' |
+ |
+ def t_COMMENT_OPEN_C(self, t): |
+ r'/\*' |
+ t.lexer.begin('ccomment') |
+ t.type = 'COMMENT_OPEN' |
+ #return t |
+ |
+ def t_ccomment_COMMENT_CLOSE(self, t): |
+ r'\*/' |
+ t.lexer.begin('INITIAL') |
+ #return t |
+ |
+ def t_COMMENT_OPEN_CPP(self, t): |
+ r'//' |
+ t.lexer.begin('cppcomment') |
+ t.type = 'COMMENT_OPEN' |
+ #return t |
+ |
+ def t_cppcomment_COMMENT_CLOSE(self, t): |
+ r'\n' |
+ t.lexer.lineno += 1 |
+ t.lexer.begin('INITIAL') |
+ #return t |
+ |
+ def t_STRING_OPEN(self, t): |
+ r'"' |
+ t.lexer.begin('string') |
+ return t |
+ |
+ def t_string_STRING_CLOSE(self, t): |
+ r'"' |
+ t.lexer.begin('INITIAL') |
+ return t |
+ |
+ def t_string_TEXT(self, t): |
+ r'[^\\\r"]+' |
+ t.lexer.lineno += t.value.count('\n') |
+ return t |
+ |
+ def t_string_TEXT_ESCAPE(self, t): |
+ r'\\.' |
+ t.type = 'TEXT' |
+ escape_map = {'n': '\n', 'r': '\r', 't': '\t'} |
+ char = t.value[1] |
+ t.value = escape_map.get(char, char) |
+ return t |
+ |
+ def t_INITIAL_newline(self, t): |
+ r'\n+' |
+ t.lexer.lineno += t.value.count('\n') |
+ |
+ def t_ANY_error(self, t): |
+ location = self._GetLocation() |
+ print ("Illegal character '%s' at file %s line %d" % |
+ (t.value[0], location.file.source, location.line)) |
+ t.lexer.skip(1) |
+ |
+ def t_ID(self, t): |
+ r'~?[a-zA-Z_][a-zA-Z_0-9]*' |
+ t.type = self._reserved.get(t.value, 'ID') # Check for reserved words |
+ return t |
+ |
+ def t_VERBATIM_OPEN(self, t): |
+ r'%{' |
+ t.lexer.begin('verbatim') |
+ return t |
+ |
+ def t_verbatim_VERBATIM_CLOSE(self, t): |
+ r'%}' |
+ t.lexer.begin('INITIAL') |
+ return t |
+ |
+ def t_DOCUMENTATION_OPEN(self, t): |
+ r'%\[' |
+ t.lexer.begin('documentation') |
+ return t |
+ |
+ def t_documentation_DOCUMENTATION_CLOSE(self, t): |
+ r'%\]' |
+ t.lexer.begin('INITIAL') |
+ return t |
+ |
+ def t_cppcomment_TEXT(self, t): |
+ r'[^\n\r]+' |
+ #return t |
+ |
+ # group characters for faster parsing, but escape at '%' and '*' to let the |
+ # rules above to take precedence |
+ def t_ccomment_TEXT(self, t): |
+ r'[^*\r]+|\*' |
+ t.lexer.lineno += t.value.count('\n') |
+ #return t |
+ |
+ # group characters for faster parsing, but escape at '%' and '*' to let the |
+ # rules above to take precedence |
+ def t_verbatim_ccomment_TEXT(self, t): |
+ r'[^%\r]+|%' |
+ t.lexer.lineno += t.value.count('\n') |
+ return t |
+ |
+ # group characters for faster parsing, but escape at '%' and '*' to let the |
+ # rules above to take precedence |
+ def t_documentation_ccomment_TEXT(self, t): |
+ r'[^%\r]+|%' |
+ t.lexer.lineno += t.value.count('\n') |
+ return t |
+ |
+ # Grammar rules |
+ |
+ def p_start(self, p): |
+ 'start : definition_list' |
+ p[0] = p[1] |
+ |
+ def p_class_definition(self, p): |
+ # This line is long, but it should not be split, PLY needs individual rules |
+ # to be on a single line. |
+ "class_definition : attributes_opt CLASS ID base_class '{' member_definition_list '}' ';'" |
+ p[0] = syntax_tree.Class(self._GetLocation(), attributes=p[1], name=p[3], |
+ base_type_ref=p[4], defn_list=p[6]) |
+ |
+ def p_base_class_1(self, p): |
+ 'base_class : empty' |
+ p[0] = None |
+ |
+ def p_base_class_2(self, p): |
+ "base_class : ':' type" |
+ p[0] = p[2] |
+ |
+ def p_attributes_opt_1(self, p): |
+ 'attributes_opt : empty' |
+ p[0] = {} |
+ |
+ # If we want to force documentation, this rule and the one above should not |
+ # exist. |
+ def p_attributes_opt_2(self, p): |
+ "attributes_opt : '[' attribute_list_opt ']'" |
+ p[0] = dict(p[2]) |
+ |
+ def p_attributes_opt_3(self, p): |
+ "attributes_opt : documentation_value '[' attribute_list_opt ']'" |
+ p[0] = dict(p[1] + p[3]) |
+ |
+ def p_attributes_opt_4(self, p): |
+ 'attributes_opt : documentation_value' |
+ p[0] = dict(p[1]) |
+ |
+ def p_attribute_list_opt(self, p): |
+ """attribute_list_opt : empty |
+ | attribute_list""" |
+ if len(p) == 1: |
+ p[0] = [] |
+ else: |
+ p[0] = p[1] |
+ |
+ def p_attribute_list_1(self, p): |
+ 'attribute_list : attribute' |
+ p[0] = [p[1]] |
+ |
+ def p_attribute_list_2(self, p): |
+ "attribute_list : attribute_list ',' attribute" |
+ p[0] = p[1] + [p[3]] |
+ |
+ def p_attribute(self, p): |
+ 'attribute : attrid attribute_value' |
+ p[0] = (p[1], p[2]) |
+ |
+ def p_attribute_value_1(self, p): |
+ 'attribute_value : empty' |
+ p[0] = None |
+ |
+ def p_attribute_value_2(self, p): |
+ "attribute_value : '=' attrid" |
+ p[0] = p[2] |
+ |
+ def p_attrid_1(self, p): |
+ """attrid : ID |
+ | QUALIFIER""" |
+ p[0] = p[1] |
+ |
+ def p_attrid_2(self, p): |
+ 'attrid : STRING_OPEN text STRING_CLOSE' |
+ p[0] = p[2] |
+ |
+ def p_documentation_value_1(self, p): |
+ 'documentation_value : DOCUMENTATION_OPEN text DOCUMENTATION_CLOSE' |
+ p[0] = [('__docs', p[2])] |
+ |
+ def p_documentation_value_2(self, p): |
+ 'documentation_value : DOCUMENTATION_OPEN DOCUMENTATION_CLOSE' |
+ p[0] = [('__docs', '')] |
+ |
+ def p_definition_list_1(self, p): |
+ 'definition_list : comments' |
+ p[0] = [] |
+ |
+ def p_definition_list_2(self, p): |
+ 'definition_list : definition_list definition comments' |
+ p[0] = p[1] + [p[2]] |
+ |
+ def p_definition(self, p): |
+ """definition : function_definition |
+ | variable_definition |
+ | typename_definition |
+ | typedef_definition |
+ | class_definition |
+ | enum_definition |
+ | callback_definition |
+ | namespace_definition |
+ | verbatim_block""" |
+ p[0] = p[1] |
+ |
+ def p_member_definition_list_1(self, p): |
+ 'member_definition_list : comments' |
+ p[0] = [] |
+ |
+ def p_member_definition_list_2(self, p): |
+ 'member_definition_list : member_definition_list member_definition comments' |
+ p[0] = p[1] + [p[2]] |
+ |
+ def p_member_definition(self, p): |
+ """member_definition : function_definition |
+ | constructor_definition |
+ | variable_definition |
+ | typename_definition |
+ | typedef_definition |
+ | class_definition |
+ | enum_definition |
+ | callback_definition |
+ | verbatim_block""" |
+ p[0] = p[1] |
+ |
+ def p_comment_opt(self, p): |
+ """comments : empty |
+ | comments comment""" |
+ pass |
+ |
+ def p_comment(self, p): |
+ 'comment : COMMENT_OPEN text COMMENT_CLOSE' |
+ p[0] = p[2] |
+ |
+ def p_verbatim_block(self, p): |
+ 'verbatim_block : attributes_opt VERBATIM_OPEN text VERBATIM_CLOSE' |
+ p[0] = syntax_tree.Verbatim(self._GetLocation(), attributes=p[1], text=p[3]) |
+ |
+ def p_text(self, p): |
+ 'text : text_list' |
+ p[0] = ''.join(p[1]) |
+ |
+ def p_text_list(self, p): |
+ """text_list : empty |
+ | text_list TEXT""" |
+ if len(p) == 3: |
+ p[0] = p[1] + [p[2]] |
+ else: |
+ p[0] = [] |
+ |
+ def p_typedef_definition(self, p): |
+ "typedef_definition : attributes_opt TYPEDEF type ID ';'" |
+ p[0] = syntax_tree.Typedef(self._GetLocation(), attributes=p[1], |
+ type_ref=p[3], name=p[4]) |
+ |
+ def p_typename_definition(self, p): |
+ "typename_definition : attributes_opt TYPENAME ID ';'" |
+ p[0] = syntax_tree.Typename(self._GetLocation(), attributes=p[1], name=p[3]) |
+ |
+ def p_variable_definition(self, p): |
+ "variable_definition : attributes_opt type ID ';'" |
+ p[0] = syntax_tree.Variable(self._GetLocation(), attributes=p[1], |
+ type_ref=p[2], name=p[3]) |
+ |
+ def p_function_definition(self, p): |
+ "function_definition : attributes_opt type ID '(' param_list_opt ')' ';'" |
+ p[0] = syntax_tree.Function(self._GetLocation(), attributes=p[1], |
+ type_ref=p[2], name=p[3], params=p[5]) |
+ |
+ def p_callback_definition(self, p): |
+ "callback_definition : attributes_opt CALLBACK type ID '(' param_list_opt ')' ';'" |
+ p[0] = syntax_tree.Callback(self._GetLocation(), attributes=p[1], |
+ type_ref=p[3], name=p[4], params=p[6]) |
+ |
+ def p_constructor_definition(self, p): |
+ "constructor_definition : attributes_opt ID '(' param_list_opt ')' ';'" |
+ p[0] = syntax_tree.Function(self._GetLocation(), attributes=p[1], |
+ type_ref=None, name=p[2], params=p[4]) |
+ |
+ def p_param_list_opt_1(self, p): |
+ """param_list_opt : empty""" |
+ p[0] = [] |
+ |
+ def p_param_list_opt_2(self, p): |
+ """param_list_opt : param_list""" |
+ p[0] = p[1] |
+ |
+ def p_param_list_1(self, p): |
+ """param_list : param |
+ | param_list ',' param""" |
+ if len(p) == 2: |
+ p[0] = [p[1]] |
+ else: |
+ p[0] = p[1] + [p[3]] |
+ |
+ def p_param(self, p): |
+ 'param : type ID' |
+ p[0] = (p[1], p[2]) |
+ |
+ def p_enum_definition(self, p): |
+ "enum_definition : attributes_opt ENUM ID '{' enum_values '}' ';'" |
+ p[0] = syntax_tree.Enum(self._GetLocation(), attributes=p[1], name=p[3], |
+ values=p[5]) |
+ |
+ def p_enum_values(self, p): |
+ """enum_values : enum_value |
+ | enum_values ',' enum_value""" |
+ if len(p) == 4: |
+ p[0] = p[1] + [p[3]] |
+ else: |
+ p[0] = [p[1]] |
+ |
+ def p_enum_value(self, p): |
+ """enum_value : ID |
+ | ID '=' NUMBER""" |
+ if len(p) == 4: |
+ value = p[3] |
+ else: |
+ value = None |
+ p[0] = syntax_tree.Enum.Value(p[1], value) |
+ |
+ def p_namespace_definition(self, p): |
+ "namespace_definition : attributes_opt NAMESPACE ID '{' definition_list '}'" |
+ p[0] = syntax_tree.Namespace(self._GetLocation(), attributes=p[1], |
+ name=p[3], defn_list=p[5]) |
+ |
+ def p_type(self, p): |
+ """type : type_reference |
+ | QUALIFIER type""" |
+ if len(p) == 3: |
+ p[0] = syntax_tree.QualifiedTypeReference(self._GetLocation(), p[1], p[2]) |
+ else: |
+ p[0] = p[1] |
+ |
+ def p_type_reference(self, p): |
+ """ type_reference : type_name |
+ | scoped_type_reference |
+ | unsized_array_type_reference |
+ | sized_array_type_reference |
+ | nullable_type_reference""" |
+ p[0] = p[1] |
+ |
+ def p_type_name(self, p): |
+ """type_name : ID |
+ | SIGNED ID""" |
+ if len(p) == 3: |
+ p[0] = syntax_tree.NameTypeReference(self._GetLocation(), '%s %s' % |
+ (p[1], p[2])) |
+ else: |
+ p[0] = syntax_tree.NameTypeReference(self._GetLocation(), p[1]) |
+ |
+ def p_scoped_type_reference(self, p): |
+ "scoped_type_reference : ID ':' ':' type_reference" |
+ p[0] = syntax_tree.ScopedTypeReference(self._GetLocation(), p[1], p[4]) |
+ |
+ def p_unsized_array_type_reference(self, p): |
+ "unsized_array_type_reference : type_reference '[' ']'" |
+ p[0] = syntax_tree.ArrayTypeReference(self._GetLocation(), p[1], None) |
+ |
+ def p_sized_array_type_reference(self, p): |
+ "sized_array_type_reference : type_reference '[' NUMBER ']'" |
+ p[0] = syntax_tree.ArrayTypeReference(self._GetLocation(), p[1], p[3]) |
+ |
+ def p_nullable_type_reference(self, p): |
+ "nullable_type_reference : type_reference '?'" |
+ p[0] = syntax_tree.QualifiedTypeReference(self._GetLocation(), |
+ 'nullable', p[1]) |
+ |
+ def p_empty(self, p): |
+ 'empty : ' |
+ pass |
+ |
+ def p_error(self, p): |
+ location = self._GetLocation() |
+ if p is None: |
+ print ('%s:%d: Syntax error - unexpected end of file' % |
+ (location.file.source, location.line)) |
+ else: |
+ print ('%s:%d: Syntax error - unexpected token %s(%s)' % |
+ (location.file.source, location.line, p.type, p.value)) |
+ |
+ def Parse(self, idl_file): |
+ """Parses an IDL file. |
+ |
+ Args: |
+ idl_file: the file to parse, as a File object. |
+ |
+ Returns: |
+ A list of syntax_tree.Definition objects which represent all the |
+ top-level definitions in the IDL file. These definitions are not |
+ 'finalized', some post-processing has to be executed (see |
+ syntax_tree.FinalizeObjects). |
+ """ |
+ self._lexer = lex.lex(module=self) |
+ # Add the output dir to the system path so that yacc finds the generated |
+ # parsetab in there. |
+ sys.path.insert(0, self.output_dir) |
+ self._parser = yacc.yacc(module=self, outputdir=self.output_dir) |
+ del sys.path[0] |
+ self.file = idl_file |
+ input_data = open(idl_file.source).read() |
+ return self._parser.parse(input=input_data, lexer=self._lexer) |
+ |
+ def _GetLocation(self): |
+ return SourceLocation(self.file, self._lexer.lineno) |
+ |
+ |
+def main(filename): |
+ parser = Parser('.') |
+ print parser.Parse(File(filename)) |
+ |
+ |
+if __name__ == '__main__': |
+ if len(sys.argv) != 2: |
+ print 'usage : idl_parser.py inputfile' |
+ raise SystemExit |
+ main(sys.argv[1]) |
Property changes on: tools/nixysa/nixysa/idl_parser.py |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |