diff --git a/Cython/CodeWriter.py b/Cython/CodeWriter.py index a4328ed148c4e4a8073452d8c89cf43e8fd7bf65_Q3l0aG9uL0NvZGVXcml0ZXIucHk=..343d02173b32f444ecabcb19382b9315eef2c743_Q3l0aG9uL0NvZGVXcml0ZXIucHk= 100644 --- a/Cython/CodeWriter.py +++ b/Cython/CodeWriter.py @@ -1,7 +1,6 @@ """ Serializes a Cython code tree to Cython code. This is primarily useful for debugging and testing purposes. - The output is in a strict format, no whitespace or comments from the input is preserved (and it could not be as it is not present in the code tree). """ @@ -10,6 +9,7 @@ from .Compiler.Visitor import TreeVisitor from .Compiler.ExprNodes import * +from .Compiler.Nodes import CNameDeclaratorNode, CSimpleBaseTypeNode class LinesResult(object): @@ -80,6 +80,14 @@ self.visit(item.default) self.put(u", ") self.visit(items[-1]) + if output_rhs and items[-1].default is not None: + self.put(u" = ") + self.visit(items[-1].default) + + def _visit_indented(self, node): + self.indent() + self.visit(node) + self.dedent() def visit_Node(self, node): raise AssertionError("Node not handled by serializer: %r" % node) @@ -96,9 +104,7 @@ else: file = u'"%s"' % node.include_file self.putline(u"cdef extern from %s:" % file) - self.indent() - self.visit(node.body) - self.dedent() + self._visit_indented(node.body) def visit_CPtrDeclaratorNode(self, node): self.put('*') @@ -133,6 +139,7 @@ self.put("short " * -node.longness) elif node.longness > 0: self.put("long " * node.longness) - self.put(node.name) + if node.name is not None: + self.put(node.name) def visit_CComplexBaseTypeNode(self, node): @@ -137,5 +144,4 @@ def visit_CComplexBaseTypeNode(self, node): - self.put(u'(') self.visit(node.base_type) self.visit(node.declarator) @@ -140,6 +146,5 @@ self.visit(node.base_type) self.visit(node.declarator) - self.put(u')') def visit_CNestedBaseTypeNode(self, node): self.visit(node.base_type) @@ -159,7 +164,7 @@ self.comma_separated_list(node.declarators, output_rhs=True) self.endline() - def visit_container_node(self, node, decl, extras, attributes): + def _visit_container_node(self, node, decl, extras, attributes): # TODO: visibility self.startline(decl) if node.name: @@ -188,7 +193,7 @@ if node.packed: decl += u'packed ' decl += node.kind - self.visit_container_node(node, decl, None, node.attributes) + self._visit_container_node(node, decl, None, node.attributes) def visit_CppClassNode(self, node): extras = "" @@ -196,6 +201,6 @@ 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) + self._visit_container_node(node, u"cdef cppclass", extras, node.attributes) def visit_CEnumDefNode(self, node): @@ -200,6 +205,6 @@ def visit_CEnumDefNode(self, node): - self.visit_container_node(node, u"cdef enum", None, node.items) + self._visit_container_node(node, u"cdef enum", None, node.items) def visit_CEnumDefItemNode(self, node): self.startline(node.name) @@ -225,9 +230,7 @@ self.put(node.base_class_name) self.put(u")") self.endline(u":") - self.indent() - self.visit(node.body) - self.dedent() + self._visit_indented(node.body) def visit_CTypeDefNode(self, node): self.startline(u"ctypedef ") @@ -241,8 +244,30 @@ self.startline(u"def %s(" % node.name) self.comma_separated_list(node.args) self.endline(u"):") - self.indent() - self.visit(node.body) - self.dedent() + self._visit_indented(node.body) + + def visit_CFuncDefNode(self, node): + self.startline(u'cpdef ' if node.overridable else u'cdef ') + if node.modifiers: + self.put(' '.join(node.modifiers)) + self.put(' ') + if node.visibility != 'private': + self.put(node.visibility) + self.put(u' ') + if node.api: + self.put(u'api ') + + if node.base_type: + self.visit(node.base_type) + if node.base_type.name is not None: + self.put(u' ') + + # visit the CFuncDeclaratorNode, but put a `:` at the end of line + self.visit(node.declarator.base) + self.put(u'(') + self.comma_separated_list(node.declarator.args) + self.endline(u'):') + + self._visit_indented(node.body) def visit_CArgDeclNode(self, node): @@ -247,4 +272,7 @@ def visit_CArgDeclNode(self, node): - if node.base_type.name is not None: + # For "CSimpleBaseTypeNode", the variable type may have been parsed as type. + # For other node types, the "name" is always None. + if not isinstance(node.base_type, CSimpleBaseTypeNode) or \ + node.base_type.name is not None: self.visit(node.base_type) @@ -250,5 +278,11 @@ self.visit(node.base_type) - self.put(u" ") + + # If we printed something for "node.base_type", we may need to print an extra ' '. + # + # Special case: if "node.declarator" is a "CNameDeclaratorNode", + # its "name" might be an empty string, for example, for "cdef f(x)". + if node.declarator.declared_name(): + self.put(u" ") self.visit(node.declarator) if node.default is not None: self.put(u" = ") @@ -328,8 +362,6 @@ self.put(u" in ") self.visit(node.iterator.sequence) self.endline(u":") - self.indent() - self.visit(node.body) - self.dedent() + self._visit_indented(node.body) if node.else_clause is not None: self.line(u"else:") @@ -334,8 +366,6 @@ if node.else_clause is not None: self.line(u"else:") - self.indent() - self.visit(node.else_clause) - self.dedent() + self._visit_indented(node.else_clause) def visit_IfStatNode(self, node): # The IfClauseNode is handled directly without a separate match @@ -343,10 +373,8 @@ self.startline(u"if ") self.visit(node.if_clauses[0].condition) self.endline(":") - self.indent() - self.visit(node.if_clauses[0].body) - self.dedent() + self._visit_indented(node.if_clauses[0].body) for clause in node.if_clauses[1:]: self.startline("elif ") self.visit(clause.condition) self.endline(":") @@ -349,9 +377,7 @@ for clause in node.if_clauses[1:]: self.startline("elif ") self.visit(clause.condition) self.endline(":") - self.indent() - self.visit(clause.body) - self.dedent() + self._visit_indented(clause.body) if node.else_clause is not None: self.line("else:") @@ -356,8 +382,21 @@ if node.else_clause is not None: self.line("else:") - self.indent() - self.visit(node.else_clause) - self.dedent() + self._visit_indented(node.else_clause) + + def visit_WhileStatNode(self, node): + self.startline(u"while ") + self.visit(node.condition) + self.endline(u":") + self._visit_indented(node.body) + if node.else_clause is not None: + self.line("else:") + self._visit_indented(node.else_clause) + + def visit_ContinueStatNode(self, node): + self.line(u"continue") + + def visit_BreakStatNode(self, node): + self.line(u"break") def visit_SequenceNode(self, node): self.comma_separated_list(node.args) # Might need to discover whether we need () around tuples...hmm... @@ -382,9 +421,7 @@ self.put(u" as ") self.visit(node.target) self.endline(u":") - self.indent() - self.visit(node.body) - self.dedent() + self._visit_indented(node.body) def visit_TryFinallyStatNode(self, node): self.line(u"try:") @@ -388,7 +425,5 @@ def visit_TryFinallyStatNode(self, node): self.line(u"try:") - self.indent() - self.visit(node.body) - self.dedent() + self._visit_indented(node.body) self.line(u"finally:") @@ -394,7 +429,5 @@ self.line(u"finally:") - self.indent() - self.visit(node.finally_clause) - self.dedent() + self._visit_indented(node.finally_clause) def visit_TryExceptStatNode(self, node): self.line(u"try:") @@ -398,9 +431,7 @@ def visit_TryExceptStatNode(self, node): self.line(u"try:") - self.indent() - self.visit(node.body) - self.dedent() + self._visit_indented(node.body) for x in node.except_clauses: self.visit(x) if node.else_clause is not None: @@ -415,9 +446,7 @@ self.put(u", ") self.visit(node.target) self.endline(":") - self.indent() - self.visit(node.body) - self.dedent() + self._visit_indented(node.body) def visit_ReturnStatNode(self, node): self.startline("return ") @@ -480,9 +509,12 @@ def visit_Node(self, node): raise AssertionError("Node not handled by serializer: %r" % node) - def visit_NameNode(self, node): - self.put(node.name) + def visit_IntNode(self, node): + self.put(node.value) + + def visit_FloatNode(self, node): + self.put(node.value) def visit_NoneNode(self, node): self.put(u"None") @@ -485,7 +517,10 @@ def visit_NoneNode(self, node): self.put(u"None") + def visit_NameNode(self, node): + self.put(node.name) + def visit_EllipsisNode(self, node): self.put(u"...") @@ -756,9 +791,7 @@ 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 ') @@ -761,7 +794,10 @@ if node.overridable: self.startline(u'cpdef ') else: self.startline(u'cdef ') + if node.modifiers: + self.put(' '.join(node.modifiers)) + self.put(' ') if node.visibility != 'private': self.put(node.visibility) self.put(u' ') diff --git a/Cython/Tests/TestCodeWriter.py b/Cython/Tests/TestCodeWriter.py index a4328ed148c4e4a8073452d8c89cf43e8fd7bf65_Q3l0aG9uL1Rlc3RzL1Rlc3RDb2RlV3JpdGVyLnB5..343d02173b32f444ecabcb19382b9315eef2c743_Q3l0aG9uL1Rlc3RzL1Rlc3RDb2RlV3JpdGVyLnB5 100644 --- a/Cython/Tests/TestCodeWriter.py +++ b/Cython/Tests/TestCodeWriter.py @@ -21,6 +21,7 @@ self.t(u""" print(x + y ** 2) print(x, y, z) + print(x + y, x + y * z, x * (y + z)) """) def test_if(self): @@ -46,6 +47,20 @@ pass """) + def test_cdef(self): + self.t(u""" + cdef f(x, y, z): + pass + cdef public void (x = 34, y = 54, z): + pass + cdef f(int *x, void *y, Value *z): + pass + cdef f(int **x, void **y, Value **z): + pass + cdef inline f(int &x, Value &z): + pass + """) + def test_longness_and_signedness(self): self.t(u"def f(unsigned long long long long long int y):\n pass") @@ -75,6 +90,14 @@ print(43) """) + def test_while_loop(self): + self.t(u""" + while True: + while True: + while True: + continue + """) + def test_inplace_assignment(self): self.t(u"x += 43")