blob: 65912c7a726b404b7e7befe64b1641d1e512999b [file] [log] [blame]
Armin Ronachere791c2a2008-04-07 18:39:54 +02001# -*- coding: utf-8 -*-
2"""
3 jinja2.compiler
4 ~~~~~~~~~~~~~~~
5
6 Compiles nodes into python code.
7
8 :copyright: Copyright 2008 by Armin Ronacher.
9 :license: GNU GPL.
10"""
Armin Ronacher8efc5222008-04-08 14:47:40 +020011from copy import copy
Armin Ronachere791c2a2008-04-07 18:39:54 +020012from random import randrange
Armin Ronachere791c2a2008-04-07 18:39:54 +020013from cStringIO import StringIO
14from jinja2 import nodes
15from jinja2.visitor import NodeVisitor, NodeTransformer
16from jinja2.exceptions import TemplateAssertionError
Armin Ronacher4dfc9752008-04-09 15:03:29 +020017from jinja2.runtime import StaticLoopContext
Armin Ronachere791c2a2008-04-07 18:39:54 +020018
19
20operators = {
21 'eq': '==',
22 'ne': '!=',
23 'gt': '>',
24 'gteq': '>=',
25 'lt': '<',
26 'lteq': '<=',
27 'in': 'in',
28 'notin': 'not in'
29}
30
31
Christoph Hack65642a52008-04-08 14:46:56 +020032def generate(node, environment, filename, stream=None):
Armin Ronachere791c2a2008-04-07 18:39:54 +020033 is_child = node.find(nodes.Extends) is not None
Christoph Hack65642a52008-04-08 14:46:56 +020034 generator = CodeGenerator(environment, is_child, filename, stream)
Armin Ronachere791c2a2008-04-07 18:39:54 +020035 generator.visit(node)
36 if stream is None:
37 return generator.stream.getvalue()
38
39
Armin Ronacher4dfc9752008-04-09 15:03:29 +020040def has_safe_repr(value):
41 """Does the node have a safe representation?"""
Armin Ronacherd55ab532008-04-09 16:13:39 +020042 if value is None or value is NotImplemented or value is Ellipsis:
Armin Ronacher4dfc9752008-04-09 15:03:29 +020043 return True
Armin Ronacherd55ab532008-04-09 16:13:39 +020044 if isinstance(value, (bool, int, long, float, complex, basestring,
45 StaticLoopContext)):
Armin Ronacher4dfc9752008-04-09 15:03:29 +020046 return True
Armin Ronacherd55ab532008-04-09 16:13:39 +020047 if isinstance(value, (tuple, list, set, frozenset)):
Armin Ronacher4dfc9752008-04-09 15:03:29 +020048 for item in value:
49 if not has_safe_repr(item):
50 return False
51 return True
52 elif isinstance(value, dict):
53 for key, value in value.iteritems():
54 if not has_safe_repr(key):
55 return False
56 if not has_safe_repr(value):
57 return False
58 return True
59 return False
60
61
Armin Ronachere791c2a2008-04-07 18:39:54 +020062class Identifiers(object):
63 """Tracks the status of identifiers in frames."""
64
65 def __init__(self):
66 # variables that are known to be declared (probably from outer
67 # frames or because they are special for the frame)
68 self.declared = set()
69
70 # names that are accessed without being explicitly declared by
71 # this one or any of the outer scopes. Names can appear both in
72 # declared and undeclared.
73 self.undeclared = set()
74
75 # names that are declared locally
76 self.declared_locally = set()
77
78 # names that are declared by parameters
79 self.declared_parameter = set()
80
Christoph Hack65642a52008-04-08 14:46:56 +020081 # filters that are declared locally
82 self.declared_filter = set()
Christoph Hackacb130e2008-04-08 15:21:53 +020083 self.undeclared_filter = dict()
Christoph Hack65642a52008-04-08 14:46:56 +020084
Armin Ronachere791c2a2008-04-07 18:39:54 +020085 def add_special(self, name):
86 """Register a special name like `loop`."""
87 self.undeclared.discard(name)
88 self.declared.add(name)
89
Armin Ronacher4f62a9f2008-04-08 18:09:13 +020090 def is_declared(self, name, local_only=False):
Armin Ronachere791c2a2008-04-07 18:39:54 +020091 """Check if a name is declared in this or an outer scope."""
Armin Ronacher4f62a9f2008-04-08 18:09:13 +020092 if name in self.declared_locally or name in self.declared_parameter:
93 return True
94 if local_only:
95 return False
96 return name in self.declared
Armin Ronachere791c2a2008-04-07 18:39:54 +020097
98 def find_shadowed(self):
99 """Find all the shadowed names."""
100 return self.declared & (self.declared_locally | self.declared_parameter)
101
102
103class Frame(object):
104
105 def __init__(self, parent=None):
106 self.identifiers = Identifiers()
Armin Ronacher8efc5222008-04-08 14:47:40 +0200107 self.toplevel = False
Armin Ronachere791c2a2008-04-07 18:39:54 +0200108 self.parent = parent
Armin Ronacher8efc5222008-04-08 14:47:40 +0200109 self.block = parent and parent.block or None
Armin Ronachere791c2a2008-04-07 18:39:54 +0200110 if parent is not None:
111 self.identifiers.declared.update(
112 parent.identifiers.declared |
Armin Ronachere791c2a2008-04-07 18:39:54 +0200113 parent.identifiers.declared_locally |
114 parent.identifiers.declared_parameter
115 )
116
Armin Ronacher8efc5222008-04-08 14:47:40 +0200117 def copy(self):
118 """Create a copy of the current one."""
119 rv = copy(self)
120 rv.identifiers = copy(self)
121 return rv
122
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200123 def inspect(self, nodes, hard_scope=False):
124 """Walk the node and check for identifiers. If the scope
125 is hard (eg: enforce on a python level) overrides from outer
126 scopes are tracked differently.
127 """
128 visitor = FrameIdentifierVisitor(self.identifiers, hard_scope)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200129 for node in nodes:
130 visitor.visit(node)
131
132 def inner(self):
133 """Return an inner frame."""
134 return Frame(self)
135
136
137class FrameIdentifierVisitor(NodeVisitor):
138 """A visitor for `Frame.inspect`."""
139
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200140 def __init__(self, identifiers, hard_scope):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200141 self.identifiers = identifiers
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200142 self.hard_scope = hard_scope
Armin Ronachere791c2a2008-04-07 18:39:54 +0200143
144 def visit_Name(self, node):
145 """All assignments to names go through this function."""
146 if node.ctx in ('store', 'param'):
147 self.identifiers.declared_locally.add(node.name)
148 elif node.ctx == 'load':
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200149 if not self.identifiers.is_declared(node.name, self.hard_scope):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200150 self.identifiers.undeclared.add(node.name)
151
Armin Ronacherd55ab532008-04-09 16:13:39 +0200152 def visit_Filter(self, node):
Christoph Hack65642a52008-04-08 14:46:56 +0200153 if not node.name in self.identifiers.declared_filter:
Christoph Hackacb130e2008-04-08 15:21:53 +0200154 uf = self.identifiers.undeclared_filter.get(node.name, 0) + 1
155 if uf > 1:
156 self.identifiers.declared_filter.add(node.name)
157 self.identifiers.undeclared_filter[node.name] = uf
Christoph Hack65642a52008-04-08 14:46:56 +0200158
Armin Ronachere791c2a2008-04-07 18:39:54 +0200159 def visit_Macro(self, node):
160 """Macros set local."""
161 self.identifiers.declared_locally.add(node.name)
162
Armin Ronacherebe55aa2008-04-10 20:51:23 +0200163 def visit_Assign(self, node):
164 """Visit assignments in the correct order."""
165 self.visit(node.node)
166 self.visit(node.target)
167
Armin Ronachere791c2a2008-04-07 18:39:54 +0200168 # stop traversing at instructions that have their own scope.
Armin Ronacher8efc5222008-04-08 14:47:40 +0200169 visit_Block = visit_CallBlock = visit_FilterBlock = \
Armin Ronachere791c2a2008-04-07 18:39:54 +0200170 visit_For = lambda s, n: None
171
172
173class CodeGenerator(NodeVisitor):
174
Christoph Hack65642a52008-04-08 14:46:56 +0200175 def __init__(self, environment, is_child, filename, stream=None):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200176 if stream is None:
177 stream = StringIO()
Christoph Hack65642a52008-04-08 14:46:56 +0200178 self.environment = environment
Armin Ronachere791c2a2008-04-07 18:39:54 +0200179 self.is_child = is_child
180 self.filename = filename
181 self.stream = stream
182 self.blocks = {}
183 self.indentation = 0
184 self.new_lines = 0
185 self.last_identifier = 0
186 self._last_line = 0
187 self._first_write = True
188
189 def temporary_identifier(self):
190 self.last_identifier += 1
191 return 't%d' % self.last_identifier
192
193 def indent(self):
194 self.indentation += 1
195
Armin Ronacher8efc5222008-04-08 14:47:40 +0200196 def outdent(self, step=1):
197 self.indentation -= step
Armin Ronachere791c2a2008-04-07 18:39:54 +0200198
199 def blockvisit(self, nodes, frame, force_generator=False):
200 self.indent()
201 if force_generator:
202 self.writeline('if 0: yield None')
203 for node in nodes:
204 self.visit(node, frame)
205 self.outdent()
206
207 def write(self, x):
208 if self.new_lines:
209 if not self._first_write:
210 self.stream.write('\n' * self.new_lines)
211 self._first_write = False
212 self.stream.write(' ' * self.indentation)
213 self.new_lines = 0
214 self.stream.write(x)
215
216 def writeline(self, x, node=None, extra=0):
217 self.newline(node, extra)
218 self.write(x)
219
220 def newline(self, node=None, extra=0):
221 self.new_lines = max(self.new_lines, 1 + extra)
222 if node is not None and node.lineno != self._last_line:
223 self.write('# line: %s' % node.lineno)
224 self.new_lines = 1
225 self._last_line = node.lineno
226
Armin Ronacher8efc5222008-04-08 14:47:40 +0200227 def signature(self, node, frame, have_comma=True):
228 have_comma = have_comma and [True] or []
229 def touch_comma():
230 if have_comma:
231 self.write(', ')
232 else:
233 have_comma.append(True)
234
235 for arg in node.args:
236 touch_comma()
237 self.visit(arg, frame)
238 for kwarg in node.kwargs:
239 touch_comma()
240 self.visit(kwarg, frame)
241 if node.dyn_args:
242 touch_comma()
243 self.visit(node.dyn_args, frame)
244 if node.dyn_kwargs:
245 touch_comma()
246 self.visit(node.dyn_kwargs, frame)
247
Armin Ronachere791c2a2008-04-07 18:39:54 +0200248 def pull_locals(self, frame, no_indent=False):
249 if not no_indent:
250 self.indent()
251 for name in frame.identifiers.undeclared:
252 self.writeline('l_%s = context[%r]' % (name, name))
Christoph Hackacb130e2008-04-08 15:21:53 +0200253 for name, count in frame.identifiers.undeclared_filter.iteritems():
254 if count > 1:
255 self.writeline('f_%s = context[%r]' % (name, name))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200256 if not no_indent:
257 self.outdent()
258
259 # -- Visitors
260
261 def visit_Template(self, node, frame=None):
262 assert frame is None, 'no root frame allowed'
263 self.writeline('from jinja2.runtime import *')
264 self.writeline('filename = %r' % self.filename)
Armin Ronacher8edbe492008-04-10 20:43:43 +0200265
266 # find all blocks
267 for block in node.find_all(nodes.Block):
268 if block.name in self.blocks:
269 raise TemplateAssertionError('block %r defined twice' %
270 block.name, block.lineno,
271 self.filename)
272 self.blocks[block.name] = block
Armin Ronachere791c2a2008-04-07 18:39:54 +0200273
Armin Ronacher8efc5222008-04-08 14:47:40 +0200274 # generate the root render function.
Armin Ronacher8edbe492008-04-10 20:43:43 +0200275 self.writeline('def root(context):', extra=1)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200276 self.indent()
277 self.writeline('parent_root = None')
278 self.outdent()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200279 frame = Frame()
280 frame.inspect(node.body)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200281 frame.toplevel = True
Armin Ronachere791c2a2008-04-07 18:39:54 +0200282 self.pull_locals(frame)
283 self.blockvisit(node.body, frame, True)
284
Armin Ronacher8efc5222008-04-08 14:47:40 +0200285 # make sure that the parent root is called.
Armin Ronachere791c2a2008-04-07 18:39:54 +0200286 self.indent()
Armin Ronacher8efc5222008-04-08 14:47:40 +0200287 self.writeline('if parent_root is not None:')
288 self.indent()
289 self.writeline('for event in parent_root(context):')
290 self.indent()
291 self.writeline('yield event')
292 self.outdent(3)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200293
294 # at this point we now have the blocks collected and can visit them too.
295 for name, block in self.blocks.iteritems():
296 block_frame = Frame()
297 block_frame.inspect(block.body)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200298 block_frame.block = name
Armin Ronacher8edbe492008-04-10 20:43:43 +0200299 print block_frame.identifiers.__dict__
Armin Ronacher8efc5222008-04-08 14:47:40 +0200300 self.writeline('def block_%s(context):' % name, block, 1)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200301 self.pull_locals(block_frame)
302 self.blockvisit(block.body, block_frame, True)
303
304 def visit_Block(self, node, frame):
305 """Call a block and register it for the template."""
Armin Ronacher8efc5222008-04-08 14:47:40 +0200306 self.writeline('for event in block_%s(context):' % node.name)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200307 self.indent()
308 self.writeline('yield event')
309 self.outdent()
310
311 def visit_Extends(self, node, frame):
312 """Calls the extender."""
Armin Ronacher8efc5222008-04-08 14:47:40 +0200313 if not frame.toplevel:
314 raise TemplateAssertionError('cannot use extend from a non '
315 'top-level scope', node.lineno,
316 self.filename)
317 self.writeline('if parent_root is not None:')
318 self.indent()
319 self.writeline('raise TemplateRuntimeError(%r)' %
320 'extended multiple times')
321 self.outdent()
322 self.writeline('parent_root = extends(', node, 1)
323 self.visit(node.template, frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200324 self.write(', globals())')
325
326 def visit_For(self, node, frame):
327 loop_frame = frame.inner()
328 loop_frame.inspect(node.iter_child_nodes())
Armin Ronachere791c2a2008-04-07 18:39:54 +0200329 extended_loop = bool(node.else_) or \
330 'loop' in loop_frame.identifiers.undeclared
Armin Ronacher8efc5222008-04-08 14:47:40 +0200331 loop_frame.identifiers.add_special('loop')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200332
333 # make sure we "backup" overridden, local identifiers
334 # TODO: we should probably optimize this and check if the
335 # identifier is in use afterwards.
336 aliases = {}
337 for name in loop_frame.identifiers.find_shadowed():
338 aliases[name] = ident = self.temporary_identifier()
339 self.writeline('%s = l_%s' % (ident, name))
340
341 self.pull_locals(loop_frame, True)
342
343 self.newline(node)
344 if node.else_:
345 self.writeline('l_loop = None')
346 self.write('for ')
347 self.visit(node.target, loop_frame)
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200348 self.write(extended_loop and ', l_loop in LoopContext(' or ' in ')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200349 self.visit(node.iter, loop_frame)
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200350 if 'loop' in aliases:
351 self.write(', ' + aliases['loop'])
Armin Ronachere791c2a2008-04-07 18:39:54 +0200352 self.write(extended_loop and '):' or ':')
353 self.blockvisit(node.body, loop_frame)
354
355 if node.else_:
356 self.writeline('if l_loop is None:')
357 self.blockvisit(node.else_, loop_frame)
358
Armin Ronacher8efc5222008-04-08 14:47:40 +0200359 # reset the aliases and clean up
360 delete = set('l_' + x for x in loop_frame.identifiers.declared_locally
361 | loop_frame.identifiers.declared_parameter)
362 if extended_loop:
363 delete.add('l_loop')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200364 for name, alias in aliases.iteritems():
Armin Ronacher8efc5222008-04-08 14:47:40 +0200365 self.writeline('l_%s = %s' % (name, alias))
366 delete.add(alias)
367 delete.discard('l_' + name)
368 self.writeline('del %s' % ', '.join(delete))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200369
370 def visit_If(self, node, frame):
371 self.writeline('if ', node)
372 self.visit(node.test, frame)
373 self.write(':')
374 self.blockvisit(node.body, frame)
375 if node.else_:
376 self.writeline('else:')
377 self.blockvisit(node.else_, frame)
378
Armin Ronacher8efc5222008-04-08 14:47:40 +0200379 def visit_Macro(self, node, frame):
380 macro_frame = frame.inner()
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200381 macro_frame.inspect(node.iter_child_nodes(), hard_scope=True)
382
383 # variables that are undeclared (accessed before declaration) and
384 # declared locally *and* part of an outside scope raise a template
385 # assertion error. Reason: we can't generate reasonable code from
386 # it without aliasing all the variables. XXX: alias them ^^
387 overriden_closure_vars = (
388 macro_frame.identifiers.undeclared &
389 macro_frame.identifiers.declared &
390 (macro_frame.identifiers.declared_locally |
391 macro_frame.identifiers.declared_parameter)
392 )
393 if overriden_closure_vars:
394 vars = ', '.join(sorted(overriden_closure_vars))
395 raise TemplateAssertionError('It\'s not possible to set and '
396 'access variables derived from '
397 'an outer scope! (affects: %s' %
398 vars, node.lineno, self.filename)
399
400 # remove variables from a closure from the frame's undeclared
401 # identifiers.
402 macro_frame.identifiers.undeclared -= (
403 macro_frame.identifiers.undeclared &
404 macro_frame.identifiers.declared
405 )
406
Armin Ronacher8efc5222008-04-08 14:47:40 +0200407 args = ['l_' + x.name for x in node.args]
408 if 'arguments' in macro_frame.identifiers.undeclared:
409 accesses_arguments = True
410 args.append('l_arguments')
411 else:
412 accesses_arguments = False
413 self.writeline('def macro(%s):' % ', '.join(args), node)
414 self.indent()
415 self.writeline('if 0: yield None')
416 self.outdent()
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200417 self.pull_locals(macro_frame)
418 self.blockvisit(node.body, macro_frame)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200419 self.newline()
420 if frame.toplevel:
421 self.write('context[%r] = ' % node.name)
422 arg_tuple = ', '.join(repr(x.name) for x in node.args)
423 if len(node.args) == 1:
424 arg_tuple += ','
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200425 self.write('l_%s = Macro(macro, %r, (%s), (' % (node.name, node.name,
426 arg_tuple))
427 for arg in node.defaults:
428 self.visit(arg)
429 self.write(', ')
Armin Ronacher4dfc9752008-04-09 15:03:29 +0200430 self.write('), %r)' % accesses_arguments)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200431
Armin Ronachere791c2a2008-04-07 18:39:54 +0200432 def visit_ExprStmt(self, node, frame):
433 self.newline(node)
434 self.visit(node, frame)
435
436 def visit_Output(self, node, frame):
437 self.newline(node)
438
Armin Ronacher8edbe492008-04-10 20:43:43 +0200439 if self.environment.finalize is unicode:
440 finalizer = 'unicode'
441 else:
442 finalizer = 'context.finalize'
443
Armin Ronachere791c2a2008-04-07 18:39:54 +0200444 # try to evaluate as many chunks as possible into a static
445 # string at compile time.
446 body = []
447 for child in node.nodes:
448 try:
449 const = unicode(child.as_const())
450 except:
451 body.append(child)
452 continue
453 if body and isinstance(body[-1], list):
454 body[-1].append(const)
455 else:
456 body.append([const])
457
458 # if we have less than 3 nodes we just yield them
459 if len(body) < 3:
460 for item in body:
461 if isinstance(item, list):
462 self.writeline('yield %s' % repr(u''.join(item)))
463 else:
464 self.newline(item)
Armin Ronacher8edbe492008-04-10 20:43:43 +0200465 self.write('yield %s(' % finalizer)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200466 self.visit(item, frame)
467 self.write(')')
468
469 # otherwise we create a format string as this is faster in that case
470 else:
471 format = []
472 arguments = []
473 for item in body:
474 if isinstance(item, list):
475 format.append(u''.join(item).replace('%', '%%'))
476 else:
477 format.append('%s')
478 arguments.append(item)
479 self.writeline('yield %r %% (' % u''.join(format))
480 idx = -1
481 for idx, argument in enumerate(arguments):
482 if idx:
483 self.write(', ')
Armin Ronacher8edbe492008-04-10 20:43:43 +0200484 if finalizer != 'unicode':
485 self.write('(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200486 self.visit(argument, frame)
Armin Ronacher8edbe492008-04-10 20:43:43 +0200487 if finalizer != 'unicode':
488 self.write(')')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200489 self.write(idx == 0 and ',)' or ')')
490
Armin Ronacher8efc5222008-04-08 14:47:40 +0200491 def visit_Assign(self, node, frame):
492 self.newline(node)
493 # toplevel assignments however go into the local namespace and
494 # the current template's context. We create a copy of the frame
495 # here and add a set so that the Name visitor can add the assigned
496 # names here.
497 if frame.toplevel:
498 assignment_frame = frame.copy()
499 assignment_frame.assigned_names = set()
500 else:
501 assignment_frame = frame
502 self.visit(node.target, assignment_frame)
503 self.write(' = ')
504 self.visit(node.node, frame)
Armin Ronacher9706fab2008-04-08 18:49:56 +0200505
506 # make sure toplevel assignments are added to the context.
Armin Ronacher8efc5222008-04-08 14:47:40 +0200507 if frame.toplevel:
508 for name in assignment_frame.assigned_names:
509 self.writeline('context[%r] = l_%s' % (name, name))
510
Armin Ronachere791c2a2008-04-07 18:39:54 +0200511 def visit_Name(self, node, frame):
Armin Ronacher8efc5222008-04-08 14:47:40 +0200512 if frame.toplevel and node.ctx == 'store':
513 frame.assigned_names.add(node.name)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200514 self.write('l_' + node.name)
515
516 def visit_Const(self, node, frame):
517 val = node.value
518 if isinstance(val, float):
519 # XXX: add checks for infinity and nan
520 self.write(str(val))
521 else:
522 self.write(repr(val))
523
Armin Ronacher8efc5222008-04-08 14:47:40 +0200524 def visit_Tuple(self, node, frame):
525 self.write('(')
526 idx = -1
527 for idx, item in enumerate(node.items):
528 if idx:
529 self.write(', ')
530 self.visit(item, frame)
531 self.write(idx == 0 and ',)' or ')')
532
Armin Ronacher8edbe492008-04-10 20:43:43 +0200533 def visit_List(self, node, frame):
534 self.write('[')
535 for idx, item in enumerate(node.items):
536 if idx:
537 self.write(', ')
538 self.visit(item, frame)
539 self.write(']')
540
541 def visit_Dict(self, node, frame):
542 self.write('{')
543 for idx, item in enumerate(node.items):
544 if idx:
545 self.write(', ')
546 self.visit(item.key, frame)
547 self.write(': ')
548 self.visit(item.value, frame)
549 self.write('}')
550
Armin Ronachere791c2a2008-04-07 18:39:54 +0200551 def binop(operator):
552 def visitor(self, node, frame):
553 self.write('(')
554 self.visit(node.left, frame)
555 self.write(' %s ' % operator)
556 self.visit(node.right, frame)
557 self.write(')')
558 return visitor
559
560 def uaop(operator):
561 def visitor(self, node, frame):
562 self.write('(' + operator)
563 self.visit(node.node)
564 self.write(')')
565 return visitor
566
567 visit_Add = binop('+')
568 visit_Sub = binop('-')
569 visit_Mul = binop('*')
570 visit_Div = binop('/')
571 visit_FloorDiv = binop('//')
572 visit_Pow = binop('**')
573 visit_Mod = binop('%')
574 visit_And = binop('and')
575 visit_Or = binop('or')
576 visit_Pos = uaop('+')
577 visit_Neg = uaop('-')
578 visit_Not = uaop('not ')
579 del binop, uaop
580
581 def visit_Compare(self, node, frame):
582 self.visit(node.expr, frame)
583 for op in node.ops:
584 self.visit(op, frame)
585
586 def visit_Operand(self, node, frame):
587 self.write(' %s ' % operators[node.op])
588 self.visit(node.expr, frame)
589
590 def visit_Subscript(self, node, frame):
Armin Ronacher8efc5222008-04-08 14:47:40 +0200591 if isinstance(node.arg, nodes.Slice):
592 self.visit(node.node, frame)
593 self.write('[')
594 self.visit(node.arg, frame)
595 self.write(']')
596 return
597 try:
598 const = node.arg.as_const()
599 have_const = True
600 except nodes.Impossible:
601 have_const = False
602 if have_const:
603 if isinstance(const, (int, long, float)):
604 self.visit(node.node, frame)
605 self.write('[%s]' % const)
606 return
607 self.write('subscribe(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200608 self.visit(node.node, frame)
609 self.write(', ')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200610 if have_const:
611 self.write(repr(const))
612 else:
613 self.visit(node.arg, frame)
Armin Ronacher4dfc9752008-04-09 15:03:29 +0200614 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200615
616 def visit_Slice(self, node, frame):
617 if node.start is not None:
618 self.visit(node.start, frame)
619 self.write(':')
620 if node.stop is not None:
621 self.visit(node.stop, frame)
622 if node.step is not None:
623 self.write(':')
624 self.visit(node.step, frame)
625
626 def visit_Filter(self, node, frame):
Armin Ronacherd55ab532008-04-09 16:13:39 +0200627 if node.name in frame.identifiers.declared_filter:
628 self.write('f_%s(' % node.name)
629 else:
630 self.write('context.filter[%r](' % node.name)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200631 self.visit(node.node, frame)
Armin Ronacherd55ab532008-04-09 16:13:39 +0200632 self.signature(node, frame)
633 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200634
635 def visit_Test(self, node, frame):
636 self.write('context.tests[%r](')
637 self.visit(node.node, frame)
638 self.signature(node, frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200639 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200640
641 def visit_Call(self, node, frame):
642 self.visit(node.node, frame)
643 self.write('(')
644 self.signature(node, frame, False)
645 self.write(')')
646
647 def visit_Keyword(self, node, frame):
648 self.visit(node.key, frame)
649 self.write('=')
650 self.visit(node.value, frame)