HTML source annotation module
authorRobert Bradshaw <robertwb@math.washington.edu>
Fri, 16 Nov 2007 21:58:58 +0000 (13:58 -0800)
committerRobert Bradshaw <robertwb@math.washington.edu>
Fri, 16 Nov 2007 21:58:58 +0000 (13:58 -0800)
Cython/Compiler/CmdLine.py
Cython/Compiler/Code.py
Cython/Compiler/ExprNodes.py
Cython/Compiler/ModuleNode.py
Cython/Compiler/Nodes.py
Cython/Compiler/Options.py
Cython/Compiler/Symtab.py

index 716e0f256e25ee4169d23cca91c95b3763da7432..bbebd36390a2e0b69ba2c0f40c9b573c9ff76eba 100644 (file)
@@ -25,6 +25,7 @@ Options:
   --incref-local-binop           Force local an extra incref on local variables before
                                  performing any binary operations.
   -D, --no-docstrings            Remove docstrings.
+  -a, --annotate                 Produce an colorized version of the source.
 """  
 #The following experimental options are supported only on MacOSX:
 #  -C, --compile    Compile generated .c file to .o file
@@ -85,6 +86,8 @@ def parse_command_line(args):
                 Options.generate_cleanup_code = int(pop_arg())
             elif option in ("-D", "--no-docstrings"):
                 Options.docstrings = False
+            elif option in ("-a", "--annotate"):
+                Options.annotate = True
             else:
                 bad_usage()
         else:
index 29457db197dbf845deffd0cbbdd12a7166471157..c333e1f631fa53868b0c8f7e87b3d8032ca87dfb 100644 (file)
@@ -96,6 +96,8 @@ class CCodeWriter:
         return "0x%02X%02X%02X%02X" % (tuple(pyversion) + (0,0,0,0))[:4]
 
     def mark_pos(self, pos):
+#        if self.marker is not None:
+#            print "new marker"
         file, line, col = pos
         contents = self.file_contents(file)
 
index 7f4dc1be72ccfd736d09af2138198ecb1ee19b7d..53629b9df939dbddaec20f621d1a9c00faa5c354 100644 (file)
@@ -12,6 +12,7 @@ import PyrexTypes
 from PyrexTypes import py_object_type, c_long_type, typecast, error_type
 import Symtab
 import Options
+from Annotate import AnnotationItem
 
 from Cython.Debugging import print_call_chain
 from DebugFlags import debug_disposal_code, debug_temp_alloc, \
@@ -404,6 +405,7 @@ class ExprNode(Node):
             code.put_incref(self.result_code, self.ctype())
     
     def generate_evaluation_code(self, code):
+        code.mark_pos(self.pos)
         #  Generate code to evaluate this node and
         #  its sub-expressions, and dispose of any
         #  temporary results of its sub-expressions.
@@ -456,6 +458,12 @@ class ExprNode(Node):
         #  will have been reported earlier.
         pass
     
+    # ---------------- Annotation ---------------------
+    
+    def annotate(self, code):
+        for node in self.subexpr_nodes():
+            node.annotate(code)
+    
     # ----------------- Coercion ----------------------
     
     def coerce_to(self, dst_type, env):
@@ -969,6 +977,14 @@ class NameNode(AtomicExprNode):
             'PyObject_DelAttrString(%s, "%s")' % (
                 Naming.module_cname,
                 self.entry.name))
+                
+    def annotate(self, code):
+        if hasattr(self, 'is_called') and self.is_called:
+            pos = (self.pos[0], self.pos[1], self.pos[2] - len(self.name) - 1)
+            if self.type.is_pyobject:
+                code.annotate(pos, AnnotationItem('py_call', 'python function', size=len(self.name)))
+            else:
+                code.annotate(pos, AnnotationItem('c_call', 'c function', size=len(self.name)))
             
 
 class BackquoteNode(ExprNode):
@@ -1970,6 +1986,12 @@ class AttributeNode(ExprNode):
         else:
             error(self.pos, "Cannot delete C attribute of extension type")
         self.obj.generate_disposal_code(code)
+        
+    def annotate(self, code):
+        if self.is_py_attr:
+            code.annotate(self.pos, AnnotationItem('py_attr', 'python attribute', size=len(self.attribute)))
+        else:
+            code.annotate(self.pos, AnnotationItem('c_attr', 'c attribute', size=len(self.attribute)))
 
 #-------------------------------------------------------------------
 #
@@ -2089,6 +2111,15 @@ class SequenceNode(ExprNode):
         self.iterator.generate_disposal_code(code)
 
         code.putln("}")
+        
+    def annotate(self, code):
+        for arg in self.args:
+            arg.annotate(code)
+        if self.unpacked_items:
+            for arg in self.unpacked_items:
+                arg.annotate(code)
+            for arg in self.coerced_unpacked_items:
+                arg.annotate(code)
 
 
 class TupleNode(SequenceNode):
@@ -2176,6 +2207,9 @@ class ListComprehensionNode(SequenceNode):
             0,
             code.error_goto_if_null(self.result_code, self.pos)))
         self.loop.generate_execution_code(code)
+        
+    def annotate(self, code):
+        self.loop.annotate(code)
 
 
 class ListComprehensionAppendNode(ExprNode):
@@ -2249,7 +2283,11 @@ class DictNode(ExprNode):
                     value.py_result()))
             key.generate_disposal_code(code)
             value.generate_disposal_code(code)
-    
+            
+    def annotate(self, code):
+        for key, value in self.key_value_pairs:
+            key.annotate(code)
+            value.annotate(code)
 
 class ClassNode(ExprNode):
     #  Helper class used in the implementation of Python
@@ -3321,6 +3359,12 @@ class PrimaryCmpNode(ExprNode, CmpNode):
         #  so only need to dispose of the two main operands.
         self.operand1.generate_disposal_code(code)
         self.operand2.generate_disposal_code(code)
+        
+    def annotate(self, code):
+        self.operand1.annotate(code)
+        self.operand2.annotate(code)
+        if self.cascade:
+            self.cascade.annotate(code)
 
 
 class CascadedCmpNode(Node, CmpNode):
@@ -3385,6 +3429,11 @@ class CascadedCmpNode(Node, CmpNode):
         self.operand2.generate_disposal_code(code)
         code.putln("}")
 
+    def annotate(self, code):
+        self.operand2.annotate(code)
+        if self.cascade:
+            self.cascade.annotate(code)
+
 
 binop_node_classes = {
     "or":              BoolBinopNode,
@@ -3434,6 +3483,12 @@ class CoercionNode(ExprNode):
         self.arg = arg
         if debug_coercion:
             print self, "Coercing", self.arg
+            
+    def annotate(self, code):
+        self.arg.annotate(code)
+        if self.arg.type != self.type:
+            file, line, col = self.pos
+            code.annotate((file, line, col-1), AnnotationItem(style='coerce', tag='coerce', text='[%s] to [%s]' % (self.arg.type, self.type)))
 
 
 class CastNode(CoercionNode):
index db95afc288cd767fdea7707217a99fe0f0b347fc..3fe0798918b1ea9223b6f204d9a3cd70e9193f4b 100644 (file)
@@ -6,6 +6,7 @@ import os, time
 from cStringIO import StringIO
 from PyrexTypes import CPtrType
 
+import Annotate
 import Code
 import Naming
 import Nodes
@@ -200,7 +201,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
     
     def generate_c_code(self, env, result):
         modules = self.referenced_modules
-        code = Code.CCodeWriter(StringIO())
+        if Options.annotate:
+            code = Annotate.AnnotationCCodeWriter(StringIO())
+        else:
+            code = Code.CCodeWriter(StringIO())
         code.h = Code.CCodeWriter(StringIO())
         code.init_labels()
         self.generate_module_preamble(env, modules, code.h)
@@ -233,6 +237,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
         f.write(code.f.getvalue())
         f.close()
         result.c_file_generated = 1
+        if Options.annotate:
+            self.annotate(code)
+            code.save_annotation(result.c_file[:-1] + "pyx") # change?
     
     def find_referenced_modules(self, env, module_list, modules_seen):
         if env not in modules_seen:
@@ -388,6 +395,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
         return header, footer
     
     def generate_struct_union_definition(self, entry, code):
+        code.mark_pos(entry.pos)
         type = entry.type
         scope = type.scope
         if scope:
@@ -407,6 +415,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
             code.putln(footer)
 
     def generate_enum_definition(self, entry, code):
+        code.mark_pos(entry.pos)
         type = entry.type
         name = entry.cname or entry.name or ""
         header, footer = \
@@ -450,6 +459,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
             #  code.putln("staticforward PyTypeObject %s;" % name)
     
     def generate_exttype_vtable_struct(self, entry, code):
+        code.mark_pos(entry.pos)
         # Generate struct declaration for an extension type's vtable.
         type = entry.type
         scope = type.scope
@@ -470,6 +480,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
                 "};")
     
     def generate_exttype_vtabptr_declaration(self, entry, code):
+        code.mark_pos(entry.pos)
         # Generate declaration of pointer to an extension type's vtable.
         type = entry.type
         if type.vtabptr_cname:
@@ -478,6 +489,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
                 type.vtabptr_cname))
     
     def generate_obj_struct_definition(self, type, code):
+        code.mark_pos(type.pos)
         # Generate object struct definition for an
         # extension type.
         if not type.scope:
index a254b657a6f0551d1349922e224cadc89a8b9906..2cf5c38f65fc43cc1022cd683d670f9b560b222e 100644 (file)
@@ -83,6 +83,13 @@ class Node:
     def generate_code(self, code):
         raise InternalError("generate_code not implemented for %s" % \
             self.__class__.__name__)
+            
+    def annotate(self, code):
+        # mro does the wrong thing
+        if isinstance(self, BlockNode):
+            self.body.annotate(code)
+        else:
+            print "skipping", self
 
 
 class BlockNode:
@@ -135,7 +142,7 @@ class BlockNode:
         for entry in entries:
             code.putln("static PyObject *%s;" % entry.cname)
         del entries[:]
-
+        
 
 class StatListNode(Node):
     # stats     a list of StatNode
@@ -160,6 +167,10 @@ class StatListNode(Node):
         for stat in self.stats:
             code.mark_pos(stat.pos)
             stat.generate_execution_code(code)
+            
+    def annotate(self, code):
+        for stat in self.stats:
+            stat.annotate(code)
     
 
 class StatNode(Node):
@@ -200,6 +211,9 @@ class CDefExternNode(StatNode):
     
     def generate_execution_code(self, code):
         pass
+
+    def annotate(self, code):
+        self.body.annotate(code)
         
 
 class CDeclaratorNode(Node):
@@ -302,6 +316,8 @@ class CFuncDeclaratorNode(CDeclaratorNode):
     # exception_check  boolean    True if PyErr_Occurred check needed
     # nogil            boolean    Can be called without gil
     # with_gil         boolean    Acquire gil around function body
+    
+    overridable = 0
 
     def analyse(self, return_type, env):
         func_type_args = []
@@ -373,6 +389,10 @@ class CArgDeclNode(Node):
         base_type = self.base_type.analyse(env)
         return self.declarator.analyse(base_type, env)
 
+    def annotate(self, code):
+        if self.default:
+            self.default.annotate(code)
+
 
 class CBaseTypeNode(Node):
     # Abstract base class for C base type nodes.
@@ -599,6 +619,7 @@ class FuncDefNode(StatNode, BlockNode):
         return 0
                 
     def generate_function_definitions(self, env, code):
+        code.mark_pos(self.pos)
         # Generate C code for header and body of function
         genv = env.global_scope()
         lenv = LocalScope(name = self.entry.name, outer_scope = genv)
@@ -1578,6 +1599,10 @@ class CClassDefNode(StatNode):
         # default values of method arguments.
         if self.body:
             self.body.generate_execution_code(code)
+            
+    def annotate(self, code):
+        if self.body:
+            self.body.annotate(code)
 
 
 class PropertyNode(StatNode):
@@ -1604,6 +1629,9 @@ class PropertyNode(StatNode):
     def generate_execution_code(self, code):
         pass
 
+    def annotate(self, code):
+        self.body.annotate(code)
+
 
 class GlobalNode(StatNode):
     # Global variable declaration.
@@ -1636,6 +1664,9 @@ class ExprStatNode(StatNode):
             code.putln("%s;" % self.expr.result_code)
         self.expr.generate_disposal_code(code)
 
+    def annotate(self, code):
+        self.expr.annotate(code)
+
 
 class AssignmentNode(StatNode):
     #  Abstract base class for assignment nodes.
@@ -1658,7 +1689,7 @@ class AssignmentNode(StatNode):
     def generate_execution_code(self, code):
         self.generate_rhs_evaluation_code(code)
         self.generate_assignment_code(code)
-
+        
 
 class SingleAssignmentNode(AssignmentNode):
     #  The simplest case:
@@ -1705,6 +1736,10 @@ class SingleAssignmentNode(AssignmentNode):
     def generate_assignment_code(self, code):
         self.lhs.generate_assignment_code(self.rhs, code)
 
+    def annotate(self, code):
+        self.lhs.annotate(code)
+        self.rhs.annotate(code)
+
 
 class CascadedAssignmentNode(AssignmentNode):
     #  An assignment with multiple left hand sides:
@@ -1781,6 +1816,13 @@ class CascadedAssignmentNode(AssignmentNode):
             # Assignment has disposed of the cloned RHS
         self.rhs.generate_disposal_code(code)
 
+    def annotate(self, code):
+        for i in range(len(self.lhs_list)):
+            lhs = self.lhs_list[i].annotate(code)
+            rhs = self.coerced_rhs_list[i].annotate(code)
+        self.rhs.annotate(code)
+        
+
 class ParallelAssignmentNode(AssignmentNode):
     #  A combined packing/unpacking assignment:
     #
@@ -1818,6 +1860,11 @@ class ParallelAssignmentNode(AssignmentNode):
         for stat in self.stats:
             stat.generate_assignment_code(code)
 
+    def annotate(self, code):
+        for stat in self.stats:
+            stat.annotate(code)
+
+
 class InPlaceAssignmentNode(AssignmentNode):
     #  An in place arithmatic operand:
     #
@@ -1922,6 +1969,11 @@ class InPlaceAssignmentNode(AssignmentNode):
         "%":           "PyNumber_InPlaceRemainder",
     }
 
+    def annotate(self, code):
+        self.lhs.annotate(code)
+        self.rhs.annotate(code)
+        self.dup.annotate(code)
+
 
 class PrintStatNode(StatNode):
     #  print statement
@@ -1952,6 +2004,10 @@ class PrintStatNode(StatNode):
             code.putln(
                 "if (__Pyx_PrintNewline() < 0) %s" %
                     code.error_goto(self.pos))
+                    
+    def annotate(self, code):
+        for arg in self.args:
+            arg.annotate(code)
 
 
 class DelStatNode(StatNode):
@@ -1976,6 +2032,10 @@ class DelStatNode(StatNode):
                 arg.generate_deletion_code(code)
             # else error reported earlier
 
+    def annotate(self, code):
+        for arg in self.args:
+            arg.annotate(code)
+
 
 class PassStatNode(StatNode):
     #  pass statement
@@ -2049,6 +2109,7 @@ class ReturnStatNode(StatNode):
                     error(self.pos, "Return value required")
     
     def generate_execution_code(self, code):
+        code.mark_pos(self.pos)
         if not self.return_type:
             # error reported earlier
             return
@@ -2074,6 +2135,10 @@ class ReturnStatNode(StatNode):
         #      "goto %s;" %
         #              code.return_label)
         code.put_goto(code.return_label)
+        
+    def annotate(self, code):
+        if self.value:
+            self.value.annotate(code)
 
 
 class RaiseStatNode(StatNode):
@@ -2141,6 +2206,14 @@ class RaiseStatNode(StatNode):
         code.putln(
             code.error_goto(self.pos))
 
+    def annotate(self, code):
+        if self.exc_type:
+            self.exc_type.annotate(code)
+        if self.exc_value:
+            self.exc_value.annotate(code)
+        if self.exc_tb:
+            self.exc_tb.annotate(code)
+
 
 class ReraiseStatNode(StatNode):
 
@@ -2195,6 +2268,12 @@ class AssertStatNode(StatNode):
         self.cond.generate_disposal_code(code)
         code.putln("#endif")
 
+    def annotate(self, code):
+        self.cond.annotate(code)
+        if self.value:
+            self.value.annotate(code)
+
+
 class IfStatNode(StatNode):
     #  if statement
     #
@@ -2214,6 +2293,7 @@ class IfStatNode(StatNode):
             self.else_clause.analyse_expressions(env)
     
     def generate_execution_code(self, code):
+        code.mark_pos(self.pos)
         end_label = code.new_label()
         for if_clause in self.if_clauses:
             if_clause.generate_execution_code(code, end_label)
@@ -2222,6 +2302,12 @@ class IfStatNode(StatNode):
             self.else_clause.generate_execution_code(code)
             code.putln("}")
         code.put_label(end_label)
+        
+    def annotate(self, code):
+        for if_clause in self.if_clauses:
+            if_clause.annotate(code)
+        if self.else_clause:
+            self.else_clause.annotate(code)
 
 
 class IfClauseNode(Node):
@@ -2252,6 +2338,10 @@ class IfClauseNode(Node):
         #              end_label)
         code.put_goto(end_label)
         code.putln("}")
+
+    def annotate(self, code):
+        self.condition.annotate(code)
+        self.body.annotate(code)
         
     
 class WhileStatNode(StatNode):
@@ -2294,6 +2384,12 @@ class WhileStatNode(StatNode):
             code.putln("}")
         code.put_label(break_label)
 
+    def annotate(self, code):
+        self.condition.annotate(code)
+        self.body.annotate(code)
+        if self.else_clause:
+            self.else_clause.annotate(code)
+
 
 def ForStatNode(pos, **kw):
     if kw.has_key('iterator'):
@@ -2351,6 +2447,14 @@ class ForInStatNode(StatNode):
         code.put_label(break_label)
         self.iterator.generate_disposal_code(code)
 
+    def annotate(self, code):
+        self.target.annotate(code)
+        self.iterator.annotate(code)
+        self.body.annotate(code)
+        if self.else_clause:
+            self.else_clause.annotate(code)
+        self.item.annotate(code)
+
 
 class ForFromStatNode(StatNode):
     #  for name from expr rel name rel expr
@@ -2468,6 +2572,16 @@ class ForFromStatNode(StatNode):
         '>=': ("",   "--"),
         '>' : ("-1", "--")
     }
+    
+    def annotate(self, code):
+        self.target.annotate(code)
+        self.bound1.annotate(code)
+        self.bound2.annotate(code)
+        if self.step:
+            self.bound2.annotate(code)
+        self.body.annotate(code)
+        if self.else_clause:
+            self.else_clause.annotate(code)
 
 
 class TryExceptStatNode(StatNode):
@@ -2524,6 +2638,13 @@ class TryExceptStatNode(StatNode):
             code.put_goto(code.error_label)
         code.put_label(end_label)
 
+    def annotate(self, code):
+        self.body.annotate(code)
+        for except_node in self.except_clauses:
+            except_node.annotate(code)
+        if self.else_clause:
+            self.else_clause.annotate(code)
+
 
 class ExceptClauseNode(Node):
     #  Part of try ... except statement.
@@ -2597,6 +2718,12 @@ class ExceptClauseNode(Node):
         code.putln(
             "}")
 
+    def annotate(self, code):
+        self.pattern.annotate(code)
+        if self.target:
+            self.target.annotate(code)
+        self.body.annotate(code)
+
 
 class TryFinallyStatNode(StatNode):
     #  try ... finally statement
@@ -2763,6 +2890,10 @@ class TryFinallyStatNode(StatNode):
         code.putln(
             "}")
 
+    def annotate(self, code):
+        self.body.annotate(code)
+        self.finally_clause.annotate(code)
+
 
 class GILStatNode(TryFinallyStatNode):
     #  'with gil' or 'with nogil' statement
index 04151a9330a48e154cb343bb2b5368ca902a08e4..533b87db1f8887c1cc10eff1010b6374fe33813a 100644 (file)
@@ -20,3 +20,5 @@ incref_local_binop = 0
 # Decref global variables in this module on exit for garbage collection. 
 # 0: None, 1+: interned objects, 2+: cdef globals, 3+: types objects
 generate_cleanup_code = 1
+
+annotate = 0
\ No newline at end of file
index 3a580778d8867243e619a4045449b3673aba9034..f680763a16c461972265967e86f8fd7dc5de3e2c 100644 (file)
@@ -847,6 +847,7 @@ class ModuleScope(Scope):
         #
         if not entry:
             type = PyExtensionType(name, typedef_flag, base_type)
+            type.pos = pos
             if visibility == 'extern':
                 type.module_name = module_name
             else: