From 3d32b841cc0e0a8c52316f81db3350c099cafc6a Mon Sep 17 00:00:00 2001 From: Robert Bradshaw Date: Fri, 16 Nov 2007 13:58:58 -0800 Subject: [PATCH] HTML source annotation module --- Cython/Compiler/CmdLine.py | 3 + Cython/Compiler/Code.py | 2 + Cython/Compiler/ExprNodes.py | 57 +++++++++++++- Cython/Compiler/ModuleNode.py | 14 +++- Cython/Compiler/Nodes.py | 135 +++++++++++++++++++++++++++++++++- Cython/Compiler/Options.py | 2 + Cython/Compiler/Symtab.py | 1 + 7 files changed, 210 insertions(+), 4 deletions(-) diff --git a/Cython/Compiler/CmdLine.py b/Cython/Compiler/CmdLine.py index 716e0f25..bbebd363 100644 --- a/Cython/Compiler/CmdLine.py +++ b/Cython/Compiler/CmdLine.py @@ -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: diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index 29457db1..c333e1f6 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -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) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 7f4dc1be..53629b9d 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -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): diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index db95afc2..3fe07989 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -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: diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index a254b657..2cf5c38f 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -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 diff --git a/Cython/Compiler/Options.py b/Cython/Compiler/Options.py index 04151a93..533b87db 100644 --- a/Cython/Compiler/Options.py +++ b/Cython/Compiler/Options.py @@ -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 diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index 3a580778..f680763a 100644 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@ -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: -- 2.26.2