blob: 162192de28175e5f87e086572d911e44d359f6dc [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 Ronacherf059ec12008-04-11 22:21:00 +0200460 self.writeline('context = TemplateContext(globals, %r, blocks'
461 ', 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'] \
616 or "Undefined('loop', extra=%r)" % \
617 '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 Ronacher4f62a9f2008-04-08 18:09:13 +0200694 self.write('l_%s = Macro(macro, %r, (%s), (' % (node.name, node.name,
695 arg_tuple))
696 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 += ','
718 self.writeline('caller = Macro(call, None, (%s), (' % arg_tuple)
719 for arg in node.defaults:
720 self.visit(arg)
721 self.write(', ')
Armin Ronacher00d5d212008-04-13 01:10:18 +0200722 self.write('), %s, 0)' % (call_frame.accesses_arguments and '1' or '0'))
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200723 if frame.buffer is None:
724 self.writeline('yield ', node)
725 else:
726 self.writeline('%s.append(' % frame.buffer, node)
Armin Ronacher71082072008-04-12 14:19:36 +0200727 self.visit_Call(node.call, call_frame, extra_kwargs='caller=caller')
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200728 if frame.buffer is not None:
729 self.write(')')
730
731 def visit_FilterBlock(self, node, frame):
732 filter_frame = frame.inner()
733 filter_frame.inspect(node.iter_child_nodes())
734
735 aliases = self.collect_shadowed(filter_frame)
Armin Ronacher625215e2008-04-13 16:31:08 +0200736 self.pull_locals(filter_frame, indent=False)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200737 filter_frame.buffer = buf = self.temporary_identifier()
738
739 self.writeline('%s = []' % buf, node)
740 for child in node.body:
741 self.visit(child, filter_frame)
742
743 if frame.buffer is None:
744 self.writeline('yield ', node)
745 else:
746 self.writeline('%s.append(' % frame.buffer, node)
747 self.visit_Filter(node.filter, filter_frame, "u''.join(%s)" % buf)
748 if frame.buffer is not None:
749 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200750
Armin Ronachere791c2a2008-04-07 18:39:54 +0200751 def visit_ExprStmt(self, node, frame):
752 self.newline(node)
753 self.visit(node, frame)
754
755 def visit_Output(self, node, frame):
Armin Ronacher75cfb862008-04-11 13:47:22 +0200756 # if we have a known extends statement, we don't output anything
Armin Ronacher7a52df82008-04-11 13:58:22 +0200757 if self.has_known_extends and frame.toplevel:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200758 return
Armin Ronachere791c2a2008-04-07 18:39:54 +0200759
Armin Ronacher75cfb862008-04-11 13:47:22 +0200760 self.newline(node)
Armin Ronacher8edbe492008-04-10 20:43:43 +0200761 if self.environment.finalize is unicode:
762 finalizer = 'unicode'
Armin Ronacherf059ec12008-04-11 22:21:00 +0200763 have_finalizer = False
Armin Ronacher8edbe492008-04-10 20:43:43 +0200764 else:
765 finalizer = 'context.finalize'
Armin Ronacherf059ec12008-04-11 22:21:00 +0200766 have_finalizer = False
Armin Ronacher8edbe492008-04-10 20:43:43 +0200767
Armin Ronacher7fb38972008-04-11 13:54:28 +0200768 # if we are in the toplevel scope and there was already an extends
769 # statement we have to add a check that disables our yield(s) here
770 # so that they don't appear in the output.
771 outdent_later = False
772 if frame.toplevel and self.extends_so_far != 0:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200773 self.writeline('if parent_root is None:')
774 self.indent()
Armin Ronacher7fb38972008-04-11 13:54:28 +0200775 outdent_later = True
Armin Ronacher75cfb862008-04-11 13:47:22 +0200776
Armin Ronachere791c2a2008-04-07 18:39:54 +0200777 # try to evaluate as many chunks as possible into a static
778 # string at compile time.
779 body = []
780 for child in node.nodes:
781 try:
782 const = unicode(child.as_const())
783 except:
784 body.append(child)
785 continue
786 if body and isinstance(body[-1], list):
787 body[-1].append(const)
788 else:
789 body.append([const])
790
791 # if we have less than 3 nodes we just yield them
792 if len(body) < 3:
793 for item in body:
794 if isinstance(item, list):
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200795 val = repr(u''.join(item))
796 if frame.buffer is None:
797 self.writeline('yield ' + val)
798 else:
799 self.writeline('%s.append(%s)' % (frame.buffer, val))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200800 else:
801 self.newline(item)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200802 if frame.buffer is None:
803 self.write('yield ')
804 else:
805 self.write('%s.append(' % frame.buffer)
806 self.write(finalizer + '(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200807 self.visit(item, frame)
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200808 self.write(')' * (1 + (frame.buffer is not None)))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200809
810 # otherwise we create a format string as this is faster in that case
811 else:
812 format = []
813 arguments = []
814 for item in body:
815 if isinstance(item, list):
816 format.append(u''.join(item).replace('%', '%%'))
817 else:
818 format.append('%s')
819 arguments.append(item)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200820 if frame.buffer is None:
821 self.writeline('yield ')
822 else:
823 self.writeline('%s.append(' % frame.buffer)
824 self.write(repr(u''.join(format)) + ' % (')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200825 idx = -1
826 for idx, argument in enumerate(arguments):
827 if idx:
828 self.write(', ')
Armin Ronacherf059ec12008-04-11 22:21:00 +0200829 if have_finalizer:
Armin Ronacher8edbe492008-04-10 20:43:43 +0200830 self.write('(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200831 self.visit(argument, frame)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200832 if have_finalizer:
Armin Ronacher8edbe492008-04-10 20:43:43 +0200833 self.write(')')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200834 self.write(idx == 0 and ',)' or ')')
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200835 if frame.buffer is not None:
836 self.write(')')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200837
Armin Ronacher7fb38972008-04-11 13:54:28 +0200838 if outdent_later:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200839 self.outdent()
840
Armin Ronacher8efc5222008-04-08 14:47:40 +0200841 def visit_Assign(self, node, frame):
842 self.newline(node)
843 # toplevel assignments however go into the local namespace and
844 # the current template's context. We create a copy of the frame
845 # here and add a set so that the Name visitor can add the assigned
846 # names here.
847 if frame.toplevel:
848 assignment_frame = frame.copy()
849 assignment_frame.assigned_names = set()
850 else:
851 assignment_frame = frame
852 self.visit(node.target, assignment_frame)
853 self.write(' = ')
854 self.visit(node.node, frame)
Armin Ronacher9706fab2008-04-08 18:49:56 +0200855
856 # make sure toplevel assignments are added to the context.
Armin Ronacher8efc5222008-04-08 14:47:40 +0200857 if frame.toplevel:
858 for name in assignment_frame.assigned_names:
859 self.writeline('context[%r] = l_%s' % (name, name))
860
Armin Ronachere791c2a2008-04-07 18:39:54 +0200861 def visit_Name(self, node, frame):
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200862 if node.ctx == 'store':
863 if frame.toplevel:
864 frame.assigned_names.add(node.name)
865 frame.name_overrides.pop(node.name, None)
866 elif node.ctx == 'load':
867 if node.name in frame.name_overrides:
868 self.write(frame.name_overrides[node.name])
869 return
Armin Ronachere791c2a2008-04-07 18:39:54 +0200870 self.write('l_' + node.name)
871
872 def visit_Const(self, node, frame):
873 val = node.value
874 if isinstance(val, float):
875 # XXX: add checks for infinity and nan
876 self.write(str(val))
877 else:
878 self.write(repr(val))
879
Armin Ronacher8efc5222008-04-08 14:47:40 +0200880 def visit_Tuple(self, node, frame):
881 self.write('(')
882 idx = -1
883 for idx, item in enumerate(node.items):
884 if idx:
885 self.write(', ')
886 self.visit(item, frame)
887 self.write(idx == 0 and ',)' or ')')
888
Armin Ronacher8edbe492008-04-10 20:43:43 +0200889 def visit_List(self, node, frame):
890 self.write('[')
891 for idx, item in enumerate(node.items):
892 if idx:
893 self.write(', ')
894 self.visit(item, frame)
895 self.write(']')
896
897 def visit_Dict(self, node, frame):
898 self.write('{')
899 for idx, item in enumerate(node.items):
900 if idx:
901 self.write(', ')
902 self.visit(item.key, frame)
903 self.write(': ')
904 self.visit(item.value, frame)
905 self.write('}')
906
Armin Ronachere791c2a2008-04-07 18:39:54 +0200907 def binop(operator):
908 def visitor(self, node, frame):
909 self.write('(')
910 self.visit(node.left, frame)
911 self.write(' %s ' % operator)
912 self.visit(node.right, frame)
913 self.write(')')
914 return visitor
915
916 def uaop(operator):
917 def visitor(self, node, frame):
918 self.write('(' + operator)
919 self.visit(node.node)
920 self.write(')')
921 return visitor
922
923 visit_Add = binop('+')
924 visit_Sub = binop('-')
925 visit_Mul = binop('*')
926 visit_Div = binop('/')
927 visit_FloorDiv = binop('//')
928 visit_Pow = binop('**')
929 visit_Mod = binop('%')
930 visit_And = binop('and')
931 visit_Or = binop('or')
932 visit_Pos = uaop('+')
933 visit_Neg = uaop('-')
934 visit_Not = uaop('not ')
935 del binop, uaop
936
937 def visit_Compare(self, node, frame):
938 self.visit(node.expr, frame)
939 for op in node.ops:
940 self.visit(op, frame)
941
942 def visit_Operand(self, node, frame):
943 self.write(' %s ' % operators[node.op])
944 self.visit(node.expr, frame)
945
946 def visit_Subscript(self, node, frame):
Armin Ronacher8efc5222008-04-08 14:47:40 +0200947 if isinstance(node.arg, nodes.Slice):
948 self.visit(node.node, frame)
949 self.write('[')
950 self.visit(node.arg, frame)
951 self.write(']')
952 return
953 try:
954 const = node.arg.as_const()
955 have_const = True
956 except nodes.Impossible:
957 have_const = False
958 if have_const:
959 if isinstance(const, (int, long, float)):
960 self.visit(node.node, frame)
961 self.write('[%s]' % const)
962 return
963 self.write('subscribe(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200964 self.visit(node.node, frame)
965 self.write(', ')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200966 if have_const:
967 self.write(repr(const))
968 else:
969 self.visit(node.arg, frame)
Armin Ronacher4dfc9752008-04-09 15:03:29 +0200970 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200971
972 def visit_Slice(self, node, frame):
973 if node.start is not None:
974 self.visit(node.start, frame)
975 self.write(':')
976 if node.stop is not None:
977 self.visit(node.stop, frame)
978 if node.step is not None:
979 self.write(':')
980 self.visit(node.step, frame)
981
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200982 def visit_Filter(self, node, frame, initial=None):
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200983 self.write('f_%s(' % node.name)
Christoph Hack80909862008-04-14 01:35:10 +0200984 func = self.environment.filters.get(node.name)
985 if getattr(func, 'contextfilter', False):
986 self.write('context, ')
987 if isinstance(node.node, nodes.Filter):
988 self.visit_Filter(node.node, frame, initial)
989 elif node.node is None:
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200990 self.write(initial)
991 else:
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200992 self.visit(node.node, frame)
Armin Ronacherd55ab532008-04-09 16:13:39 +0200993 self.signature(node, frame)
994 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200995
996 def visit_Test(self, node, frame):
Armin Ronacherf059ec12008-04-11 22:21:00 +0200997 self.write('t_%s(' % node.name)
998 func = self.environment.tests.get(node.name)
999 if getattr(func, 'contexttest', False):
1000 self.write('context, ')
Armin Ronacher8efc5222008-04-08 14:47:40 +02001001 self.visit(node.node, frame)
1002 self.signature(node, frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +02001003 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +02001004
Armin Ronacher3d8b7842008-04-13 13:16:50 +02001005 def visit_CondExpr(self, node, frame):
1006 if not have_condexpr:
1007 self.write('((')
1008 self.visit(node.test, frame)
1009 self.write(') and (')
1010 self.visit(node.expr1, frame)
1011 self.write(',) or (')
1012 self.visit(node.expr2, frame)
1013 self.write(',))[0]')
1014 else:
1015 self.write('(')
1016 self.visit(node.expr1, frame)
1017 self.write(' if ')
1018 self.visit(node.test, frame)
1019 self.write(' else ')
1020 self.visit(node.expr2, frame)
1021 self.write(')')
1022
Armin Ronacher71082072008-04-12 14:19:36 +02001023 def visit_Call(self, node, frame, extra_kwargs=None):
Armin Ronacher8efc5222008-04-08 14:47:40 +02001024 self.visit(node.node, frame)
1025 self.write('(')
Armin Ronacher71082072008-04-12 14:19:36 +02001026 self.signature(node, frame, False, extra_kwargs)
Armin Ronacher8efc5222008-04-08 14:47:40 +02001027 self.write(')')
1028
1029 def visit_Keyword(self, node, frame):
1030 self.visit(node.key, frame)
1031 self.write('=')
1032 self.visit(node.value, frame)