extended test case: coroutine.throw() while handling an exception already
[cython.git] / Cython / CodeWriter.py
index 2d1928569cf8fa6271a159742e5e07c5a242a7de..d38e388f506c8274dcbf94180a47978aaf6306b1 100644 (file)
@@ -1,4 +1,4 @@
-from Cython.Compiler.Visitor import TreeVisitor, get_temp_name_handle_desc
+from Cython.Compiler.Visitor import TreeVisitor
 from Cython.Compiler.Nodes import *
 from Cython.Compiler.ExprNodes import *
 
@@ -14,45 +14,50 @@ class LinesResult(object):
     def __init__(self):
         self.lines = []
         self.s = u""
-        
+
     def put(self, s):
         self.s += s
-    
+
     def newline(self):
         self.lines.append(self.s)
         self.s = u""
-    
+
     def putline(self, s):
         self.put(s)
         self.newline()
 
-class CodeWriter(TreeVisitor):
+class DeclarationWriter(TreeVisitor):
 
     indent_string = u"    "
-    
+
     def __init__(self, result = None):
-        super(CodeWriter, self).__init__()
+        super(DeclarationWriter, self).__init__()
         if result is None:
             result = LinesResult()
         self.result = result
         self.numindents = 0
         self.tempnames = {}
-    
+        self.tempblockindex = 0
+
     def write(self, tree):
         self.visit(tree)
-    
+        return self.result
+
     def indent(self):
         self.numindents += 1
-    
+
     def dedent(self):
         self.numindents -= 1
-    
+
     def startline(self, s = u""):
         self.result.put(self.indent_string * self.numindents + s)
-    
+
     def put(self, s):
         self.result.put(s)
-    
+
+    def putline(self, s):
+        self.result.putline(self.indent_string * self.numindents + s)
+
     def endline(self, s = u""):
         self.result.putline(s)
 
@@ -60,39 +65,186 @@ class CodeWriter(TreeVisitor):
         self.startline(s)
         self.endline()
 
-    def putname(self, name):
-        tmpdesc = get_temp_name_handle_desc(name)
-        if tmpdesc is not None:
-            name = self.tempnames.setdefault(tmpdesc, u"$" +tmpdesc)
-        self.put(name)
-    
-    def comma_seperated_list(self, items, output_rhs=False):
+    def comma_separated_list(self, items, output_rhs=False):
         if len(items) > 0:
             for item in items[:-1]:
                 self.visit(item)
-                if output_rhs and item.rhs is not None:
+                if output_rhs and item.default is not None:
                     self.put(u" = ")
-                    self.visit(item.rhs)
+                    self.visit(item.default)
                 self.put(u", ")
             self.visit(items[-1])
-    
+
     def visit_Node(self, node):
         raise AssertionError("Node not handled by serializer: %r" % node)
-    
+
     def visit_ModuleNode(self, node):
         self.visitchildren(node)
-    
+
     def visit_StatListNode(self, node):
         self.visitchildren(node)
+    
+    def visit_CDefExternNode(self, node):
+        if node.include_file is None:
+            file = u'*'
+        else:
+            file = u'"%s"' % node.include_file
+        self.putline(u"cdef extern from %s:" % file)
+        self.indent()
+        self.visit(node.body)
+        self.dedent()
+
+    def visit_CPtrDeclaratorNode(self, node):
+        self.put('*')
+        self.visit(node.base)
+
+    def visit_CReferenceDeclaratorNode(self, node):
+        self.put('&')
+        self.visit(node.base)
+
+    def visit_CArrayDeclaratorNode(self, node):
+        self.visit(node.base)
+        self.put(u'[')
+        if node.dimension is not None:
+            self.visit(node.dimension)
+        self.put(u']')
+
+    def visit_CArrayDeclaratorNode(self, node):
+        self.visit(node.base)
+        self.put(u'[')
+        if node.dimension is not None:
+            self.visit(node.dimension)
+        self.put(u']')
+
+    def visit_CFuncDeclaratorNode(self, node):
+        # TODO: except, gil, etc.
+        self.visit(node.base)
+        self.put(u'(')
+        self.comma_separated_list(node.args)
+        self.endline(u')')
+
+    def visit_CNameDeclaratorNode(self, node):
+        self.put(node.name)
+
+    def visit_CSimpleBaseTypeNode(self, node):
+        # See Parsing.p_sign_and_longness
+        if node.is_basic_c_type:
+            self.put(("unsigned ", "", "signed ")[node.signed])
+            if node.longness < 0:
+                self.put("short " * -node.longness)
+            elif node.longness > 0:
+                self.put("long " * node.longness)
+        self.put(node.name)
+
+    def visit_CComplexBaseTypeNode(self, node):
+        self.put(u'(')
+        self.visit(node.base_type)
+        self.visit(node.declarator)
+        self.put(u')')
+
+    def visit_CNestedBaseTypeNode(self, node):
+        self.visit(node.base_type)
+        self.put(u'.')
+        self.put(node.name)
+
+    def visit_TemplatedTypeNode(self, node):
+        self.visit(node.base_type_node)
+        self.put(u'[')
+        self.comma_separated_list(node.positional_args + node.keyword_args.key_value_pairs)
+        self.put(u']')
+
+    def visit_CVarDefNode(self, node):
+        self.startline(u"cdef ")
+        self.visit(node.base_type)
+        self.put(u" ")
+        self.comma_separated_list(node.declarators, output_rhs=True)
+        self.endline()
+
+    def visit_container_node(self, node, decl, extras, attributes):
+        # TODO: visibility
+        self.startline(decl)
+        if node.name:
+            self.put(u' ')
+            self.put(node.name)
+            if node.cname is not None:
+                self.put(u' "%s"' % node.cname)
+        if extras:
+            self.put(extras)
+        self.endline(':')
+        self.indent()
+        if not attributes:
+            self.putline('pass')
+        else:
+            for attribute in attributes:
+                self.visit(attribute)
+        self.dedent()
+
+    def visit_CStructOrUnionDefNode(self, node):
+        if node.typedef_flag:
+            decl = u'ctypedef '
+        else:
+            decl = u'cdef '
+        if node.visibility == 'public':
+            decl += u'public '
+        if node.packed:
+            decl += u'packed '
+        decl += node.kind
+        self.visit_container_node(node, decl, None, node.attributes)
+
+    def visit_CppClassNode(self, node):
+        extras = ""
+        if node.templates:
+            extras = u"[%s]" % ", ".join(node.templates)
+        if node.base_classes:
+            extras += "(%s)" % ", ".join(node.base_classes)
+        self.visit_container_node(node, u"cdef cppclass", extras, node.attributes)
+
+    def visit_CEnumDefNode(self, node):
+        self.visit_container_node(node, u"cdef enum", None, node.items)
+
+    def visit_CEnumDefItemNode(self, node):
+        self.startline(node.name)
+        if node.cname:
+            self.put(u' "%s"' % node.cname)
+        if node.value:
+            self.put(u" = ")
+            self.visit(node.value)
+        self.endline()
+
+    def visit_CClassDefNode(self, node):
+        assert not node.module_name
+        if node.decorators:
+            for decorator in node.decorators:
+                self.visit(decorator)
+        self.startline(u"cdef class ")
+        self.put(node.class_name)
+        if node.base_class_name:
+            self.put(u"(")
+            if node.base_class_module:
+                self.put(node.base_class_module)
+                self.put(u".")
+            self.put(node.base_class_name)
+            self.put(u")")
+        self.endline(u":")
+        self.indent()
+        self.visit(node.body)
+        self.dedent()
+
+    def visit_CTypeDefNode(self, node):
+        self.startline(u"ctypedef ")
+        self.visit(node.base_type)
+        self.put(u" ")
+        self.visit(node.declarator)
+        self.endline()
 
     def visit_FuncDefNode(self, node):
         self.startline(u"def %s(" % node.name)
-        self.comma_seperated_list(node.args)
+        self.comma_separated_list(node.args)
         self.endline(u"):")
         self.indent()
         self.visit(node.body)
         self.dedent()
-    
+
     def visit_CArgDeclNode(self, node):
         if node.base_type.name is not None:
             self.visit(node.base_type)
@@ -101,79 +253,98 @@ class CodeWriter(TreeVisitor):
         if node.default is not None:
             self.put(u" = ")
             self.visit(node.default)
