blob: bf7296389ad93fff6091f6c997574256a970ea11 [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 Ronacherd4c64f72008-04-11 17:15:29 +0200494 self.writeline('def block_%s(context, environment=environment):'
495 % name, block, 1)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200496 self.pull_locals(block_frame)
Armin Ronacher625215e2008-04-13 16:31:08 +0200497 self.blockvisit(block.body, block_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200498
Armin Ronacher75cfb862008-04-11 13:47:22 +0200499 self.writeline('blocks = {%s}' % ', '.join('%r: block_%s' % (x, x)
500 for x in self.blocks), extra=1)
501
Armin Ronachere791c2a2008-04-07 18:39:54 +0200502 def visit_Block(self, node, frame):
503 """Call a block and register it for the template."""
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200504 level = 1
Armin Ronacher75cfb862008-04-11 13:47:22 +0200505 if frame.toplevel:
Armin Ronacherbcb7c532008-04-11 16:30:34 +0200506 # if we know that we are a child template, there is no need to
507 # check if we are one
508 if self.has_known_extends:
509 return
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200510 if self.extends_so_far > 0:
511 self.writeline('if parent_root is None:')
512 self.indent()
513 level += 1
514 self.writeline('for event in context.blocks[%r][-1](context):' % node.name)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200515 self.indent()
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200516 if frame.buffer is None:
517 self.writeline('yield event')
518 else:
519 self.writeline('%s.append(event)' % frame.buffer)
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200520 self.outdent(level)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200521
522 def visit_Extends(self, node, frame):
523 """Calls the extender."""
Armin Ronacher8efc5222008-04-08 14:47:40 +0200524 if not frame.toplevel:
525 raise TemplateAssertionError('cannot use extend from a non '
526 'top-level scope', node.lineno,
527 self.filename)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200528
Armin Ronacher7fb38972008-04-11 13:54:28 +0200529 # if the number of extends statements in general is zero so
530 # far, we don't have to add a check if something extended
531 # the template before this one.
532 if self.extends_so_far > 0:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200533
Armin Ronacher7fb38972008-04-11 13:54:28 +0200534 # if we have a known extends we just add a template runtime
535 # error into the generated code. We could catch that at compile
536 # time too, but i welcome it not to confuse users by throwing the
537 # same error at different times just "because we can".
538 if not self.has_known_extends:
539 self.writeline('if parent_root is not None:')
540 self.indent()
541 self.writeline('raise TemplateRuntimeError(%r)' %
542 'extended multiple times')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200543
Armin Ronacher7fb38972008-04-11 13:54:28 +0200544 # if we have a known extends already we don't need that code here
545 # as we know that the template execution will end here.
546 if self.has_known_extends:
547 raise CompilerExit()
548 self.outdent()
549
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200550 self.writeline('parent_root = environment.get_template(', node, 1)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200551 self.visit(node.template, frame)
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200552 self.write(', %r).root_render_func' % self.filename)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200553
554 # if this extends statement was in the root level we can take
555 # advantage of that information and simplify the generated code
556 # in the top level from this point onwards
557 self.has_known_extends = True
Armin Ronachere791c2a2008-04-07 18:39:54 +0200558
Armin Ronacher7fb38972008-04-11 13:54:28 +0200559 # and now we have one more
560 self.extends_so_far += 1
561
Armin Ronacherf059ec12008-04-11 22:21:00 +0200562 def visit_Include(self, node, frame):
563 """Handles includes."""
564 # simpled include is include into a variable. This kind of
565 # include works the same on every level, so we handle it first.
566 if node.target is not None:
567 self.writeline('l_%s = ' % node.target, node)
568 if frame.toplevel:
569 self.write('context[%r] = ' % node.target)
570 self.write('IncludedTemplate(environment, context, ')
571 self.visit(node.template, frame)
572 self.write(')')
573 return
574
575 self.writeline('included_stream = environment.get_template(', node)
576 self.visit(node.template, frame)
577 self.write(').root_render_func(context, standalone=True)')
578 self.writeline('included_context = included_stream.next()')
579 self.writeline('for event in included_stream:')
580 self.indent()
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200581 if frame.buffer is None:
582 self.writeline('yield event')
583 else:
584 self.writeline('%s.append(event)' % frame.buffer)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200585 self.outdent()
586
587 # if we have a toplevel include the exported variables are copied
588 # into the current context without exporting them. context.udpate
589 # does *not* mark the variables as exported
590 if frame.toplevel:
591 self.writeline('context.update(included_context.get_exported())')
592
Armin Ronachere791c2a2008-04-07 18:39:54 +0200593 def visit_For(self, node, frame):
594 loop_frame = frame.inner()
595 loop_frame.inspect(node.iter_child_nodes())
Armin Ronachere791c2a2008-04-07 18:39:54 +0200596 extended_loop = bool(node.else_) or \
597 'loop' in loop_frame.identifiers.undeclared
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200598 if extended_loop:
599 loop_frame.identifiers.add_special('loop')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200600
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200601 aliases = self.collect_shadowed(loop_frame)
Armin Ronacher625215e2008-04-13 16:31:08 +0200602 self.pull_locals(loop_frame, indent=False)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200603 if node.else_:
604 self.writeline('l_loop = None')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200605
606 self.newline(node)
607 self.writeline('for ')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200608 self.visit(node.target, loop_frame)
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200609 self.write(extended_loop and ', l_loop in LoopContext(' or ' in ')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200610
611 # the expression pointing to the parent loop. We make the
612 # undefined a bit more debug friendly at the same time.
613 parent_loop = 'loop' in aliases and aliases['loop'] \
614 or "Undefined('loop', extra=%r)" % \
615 'the filter section of a loop as well as the ' \
616 'else block doesn\'t have access to the special ' \
617 "'loop' variable of the current loop. Because " \
618 'there is no parent loop it\'s undefined.'
619
620 # if we have an extened loop and a node test, we filter in the
621 # "outer frame".
622 if extended_loop and node.test is not None:
623 self.write('(')
624 self.visit(node.target, loop_frame)
625 self.write(' for ')
626 self.visit(node.target, loop_frame)
627 self.write(' in ')
628 self.visit(node.iter, loop_frame)
629 self.write(' if (')
630 test_frame = loop_frame.copy()
631 test_frame.name_overrides['loop'] = parent_loop
632 self.visit(node.test, test_frame)
633 self.write('))')
634
635 else:
636 self.visit(node.iter, loop_frame)
637
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200638 if 'loop' in aliases:
639 self.write(', ' + aliases['loop'])
Armin Ronachere791c2a2008-04-07 18:39:54 +0200640 self.write(extended_loop and '):' or ':')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200641
642 # tests in not extended loops become a continue
643 if not extended_loop and node.test is not None:
644 self.indent()
645 self.writeline('if ')
646 self.visit(node.test)
647 self.write(':')
648 self.indent()
649 self.writeline('continue')
650 self.outdent(2)
651
Armin Ronacher625215e2008-04-13 16:31:08 +0200652 self.blockvisit(node.body, loop_frame, force_generator=False)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200653
654 if node.else_:
655 self.writeline('if l_loop is None:')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200656 self.indent()
657 self.writeline('l_loop = ' + parent_loop)
658 self.outdent()
Armin Ronacher625215e2008-04-13 16:31:08 +0200659 self.blockvisit(node.else_, loop_frame, force_generator=False)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200660
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200661 # reset the aliases if there are any.
Armin Ronachere791c2a2008-04-07 18:39:54 +0200662 for name, alias in aliases.iteritems():
Armin Ronacher8efc5222008-04-08 14:47:40 +0200663 self.writeline('l_%s = %s' % (name, alias))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200664
665 def visit_If(self, node, frame):
Armin Ronacher75cfb862008-04-11 13:47:22 +0200666 if_frame = frame.soft()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200667 self.writeline('if ', node)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200668 self.visit(node.test, if_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200669 self.write(':')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200670 self.blockvisit(node.body, if_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200671 if node.else_:
672 self.writeline('else:')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200673 self.blockvisit(node.else_, if_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200674
Armin Ronacher8efc5222008-04-08 14:47:40 +0200675 def visit_Macro(self, node, frame):
Armin Ronacher71082072008-04-12 14:19:36 +0200676 macro_frame = self.function_scoping(node, frame)
677 args = macro_frame.arguments
Armin Ronacher8efc5222008-04-08 14:47:40 +0200678 self.writeline('def macro(%s):' % ', '.join(args), node)
Armin Ronacher625215e2008-04-13 16:31:08 +0200679 macro_frame.buffer = buf = self.temporary_identifier()
680 self.indent()
681 self.pull_locals(macro_frame, indent=False)
682 self.writeline('%s = []' % buf)
683 self.blockvisit(node.body, macro_frame, indent=False)
684 self.writeline("return TemplateData(u''.join(%s))" % buf)
685 self.outdent()
Armin Ronacher8efc5222008-04-08 14:47:40 +0200686 self.newline()
687 if frame.toplevel:
688 self.write('context[%r] = ' % node.name)
689 arg_tuple = ', '.join(repr(x.name) for x in node.args)
690 if len(node.args) == 1:
691 arg_tuple += ','
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200692 self.write('l_%s = Macro(macro, %r, (%s), (' % (node.name, node.name,
693 arg_tuple))
694 for arg in node.defaults:
Armin Ronacher625215e2008-04-13 16:31:08 +0200695 self.visit(arg, macro_frame)
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200696 self.write(', ')
Armin Ronacher00d5d212008-04-13 01:10:18 +0200697 self.write('), %s, %s)' % (
698 macro_frame.accesses_arguments and '1' or '0',
699 macro_frame.accesses_caller and '1' or '0'
Armin Ronacher71082072008-04-12 14:19:36 +0200700 ))
701
702 def visit_CallBlock(self, node, frame):
703 call_frame = self.function_scoping(node, frame)
704 args = call_frame.arguments
705 self.writeline('def call(%s):' % ', '.join(args), node)
Armin Ronacher625215e2008-04-13 16:31:08 +0200706 call_frame.buffer = buf = self.temporary_identifier()
707 self.indent()
708 self.pull_locals(call_frame, indent=False)
709 self.writeline('%s = []' % buf)
710 self.blockvisit(node.body, call_frame, indent=False)
711 self.writeline("return TemplateData(u''.join(%s))" % buf)
712 self.outdent()
Armin Ronacher71082072008-04-12 14:19:36 +0200713 arg_tuple = ', '.join(repr(x.name) for x in node.args)
714 if len(node.args) == 1:
715 arg_tuple += ','
716 self.writeline('caller = Macro(call, None, (%s), (' % arg_tuple)
717 for arg in node.defaults:
718 self.visit(arg)
719 self.write(', ')
Armin Ronacher00d5d212008-04-13 01:10:18 +0200720 self.write('), %s, 0)' % (call_frame.accesses_arguments and '1' or '0'))
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200721 if frame.buffer is None:
722 self.writeline('yield ', node)
723 else:
724 self.writeline('%s.append(' % frame.buffer, node)
Armin Ronacher71082072008-04-12 14:19:36 +0200725 self.visit_Call(node.call, call_frame, extra_kwargs='caller=caller')
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200726 if frame.buffer is not None:
727 self.write(')')
728
729 def visit_FilterBlock(self, node, frame):
730 filter_frame = frame.inner()
731 filter_frame.inspect(node.iter_child_nodes())
732
733 aliases = self.collect_shadowed(filter_frame)
Armin Ronacher625215e2008-04-13 16:31:08 +0200734 self.pull_locals(filter_frame, indent=False)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200735 filter_frame.buffer = buf = self.temporary_identifier()
736
737 self.writeline('%s = []' % buf, node)
738 for child in node.body:
739 self.visit(child, filter_frame)
740
741 if frame.buffer is None:
742 self.writeline('yield ', node)
743 else:
744 self.writeline('%s.append(' % frame.buffer, node)
745 self.visit_Filter(node.filter, filter_frame, "u''.join(%s)" % buf)
746 if frame.buffer is not None:
747 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200748
Armin Ronachere791c2a2008-04-07 18:39:54 +0200749 def visit_ExprStmt(self, node, frame):
750 self.newline(node)
751 self.visit(node, frame)
752
753 def visit_Output(self, node, frame):
Armin Ronacher75cfb862008-04-11 13:47:22 +0200754 # if we have a known extends statement, we don't output anything
Armin Ronacher7a52df82008-04-11 13:58:22 +0200755 if self.has_known_extends and frame.toplevel:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200756 return
Armin Ronachere791c2a2008-04-07 18:39:54 +0200757
Armin Ronacher75cfb862008-04-11 13:47:22 +0200758 self.newline(node)
Armin Ronacher8edbe492008-04-10 20:43:43 +0200759 if self.environment.finalize is unicode:
760 finalizer = 'unicode'
Armin Ronacherf059ec12008-04-11 22:21:00 +0200761 have_finalizer = False
Armin Ronacher8edbe492008-04-10 20:43:43 +0200762 else:
763 finalizer = 'context.finalize'
Armin Ronacherf059ec12008-04-11 22:21:00 +0200764 have_finalizer = False
Armin Ronacher8edbe492008-04-10 20:43:43 +0200765
Armin Ronacher7fb38972008-04-11 13:54:28 +0200766 # if we are in the toplevel scope and there was already an extends
767 # statement we have to add a check that disables our yield(s) here
768 # so that they don't appear in the output.
769 outdent_later = False
770 if frame.toplevel and self.extends_so_far != 0:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200771 self.writeline('if parent_root is None:')
772 self.indent()
Armin Ronacher7fb38972008-04-11 13:54:28 +0200773 outdent_later = True
Armin Ronacher75cfb862008-04-11 13:47:22 +0200774
Armin Ronachere791c2a2008-04-07 18:39:54 +0200775 # try to evaluate as many chunks as possible into a static
776 # string at compile time.
777 body = []
778 for child in node.nodes:
779 try:
780 const = unicode(child.as_const())
781 except:
782 body.append(child)
783 continue
784 if body and isinstance(body[-1], list):
785 body[-1].append(const)
786 else:
787 body.append([const])
788
789 # if we have less than 3 nodes we just yield them
790 if len(body) < 3:
791 for item in body:
792 if isinstance(item, list):
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200793 val = repr(u''.join(item))
794 if frame.buffer is None:
795 self.writeline('yield ' + val)
796 else:
797 self.writeline('%s.append(%s)' % (frame.buffer, val))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200798 else:
799 self.newline(item)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200800 if frame.buffer is None:
801 self.write('yield ')
802 else:
803 self.write('%s.append(' % frame.buffer)
804 self.write(finalizer + '(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200805 self.visit(item, frame)
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200806 self.write(')' * (1 + (frame.buffer is not None)))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200807
808 # otherwise we create a format string as this is faster in that case
809 else:
810 format = []
811 arguments = []
812 for item in body:
813 if isinstance(item, list):
814 format.append(u''.join(item).replace('%', '%%'))
815 else:
816 format.append('%s')
817 arguments.append(item)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200818 if frame.buffer is None:
819 self.writeline('yield ')
820 else:
821 self.writeline('%s.append(' % frame.buffer)
822 self.write(repr(u''.join(format)) + ' % (')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200823 idx = -1
824 for idx, argument in enumerate(arguments):
825 if idx:
826 self.write(', ')
Armin Ronacherf059ec12008-04-11 22:21:00 +0200827 if have_finalizer:
Armin Ronacher8edbe492008-04-10 20:43:43 +0200828 self.write('(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200829 self.visit(argument, frame)
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.write(idx == 0 and ',)' or ')')
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200833 if frame.buffer is not None:
834 self.write(')')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200835
Armin Ronacher7fb38972008-04-11 13:54:28 +0200836 if outdent_later:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200837 self.outdent()
838
Armin Ronacher8efc5222008-04-08 14:47:40 +0200839 def visit_Assign(self, node, frame):
840 self.newline(node)
841 # toplevel assignments however go into the local namespace and
842 # the current template's context. We create a copy of the frame
843 # here and add a set so that the Name visitor can add the assigned
844 # names here.
845 if frame.toplevel:
846 assignment_frame = frame.copy()
847 assignment_frame.assigned_names = set()
848 else:
849 assignment_frame = frame
850 self.visit(node.target, assignment_frame)
851 self.write(' = ')
852 self.visit(node.node, frame)
Armin Ronacher9706fab2008-04-08 18:49:56 +0200853
854 # make sure toplevel assignments are added to the context.
Armin Ronacher8efc5222008-04-08 14:47:40 +0200855 if frame.toplevel:
856 for name in assignment_frame.assigned_names:
857 self.writeline('context[%r] = l_%s' % (name, name))
858
Armin Ronachere791c2a2008-04-07 18:39:54 +0200859 def visit_Name(self, node, frame):
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200860 if node.ctx == 'store':
861 if frame.toplevel:
862 frame.assigned_names.add(node.name)
863 frame.name_overrides.pop(node.name, None)
864 elif node.ctx == 'load':
865 if node.name in frame.name_overrides:
866 self.write(frame.name_overrides[node.name])
867 return
Armin Ronachere791c2a2008-04-07 18:39:54 +0200868 self.write('l_' + node.name)
869
870 def visit_Const(self, node, frame):
871 val = node.value
872 if isinstance(val, float):
873 # XXX: add checks for infinity and nan
874 self.write(str(val))
875 else:
876 self.write(repr(val))
877
Armin Ronacher8efc5222008-04-08 14:47:40 +0200878 def visit_Tuple(self, node, frame):
879 self.write('(')
880 idx = -1
881 for idx, item in enumerate(node.items):
882 if idx:
883 self.write(', ')
884 self.visit(item, frame)
885 self.write(idx == 0 and ',)' or ')')
886
Armin Ronacher8edbe492008-04-10 20:43:43 +0200887 def visit_List(self, node, frame):
888 self.write('[')
889 for idx, item in enumerate(node.items):
890 if idx:
891 self.write(', ')
892 self.visit(item, frame)
893 self.write(']')
894
895 def visit_Dict(self, node, frame):
896 self.write('{')
897 for idx, item in enumerate(node.items):
898 if idx:
899 self.write(', ')
900 self.visit(item.key, frame)
901 self.write(': ')
902 self.visit(item.value, frame)
903 self.write('}')
904
Armin Ronachere791c2a2008-04-07 18:39:54 +0200905 def binop(operator):
906 def visitor(self, node, frame):
907 self.write('(')
908 self.visit(node.left, frame)
909 self.write(' %s ' % operator)
910 self.visit(node.right, frame)
911 self.write(')')
912 return visitor
913
914 def uaop(operator):
915 def visitor(self, node, frame):
916 self.write('(' + operator)
917 self.visit(node.node)
918 self.write(')')
919 return visitor
920
921 visit_Add = binop('+')
922 visit_Sub = binop('-')
923 visit_Mul = binop('*')
924 visit_Div = binop('/')
925 visit_FloorDiv = binop('//')
926 visit_Pow = binop('**')
927 visit_Mod = binop('%')
928 visit_And = binop('and')
929 visit_Or = binop('or')
930 visit_Pos = uaop('+')
931 visit_Neg = uaop('-')
932 visit_Not = uaop('not ')
933 del binop, uaop
934
935 def visit_Compare(self, node, frame):
936 self.visit(node.expr, frame)
937 for op in node.ops:
938 self.visit(op, frame)
939
940 def visit_Operand(self, node, frame):
941 self.write(' %s ' % operators[node.op])
942 self.visit(node.expr, frame)
943
944 def visit_Subscript(self, node, frame):
Armin Ronacher8efc5222008-04-08 14:47:40 +0200945 if isinstance(node.arg, nodes.Slice):
946 self.visit(node.node, frame)
947 self.write('[')
948 self.visit(node.arg, frame)
949 self.write(']')
950 return
951 try:
952 const = node.arg.as_const()
953 have_const = True
954 except nodes.Impossible:
955 have_const = False
956 if have_const:
957 if isinstance(const, (int, long, float)):
958 self.visit(node.node, frame)
959 self.write('[%s]' % const)
960 return
961 self.write('subscribe(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200962 self.visit(node.node, frame)
963 self.write(', ')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200964 if have_const:
965 self.write(repr(const))
966 else:
967 self.visit(node.arg, frame)
Armin Ronacher4dfc9752008-04-09 15:03:29 +0200968 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200969
970 def visit_Slice(self, node, frame):
971 if node.start is not None:
972 self.visit(node.start, frame)
973 self.write(':')
974 if node.stop is not None:
975 self.visit(node.stop, frame)
976 if node.step is not None:
977 self.write(':')
978 self.visit(node.step, frame)
979
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200980 def visit_Filter(self, node, frame, initial=None):
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200981 self.write('f_%s(' % node.name)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200982 if initial is not None:
983 self.write(initial)
984 else:
985 func = self.environment.filters.get(node.name)
986 if getattr(func, 'contextfilter', False):
987 self.write('context, ')
988 self.visit(node.node, frame)
Armin Ronacherd55ab532008-04-09 16:13:39 +0200989 self.signature(node, frame)
990 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200991
992 def visit_Test(self, node, frame):
Armin Ronacherf059ec12008-04-11 22:21:00 +0200993 self.write('t_%s(' % node.name)
994 func = self.environment.tests.get(node.name)
995 if getattr(func, 'contexttest', False):
996 self.write('context, ')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200997 self.visit(node.node, frame)
998 self.signature(node, frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200999 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +02001000
Armin Ronacher3d8b7842008-04-13 13:16:50 +02001001 def visit_CondExpr(self, node, frame):
1002 if not have_condexpr:
1003 self.write('((')
1004 self.visit(node.test, frame)
1005 self.write(') and (')
1006 self.visit(node.expr1, frame)
1007 self.write(',) or (')
1008 self.visit(node.expr2, frame)
1009 self.write(',))[0]')
1010 else:
1011 self.write('(')
1012 self.visit(node.expr1, frame)
1013 self.write(' if ')
1014 self.visit(node.test, frame)
1015 self.write(' else ')
1016 self.visit(node.expr2, frame)
1017 self.write(')')
1018
Armin Ronacher71082072008-04-12 14:19:36 +02001019 def visit_Call(self, node, frame, extra_kwargs=None):
Armin Ronacher8efc5222008-04-08 14:47:40 +02001020 self.visit(node.node, frame)
1021 self.write('(')
Armin Ronacher71082072008-04-12 14:19:36 +02001022 self.signature(node, frame, False, extra_kwargs)
Armin Ronacher8efc5222008-04-08 14:47:40 +02001023 self.write(')')
1024
1025 def visit_Keyword(self, node, frame):
1026 self.visit(node.key, frame)
1027 self.write('=')
1028 self.visit(node.value, frame)