blob: e299c6ff29b6bc155023f962695ba76ac2410d3a [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,
53 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
250 # the number of new lines before the next write()
251 self._new_lines = 0
252
253 # the line number of the last written statement
Armin Ronachere791c2a2008-04-07 18:39:54 +0200254 self._last_line = 0
Armin Ronacherfed44b52008-04-13 19:42:53 +0200255
256 # true if nothing was written so far.
Armin Ronachere791c2a2008-04-07 18:39:54 +0200257 self._first_write = True
258
Armin Ronacherfed44b52008-04-13 19:42:53 +0200259 # used by the `temporary_identifier` method to get new
260 # unique, temporary identifier
261 self._last_identifier = 0
262
263 # the current indentation
264 self._indentation = 0
265
Armin Ronachere791c2a2008-04-07 18:39:54 +0200266 def temporary_identifier(self):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200267 """Get a new unique identifier."""
268 self._last_identifier += 1
269 return 't%d' % self._last_identifier
Armin Ronachere791c2a2008-04-07 18:39:54 +0200270
271 def indent(self):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200272 """Indent by one."""
273 self._indentation += 1
Armin Ronachere791c2a2008-04-07 18:39:54 +0200274
Armin Ronacher8efc5222008-04-08 14:47:40 +0200275 def outdent(self, step=1):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200276 """Outdent by step."""
277 self._indentation -= step
Armin Ronachere791c2a2008-04-07 18:39:54 +0200278
Armin Ronacher625215e2008-04-13 16:31:08 +0200279 def blockvisit(self, nodes, frame, indent=True, force_generator=True):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200280 """Visit a list of nodes as block in a frame. Per default the
281 code is indented, but this can be disabled by setting the indent
282 parameter to False. If the current frame is no buffer a dummy
283 ``if 0: yield None`` is written automatically unless the
284 force_generator parameter is set to False.
285 """
Armin Ronacher625215e2008-04-13 16:31:08 +0200286 if indent:
287 self.indent()
288 if frame.buffer is None and force_generator:
Armin Ronachere791c2a2008-04-07 18:39:54 +0200289 self.writeline('if 0: yield None')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200290 try:
291 for node in nodes:
292 self.visit(node, frame)
293 except CompilerExit:
294 pass
Armin Ronacher625215e2008-04-13 16:31:08 +0200295 if indent:
296 self.outdent()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200297
298 def write(self, x):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200299 """Write a string into the output stream."""
300 if self._new_lines:
Armin Ronachere791c2a2008-04-07 18:39:54 +0200301 if not self._first_write:
Armin Ronacherfed44b52008-04-13 19:42:53 +0200302 self.stream.write('\n' * self._new_lines)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200303 self._first_write = False
Armin Ronacherfed44b52008-04-13 19:42:53 +0200304 self.stream.write(' ' * self._indentation)
305 self._new_lines = 0
Armin Ronachere791c2a2008-04-07 18:39:54 +0200306 self.stream.write(x)
307
308 def writeline(self, x, node=None, extra=0):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200309 """Combination of newline and write."""
Armin Ronachere791c2a2008-04-07 18:39:54 +0200310 self.newline(node, extra)
311 self.write(x)
312
313 def newline(self, node=None, extra=0):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200314 """Add one or more newlines before the next write."""
315 self._new_lines = max(self._new_lines, 1 + extra)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200316 if node is not None and node.lineno != self._last_line:
317 self.write('# line: %s' % node.lineno)
Armin Ronacherfed44b52008-04-13 19:42:53 +0200318 self._new_lines = 1
Armin Ronachere791c2a2008-04-07 18:39:54 +0200319 self._last_line = node.lineno
320
Armin Ronacher71082072008-04-12 14:19:36 +0200321 def signature(self, node, frame, have_comma=True, extra_kwargs=None):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200322 """Writes a function call to the stream for the current node.
323 Per default it will write a leading comma but this can be
324 disabled by setting have_comma to False. If extra_kwargs is
325 given it must be a string that represents a single keyword
326 argument call that is inserted at the end of the regular
327 keyword argument calls.
328 """
Armin Ronacher8efc5222008-04-08 14:47:40 +0200329 have_comma = have_comma and [True] or []
330 def touch_comma():
331 if have_comma:
332 self.write(', ')
333 else:
334 have_comma.append(True)
335
336 for arg in node.args:
337 touch_comma()
338 self.visit(arg, frame)
339 for kwarg in node.kwargs:
340 touch_comma()
341 self.visit(kwarg, frame)
Armin Ronacher71082072008-04-12 14:19:36 +0200342 if extra_kwargs is not None:
343 touch_comma()
344 self.write(extra_kwargs)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200345 if node.dyn_args:
346 touch_comma()
Armin Ronacher71082072008-04-12 14:19:36 +0200347 self.write('*')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200348 self.visit(node.dyn_args, frame)
349 if node.dyn_kwargs:
350 touch_comma()
Armin Ronacher71082072008-04-12 14:19:36 +0200351 self.write('**')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200352 self.visit(node.dyn_kwargs, frame)
353
Armin Ronacher625215e2008-04-13 16:31:08 +0200354 def pull_locals(self, frame, indent=True):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200355 """Pull all the references identifiers into the local scope.
356 This affects regular names, filters and tests. If indent is
357 set to False, no automatic indentation will take place.
358 """
Armin Ronacher625215e2008-04-13 16:31:08 +0200359 if indent:
Armin Ronachere791c2a2008-04-07 18:39:54 +0200360 self.indent()
361 for name in frame.identifiers.undeclared:
362 self.writeline('l_%s = context[%r]' % (name, name))
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200363 for name in frame.identifiers.filters:
364 self.writeline('f_%s = environment.filters[%r]' % (name, name))
Armin Ronacherf059ec12008-04-11 22:21:00 +0200365 for name in frame.identifiers.tests:
366 self.writeline('t_%s = environment.tests[%r]' % (name, name))
Armin Ronacher625215e2008-04-13 16:31:08 +0200367 if indent:
Armin Ronachere791c2a2008-04-07 18:39:54 +0200368 self.outdent()
369
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200370 def collect_shadowed(self, frame):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200371 """This function returns all the shadowed variables in a dict
372 in the form name: alias and will write the required assignments
373 into the current scope. No indentation takes place.
374 """
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200375 # make sure we "backup" overridden, local identifiers
376 # TODO: we should probably optimize this and check if the
377 # identifier is in use afterwards.
378 aliases = {}
379 for name in frame.identifiers.find_shadowed():
380 aliases[name] = ident = self.temporary_identifier()
381 self.writeline('%s = l_%s' % (ident, name))
382 return aliases
383
Armin Ronacher71082072008-04-12 14:19:36 +0200384 def function_scoping(self, node, frame):
Armin Ronacherfed44b52008-04-13 19:42:53 +0200385 """In Jinja a few statements require the help of anonymous
386 functions. Those are currently macros and call blocks and in
387 the future also recursive loops. As there is currently
388 technical limitation that doesn't allow reading and writing a
389 variable in a scope where the initial value is coming from an
390 outer scope, this function tries to fall back with a common
391 error message. Additionally the frame passed is modified so
392 that the argumetns are collected and callers are looked up.
393
394 This will return the modified frame.
395 """
Armin Ronacher71082072008-04-12 14:19:36 +0200396 func_frame = frame.inner()
397 func_frame.inspect(node.iter_child_nodes(), hard_scope=True)
398
399 # variables that are undeclared (accessed before declaration) and
400 # declared locally *and* part of an outside scope raise a template
401 # assertion error. Reason: we can't generate reasonable code from
402 # it without aliasing all the variables. XXX: alias them ^^
403 overriden_closure_vars = (
404 func_frame.identifiers.undeclared &
405 func_frame.identifiers.declared &
406 (func_frame.identifiers.declared_locally |
407 func_frame.identifiers.declared_parameter)
408 )
409 if overriden_closure_vars:
410 vars = ', '.join(sorted(overriden_closure_vars))
411 raise TemplateAssertionError('It\'s not possible to set and '
412 'access variables derived from '
413 'an outer scope! (affects: %s' %
414 vars, node.lineno, self.filename)
415
416 # remove variables from a closure from the frame's undeclared
417 # identifiers.
418 func_frame.identifiers.undeclared -= (
419 func_frame.identifiers.undeclared &
420 func_frame.identifiers.declared
421 )
422
423 func_frame.accesses_arguments = False
424 func_frame.accesses_caller = False
425 func_frame.arguments = args = ['l_' + x.name for x in node.args]
426
427 if 'arguments' in func_frame.identifiers.undeclared:
428 func_frame.accesses_arguments = True
429 func_frame.identifiers.add_special('arguments')
430 args.append('l_arguments')
431 if 'caller' in func_frame.identifiers.undeclared:
432 func_frame.accesses_caller = True
433 func_frame.identifiers.add_special('caller')
434 args.append('l_caller')
435 return func_frame
436
Armin Ronachere791c2a2008-04-07 18:39:54 +0200437 # -- Visitors
438
439 def visit_Template(self, node, frame=None):
440 assert frame is None, 'no root frame allowed'
441 self.writeline('from jinja2.runtime import *')
442 self.writeline('filename = %r' % self.filename)
Armin Ronacher8edbe492008-04-10 20:43:43 +0200443
Armin Ronacher75cfb862008-04-11 13:47:22 +0200444 # do we have an extends tag at all? If not, we can save some
445 # overhead by just not processing any inheritance code.
446 have_extends = node.find(nodes.Extends) is not None
447
Armin Ronacher8edbe492008-04-10 20:43:43 +0200448 # find all blocks
449 for block in node.find_all(nodes.Block):
450 if block.name in self.blocks:
451 raise TemplateAssertionError('block %r defined twice' %
452 block.name, block.lineno,
453 self.filename)
454 self.blocks[block.name] = block
Armin Ronachere791c2a2008-04-07 18:39:54 +0200455
Armin Ronacher8efc5222008-04-08 14:47:40 +0200456 # generate the root render function.
Armin Ronacherf059ec12008-04-11 22:21:00 +0200457 self.writeline('def root(globals, environment=environment'
458 ', standalone=False):', extra=1)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200459 self.indent()
Armin Ronacherc63243e2008-04-14 22:53:58 +0200460 self.writeline('context = TemplateContext(environment, globals, %r, '
461 'blocks, standalone)' % self.filename)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200462 if have_extends:
463 self.writeline('parent_root = None')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200464 self.outdent()
Armin Ronacher75cfb862008-04-11 13:47:22 +0200465
466 # process the root
Armin Ronachere791c2a2008-04-07 18:39:54 +0200467 frame = Frame()
468 frame.inspect(node.body)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200469 frame.toplevel = frame.rootlevel = True
Armin Ronacherf059ec12008-04-11 22:21:00 +0200470 self.indent()
Armin Ronacher625215e2008-04-13 16:31:08 +0200471 self.pull_locals(frame, indent=False)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200472 self.writeline('yield context')
Armin Ronacher625215e2008-04-13 16:31:08 +0200473 self.blockvisit(node.body, frame, indent=False)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200474 self.outdent()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200475
Armin Ronacher8efc5222008-04-08 14:47:40 +0200476 # make sure that the parent root is called.
Armin Ronacher75cfb862008-04-11 13:47:22 +0200477 if have_extends:
478 if not self.has_known_extends:
479 self.indent()
480 self.writeline('if parent_root is not None:')
481 self.indent()
Armin Ronacherf059ec12008-04-11 22:21:00 +0200482 self.writeline('stream = parent_root(context)')
483 self.writeline('stream.next()')
484 self.writeline('for event in stream:')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200485 self.indent()
486 self.writeline('yield event')
Armin Ronacher7a52df82008-04-11 13:58:22 +0200487 self.outdent(1 + self.has_known_extends)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200488
489 # at this point we now have the blocks collected and can visit them too.
490 for name, block in self.blocks.iteritems():
491 block_frame = Frame()
492 block_frame.inspect(block.body)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200493 block_frame.block = name
Armin Ronacher62f8a292008-04-13 23:18:05 +0200494 block_frame.identifiers.add_special('super')
495 block_frame.name_overrides['super'] = 'context.super(%r)' % name
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200496 self.writeline('def block_%s(context, environment=environment):'
497 % name, block, 1)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200498 self.pull_locals(block_frame)
Armin Ronacher625215e2008-04-13 16:31:08 +0200499 self.blockvisit(block.body, block_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200500
Armin Ronacher75cfb862008-04-11 13:47:22 +0200501 self.writeline('blocks = {%s}' % ', '.join('%r: block_%s' % (x, x)
502 for x in self.blocks), extra=1)
503
Armin Ronachere791c2a2008-04-07 18:39:54 +0200504 def visit_Block(self, node, frame):
505 """Call a block and register it for the template."""
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200506 level = 1
Armin Ronacher75cfb862008-04-11 13:47:22 +0200507 if frame.toplevel:
Armin Ronacherbcb7c532008-04-11 16:30:34 +0200508 # if we know that we are a child template, there is no need to
509 # check if we are one
510 if self.has_known_extends:
511 return
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200512 if self.extends_so_far > 0:
513 self.writeline('if parent_root is None:')
514 self.indent()
515 level += 1
516 self.writeline('for event in context.blocks[%r][-1](context):' % node.name)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200517 self.indent()
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200518 if frame.buffer is None:
519 self.writeline('yield event')
520 else:
521 self.writeline('%s.append(event)' % frame.buffer)
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200522 self.outdent(level)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200523
524 def visit_Extends(self, node, frame):
525 """Calls the extender."""
Armin Ronacher8efc5222008-04-08 14:47:40 +0200526 if not frame.toplevel:
527 raise TemplateAssertionError('cannot use extend from a non '
528 'top-level scope', node.lineno,
529 self.filename)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200530
Armin Ronacher7fb38972008-04-11 13:54:28 +0200531 # if the number of extends statements in general is zero so
532 # far, we don't have to add a check if something extended
533 # the template before this one.
534 if self.extends_so_far > 0:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200535
Armin Ronacher7fb38972008-04-11 13:54:28 +0200536 # if we have a known extends we just add a template runtime
537 # error into the generated code. We could catch that at compile
538 # time too, but i welcome it not to confuse users by throwing the
539 # same error at different times just "because we can".
540 if not self.has_known_extends:
541 self.writeline('if parent_root is not None:')
542 self.indent()
543 self.writeline('raise TemplateRuntimeError(%r)' %
544 'extended multiple times')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200545
Armin Ronacher7fb38972008-04-11 13:54:28 +0200546 # if we have a known extends already we don't need that code here
547 # as we know that the template execution will end here.
548 if self.has_known_extends:
549 raise CompilerExit()
550 self.outdent()
551
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200552 self.writeline('parent_root = environment.get_template(', node, 1)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200553 self.visit(node.template, frame)
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200554 self.write(', %r).root_render_func' % self.filename)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200555
556 # if this extends statement was in the root level we can take
557 # advantage of that information and simplify the generated code
558 # in the top level from this point onwards
559 self.has_known_extends = True
Armin Ronachere791c2a2008-04-07 18:39:54 +0200560
Armin Ronacher7fb38972008-04-11 13:54:28 +0200561 # and now we have one more
562 self.extends_so_far += 1
563
Armin Ronacherf059ec12008-04-11 22:21:00 +0200564 def visit_Include(self, node, frame):
565 """Handles includes."""
566 # simpled include is include into a variable. This kind of
567 # include works the same on every level, so we handle it first.
568 if node.target is not None:
569 self.writeline('l_%s = ' % node.target, node)
570 if frame.toplevel:
571 self.write('context[%r] = ' % node.target)
572 self.write('IncludedTemplate(environment, context, ')
573 self.visit(node.template, frame)
574 self.write(')')
575 return
576
577 self.writeline('included_stream = environment.get_template(', node)
578 self.visit(node.template, frame)
579 self.write(').root_render_func(context, standalone=True)')
580 self.writeline('included_context = included_stream.next()')
581 self.writeline('for event in included_stream:')
582 self.indent()
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200583 if frame.buffer is None:
584 self.writeline('yield event')
585 else:
586 self.writeline('%s.append(event)' % frame.buffer)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200587 self.outdent()
588
589 # if we have a toplevel include the exported variables are copied
590 # into the current context without exporting them. context.udpate
591 # does *not* mark the variables as exported
592 if frame.toplevel:
593 self.writeline('context.update(included_context.get_exported())')
594
Armin Ronachere791c2a2008-04-07 18:39:54 +0200595 def visit_For(self, node, frame):
596 loop_frame = frame.inner()
597 loop_frame.inspect(node.iter_child_nodes())
Armin Ronachere791c2a2008-04-07 18:39:54 +0200598 extended_loop = bool(node.else_) or \
599 'loop' in loop_frame.identifiers.undeclared
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200600 if extended_loop:
601 loop_frame.identifiers.add_special('loop')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200602
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200603 aliases = self.collect_shadowed(loop_frame)
Armin Ronacher625215e2008-04-13 16:31:08 +0200604 self.pull_locals(loop_frame, indent=False)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200605 if node.else_:
606 self.writeline('l_loop = None')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200607
608 self.newline(node)
609 self.writeline('for ')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200610 self.visit(node.target, loop_frame)
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200611 self.write(extended_loop and ', l_loop in LoopContext(' or ' in ')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200612
613 # the expression pointing to the parent loop. We make the
614 # undefined a bit more debug friendly at the same time.
615 parent_loop = 'loop' in aliases and aliases['loop'] \
Armin Ronacherc63243e2008-04-14 22:53:58 +0200616 or "environment.undefined('loop', extra=%r)" % \
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200617 'the filter section of a loop as well as the ' \
618 'else block doesn\'t have access to the special ' \
619 "'loop' variable of the current loop. Because " \
620 'there is no parent loop it\'s undefined.'
621
622 # if we have an extened loop and a node test, we filter in the
623 # "outer frame".
624 if extended_loop and node.test is not None:
625 self.write('(')
626 self.visit(node.target, loop_frame)
627 self.write(' for ')
628 self.visit(node.target, loop_frame)
629 self.write(' in ')
630 self.visit(node.iter, loop_frame)
631 self.write(' if (')
632 test_frame = loop_frame.copy()
633 test_frame.name_overrides['loop'] = parent_loop
634 self.visit(node.test, test_frame)
635 self.write('))')
636
637 else:
638 self.visit(node.iter, loop_frame)
639
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200640 if 'loop' in aliases:
641 self.write(', ' + aliases['loop'])
Armin Ronachere791c2a2008-04-07 18:39:54 +0200642 self.write(extended_loop and '):' or ':')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200643
644 # tests in not extended loops become a continue
645 if not extended_loop and node.test is not None:
646 self.indent()
647 self.writeline('if ')
648 self.visit(node.test)
649 self.write(':')
650 self.indent()
651 self.writeline('continue')
652 self.outdent(2)
653
Armin Ronacher625215e2008-04-13 16:31:08 +0200654 self.blockvisit(node.body, loop_frame, force_generator=False)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200655
656 if node.else_:
657 self.writeline('if l_loop is None:')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200658 self.indent()
659 self.writeline('l_loop = ' + parent_loop)
660 self.outdent()
Armin Ronacher625215e2008-04-13 16:31:08 +0200661 self.blockvisit(node.else_, loop_frame, force_generator=False)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200662
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200663 # reset the aliases if there are any.
Armin Ronachere791c2a2008-04-07 18:39:54 +0200664 for name, alias in aliases.iteritems():
Armin Ronacher8efc5222008-04-08 14:47:40 +0200665 self.writeline('l_%s = %s' % (name, alias))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200666
667 def visit_If(self, node, frame):
Armin Ronacher75cfb862008-04-11 13:47:22 +0200668 if_frame = frame.soft()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200669 self.writeline('if ', node)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200670 self.visit(node.test, if_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200671 self.write(':')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200672 self.blockvisit(node.body, if_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200673 if node.else_:
674 self.writeline('else:')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200675 self.blockvisit(node.else_, if_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200676
Armin Ronacher8efc5222008-04-08 14:47:40 +0200677 def visit_Macro(self, node, frame):
Armin Ronacher71082072008-04-12 14:19:36 +0200678 macro_frame = self.function_scoping(node, frame)
679 args = macro_frame.arguments
Armin Ronacher8efc5222008-04-08 14:47:40 +0200680 self.writeline('def macro(%s):' % ', '.join(args), node)
Armin Ronacher625215e2008-04-13 16:31:08 +0200681 macro_frame.buffer = buf = self.temporary_identifier()
682 self.indent()
683 self.pull_locals(macro_frame, indent=False)
684 self.writeline('%s = []' % buf)
685 self.blockvisit(node.body, macro_frame, indent=False)
686 self.writeline("return TemplateData(u''.join(%s))" % buf)
687 self.outdent()
Armin Ronacher8efc5222008-04-08 14:47:40 +0200688 self.newline()
689 if frame.toplevel:
690 self.write('context[%r] = ' % node.name)
691 arg_tuple = ', '.join(repr(x.name) for x in node.args)
692 if len(node.args) == 1:
693 arg_tuple += ','
Armin Ronacherc63243e2008-04-14 22:53:58 +0200694 self.write('l_%s = Macro(environment, macro, %r, (%s), (' %
695 (node.name, node.name, arg_tuple))
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200696 for arg in node.defaults:
Armin Ronacher625215e2008-04-13 16:31:08 +0200697 self.visit(arg, macro_frame)
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200698 self.write(', ')
Armin Ronacher00d5d212008-04-13 01:10:18 +0200699 self.write('), %s, %s)' % (
700 macro_frame.accesses_arguments and '1' or '0',
701 macro_frame.accesses_caller and '1' or '0'
Armin Ronacher71082072008-04-12 14:19:36 +0200702 ))
703
704 def visit_CallBlock(self, node, frame):
705 call_frame = self.function_scoping(node, frame)
706 args = call_frame.arguments
707 self.writeline('def call(%s):' % ', '.join(args), node)
Armin Ronacher625215e2008-04-13 16:31:08 +0200708 call_frame.buffer = buf = self.temporary_identifier()
709 self.indent()
710 self.pull_locals(call_frame, indent=False)
711 self.writeline('%s = []' % buf)
712 self.blockvisit(node.body, call_frame, indent=False)
713 self.writeline("return TemplateData(u''.join(%s))" % buf)
714 self.outdent()
Armin Ronacher71082072008-04-12 14:19:36 +0200715 arg_tuple = ', '.join(repr(x.name) for x in node.args)
716 if len(node.args) == 1:
717 arg_tuple += ','
Armin Ronacherc63243e2008-04-14 22:53:58 +0200718 self.writeline('caller = Macro(environment, call, None, (%s), (' %
719 arg_tuple)
Armin Ronacher71082072008-04-12 14:19:36 +0200720 for arg in node.defaults:
721 self.visit(arg)
722 self.write(', ')
Armin Ronacher00d5d212008-04-13 01:10:18 +0200723 self.write('), %s, 0)' % (call_frame.accesses_arguments and '1' or '0'))
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200724 if frame.buffer is None:
725 self.writeline('yield ', node)
726 else:
727 self.writeline('%s.append(' % frame.buffer, node)
Armin Ronacher71082072008-04-12 14:19:36 +0200728 self.visit_Call(node.call, call_frame, extra_kwargs='caller=caller')
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200729 if frame.buffer is not None:
730 self.write(')')
731
732 def visit_FilterBlock(self, node, frame):
733 filter_frame = frame.inner()
734 filter_frame.inspect(node.iter_child_nodes())
735
736 aliases = self.collect_shadowed(filter_frame)
Armin Ronacher625215e2008-04-13 16:31:08 +0200737 self.pull_locals(filter_frame, indent=False)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200738 filter_frame.buffer = buf = self.temporary_identifier()
739
740 self.writeline('%s = []' % buf, node)
741 for child in node.body:
742 self.visit(child, filter_frame)
743
744 if frame.buffer is None:
745 self.writeline('yield ', node)
746 else:
747 self.writeline('%s.append(' % frame.buffer, node)
748 self.visit_Filter(node.filter, filter_frame, "u''.join(%s)" % buf)
749 if frame.buffer is not None:
750 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200751
Armin Ronachere791c2a2008-04-07 18:39:54 +0200752 def visit_ExprStmt(self, node, frame):
753 self.newline(node)
754 self.visit(node, frame)
755
756 def visit_Output(self, node, frame):
Armin Ronacher75cfb862008-04-11 13:47:22 +0200757 # if we have a known extends statement, we don't output anything
Armin Ronacher7a52df82008-04-11 13:58:22 +0200758 if self.has_known_extends and frame.toplevel:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200759 return
Armin Ronachere791c2a2008-04-07 18:39:54 +0200760
Armin Ronacher75cfb862008-04-11 13:47:22 +0200761 self.newline(node)
Armin Ronacher8edbe492008-04-10 20:43:43 +0200762 if self.environment.finalize is unicode:
763 finalizer = 'unicode'
Armin Ronacherf059ec12008-04-11 22:21:00 +0200764 have_finalizer = False
Armin Ronacher8edbe492008-04-10 20:43:43 +0200765 else:
766 finalizer = 'context.finalize'
Armin Ronacherf059ec12008-04-11 22:21:00 +0200767 have_finalizer = False
Armin Ronacher8edbe492008-04-10 20:43:43 +0200768
Armin Ronacher7fb38972008-04-11 13:54:28 +0200769 # if we are in the toplevel scope and there was already an extends
770 # statement we have to add a check that disables our yield(s) here
771 # so that they don't appear in the output.
772 outdent_later = False
773 if frame.toplevel and self.extends_so_far != 0:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200774 self.writeline('if parent_root is None:')
775 self.indent()
Armin Ronacher7fb38972008-04-11 13:54:28 +0200776 outdent_later = True
Armin Ronacher75cfb862008-04-11 13:47:22 +0200777
Armin Ronachere791c2a2008-04-07 18:39:54 +0200778 # try to evaluate as many chunks as possible into a static
779 # string at compile time.
780 body = []
781 for child in node.nodes:
782 try:
783 const = unicode(child.as_const())
784 except:
785 body.append(child)
786 continue
787 if body and isinstance(body[-1], list):
788 body[-1].append(const)
789 else:
790 body.append([const])
791
792 # if we have less than 3 nodes we just yield them
793 if len(body) < 3:
794 for item in body:
795 if isinstance(item, list):
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200796 val = repr(u''.join(item))
797 if frame.buffer is None:
798 self.writeline('yield ' + val)
799 else:
800 self.writeline('%s.append(%s)' % (frame.buffer, val))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200801 else:
802 self.newline(item)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200803 if frame.buffer is None:
804 self.write('yield ')
805 else:
806 self.write('%s.append(' % frame.buffer)
807 self.write(finalizer + '(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200808 self.visit(item, frame)
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200809 self.write(')' * (1 + (frame.buffer is not None)))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200810
811 # otherwise we create a format string as this is faster in that case
812 else:
813 format = []
814 arguments = []
815 for item in body:
816 if isinstance(item, list):
817 format.append(u''.join(item).replace('%', '%%'))
818 else:
819 format.append('%s')
820 arguments.append(item)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200821 if frame.buffer is None:
822 self.writeline('yield ')
823 else:
824 self.writeline('%s.append(' % frame.buffer)
825 self.write(repr(u''.join(format)) + ' % (')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200826 idx = -1
827 for idx, argument in enumerate(arguments):
828 if idx:
829 self.write(', ')
Armin Ronacherf059ec12008-04-11 22:21:00 +0200830 if have_finalizer:
Armin Ronacher8edbe492008-04-10 20:43:43 +0200831 self.write('(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200832 self.visit(argument, frame)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200833 if have_finalizer:
Armin Ronacher8edbe492008-04-10 20:43:43 +0200834 self.write(')')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200835 self.write(idx == 0 and ',)' or ')')
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200836 if frame.buffer is not None:
837 self.write(')')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200838
Armin Ronacher7fb38972008-04-11 13:54:28 +0200839 if outdent_later:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200840 self.outdent()
841
Armin Ronacher8efc5222008-04-08 14:47:40 +0200842 def visit_Assign(self, node, frame):
843 self.newline(node)
844 # toplevel assignments however go into the local namespace and
845 # the current template's context. We create a copy of the frame
846 # here and add a set so that the Name visitor can add the assigned
847 # names here.
848 if frame.toplevel:
849 assignment_frame = frame.copy()
850 assignment_frame.assigned_names = set()
851 else:
852 assignment_frame = frame
853 self.visit(node.target, assignment_frame)
854 self.write(' = ')
855 self.visit(node.node, frame)
Armin Ronacher9706fab2008-04-08 18:49:56 +0200856
857 # make sure toplevel assignments are added to the context.
Armin Ronacher8efc5222008-04-08 14:47:40 +0200858 if frame.toplevel:
859 for name in assignment_frame.assigned_names:
860 self.writeline('context[%r] = l_%s' % (name, name))
861
Armin Ronachere791c2a2008-04-07 18:39:54 +0200862 def visit_Name(self, node, frame):
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200863 if node.ctx == 'store':
864 if frame.toplevel:
865 frame.assigned_names.add(node.name)
866 frame.name_overrides.pop(node.name, None)
867 elif node.ctx == 'load':
868 if node.name in frame.name_overrides:
869 self.write(frame.name_overrides[node.name])
870 return
Armin Ronachere791c2a2008-04-07 18:39:54 +0200871 self.write('l_' + node.name)
872
873 def visit_Const(self, node, frame):
874 val = node.value
875 if isinstance(val, float):
876 # XXX: add checks for infinity and nan
877 self.write(str(val))
878 else:
879 self.write(repr(val))
880
Armin Ronacher8efc5222008-04-08 14:47:40 +0200881 def visit_Tuple(self, node, frame):
882 self.write('(')
883 idx = -1
884 for idx, item in enumerate(node.items):
885 if idx:
886 self.write(', ')
887 self.visit(item, frame)
888 self.write(idx == 0 and ',)' or ')')
889
Armin Ronacher8edbe492008-04-10 20:43:43 +0200890 def visit_List(self, node, frame):
891 self.write('[')
892 for idx, item in enumerate(node.items):
893 if idx:
894 self.write(', ')
895 self.visit(item, frame)
896 self.write(']')
897
898 def visit_Dict(self, node, frame):
899 self.write('{')
900 for idx, item in enumerate(node.items):
901 if idx:
902 self.write(', ')
903 self.visit(item.key, frame)
904 self.write(': ')
905 self.visit(item.value, frame)
906 self.write('}')
907
Armin Ronachere791c2a2008-04-07 18:39:54 +0200908 def binop(operator):
909 def visitor(self, node, frame):
910 self.write('(')
911 self.visit(node.left, frame)
912 self.write(' %s ' % operator)
913 self.visit(node.right, frame)
914 self.write(')')
915 return visitor
916
917 def uaop(operator):
918 def visitor(self, node, frame):
919 self.write('(' + operator)
920 self.visit(node.node)
921 self.write(')')
922 return visitor
923
924 visit_Add = binop('+')
925 visit_Sub = binop('-')
926 visit_Mul = binop('*')
927 visit_Div = binop('/')
928 visit_FloorDiv = binop('//')
929 visit_Pow = binop('**')
930 visit_Mod = binop('%')
931 visit_And = binop('and')
932 visit_Or = binop('or')
933 visit_Pos = uaop('+')
934 visit_Neg = uaop('-')
935 visit_Not = uaop('not ')
936 del binop, uaop
937
938 def visit_Compare(self, node, frame):
939 self.visit(node.expr, frame)
940 for op in node.ops:
941 self.visit(op, frame)
942
943 def visit_Operand(self, node, frame):
944 self.write(' %s ' % operators[node.op])
945 self.visit(node.expr, frame)
946
947 def visit_Subscript(self, node, frame):
Armin Ronacher8efc5222008-04-08 14:47:40 +0200948 if isinstance(node.arg, nodes.Slice):
949 self.visit(node.node, frame)
950 self.write('[')
951 self.visit(node.arg, frame)
952 self.write(']')
953 return
954 try:
955 const = node.arg.as_const()
956 have_const = True
957 except nodes.Impossible:
958 have_const = False
959 if have_const:
960 if isinstance(const, (int, long, float)):
961 self.visit(node.node, frame)
962 self.write('[%s]' % const)
963 return
Armin Ronacherc63243e2008-04-14 22:53:58 +0200964 self.write('environment.subscribe(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200965 self.visit(node.node, frame)
966 self.write(', ')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200967 if have_const:
968 self.write(repr(const))
969 else:
970 self.visit(node.arg, frame)
Armin Ronacher4dfc9752008-04-09 15:03:29 +0200971 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200972
973 def visit_Slice(self, node, frame):
974 if node.start is not None:
975 self.visit(node.start, frame)
976 self.write(':')
977 if node.stop is not None:
978 self.visit(node.stop, frame)
979 if node.step is not None:
980 self.write(':')
981 self.visit(node.step, frame)
982
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200983 def visit_Filter(self, node, frame, initial=None):
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200984 self.write('f_%s(' % node.name)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200985 if initial is not None:
986 self.write(initial)
987 else:
988 func = self.environment.filters.get(node.name)
989 if getattr(func, 'contextfilter', False):
990 self.write('context, ')
991 self.visit(node.node, frame)
Armin Ronacherd55ab532008-04-09 16:13:39 +0200992 self.signature(node, frame)
993 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200994
995 def visit_Test(self, node, frame):
Armin Ronacherf059ec12008-04-11 22:21:00 +0200996 self.write('t_%s(' % node.name)
997 func = self.environment.tests.get(node.name)
998 if getattr(func, 'contexttest', False):
999 self.write('context, ')
Armin Ronacher8efc5222008-04-08 14:47:40 +02001000 self.visit(node.node, frame)
1001 self.signature(node, frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +02001002 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +02001003
Armin Ronacher3d8b7842008-04-13 13:16:50 +02001004 def visit_CondExpr(self, node, frame):
1005 if not have_condexpr:
1006 self.write('((')
1007 self.visit(node.test, frame)
1008 self.write(') and (')
1009 self.visit(node.expr1, frame)
1010 self.write(',) or (')
1011 self.visit(node.expr2, frame)
1012 self.write(',))[0]')
1013 else:
1014 self.write('(')
1015 self.visit(node.expr1, frame)
1016 self.write(' if ')
1017 self.visit(node.test, frame)
1018 self.write(' else ')
1019 self.visit(node.expr2, frame)
1020 self.write(')')
1021
Armin Ronacher71082072008-04-12 14:19:36 +02001022 def visit_Call(self, node, frame, extra_kwargs=None):
Armin Ronacherc63243e2008-04-14 22:53:58 +02001023 if self.environment.sandboxed:
1024 self.write('environment.call(')
Armin Ronacher8efc5222008-04-08 14:47:40 +02001025 self.visit(node.node, frame)
Armin Ronacherc63243e2008-04-14 22:53:58 +02001026 self.write(self.environment.sandboxed and ', ' or '(')
Armin Ronacher71082072008-04-12 14:19:36 +02001027 self.signature(node, frame, False, extra_kwargs)
Armin Ronacher8efc5222008-04-08 14:47:40 +02001028 self.write(')')
1029
1030 def visit_Keyword(self, node, frame):
1031 self.visit(node.key, frame)
1032 self.write('=')
1033 self.visit(node.value, frame)