-    
-    def visit_CNameDeclaratorNode(self, node):
-        self.put(node.name)
-    
-    def visit_CSimpleBaseTypeNode(self, node):
-        # See Parsing.p_sign_and_longness
-        if node.is_basic_c_type:
-            self.put(("unsigned ", "", "signed ")[node.signed])
-            if node.longness < 0:
-                self.put("short " * -node.longness)
-            elif node.longness > 0:
-                self.put("long " * node.longness)
-            
+
+    def visit_CImportStatNode(self, node):
+        self.startline(u"cimport ")
+        self.put(node.module_name)
+        if node.as_name:
+            self.put(u" as ")
+            self.put(node.as_name)
+        self.endline()
+
+    def visit_FromCImportStatNode(self, node):
+        self.startline(u"from ")
+        self.put(node.module_name)
+        self.put(u" cimport ")
+        first = True
+        for pos, name, as_name, kind in node.imported_names:
+            assert kind is None
+            if first:
+                first = False
+            else:
+                self.put(u", ")
+            self.put(name)
+            if as_name:
+                self.put(u" as ")
+                self.put(as_name)
+        self.endline()
+
+    def visit_NameNode(self, node):
         self.put(node.name)
-    
+
+    def visit_IntNode(self, node):
+        self.put(node.value)
+
+    def visit_NoneNode(self, node):
+        self.put(u"None")
+
+    def visit_NotNode(self, node):
+        self.put(u"(not ")
+        self.visit(node.operand)
+        self.put(u")")
+
+    def visit_DecoratorNode(self, node):
+        self.startline("@")
+        self.visit(node.decorator)
+        self.endline()
+
+    def visit_BinopNode(self, node):
+        self.visit(node.operand1)
+        self.put(u" %s " % node.operator)
+        self.visit(node.operand2)
+
+    def visit_AttributeNode(self, node):
+        self.visit(node.obj)
+        self.put(u".%s" % node.attribute)
+
+    def visit_BoolNode(self, node):
+        self.put(str(node.value))
+
+    # FIXME: represent string nodes correctly
+    def visit_StringNode(self, node):
+        value = node.value
+        if value.encoding is not None:
+            value = value.encode(value.encoding)
+        self.put(repr(value))
+
+    def visit_PassStatNode(self, node):
+        self.startline(u"pass")
+        self.endline()
+
+class CodeWriter(DeclarationWriter):
+
     def visit_SingleAssignmentNode(self, node):
         self.startline()
         self.visit(node.lhs)
         self.put(u" = ")
         self.visit(node.rhs)
         self.endline()
-    
-    def visit_NameNode(self, node):
-        self.putname(node.name)
-    
-    def visit_IntNode(self, node):
-        self.put(node.value)
-        
-    def visit_IfStatNode(self, node):
-        # The IfClauseNode is handled directly without a seperate match
-        # for clariy.
-        self.startline(u"if ")
-        self.visit(node.if_clauses[0].condition)
-        self.endline(":")
-        self.indent()
-        self.visit(node.if_clauses[0].body)
-        self.dedent()
-        for clause in node.if_clauses[1:]:
-            self.startline("elif ")
-            self.visit(clause.condition)
-            self.endline(":")
-            self.indent()
-            self.visit(clause.body)
-            self.dedent()
-        if node.else_clause is not None:
-            self.line("else:")
-            self.indent()
-            self.visit(node.else_clause)
-            self.dedent()
 
-    def visit_PassStatNode(self, node):
-        self.startline(u"pass")
+    def visit_CascadedAssignmentNode(self, node):
+        self.startline()
+        for lhs in node.lhs_list:
+            self.visit(lhs)
+            self.put(u" = ")
+        self.visit(node.rhs)
         self.endline()
-    
+
     def visit_PrintStatNode(self, node):
         self.startline(u"print ")
-        self.comma_seperated_list(node.arg_tuple.args)
+        self.comma_separated_list(node.arg_tuple.args)
         if not node.append_newline:
             self.put(u",")
         self.endline()
 
-    def visit_BinopNode(self, node):
-        self.visit(node.operand1)
-        self.put(u" %s " % node.operator)
-        self.visit(node.operand2)
-    
-    def visit_CVarDefNode(self, node):
-        self.startline(u"cdef ")
-        self.visit(node.base_type)
-        self.put(u" ")
-        self.comma_seperated_list(node.declarators, output_rhs=True)
-        self.endline()
-
     def visit_ForInStatNode(self, node):
         self.startline(u"for ")
         self.visit(node.target)
@@ -189,13 +360,35 @@ class CodeWriter(TreeVisitor):
             self.visit(node.else_clause)
             self.dedent()
 
