2
Serializes a Cython code tree to Cython code. This is primarily useful for
3
debugging and testing purposes.
4
The output is in a strict format, no whitespace or comments from the input
5
is preserved (and it could not be as it is not present in the code tree).
9
from .Compiler.Visitor import TreeVisitor
10
from .Compiler.ExprNodes import *
11
from .Compiler.Nodes import CSimpleBaseTypeNode
23
self.lines.append(self.s)
31
class DeclarationWriter(TreeVisitor):
33
A Cython code writer that is limited to declarations nodes.
38
def __init__(self, result=None):
41
result = LinesResult()
45
self.tempblockindex = 0
47
def write(self, tree):
57
def startline(self, s=""):
58
self.result.put(self.indent_string * self.numindents + s)
64
self.result.putline(self.indent_string * self.numindents + s)
66
def endline(self, s=""):
67
self.result.putline(s)
73
def comma_separated_list(self, items, output_rhs=False):
75
for item in items[:-1]:
77
if output_rhs and item.default is not None:
79
self.visit(item.default)
82
if output_rhs and items[-1].default is not None:
84
self.visit(items[-1].default)
86
def _visit_indented(self, node):
91
def visit_Node(self, node):
92
raise AssertionError("Node not handled by serializer: %r" % node)
94
def visit_ModuleNode(self, node):
95
self.visitchildren(node)
97
def visit_StatListNode(self, node):
98
self.visitchildren(node)
100
def visit_CDefExternNode(self, node):
101
if node.include_file is None:
104
file = '"%s"' % node.include_file
105
self.putline("cdef extern from %s:" % file)
106
self._visit_indented(node.body)
108
def visit_CPtrDeclaratorNode(self, node):
110
self.visit(node.base)
112
def visit_CReferenceDeclaratorNode(self, node):
114
self.visit(node.base)
116
def visit_CArrayDeclaratorNode(self, node):
117
self.visit(node.base)
119
if node.dimension is not None:
120
self.visit(node.dimension)
123
def visit_CFuncDeclaratorNode(self, node):
124
# TODO: except, gil, etc.
125
self.visit(node.base)
127
self.comma_separated_list(node.args)
130
def visit_CNameDeclaratorNode(self, node):
133
def visit_CSimpleBaseTypeNode(self, node):
134
# See Parsing.p_sign_and_longness
135
if node.is_basic_c_type:
136
self.put(("unsigned ", "", "signed ")[node.signed])
137
if node.longness < 0:
138
self.put("short " * -node.longness)
139
elif node.longness > 0:
140
self.put("long " * node.longness)
141
if node.name is not None:
144
def visit_CComplexBaseTypeNode(self, node):
145
self.visit(node.base_type)
146
self.visit(node.declarator)
148
def visit_CNestedBaseTypeNode(self, node):
149
self.visit(node.base_type)
153
def visit_TemplatedTypeNode(self, node):
154
self.visit(node.base_type_node)
156
self.comma_separated_list(node.positional_args + node.keyword_args.key_value_pairs)
159
def visit_CVarDefNode(self, node):
160
self.startline("cdef ")
161
self.visit(node.base_type)
163
self.comma_separated_list(node.declarators, output_rhs=True)
166
def _visit_container_node(self, node, decl, extras, attributes):
172
if node.cname is not None:
173
self.put(' "%s"' % node.cname)
181
for attribute in attributes:
182
self.visit(attribute)
185
def visit_CStructOrUnionDefNode(self, node):
186
if node.typedef_flag:
190
if node.visibility == 'public':
195
self._visit_container_node(node, decl, None, node.attributes)
197
def visit_CppClassNode(self, node):
200
extras = "[%s]" % ", ".join(node.templates)
201
if node.base_classes:
202
extras += "(%s)" % ", ".join(node.base_classes)
203
self._visit_container_node(node, "cdef cppclass", extras, node.attributes)
205
def visit_CEnumDefNode(self, node):
206
self._visit_container_node(node, "cdef enum", None, node.items)
208
def visit_CEnumDefItemNode(self, node):
209
self.startline(node.name)
211
self.put(' "%s"' % node.cname)
214
self.visit(node.value)
217
def visit_CClassDefNode(self, node):
218
assert not node.module_name
220
for decorator in node.decorators:
221
self.visit(decorator)
222
self.startline("cdef class ")
223
self.put(node.class_name)
224
if node.base_class_name:
226
if node.base_class_module:
227
self.put(node.base_class_module)
229
self.put(node.base_class_name)
232
self._visit_indented(node.body)
234
def visit_CTypeDefNode(self, node):
235
self.startline("ctypedef ")
236
self.visit(node.base_type)
238
self.visit(node.declarator)
241
def visit_FuncDefNode(self, node):
242
# TODO: support cdef + cpdef functions
243
self.startline("def %s(" % node.name)
244
self.comma_separated_list(node.args)
246
self._visit_indented(node.body)
248
def visit_CFuncDefNode(self, node):
249
self.startline('cpdef ' if node.overridable else 'cdef ')
251
self.put(' '.join(node.modifiers))
253
if node.visibility != 'private':
254
self.put(node.visibility)
260
self.visit(node.base_type)
261
if node.base_type.name is not None:
264
# visit the CFuncDeclaratorNode, but put a `:` at the end of line
265
self.visit(node.declarator.base)
267
self.comma_separated_list(node.declarator.args)
270
self._visit_indented(node.body)
272
def visit_CArgDeclNode(self, node):
273
# For "CSimpleBaseTypeNode", the variable type may have been parsed as type.
274
# For other node types, the "name" is always None.
275
if not isinstance(node.base_type, CSimpleBaseTypeNode) or \
276
node.base_type.name is not None:
277
self.visit(node.base_type)
279
# If we printed something for "node.base_type", we may need to print an extra ' '.
281
# Special case: if "node.declarator" is a "CNameDeclaratorNode",
282
# its "name" might be an empty string, for example, for "cdef f(x)".
283
if node.declarator.declared_name():
285
self.visit(node.declarator)
286
if node.default is not None:
288
self.visit(node.default)
290
def visit_CImportStatNode(self, node):
291
self.startline("cimport ")
292
self.put(node.module_name)
295
self.put(node.as_name)
298
def visit_FromCImportStatNode(self, node):
299
self.startline("from ")
300
self.put(node.module_name)
301
self.put(" cimport ")
303
for pos, name, as_name, kind in node.imported_names:
315
def visit_NameNode(self, node):
318
def visit_DecoratorNode(self, node):
320
self.visit(node.decorator)
323
def visit_PassStatNode(self, node):
324
self.startline("pass")
328
class StatementWriter(DeclarationWriter):
330
A Cython code writer for most language statement features.
333
def visit_SingleAssignmentNode(self, node):
340
def visit_CascadedAssignmentNode(self, node):
342
for lhs in node.lhs_list:
348
def visit_PrintStatNode(self, node):
349
self.startline("print ")
350
self.comma_separated_list(node.arg_tuple.args)
351
if not node.append_newline:
355
def visit_ForInStatNode(self, node):
356
self.startline("for ")
357
if node.target.is_sequence_constructor:
358
self.comma_separated_list(node.target.args)
360
self.visit(node.target)
362
self.visit(node.iterator.sequence)
364
self._visit_indented(node.body)
365
if node.else_clause is not None:
367
self._visit_indented(node.else_clause)
369
def visit_IfStatNode(self, node):
370
# The IfClauseNode is handled directly without a separate match
372
self.startline("if ")
373
self.visit(node.if_clauses[0].condition)
375
self._visit_indented(node.if_clauses[0].body)
376
for clause in node.if_clauses[1:]:
377
self.startline("elif ")
378
self.visit(clause.condition)
380
self._visit_indented(clause.body)
381
if node.else_clause is not None:
383
self._visit_indented(node.else_clause)
385
def visit_WhileStatNode(self, node):
386
self.startline("while ")
387
self.visit(node.condition)
389
self._visit_indented(node.body)
390
if node.else_clause is not None:
392
self._visit_indented(node.else_clause)
394
def visit_ContinueStatNode(self, node):
395
self.line("continue")
397
def visit_BreakStatNode(self, node):
400
def visit_SequenceNode(self, node):
401
self.comma_separated_list(node.args) # Might need to discover whether we need () around tuples...hmm...
403
def visit_ExprStatNode(self, node):
405
self.visit(node.expr)
408
def visit_InPlaceAssignmentNode(self, node):
411
self.put(" %s= " % node.operator)
415
def visit_WithStatNode(self, node):
418
self.visit(node.manager)
419
if node.target is not None:
421
self.visit(node.target)
423
self._visit_indented(node.body)
425
def visit_TryFinallyStatNode(self, node):
427
self._visit_indented(node.body)
428
self.line("finally:")
429
self._visit_indented(node.finally_clause)
431
def visit_TryExceptStatNode(self, node):
433
self._visit_indented(node.body)
434
for x in node.except_clauses:
436
if node.else_clause is not None:
437
self.visit(node.else_clause)
439
def visit_ExceptClauseNode(self, node):
440
self.startline("except")
441
if node.pattern is not None:
443
self.visit(node.pattern)
444
if node.target is not None:
446
self.visit(node.target)
448
self._visit_indented(node.body)
450
def visit_ReturnStatNode(self, node):
451
self.startline("return")
452
if node.value is not None:
454
self.visit(node.value)
457
def visit_ReraiseStatNode(self, node):
460
def visit_ImportNode(self, node):
461
self.put("(import %s)" % node.module_name.value)
463
def visit_TempsBlockNode(self, node):
465
Temporaries are output like $1_1', where the first number is
466
an index of the TempsBlockNode and the second number is an index
467
of the temporary which that block allocates.
470
for handle in node.temps:
471
self.tempnames[handle] = "$%d_%d" % (self.tempblockindex, idx)
473
self.tempblockindex += 1
474
self.visit(node.body)
476
def visit_TempRefNode(self, node):
477
self.put(self.tempnames[node.handle])
480
class ExpressionWriter(TreeVisitor):
482
A Cython code writer that is intentionally limited to expressions.
485
def __init__(self, result=None):
490
self.precedence = [0]
492
def write(self, tree):
500
if self.result.endswith(s):
501
self.result = self.result[:-len(s)]
503
def comma_separated_list(self, items):
505
for item in items[:-1]:
508
self.visit(items[-1])
510
def visit_Node(self, node):
511
raise AssertionError("Node not handled by serializer: %r" % node)
513
# TODO: Remove redundancy below. Most constants serialise fine as just "repr(node.value)".
515
def visit_IntNode(self, node):
518
def visit_FloatNode(self, node):
521
def visit_NoneNode(self, node):
524
def visit_NameNode(self, node):
527
def visit_EllipsisNode(self, node):
530
def visit_BoolNode(self, node):
531
self.put(str(node.value))
533
def visit_ConstNode(self, node):
534
self.put(str(node.value))
536
def visit_ImagNode(self, node):
537
self.put(f"{node.value}j")
539
def visit_BytesNode(self, node):
540
self.put(repr(node.value))
542
def visit_UnicodeNode(self, node):
543
self.put(repr(node.value))
545
def emit_sequence(self, node, parens=("", "")):
546
open_paren, close_paren = parens
547
items = node.subexpr_nodes()
549
self.comma_separated_list(items)
550
self.put(close_paren)
552
def visit_ListNode(self, node):
553
self.emit_sequence(node, "[]")
555
def visit_TupleNode(self, node):
556
self.emit_sequence(node, "()")
558
def visit_SetNode(self, node):
559
if len(node.subexpr_nodes()) > 0:
560
self.emit_sequence(node, "{}")
564
def visit_DictNode(self, node):
565
self.emit_sequence(node, "{}")
567
def visit_DictItemNode(self, node):
570
self.visit(node.value)
574
'+': 11, '-': 11, '~': 11,
579
# unary: 'not': 3, '!': 3,
580
'in': 4, 'not_in': 4, 'is': 4, 'is_not': 4, '<': 4, '<=': 4, '>': 4, '>=': 4, '!=': 4, '==': 4,
586
'*': 10, '@': 10, '/': 10, '//': 10, '%': 10,
587
# unary: '+': 11, '-': 11, '~': 11
591
def operator_enter(self, new_prec):
592
old_prec = self.precedence[-1]
593
if old_prec > new_prec:
595
self.precedence.append(new_prec)
597
def operator_exit(self):
598
old_prec, new_prec = self.precedence[-2:]
599
if old_prec > new_prec:
601
self.precedence.pop()
603
def visit_NotNode(self, node):
605
prec = self.unop_precedence[op]
606
self.operator_enter(prec)
608
self.visit(node.operand)
611
def visit_UnopNode(self, node):
613
prec = self.unop_precedence[op]
614
self.operator_enter(prec)
615
self.put("%s" % node.operator)
616
self.visit(node.operand)
619
def visit_BinopNode(self, node):
621
prec = self.binop_precedence.get(op, 0)
622
self.operator_enter(prec)
623
self.visit(node.operand1)
624
self.put(" %s " % op.replace('_', ' '))
625
self.visit(node.operand2)
628
def visit_BoolBinopNode(self, node):
629
self.visit_BinopNode(node)
631
def visit_PrimaryCmpNode(self, node):
632
self.visit_BinopNode(node)
634
def visit_IndexNode(self, node):
635
self.visit(node.base)
637
if isinstance(node.index, TupleNode):
638
if node.index.subexpr_nodes():
639
self.emit_sequence(node.index)
643
self.visit(node.index)
646
def visit_SliceIndexNode(self, node):
647
self.visit(node.base)
650
self.visit(node.start)
653
self.visit(node.stop)
656
self.visit(node.slice)
659
def visit_SliceNode(self, node):
660
if not node.start.is_none:
661
self.visit(node.start)
663
if not node.stop.is_none:
664
self.visit(node.stop)
665
if not node.step.is_none:
667
self.visit(node.step)
669
def visit_CondExprNode(self, node):
670
self.visit(node.true_val)
672
self.visit(node.test)
674
self.visit(node.false_val)
676
def visit_AttributeNode(self, node):
678
self.put(".%s" % node.attribute)
680
def visit_SimpleCallNode(self, node):
681
self.visit(node.function)
683
self.comma_separated_list(node.args)
686
def emit_pos_args(self, node):
689
if isinstance(node, AddNode):
690
self.emit_pos_args(node.operand1)
691
self.emit_pos_args(node.operand2)
692
elif isinstance(node, TupleNode):
693
for expr in node.subexpr_nodes():
696
elif isinstance(node, AsTupleNode):
704
def emit_kwd_args(self, node):
707
if isinstance(node, MergedDictNode):
708
for expr in node.subexpr_nodes():
709
self.emit_kwd_args(expr)
710
elif isinstance(node, DictNode):
711
for expr in node.subexpr_nodes():
712
self.put("%s=" % expr.key.value)
713
self.visit(expr.value)
720
def visit_GeneralCallNode(self, node):
721
self.visit(node.function)
723
self.emit_pos_args(node.positional_args)
724
self.emit_kwd_args(node.keyword_args)
728
def emit_comprehension(self, body, target,
731
open_paren, close_paren = parens
740
self.visit(condition)
741
self.put(close_paren)
743
def visit_ComprehensionAppendNode(self, node):
744
self.visit(node.expr)
746
def visit_DictComprehensionAppendNode(self, node):
747
self.visit(node.key_expr)
749
self.visit(node.value_expr)
751
def visit_ComprehensionNode(self, node):
752
tpmap = {'list': "[]", 'dict': "{}", 'set': "{}"}
753
parens = tpmap[node.type.py_type_name()]
754
body = node.loop.body
755
target = node.loop.target
756
sequence = node.loop.iterator.sequence
758
if hasattr(body, 'if_clauses'):
759
# type(body) is Nodes.IfStatNode
760
condition = body.if_clauses[0].condition
761
body = body.if_clauses[0].body
762
self.emit_comprehension(body, target, sequence, condition, parens)
764
def visit_GeneratorExpressionNode(self, node):
765
body = node.loop.body
766
target = node.loop.target
767
sequence = node.loop.iterator.sequence
769
if hasattr(body, 'if_clauses'):
770
# type(body) is Nodes.IfStatNode
771
condition = body.if_clauses[0].condition
772
body = body.if_clauses[0].body.expr.arg
773
elif hasattr(body, 'expr'):
774
# type(body) is Nodes.ExprStatNode
776
self.emit_comprehension(body, target, sequence, condition, "()")
779
class PxdWriter(DeclarationWriter, ExpressionWriter):
781
A Cython code writer for everything supported in pxd files.
785
def __call__(self, node):
786
print('\n'.join(self.write(node).lines))
789
def visit_CFuncDefNode(self, node):
791
self.startline('cpdef ')
793
self.startline('cdef ')
795
self.put(' '.join(node.modifiers))
797
if node.visibility != 'private':
798
self.put(node.visibility)
802
self.visit(node.declarator)
804
def visit_StatNode(self, node):
808
class CodeWriter(StatementWriter, ExpressionWriter):
810
A complete Cython code writer.