blob: d1025d4d126b8a998cabc0e3022aa2a4df605657 [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
Armin Ronacher8e8d0712008-04-16 23:10:49 +020040def generate(node, environment, name, filename, stream=None):
Armin Ronacherbcb7c532008-04-11 16:30:34 +020041 """Generate the python source for a node tree."""
Armin Ronacher8e8d0712008-04-16 23:10:49 +020042 generator = CodeGenerator(environment, name, 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 Ronacher8e8d0712008-04-16 23:10:49 +0200231 def __init__(self, environment, name, 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 Ronacher8e8d0712008-04-16 23:10:49 +0200235 self.name = name
Armin Ronachere791c2a2008-04-07 18:39:54 +0200236 self.filename = filename
237 self.stream = stream
Armin Ronacherfed44b52008-04-13 19:42:53 +0200238
239 # a registry for all blocks. Because blocks are moved out
240 # into the global python scope they are registered here
Armin Ronachere791c2a2008-04-07 18:39:54 +0200241 self.blocks = {}
Armin Ronacherfed44b52008-04-13 19:42:53 +0200242
243 # the number of extends statements so far
Armin Ronacher7fb38972008-04-11 13:54:28 +0200244 self.extends_so_far = 0
Armin Ronacherfed44b52008-04-13 19:42:53 +0200245
246 # some templates have a rootlevel extends. In this case we
247 # can safely assume that we're a child template and do some
248 # more optimizations.
Armin Ronacher75cfb862008-04-11 13:47:22 +0200249 self.has_known_extends = False
Armin Ronacherfed44b52008-04-13 19:42:53 +0200250
Armin Ronacherba3757b2008-04-16 19:43:16 +0200251 # the current line number
Armin Ronacher8e8d0712008-04-16 23:10:49 +0200252 self.code_lineno = 1
Armin Ronacherba3757b2008-04-16 19:43:16 +0200253
254 # the debug information
255 self.debug_info = []
Armin Ronacher8e8d0712008-04-16 23:10:49 +0200256 self._write_debug_info = None
Armin Ronacherba3757b2008-04-16 19:43:16 +0200257
Armin Ronacherfed44b52008-04-13 19:42:53 +0200258 # the number of new lines before the next write()
259 self._new_lines = 0
260
261 # the line number of the last written statement
Armin Ronachere791c2a2008-04-07 18:39:54 +0200262 self._last_line = 0
Armin Ronacherfed44b52008-04-13 19:42:53 +0200263
264 # true if nothing was written so far.
Armin Ronachere791c2a2008-04-07 18:39:54 +0200265 self._first_write = True
266
Armin Ronacherfed44b52008-04-13 19:42:53 +0200267 # used by the `temporary_identifier` method to get new
268 # unique, temporary identifier
269 self._last_identifier = 0
270
271 # the current indentation
272 self._indentation = 0
273
Armin Ronachere791c2a2008-04-07 18:39:54 +0200274 def temporary_identifier(self):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200275 """Get a new unique identifier."""
276 self._last_identifier += 1
277 return 't%d' % self._last_identifier
Armin Ronachere791c2a2008-04-07 18:39:54 +0200278
279 def indent(self):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200280 """Indent by one."""
281 self._indentation += 1
Armin Ronachere791c2a2008-04-07 18:39:54 +0200282
Armin Ronacher8efc5222008-04-08 14:47:40 +0200283 def outdent(self, step=1):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200284 """Outdent by step."""
285 self._indentation -= step
Armin Ronachere791c2a2008-04-07 18:39:54 +0200286
Armin Ronacher625215e2008-04-13 16:31:08 +0200287 def blockvisit(self, nodes, frame, indent=True, force_generator=True):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200288 """Visit a list of nodes as block in a frame. Per default the
289 code is indented, but this can be disabled by setting the indent
290 parameter to False. If the current frame is no buffer a dummy
291 ``if 0: yield None`` is written automatically unless the
292 force_generator parameter is set to False.
293 """
Armin Ronacher625215e2008-04-13 16:31:08 +0200294 if indent:
295 self.indent()
296 if frame.buffer is None and force_generator:
Armin Ronachere791c2a2008-04-07 18:39:54 +0200297 self.writeline('if 0: yield None')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200298 try:
299 for node in nodes:
300 self.visit(node, frame)
301 except CompilerExit:
302 pass
Armin Ronacher625215e2008-04-13 16:31:08 +0200303 if indent:
304 self.outdent()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200305
306 def write(self, x):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200307 """Write a string into the output stream."""
308 if self._new_lines:
Armin Ronachere791c2a2008-04-07 18:39:54 +0200309 if not self._first_write:
Armin Ronacherfed44b52008-04-13 19:42:53 +0200310 self.stream.write('\n' * self._new_lines)
Armin Ronacher8e8d0712008-04-16 23:10:49 +0200311 self.code_lineno += self._new_lines
312 if self._write_debug_info is not None:
313 self.debug_info.append((self._write_debug_info,
314 self.code_lineno))
315 self._write_debug_info = None
Armin Ronachere791c2a2008-04-07 18:39:54 +0200316 self._first_write = False
Armin Ronacherfed44b52008-04-13 19:42:53 +0200317 self.stream.write(' ' * self._indentation)
318 self._new_lines = 0
Armin Ronachere791c2a2008-04-07 18:39:54 +0200319 self.stream.write(x)
320
321 def writeline(self, x, node=None, extra=0):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200322 """Combination of newline and write."""
Armin Ronachere791c2a2008-04-07 18:39:54 +0200323 self.newline(node, extra)
324 self.write(x)
325
326 def newline(self, node=None, extra=0):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200327 """Add one or more newlines before the next write."""
328 self._new_lines = max(self._new_lines, 1 + extra)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200329 if node is not None and node.lineno != self._last_line:
Armin Ronacher8e8d0712008-04-16 23:10:49 +0200330 self._write_debug_info = node.lineno
331 self._last_line = node.lineno
Armin Ronachere791c2a2008-04-07 18:39:54 +0200332
Armin Ronacher71082072008-04-12 14:19:36 +0200333 def signature(self, node, frame, have_comma=True, extra_kwargs=None):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200334 """Writes a function call to the stream for the current node.
335 Per default it will write a leading comma but this can be
336 disabled by setting have_comma to False. If extra_kwargs is
337 given it must be a string that represents a single keyword
338 argument call that is inserted at the end of the regular
339 keyword argument calls.
340 """
Armin Ronacher8efc5222008-04-08 14:47:40 +0200341 have_comma = have_comma and [True] or []
342 def touch_comma():
343 if have_comma:
344 self.write(', ')
345 else:
346 have_comma.append(True)
347
348 for arg in node.args:
349 touch_comma()
350 self.visit(arg, frame)
351 for kwarg in node.kwargs:
352 touch_comma()
353 self.visit(kwarg, frame)
Armin Ronacher71082072008-04-12 14:19:36 +0200354 if extra_kwargs is not None:
355 touch_comma()
356 self.write(extra_kwargs)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200357 if node.dyn_args:
358 touch_comma()
Armin Ronacher71082072008-04-12 14:19:36 +0200359 self.write('*')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200360 self.visit(node.dyn_args, frame)
361 if node.dyn_kwargs:
362 touch_comma()
Armin Ronacher71082072008-04-12 14:19:36 +0200363 self.write('**')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200364 self.visit(node.dyn_kwargs, frame)
365
Armin Ronacher625215e2008-04-13 16:31:08 +0200366 def pull_locals(self, frame, indent=True):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200367 """Pull all the references identifiers into the local scope.
368 This affects regular names, filters and tests. If indent is
369 set to False, no automatic indentation will take place.
370 """
Armin Ronacher625215e2008-04-13 16:31:08 +0200371 if indent:
Armin Ronachere791c2a2008-04-07 18:39:54 +0200372 self.indent()
373 for name in frame.identifiers.undeclared:
374 self.writeline('l_%s = context[%r]' % (name, name))
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200375 for name in frame.identifiers.filters:
376 self.writeline('f_%s = environment.filters[%r]' % (name, name))
Armin Ronacherf059ec12008-04-11 22:21:00 +0200377 for name in frame.identifiers.tests:
378 self.writeline('t_%s = environment.tests[%r]' % (name, name))
Armin Ronacher625215e2008-04-13 16:31:08 +0200379 if indent:
Armin Ronachere791c2a2008-04-07 18:39:54 +0200380 self.outdent()
381
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200382 def collect_shadowed(self, frame):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200383 """This function returns all the shadowed variables in a dict
384 in the form name: alias and will write the required assignments
385 into the current scope. No indentation takes place.
386 """
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200387 # make sure we "backup" overridden, local identifiers
388 # TODO: we should probably optimize this and check if the
389 # identifier is in use afterwards.
390 aliases = {}
391 for name in frame.identifiers.find_shadowed():
392 aliases[name] = ident = self.temporary_identifier()
393 self.writeline('%s = l_%s' % (ident, name))
394 return aliases
395
Armin Ronacher71082072008-04-12 14:19:36 +0200396 def function_scoping(self, node, frame):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200397 """In Jinja a few statements require the help of anonymous
398 functions. Those are currently macros and call blocks and in
399 the future also recursive loops. As there is currently
400 technical limitation that doesn't allow reading and writing a
401 variable in a scope where the initial value is coming from an
402 outer scope, this function tries to fall back with a common
403 error message. Additionally the frame passed is modified so
404 that the argumetns are collected and callers are looked up.
405
406 This will return the modified frame.
407 """
Armin Ronacher71082072008-04-12 14:19:36 +0200408 func_frame = frame.inner()
409 func_frame.inspect(node.iter_child_nodes(), hard_scope=True)
410
411 # variables that are undeclared (accessed before declaration) and
412 # declared locally *and* part of an outside scope raise a template
413 # assertion error. Reason: we can't generate reasonable code from
414 # it without aliasing all the variables. XXX: alias them ^^
415 overriden_closure_vars = (
416 func_frame.identifiers.undeclared &
417 func_frame.identifiers.declared &
418 (func_frame.identifiers.declared_locally |
419 func_frame.identifiers.declared_parameter)
420 )
421 if overriden_closure_vars:
422 vars = ', '.join(sorted(overriden_closure_vars))
423 raise TemplateAssertionError('It\'s not possible to set and '
424 'access variables derived from '
425 'an outer scope! (affects: %s' %
426 vars, node.lineno, self.filename)
427
428 # remove variables from a closure from the frame's undeclared
429 # identifiers.
430 func_frame.identifiers.undeclared -= (
431 func_frame.identifiers.undeclared &
432 func_frame.identifiers.declared
433 )
434
435 func_frame.accesses_arguments = False
436 func_frame.accesses_caller = False
437 func_frame.arguments = args = ['l_' + x.name for x in node.args]
438
439 if 'arguments' in func_frame.identifiers.undeclared:
440 func_frame.accesses_arguments = True
441 func_frame.identifiers.add_special('arguments')
442 args.append('l_arguments')
443 if 'caller' in func_frame.identifiers.undeclared:
444 func_frame.accesses_caller = True
445 func_frame.identifiers.add_special('caller')
446 args.append('l_caller')
447 return func_frame
448
Armin Ronachere791c2a2008-04-07 18:39:54 +0200449 # -- Visitors
450
451 def visit_Template(self, node, frame=None):
452 assert frame is None, 'no root frame allowed'
453 self.writeline('from jinja2.runtime import *')
Armin Ronacher8e8d0712008-04-16 23:10:49 +0200454 self.writeline('name = %r' % self.name)
Armin Ronacher8edbe492008-04-10 20:43:43 +0200455
Armin Ronacher75cfb862008-04-11 13:47:22 +0200456 # do we have an extends tag at all? If not, we can save some
457 # overhead by just not processing any inheritance code.
458 have_extends = node.find(nodes.Extends) is not None
459
Armin Ronacher8edbe492008-04-10 20:43:43 +0200460 # find all blocks
461 for block in node.find_all(nodes.Block):
462 if block.name in self.blocks:
463 raise TemplateAssertionError('block %r defined twice' %
464 block.name, block.lineno,
465 self.filename)
466 self.blocks[block.name] = block
Armin Ronachere791c2a2008-04-07 18:39:54 +0200467
Armin Ronacher8efc5222008-04-08 14:47:40 +0200468 # generate the root render function.
Armin Ronacherf059ec12008-04-11 22:21:00 +0200469 self.writeline('def root(globals, environment=environment'
470 ', standalone=False):', extra=1)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200471 self.indent()
Armin Ronacherc63243e2008-04-14 22:53:58 +0200472 self.writeline('context = TemplateContext(environment, globals, %r, '
Armin Ronacher8e8d0712008-04-16 23:10:49 +0200473 'blocks, standalone)' % self.name)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200474 if have_extends:
475 self.writeline('parent_root = None')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200476 self.outdent()
Armin Ronacher75cfb862008-04-11 13:47:22 +0200477
478 # process the root
Armin Ronachere791c2a2008-04-07 18:39:54 +0200479 frame = Frame()
480 frame.inspect(node.body)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200481 frame.toplevel = frame.rootlevel = True
Armin Ronacherf059ec12008-04-11 22:21:00 +0200482 self.indent()
Armin Ronacher625215e2008-04-13 16:31:08 +0200483 self.pull_locals(frame, indent=False)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200484 self.writeline('yield context')
Armin Ronacher625215e2008-04-13 16:31:08 +0200485 self.blockvisit(node.body, frame, indent=False)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200486 self.outdent()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200487
Armin Ronacher8efc5222008-04-08 14:47:40 +0200488 # make sure that the parent root is called.
Armin Ronacher75cfb862008-04-11 13:47:22 +0200489 if have_extends:
490 if not self.has_known_extends:
491 self.indent()
492 self.writeline('if parent_root is not None:')
493 self.indent()
Armin Ronacherf059ec12008-04-11 22:21:00 +0200494 self.writeline('stream = parent_root(context)')
495 self.writeline('stream.next()')
496 self.writeline('for event in stream:')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200497 self.indent()
498 self.writeline('yield event')
Armin Ronacher7a52df82008-04-11 13:58:22 +0200499 self.outdent(1 + self.has_known_extends)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200500
501 # at this point we now have the blocks collected and can visit them too.
502 for name, block in self.blocks.iteritems():
503 block_frame = Frame()
504 block_frame.inspect(block.body)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200505 block_frame.block = name
Armin Ronacher62f8a292008-04-13 23:18:05 +0200506 block_frame.identifiers.add_special('super')
507 block_frame.name_overrides['super'] = 'context.super(%r)' % name
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200508 self.writeline('def block_%s(context, environment=environment):'
509 % name, block, 1)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200510 self.pull_locals(block_frame)
Armin Ronacher625215e2008-04-13 16:31:08 +0200511 self.blockvisit(block.body, block_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200512
Armin Ronacher75cfb862008-04-11 13:47:22 +0200513 self.writeline('blocks = {%s}' % ', '.join('%r: block_%s' % (x, x)
Armin Ronacherba3757b2008-04-16 19:43:16 +0200514 for x in self.blocks),
515 extra=1)
516
517 # add a function that returns the debug info
518 self.writeline('def get_debug_info():', extra=1)
519 self.indent()
520 self.writeline('return %r' % self.debug_info)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200521
Armin Ronachere791c2a2008-04-07 18:39:54 +0200522 def visit_Block(self, node, frame):
523 """Call a block and register it for the template."""
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200524 level = 1
Armin Ronacher75cfb862008-04-11 13:47:22 +0200525 if frame.toplevel:
Armin Ronacherbcb7c532008-04-11 16:30:34 +0200526 # if we know that we are a child template, there is no need to
527 # check if we are one
528 if self.has_known_extends:
529 return
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200530 if self.extends_so_far > 0:
531 self.writeline('if parent_root is None:')
532 self.indent()
533 level += 1
534 self.writeline('for event in context.blocks[%r][-1](context):' % node.name)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200535 self.indent()
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200536 if frame.buffer is None:
537 self.writeline('yield event')
538 else:
539 self.writeline('%s.append(event)' % frame.buffer)
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200540 self.outdent(level)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200541
542 def visit_Extends(self, node, frame):
543 """Calls the extender."""
Armin Ronacher8efc5222008-04-08 14:47:40 +0200544 if not frame.toplevel:
545 raise TemplateAssertionError('cannot use extend from a non '
546 'top-level scope', node.lineno,
547 self.filename)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200548
Armin Ronacher7fb38972008-04-11 13:54:28 +0200549 # if the number of extends statements in general is zero so
550 # far, we don't have to add a check if something extended
551 # the template before this one.
552 if self.extends_so_far > 0:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200553
Armin Ronacher7fb38972008-04-11 13:54:28 +0200554 # if we have a known extends we just add a template runtime
555 # error into the generated code. We could catch that at compile
556 # time too, but i welcome it not to confuse users by throwing the
557 # same error at different times just "because we can".
558 if not self.has_known_extends:
559 self.writeline('if parent_root is not None:')
560 self.indent()
561 self.writeline('raise TemplateRuntimeError(%r)' %
562 'extended multiple times')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200563
Armin Ronacher7fb38972008-04-11 13:54:28 +0200564 # if we have a known extends already we don't need that code here
565 # as we know that the template execution will end here.
566 if self.has_known_extends:
567 raise CompilerExit()
568 self.outdent()
569
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200570 self.writeline('parent_root = environment.get_template(', node, 1)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200571 self.visit(node.template, frame)
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200572 self.write(', %r).root_render_func' % self.filename)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200573
574 # if this extends statement was in the root level we can take
575 # advantage of that information and simplify the generated code
576 # in the top level from this point onwards
577 self.has_known_extends = True
Armin Ronachere791c2a2008-04-07 18:39:54 +0200578
Armin Ronacher7fb38972008-04-11 13:54:28 +0200579 # and now we have one more
580 self.extends_so_far += 1
581
Armin Ronacherf059ec12008-04-11 22:21:00 +0200582 def visit_Include(self, node, frame):
583 """Handles includes."""
584 # simpled include is include into a variable. This kind of
585 # include works the same on every level, so we handle it first.
586 if node.target is not None:
587 self.writeline('l_%s = ' % node.target, node)
588 if frame.toplevel:
589 self.write('context[%r] = ' % node.target)
590 self.write('IncludedTemplate(environment, context, ')
591 self.visit(node.template, frame)
592 self.write(')')
593 return
594
595 self.writeline('included_stream = environment.get_template(', node)
596 self.visit(node.template, frame)
597 self.write(').root_render_func(context, standalone=True)')
598 self.writeline('included_context = included_stream.next()')
599 self.writeline('for event in included_stream:')
600 self.indent()
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200601 if frame.buffer is None:
602 self.writeline('yield event')
603 else:
604 self.writeline('%s.append(event)' % frame.buffer)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200605 self.outdent()
606
607 # if we have a toplevel include the exported variables are copied
608 # into the current context without exporting them. context.udpate
609 # does *not* mark the variables as exported
610 if frame.toplevel:
611 self.writeline('context.update(included_context.get_exported())')
612
Armin Ronachere791c2a2008-04-07 18:39:54 +0200613 def visit_For(self, node, frame):
614 loop_frame = frame.inner()
615 loop_frame.inspect(node.iter_child_nodes())
Armin Ronachere791c2a2008-04-07 18:39:54 +0200616 extended_loop = bool(node.else_) or \
617 'loop' in loop_frame.identifiers.undeclared
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200618 if extended_loop:
619 loop_frame.identifiers.add_special('loop')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200620
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200621 aliases = self.collect_shadowed(loop_frame)
Armin Ronacher625215e2008-04-13 16:31:08 +0200622 self.pull_locals(loop_frame, indent=False)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200623 if node.else_:
624 self.writeline('l_loop = None')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200625
626 self.newline(node)
627 self.writeline('for ')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200628 self.visit(node.target, loop_frame)
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200629 self.write(extended_loop and ', l_loop in LoopContext(' or ' in ')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200630
631 # the expression pointing to the parent loop. We make the
632 # undefined a bit more debug friendly at the same time.
633 parent_loop = 'loop' in aliases and aliases['loop'] \
Armin Ronacherc63243e2008-04-14 22:53:58 +0200634 or "environment.undefined('loop', extra=%r)" % \
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200635 'the filter section of a loop as well as the ' \
636 'else block doesn\'t have access to the special ' \
637 "'loop' variable of the current loop. Because " \
638 'there is no parent loop it\'s undefined.'
639
640 # if we have an extened loop and a node test, we filter in the
641 # "outer frame".
642 if extended_loop and node.test is not None:
643 self.write('(')
644 self.visit(node.target, loop_frame)
645 self.write(' for ')
646 self.visit(node.target, loop_frame)
647 self.write(' in ')
648 self.visit(node.iter, loop_frame)
649 self.write(' if (')
650 test_frame = loop_frame.copy()
651 test_frame.name_overrides['loop'] = parent_loop
652 self.visit(node.test, test_frame)
653 self.write('))')
654
655 else:
656 self.visit(node.iter, loop_frame)
657
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200658 if 'loop' in aliases:
659 self.write(', ' + aliases['loop'])
Armin Ronachere791c2a2008-04-07 18:39:54 +0200660 self.write(extended_loop and '):' or ':')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200661
662 # tests in not extended loops become a continue
663 if not extended_loop and node.test is not None:
664 self.indent()
665 self.writeline('if ')
666 self.visit(node.test)
667 self.write(':')
668 self.indent()
669 self.writeline('continue')
670 self.outdent(2)
671
Armin Ronacher625215e2008-04-13 16:31:08 +0200672 self.blockvisit(node.body, loop_frame, force_generator=False)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200673
674 if node.else_:
675 self.writeline('if l_loop is None:')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200676 self.indent()
677 self.writeline('l_loop = ' + parent_loop)
678 self.outdent()
Armin Ronacher625215e2008-04-13 16:31:08 +0200679 self.blockvisit(node.else_, loop_frame, force_generator=False)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200680
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200681 # reset the aliases if there are any.
Armin Ronachere791c2a2008-04-07 18:39:54 +0200682 for name, alias in aliases.iteritems():
Armin Ronacher8efc5222008-04-08 14:47:40 +0200683 self.writeline('l_%s = %s' % (name, alias))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200684
685 def visit_If(self, node, frame):
Armin Ronacher75cfb862008-04-11 13:47:22 +0200686 if_frame = frame.soft()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200687 self.writeline('if ', node)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200688 self.visit(node.test, if_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200689 self.write(':')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200690 self.blockvisit(node.body, if_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200691 if node.else_:
692 self.writeline('else:')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200693 self.blockvisit(node.else_, if_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200694
Armin Ronacher8efc5222008-04-08 14:47:40 +0200695 def visit_Macro(self, node, frame):
Armin Ronacher71082072008-04-12 14:19:36 +0200696 macro_frame = self.function_scoping(node, frame)
697 args = macro_frame.arguments
Armin Ronacher8efc5222008-04-08 14:47:40 +0200698 self.writeline('def macro(%s):' % ', '.join(args), node)
Armin Ronacher625215e2008-04-13 16:31:08 +0200699 macro_frame.buffer = buf = self.temporary_identifier()
700 self.indent()
701 self.pull_locals(macro_frame, indent=False)
702 self.writeline('%s = []' % buf)
703 self.blockvisit(node.body, macro_frame, indent=False)
704 self.writeline("return TemplateData(u''.join(%s))" % buf)
705 self.outdent()
Armin Ronacher8efc5222008-04-08 14:47:40 +0200706 self.newline()
707 if frame.toplevel:
708 self.write('context[%r] = ' % node.name)
709 arg_tuple = ', '.join(repr(x.name) for x in node.args)
710 if len(node.args) == 1:
711 arg_tuple += ','
Armin Ronacherc63243e2008-04-14 22:53:58 +0200712 self.write('l_%s = Macro(environment, macro, %r, (%s), (' %
713 (node.name, node.name, arg_tuple))
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200714 for arg in node.defaults:
Armin Ronacher625215e2008-04-13 16:31:08 +0200715 self.visit(arg, macro_frame)
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200716 self.write(', ')
Armin Ronacher00d5d212008-04-13 01:10:18 +0200717 self.write('), %s, %s)' % (
718 macro_frame.accesses_arguments and '1' or '0',
719 macro_frame.accesses_caller and '1' or '0'
Armin Ronacher71082072008-04-12 14:19:36 +0200720 ))
721
722 def visit_CallBlock(self, node, frame):
723 call_frame = self.function_scoping(node, frame)
724 args = call_frame.arguments
725 self.writeline('def call(%s):' % ', '.join(args), node)
Armin Ronacher625215e2008-04-13 16:31:08 +0200726 call_frame.buffer = buf = self.temporary_identifier()
727 self.indent()
728 self.pull_locals(call_frame, indent=False)
729 self.writeline('%s = []' % buf)
730 self.blockvisit(node.body, call_frame, indent=False)
731 self.writeline("return TemplateData(u''.join(%s))" % buf)
732 self.outdent()
Armin Ronacher71082072008-04-12 14:19:36 +0200733 arg_tuple = ', '.join(repr(x.name) for x in node.args)
734 if len(node.args) == 1:
735 arg_tuple += ','
Armin Ronacherc63243e2008-04-14 22:53:58 +0200736 self.writeline('caller = Macro(environment, call, None, (%s), (' %
737 arg_tuple)
Armin Ronacher71082072008-04-12 14:19:36 +0200738 for arg in node.defaults:
739 self.visit(arg)
740 self.write(', ')
Armin Ronacher00d5d212008-04-13 01:10:18 +0200741 self.write('), %s, 0)' % (call_frame.accesses_arguments and '1' or '0'))
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200742 if frame.buffer is None:
743 self.writeline('yield ', node)
744 else:
745 self.writeline('%s.append(' % frame.buffer, node)
Armin Ronacher71082072008-04-12 14:19:36 +0200746 self.visit_Call(node.call, call_frame, extra_kwargs='caller=caller')
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200747 if frame.buffer is not None:
748 self.write(')')
749
750 def visit_FilterBlock(self, node, frame):
751 filter_frame = frame.inner()
752 filter_frame.inspect(node.iter_child_nodes())
753
754 aliases = self.collect_shadowed(filter_frame)
Armin Ronacher625215e2008-04-13 16:31:08 +0200755 self.pull_locals(filter_frame, indent=False)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200756 filter_frame.buffer = buf = self.temporary_identifier()
757
758 self.writeline('%s = []' % buf, node)
759 for child in node.body:
760 self.visit(child, filter_frame)
761
762 if frame.buffer is None:
763 self.writeline('yield ', node)
764 else:
765 self.writeline('%s.append(' % frame.buffer, node)
766 self.visit_Filter(node.filter, filter_frame, "u''.join(%s)" % buf)
767 if frame.buffer is not None:
768 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200769
Armin Ronachere791c2a2008-04-07 18:39:54 +0200770 def visit_ExprStmt(self, node, frame):
771 self.newline(node)
772 self.visit(node, frame)
773
774 def visit_Output(self, node, frame):
Armin Ronacher75cfb862008-04-11 13:47:22 +0200775 # if we have a known extends statement, we don't output anything
Armin Ronacher7a52df82008-04-11 13:58:22 +0200776 if self.has_known_extends and frame.toplevel:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200777 return
Armin Ronachere791c2a2008-04-07 18:39:54 +0200778
Armin Ronacher75cfb862008-04-11 13:47:22 +0200779 self.newline(node)
Armin Ronacher8edbe492008-04-10 20:43:43 +0200780 if self.environment.finalize is unicode:
781 finalizer = 'unicode'
Armin Ronacherf059ec12008-04-11 22:21:00 +0200782 have_finalizer = False
Armin Ronacher8edbe492008-04-10 20:43:43 +0200783 else:
784 finalizer = 'context.finalize'
Armin Ronacherf059ec12008-04-11 22:21:00 +0200785 have_finalizer = False
Armin Ronacher8edbe492008-04-10 20:43:43 +0200786
Armin Ronacher7fb38972008-04-11 13:54:28 +0200787 # if we are in the toplevel scope and there was already an extends
788 # statement we have to add a check that disables our yield(s) here
789 # so that they don't appear in the output.
790 outdent_later = False
791 if frame.toplevel and self.extends_so_far != 0:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200792 self.writeline('if parent_root is None:')
793 self.indent()
Armin Ronacher7fb38972008-04-11 13:54:28 +0200794 outdent_later = True
Armin Ronacher75cfb862008-04-11 13:47:22 +0200795
Armin Ronachere791c2a2008-04-07 18:39:54 +0200796 # try to evaluate as many chunks as possible into a static
797 # string at compile time.
798 body = []
799 for child in node.nodes:
800 try:
801 const = unicode(child.as_const())
802 except:
803 body.append(child)
804 continue
805 if body and isinstance(body[-1], list):
806 body[-1].append(const)
807 else:
808 body.append([const])
809
810 # if we have less than 3 nodes we just yield them
811 if len(body) < 3:
812 for item in body:
813 if isinstance(item, list):
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200814 val = repr(u''.join(item))
815 if frame.buffer is None:
816 self.writeline('yield ' + val)
817 else:
818 self.writeline('%s.append(%s)' % (frame.buffer, val))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200819 else:
820 self.newline(item)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200821 if frame.buffer is None:
822 self.write('yield ')
823 else:
824 self.write('%s.append(' % frame.buffer)
825 self.write(finalizer + '(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200826 self.visit(item, frame)
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200827 self.write(')' * (1 + (frame.buffer is not None)))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200828
829 # otherwise we create a format string as this is faster in that case
830 else:
831 format = []
832 arguments = []
833 for item in body:
834 if isinstance(item, list):
835 format.append(u''.join(item).replace('%', '%%'))
836 else:
837 format.append('%s')
838 arguments.append(item)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200839 if frame.buffer is None:
840 self.writeline('yield ')
841 else:
842 self.writeline('%s.append(' % frame.buffer)
843 self.write(repr(u''.join(format)) + ' % (')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200844 idx = -1
Armin Ronacher8e8d0712008-04-16 23:10:49 +0200845 self.indent()
846 for argument in arguments:
847 self.newline(argument)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200848 if have_finalizer:
Armin Ronacher8edbe492008-04-10 20:43:43 +0200849 self.write('(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200850 self.visit(argument, frame)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200851 if have_finalizer:
Armin Ronacher8edbe492008-04-10 20:43:43 +0200852 self.write(')')
Armin Ronacher8e8d0712008-04-16 23:10:49 +0200853 self.write(',')
854 self.outdent()
855 self.writeline(')')
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200856 if frame.buffer is not None:
857 self.write(')')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200858
Armin Ronacher7fb38972008-04-11 13:54:28 +0200859 if outdent_later:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200860 self.outdent()
861
Armin Ronacher8efc5222008-04-08 14:47:40 +0200862 def visit_Assign(self, node, frame):
863 self.newline(node)
864 # toplevel assignments however go into the local namespace and
865 # the current template's context. We create a copy of the frame
866 # here and add a set so that the Name visitor can add the assigned
867 # names here.
868 if frame.toplevel:
869 assignment_frame = frame.copy()
870 assignment_frame.assigned_names = set()
871 else:
872 assignment_frame = frame
873 self.visit(node.target, assignment_frame)
874 self.write(' = ')
875 self.visit(node.node, frame)
Armin Ronacher9706fab2008-04-08 18:49:56 +0200876
877 # make sure toplevel assignments are added to the context.
Armin Ronacher8efc5222008-04-08 14:47:40 +0200878 if frame.toplevel:
879 for name in assignment_frame.assigned_names:
880 self.writeline('context[%r] = l_%s' % (name, name))
881
Armin Ronachere791c2a2008-04-07 18:39:54 +0200882 def visit_Name(self, node, frame):
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200883 if node.ctx == 'store':
884 if frame.toplevel:
885 frame.assigned_names.add(node.name)
886 frame.name_overrides.pop(node.name, None)
887 elif node.ctx == 'load':
888 if node.name in frame.name_overrides:
889 self.write(frame.name_overrides[node.name])
890 return
Armin Ronachere791c2a2008-04-07 18:39:54 +0200891 self.write('l_' + node.name)
892
893 def visit_Const(self, node, frame):
894 val = node.value
895 if isinstance(val, float):
896 # XXX: add checks for infinity and nan
897 self.write(str(val))
898 else:
899 self.write(repr(val))
900
Armin Ronacher8efc5222008-04-08 14:47:40 +0200901 def visit_Tuple(self, node, frame):
902 self.write('(')
903 idx = -1
904 for idx, item in enumerate(node.items):
905 if idx:
906 self.write(', ')
907 self.visit(item, frame)
908 self.write(idx == 0 and ',)' or ')')
909
Armin Ronacher8edbe492008-04-10 20:43:43 +0200910 def visit_List(self, node, frame):
911 self.write('[')
912 for idx, item in enumerate(node.items):
913 if idx:
914 self.write(', ')
915 self.visit(item, frame)
916 self.write(']')
917
918 def visit_Dict(self, node, frame):
919 self.write('{')
920 for idx, item in enumerate(node.items):
921 if idx:
922 self.write(', ')
923 self.visit(item.key, frame)
924 self.write(': ')
925 self.visit(item.value, frame)
926 self.write('}')
927
Armin Ronachere791c2a2008-04-07 18:39:54 +0200928 def binop(operator):
929 def visitor(self, node, frame):
930 self.write('(')
931 self.visit(node.left, frame)
932 self.write(' %s ' % operator)
933 self.visit(node.right, frame)
934 self.write(')')
935 return visitor
936
937 def uaop(operator):
938 def visitor(self, node, frame):
939 self.write('(' + operator)
940 self.visit(node.node)
941 self.write(')')
942 return visitor
943
944 visit_Add = binop('+')
945 visit_Sub = binop('-')
946 visit_Mul = binop('*')
947 visit_Div = binop('/')
948 visit_FloorDiv = binop('//')
949 visit_Pow = binop('**')
950 visit_Mod = binop('%')
951 visit_And = binop('and')
952 visit_Or = binop('or')
953 visit_Pos = uaop('+')
954 visit_Neg = uaop('-')
955 visit_Not = uaop('not ')
956 del binop, uaop
957
958 def visit_Compare(self, node, frame):
959 self.visit(node.expr, frame)
960 for op in node.ops:
961 self.visit(op, frame)
962
963 def visit_Operand(self, node, frame):
964 self.write(' %s ' % operators[node.op])
965 self.visit(node.expr, frame)
966
967 def visit_Subscript(self, node, frame):
Armin Ronacher8efc5222008-04-08 14:47:40 +0200968 if isinstance(node.arg, nodes.Slice):
969 self.visit(node.node, frame)
970 self.write('[')
971 self.visit(node.arg, frame)
972 self.write(']')
973 return
974 try:
975 const = node.arg.as_const()
976 have_const = True
977 except nodes.Impossible:
978 have_const = False
979 if have_const:
980 if isinstance(const, (int, long, float)):
981 self.visit(node.node, frame)
982 self.write('[%s]' % const)
983 return
Armin Ronacherc63243e2008-04-14 22:53:58 +0200984 self.write('environment.subscribe(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200985 self.visit(node.node, frame)
986 self.write(', ')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200987 if have_const:
988 self.write(repr(const))
989 else:
990 self.visit(node.arg, frame)
Armin Ronacher4dfc9752008-04-09 15:03:29 +0200991 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200992
993 def visit_Slice(self, node, frame):
994 if node.start is not None:
995 self.visit(node.start, frame)
996 self.write(':')
997 if node.stop is not None:
998 self.visit(node.stop, frame)
999 if node.step is not None:
1000 self.write(':')
1001 self.visit(node.step, frame)
1002
Armin Ronacherfa865fb2008-04-12 22:11:53 +02001003 def visit_Filter(self, node, frame, initial=None):
Armin Ronacherd4c64f72008-04-11 17:15:29 +02001004 self.write('f_%s(' % node.name)
Christoph Hack80909862008-04-14 01:35:10 +02001005 func = self.environment.filters.get(node.name)
1006 if getattr(func, 'contextfilter', False):
1007 self.write('context, ')
1008 if isinstance(node.node, nodes.Filter):
1009 self.visit_Filter(node.node, frame, initial)
1010 elif node.node is None:
Armin Ronacherfa865fb2008-04-12 22:11:53 +02001011 self.write(initial)
1012 else:
Armin Ronacherfa865fb2008-04-12 22:11:53 +02001013 self.visit(node.node, frame)
Armin Ronacherd55ab532008-04-09 16:13:39 +02001014 self.signature(node, frame)
1015 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +02001016
1017 def visit_Test(self, node, frame):
Armin Ronacherf059ec12008-04-11 22:21:00 +02001018 self.write('t_%s(' % node.name)
1019 func = self.environment.tests.get(node.name)
1020 if getattr(func, 'contexttest', False):
1021 self.write('context, ')
Armin Ronacher8efc5222008-04-08 14:47:40 +02001022 self.visit(node.node, frame)
1023 self.signature(node, frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +02001024 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +02001025
Armin Ronacher3d8b7842008-04-13 13:16:50 +02001026 def visit_CondExpr(self, node, frame):
1027 if not have_condexpr:
1028 self.write('((')
1029 self.visit(node.test, frame)
1030 self.write(') and (')
1031 self.visit(node.expr1, frame)
1032 self.write(',) or (')
1033 self.visit(node.expr2, frame)
1034 self.write(',))[0]')
1035 else:
1036 self.write('(')
1037 self.visit(node.expr1, frame)
1038 self.write(' if ')
1039 self.visit(node.test, frame)
1040 self.write(' else ')
1041 self.visit(node.expr2, frame)
1042 self.write(')')
1043
Armin Ronacher71082072008-04-12 14:19:36 +02001044 def visit_Call(self, node, frame, extra_kwargs=None):
Armin Ronacherc63243e2008-04-14 22:53:58 +02001045 if self.environment.sandboxed:
1046 self.write('environment.call(')
Armin Ronacher8efc5222008-04-08 14:47:40 +02001047 self.visit(node.node, frame)
Armin Ronacherc63243e2008-04-14 22:53:58 +02001048 self.write(self.environment.sandboxed and ', ' or '(')
Armin Ronacher71082072008-04-12 14:19:36 +02001049 self.signature(node, frame, False, extra_kwargs)
Armin Ronacher8efc5222008-04-08 14:47:40 +02001050 self.write(')')
1051
1052 def visit_Keyword(self, node, frame):
Armin Ronacher2e9396b2008-04-16 14:21:57 +02001053 self.write(node.key + '=')
Armin Ronacher8efc5222008-04-08 14:47:40 +02001054 self.visit(node.value, frame)