Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes CodeWriter for cdef functions aka. CFuncDefNode. #3514

Merged
merged 6 commits into from Jul 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
142 changes: 89 additions & 53 deletions 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).
"""
Expand All @@ -10,6 +9,7 @@

from .Compiler.Visitor import TreeVisitor
from .Compiler.ExprNodes import *
from .Compiler.Nodes import CNameDeclaratorNode, CSimpleBaseTypeNode


class LinesResult(object):
Expand Down Expand Up @@ -80,6 +80,14 @@ def comma_separated_list(self, items, output_rhs=False):
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)
Expand All @@ -96,9 +104,7 @@ def visit_CDefExternNode(self, node):
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('*')
Expand Down Expand Up @@ -133,13 +139,12 @@ def visit_CSimpleBaseTypeNode(self, node):
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):
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)
Expand All @@ -159,7 +164,7 @@ def visit_CVarDefNode(self, node):
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:
Expand Down Expand Up @@ -188,18 +193,18 @@ def visit_CStructOrUnionDefNode(self, node):
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 = ""
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)
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)
self._visit_container_node(node, u"cdef enum", None, node.items)

def visit_CEnumDefItemNode(self, node):
self.startline(node.name)
Expand All @@ -225,9 +230,7 @@ def visit_CClassDefNode(self, node):
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 ")
Expand All @@ -241,14 +244,45 @@ def visit_FuncDefNode(self, node):
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' ')
scoder marked this conversation as resolved.
Show resolved Hide resolved

# 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):
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)
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" = ")
Expand Down Expand Up @@ -328,36 +362,41 @@ def visit_ForInStatNode(self, node):
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:")
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
# 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()
self._visit_indented(node.if_clauses[0].body)
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:")
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...
Expand All @@ -382,25 +421,17 @@ def visit_WithStatNode(self, node):
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:")
self.indent()
self.visit(node.body)
self.dedent()
self._visit_indented(node.body)
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:")
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:
Expand All @@ -415,9 +446,7 @@ def visit_ExceptClauseNode(self, node):
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 ")
Expand Down Expand Up @@ -480,12 +509,18 @@ def comma_separated_list(self, items):
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")

def visit_NameNode(self, node):
self.put(node.name)

def visit_EllipsisNode(self, node):
self.put(u"...")

Expand Down Expand Up @@ -756,12 +791,13 @@ def __call__(self, node):
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.modifiers:
self.put(' '.join(node.modifiers))
self.put(' ')
if node.visibility != 'private':
self.put(node.visibility)
self.put(u' ')
Expand Down
23 changes: 23 additions & 0 deletions Cython/Tests/TestCodeWriter.py
Expand Up @@ -21,6 +21,7 @@ def test_print(self):
self.t(u"""
print(x + y ** 2)
print(x, y, z)
print(x + y, x + y * z, x * (y + z))
""")

def test_if(self):
Expand All @@ -46,6 +47,20 @@ def f(x = 34, y = 54, z):
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")

Expand Down Expand Up @@ -75,6 +90,14 @@ def test_for_loop(self):
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")

Expand Down