Merge branch 'master' of https://github.com/gotgenes/cython into gotgenes-master
[cython.git] / Cython / CodeWriter.py
1 from Cython.Compiler.Visitor import TreeVisitor
2 from Cython.Compiler.Nodes import *
3 from Cython.Compiler.ExprNodes import *
4
5 """
6 Serializes a Cython code tree to Cython code. This is primarily useful for
7 debugging and testing purposes.
8
9 The output is in a strict format, no whitespace or comments from the input
10 is preserved (and it could not be as it is not present in the code tree).
11 """
12
13 class LinesResult(object):
14     def __init__(self):
15         self.lines = []
16         self.s = u""
17
18     def put(self, s):
19         self.s += s
20
21     def newline(self):
22         self.lines.append(self.s)
23         self.s = u""
24
25     def putline(self, s):
26         self.put(s)
27         self.newline()
28
29 class DeclarationWriter(TreeVisitor):
30
31     indent_string = u"    "
32
33     def __init__(self, result = None):
34         super(DeclarationWriter, self).__init__()
35         if result is None:
36             result = LinesResult()
37         self.result = result
38         self.numindents = 0
39         self.tempnames = {}
40         self.tempblockindex = 0
41
42     def write(self, tree):
43         self.visit(tree)
44         return self.result
45
46     def indent(self):
47         self.numindents += 1
48
49     def dedent(self):
50         self.numindents -= 1
51
52     def startline(self, s = u""):
53         self.result.put(self.indent_string * self.numindents + s)
54
55     def put(self, s):
56         self.result.put(s)
57
58     def putline(self, s):
59         self.result.putline(self.indent_string * self.numindents + s)
60
61     def endline(self, s = u""):
62         self.result.putline(s)
63
64     def line(self, s):
65         self.startline(s)
66         self.endline()
67
68     def comma_separated_list(self, items, output_rhs=False):
69         if len(items) > 0:
70             for item in items[:-1]:
71                 self.visit(item)
72                 if output_rhs and item.default is not None:
73                     self.put(u" = ")
74                     self.visit(item.default)
75                 self.put(u", ")
76             self.visit(items[-1])
77
78     def visit_Node(self, node):
79         raise AssertionError("Node not handled by serializer: %r" % node)
80
81     def visit_ModuleNode(self, node):
82         self.visitchildren(node)
83
84     def visit_StatListNode(self, node):
85         self.visitchildren(node)
86     
87     def visit_CDefExternNode(self, node):
88         if node.include_file is None:
89             file = u'*'
90         else:
91             file = u'"%s"' % node.include_file
92         self.putline(u"cdef extern from %s:" % file)
93         self.indent()
94         self.visit(node.body)
95         self.dedent()
96
97     def visit_CPtrDeclaratorNode(self, node):
98         self.put('*')
99         self.visit(node.base)
100
101     def visit_CReferenceDeclaratorNode(self, node):
102         self.put('&')
103         self.visit(node.base)
104
105     def visit_CArrayDeclaratorNode(self, node):
106         self.visit(node.base)
107         self.put(u'[')
108         if node.dimension is not None:
109             self.visit(node.dimension)
110         self.put(u']')
111
112     def visit_CArrayDeclaratorNode(self, node):
113         self.visit(node.base)
114         self.put(u'[')
115         if node.dimension is not None:
116             self.visit(node.dimension)
117         self.put(u']')
118
119     def visit_CFuncDeclaratorNode(self, node):
120         # TODO: except, gil, etc.
121         self.visit(node.base)
122         self.put(u'(')
123         self.comma_separated_list(node.args)
124         self.endline(u')')
125
126     def visit_CNameDeclaratorNode(self, node):
127         self.put(node.name)
128
129     def visit_CSimpleBaseTypeNode(self, node):
130         # See Parsing.p_sign_and_longness
131         if node.is_basic_c_type:
132             self.put(("unsigned ", "", "signed ")[node.signed])
133             if node.longness < 0:
134                 self.put("short " * -node.longness)
135             elif node.longness > 0:
136                 self.put("long " * node.longness)
137         self.put(node.name)
138
139     def visit_CComplexBaseTypeNode(self, node):
140         self.put(u'(')
141         self.visit(node.base_type)
142         self.visit(node.declarator)
143         self.put(u')')
144
145     def visit_CNestedBaseTypeNode(self, node):
146         self.visit(node.base_type)
147         self.put(u'.')
148         self.put(node.name)
149
150     def visit_TemplatedTypeNode(self, node):
151         self.visit(node.base_type_node)
152         self.put(u'[')
153         self.comma_separated_list(node.positional_args + node.keyword_args.key_value_pairs)
154         self.put(u']')
155
156     def visit_CVarDefNode(self, node):
157         self.startline(u"cdef ")
158         self.visit(node.base_type)
159         self.put(u" ")
160         self.comma_separated_list(node.declarators, output_rhs=True)
161         self.endline()
162
163     def visit_container_node(self, node, decl, extras, attributes):
164         # TODO: visibility
165         self.startline(decl)
166         if node.name:
167             self.put(u' ')
168             self.put(node.name)
169             if node.cname is not None:
170                 self.put(u' "%s"' % node.cname)
171         if extras:
172             self.put(extras)
173         self.endline(':')
174         self.indent()
175         if not attributes:
176             self.putline('pass')
177         else:
178             for attribute in attributes:
179                 self.visit(attribute)
180         self.dedent()
181
182     def visit_CStructOrUnionDefNode(self, node):
183         if node.typedef_flag:
184             decl = u'ctypedef '
185         else:
186             decl = u'cdef '
187         if node.visibility == 'public':
188             decl += u'public '
189         if node.packed:
190             decl += u'packed '
191         decl += node.kind
192         self.visit_container_node(node, decl, None, node.attributes)
193
194     def visit_CppClassNode(self, node):
195         extras = ""
196         if node.templates:
197             extras = u"[%s]" % ", ".join(node.templates)
198         if node.base_classes:
199             extras += "(%s)" % ", ".join(node.base_classes)
200         self.visit_container_node(node, u"cdef cppclass", extras, node.attributes)
201
202     def visit_CEnumDefNode(self, node):
203         self.visit_container_node(node, u"cdef enum", None, node.items)
204
205     def visit_CEnumDefItemNode(self, node):
206         self.startline(node.name)
207         if node.cname:
208             self.put(u' "%s"' % node.cname)
209         if node.value:
210             self.put(u" = ")
211             self.visit(node.value)
212         self.endline()
213
214     def visit_CClassDefNode(self, node):
215         assert not node.module_name
216         if node.decorators:
217             for decorator in node.decorators:
218                 self.visit(decorator)
219         self.startline(u"cdef class ")
220         self.put(node.class_name)
221         if node.base_class_name:
222             self.put(u"(")
223             if node.base_class_module:
224                 self.put(node.base_class_module)
225                 self.put(u".")
226             self.put(node.base_class_name)
227             self.put(u")")
228         self.endline(u":")
229         self.indent()
230         self.visit(node.body)
231         self.dedent()
232
233     def visit_CTypeDefNode(self, node):
234         self.startline(u"ctypedef ")
235         self.visit(node.base_type)
236         self.put(u" ")
237         self.visit(node.declarator)
238         self.endline()
239
240     def visit_FuncDefNode(self, node):
241         self.startline(u"def %s(" % node.name)
242         self.comma_separated_list(node.args)
243         self.endline(u"):")
244         self.indent()
245         self.visit(node.body)
246         self.dedent()
247
248     def visit_CArgDeclNode(self, node):
249         if node.base_type.name is not None:
250             self.visit(node.base_type)
251             self.put(u" ")
252         self.visit(node.declarator)
253         if node.default is not None:
254             self.put(u" = ")
255             self.visit(node.default)
256
257     def visit_CImportStatNode(self, node):
258         self.startline(u"cimport ")
259         self.put(node.module_name)
260         if node.as_name:
261             self.put(u" as ")
262             self.put(node.as_name)
263         self.endline()
264
265     def visit_FromCImportStatNode(self, node):
266         self.startline(u"from ")
267         self.put(node.module_name)
268         self.put(u" cimport ")
269         first = True
270         for pos, name, as_name, kind in node.imported_names:
271             assert kind is None
272             if first:
273                 first = False
274             else:
275                 self.put(u", ")
276             self.put(name)
277             if as_name:
278                 self.put(u" as ")
279                 self.put(as_name)
280         self.endline()
281
282     def visit_NameNode(self, node):
283         self.put(node.name)
284
285     def visit_IntNode(self, node):
286         self.put(node.value)
287
288     def visit_NoneNode(self, node):
289         self.put(u"None")
290
291     def visit_NotNode(self, node):
292         self.put(u"(not ")
293         self.visit(node.operand)
294         self.put(u")")
295
296     def visit_DecoratorNode(self, node):
297         self.startline("@")
298         self.visit(node.decorator)
299         self.endline()
300
301     def visit_BinopNode(self, node):
302         self.visit(node.operand1)
303         self.put(u" %s " % node.operator)
304         self.visit(node.operand2)
305
306     def visit_AttributeNode(self, node):
307         self.visit(node.obj)
308         self.put(u".%s" % node.attribute)
309
310     def visit_BoolNode(self, node):
311         self.put(str(node.value))
312
313     # FIXME: represent string nodes correctly
314     def visit_StringNode(self, node):
315         value = node.value
316         if value.encoding is not None:
317             value = value.encode(value.encoding)
318         self.put(repr(value))
319
320     def visit_PassStatNode(self, node):
321         self.startline(u"pass")
322         self.endline()
323
324 class CodeWriter(DeclarationWriter):
325
326     def visit_SingleAssignmentNode(self, node):
327         self.startline()
328         self.visit(node.lhs)
329         self.put(u" = ")
330         self.visit(node.rhs)
331         self.endline()
332
333     def visit_CascadedAssignmentNode(self, node):
334         self.startline()
335         for lhs in node.lhs_list:
336             self.visit(lhs)
337             self.put(u" = ")
338         self.visit(node.rhs)
339         self.endline()
340
341     def visit_PrintStatNode(self, node):
342         self.startline(u"print ")
343         self.comma_separated_list(node.arg_tuple.args)
344         if not node.append_newline:
345             self.put(u",")
346         self.endline()
347
348     def visit_ForInStatNode(self, node):
349         self.startline(u"for ")
350         self.visit(node.target)
351         self.put(u" in ")
352         self.visit(node.iterator.sequence)
353         self.endline(u":")
354         self.indent()
355         self.visit(node.body)
356         self.dedent()
357         if node.else_clause is not None:
358             self.line(u"else:")
359             self.indent()
360             self.visit(node.else_clause)
361             self.dedent()
362
363     def visit_IfStatNode(self, node):
364         # The IfClauseNode is handled directly without a seperate match
365         # for clariy.
366         self.startline(u"if ")
367         self.visit(node.if_clauses[0].condition)
368         self.endline(":")
369         self.indent()
370         self.visit(node.if_clauses[0].body)
371         self.dedent()
372         for clause in node.if_clauses[1:]:
373             self.startline("elif ")
374             self.visit(clause.condition)
375             self.endline(":")
376             self.indent()
377             self.visit(clause.body)
378             self.dedent()
379         if node.else_clause is not None:
380             self.line("else:")
381             self.indent()
382             self.visit(node.else_clause)
383             self.dedent()
384
385     def visit_SequenceNode(self, node):
386         self.comma_separated_list(node.args) # Might need to discover whether we need () around tuples...hmm...
387
388     def visit_SimpleCallNode(self, node):
389         self.visit(node.function)
390         self.put(u"(")
391         self.comma_separated_list(node.args)
392         self.put(")")
393
394     def visit_GeneralCallNode(self, node):
395         self.visit(node.function)
396         self.put(u"(")
397         posarg = node.positional_args
398         if isinstance(posarg, AsTupleNode):
399             self.visit(posarg.arg)
400         else:
401             self.comma_separated_list(posarg)
402         if node.keyword_args is not None or node.starstar_arg is not None:
403             raise Exception("Not implemented yet")
404         self.put(u")")
405
406     def visit_ExprStatNode(self, node):
407         self.startline()
408         self.visit(node.expr)
409         self.endline()
410
411     def visit_InPlaceAssignmentNode(self, node):
412         self.startline()
413         self.visit(node.lhs)
414         self.put(u" %s= " % node.operator)
415         self.visit(node.rhs)
416         self.endline()
417
418     def visit_WithStatNode(self, node):
419         self.startline()
420         self.put(u"with ")
421         self.visit(node.manager)
422         if node.target is not None:
423             self.put(u" as ")
424             self.visit(node.target)
425         self.endline(u":")
426         self.indent()
427         self.visit(node.body)
428         self.dedent()
429
430     def visit_TryFinallyStatNode(self, node):
431         self.line(u"try:")
432         self.indent()
433         self.visit(node.body)
434         self.dedent()
435         self.line(u"finally:")
436         self.indent()
437         self.visit(node.finally_clause)
438         self.dedent()
439
440     def visit_TryExceptStatNode(self, node):
441         self.line(u"try:")
442         self.indent()
443         self.visit(node.body)
444         self.dedent()
445         for x in node.except_clauses:
446             self.visit(x)
447         if node.else_clause is not None:
448             self.visit(node.else_clause)
449
450     def visit_ExceptClauseNode(self, node):
451         self.startline(u"except")
452         if node.pattern is not None:
453             self.put(u" ")
454             self.visit(node.pattern)
455         if node.target is not None:
456             self.put(u", ")
457             self.visit(node.target)
458         self.endline(":")
459         self.indent()
460         self.visit(node.body)
461         self.dedent()
462
463     def visit_ReturnStatNode(self, node):
464         self.startline("return ")
465         self.visit(node.value)
466         self.endline()
467
468     def visit_ReraiseStatNode(self, node):
469         self.line("raise")
470
471     def visit_ImportNode(self, node):
472         self.put(u"(import %s)" % node.module_name.value)
473
474     def visit_TempsBlockNode(self, node):
475         """
476         Temporaries are output like $1_1', where the first number is
477         an index of the TempsBlockNode and the second number is an index
478         of the temporary which that block allocates.
479         """
480         idx = 0
481         for handle in node.temps:
482             self.tempnames[handle] = "$%d_%d" % (self.tempblockindex, idx)
483             idx += 1
484         self.tempblockindex += 1
485         self.visit(node.body)
486
487     def visit_TempRefNode(self, node):
488         self.put(self.tempnames[node.handle])
489
490
491 class PxdWriter(DeclarationWriter):
492     def __call__(self, node):
493         print u'\n'.join(self.write(node).lines)
494         return node
495
496     def visit_CFuncDefNode(self, node):
497         if 'inline' in node.modifiers:
498             return
499         if node.overridable:
500             self.startline(u'cpdef ')
501         else:
502             self.startline(u'cdef ')
503         if node.visibility != 'private':
504             self.put(node.visibility)
505             self.put(u' ')
506         if node.api:
507             self.put(u'api ')
508         self.visit(node.declarator)
509     
510     def visit_StatNode(self, node):
511         pass
512
513