blob: 63a140f6c200920f7fb5a40240b92023e3e3e5e3 [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
Christoph Hack65642a52008-04-08 14:46:56 +020032def generate(node, environment, filename, stream=None):
Armin Ronacherbcb7c532008-04-11 16:30:34 +020033 """Generate the python source for a node tree."""
Armin Ronachere791c2a2008-04-07 18:39:54 +020034 is_child = node.find(nodes.Extends) is not None
Christoph Hack65642a52008-04-08 14:46:56 +020035 generator = CodeGenerator(environment, is_child, filename, stream)
Armin Ronachere791c2a2008-04-07 18:39:54 +020036 generator.visit(node)
37 if stream is None:
38 return generator.stream.getvalue()
39
40
Armin Ronacher4dfc9752008-04-09 15:03:29 +020041def has_safe_repr(value):
42 """Does the node have a safe representation?"""
Armin Ronacherd55ab532008-04-09 16:13:39 +020043 if value is None or value is NotImplemented or value is Ellipsis:
Armin Ronacher4dfc9752008-04-09 15:03:29 +020044 return True
Armin Ronacherd55ab532008-04-09 16:13:39 +020045 if isinstance(value, (bool, int, long, float, complex, basestring,
46 StaticLoopContext)):
Armin Ronacher4dfc9752008-04-09 15:03:29 +020047 return True
Armin Ronacherd55ab532008-04-09 16:13:39 +020048 if isinstance(value, (tuple, list, set, frozenset)):
Armin Ronacher4dfc9752008-04-09 15:03:29 +020049 for item in value:
50 if not has_safe_repr(item):
51 return False
52 return True
53 elif isinstance(value, dict):
54 for key, value in value.iteritems():
55 if not has_safe_repr(key):
56 return False
57 if not has_safe_repr(value):
58 return False
59 return True
60 return False
61
62
Armin Ronachere791c2a2008-04-07 18:39:54 +020063class Identifiers(object):
64 """Tracks the status of identifiers in frames."""
65
66 def __init__(self):
67 # variables that are known to be declared (probably from outer
68 # frames or because they are special for the frame)
69 self.declared = set()
70
71 # names that are accessed without being explicitly declared by
72 # this one or any of the outer scopes. Names can appear both in
73 # declared and undeclared.
74 self.undeclared = set()
75
76 # names that are declared locally
77 self.declared_locally = set()
78
79 # names that are declared by parameters
80 self.declared_parameter = set()
81
Armin Ronacherf059ec12008-04-11 22:21:00 +020082 # filters/tests that are referenced
Armin Ronacherd4c64f72008-04-11 17:15:29 +020083 self.filters = set()
Armin Ronacherf059ec12008-04-11 22:21:00 +020084 self.tests = set()
Christoph Hack65642a52008-04-08 14:46:56 +020085
Armin Ronachere791c2a2008-04-07 18:39:54 +020086 def add_special(self, name):
87 """Register a special name like `loop`."""
88 self.undeclared.discard(name)
89 self.declared.add(name)
90
Armin Ronacher4f62a9f2008-04-08 18:09:13 +020091 def is_declared(self, name, local_only=False):
Armin Ronachere791c2a2008-04-07 18:39:54 +020092 """Check if a name is declared in this or an outer scope."""
Armin Ronacher4f62a9f2008-04-08 18:09:13 +020093 if name in self.declared_locally or name in self.declared_parameter:
94 return True
95 if local_only:
96 return False
97 return name in self.declared
Armin Ronachere791c2a2008-04-07 18:39:54 +020098
99 def find_shadowed(self):
100 """Find all the shadowed names."""
101 return self.declared & (self.declared_locally | self.declared_parameter)
102
103
104class Frame(object):
Armin Ronacher75cfb862008-04-11 13:47:22 +0200105 """Holds compile time information for us."""
Armin Ronachere791c2a2008-04-07 18:39:54 +0200106
107 def __init__(self, parent=None):
108 self.identifiers = Identifiers()
Armin Ronacher75cfb862008-04-11 13:47:22 +0200109 # a toplevel frame is the root + soft frames such as if conditions.
Armin Ronacher8efc5222008-04-08 14:47:40 +0200110 self.toplevel = False
Armin Ronacher75cfb862008-04-11 13:47:22 +0200111 # the root frame is basically just the outermost frame, so no if
112 # conditions. This information is used to optimize inheritance
113 # situations.
114 self.rootlevel = False
Armin Ronachere791c2a2008-04-07 18:39:54 +0200115 self.parent = parent
Armin Ronacher8efc5222008-04-08 14:47:40 +0200116 self.block = parent and parent.block or None
Armin Ronachere791c2a2008-04-07 18:39:54 +0200117 if parent is not None:
118 self.identifiers.declared.update(
119 parent.identifiers.declared |
Armin Ronachere791c2a2008-04-07 18:39:54 +0200120 parent.identifiers.declared_locally |
121 parent.identifiers.declared_parameter
122 )
123
Armin Ronacher8efc5222008-04-08 14:47:40 +0200124 def copy(self):
125 """Create a copy of the current one."""
126 rv = copy(self)
127 rv.identifiers = copy(self)
128 return rv
129
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200130 def inspect(self, nodes, hard_scope=False):
131 """Walk the node and check for identifiers. If the scope
132 is hard (eg: enforce on a python level) overrides from outer
133 scopes are tracked differently.
134 """
135 visitor = FrameIdentifierVisitor(self.identifiers, hard_scope)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200136 for node in nodes:
137 visitor.visit(node)
138
139 def inner(self):
140 """Return an inner frame."""
141 return Frame(self)
142
Armin Ronacher75cfb862008-04-11 13:47:22 +0200143 def soft(self):
144 """Return a soft frame. A soft frame may not be modified as
145 standalone thing as it shares the resources with the frame it
146 was created of, but it's not a rootlevel frame any longer.
147 """
148 rv = copy(self)
149 rv.rootlevel = False
150 return rv
151
Armin Ronachere791c2a2008-04-07 18:39:54 +0200152
153class FrameIdentifierVisitor(NodeVisitor):
154 """A visitor for `Frame.inspect`."""
155
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200156 def __init__(self, identifiers, hard_scope):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200157 self.identifiers = identifiers
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200158 self.hard_scope = hard_scope
Armin Ronachere791c2a2008-04-07 18:39:54 +0200159
160 def visit_Name(self, node):
161 """All assignments to names go through this function."""
162 if node.ctx in ('store', 'param'):
163 self.identifiers.declared_locally.add(node.name)
164 elif node.ctx == 'load':
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200165 if not self.identifiers.is_declared(node.name, self.hard_scope):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200166 self.identifiers.undeclared.add(node.name)
167
Armin Ronacherd55ab532008-04-09 16:13:39 +0200168 def visit_Filter(self, node):
Armin Ronacher449167d2008-04-11 17:55:05 +0200169 self.generic_visit(node)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200170 self.identifiers.filters.add(node.name)
171
172 def visit_Test(self, node):
173 self.generic_visit(node)
174 self.identifiers.tests.add(node.name)
Christoph Hack65642a52008-04-08 14:46:56 +0200175
Armin Ronachere791c2a2008-04-07 18:39:54 +0200176 def visit_Macro(self, node):
177 """Macros set local."""
178 self.identifiers.declared_locally.add(node.name)
179
Armin Ronacherf059ec12008-04-11 22:21:00 +0200180 def visit_Include(self, node):
181 """Some includes set local."""
182 self.generic_visit(node)
183 if node.target is not None:
184 self.identifiers.declared_locally.add(node.target)
185
Armin Ronacherebe55aa2008-04-10 20:51:23 +0200186 def visit_Assign(self, node):
187 """Visit assignments in the correct order."""
188 self.visit(node.node)
189 self.visit(node.target)
190
Armin Ronachere791c2a2008-04-07 18:39:54 +0200191 # stop traversing at instructions that have their own scope.
Armin Ronacher8efc5222008-04-08 14:47:40 +0200192 visit_Block = visit_CallBlock = visit_FilterBlock = \
Armin Ronachere791c2a2008-04-07 18:39:54 +0200193 visit_For = lambda s, n: None
194
195
Armin Ronacher75cfb862008-04-11 13:47:22 +0200196class CompilerExit(Exception):
197 """Raised if the compiler encountered a situation where it just
198 doesn't make sense to further process the code. Any block that
199 raises such an exception is not further processed."""
200
201
Armin Ronachere791c2a2008-04-07 18:39:54 +0200202class CodeGenerator(NodeVisitor):
203
Christoph Hack65642a52008-04-08 14:46:56 +0200204 def __init__(self, environment, is_child, filename, stream=None):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200205 if stream is None:
206 stream = StringIO()
Christoph Hack65642a52008-04-08 14:46:56 +0200207 self.environment = environment
Armin Ronachere791c2a2008-04-07 18:39:54 +0200208 self.is_child = is_child
209 self.filename = filename
210 self.stream = stream
211 self.blocks = {}
212 self.indentation = 0
213 self.new_lines = 0
214 self.last_identifier = 0
Armin Ronacher7fb38972008-04-11 13:54:28 +0200215 self.extends_so_far = 0
Armin Ronacher75cfb862008-04-11 13:47:22 +0200216 self.has_known_extends = False
Armin Ronachere791c2a2008-04-07 18:39:54 +0200217 self._last_line = 0
218 self._first_write = True
219
220 def temporary_identifier(self):
221 self.last_identifier += 1
222 return 't%d' % self.last_identifier
223
224 def indent(self):
225 self.indentation += 1
226
Armin Ronacher8efc5222008-04-08 14:47:40 +0200227 def outdent(self, step=1):
228 self.indentation -= step
Armin Ronachere791c2a2008-04-07 18:39:54 +0200229
230 def blockvisit(self, nodes, frame, force_generator=False):
231 self.indent()
232 if force_generator:
233 self.writeline('if 0: yield None')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200234 try:
235 for node in nodes:
236 self.visit(node, frame)
237 except CompilerExit:
238 pass
Armin Ronachere791c2a2008-04-07 18:39:54 +0200239 self.outdent()
240
241 def write(self, x):
242 if self.new_lines:
243 if not self._first_write:
244 self.stream.write('\n' * self.new_lines)
245 self._first_write = False
246 self.stream.write(' ' * self.indentation)
247 self.new_lines = 0
248 self.stream.write(x)
249
250 def writeline(self, x, node=None, extra=0):
251 self.newline(node, extra)
252 self.write(x)
253
254 def newline(self, node=None, extra=0):
255 self.new_lines = max(self.new_lines, 1 + extra)
256 if node is not None and node.lineno != self._last_line:
257 self.write('# line: %s' % node.lineno)
258 self.new_lines = 1
259 self._last_line = node.lineno
260
Armin Ronacher8efc5222008-04-08 14:47:40 +0200261 def signature(self, node, frame, have_comma=True):
262 have_comma = have_comma and [True] or []
263 def touch_comma():
264 if have_comma:
265 self.write(', ')
266 else:
267 have_comma.append(True)
268
269 for arg in node.args:
270 touch_comma()
271 self.visit(arg, frame)
272 for kwarg in node.kwargs:
273 touch_comma()
274 self.visit(kwarg, frame)
275 if node.dyn_args:
276 touch_comma()
277 self.visit(node.dyn_args, frame)
278 if node.dyn_kwargs:
279 touch_comma()
280 self.visit(node.dyn_kwargs, frame)
281
Armin Ronachere791c2a2008-04-07 18:39:54 +0200282 def pull_locals(self, frame, no_indent=False):
283 if not no_indent:
284 self.indent()
285 for name in frame.identifiers.undeclared:
286 self.writeline('l_%s = context[%r]' % (name, name))
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200287 for name in frame.identifiers.filters:
288 self.writeline('f_%s = environment.filters[%r]' % (name, name))
Armin Ronacherf059ec12008-04-11 22:21:00 +0200289 for name in frame.identifiers.tests:
290 self.writeline('t_%s = environment.tests[%r]' % (name, name))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200291 if not no_indent:
292 self.outdent()
293
294 # -- Visitors
295
296 def visit_Template(self, node, frame=None):
297 assert frame is None, 'no root frame allowed'
298 self.writeline('from jinja2.runtime import *')
299 self.writeline('filename = %r' % self.filename)
Armin Ronacher8edbe492008-04-10 20:43:43 +0200300
Armin Ronacher75cfb862008-04-11 13:47:22 +0200301 # do we have an extends tag at all? If not, we can save some
302 # overhead by just not processing any inheritance code.
303 have_extends = node.find(nodes.Extends) is not None
304
Armin Ronacher8edbe492008-04-10 20:43:43 +0200305 # find all blocks
306 for block in node.find_all(nodes.Block):
307 if block.name in self.blocks:
308 raise TemplateAssertionError('block %r defined twice' %
309 block.name, block.lineno,
310 self.filename)
311 self.blocks[block.name] = block
Armin Ronachere791c2a2008-04-07 18:39:54 +0200312
Armin Ronacher8efc5222008-04-08 14:47:40 +0200313 # generate the root render function.
Armin Ronacherf059ec12008-04-11 22:21:00 +0200314 self.writeline('def root(globals, environment=environment'
315 ', standalone=False):', extra=1)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200316 self.indent()
Armin Ronacherf059ec12008-04-11 22:21:00 +0200317 self.writeline('context = TemplateContext(globals, %r, blocks'
318 ', standalone)' % self.filename)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200319 if have_extends:
320 self.writeline('parent_root = None')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200321 self.outdent()
Armin Ronacher75cfb862008-04-11 13:47:22 +0200322
323 # process the root
Armin Ronachere791c2a2008-04-07 18:39:54 +0200324 frame = Frame()
325 frame.inspect(node.body)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200326 frame.toplevel = frame.rootlevel = True
Armin Ronachere791c2a2008-04-07 18:39:54 +0200327 self.pull_locals(frame)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200328 self.indent()
329 self.writeline('yield context')
330 self.outdent()
331 self.blockvisit(node.body, frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200332
Armin Ronacher8efc5222008-04-08 14:47:40 +0200333 # make sure that the parent root is called.
Armin Ronacher75cfb862008-04-11 13:47:22 +0200334 if have_extends:
335 if not self.has_known_extends:
336 self.indent()
337 self.writeline('if parent_root is not None:')
338 self.indent()
Armin Ronacherf059ec12008-04-11 22:21:00 +0200339 self.writeline('stream = parent_root(context)')
340 self.writeline('stream.next()')
341 self.writeline('for event in stream:')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200342 self.indent()
343 self.writeline('yield event')
Armin Ronacher7a52df82008-04-11 13:58:22 +0200344 self.outdent(1 + self.has_known_extends)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200345
346 # at this point we now have the blocks collected and can visit them too.
347 for name, block in self.blocks.iteritems():
348 block_frame = Frame()
349 block_frame.inspect(block.body)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200350 block_frame.block = name
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200351 self.writeline('def block_%s(context, environment=environment):'
352 % name, block, 1)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200353 self.pull_locals(block_frame)
354 self.blockvisit(block.body, block_frame, True)
355
Armin Ronacher75cfb862008-04-11 13:47:22 +0200356 self.writeline('blocks = {%s}' % ', '.join('%r: block_%s' % (x, x)
357 for x in self.blocks), extra=1)
358
Armin Ronachere791c2a2008-04-07 18:39:54 +0200359 def visit_Block(self, node, frame):
360 """Call a block and register it for the template."""
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200361 level = 1
Armin Ronacher75cfb862008-04-11 13:47:22 +0200362 if frame.toplevel:
Armin Ronacherbcb7c532008-04-11 16:30:34 +0200363 # if we know that we are a child template, there is no need to
364 # check if we are one
365 if self.has_known_extends:
366 return
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200367 if self.extends_so_far > 0:
368 self.writeline('if parent_root is None:')
369 self.indent()
370 level += 1
371 self.writeline('for event in context.blocks[%r][-1](context):' % node.name)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200372 self.indent()
373 self.writeline('yield event')
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200374 self.outdent(level)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200375
376 def visit_Extends(self, node, frame):
377 """Calls the extender."""
Armin Ronacher8efc5222008-04-08 14:47:40 +0200378 if not frame.toplevel:
379 raise TemplateAssertionError('cannot use extend from a non '
380 'top-level scope', node.lineno,
381 self.filename)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200382
Armin Ronacher7fb38972008-04-11 13:54:28 +0200383 # if the number of extends statements in general is zero so
384 # far, we don't have to add a check if something extended
385 # the template before this one.
386 if self.extends_so_far > 0:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200387
Armin Ronacher7fb38972008-04-11 13:54:28 +0200388 # if we have a known extends we just add a template runtime
389 # error into the generated code. We could catch that at compile
390 # time too, but i welcome it not to confuse users by throwing the
391 # same error at different times just "because we can".
392 if not self.has_known_extends:
393 self.writeline('if parent_root is not None:')
394 self.indent()
395 self.writeline('raise TemplateRuntimeError(%r)' %
396 'extended multiple times')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200397
Armin Ronacher7fb38972008-04-11 13:54:28 +0200398 # if we have a known extends already we don't need that code here
399 # as we know that the template execution will end here.
400 if self.has_known_extends:
401 raise CompilerExit()
402 self.outdent()
403
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200404 self.writeline('parent_root = environment.get_template(', node, 1)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200405 self.visit(node.template, frame)
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200406 self.write(', %r).root_render_func' % self.filename)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200407
408 # if this extends statement was in the root level we can take
409 # advantage of that information and simplify the generated code
410 # in the top level from this point onwards
411 self.has_known_extends = True
Armin Ronachere791c2a2008-04-07 18:39:54 +0200412
Armin Ronacher7fb38972008-04-11 13:54:28 +0200413 # and now we have one more
414 self.extends_so_far += 1
415
Armin Ronacherf059ec12008-04-11 22:21:00 +0200416 def visit_Include(self, node, frame):
417 """Handles includes."""
418 # simpled include is include into a variable. This kind of
419 # include works the same on every level, so we handle it first.
420 if node.target is not None:
421 self.writeline('l_%s = ' % node.target, node)
422 if frame.toplevel:
423 self.write('context[%r] = ' % node.target)
424 self.write('IncludedTemplate(environment, context, ')
425 self.visit(node.template, frame)
426 self.write(')')
427 return
428
429 self.writeline('included_stream = environment.get_template(', node)
430 self.visit(node.template, frame)
431 self.write(').root_render_func(context, standalone=True)')
432 self.writeline('included_context = included_stream.next()')
433 self.writeline('for event in included_stream:')
434 self.indent()
435 self.writeline('yield event')
436 self.outdent()
437
438 # if we have a toplevel include the exported variables are copied
439 # into the current context without exporting them. context.udpate
440 # does *not* mark the variables as exported
441 if frame.toplevel:
442 self.writeline('context.update(included_context.get_exported())')
443
Armin Ronachere791c2a2008-04-07 18:39:54 +0200444 def visit_For(self, node, frame):
445 loop_frame = frame.inner()
446 loop_frame.inspect(node.iter_child_nodes())
Armin Ronachere791c2a2008-04-07 18:39:54 +0200447 extended_loop = bool(node.else_) or \
448 'loop' in loop_frame.identifiers.undeclared
Armin Ronacher8efc5222008-04-08 14:47:40 +0200449 loop_frame.identifiers.add_special('loop')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200450
451 # make sure we "backup" overridden, local identifiers
452 # TODO: we should probably optimize this and check if the
453 # identifier is in use afterwards.
454 aliases = {}
455 for name in loop_frame.identifiers.find_shadowed():
456 aliases[name] = ident = self.temporary_identifier()
457 self.writeline('%s = l_%s' % (ident, name))
458
459 self.pull_locals(loop_frame, True)
460
461 self.newline(node)
462 if node.else_:
463 self.writeline('l_loop = None')
464 self.write('for ')
465 self.visit(node.target, loop_frame)
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200466 self.write(extended_loop and ', l_loop in LoopContext(' or ' in ')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200467 self.visit(node.iter, loop_frame)
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200468 if 'loop' in aliases:
469 self.write(', ' + aliases['loop'])
Armin Ronachere791c2a2008-04-07 18:39:54 +0200470 self.write(extended_loop and '):' or ':')
471 self.blockvisit(node.body, loop_frame)
472
473 if node.else_:
474 self.writeline('if l_loop is None:')
475 self.blockvisit(node.else_, loop_frame)
476
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200477 # reset the aliases if there are any.
Armin Ronachere791c2a2008-04-07 18:39:54 +0200478 for name, alias in aliases.iteritems():
Armin Ronacher8efc5222008-04-08 14:47:40 +0200479 self.writeline('l_%s = %s' % (name, alias))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200480
481 def visit_If(self, node, frame):
Armin Ronacher75cfb862008-04-11 13:47:22 +0200482 if_frame = frame.soft()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200483 self.writeline('if ', node)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200484 self.visit(node.test, if_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200485 self.write(':')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200486 self.blockvisit(node.body, if_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200487 if node.else_:
488 self.writeline('else:')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200489 self.blockvisit(node.else_, if_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200490
Armin Ronacher8efc5222008-04-08 14:47:40 +0200491 def visit_Macro(self, node, frame):
492 macro_frame = frame.inner()
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200493 macro_frame.inspect(node.iter_child_nodes(), hard_scope=True)
494
495 # variables that are undeclared (accessed before declaration) and
496 # declared locally *and* part of an outside scope raise a template
497 # assertion error. Reason: we can't generate reasonable code from
498 # it without aliasing all the variables. XXX: alias them ^^
499 overriden_closure_vars = (
500 macro_frame.identifiers.undeclared &
501 macro_frame.identifiers.declared &
502 (macro_frame.identifiers.declared_locally |
503 macro_frame.identifiers.declared_parameter)
504 )
505 if overriden_closure_vars:
506 vars = ', '.join(sorted(overriden_closure_vars))
507 raise TemplateAssertionError('It\'s not possible to set and '
508 'access variables derived from '
509 'an outer scope! (affects: %s' %
510 vars, node.lineno, self.filename)
511
512 # remove variables from a closure from the frame's undeclared
513 # identifiers.
514 macro_frame.identifiers.undeclared -= (
515 macro_frame.identifiers.undeclared &
516 macro_frame.identifiers.declared
517 )
518
Armin Ronacher8efc5222008-04-08 14:47:40 +0200519 args = ['l_' + x.name for x in node.args]
520 if 'arguments' in macro_frame.identifiers.undeclared:
521 accesses_arguments = True
522 args.append('l_arguments')
523 else:
524 accesses_arguments = False
525 self.writeline('def macro(%s):' % ', '.join(args), node)
526 self.indent()
527 self.writeline('if 0: yield None')
528 self.outdent()
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200529 self.pull_locals(macro_frame)
530 self.blockvisit(node.body, macro_frame)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200531 self.newline()
532 if frame.toplevel:
533 self.write('context[%r] = ' % node.name)
534 arg_tuple = ', '.join(repr(x.name) for x in node.args)
535 if len(node.args) == 1:
536 arg_tuple += ','
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200537 self.write('l_%s = Macro(macro, %r, (%s), (' % (node.name, node.name,
538 arg_tuple))
539 for arg in node.defaults:
540 self.visit(arg)
541 self.write(', ')
Armin Ronacher4dfc9752008-04-09 15:03:29 +0200542 self.write('), %r)' % accesses_arguments)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200543
Armin Ronachere791c2a2008-04-07 18:39:54 +0200544 def visit_ExprStmt(self, node, frame):
545 self.newline(node)
546 self.visit(node, frame)
547
548 def visit_Output(self, node, frame):
Armin Ronacher75cfb862008-04-11 13:47:22 +0200549 # if we have a known extends statement, we don't output anything
Armin Ronacher7a52df82008-04-11 13:58:22 +0200550 if self.has_known_extends and frame.toplevel:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200551 return
Armin Ronachere791c2a2008-04-07 18:39:54 +0200552
Armin Ronacher75cfb862008-04-11 13:47:22 +0200553 self.newline(node)
Armin Ronacher8edbe492008-04-10 20:43:43 +0200554 if self.environment.finalize is unicode:
555 finalizer = 'unicode'
Armin Ronacherf059ec12008-04-11 22:21:00 +0200556 have_finalizer = False
Armin Ronacher8edbe492008-04-10 20:43:43 +0200557 else:
558 finalizer = 'context.finalize'
Armin Ronacherf059ec12008-04-11 22:21:00 +0200559 have_finalizer = False
Armin Ronacher8edbe492008-04-10 20:43:43 +0200560
Armin Ronacher7fb38972008-04-11 13:54:28 +0200561 # if we are in the toplevel scope and there was already an extends
562 # statement we have to add a check that disables our yield(s) here
563 # so that they don't appear in the output.
564 outdent_later = False
565 if frame.toplevel and self.extends_so_far != 0:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200566 self.writeline('if parent_root is None:')
567 self.indent()
Armin Ronacher7fb38972008-04-11 13:54:28 +0200568 outdent_later = True
Armin Ronacher75cfb862008-04-11 13:47:22 +0200569
Armin Ronachere791c2a2008-04-07 18:39:54 +0200570 # try to evaluate as many chunks as possible into a static
571 # string at compile time.
572 body = []
573 for child in node.nodes:
574 try:
575 const = unicode(child.as_const())
576 except:
577 body.append(child)
578 continue
579 if body and isinstance(body[-1], list):
580 body[-1].append(const)
581 else:
582 body.append([const])
583
584 # if we have less than 3 nodes we just yield them
585 if len(body) < 3:
586 for item in body:
587 if isinstance(item, list):
588 self.writeline('yield %s' % repr(u''.join(item)))
589 else:
590 self.newline(item)
Armin Ronacher8edbe492008-04-10 20:43:43 +0200591 self.write('yield %s(' % finalizer)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200592 self.visit(item, frame)
593 self.write(')')
594
595 # otherwise we create a format string as this is faster in that case
596 else:
597 format = []
598 arguments = []
599 for item in body:
600 if isinstance(item, list):
601 format.append(u''.join(item).replace('%', '%%'))
602 else:
603 format.append('%s')
604 arguments.append(item)
605 self.writeline('yield %r %% (' % u''.join(format))
606 idx = -1
607 for idx, argument in enumerate(arguments):
608 if idx:
609 self.write(', ')
Armin Ronacherf059ec12008-04-11 22:21:00 +0200610 if have_finalizer:
Armin Ronacher8edbe492008-04-10 20:43:43 +0200611 self.write('(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200612 self.visit(argument, frame)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200613 if have_finalizer:
Armin Ronacher8edbe492008-04-10 20:43:43 +0200614 self.write(')')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200615 self.write(idx == 0 and ',)' or ')')
616
Armin Ronacher7fb38972008-04-11 13:54:28 +0200617 if outdent_later:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200618 self.outdent()
619
Armin Ronacher8efc5222008-04-08 14:47:40 +0200620 def visit_Assign(self, node, frame):
621 self.newline(node)
622 # toplevel assignments however go into the local namespace and
623 # the current template's context. We create a copy of the frame
624 # here and add a set so that the Name visitor can add the assigned
625 # names here.
626 if frame.toplevel:
627 assignment_frame = frame.copy()
628 assignment_frame.assigned_names = set()
629 else:
630 assignment_frame = frame
631 self.visit(node.target, assignment_frame)
632 self.write(' = ')
633 self.visit(node.node, frame)
Armin Ronacher9706fab2008-04-08 18:49:56 +0200634
635 # make sure toplevel assignments are added to the context.
Armin Ronacher8efc5222008-04-08 14:47:40 +0200636 if frame.toplevel:
637 for name in assignment_frame.assigned_names:
638 self.writeline('context[%r] = l_%s' % (name, name))
639
Armin Ronachere791c2a2008-04-07 18:39:54 +0200640 def visit_Name(self, node, frame):
Armin Ronacher8efc5222008-04-08 14:47:40 +0200641 if frame.toplevel and node.ctx == 'store':
642 frame.assigned_names.add(node.name)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200643 self.write('l_' + node.name)
644
645 def visit_Const(self, node, frame):
646 val = node.value
647 if isinstance(val, float):
648 # XXX: add checks for infinity and nan
649 self.write(str(val))
650 else:
651 self.write(repr(val))
652
Armin Ronacher8efc5222008-04-08 14:47:40 +0200653 def visit_Tuple(self, node, frame):
654 self.write('(')
655 idx = -1
656 for idx, item in enumerate(node.items):
657 if idx:
658 self.write(', ')
659 self.visit(item, frame)
660 self.write(idx == 0 and ',)' or ')')
661
Armin Ronacher8edbe492008-04-10 20:43:43 +0200662 def visit_List(self, node, frame):
663 self.write('[')
664 for idx, item in enumerate(node.items):
665 if idx:
666 self.write(', ')
667 self.visit(item, frame)
668 self.write(']')
669
670 def visit_Dict(self, node, frame):
671 self.write('{')
672 for idx, item in enumerate(node.items):
673 if idx:
674 self.write(', ')
675 self.visit(item.key, frame)
676 self.write(': ')
677 self.visit(item.value, frame)
678 self.write('}')
679
Armin Ronachere791c2a2008-04-07 18:39:54 +0200680 def binop(operator):
681 def visitor(self, node, frame):
682 self.write('(')
683 self.visit(node.left, frame)
684 self.write(' %s ' % operator)
685 self.visit(node.right, frame)
686 self.write(')')
687 return visitor
688
689 def uaop(operator):
690 def visitor(self, node, frame):
691 self.write('(' + operator)
692 self.visit(node.node)
693 self.write(')')
694 return visitor
695
696 visit_Add = binop('+')
697 visit_Sub = binop('-')
698 visit_Mul = binop('*')
699 visit_Div = binop('/')
700 visit_FloorDiv = binop('//')
701 visit_Pow = binop('**')
702 visit_Mod = binop('%')
703 visit_And = binop('and')
704 visit_Or = binop('or')
705 visit_Pos = uaop('+')
706 visit_Neg = uaop('-')
707 visit_Not = uaop('not ')
708 del binop, uaop
709
710 def visit_Compare(self, node, frame):
711 self.visit(node.expr, frame)
712 for op in node.ops:
713 self.visit(op, frame)
714
715 def visit_Operand(self, node, frame):
716 self.write(' %s ' % operators[node.op])
717 self.visit(node.expr, frame)
718
719 def visit_Subscript(self, node, frame):
Armin Ronacher8efc5222008-04-08 14:47:40 +0200720 if isinstance(node.arg, nodes.Slice):
721 self.visit(node.node, frame)
722 self.write('[')
723 self.visit(node.arg, frame)
724 self.write(']')
725 return
726 try:
727 const = node.arg.as_const()
728 have_const = True
729 except nodes.Impossible:
730 have_const = False
731 if have_const:
732 if isinstance(const, (int, long, float)):
733 self.visit(node.node, frame)
734 self.write('[%s]' % const)
735 return
736 self.write('subscribe(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200737 self.visit(node.node, frame)
738 self.write(', ')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200739 if have_const:
740 self.write(repr(const))
741 else:
742 self.visit(node.arg, frame)
Armin Ronacher4dfc9752008-04-09 15:03:29 +0200743 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200744
745 def visit_Slice(self, node, frame):
746 if node.start is not None:
747 self.visit(node.start, frame)
748 self.write(':')
749 if node.stop is not None:
750 self.visit(node.stop, frame)
751 if node.step is not None:
752 self.write(':')
753 self.visit(node.step, frame)
754
755 def visit_Filter(self, node, frame):
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200756 self.write('f_%s(' % node.name)
Armin Ronacherf059ec12008-04-11 22:21:00 +0200757 func = self.environment.filters.get(node.name)
758 if getattr(func, 'contextfilter', False):
759 self.write('context, ')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200760 self.visit(node.node, frame)
Armin Ronacherd55ab532008-04-09 16:13:39 +0200761 self.signature(node, frame)
762 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200763
764 def visit_Test(self, node, frame):
Armin Ronacherf059ec12008-04-11 22:21:00 +0200765 self.write('t_%s(' % node.name)
766 func = self.environment.tests.get(node.name)
767 if getattr(func, 'contexttest', False):
768 self.write('context, ')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200769 self.visit(node.node, frame)
770 self.signature(node, frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200771 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200772
773 def visit_Call(self, node, frame):
774 self.visit(node.node, frame)
775 self.write('(')
776 self.signature(node, frame, False)
777 self.write(')')
778
779 def visit_Keyword(self, node, frame):
780 self.visit(node.key, frame)
781 self.write('=')
782 self.visit(node.value, frame)