blob: c963b8f4b256f5174094d8a2b01980502a4b390a [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?"""
42 if value is None:
43 return True
44 if isinstance(value, (int, long, float, basestring, StaticLoopContext)):
45 return True
46 if isinstance(value, (tuple, list)):
47 for item in value:
48 if not has_safe_repr(item):
49 return False
50 return True
51 elif isinstance(value, dict):
52 for key, value in value.iteritems():
53 if not has_safe_repr(key):
54 return False
55 if not has_safe_repr(value):
56 return False
57 return True
58 return False
59
60
Armin Ronachere791c2a2008-04-07 18:39:54 +020061class Identifiers(object):
62 """Tracks the status of identifiers in frames."""
63
64 def __init__(self):
65 # variables that are known to be declared (probably from outer
66 # frames or because they are special for the frame)
67 self.declared = set()
68
69 # names that are accessed without being explicitly declared by
70 # this one or any of the outer scopes. Names can appear both in
71 # declared and undeclared.
72 self.undeclared = set()
73
74 # names that are declared locally
75 self.declared_locally = set()
76
77 # names that are declared by parameters
78 self.declared_parameter = set()
79
Christoph Hack65642a52008-04-08 14:46:56 +020080 # filters that are declared locally
81 self.declared_filter = set()
Christoph Hackacb130e2008-04-08 15:21:53 +020082 self.undeclared_filter = dict()
Christoph Hack65642a52008-04-08 14:46:56 +020083
Armin Ronachere791c2a2008-04-07 18:39:54 +020084 def add_special(self, name):
85 """Register a special name like `loop`."""
86 self.undeclared.discard(name)
87 self.declared.add(name)
88
Armin Ronacher4f62a9f2008-04-08 18:09:13 +020089 def is_declared(self, name, local_only=False):
Armin Ronachere791c2a2008-04-07 18:39:54 +020090 """Check if a name is declared in this or an outer scope."""
Armin Ronacher4f62a9f2008-04-08 18:09:13 +020091 if name in self.declared_locally or name in self.declared_parameter:
92 return True
93 if local_only:
94 return False
95 return name in self.declared
Armin Ronachere791c2a2008-04-07 18:39:54 +020096
97 def find_shadowed(self):
98 """Find all the shadowed names."""
99 return self.declared & (self.declared_locally | self.declared_parameter)
100
101
102class Frame(object):
103
104 def __init__(self, parent=None):
105 self.identifiers = Identifiers()
Armin Ronacher8efc5222008-04-08 14:47:40 +0200106 self.toplevel = False
Armin Ronachere791c2a2008-04-07 18:39:54 +0200107 self.parent = parent
Armin Ronacher8efc5222008-04-08 14:47:40 +0200108 self.block = parent and parent.block or None
Armin Ronachere791c2a2008-04-07 18:39:54 +0200109 if parent is not None:
110 self.identifiers.declared.update(
111 parent.identifiers.declared |
Armin Ronachere791c2a2008-04-07 18:39:54 +0200112 parent.identifiers.declared_locally |
113 parent.identifiers.declared_parameter
114 )
115
Armin Ronacher8efc5222008-04-08 14:47:40 +0200116 def copy(self):
117 """Create a copy of the current one."""
118 rv = copy(self)
119 rv.identifiers = copy(self)
120 return rv
121
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200122 def inspect(self, nodes, hard_scope=False):
123 """Walk the node and check for identifiers. If the scope
124 is hard (eg: enforce on a python level) overrides from outer
125 scopes are tracked differently.
126 """
127 visitor = FrameIdentifierVisitor(self.identifiers, hard_scope)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200128 for node in nodes:
129 visitor.visit(node)
130
131 def inner(self):
132 """Return an inner frame."""
133 return Frame(self)
134
135
136class FrameIdentifierVisitor(NodeVisitor):
137 """A visitor for `Frame.inspect`."""
138
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200139 def __init__(self, identifiers, hard_scope):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200140 self.identifiers = identifiers
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200141 self.hard_scope = hard_scope
Armin Ronachere791c2a2008-04-07 18:39:54 +0200142
143 def visit_Name(self, node):
144 """All assignments to names go through this function."""
145 if node.ctx in ('store', 'param'):
146 self.identifiers.declared_locally.add(node.name)
147 elif node.ctx == 'load':
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200148 if not self.identifiers.is_declared(node.name, self.hard_scope):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200149 self.identifiers.undeclared.add(node.name)
150
Christoph Hack65642a52008-04-08 14:46:56 +0200151 def visit_FilterCall(self, node):
152 if not node.name in self.identifiers.declared_filter:
Christoph Hackacb130e2008-04-08 15:21:53 +0200153 uf = self.identifiers.undeclared_filter.get(node.name, 0) + 1
154 if uf > 1:
155 self.identifiers.declared_filter.add(node.name)
156 self.identifiers.undeclared_filter[node.name] = uf
Christoph Hack65642a52008-04-08 14:46:56 +0200157
Armin Ronachere791c2a2008-04-07 18:39:54 +0200158 def visit_Macro(self, node):
159 """Macros set local."""
160 self.identifiers.declared_locally.add(node.name)
161
162 # stop traversing at instructions that have their own scope.
Armin Ronacher8efc5222008-04-08 14:47:40 +0200163 visit_Block = visit_CallBlock = visit_FilterBlock = \
Armin Ronachere791c2a2008-04-07 18:39:54 +0200164 visit_For = lambda s, n: None
165
166
167class CodeGenerator(NodeVisitor):
168
Christoph Hack65642a52008-04-08 14:46:56 +0200169 def __init__(self, environment, is_child, filename, stream=None):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200170 if stream is None:
171 stream = StringIO()
Christoph Hack65642a52008-04-08 14:46:56 +0200172 self.environment = environment
Armin Ronachere791c2a2008-04-07 18:39:54 +0200173 self.is_child = is_child
174 self.filename = filename
175 self.stream = stream
176 self.blocks = {}
177 self.indentation = 0
178 self.new_lines = 0
179 self.last_identifier = 0
180 self._last_line = 0
181 self._first_write = True
182
183 def temporary_identifier(self):
184 self.last_identifier += 1
185 return 't%d' % self.last_identifier
186
187 def indent(self):
188 self.indentation += 1
189
Armin Ronacher8efc5222008-04-08 14:47:40 +0200190 def outdent(self, step=1):
191 self.indentation -= step
Armin Ronachere791c2a2008-04-07 18:39:54 +0200192
193 def blockvisit(self, nodes, frame, force_generator=False):
194 self.indent()
195 if force_generator:
196 self.writeline('if 0: yield None')
197 for node in nodes:
198 self.visit(node, frame)
199 self.outdent()
200
201 def write(self, x):
202 if self.new_lines:
203 if not self._first_write:
204 self.stream.write('\n' * self.new_lines)
205 self._first_write = False
206 self.stream.write(' ' * self.indentation)
207 self.new_lines = 0
208 self.stream.write(x)
209
210 def writeline(self, x, node=None, extra=0):
211 self.newline(node, extra)
212 self.write(x)
213
214 def newline(self, node=None, extra=0):
215 self.new_lines = max(self.new_lines, 1 + extra)
216 if node is not None and node.lineno != self._last_line:
217 self.write('# line: %s' % node.lineno)
218 self.new_lines = 1
219 self._last_line = node.lineno
220
Armin Ronacher8efc5222008-04-08 14:47:40 +0200221 def signature(self, node, frame, have_comma=True):
222 have_comma = have_comma and [True] or []
223 def touch_comma():
224 if have_comma:
225 self.write(', ')
226 else:
227 have_comma.append(True)
228
229 for arg in node.args:
230 touch_comma()
231 self.visit(arg, frame)
232 for kwarg in node.kwargs:
233 touch_comma()
234 self.visit(kwarg, frame)
235 if node.dyn_args:
236 touch_comma()
237 self.visit(node.dyn_args, frame)
238 if node.dyn_kwargs:
239 touch_comma()
240 self.visit(node.dyn_kwargs, frame)
241
Armin Ronachere791c2a2008-04-07 18:39:54 +0200242 def pull_locals(self, frame, no_indent=False):
243 if not no_indent:
244 self.indent()
245 for name in frame.identifiers.undeclared:
246 self.writeline('l_%s = context[%r]' % (name, name))
Christoph Hackacb130e2008-04-08 15:21:53 +0200247 for name, count in frame.identifiers.undeclared_filter.iteritems():
248 if count > 1:
249 self.writeline('f_%s = context[%r]' % (name, name))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200250 if not no_indent:
251 self.outdent()
252
253 # -- Visitors
254
255 def visit_Template(self, node, frame=None):
256 assert frame is None, 'no root frame allowed'
257 self.writeline('from jinja2.runtime import *')
258 self.writeline('filename = %r' % self.filename)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200259 self.writeline('template_context = TemplateContext(global_context, '
Armin Ronacher4dfc9752008-04-09 15:03:29 +0200260 'filename)')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200261
Armin Ronacher8efc5222008-04-08 14:47:40 +0200262 # generate the root render function.
263 self.writeline('def root(context=template_context):', extra=1)
264 self.indent()
265 self.writeline('parent_root = None')
266 self.outdent()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200267 frame = Frame()
268 frame.inspect(node.body)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200269 frame.toplevel = True
Armin Ronachere791c2a2008-04-07 18:39:54 +0200270 self.pull_locals(frame)
271 self.blockvisit(node.body, frame, True)
272
Armin Ronacher8efc5222008-04-08 14:47:40 +0200273 # make sure that the parent root is called.
Armin Ronachere791c2a2008-04-07 18:39:54 +0200274 self.indent()
Armin Ronacher8efc5222008-04-08 14:47:40 +0200275 self.writeline('if parent_root is not None:')
276 self.indent()
277 self.writeline('for event in parent_root(context):')
278 self.indent()
279 self.writeline('yield event')
280 self.outdent(3)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200281
282 # at this point we now have the blocks collected and can visit them too.
283 for name, block in self.blocks.iteritems():
284 block_frame = Frame()
285 block_frame.inspect(block.body)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200286 block_frame.block = name
287 self.writeline('def block_%s(context):' % name, block, 1)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200288 self.pull_locals(block_frame)
289 self.blockvisit(block.body, block_frame, True)
290
291 def visit_Block(self, node, frame):
292 """Call a block and register it for the template."""
293 if node.name in self.blocks:
294 raise TemplateAssertionError("the block '%s' was already defined" %
295 node.name, node.lineno,
296 self.filename)
297 self.blocks[node.name] = node
Armin Ronacher8efc5222008-04-08 14:47:40 +0200298 self.writeline('for event in block_%s(context):' % node.name)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200299 self.indent()
300 self.writeline('yield event')
301 self.outdent()
302
303 def visit_Extends(self, node, frame):
304 """Calls the extender."""
Armin Ronacher8efc5222008-04-08 14:47:40 +0200305 if not frame.toplevel:
306 raise TemplateAssertionError('cannot use extend from a non '
307 'top-level scope', node.lineno,
308 self.filename)
309 self.writeline('if parent_root is not None:')
310 self.indent()
311 self.writeline('raise TemplateRuntimeError(%r)' %
312 'extended multiple times')
313 self.outdent()
314 self.writeline('parent_root = extends(', node, 1)
315 self.visit(node.template, frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200316 self.write(', globals())')
317
318 def visit_For(self, node, frame):
319 loop_frame = frame.inner()
320 loop_frame.inspect(node.iter_child_nodes())
Armin Ronachere791c2a2008-04-07 18:39:54 +0200321 extended_loop = bool(node.else_) or \
322 'loop' in loop_frame.identifiers.undeclared
Armin Ronacher8efc5222008-04-08 14:47:40 +0200323 loop_frame.identifiers.add_special('loop')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200324
325 # make sure we "backup" overridden, local identifiers
326 # TODO: we should probably optimize this and check if the
327 # identifier is in use afterwards.
328 aliases = {}
329 for name in loop_frame.identifiers.find_shadowed():
330 aliases[name] = ident = self.temporary_identifier()
331 self.writeline('%s = l_%s' % (ident, name))
332
333 self.pull_locals(loop_frame, True)
334
335 self.newline(node)
336 if node.else_:
337 self.writeline('l_loop = None')
338 self.write('for ')
339 self.visit(node.target, loop_frame)
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200340 self.write(extended_loop and ', l_loop in LoopContext(' or ' in ')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200341 self.visit(node.iter, loop_frame)
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200342 if 'loop' in aliases:
343 self.write(', ' + aliases['loop'])
Armin Ronachere791c2a2008-04-07 18:39:54 +0200344 self.write(extended_loop and '):' or ':')
345 self.blockvisit(node.body, loop_frame)
346
347 if node.else_:
348 self.writeline('if l_loop is None:')
349 self.blockvisit(node.else_, loop_frame)
350
Armin Ronacher8efc5222008-04-08 14:47:40 +0200351 # reset the aliases and clean up
352 delete = set('l_' + x for x in loop_frame.identifiers.declared_locally
353 | loop_frame.identifiers.declared_parameter)
354 if extended_loop:
355 delete.add('l_loop')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200356 for name, alias in aliases.iteritems():
Armin Ronacher8efc5222008-04-08 14:47:40 +0200357 self.writeline('l_%s = %s' % (name, alias))
358 delete.add(alias)
359 delete.discard('l_' + name)
360 self.writeline('del %s' % ', '.join(delete))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200361
362 def visit_If(self, node, frame):
363 self.writeline('if ', node)
364 self.visit(node.test, frame)
365 self.write(':')
366 self.blockvisit(node.body, frame)
367 if node.else_:
368 self.writeline('else:')
369 self.blockvisit(node.else_, frame)
370
Armin Ronacher8efc5222008-04-08 14:47:40 +0200371 def visit_Macro(self, node, frame):
372 macro_frame = frame.inner()
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200373 macro_frame.inspect(node.iter_child_nodes(), hard_scope=True)
374
375 # variables that are undeclared (accessed before declaration) and
376 # declared locally *and* part of an outside scope raise a template
377 # assertion error. Reason: we can't generate reasonable code from
378 # it without aliasing all the variables. XXX: alias them ^^
379 overriden_closure_vars = (
380 macro_frame.identifiers.undeclared &
381 macro_frame.identifiers.declared &
382 (macro_frame.identifiers.declared_locally |
383 macro_frame.identifiers.declared_parameter)
384 )
385 if overriden_closure_vars:
386 vars = ', '.join(sorted(overriden_closure_vars))
387 raise TemplateAssertionError('It\'s not possible to set and '
388 'access variables derived from '
389 'an outer scope! (affects: %s' %
390 vars, node.lineno, self.filename)
391
392 # remove variables from a closure from the frame's undeclared
393 # identifiers.
394 macro_frame.identifiers.undeclared -= (
395 macro_frame.identifiers.undeclared &
396 macro_frame.identifiers.declared
397 )
398
Armin Ronacher8efc5222008-04-08 14:47:40 +0200399 args = ['l_' + x.name for x in node.args]
400 if 'arguments' in macro_frame.identifiers.undeclared:
401 accesses_arguments = True
402 args.append('l_arguments')
403 else:
404 accesses_arguments = False
405 self.writeline('def macro(%s):' % ', '.join(args), node)
406 self.indent()
407 self.writeline('if 0: yield None')
408 self.outdent()
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200409 self.pull_locals(macro_frame)
410 self.blockvisit(node.body, macro_frame)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200411 self.newline()
412 if frame.toplevel:
413 self.write('context[%r] = ' % node.name)
414 arg_tuple = ', '.join(repr(x.name) for x in node.args)
415 if len(node.args) == 1:
416 arg_tuple += ','
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200417 self.write('l_%s = Macro(macro, %r, (%s), (' % (node.name, node.name,
418 arg_tuple))
419 for arg in node.defaults:
420 self.visit(arg)
421 self.write(', ')
Armin Ronacher4dfc9752008-04-09 15:03:29 +0200422 self.write('), %r)' % accesses_arguments)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200423
Armin Ronachere791c2a2008-04-07 18:39:54 +0200424 def visit_ExprStmt(self, node, frame):
425 self.newline(node)
426 self.visit(node, frame)
427
428 def visit_Output(self, node, frame):
429 self.newline(node)
430
431 # try to evaluate as many chunks as possible into a static
432 # string at compile time.
433 body = []
434 for child in node.nodes:
435 try:
436 const = unicode(child.as_const())
437 except:
438 body.append(child)
439 continue
440 if body and isinstance(body[-1], list):
441 body[-1].append(const)
442 else:
443 body.append([const])
444
445 # if we have less than 3 nodes we just yield them
446 if len(body) < 3:
447 for item in body:
448 if isinstance(item, list):
449 self.writeline('yield %s' % repr(u''.join(item)))
450 else:
451 self.newline(item)
452 self.write('yield unicode(')
453 self.visit(item, frame)
454 self.write(')')
455
456 # otherwise we create a format string as this is faster in that case
457 else:
458 format = []
459 arguments = []
460 for item in body:
461 if isinstance(item, list):
462 format.append(u''.join(item).replace('%', '%%'))
463 else:
464 format.append('%s')
465 arguments.append(item)
466 self.writeline('yield %r %% (' % u''.join(format))
467 idx = -1
468 for idx, argument in enumerate(arguments):
469 if idx:
470 self.write(', ')
471 self.visit(argument, frame)
472 self.write(idx == 0 and ',)' or ')')
473
Armin Ronacher8efc5222008-04-08 14:47:40 +0200474 def visit_Assign(self, node, frame):
475 self.newline(node)
476 # toplevel assignments however go into the local namespace and
477 # the current template's context. We create a copy of the frame
478 # here and add a set so that the Name visitor can add the assigned
479 # names here.
480 if frame.toplevel:
481 assignment_frame = frame.copy()
482 assignment_frame.assigned_names = set()
483 else:
484 assignment_frame = frame
485 self.visit(node.target, assignment_frame)
486 self.write(' = ')
487 self.visit(node.node, frame)
Armin Ronacher9706fab2008-04-08 18:49:56 +0200488
489 # make sure toplevel assignments are added to the context.
Armin Ronacher8efc5222008-04-08 14:47:40 +0200490 if frame.toplevel:
491 for name in assignment_frame.assigned_names:
492 self.writeline('context[%r] = l_%s' % (name, name))
493
Armin Ronachere791c2a2008-04-07 18:39:54 +0200494 def visit_Name(self, node, frame):
Armin Ronacher8efc5222008-04-08 14:47:40 +0200495 if frame.toplevel and node.ctx == 'store':
496 frame.assigned_names.add(node.name)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200497 self.write('l_' + node.name)
498
499 def visit_Const(self, node, frame):
500 val = node.value
501 if isinstance(val, float):
502 # XXX: add checks for infinity and nan
503 self.write(str(val))
504 else:
505 self.write(repr(val))
506
Armin Ronacher8efc5222008-04-08 14:47:40 +0200507 def visit_Tuple(self, node, frame):
508 self.write('(')
509 idx = -1
510 for idx, item in enumerate(node.items):
511 if idx:
512 self.write(', ')
513 self.visit(item, frame)
514 self.write(idx == 0 and ',)' or ')')
515
Armin Ronachere791c2a2008-04-07 18:39:54 +0200516 def binop(operator):
517 def visitor(self, node, frame):
518 self.write('(')
519 self.visit(node.left, frame)
520 self.write(' %s ' % operator)
521 self.visit(node.right, frame)
522 self.write(')')
523 return visitor
524
525 def uaop(operator):
526 def visitor(self, node, frame):
527 self.write('(' + operator)
528 self.visit(node.node)
529 self.write(')')
530 return visitor
531
532 visit_Add = binop('+')
533 visit_Sub = binop('-')
534 visit_Mul = binop('*')
535 visit_Div = binop('/')
536 visit_FloorDiv = binop('//')
537 visit_Pow = binop('**')
538 visit_Mod = binop('%')
539 visit_And = binop('and')
540 visit_Or = binop('or')
541 visit_Pos = uaop('+')
542 visit_Neg = uaop('-')
543 visit_Not = uaop('not ')
544 del binop, uaop
545
546 def visit_Compare(self, node, frame):
547 self.visit(node.expr, frame)
548 for op in node.ops:
549 self.visit(op, frame)
550
551 def visit_Operand(self, node, frame):
552 self.write(' %s ' % operators[node.op])
553 self.visit(node.expr, frame)
554
555 def visit_Subscript(self, node, frame):
Armin Ronacher8efc5222008-04-08 14:47:40 +0200556 if isinstance(node.arg, nodes.Slice):
557 self.visit(node.node, frame)
558 self.write('[')
559 self.visit(node.arg, frame)
560 self.write(']')
561 return
562 try:
563 const = node.arg.as_const()
564 have_const = True
565 except nodes.Impossible:
566 have_const = False
567 if have_const:
568 if isinstance(const, (int, long, float)):
569 self.visit(node.node, frame)
570 self.write('[%s]' % const)
571 return
572 self.write('subscribe(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200573 self.visit(node.node, frame)
574 self.write(', ')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200575 if have_const:
576 self.write(repr(const))
577 else:
578 self.visit(node.arg, frame)
Armin Ronacher4dfc9752008-04-09 15:03:29 +0200579 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200580
581 def visit_Slice(self, node, frame):
582 if node.start is not None:
583 self.visit(node.start, frame)
584 self.write(':')
585 if node.stop is not None:
586 self.visit(node.stop, frame)
587 if node.step is not None:
588 self.write(':')
589 self.visit(node.step, frame)
590
591 def visit_Filter(self, node, frame):
Armin Ronacher8efc5222008-04-08 14:47:40 +0200592 for filter in node.filters:
Christoph Hackacb130e2008-04-08 15:21:53 +0200593 if filter.name in frame.identifiers.declared_filter:
594 self.write('f_%s(' % filter.name)
595 else:
596 self.write('context.filter[%r](' % filter.name)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200597 self.visit(node.node, frame)
598 for filter in reversed(node.filters):
Armin Ronacher8efc5222008-04-08 14:47:40 +0200599 self.signature(filter, frame)
600 self.write(')')
601
602 def visit_Test(self, node, frame):
603 self.write('context.tests[%r](')
604 self.visit(node.node, frame)
605 self.signature(node, frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200606 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200607
608 def visit_Call(self, node, frame):
609 self.visit(node.node, frame)
610 self.write('(')
611 self.signature(node, frame, False)
612 self.write(')')
613
614 def visit_Keyword(self, node, frame):
615 self.visit(node.key, frame)
616 self.write('=')
617 self.visit(node.value, frame)