+    def visit_IfStatNode(self, node):
+        # The IfClauseNode is handled directly without a seperate match
+        # for clariy.
+        self.startline(u"if ")
+        self.visit(node.if_clauses[0].condition)
+        self.endline(":")
+        self.indent()
+        self.visit(node.if_clauses[0].body)
+        self.dedent()
+        for clause in node.if_clauses[1:]:
+            self.startline("elif ")
+            self.visit(clause.condition)
+            self.endline(":")
+            self.indent()
+            self.visit(clause.body)
+            self.dedent()
+        if node.else_clause is not None:
+            self.line("else:")
+            self.indent()
+            self.visit(node.else_clause)
+            self.dedent()
+
     def visit_SequenceNode(self, node):
-        self.comma_seperated_list(node.args) # Might need to discover whether we need () around tuples...hmm...
-    
+        self.comma_separated_list(node.args) # Might need to discover whether we need () around tuples...hmm...
+
     def visit_SimpleCallNode(self, node):
         self.visit(node.function)
         self.put(u"(")
-        self.comma_seperated_list(node.args)
+        self.comma_separated_list(node.args)
         self.put(")")
 
     def visit_GeneralCallNode(self, node):
@@ -205,7 +398,7 @@ class CodeWriter(TreeVisitor):
         if isinstance(posarg, AsTupleNode):
             self.visit(posarg.arg)
         else:
-            self.comma_seperated_list(posarg)
+            self.comma_separated_list(posarg)
         if node.keyword_args is not None or node.starstar_arg is not None:
             raise Exception("Not implemented yet")
         self.put(u")")
@@ -214,14 +407,14 @@ class CodeWriter(TreeVisitor):
         self.startline()
         self.visit(node.expr)
         self.endline()
-    
+
     def visit_InPlaceAssignmentNode(self, node):
         self.startline()
         self.visit(node.lhs)
         self.put(u" %s= " % node.operator)
         self.visit(node.rhs)
         self.endline()
-        
+
     def visit_WithStatNode(self, node):
         self.startline()
         self.put(u"with ")
@@ -233,13 +426,6 @@ class CodeWriter(TreeVisitor):
         self.indent()
         self.visit(node.body)
         self.dedent()
-        
-    def visit_AttributeNode(self, node):
-        self.visit(node.obj)
-        self.put(u".%s" % node.attribute)
-
-    def visit_BoolNode(self, node):
-        self.put(str(node.value))
 
     def visit_TryFinallyStatNode(self, node):
         self.line(u"try:")
@@ -279,22 +465,49 @@ class CodeWriter(TreeVisitor):
         self.visit(node.value)
         self.endline()
 
-    def visit_DecoratorNode(self, node):
-        self.startline("@")
-        self.visit(node.decorator)
-        self.endline()
-
     def visit_ReraiseStatNode(self, node):
         self.line("raise")
 
-    def visit_NoneNode(self, node):
-        self.put(u"None")
-
     def visit_ImportNode(self, node):
         self.put(u"(import %s)" % node.module_name.value)
 
-    def visit_NotNode(self, node):
-        self.put(u"(not ")
-        self.visit(node.operand)
-        self.put(u")")
+    def visit_TempsBlockNode(self, node):
+        """
+        Temporaries are output like $1_1', where the first number is
+        an index of the TempsBlockNode and the second number is an index
+        of the temporary which that block allocates.
+        """
+        idx = 0
+        for handle in node.temps:
+            self.tempnames[handle] = "$%d_%d" % (self.tempblockindex, idx)
+            idx += 1
+        self.tempblockindex += 1
+        self.visit(node.body)
+
+    def visit_TempRefNode(self, node):
+        self.put(self.tempnames[node.handle])
+
+
+class PxdWriter(DeclarationWriter):
+    def __call__(self, node):
+        print u'\n'.join(self.write(node).lines)
+        return node
+
+    def visit_CFuncDefNode(self, node):
+        if 'inline' in node.modifiers:
+            return
+        if node.overridable:
+            self.startline(u'cpdef ')
+        else:
+            self.startline(u'cdef ')
+        if node.visibility != 'private':
+            self.put(node.visibility)
+            self.put(u' ')
+        if node.api:
+            self.put(u'api ')
+        self.visit(node.declarator)
+    
+    def visit_StatNode(self, node):
+        pass
 
+    
\ No newline at end of file