Index: tools/nixysa/nixysa/header_generator.py |
=================================================================== |
--- tools/nixysa/nixysa/header_generator.py (revision 0) |
+++ tools/nixysa/nixysa/header_generator.py (revision 0) |
@@ -0,0 +1,552 @@ |
+#!/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. |
+ |
+"""C++ header file generator. |
+ |
+This module generates C++ header files from the parsed syntax tree. |
+""" |
+ |
+import cpp_utils |
+import naming |
+import syntax_tree |
+ |
+# TODO: have these exceptions derive from a common Error. |
+ |
+ |
+class CircularDefinition(Exception): |
+ """Raised when a circular type definition is found.""" |
+ |
+ def __init__(self, type_defn): |
+ Exception.__init__(self) |
+ self.type_defn = type_defn |
+ |
+ |
+class BadForwardDeclaration(Exception): |
+ """Raised when an impossible forward declaration is required.""" |
+ |
+ |
+def ForwardDecl(section, type_defn): |
+ """Emits the forward declaration of a type, if possible. |
+ |
+ Inner types (declared inside a class) cannot be forward-declared. |
+ Only classes can be forward-declared. |
+ |
+ Args: |
+ section: the section to emit to. |
+ type_defn: the Definition for the type to forward-declare. |
+ |
+ Raises: |
+ BadForwardDeclaration: an inner type or a non-class was passed as an |
+ argument. |
+ """ |
+ # inner types cannot be forward-declared |
+ if type_defn.parent.defn_type != 'Namespace': |
+ raise BadForwardDeclaration |
+ stack = type_defn.GetParentScopeStack() |
+ if type_defn.defn_type == 'Class': |
+ for scope in stack: |
+ if scope.name: |
+ section.PushNamespace(scope.name) |
+ section.EmitCode('class %s;' % type_defn.name) |
+ for scope in stack[::-1]: |
+ if scope.name: |
+ section.PopNamespace() |
+ else: |
+ raise BadForwardDeclaration |
+ |
+ |
+class HeaderGenerator(object): |
+ """Header generator class. |
+ |
+ This class takes care of the details of generating a C++ header file |
+ containing all the definitions from a syntax tree. |
+ |
+ It contains a list of functions named after each of the Definition classes in |
+ syntax_tree, with a common signature. The appropriate function will be called |
+ for each definition, to generate the code for that definition. |
+ """ |
+ |
+ def __init__(self, output_dir): |
+ self._output_dir = output_dir |
+ |
+ def GetSectionFromAttributes(self, parent_section, defn): |
+ """Gets the code section appropriate for a given definition. |
+ |
+ Classes have 3 definition sections: private, protected and public. This |
+ function will pick one of the sections, based on the attributes of the |
+ definition, if its parent is a class. For other scopes (namespaces) it will |
+ return the parent scope main section. |
+ |
+ Args: |
+ parent_section: the main section for the parent scope. |
+ defn: the definition. |
+ |
+ Returns: |
+ the appropriate section. |
+ """ |
+ if defn.parent and defn.parent.defn_type == 'Class': |
+ if 'private' in defn.attributes: |
+ return parent_section.GetSection('private:') or parent_section |
+ elif 'protected' in defn.attributes: |
+ return parent_section.GetSection('protected:') or parent_section |
+ else: |
+ return parent_section.GetSection('public:') or parent_section |
+ else: |
+ return parent_section |
+ |
+ def Verbatim(self, parent_section, scope, obj): |
+ """Generates the code for a Verbatim definition. |
+ |
+ Verbatim definitions being written for a particular type of output file, |
+ this function will check the 'verbatim' attribute, and only emit the |
+ verbatim code if it is 'cpp_header'. |
+ |
+ Args: |
+ parent_section: the main section of the parent scope. |
+ scope: the parent scope. |
+ obj: the Verbatim definition. |
+ |
+ Returns: |
+ a list of (boolean, Definition) pairs, of all the types that need |
+ to be declared (boolean is False) or defined (boolean is True) before |
+ this definition. |
+ """ |
+ scope = scope # silence gpylint. |
+ try: |
+ if obj.attributes['verbatim'] == 'cpp_header': |
+ section = self.GetSectionFromAttributes(parent_section, obj) |
+ section.EmitCode(obj.text) |
+ return [] |
+ else: |
+ return [] |
+ except KeyError: |
+ source = obj.source |
+ print ('%s:%d ignoring verbatim with no verbatim attribute' % |
+ (source.file.source, source.line)) |
+ return [] |
+ |
+ def Typedef(self, parent_section, scope, obj): |
+ """Generates the code for a Typedef definition. |
+ |
+ Args: |
+ parent_section: the main section of the parent scope. |
+ scope: the parent scope. |
+ obj: the Typedef definition. |
+ |
+ Returns: |
+ a list of (boolean, Definition) pairs, of all the types that need |
+ to be declared (boolean is False) or defined (boolean is True) before |
+ this definition. |
+ """ |
+ section = self.GetSectionFromAttributes(parent_section, obj) |
+ bm = obj.type_defn.binding_model |
+ type_string, unused_need_defn = bm.CppTypedefString(scope, obj.type_defn) |
+ check_types = [(True, obj.type_defn)] |
+ section.EmitCode('typedef %s %s;' % (type_string, obj.name)) |
+ return check_types |
+ |
+ def Variable(self, parent_section, scope, obj): |
+ """Generates the code for a Variable definition. |
+ |
+ This function will generate the member/global variable declaration, as well |
+ as the setter/getter functions if specified in the attributes. |
+ |
+ Args: |
+ parent_section: the main section of the parent scope. |
+ scope: the parent scope. |
+ obj: the Variable definition. |
+ |
+ Returns: |
+ a list of (boolean, Definition) pairs, of all the types that need |
+ to be declared (boolean is False) or defined (boolean is True) before |
+ this definition. |
+ """ |
+ if obj.parent.defn_type == 'Class': |
+ if 'field_access' in obj.attributes: |
+ member_section = parent_section.GetSection( |
+ obj.attributes['field_access'] + ':') |
+ else: |
+ member_section = parent_section.GetSection('private:') |
+ else: |
+ member_section = parent_section |
+ getter_section = self.GetSectionFromAttributes(parent_section, obj) |
+ |
+ bm = obj.type_defn.binding_model |
+ type_string, need_defn = bm.CppMemberString(scope, obj.type_defn) |
+ check_types = [(need_defn, obj.type_defn)] |
+ if 'static' in obj.attributes: |
+ static = 'static ' |
+ else: |
+ static = '' |
+ field_name = naming.Normalize(obj.name, naming.Java) |
+ self.Documentation(member_section, scope, obj) |
+ member_section.EmitCode('%s%s %s;' % (static, type_string, field_name)) |
+ if 'getter' in obj.attributes: |
+ return_type, need_defn = bm.CppReturnValueString(scope, obj.type_defn) |
+ check_types += [(need_defn, obj.type_defn)] |
+ getter_name = cpp_utils.GetGetterName(obj) |
+ self.FieldFunctionDocumentation(getter_section, 'Accessor', type_string, |
+ field_name) |
+ getter_section.EmitCode('%s%s %s() const { return %s; }' % |
+ (static, return_type, getter_name, field_name)) |
+ if 'setter' in obj.attributes: |
+ param_type, need_defn = bm.CppParameterString(scope, obj.type_defn) |
+ check_types += [(need_defn, obj.type_defn)] |
+ setter_name = cpp_utils.GetSetterName(obj) |
+ self.FieldFunctionDocumentation(getter_section, 'Mutator', type_string, |
+ field_name) |
+ getter_section.EmitCode('%svoid %s(%s %s) { %s = %s; }' % |
+ (static, setter_name, param_type, obj.name, |
+ field_name, obj.name)) |
+ return check_types |
+ |
+ def Function(self, parent_section, scope, obj): |
+ """Generates the code for a Function definition. |
+ |
+ Args: |
+ parent_section: the main section of the parent scope. |
+ scope: the parent scope. |
+ obj: the Function definition. |
+ |
+ Returns: |
+ a list of (boolean, Definition) pairs, of all the types that need |
+ to be declared (boolean is False) or defined (boolean is True) before |
+ this definition. |
+ """ |
+ section = self.GetSectionFromAttributes(parent_section, obj) |
+ self.Documentation(section, scope, obj) |
+ |
+ # create temporary function for generating Java syntax |
+ func_name = naming.Normalize(obj.name, naming.Java) |
+ function = syntax_tree.Function(obj.source, obj.attributes, func_name, |
+ None, []) |
+ function.type_defn = obj.type_defn |
+ function.parent = obj.parent |
+ function.params = obj.params |
+ prototype, check_types = cpp_utils.GetFunctionPrototype(scope, function, '') |
+ section.EmitCode(prototype + ';') |
+ return check_types |
+ |
+ def Callback(self, parent_section, scope, obj): |
+ """Generates the code for a Callback definition. |
+ |
+ Args: |
+ parent_section: the main section of the parent scope. |
+ scope: the parent scope. |
+ obj: the Class definition. |
+ |
+ Returns: |
+ a list of (boolean, Definition) pairs, of all the types that need |
+ to be declared (boolean is False) or defined (boolean is True) before |
+ this definition. |
+ """ |
+ parent_section, scope, obj = parent_section, scope, obj # silence gpylint |
+ # TODO: implement this. Do we want to generate the C++ callback object |
+ # (either through a CallbackN<A0, A1, ...> typedef, or explicitly), or |
+ # something else that could be more useful to generate JS docs ? |
+ return [] |
+ |
+ def Class(self, parent_section, scope, obj): |
+ """Generates the code for a Class definition. |
+ |
+ This function will recursively generate the code for all the definitions |
+ inside the class. These definitions will be output into one of 3 sections |
+ (private, protected, public), depending on their attributes. These |
+ individual sections will only be output if they are not empty. |
+ |
+ Args: |
+ parent_section: the main section of the parent scope. |
+ scope: the parent scope. |
+ obj: the Class definition. |
+ |
+ Returns: |
+ a list of (boolean, Definition) pairs, of all the types that need |
+ to be declared (boolean is False) or defined (boolean is True) before |
+ this definition. |
+ """ |
+ self.Documentation(parent_section, scope, obj) |
+ section = self.GetSectionFromAttributes(parent_section, obj).CreateSection( |
+ obj.name) |
+ check_types = [] |
+ if obj.base_type: |
+ bm = obj.base_type.binding_model |
+ section.EmitCode('class %s : public %s {' % |
+ (obj.name, bm.CppBaseClassString(scope, obj.base_type))) |
+ check_types += [(True, obj.base_type)] |
+ else: |
+ section.EmitCode('class %s {' % obj.name) |
+ public_section = section.CreateSection('public:') |
+ protected_section = section.CreateSection('protected:') |
+ private_section = section.CreateSection('private:') |
+ self.DefinitionList(section, obj, obj.defn_list) |
+ if not public_section.IsEmpty(): |
+ public_section.AddPrefix('public:') |
+ if not protected_section.IsEmpty(): |
+ protected_section.AddPrefix('protected:') |
+ if not private_section.IsEmpty(): |
+ private_section.AddPrefix('private:') |
+ section.EmitCode('};') |
+ return check_types |
+ |
+ def Namespace(self, parent_section, scope, obj): |
+ """Generates the code for a Namespace definition. |
+ |
+ This function will recursively generate the code for all the definitions |
+ inside the namespace. |
+ |
+ Args: |
+ parent_section: the main section of the parent scope. |
+ scope: the parent scope. |
+ obj: the Namespace definition. |
+ |
+ Returns: |
+ a list of (boolean, Definition) pairs, of all the types that need |
+ to be declared (boolean is False) or defined (boolean is True) before |
+ this definition. |
+ """ |
+ scope = scope # silence gpylint. |
+ self.Documentation(parent_section, scope, obj) |
+ parent_section.PushNamespace(obj.name) |
+ self.DefinitionList(parent_section, obj, obj.defn_list) |
+ parent_section.PopNamespace() |
+ return [] |
+ |
+ def Typename(self, parent_section, scope, obj): |
+ """Generates the code for a Typename definition. |
+ |
+ Since typenames (undefined types) cannot be expressed in C++, this function |
+ will not output code. The definition may be output with a verbatim section. |
+ |
+ Args: |
+ parent_section: the main section of the parent scope. |
+ scope: the parent scope. |
+ obj: the Typename definition. |
+ |
+ Returns: |
+ a list of (boolean, Definition) pairs, of all the types that need |
+ to be declared (boolean is False) or defined (boolean is True) before |
+ this definition. |
+ """ |
+ # silence gpylint. |
+ (parent_section, scope, obj) = (parent_section, scope, obj) |
+ return [] |
+ |
+ def Enum(self, parent_section, scope, obj): |
+ """Generates the code for an Enum definition. |
+ |
+ Args: |
+ parent_section: the main section of the parent scope. |
+ scope: the parent scope. |
+ obj: the Enum definition. |
+ |
+ Returns: |
+ a list of (boolean, Definition) pairs, of all the types that need |
+ to be declared (boolean is False) or defined (boolean is True) before |
+ this definition. |
+ """ |
+ scope = scope # silence gpylint. |
+ section = self.GetSectionFromAttributes(parent_section, obj) |
+ self.Documentation(parent_section, scope, obj) |
+ section.EmitCode('enum %s {' % obj.name) |
+ for value in obj.values: |
+ if value.value is None: |
+ section.EmitCode('%s,' % value.name) |
+ else: |
+ section.EmitCode('%s = %s,' % (value.name, value.value)) |
+ section.EmitCode('};') |
+ return [] |
+ |
+ def DefinitionList(self, parent_section, scope, defn_list): |
+ """Generates the code for all the definitions in a list. |
+ |
+ Args: |
+ parent_section: the main section of the parent scope. |
+ scope: the parent scope. |
+ defn_list: the list of definitions. |
+ """ |
+ for obj in defn_list: |
+ self.emitted_defn.add(obj) |
+ # array types are implicitly defined |
+ for k in obj.array_defns: |
+ self.emitted_defn.add(obj.array_defns[k]) |
+ func = getattr(self, obj.defn_type) |
+ check_types = func(parent_section, scope, obj) |
+ for need_defn, type_defn in check_types: |
+ self.CheckType(need_defn, type_defn) |
+ |
+ def Documentation(self, parent_section, scope, obj): |
+ """Generates the documentation code. |
+ |
+ Args: |
+ parent_section: the main section of the parent scope. |
+ scope: the parent scope. |
+ obj: the object to be documented; may be class, function, enum or field. |
+ """ |
+ for scoped_obj in scope.defn_list: |
+ # TODO: make documentation defn_type more generalized |
+ if scoped_obj.defn_type == 'Verbatim': |
+ if ('verbatim' in scoped_obj.attributes and |
+ scoped_obj.attributes['verbatim'] == 'docs'): |
+ try: |
+ # Should skip documentation if object does not have matching name |
+ # If id exists in documentation block, but not in object or does |
+ # not match object's id, skip this documentation block |
+ # If id does not exist as an attribute, look only for matching type |
+ found_documentation = False |
+ if scoped_obj.attributes['name'] == obj.name: |
+ if 'id' in scoped_obj.attributes: |
+ if ('id' in obj.attributes and |
+ scoped_obj.attributes['id'] == obj.attributes['id']): |
+ found_documentation = True |
+ elif scoped_obj.attributes['type'] == obj.defn_type: |
+ found_documentation = True |
+ if found_documentation: |
+ section = self.GetSectionFromAttributes(parent_section, |
+ scoped_obj) |
+ # Break up text and insert comment formatting |
+ comment_lines = scoped_obj.text.splitlines(False) |
+ section.EmitCode('/*! ') |
+ for line in comment_lines: |
+ section.EmitCode('* %s' % (line.strip())) |
+ section.EmitCode('*/') |
+ except KeyError: |
+ source = obj.source |
+ print ('%s:%d ignoring documentation with incorrect attributes' % |
+ (source.file.source, source.line)) |
+ |
+ def FieldFunctionDocumentation(self, member_section, description, |
+ type_string, field_name): |
+ """Automatically generate the get/set function documentation code. |
+ |
+ Args: |
+ member_section: the main section of the getter/setter scope. |
+ description: describes the field function. |
+ type_string: string defining field type. |
+ field_name: getter/setter field name. |
+ """ |
+ member_section.EmitCode('/*!') |
+ member_section.EmitCode('* %s for %s %s' % |
+ (description, type_string, field_name)) |
+ member_section.EmitCode('*/') |
+ |
+ def CheckType(self, need_defn, type_defn): |
+ """Checks for the definition or declaration of a type. |
+ |
+ This function helps keeping track of which types are needed to be defined |
+ or declared in the C++ file before other definitions can happen. If the |
+ definition is needed (and is not in this C++ header file), the proper |
+ include will be generated. If the type only needs to be forward-declared, |
+ the forward declaration will be output (if the type is not otherwise |
+ defined). |
+ |
+ Args: |
+ need_defn: a boolean, True if the C++ definition of the type is needed, |
+ False if only the declaration is needed. |
+ type_defn: the Definition of the type to check. |
+ """ |
+ while type_defn.defn_type == 'Array': |
+ # arrays are implicitly defined with their data type |
+ type_defn = type_defn.data_type |
+ if need_defn: |
+ if type_defn not in self.emitted_defn: |
+ self.needed_defn.add(type_defn) |
+ else: |
+ if type_defn in self.emitted_defn: |
+ return |
+ if type_defn.parent and type_defn.parent.defn_type != 'Namespace': |
+ # inner type: need the definition of the parent. |
+ self.CheckType(True, type_defn.parent) |
+ else: |
+ # only forward-declare classes. |
+ # typedefs could be forward-declared by emitting the definition again, |
+ # but this necessitates the source type to be forward-declared before. |
+ # TODO: see if it is possible to find a proper ordering that let us |
+ # forward-declare typedefs instead of needing to include the definition. |
+ if type_defn.defn_type == 'Class': |
+ self.needed_decl.add(type_defn) |
+ else: |
+ self.needed_defn.add(type_defn) |
+ |
+ def Generate(self, idl_file, namespace, defn_list): |
+ """Generates the header file. |
+ |
+ Args: |
+ idl_file: the source IDL file containing the definitions, as a |
+ idl_parser.File instance. |
+ namespace: a Definition for the global namespace. |
+ defn_list: the list of top-level definitions. |
+ |
+ Returns: |
+ a cpp_utils.CppFileWriter that contains the C++ header file code. |
+ |
+ Raises: |
+ CircularDefinition: circular definitions were found in the file. |
+ """ |
+ self.needed_decl = set() |
+ self.needed_defn = set() |
+ self.emitted_defn = set() |
+ writer = cpp_utils.CppFileWriter('%s/%s' % (self._output_dir, |
+ idl_file.header), True) |
+ |
+ decl_section = writer.CreateSection('decls') |
+ code_section = writer.CreateSection('defns') |
+ |
+ self.DefinitionList(code_section, namespace, defn_list) |
+ |
+ self.needed_decl -= self.needed_defn |
+ if self.needed_decl: |
+ for type_defn in self.needed_decl: |
+ # TODO: sort by namespace so that we don't open and close them more |
+ # than necessary. |
+ ForwardDecl(decl_section, type_defn) |
+ decl_section.EmitCode('') |
+ |
+ # TODO: disabling temporarily because of problems |
+ # for type_defn in self.needed_defn: |
+ # if type_defn.source.file == idl_file: |
+ # raise CircularDefinition(type_defn) |
+ includes = set(type_defn.GetDefinitionInclude() |
+ for type_defn in self.needed_defn) |
+ for include_file in includes: |
+ if include_file is not None: |
+ writer.AddInclude(include_file) |
+ return writer |
+ |
+ |
+def ProcessFiles(output_dir, pairs, namespace): |
+ """Generates the headers for all input files. |
+ |
+ Args: |
+ output_dir: the output directory. |
+ pairs: a list of (idl_parser.File, syntax_tree.Definition list) describing |
+ the list of top-level definitions in each source file. |
+ namespace: a syntax_tree.Namespace for the global namespace. |
+ |
+ Returns: |
+ a list of cpp_utils.CppFileWriter, one for each output header file. |
+ """ |
+ generator = HeaderGenerator(output_dir) |
+ writer_list = [] |
+ for (f, defn) in pairs: |
+ writer_list.append(generator.Generate(f, namespace, defn)) |
+ return writer_list |
+ |
+ |
+def main(): |
+ pass |
+ |
+if __name__ == '__main__': |
+ main() |
Property changes on: tools/nixysa/nixysa/header_generator.py |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |