blob: 06017f808b4b91a4c85d0f38cb6fc24e0660610b [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
Armin Ronacher3d8b7842008-04-13 13:16:50 +020032try:
33 exec '(0 if 0 else 0)'
34except SyntaxError:
35 have_condexpr = False
36else:
37 have_condexpr = True
38
39
Christoph Hack65642a52008-04-08 14:46:56 +020040def generate(node, environment, filename, stream=None):
Armin Ronacherbcb7c532008-04-11 16:30:34 +020041 """Generate the python source for a node tree."""
Armin Ronacherfed44b52008-04-13 19:42:53 +020042 generator = CodeGenerator(environment, filename, stream)
Armin Ronachere791c2a2008-04-07 18:39:54 +020043 generator.visit(node)
44 if stream is None:
45 return generator.stream.getvalue()
46
47
Armin Ronacher4dfc9752008-04-09 15:03:29 +020048def has_safe_repr(value):
49 """Does the node have a safe representation?"""
Armin Ronacherd55ab532008-04-09 16:13:39 +020050 if value is None or value is NotImplemented or value is Ellipsis:
Armin Ronacher4dfc9752008-04-09 15:03:29 +020051 return True
Armin Ronacherd55ab532008-04-09 16:13:39 +020052 if isinstance(value, (bool, int, long, float, complex, basestring,
Armin Ronacher2e9396b2008-04-16 14:21:57 +020053 xrange, StaticLoopContext)):
Armin Ronacher4dfc9752008-04-09 15:03:29 +020054 return True
Armin Ronacherd55ab532008-04-09 16:13:39 +020055 if isinstance(value, (tuple, list, set, frozenset)):
Armin Ronacher4dfc9752008-04-09 15:03:29 +020056 for item in value:
57 if not has_safe_repr(item):
58 return False
59 return True
60 elif isinstance(value, dict):
61 for key, value in value.iteritems():
62 if not has_safe_repr(key):
63 return False
64 if not has_safe_repr(value):
65 return False
66 return True
67 return False
68
69
Armin Ronachere791c2a2008-04-07 18:39:54 +020070class Identifiers(object):
71 """Tracks the status of identifiers in frames."""
72
73 def __init__(self):
74 # variables that are known to be declared (probably from outer
75 # frames or because they are special for the frame)
76 self.declared = set()
77
78 # names that are accessed without being explicitly declared by
79 # this one or any of the outer scopes. Names can appear both in
80 # declared and undeclared.
81 self.undeclared = set()
82
83 # names that are declared locally
84 self.declared_locally = set()
85
86 # names that are declared by parameters
87 self.declared_parameter = set()
88
Armin Ronacherf059ec12008-04-11 22:21:00 +020089 # filters/tests that are referenced
Armin Ronacherd4c64f72008-04-11 17:15:29 +020090 self.filters = set()
Armin Ronacherf059ec12008-04-11 22:21:00 +020091 self.tests = set()
Christoph Hack65642a52008-04-08 14:46:56 +020092
Armin Ronachere791c2a2008-04-07 18:39:54 +020093 def add_special(self, name):
94 """Register a special name like `loop`."""
95 self.undeclared.discard(name)
96 self.declared.add(name)
97
Armin Ronacher4f62a9f2008-04-08 18:09:13 +020098 def is_declared(self, name, local_only=False):
Armin Ronachere791c2a2008-04-07 18:39:54 +020099 """Check if a name is declared in this or an outer scope."""
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200100 if name in self.declared_locally or name in self.declared_parameter:
101 return True
102 if local_only:
103 return False
104 return name in self.declared
Armin Ronachere791c2a2008-04-07 18:39:54 +0200105
106 def find_shadowed(self):
107 """Find all the shadowed names."""
108 return self.declared & (self.declared_locally | self.declared_parameter)
109
110
111class Frame(object):
Armin Ronacher75cfb862008-04-11 13:47:22 +0200112 """Holds compile time information for us."""
Armin Ronachere791c2a2008-04-07 18:39:54 +0200113
114 def __init__(self, parent=None):
115 self.identifiers = Identifiers()
Armin Ronacherfed44b52008-04-13 19:42:53 +0200116
Armin Ronacher75cfb862008-04-11 13:47:22 +0200117 # a toplevel frame is the root + soft frames such as if conditions.
Armin Ronacher8efc5222008-04-08 14:47:40 +0200118 self.toplevel = False
Armin Ronacherfed44b52008-04-13 19:42:53 +0200119
Armin Ronacher75cfb862008-04-11 13:47:22 +0200120 # the root frame is basically just the outermost frame, so no if
121 # conditions. This information is used to optimize inheritance
122 # situations.
123 self.rootlevel = False
Armin Ronacherfed44b52008-04-13 19:42:53 +0200124
125 # inside some tags we are using a buffer rather than yield statements.
126 # this for example affects {% filter %} or {% macro %}. If a frame
127 # is buffered this variable points to the name of the list used as
128 # buffer.
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200129 self.buffer = None
Armin Ronacherfed44b52008-04-13 19:42:53 +0200130
131 # if a frame has name_overrides, all read access to a name in this
132 # dict is redirected to a string expression.
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200133 self.name_overrides = {}
Armin Ronacherfed44b52008-04-13 19:42:53 +0200134
135 # the name of the block we're in, otherwise None.
Armin Ronacher8efc5222008-04-08 14:47:40 +0200136 self.block = parent and parent.block or None
Armin Ronacherfed44b52008-04-13 19:42:53 +0200137
138 # the parent of this frame
139 self.parent = parent
140
Armin Ronachere791c2a2008-04-07 18:39:54 +0200141 if parent is not None:
142 self.identifiers.declared.update(
143 parent.identifiers.declared |
Armin Ronachere791c2a2008-04-07 18:39:54 +0200144 parent.identifiers.declared_locally |
145 parent.identifiers.declared_parameter
146 )
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200147 self.buffer = parent.buffer
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200148 self.name_overrides = parent.name_overrides.copy()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200149
Armin Ronacher8efc5222008-04-08 14:47:40 +0200150 def copy(self):
151 """Create a copy of the current one."""
152 rv = copy(self)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200153 rv.identifiers = copy(self.identifiers)
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200154 rv.name_overrides = self.name_overrides.copy()
Armin Ronacher8efc5222008-04-08 14:47:40 +0200155 return rv
156
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200157 def inspect(self, nodes, hard_scope=False):
158 """Walk the node and check for identifiers. If the scope
159 is hard (eg: enforce on a python level) overrides from outer
160 scopes are tracked differently.
161 """
162 visitor = FrameIdentifierVisitor(self.identifiers, hard_scope)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200163 for node in nodes:
164 visitor.visit(node)
165
166 def inner(self):
167 """Return an inner frame."""
168 return Frame(self)
169
Armin Ronacher75cfb862008-04-11 13:47:22 +0200170 def soft(self):
171 """Return a soft frame. A soft frame may not be modified as
172 standalone thing as it shares the resources with the frame it
173 was created of, but it's not a rootlevel frame any longer.
174 """
175 rv = copy(self)
176 rv.rootlevel = False
177 return rv
178
Armin Ronachere791c2a2008-04-07 18:39:54 +0200179
180class FrameIdentifierVisitor(NodeVisitor):
181 """A visitor for `Frame.inspect`."""
182
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200183 def __init__(self, identifiers, hard_scope):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200184 self.identifiers = identifiers
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200185 self.hard_scope = hard_scope
Armin Ronachere791c2a2008-04-07 18:39:54 +0200186
187 def visit_Name(self, node):
188 """All assignments to names go through this function."""
189 if node.ctx in ('store', 'param'):
190 self.identifiers.declared_locally.add(node.name)
191 elif node.ctx == 'load':
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200192 if not self.identifiers.is_declared(node.name, self.hard_scope):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200193 self.identifiers.undeclared.add(node.name)
194
Armin Ronacherd55ab532008-04-09 16:13:39 +0200195 def visit_Filter(self, node):
Armin Ronacher449167d2008-04-11 17:55:05 +0200196 self.generic_visit(node)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200197 self.identifiers.filters.add(node.name)
198
199 def visit_Test(self, node):
200 self.generic_visit(node)
201 self.identifiers.tests.add(node.name)
Christoph Hack65642a52008-04-08 14:46:56 +0200202
Armin Ronachere791c2a2008-04-07 18:39:54 +0200203 def visit_Macro(self, node):
204 """Macros set local."""
205 self.identifiers.declared_locally.add(node.name)
206
Armin Ronacherf059ec12008-04-11 22:21:00 +0200207 def visit_Include(self, node):
208 """Some includes set local."""
209 self.generic_visit(node)
210 if node.target is not None:
211 self.identifiers.declared_locally.add(node.target)
212
Armin Ronacherebe55aa2008-04-10 20:51:23 +0200213 def visit_Assign(self, node):
214 """Visit assignments in the correct order."""
215 self.visit(node.node)
216 self.visit(node.target)
217
Armin Ronachere791c2a2008-04-07 18:39:54 +0200218 # stop traversing at instructions that have their own scope.
Armin Ronacher8efc5222008-04-08 14:47:40 +0200219 visit_Block = visit_CallBlock = visit_FilterBlock = \
Armin Ronachere791c2a2008-04-07 18:39:54 +0200220 visit_For = lambda s, n: None
221
222
Armin Ronacher75cfb862008-04-11 13:47:22 +0200223class CompilerExit(Exception):
224 """Raised if the compiler encountered a situation where it just
225 doesn't make sense to further process the code. Any block that
226 raises such an exception is not further processed."""
227
228
Armin Ronachere791c2a2008-04-07 18:39:54 +0200229class CodeGenerator(NodeVisitor):
230
Armin Ronacherfed44b52008-04-13 19:42:53 +0200231 def __init__(self, environment, filename, stream=None):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200232 if stream is None:
233 stream = StringIO()
Christoph Hack65642a52008-04-08 14:46:56 +0200234 self.environment = environment
Armin Ronachere791c2a2008-04-07 18:39:54 +0200235 self.filename = filename
236 self.stream = stream
Armin Ronacherfed44b52008-04-13 19:42:53 +0200237
238 # a registry for all blocks. Because blocks are moved out
239 # into the global python scope they are registered here
Armin Ronachere791c2a2008-04-07 18:39:54 +0200240 self.blocks = {}
Armin Ronacherfed44b52008-04-13 19:42:53 +0200241
242 # the number of extends statements so far
Armin Ronacher7fb38972008-04-11 13:54:28 +0200243 self.extends_so_far = 0
Armin Ronacherfed44b52008-04-13 19:42:53 +0200244
245 # some templates have a rootlevel extends. In this case we
246 # can safely assume that we're a child template and do some
247 # more optimizations.
Armin Ronacher75cfb862008-04-11 13:47:22 +0200248 self.has_known_extends = False
Armin Ronacherfed44b52008-04-13 19:42:53 +0200249
Armin Ronacherba3757b2008-04-16 19:43:16 +0200250 # the current line number
251 self.lineno = 1
252
253 # the debug information
254 self.debug_info = []
255
Armin Ronacherfed44b52008-04-13 19:42:53 +0200256 # the number of new lines before the next write()
257 self._new_lines = 0
258
259 # the line number of the last written statement
Armin Ronachere791c2a2008-04-07 18:39:54 +0200260 self._last_line = 0
Armin Ronacherfed44b52008-04-13 19:42:53 +0200261
262 # true if nothing was written so far.
Armin Ronachere791c2a2008-04-07 18:39:54 +0200263 self._first_write = True
264
Armin Ronacherfed44b52008-04-13 19:42:53 +0200265 # used by the `temporary_identifier` method to get new
266 # unique, temporary identifier
267 self._last_identifier = 0
268
269 # the current indentation
270 self._indentation = 0
271
Armin Ronachere791c2a2008-04-07 18:39:54 +0200272 def temporary_identifier(self):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200273 """Get a new unique identifier."""
274 self._last_identifier += 1
275 return 't%d' % self._last_identifier
Armin Ronachere791c2a2008-04-07 18:39:54 +0200276
277 def indent(self):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200278 """Indent by one."""
279 self._indentation += 1
Armin Ronachere791c2a2008-04-07 18:39:54 +0200280
Armin Ronacher8efc5222008-04-08 14:47:40 +0200281 def outdent(self, step=1):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200282 """Outdent by step."""
283 self._indentation -= step
Armin Ronachere791c2a2008-04-07 18:39:54 +0200284
Armin Ronacher625215e2008-04-13 16:31:08 +0200285 def blockvisit(self, nodes, frame, indent=True, force_generator=True):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200286 """Visit a list of nodes as block in a frame. Per default the
287 code is indented, but this can be disabled by setting the indent
288 parameter to False. If the current frame is no buffer a dummy
289 ``if 0: yield None`` is written automatically unless the
290 force_generator parameter is set to False.
291 """
Armin Ronacher625215e2008-04-13 16:31:08 +0200292 if indent:
293 self.indent()
294 if frame.buffer is None and force_generator:
Armin Ronachere791c2a2008-04-07 18:39:54 +0200295 self.writeline('if 0: yield None')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200296 try:
297 for node in nodes:
298 self.visit(node, frame)
299 except CompilerExit:
300 pass
Armin Ronacher625215e2008-04-13 16:31:08 +0200301 if indent:
302 self.outdent()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200303
304 def write(self, x):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200305 """Write a string into the output stream."""
306 if self._new_lines:
Armin Ronachere791c2a2008-04-07 18:39:54 +0200307 if not self._first_write:
Armin Ronacherfed44b52008-04-13 19:42:53 +0200308 self.stream.write('\n' * self._new_lines)
Armin Ronacherba3757b2008-04-16 19:43:16 +0200309 self.lineno += self._new_lines
Armin Ronachere791c2a2008-04-07 18:39:54 +0200310 self._first_write = False
Armin Ronacherfed44b52008-04-13 19:42:53 +0200311 self.stream.write(' ' * self._indentation)
312 self._new_lines = 0
Armin Ronachere791c2a2008-04-07 18:39:54 +0200313 self.stream.write(x)
314
315 def writeline(self, x, node=None, extra=0):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200316 """Combination of newline and write."""
Armin Ronachere791c2a2008-04-07 18:39:54 +0200317 self.newline(node, extra)
318 self.write(x)
319
320 def newline(self, node=None, extra=0):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200321 """Add one or more newlines before the next write."""
322 self._new_lines = max(self._new_lines, 1 + extra)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200323 if node is not None and node.lineno != self._last_line:
Armin Ronacherba3757b2008-04-16 19:43:16 +0200324 self.debug_info.append((node.lineno, self.lineno))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200325
Armin Ronacher71082072008-04-12 14:19:36 +0200326 def signature(self, node, frame, have_comma=True, extra_kwargs=None):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200327 """Writes a function call to the stream for the current node.
328 Per default it will write a leading comma but this can be
329 disabled by setting have_comma to False. If extra_kwargs is
330 given it must be a string that represents a single keyword
331 argument call that is inserted at the end of the regular
332 keyword argument calls.
333 """
Armin Ronacher8efc5222008-04-08 14:47:40 +0200334 have_comma = have_comma and [True] or []
335 def touch_comma():
336 if have_comma:
337 self.write(', ')
338 else:
339 have_comma.append(True)
340
341 for arg in node.args:
342 touch_comma()
343 self.visit(arg, frame)
344 for kwarg in node.kwargs:
345 touch_comma()
346 self.visit(kwarg, frame)
Armin Ronacher71082072008-04-12 14:19:36 +0200347 if extra_kwargs is not None:
348 touch_comma()
349 self.write(extra_kwargs)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200350 if node.dyn_args:
351 touch_comma()
Armin Ronacher71082072008-04-12 14:19:36 +0200352 self.write('*')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200353 self.visit(node.dyn_args, frame)
354 if node.dyn_kwargs:
355 touch_comma()
Armin Ronacher71082072008-04-12 14:19:36 +0200356 self.write('**')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200357 self.visit(node.dyn_kwargs, frame)
358
Armin Ronacher625215e2008-04-13 16:31:08 +0200359 def pull_locals(self, frame, indent=True):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200360 """Pull all the references identifiers into the local scope.
361 This affects regular names, filters and tests. If indent is
362 set to False, no automatic indentation will take place.
363 """
Armin Ronacher625215e2008-04-13 16:31:08 +0200364 if indent:
Armin Ronachere791c2a2008-04-07 18:39:54 +0200365 self.indent()
366 for name in frame.identifiers.undeclared:
367 self.writeline('l_%s = context[%r]' % (name, name))
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200368 for name in frame.identifiers.filters:
369 self.writeline('f_%s = environment.filters[%r]' % (name, name))
Armin Ronacherf059ec12008-04-11 22:21:00 +0200370 for name in frame.identifiers.tests:
371 self.writeline('t_%s = environment.tests[%r]' % (name, name))
Armin Ronacher625215e2008-04-13 16:31:08 +0200372 if indent:
Armin Ronachere791c2a2008-04-07 18:39:54 +0200373 self.outdent()
374
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200375 def collect_shadowed(self, frame):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200376 """This function returns all the shadowed variables in a dict
377 in the form name: alias and will write the required assignments
378 into the current scope. No indentation takes place.
379 """
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200380 # make sure we "backup" overridden, local identifiers
381 # TODO: we should probably optimize this and check if the
382 # identifier is in use afterwards.
383 aliases = {}
384 for name in frame.identifiers.find_shadowed():
385 aliases[name] = ident = self.temporary_identifier()
386 self.writeline('%s = l_%s' % (ident, name))
387 return aliases
388
Armin Ronacher71082072008-04-12 14:19:36 +0200389 def function_scoping(self, node, frame):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200390 """In Jinja a few statements require the help of anonymous
391 functions. Those are currently macros and call blocks and in
392 the future also recursive loops. As there is currently
393 technical limitation that doesn't allow reading and writing a
394 variable in a scope where the initial value is coming from an
395 outer scope, this function tries to fall back with a common
396 error message. Additionally the frame passed is modified so
397 that the argumetns are collected and callers are looked up.
398
399 This will return the modified frame.
400 """
Armin Ronacher71082072008-04-12 14:19:36 +0200401 func_frame = frame.inner()
402 func_frame.inspect(node.iter_child_nodes(), hard_scope=True)
403
404 # variables that are undeclared (accessed before declaration) and
405 # declared locally *and* part of an outside scope raise a template
406 # assertion error. Reason: we can't generate reasonable code from
407 # it without aliasing all the variables. XXX: alias them ^^
408 overriden_closure_vars = (
409 func_frame.identifiers.undeclared &
410 func_frame.identifiers.declared &
411 (func_frame.identifiers.declared_locally |
412 func_frame.identifiers.declared_parameter)
413 )
414 if overriden_closure_vars:
415 vars = ', '.join(sorted(overriden_closure_vars))
416 raise TemplateAssertionError('It\'s not possible to set and '
417 'access variables derived from '
418 'an outer scope! (affects: %s' %
419 vars, node.lineno, self.filename)
420
421 # remove variables from a closure from the frame's undeclared
422 # identifiers.
423 func_frame.identifiers.undeclared -= (
424 func_frame.identifiers.undeclared &
425 func_frame.identifiers.declared
426 )
427
428 func_frame.accesses_arguments = False
429 func_frame.accesses_caller = False
430 func_frame.arguments = args = ['l_' + x.name for x in node.args]
431
432 if 'arguments' in func_frame.identifiers.undeclared:
433 func_frame.accesses_arguments = True
434 func_frame.identifiers.add_special('arguments')
435 args.append('l_arguments')
436 if 'caller' in func_frame.identifiers.undeclared:
437 func_frame.accesses_caller = True
438 func_frame.identifiers.add_special('caller')
439 args.append('l_caller')
440 return func_frame
441
Armin Ronachere791c2a2008-04-07 18:39:54 +0200442 # -- Visitors
443
444 def visit_Template(self, node, frame=None):
445 assert frame is None, 'no root frame allowed'
446 self.writeline('from jinja2.runtime import *')
Armin Ronacherba3757b2008-04-16 19:43:16 +0200447 self.writeline('name = %r' % self.filename)
Armin Ronacher8edbe492008-04-10 20:43:43 +0200448
Armin Ronacher75cfb862008-04-11 13:47:22 +0200449 # do we have an extends tag at all? If not, we can save some
450 # overhead by just not processing any inheritance code.
451 have_extends = node.find(nodes.Extends) is not None
452
Armin Ronacher8edbe492008-04-10 20:43:43 +0200453 # find all blocks
454 for block in node.find_all(nodes.Block):
455 if block.name in self.blocks:
456 raise TemplateAssertionError('block %r defined twice' %
457 block.name, block.lineno,
458 self.filename)
459 self.blocks[block.name] = block
Armin Ronachere791c2a2008-04-07 18:39:54 +0200460
Armin Ronacher8efc5222008-04-08 14:47:40 +0200461 # generate the root render function.
Armin Ronacherf059ec12008-04-11 22:21:00 +0200462 self.writeline('def root(globals, environment=environment'
463 ', standalone=False):', extra=1)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200464 self.indent()
Armin Ronacherc63243e2008-04-14 22:53:58 +0200465 self.writeline('context = TemplateContext(environment, globals, %r, '
466 'blocks, standalone)' % self.filename)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200467 if have_extends:
468 self.writeline('parent_root = None')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200469 self.outdent()
Armin Ronacher75cfb862008-04-11 13:47:22 +0200470
471 # process the root
Armin Ronachere791c2a2008-04-07 18:39:54 +0200472 frame = Frame()
473 frame.inspect(node.body)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200474 frame.toplevel = frame.rootlevel = True
Armin Ronacherf059ec12008-04-11 22:21:00 +0200475 self.indent()
Armin Ronacher625215e2008-04-13 16:31:08 +0200476 self.pull_locals(frame, indent=False)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200477 self.writeline('yield context')
Armin Ronacher625215e2008-04-13 16:31:08 +0200478 self.blockvisit(node.body, frame, indent=False)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200479 self.outdent()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200480
Armin Ronacher8efc5222008-04-08 14:47:40 +0200481 # make sure that the parent root is called.
Armin Ronacher75cfb862008-04-11 13:47:22 +0200482 if have_extends:
483 if not self.has_known_extends:
484 self.indent()
485 self.writeline('if parent_root is not None:')
486 self.indent()
Armin Ronacherf059ec12008-04-11 22:21:00 +0200487 self.writeline('stream = parent_root(context)')
488 self.writeline('stream.next()')
489 self.writeline('for event in stream:')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200490 self.indent()
491 self.writeline('yield event')
Armin Ronacher7a52df82008-04-11 13:58:22 +0200492 self.outdent(1 + self.has_known_extends)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200493
494 # at this point we now have the blocks collected and can visit them too.
495 for name, block in self.blocks.iteritems():
496 block_frame = Frame()
497 block_frame.inspect(block.body)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200498 block_frame.block = name
Armin Ronacher62f8a292008-04-13 23:18:05 +0200499 block_frame.identifiers.add_special('super')
500 block_frame.name_overrides['super'] = 'context.super(%r)' % name
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200501 self.writeline('def block_%s(context, environment=environment):'
502 % name, block, 1)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200503 self.pull_locals(block_frame)
Armin Ronacher625215e2008-04-13 16:31:08 +0200504 self.blockvisit(block.body, block_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200505
Armin Ronacher75cfb862008-04-11 13:47:22 +0200506 self.writeline('blocks = {%s}' % ', '.join('%r: block_%s' % (x, x)
Armin Ronacherba3757b2008-04-16 19:43:16 +0200507 for x in self.blocks),
508 extra=1)
509
510 # add a function that returns the debug info
511 self.writeline('def get_debug_info():', extra=1)
512 self.indent()
513 self.writeline('return %r' % self.debug_info)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200514
Armin Ronachere791c2a2008-04-07 18:39:54 +0200515 def visit_Block(self, node, frame):
516 """Call a block and register it for the template."""
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200517 level = 1
Armin Ronacher75cfb862008-04-11 13:47:22 +0200518 if frame.toplevel:
Armin Ronacherbcb7c532008-04-11 16:30:34 +0200519 # if we know that we are a child template, there is no need to
520 # check if we are one
521 if self.has_known_extends:
522 return
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200523 if self.extends_so_far > 0:
524 self.writeline('if parent_root is None:')
525 self.indent()
526 level += 1
527 self.writeline('for event in context.blocks[%r][-1](context):' % node.name)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200528 self.indent()
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200529 if frame.buffer is None:
530 self.writeline('yield event')
531 else:
532 self.writeline('%s.append(event)' % frame.buffer)
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200533 self.outdent(level)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200534
535 def visit_Extends(self, node, frame):
536 """Calls the extender."""
Armin Ronacher8efc5222008-04-08 14:47:40 +0200537 if not frame.toplevel:
538 raise TemplateAssertionError('cannot use extend from a non '
539 'top-level scope', node.lineno,
540 self.filename)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200541
Armin Ronacher7fb38972008-04-11 13:54:28 +0200542 # if the number of extends statements in general is zero so
543 # far, we don't have to add a check if something extended
544 # the template before this one.
545 if self.extends_so_far > 0:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200546
Armin Ronacher7fb38972008-04-11 13:54:28 +0200547 # if we have a known extends we just add a template runtime
548 # error into the generated code. We could catch that at compile
549 # time too, but i welcome it not to confuse users by throwing the
550 # same error at different times just "because we can".
551 if not self.has_known_extends:
552 self.writeline('if parent_root is not None:')
553 self.indent()
554 self.writeline('raise TemplateRuntimeError(%r)' %
555 'extended multiple times')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200556
Armin Ronacher7fb38972008-04-11 13:54:28 +0200557 # if we have a known extends already we don't need that code here
558 # as we know that the template execution will end here.
559 if self.has_known_extends:
560 raise CompilerExit()
561 self.outdent()
562
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200563 self.writeline('parent_root = environment.get_template(', node, 1)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200564 self.visit(node.template, frame)
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200565 self.write(', %r).root_render_func' % self.filename)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200566
567 # if this extends statement was in the root level we can take
568 # advantage of that information and simplify the generated code
569 # in the top level from this point onwards
570 self.has_known_extends = True
Armin Ronachere791c2a2008-04-07 18:39:54 +0200571
Armin Ronacher7fb38972008-04-11 13:54:28 +0200572 # and now we have one more
573 self.extends_so_far += 1
574
Armin Ronacherf059ec12008-04-11 22:21:00 +0200575 def visit_Include(self, node, frame):
576 """Handles includes."""
577 # simpled include is include into a variable. This kind of
578 # include works the same on every level, so we handle it first.
579 if node.target is not None:
580 self.writeline('l_%s = ' % node.target, node)
581 if frame.toplevel:
582 self.write('context[%r] = ' % node.target)
583 self.write('IncludedTemplate(environment, context, ')
584 self.visit(node.template, frame)
585 self.write(')')
586 return
587
588 self.writeline('included_stream = environment.get_template(', node)
589 self.visit(node.template, frame)
590 self.write(').root_render_func(context, standalone=True)')
591 self.writeline('included_context = included_stream.next()')
592 self.writeline('for event in included_stream:')
593 self.indent()
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200594 if frame.buffer is None:
595 self.writeline('yield event')
596 else:
597 self.writeline('%s.append(event)' % frame.buffer)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200598 self.outdent()
599
600 # if we have a toplevel include the exported variables are copied
601 # into the current context without exporting them. context.udpate
602 # does *not* mark the variables as exported
603 if frame.toplevel:
604 self.writeline('context.update(included_context.get_exported())')
605
Armin Ronachere791c2a2008-04-07 18:39:54 +0200606 def visit_For(self, node, frame):
607 loop_frame = frame.inner()
608 loop_frame.inspect(node.iter_child_nodes())
Armin Ronachere791c2a2008-04-07 18:39:54 +0200609 extended_loop = bool(node.else_) or \
610 'loop' in loop_frame.identifiers.undeclared
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200611 if extended_loop:
612 loop_frame.identifiers.add_special('loop')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200613
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200614 aliases = self.collect_shadowed(loop_frame)
Armin Ronacher625215e2008-04-13 16:31:08 +0200615 self.pull_locals(loop_frame, indent=False)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200616 if node.else_:
617 self.writeline('l_loop = None')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200618
619 self.newline(node)
620 self.writeline('for ')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200621 self.visit(node.target, loop_frame)
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200622 self.write(extended_loop and ', l_loop in LoopContext(' or ' in ')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200623
624 # the expression pointing to the parent loop. We make the
625 # undefined a bit more debug friendly at the same time.
626 parent_loop = 'loop' in aliases and aliases['loop'] \
Armin Ronacherc63243e2008-04-14 22:53:58 +0200627 or "environment.undefined('loop', extra=%r)" % \
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200628 'the filter section of a loop as well as the ' \
629 'else block doesn\'t have access to the special ' \
630 "'loop' variable of the current loop. Because " \
631 'there is no parent loop it\'s undefined.'
632
633 # if we have an extened loop and a node test, we filter in the
634 # "outer frame".
635 if extended_loop and node.test is not None:
636 self.write('(')
637 self.visit(node.target, loop_frame)
638 self.write(' for ')
639 self.visit(node.target, loop_frame)
640 self.write(' in ')
641 self.visit(node.iter, loop_frame)
642 self.write(' if (')
643 test_frame = loop_frame.copy()
644 test_frame.name_overrides['loop'] = parent_loop
645 self.visit(node.test, test_frame)
646 self.write('))')
647
648 else:
649 self.visit(node.iter, loop_frame)
650
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200651 if 'loop' in aliases:
652 self.write(', ' + aliases['loop'])
Armin Ronachere791c2a2008-04-07 18:39:54 +0200653 self.write(extended_loop and '):' or ':')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200654
655 # tests in not extended loops become a continue
656 if not extended_loop and node.test is not None:
657 self.indent()
658 self.writeline('if ')
659 self.visit(node.test)
660 self.write(':')
661 self.indent()
662 self.writeline('continue')
663 self.outdent(2)
664
Armin Ronacher625215e2008-04-13 16:31:08 +0200665 self.blockvisit(node.body, loop_frame, force_generator=False)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200666
667 if node.else_:
668 self.writeline('if l_loop is None:')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200669 self.indent()
670 self.writeline('l_loop = ' + parent_loop)
671 self.outdent()
Armin Ronacher625215e2008-04-13 16:31:08 +0200672 self.blockvisit(node.else_, loop_frame, force_generator=False)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200673
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200674 # reset the aliases if there are any.
Armin Ronachere791c2a2008-04-07 18:39:54 +0200675 for name, alias in aliases.iteritems():
Armin Ronacher8efc5222008-04-08 14:47:40 +0200676 self.writeline('l_%s = %s' % (name, alias))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200677
678 def visit_If(self, node, frame):
Armin Ronacher75cfb862008-04-11 13:47:22 +0200679 if_frame = frame.soft()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200680 self.writeline('if ', node)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200681 self.visit(node.test, if_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200682 self.write(':')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200683 self.blockvisit(node.body, if_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200684 if node.else_:
685 self.writeline('else:')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200686 self.blockvisit(node.else_, if_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200687
Armin Ronacher8efc5222008-04-08 14:47:40 +0200688 def visit_Macro(self, node, frame):
Armin Ronacher71082072008-04-12 14:19:36 +0200689 macro_frame = self.function_scoping(node, frame)
690 args = macro_frame.arguments
Armin Ronacher8efc5222008-04-08 14:47:40 +0200691 self.writeline('def macro(%s):' % ', '.join(args), node)
Armin Ronacher625215e2008-04-13 16:31:08 +0200692 macro_frame.buffer = buf = self.temporary_identifier()
693 self.indent()
694 self.pull_locals(macro_frame, indent=False)
695 self.writeline('%s = []' % buf)
696 self.blockvisit(node.body, macro_frame, indent=False)
697 self.writeline("return TemplateData(u''.join(%s))" % buf)
698 self.outdent()
Armin Ronacher8efc5222008-04-08 14:47:40 +0200699 self.newline()
700 if frame.toplevel:
701 self.write('context[%r] = ' % node.name)
702 arg_tuple = ', '.join(repr(x.name) for x in node.args)
703 if len(node.args) == 1:
704 arg_tuple += ','
Armin Ronacherc63243e2008-04-14 22:53:58 +0200705 self.write('l_%s = Macro(environment, macro, %r, (%s), (' %
706 (node.name, node.name, arg_tuple))
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200707 for arg in node.defaults:
Armin Ronacher625215e2008-04-13 16:31:08 +0200708 self.visit(arg, macro_frame)
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200709 self.write(', ')
Armin Ronacher00d5d212008-04-13 01:10:18 +0200710 self.write('), %s, %s)' % (
711 macro_frame.accesses_arguments and '1' or '0',
712 macro_frame.accesses_caller and '1' or '0'
Armin Ronacher71082072008-04-12 14:19:36 +0200713 ))
714
715 def visit_CallBlock(self, node, frame):
716 call_frame = self.function_scoping(node, frame)
717 args = call_frame.arguments
718 self.writeline('def call(%s):' % ', '.join(args), node)
Armin Ronacher625215e2008-04-13 16:31:08 +0200719 call_frame.buffer = buf = self.temporary_identifier()
720 self.indent()
721 self.pull_locals(call_frame, indent=False)
722 self.writeline('%s = []' % buf)
723 self.blockvisit(node.body, call_frame, indent=False)
724 self.writeline("return TemplateData(u''.join(%s))" % buf)
725 self.outdent()
Armin Ronacher71082072008-04-12 14:19:36 +0200726 arg_tuple = ', '.join(repr(x.name) for x in node.args)
727 if len(node.args) == 1:
728 arg_tuple += ','
Armin Ronacherc63243e2008-04-14 22:53:58 +0200729 self.writeline('caller = Macro(environment, call, None, (%s), (' %
730 arg_tuple)
Armin Ronacher71082072008-04-12 14:19:36 +0200731 for arg in node.defaults:
732 self.visit(arg)
733 self.write(', ')
Armin Ronacher00d5d212008-04-13 01:10:18 +0200734 self.write('), %s, 0)' % (call_frame.accesses_arguments and '1' or '0'))
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200735 if frame.buffer is None:
736 self.writeline('yield ', node)
737 else:
738 self.writeline('%s.append(' % frame.buffer, node)
Armin Ronacher71082072008-04-12 14:19:36 +0200739 self.visit_Call(node.call, call_frame, extra_kwargs='caller=caller')
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200740 if frame.buffer is not None:
741 self.write(')')
742
743 def visit_FilterBlock(self, node, frame):
744 filter_frame = frame.inner()
745 filter_frame.inspect(node.iter_child_nodes())
746
747 aliases = self.collect_shadowed(filter_frame)
Armin Ronacher625215e2008-04-13 16:31:08 +0200748 self.pull_locals(filter_frame, indent=False)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200749 filter_frame.buffer = buf = self.temporary_identifier()
750
751 self.writeline('%s = []' % buf, node)
752 for child in node.body:
753 self.visit(child, filter_frame)
754
755 if frame.buffer is None:
756 self.writeline('yield ', node)
757 else:
758 self.writeline('%s.append(' % frame.buffer, node)
759 self.visit_Filter(node.filter, filter_frame, "u''.join(%s)" % buf)
760 if frame.buffer is not None:
761 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200762
Armin Ronachere791c2a2008-04-07 18:39:54 +0200763 def visit_ExprStmt(self, node, frame):
764 self.newline(node)
765 self.visit(node, frame)
766
767 def visit_Output(self, node, frame):
Armin Ronacher75cfb862008-04-11 13:47:22 +0200768 # if we have a known extends statement, we don't output anything
Armin Ronacher7a52df82008-04-11 13:58:22 +0200769 if self.has_known_extends and frame.toplevel:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200770 return
Armin Ronachere791c2a2008-04-07 18:39:54 +0200771
Armin Ronacher75cfb862008-04-11 13:47:22 +0200772 self.newline(node)
Armin Ronacher8edbe492008-04-10 20:43:43 +0200773 if self.environment.finalize is unicode:
774 finalizer = 'unicode'
Armin Ronacherf059ec12008-04-11 22:21:00 +0200775 have_finalizer = False
Armin Ronacher8edbe492008-04-10 20:43:43 +0200776 else:
777 finalizer = 'context.finalize'
Armin Ronacherf059ec12008-04-11 22:21:00 +0200778 have_finalizer = False
Armin Ronacher8edbe492008-04-10 20:43:43 +0200779
Armin Ronacher7fb38972008-04-11 13:54:28 +0200780 # if we are in the toplevel scope and there was already an extends
781 # statement we have to add a check that disables our yield(s) here
782 # so that they don't appear in the output.
783 outdent_later = False
784 if frame.toplevel and self.extends_so_far != 0:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200785 self.writeline('if parent_root is None:')
786 self.indent()
Armin Ronacher7fb38972008-04-11 13:54:28 +0200787 outdent_later = True
Armin Ronacher75cfb862008-04-11 13:47:22 +0200788
Armin Ronachere791c2a2008-04-07 18:39:54 +0200789 # try to evaluate as many chunks as possible into a static
790 # string at compile time.
791 body = []
792 for child in node.nodes:
793 try:
794 const = unicode(child.as_const())
795 except:
796 body.append(child)
797 continue
798 if body and isinstance(body[-1], list):
799 body[-1].append(const)
800 else:
801 body.append([const])
802
803 # if we have less than 3 nodes we just yield them
804 if len(body) < 3:
805 for item in body:
806 if isinstance(item, list):
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200807 val = repr(u''.join(item))
808 if frame.buffer is None:
809 self.writeline('yield ' + val)
810 else:
811 self.writeline('%s.append(%s)' % (frame.buffer, val))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200812 else:
813 self.newline(item)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200814 if frame.buffer is None:
815 self.write('yield ')
816 else:
817 self.write('%s.append(' % frame.buffer)
818 self.write(finalizer + '(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200819 self.visit(item, frame)
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200820 self.write(')' * (1 + (frame.buffer is not None)))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200821
822 # otherwise we create a format string as this is faster in that case
823 else:
824 format = []
825 arguments = []
826 for item in body:
827 if isinstance(item, list):
828 format.append(u''.join(item).replace('%', '%%'))
829 else:
830 format.append('%s')
831 arguments.append(item)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200832 if frame.buffer is None:
833 self.writeline('yield ')
834 else:
835 self.writeline('%s.append(' % frame.buffer)
836 self.write(repr(u''.join(format)) + ' % (')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200837 idx = -1
838 for idx, argument in enumerate(arguments):
839 if idx:
840 self.write(', ')
Armin Ronacherf059ec12008-04-11 22:21:00 +0200841 if have_finalizer:
Armin Ronacher8edbe492008-04-10 20:43:43 +0200842 self.write('(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200843 self.visit(argument, frame)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200844 if have_finalizer:
Armin Ronacher8edbe492008-04-10 20:43:43 +0200845 self.write(')')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200846 self.write(idx == 0 and ',)' or ')')
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200847 if frame.buffer is not None:
848 self.write(')')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200849
Armin Ronacher7fb38972008-04-11 13:54:28 +0200850 if outdent_later:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200851 self.outdent()
852
Armin Ronacher8efc5222008-04-08 14:47:40 +0200853 def visit_Assign(self, node, frame):
854 self.newline(node)
855 # toplevel assignments however go into the local namespace and
856 # the current template's context. We create a copy of the frame
857 # here and add a set so that the Name visitor can add the assigned
858 # names here.
859 if frame.toplevel:
860 assignment_frame = frame.copy()
861 assignment_frame.assigned_names = set()
862 else:
863 assignment_frame = frame
864 self.visit(node.target, assignment_frame)
865 self.write(' = ')
866 self.visit(node.node, frame)
Armin Ronacher9706fab2008-04-08 18:49:56 +0200867
868 # make sure toplevel assignments are added to the context.
Armin Ronacher8efc5222008-04-08 14:47:40 +0200869 if frame.toplevel:
870 for name in assignment_frame.assigned_names:
871 self.writeline('context[%r] = l_%s' % (name, name))
872
Armin Ronachere791c2a2008-04-07 18:39:54 +0200873 def visit_Name(self, node, frame):
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200874 if node.ctx == 'store':
875 if frame.toplevel:
876 frame.assigned_names.add(node.name)
877 frame.name_overrides.pop(node.name, None)
878 elif node.ctx == 'load':
879 if node.name in frame.name_overrides:
880 self.write(frame.name_overrides[node.name])
881 return
Armin Ronachere791c2a2008-04-07 18:39:54 +0200882 self.write('l_' + node.name)
883
884 def visit_Const(self, node, frame):
885 val = node.value
886 if isinstance(val, float):
887 # XXX: add checks for infinity and nan
888 self.write(str(val))
889 else:
890 self.write(repr(val))
891
Armin Ronacher8efc5222008-04-08 14:47:40 +0200892 def visit_Tuple(self, node, frame):
893 self.write('(')
894 idx = -1
895 for idx, item in enumerate(node.items):
896 if idx:
897 self.write(', ')
898 self.visit(item, frame)
899 self.write(idx == 0 and ',)' or ')')
900
Armin Ronacher8edbe492008-04-10 20:43:43 +0200901 def visit_List(self, node, frame):
902 self.write('[')
903 for idx, item in enumerate(node.items):
904 if idx:
905 self.write(', ')
906 self.visit(item, frame)
907 self.write(']')
908
909 def visit_Dict(self, node, frame):
910 self.write('{')
911 for idx, item in enumerate(node.items):
912 if idx:
913 self.write(', ')
914 self.visit(item.key, frame)
915 self.write(': ')
916 self.visit(item.value, frame)
917 self.write('}')
918
Armin Ronachere791c2a2008-04-07 18:39:54 +0200919 def binop(operator):
920 def visitor(self, node, frame):
921 self.write('(')
922 self.visit(node.left, frame)
923 self.write(' %s ' % operator)
924 self.visit(node.right, frame)
925 self.write(')')
926 return visitor
927
928 def uaop(operator):
929 def visitor(self, node, frame):
930 self.write('(' + operator)
931 self.visit(node.node)
932 self.write(')')
933 return visitor
934
935 visit_Add = binop('+')
936 visit_Sub = binop('-')
937 visit_Mul = binop('*')
938 visit_Div = binop('/')
939 visit_FloorDiv = binop('//')
940 visit_Pow = binop('**')
941 visit_Mod = binop('%')
942 visit_And = binop('and')
943 visit_Or = binop('or')
944 visit_Pos = uaop('+')
945 visit_Neg = uaop('-')
946 visit_Not = uaop('not ')
947 del binop, uaop
948
949 def visit_Compare(self, node, frame):
950 self.visit(node.expr, frame)
951 for op in node.ops:
952 self.visit(op, frame)
953
954 def visit_Operand(self, node, frame):
955 self.write(' %s ' % operators[node.op])
956 self.visit(node.expr, frame)
957
958 def visit_Subscript(self, node, frame):
Armin Ronacher8efc5222008-04-08 14:47:40 +0200959 if isinstance(node.arg, nodes.Slice):
960 self.visit(node.node, frame)
961 self.write('[')
962 self.visit(node.arg, frame)
963 self.write(']')
964 return
965 try:
966 const = node.arg.as_const()
967 have_const = True
968 except nodes.Impossible:
969 have_const = False
970 if have_const:
971 if isinstance(const, (int, long, float)):
972 self.visit(node.node, frame)
973 self.write('[%s]' % const)
974 return
Armin Ronacherc63243e2008-04-14 22:53:58 +0200975 self.write('environment.subscribe(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200976 self.visit(node.node, frame)
977 self.write(', ')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200978 if have_const:
979 self.write(repr(const))
980 else:
981 self.visit(node.arg, frame)
Armin Ronacher4dfc9752008-04-09 15:03:29 +0200982 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200983
984 def visit_Slice(self, node, frame):
985 if node.start is not None:
986 self.visit(node.start, frame)
987 self.write(':')
988 if node.stop is not None:
989 self.visit(node.stop, frame)
990 if node.step is not None:
991 self.write(':')
992 self.visit(node.step, frame)
993
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200994 def visit_Filter(self, node, frame, initial=None):
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200995 self.write('f_%s(' % node.name)
Christoph Hack80909862008-04-14 01:35:10 +0200996 func = self.environment.filters.get(node.name)
997 if getattr(func, 'contextfilter', False):
998 self.write('context, ')
999 if isinstance(node.node, nodes.Filter):
1000 self.visit_Filter(node.node, frame, initial)
1001 elif node.node is None:
Armin Ronacherfa865fb2008-04-12 22:11:53 +02001002 self.write(initial)
1003 else:
Armin Ronacherfa865fb2008-04-12 22:11:53 +02001004 self.visit(node.node, frame)
Armin Ronacherd55ab532008-04-09 16:13:39 +02001005 self.signature(node, frame)
1006 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +02001007
1008 def visit_Test(self, node, frame):
Armin Ronacherf059ec12008-04-11 22:21:00 +02001009 self.write('t_%s(' % node.name)
1010 func = self.environment.tests.get(node.name)
1011 if getattr(func, 'contexttest', False):
1012 self.write('context, ')
Armin Ronacher8efc5222008-04-08 14:47:40 +02001013 self.visit(node.node, frame)
1014 self.signature(node, frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +02001015 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +02001016
Armin Ronacher3d8b7842008-04-13 13:16:50 +02001017 def visit_CondExpr(self, node, frame):
1018 if not have_condexpr:
1019 self.write('((')
1020 self.visit(node.test, frame)
1021 self.write(') and (')
1022 self.visit(node.expr1, frame)
1023 self.write(',) or (')
1024 self.visit(node.expr2, frame)
1025 self.write(',))[0]')
1026 else:
1027 self.write('(')
1028 self.visit(node.expr1, frame)
1029 self.write(' if ')
1030 self.visit(node.test, frame)
1031 self.write(' else ')
1032 self.visit(node.expr2, frame)
1033 self.write(')')
1034
Armin Ronacher71082072008-04-12 14:19:36 +02001035 def visit_Call(self, node, frame, extra_kwargs=None):
Armin Ronacherc63243e2008-04-14 22:53:58 +02001036 if self.environment.sandboxed:
1037 self.write('environment.call(')
Armin Ronacher8efc5222008-04-08 14:47:40 +02001038 self.visit(node.node, frame)
Armin Ronacherc63243e2008-04-14 22:53:58 +02001039 self.write(self.environment.sandboxed and ', ' or '(')
Armin Ronacher71082072008-04-12 14:19:36 +02001040 self.signature(node, frame, False, extra_kwargs)
Armin Ronacher8efc5222008-04-08 14:47:40 +02001041 self.write(')')
1042
1043 def visit_Keyword(self, node, frame):
Armin Ronacher2e9396b2008-04-16 14:21:57 +02001044 self.write(node.key + '=')
Armin Ronacher8efc5222008-04-08 14:47:40 +02001045 self.visit(node.value, frame)