support enumerate()
[pythonc.git] / syntax.py
blob1c32979a83b3975e8ec09e305d8eed146821141f
1 ################################################################################
2 ##
3 ## Pythonc--Python to C++ translator
4 ##
5 ## Copyright 2011 Zach Wegner
6 ##
7 ## This file is part of Pythonc.
8 ##
9 ## Pythonc is free software: you can redistribute it and/or modify
10 ## it under the terms of the GNU General Public License as published by
11 ## the Free Software Foundation, either version 3 of the License, or
12 ## (at your option) any later version.
14 ## Pythonc is distributed in the hope that it will be useful,
15 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ## GNU General Public License for more details.
19 ## You should have received a copy of the GNU General Public License
20 ## along with Pythonc. If not, see <http://www.gnu.org/licenses/>.
22 ################################################################################
24 import copy
26 def block_str(stmts):
27 stmts = '\n'.join('%s;' % s for s in stmts).splitlines()
28 return '\n'.join(' %s' % s for s in stmts)
30 all_ints = set()
31 def register_int(value):
32 global all_ints
33 all_ints |= {value}
35 all_strings = {}
36 def register_string(value):
37 global all_strings
38 if value in all_strings:
39 return all_strings[value][0]
40 # Compute hash via FNV-1a algorithm. Python makes signed 64-bit arithmetic hard.
41 hashkey = 14695981039346656037
42 for c in value:
43 hashkey ^= ord(c)
44 hashkey *= 1099511628211
45 hashkey &= (1 << 64) - 1
46 all_strings[value] = (len(all_strings), hashkey)
47 return all_strings[value][0]
49 def int_name(i):
50 return 'int_singleton_neg%d' % -i if i < 0 else 'int_singleton_%d' % i
52 def export_consts(f):
53 global all_ints, all_strings
55 for i in all_ints:
56 f.write('int_const_singleton %s(%sll);\n' % (int_name(i), i))
58 for k, (v, hashkey) in all_strings.items():
59 f.write('string_const_singleton string_singleton_%s("%s", %sull);\n' % (v, repr(k)[1:-1], hashkey))
61 class Node:
62 def is_atom(self):
63 return False
65 class NoneConst(Node):
66 def __init__(self):
67 pass
69 def is_atom(self):
70 return True
72 def __str__(self):
73 return '(&none_singleton)'
75 class BoolConst(Node):
76 def __init__(self, value):
77 self.value = value
79 def __str__(self):
80 return '(&bool_singleton_%s)' % self.value
82 class IntConst(Node):
83 def __init__(self, value):
84 self.value = value
85 register_int(value)
87 def is_atom(self):
88 return True
90 def __str__(self):
91 return '(&%s)' % int_name(self.value)
93 class StringConst(Node):
94 def __init__(self, value):
95 self.value = value
96 self.id = register_string(value)
98 def is_atom(self):
99 return True
101 def __str__(self):
102 return '(&string_singleton_%s)' % self.id
104 class Identifier(Node):
105 def __init__(self, name):
106 self.name = name
108 def is_atom(self):
109 return True
111 def __str__(self):
112 return self.name
114 class Ref(Node):
115 def __init__(self, ref_type, *args):
116 self.ref_type = ref_type
117 self.args = args
119 def __str__(self):
120 return '(new(allocator) %s(%s))' % (self.ref_type, ', '.join(str(a) for a in self.args))
122 class UnaryOp(Node):
123 def __init__(self, op, rhs):
124 self.op = op
125 self.rhs = rhs
127 def __str__(self):
128 return '%s->%s()' % (self.rhs, self.op)
130 class BinaryOp(Node):
131 def __init__(self, op, lhs, rhs):
132 self.op = op
133 self.lhs = lhs
134 self.rhs = rhs
136 def __str__(self):
137 return '%s->%s(%s)' % (self.lhs, self.op, self.rhs)
139 class Load(Node):
140 def __init__(self, name, binding):
141 self.name = name
142 self.binding = binding
144 def __str__(self):
145 if self.binding == 'global':
146 return 'globals->load("%s")' % self.name
147 elif self.binding == 'class':
148 return 'class_ctx->load("%s")' % self.name
149 return 'ctx.load("%s")' % self.name
151 class Store(Node):
152 def __init__(self, name, expr, binding):
153 self.name = name
154 self.expr = expr
155 self.binding = binding
157 def __str__(self):
158 if self.binding == 'global':
159 return 'globals->store("%s", %s)' % (self.name, self.expr)
160 elif self.binding == 'class':
161 return 'class_ctx->store("%s", %s)' % (self.name, self.expr)
162 return 'ctx.store("%s", %s)' % (self.name, self.expr)
164 class StoreAttr(Node):
165 def __init__(self, name, attr, expr):
166 self.name = name
167 self.attr = attr
168 self.expr = expr
170 def __str__(self):
171 return '%s->__setattr__(%s, %s)' % (self.name, self.attr, self.expr)
173 class StoreSubscript(Node):
174 def __init__(self, expr, index, value):
175 self.expr = expr
176 self.index = index
177 self.value = value
179 def __str__(self):
180 return '%s->__setitem__(%s, %s)' % (self.expr, self.index, self.value)
182 class DeleteSubscript(Node):
183 def __init__(self, expr, index):
184 self.expr = expr
185 self.index = index
187 def __str__(self):
188 return '%s->__delitem__(%s)' % (self.expr, self.index)
190 class List(Node):
191 def __init__(self, items):
192 self.items = items
194 def flatten(self, ctx):
195 name = ctx.get_temp()
196 ctx.statements += [Assign(name, Ref('list'), target_type='list')]
197 for i in self.items:
198 # XXX HACK: add just some C++ text instead of syntax nodes...
199 ctx.statements += ['%s->append(%s)' % (name, i)]
200 return name
202 def __str__(self):
203 return ''
205 class Dict(Node):
206 def __init__(self, keys, values):
207 self.keys = keys
208 self.values = values
210 def flatten(self, ctx):
211 name = ctx.get_temp()
212 ctx.statements += [Assign(name, Ref('dict'), target_type='dict')]
213 for k, v in zip(self.keys, self.values):
214 # XXX HACK: add just some C++ text instead of syntax nodes...
215 ctx.statements += ['%s->__setitem__(%s, %s)' % (name, k, v)]
216 return name
218 def __str__(self):
219 return ''
221 class Set(Node):
222 def __init__(self, items):
223 self.items = items
225 def flatten(self, ctx):
226 name = ctx.get_temp()
227 ctx.statements += [Assign(name, Ref('set'), target_type='set')]
228 for i in self.items:
229 # XXX HACK: add just some C++ text instead of syntax nodes...
230 ctx.statements += ['%s->add(%s)' % (name, i)]
231 return name
233 def __str__(self):
234 return ''
236 class Slice(Node):
237 def __init__(self, expr, start, end, step):
238 self.expr = expr
239 self.start = start
240 self.end = end
241 self.step = step
243 def __str__(self):
244 return '%s->__slice__(%s, %s, %s)' % (self.expr, self.start, self.end, self.step)
246 class Subscript(Node):
247 def __init__(self, expr, index):
248 self.expr = expr
249 self.index = index
251 def __str__(self):
252 return '%s->__getitem__(%s)' % (self.expr, self.index)
254 class Attribute(Node):
255 def __init__(self, expr, attr):
256 self.expr = expr
257 self.attr = attr
259 def __str__(self):
260 return '%s->__getattr__(%s)' % (self.expr, self.attr)
262 class Call(Node):
263 def __init__(self, func, args, kwargs):
264 self.func = func
265 self.args = args
266 self.kwargs = kwargs
268 def __str__(self):
269 return '%s->__call__(globals, &ctx, %s, %s)' % (self.func, self.args, self.kwargs)
271 class IfExp(Node):
272 def __init__(self, expr, true_stmts, true_expr, false_stmts, false_expr):
273 self.expr = expr
274 self.true_stmts = true_stmts
275 self.true_expr = true_expr
276 self.false_stmts = false_stmts
277 self.false_expr = false_expr
279 def flatten(self, ctx):
280 self.temp = ctx.get_temp()
281 ctx.statements += [Assign(self.temp, 'NULL'), self]
282 return self.temp
284 def __str__(self):
285 true_stmts = block_str(self.true_stmts)
286 false_stmts = block_str(self.false_stmts)
287 body = """if ({expr}->bool_value()) {{
288 {true_stmts}
289 {temp} = {true_expr};
290 }} else {{
291 {false_stmts}
292 {temp} = {false_expr};
294 """.format(expr=self.expr, temp=self.temp.name, true_stmts=true_stmts,
295 true_expr=self.true_expr, false_stmts=false_stmts, false_expr=self.false_expr)
296 return body
298 class BoolOp(Node):
299 def __init__(self, op, lhs_expr, rhs_stmts, rhs_expr):
300 self.op = op
301 self.lhs_expr = lhs_expr
302 self.rhs_stmts = rhs_stmts
303 self.rhs_expr = rhs_expr
305 # XXX hack
306 def flatten(self, ctx, statements):
307 self.temp = ctx.get_temp()
308 statements += [Assign(self.temp, self.lhs_expr), self]
309 return self.temp
311 def __str__(self):
312 rhs_stmts = block_str(self.rhs_stmts)
313 body = """if ({op}{lhs_expr}->bool_value()) {{
314 {rhs_stmts}
315 {temp} = {rhs_expr};
317 """.format(op='!' if self.op == 'or' else '', lhs_expr=self.lhs_expr,
318 temp=self.temp.name, rhs_stmts=rhs_stmts, rhs_expr=self.rhs_expr)
319 return body
321 class Assign(Node):
322 def __init__(self, target, expr, target_type='node'):
323 self.target = target
324 self.expr = expr
325 self.target_type = target_type
327 def __str__(self):
328 if self.target_type is None:
329 return '%s = %s' % (self.target, self.expr)
330 else:
331 return '%s *%s = %s' % (self.target_type, self.target, self.expr)
333 class If(Node):
334 def __init__(self, expr, stmts, else_block):
335 self.expr = expr
336 self.stmts = stmts
337 self.else_block = else_block
339 def __str__(self):
340 stmts = block_str(self.stmts)
341 body = """if ({expr}->bool_value()) {{
342 {stmts}
344 """.format(expr=self.expr, stmts=stmts)
345 if self.else_block:
346 stmts = block_str(self.else_block)
347 body += """else {{
348 {stmts}
350 """.format(expr=self.expr, stmts=stmts)
351 return body
353 class ListComp(Node):
354 def __init__(self, target, iter, stmts, expr):
355 self.target = target
356 self.iter = iter
357 self.stmts = stmts
358 self.expr = expr
360 def flatten(self, ctx):
361 l = List([])
362 self.temp = l.flatten(ctx)
363 ctx.statements += [self]
364 # HACK: prevent iterable from being garbage collected
365 self.iter_store = Store(ctx.get_temp(), self.iter, 'local')
366 return self.temp
368 def __str__(self):
369 stmts = block_str(self.stmts)
370 arg_unpacking = []
371 if isinstance(self.target, list):
372 for i, arg in enumerate(self.target):
373 arg_unpacking += [Store(arg.id, '(*__iter)->__getitem__(%s)' % i, 'local')]
374 else:
375 arg_unpacking = [Store(self.target, '*__iter', 'local')]
376 arg_unpacking = block_str(arg_unpacking)
377 body = """
378 {iter_store};
379 for (node_list::iterator __iter = {iter}->list_value()->begin(); __iter != {iter}->list_value()->end(); __iter++) {{
380 {arg_unpacking}
381 {stmts}
382 {temp}->append({expr});
384 """.format(iter=self.iter, iter_store=self.iter_store, arg_unpacking=arg_unpacking,
385 stmts=stmts, temp=self.temp, expr=self.expr)
386 return body
388 class Break(Node):
389 def __init__(self):
390 pass
392 def is_atom(self):
393 return True
395 def __str__(self):
396 return 'break'
398 class Continue(Node):
399 def __init__(self):
400 pass
402 def is_atom(self):
403 return True
405 def __str__(self):
406 return 'continue'
408 class For(Node):
409 def __init__(self, target, iter, stmts):
410 self.target = target
411 self.iter = iter
412 self.stmts = stmts
414 def flatten(self, ctx):
415 # HACK: prevent iterable from being garbage collected
416 self.iter_store = Store(ctx.get_temp(), self.iter, 'local')
417 return self
419 def __str__(self):
420 stmts = block_str(self.stmts)
421 arg_unpacking = []
422 if isinstance(self.target, list):
423 for i, (arg, binding) in enumerate(self.target):
424 arg_unpacking += [Store(arg, '(*__iter)->__getitem__(%s)' % i, binding)]
425 else:
426 arg, binding = self.target
427 arg_unpacking = [Store(arg, '*__iter', binding)]
428 arg_unpacking = block_str(arg_unpacking)
429 # XXX sorta weird?
430 body = """
431 {iter_store};
432 for (node_list::iterator __iter = {iter}->list_value()->begin(); __iter != {iter}->list_value()->end(); __iter++) {{
433 {arg_unpacking}
434 {stmts}
435 collect_garbage(&ctx, NULL);
437 """.format(iter=self.iter, iter_store=self.iter_store, arg_unpacking=arg_unpacking,
438 stmts=stmts)
439 return body
441 class While(Node):
442 def __init__(self, test_stmts, test, stmts):
443 self.test_stmts = test_stmts
444 self.test = test
445 self.stmts = stmts
447 def __str__(self):
448 # XXX Super hack: too lazy to do this properly now
449 dup_test_stmts = copy.deepcopy(self.test_stmts)
450 assert isinstance(dup_test_stmts[-1], Assign)
451 dup_test_stmts[-1].target_type = None
453 test_stmts = block_str(self.test_stmts)
454 dup_test_stmts = block_str(dup_test_stmts)
455 stmts = block_str(self.stmts)
456 body = """
457 {test_stmts}
458 while ({test}->bool_value())
460 {stmts}
461 collect_garbage(&ctx, NULL);
462 {dup_test_stmts}
464 """.format(test_stmts=test_stmts, dup_test_stmts=dup_test_stmts, test=self.test, stmts=stmts)
465 return body
467 class Return(Node):
468 def __init__(self, value):
469 self.value = value
470 if self.value is None:
471 self.value = NoneConst()
473 def __str__(self):
474 body = """
475 collect_garbage(&ctx, %s);
476 return %s;
477 """ % (self.value, self.value)
478 return body
480 class Assert(Node):
481 def __init__(self, expr, lineno):
482 self.expr = expr
483 self.lineno = lineno
485 def __str__(self):
486 body = """if (!{expr}->bool_value()) {{
487 error("assert failed at line {lineno}");
489 """.format(expr=self.expr, lineno=self.lineno)
490 return body
492 class Arguments(Node):
493 def __init__(self, args, defaults):
494 self.args = args
495 self.defaults = defaults
497 def flatten(self, ctx):
498 new_def = [None] * (len(self.args) - len(self.defaults))
499 self.defaults = new_def + self.defaults
500 self.name_strings = [StringConst(a) for a in self.args]
501 return self
503 def __str__(self):
504 arg_unpacking = []
505 for i, (arg, default, name) in enumerate(zip(self.args, self.defaults, self.name_strings)):
506 if default:
507 arg_unpacking += [Store(arg, 'kwargs->lookup(%s) ? kwargs->lookup(%s) '
508 ': (args->len() > %s ? args->__getitem__(%s) : %s)' %
509 (name, name, i, i, default), 'local')]
510 else:
511 arg_unpacking += [Store(arg, 'args->__getitem__(%s)' % i, 'local')]
512 return block_str(arg_unpacking)
514 class FunctionDef(Node):
515 def __init__(self, name, args, stmts, exp_name, binding):
516 self.name = name
517 self.exp_name = exp_name if exp_name else name
518 self.exp_name = 'fn_%s' % self.exp_name # make sure no name collisions
519 self.args = args
520 self.stmts = stmts
521 self.binding = binding
523 def flatten(self, ctx):
524 ctx.functions += [self]
525 return [Store(self.name, Ref('function_def', Identifier(self.exp_name)), self.binding)]
527 def __str__(self):
528 stmts = block_str(self.stmts)
529 arg_unpacking = str(self.args)
530 body = """
531 node *{name}(context *globals, context *parent_ctx, list *args, dict *kwargs) {{
532 context ctx(parent_ctx);
533 {arg_unpacking}
534 {stmts}
535 return &none_singleton;
536 }}""".format(name=self.exp_name, arg_unpacking=arg_unpacking, stmts=stmts)
537 return body
539 class ClassDef(Node):
540 def __init__(self, name, stmts):
541 self.name = name
542 self.stmts = stmts
544 def flatten(self, ctx):
545 ctx.functions += [self]
546 return [Store(self.name, Ref('class_def', '"%s"' % self.name, Identifier('_%s__create__' % self.name)), 'global')]
548 def __str__(self):
549 stmts = block_str(self.stmts)
550 body = """
551 void _{name}__create__(class_def *class_ctx) {{
552 {stmts}
553 }}""".format(name=self.name, stmts=stmts)
554 return body