blob: 74cd0a52774b27a1008b9379cd3cc6c86a30d0cb [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 Ronacherd4c64f72008-04-11 17:15:29 +020082 # filters that are referenced
83 self.filters = set()
Christoph Hack65642a52008-04-08 14:46:56 +020084
Armin Ronachere791c2a2008-04-07 18:39:54 +020085 def add_special(self, name):
86 """Register a special name like `loop`."""
87 self.undeclared.discard(name)
88 self.declared.add(name)
89
Armin Ronacher4f62a9f2008-04-08 18:09:13 +020090 def is_declared(self, name, local_only=False):
Armin Ronachere791c2a2008-04-07 18:39:54 +020091 """Check if a name is declared in this or an outer scope."""
Armin Ronacher4f62a9f2008-04-08 18:09:13 +020092 if name in self.declared_locally or name in self.declared_parameter:
93 return True
94 if local_only:
95 return False
96 return name in self.declared
Armin Ronachere791c2a2008-04-07 18:39:54 +020097
98 def find_shadowed(self):
99 """Find all the shadowed names."""
100 return self.declared & (self.declared_locally | self.declared_parameter)
101
102
103class Frame(object):
Armin Ronacher75cfb862008-04-11 13:47:22 +0200104 """Holds compile time information for us."""
Armin Ronachere791c2a2008-04-07 18:39:54 +0200105
106 def __init__(self, parent=None):
107 self.identifiers = Identifiers()
Armin Ronacher75cfb862008-04-11 13:47:22 +0200108 # a toplevel frame is the root + soft frames such as if conditions.
Armin Ronacher8efc5222008-04-08 14:47:40 +0200109 self.toplevel = False
Armin Ronacher75cfb862008-04-11 13:47:22 +0200110 # the root frame is basically just the outermost frame, so no if
111 # conditions. This information is used to optimize inheritance
112 # situations.
113 self.rootlevel = False
Armin Ronachere791c2a2008-04-07 18:39:54 +0200114 self.parent = parent
Armin Ronacher8efc5222008-04-08 14:47:40 +0200115 self.block = parent and parent.block or None
Armin Ronachere791c2a2008-04-07 18:39:54 +0200116 if parent is not None:
117 self.identifiers.declared.update(
118 parent.identifiers.declared |
Armin Ronachere791c2a2008-04-07 18:39:54 +0200119 parent.identifiers.declared_locally |
120 parent.identifiers.declared_parameter
121 )
122
Armin Ronacher8efc5222008-04-08 14:47:40 +0200123 def copy(self):
124 """Create a copy of the current one."""
125 rv = copy(self)
126 rv.identifiers = copy(self)
127 return rv
128
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200129 def inspect(self, nodes, hard_scope=False):
130 """Walk the node and check for identifiers. If the scope
131 is hard (eg: enforce on a python level) overrides from outer
132 scopes are tracked differently.
133 """
134 visitor = FrameIdentifierVisitor(self.identifiers, hard_scope)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200135 for node in nodes:
136 visitor.visit(node)
137
138 def inner(self):
139 """Return an inner frame."""
140 return Frame(self)
141
Armin Ronacher75cfb862008-04-11 13:47:22 +0200142 def soft(self):
143 """Return a soft frame. A soft frame may not be modified as
144 standalone thing as it shares the resources with the frame it
145 was created of, but it's not a rootlevel frame any longer.
146 """
147 rv = copy(self)
148 rv.rootlevel = False
149 return rv
150
Armin Ronachere791c2a2008-04-07 18:39:54 +0200151
152class FrameIdentifierVisitor(NodeVisitor):
153 """A visitor for `Frame.inspect`."""
154
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200155 def __init__(self, identifiers, hard_scope):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200156 self.identifiers = identifiers
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200157 self.hard_scope = hard_scope
Armin Ronachere791c2a2008-04-07 18:39:54 +0200158
159 def visit_Name(self, node):
160 """All assignments to names go through this function."""
161 if node.ctx in ('store', 'param'):
162 self.identifiers.declared_locally.add(node.name)
163 elif node.ctx == 'load':
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200164 if not self.identifiers.is_declared(node.name, self.hard_scope):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200165 self.identifiers.undeclared.add(node.name)
166
Armin Ronacherd55ab532008-04-09 16:13:39 +0200167 def visit_Filter(self, node):
Armin Ronacher449167d2008-04-11 17:55:05 +0200168 self.generic_visit(node)
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200169 if node.name not in self.identifiers.filters:
170 self.identifiers.filters.add(node.name)
Christoph Hack65642a52008-04-08 14:46:56 +0200171
Armin Ronachere791c2a2008-04-07 18:39:54 +0200172 def visit_Macro(self, node):
173 """Macros set local."""
174 self.identifiers.declared_locally.add(node.name)
175
Armin Ronacherebe55aa2008-04-10 20:51:23 +0200176 def visit_Assign(self, node):
177 """Visit assignments in the correct order."""
178 self.visit(node.node)
179 self.visit(node.target)
180
Armin Ronachere791c2a2008-04-07 18:39:54 +0200181 # stop traversing at instructions that have their own scope.
Armin Ronacher8efc5222008-04-08 14:47:40 +0200182 visit_Block = visit_CallBlock = visit_FilterBlock = \
Armin Ronachere791c2a2008-04-07 18:39:54 +0200183 visit_For = lambda s, n: None
184
185
Armin Ronacher75cfb862008-04-11 13:47:22 +0200186class CompilerExit(Exception):
187 """Raised if the compiler encountered a situation where it just
188 doesn't make sense to further process the code. Any block that
189 raises such an exception is not further processed."""
190
191
Armin Ronachere791c2a2008-04-07 18:39:54 +0200192class CodeGenerator(NodeVisitor):
193
Christoph Hack65642a52008-04-08 14:46:56 +0200194 def __init__(self, environment, is_child, filename, stream=None):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200195 if stream is None:
196 stream = StringIO()
Christoph Hack65642a52008-04-08 14:46:56 +0200197 self.environment = environment
Armin Ronachere791c2a2008-04-07 18:39:54 +0200198 self.is_child = is_child
199 self.filename = filename
200 self.stream = stream
201 self.blocks = {}
202 self.indentation = 0
203 self.new_lines = 0
204 self.last_identifier = 0
Armin Ronacher7fb38972008-04-11 13:54:28 +0200205 self.extends_so_far = 0
Armin Ronacher75cfb862008-04-11 13:47:22 +0200206 self.has_known_extends = False
Armin Ronachere791c2a2008-04-07 18:39:54 +0200207 self._last_line = 0
208 self._first_write = True
209
210 def temporary_identifier(self):
211 self.last_identifier += 1
212 return 't%d' % self.last_identifier
213
214 def indent(self):
215 self.indentation += 1
216
Armin Ronacher8efc5222008-04-08 14:47:40 +0200217 def outdent(self, step=1):
218 self.indentation -= step
Armin Ronachere791c2a2008-04-07 18:39:54 +0200219
220 def blockvisit(self, nodes, frame, force_generator=False):
221 self.indent()
222 if force_generator:
223 self.writeline('if 0: yield None')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200224 try:
225 for node in nodes:
226 self.visit(node, frame)
227 except CompilerExit:
228 pass
Armin Ronachere791c2a2008-04-07 18:39:54 +0200229 self.outdent()
230
231 def write(self, x):
232 if self.new_lines:
233 if not self._first_write:
234 self.stream.write('\n' * self.new_lines)
235 self._first_write = False
236 self.stream.write(' ' * self.indentation)
237 self.new_lines = 0
238 self.stream.write(x)
239
240 def writeline(self, x, node=None, extra=0):
241 self.newline(node, extra)
242 self.write(x)
243
244 def newline(self, node=None, extra=0):
245 self.new_lines = max(self.new_lines, 1 + extra)
246 if node is not None and node.lineno != self._last_line:
247 self.write('# line: %s' % node.lineno)
248 self.new_lines = 1
249 self._last_line = node.lineno
250
Armin Ronacher8efc5222008-04-08 14:47:40 +0200251 def signature(self, node, frame, have_comma=True):
252 have_comma = have_comma and [True] or []
253 def touch_comma():
254 if have_comma:
255 self.write(', ')
256 else:
257 have_comma.append(True)
258
259 for arg in node.args:
260 touch_comma()
261 self.visit(arg, frame)
262 for kwarg in node.kwargs:
263 touch_comma()
264 self.visit(kwarg, frame)
265 if node.dyn_args:
266 touch_comma()
267 self.visit(node.dyn_args, frame)
268 if node.dyn_kwargs:
269 touch_comma()
270 self.visit(node.dyn_kwargs, frame)
271
Armin Ronachere791c2a2008-04-07 18:39:54 +0200272 def pull_locals(self, frame, no_indent=False):
273 if not no_indent:
274 self.indent()
275 for name in frame.identifiers.undeclared:
276 self.writeline('l_%s = context[%r]' % (name, name))
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200277 for name in frame.identifiers.filters:
278 self.writeline('f_%s = environment.filters[%r]' % (name, name))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200279 if not no_indent:
280 self.outdent()
281
282 # -- Visitors
283
284 def visit_Template(self, node, frame=None):
285 assert frame is None, 'no root frame allowed'
286 self.writeline('from jinja2.runtime import *')
287 self.writeline('filename = %r' % self.filename)
Armin Ronacher8edbe492008-04-10 20:43:43 +0200288
Armin Ronacher75cfb862008-04-11 13:47:22 +0200289 # do we have an extends tag at all? If not, we can save some
290 # overhead by just not processing any inheritance code.
291 have_extends = node.find(nodes.Extends) is not None
292
Armin Ronacher8edbe492008-04-10 20:43:43 +0200293 # find all blocks
294 for block in node.find_all(nodes.Block):
295 if block.name in self.blocks:
296 raise TemplateAssertionError('block %r defined twice' %
297 block.name, block.lineno,
298 self.filename)
299 self.blocks[block.name] = block
Armin Ronachere791c2a2008-04-07 18:39:54 +0200300
Armin Ronacher8efc5222008-04-08 14:47:40 +0200301 # generate the root render function.
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200302 self.writeline('def root(globals, environment=environment):', extra=1)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200303 self.indent()
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200304 self.writeline('context = TemplateContext(globals, %r, blocks)' %
305 self.filename)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200306 if have_extends:
307 self.writeline('parent_root = None')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200308 self.outdent()
Armin Ronacher75cfb862008-04-11 13:47:22 +0200309
310 # process the root
Armin Ronachere791c2a2008-04-07 18:39:54 +0200311 frame = Frame()
312 frame.inspect(node.body)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200313 frame.toplevel = frame.rootlevel = True
Armin Ronachere791c2a2008-04-07 18:39:54 +0200314 self.pull_locals(frame)
315 self.blockvisit(node.body, frame, True)
316
Armin Ronacher8efc5222008-04-08 14:47:40 +0200317 # make sure that the parent root is called.
Armin Ronacher75cfb862008-04-11 13:47:22 +0200318 if have_extends:
319 if not self.has_known_extends:
320 self.indent()
321 self.writeline('if parent_root is not None:')
322 self.indent()
323 self.writeline('for event in parent_root(context):')
324 self.indent()
325 self.writeline('yield event')
Armin Ronacher7a52df82008-04-11 13:58:22 +0200326 self.outdent(1 + self.has_known_extends)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200327
328 # at this point we now have the blocks collected and can visit them too.
329 for name, block in self.blocks.iteritems():
330 block_frame = Frame()
331 block_frame.inspect(block.body)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200332 block_frame.block = name
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200333 self.writeline('def block_%s(context, environment=environment):'
334 % name, block, 1)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200335 self.pull_locals(block_frame)
336 self.blockvisit(block.body, block_frame, True)
337
Armin Ronacher75cfb862008-04-11 13:47:22 +0200338 self.writeline('blocks = {%s}' % ', '.join('%r: block_%s' % (x, x)
339 for x in self.blocks), extra=1)
340
Armin Ronachere791c2a2008-04-07 18:39:54 +0200341 def visit_Block(self, node, frame):
342 """Call a block and register it for the template."""
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200343 level = 1
Armin Ronacher75cfb862008-04-11 13:47:22 +0200344 if frame.toplevel:
Armin Ronacherbcb7c532008-04-11 16:30:34 +0200345 # if we know that we are a child template, there is no need to
346 # check if we are one
347 if self.has_known_extends:
348 return
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200349 if self.extends_so_far > 0:
350 self.writeline('if parent_root is None:')
351 self.indent()
352 level += 1
353 self.writeline('for event in context.blocks[%r][-1](context):' % node.name)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200354 self.indent()
355 self.writeline('yield event')
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200356 self.outdent(level)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200357
358 def visit_Extends(self, node, frame):
359 """Calls the extender."""
Armin Ronacher8efc5222008-04-08 14:47:40 +0200360 if not frame.toplevel:
361 raise TemplateAssertionError('cannot use extend from a non '
362 'top-level scope', node.lineno,
363 self.filename)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200364
Armin Ronacher7fb38972008-04-11 13:54:28 +0200365 # if the number of extends statements in general is zero so
366 # far, we don't have to add a check if something extended
367 # the template before this one.
368 if self.extends_so_far > 0:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200369
Armin Ronacher7fb38972008-04-11 13:54:28 +0200370 # if we have a known extends we just add a template runtime
371 # error into the generated code. We could catch that at compile
372 # time too, but i welcome it not to confuse users by throwing the
373 # same error at different times just "because we can".
374 if not self.has_known_extends:
375 self.writeline('if parent_root is not None:')
376 self.indent()
377 self.writeline('raise TemplateRuntimeError(%r)' %
378 'extended multiple times')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200379
Armin Ronacher7fb38972008-04-11 13:54:28 +0200380 # if we have a known extends already we don't need that code here
381 # as we know that the template execution will end here.
382 if self.has_known_extends:
383 raise CompilerExit()
384 self.outdent()
385
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200386 self.writeline('parent_root = environment.get_template(', node, 1)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200387 self.visit(node.template, frame)
Armin Ronacher41ef36f2008-04-11 19:55:08 +0200388 self.write(', %r).root_render_func' % self.filename)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200389
390 # if this extends statement was in the root level we can take
391 # advantage of that information and simplify the generated code
392 # in the top level from this point onwards
393 self.has_known_extends = True
Armin Ronachere791c2a2008-04-07 18:39:54 +0200394
Armin Ronacher7fb38972008-04-11 13:54:28 +0200395 # and now we have one more
396 self.extends_so_far += 1
397
Armin Ronachere791c2a2008-04-07 18:39:54 +0200398 def visit_For(self, node, frame):
399 loop_frame = frame.inner()
400 loop_frame.inspect(node.iter_child_nodes())
Armin Ronachere791c2a2008-04-07 18:39:54 +0200401 extended_loop = bool(node.else_) or \
402 'loop' in loop_frame.identifiers.undeclared
Armin Ronacher8efc5222008-04-08 14:47:40 +0200403 loop_frame.identifiers.add_special('loop')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200404
405 # make sure we "backup" overridden, local identifiers
406 # TODO: we should probably optimize this and check if the
407 # identifier is in use afterwards.
408 aliases = {}
409 for name in loop_frame.identifiers.find_shadowed():
410 aliases[name] = ident = self.temporary_identifier()
411 self.writeline('%s = l_%s' % (ident, name))
412
413 self.pull_locals(loop_frame, True)
414
415 self.newline(node)
416 if node.else_:
417 self.writeline('l_loop = None')
418 self.write('for ')
419 self.visit(node.target, loop_frame)
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200420 self.write(extended_loop and ', l_loop in LoopContext(' or ' in ')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200421 self.visit(node.iter, loop_frame)
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200422 if 'loop' in aliases:
423 self.write(', ' + aliases['loop'])
Armin Ronachere791c2a2008-04-07 18:39:54 +0200424 self.write(extended_loop and '):' or ':')
425 self.blockvisit(node.body, loop_frame)
426
427 if node.else_:
428 self.writeline('if l_loop is None:')
429 self.blockvisit(node.else_, loop_frame)
430
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200431 # reset the aliases if there are any.
Armin Ronachere791c2a2008-04-07 18:39:54 +0200432 for name, alias in aliases.iteritems():
Armin Ronacher8efc5222008-04-08 14:47:40 +0200433 self.writeline('l_%s = %s' % (name, alias))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200434
435 def visit_If(self, node, frame):
Armin Ronacher75cfb862008-04-11 13:47:22 +0200436 if_frame = frame.soft()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200437 self.writeline('if ', node)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200438 self.visit(node.test, if_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200439 self.write(':')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200440 self.blockvisit(node.body, if_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200441 if node.else_:
442 self.writeline('else:')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200443 self.blockvisit(node.else_, if_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200444
Armin Ronacher8efc5222008-04-08 14:47:40 +0200445 def visit_Macro(self, node, frame):
446 macro_frame = frame.inner()
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200447 macro_frame.inspect(node.iter_child_nodes(), hard_scope=True)
448
449 # variables that are undeclared (accessed before declaration) and
450 # declared locally *and* part of an outside scope raise a template
451 # assertion error. Reason: we can't generate reasonable code from
452 # it without aliasing all the variables. XXX: alias them ^^
453 overriden_closure_vars = (
454 macro_frame.identifiers.undeclared &
455 macro_frame.identifiers.declared &
456 (macro_frame.identifiers.declared_locally |
457 macro_frame.identifiers.declared_parameter)
458 )
459 if overriden_closure_vars:
460 vars = ', '.join(sorted(overriden_closure_vars))
461 raise TemplateAssertionError('It\'s not possible to set and '
462 'access variables derived from '
463 'an outer scope! (affects: %s' %
464 vars, node.lineno, self.filename)
465
466 # remove variables from a closure from the frame's undeclared
467 # identifiers.
468 macro_frame.identifiers.undeclared -= (
469 macro_frame.identifiers.undeclared &
470 macro_frame.identifiers.declared
471 )
472
Armin Ronacher8efc5222008-04-08 14:47:40 +0200473 args = ['l_' + x.name for x in node.args]
474 if 'arguments' in macro_frame.identifiers.undeclared:
475 accesses_arguments = True
476 args.append('l_arguments')
477 else:
478 accesses_arguments = False
479 self.writeline('def macro(%s):' % ', '.join(args), node)
480 self.indent()
481 self.writeline('if 0: yield None')
482 self.outdent()
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200483 self.pull_locals(macro_frame)
484 self.blockvisit(node.body, macro_frame)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200485 self.newline()
486 if frame.toplevel:
487 self.write('context[%r] = ' % node.name)
488 arg_tuple = ', '.join(repr(x.name) for x in node.args)
489 if len(node.args) == 1:
490 arg_tuple += ','
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200491 self.write('l_%s = Macro(macro, %r, (%s), (' % (node.name, node.name,
492 arg_tuple))
493 for arg in node.defaults:
494 self.visit(arg)
495 self.write(', ')
Armin Ronacher4dfc9752008-04-09 15:03:29 +0200496 self.write('), %r)' % accesses_arguments)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200497
Armin Ronachere791c2a2008-04-07 18:39:54 +0200498 def visit_ExprStmt(self, node, frame):
499 self.newline(node)
500 self.visit(node, frame)
501
502 def visit_Output(self, node, frame):
Armin Ronacher75cfb862008-04-11 13:47:22 +0200503 # if we have a known extends statement, we don't output anything
Armin Ronacher7a52df82008-04-11 13:58:22 +0200504 if self.has_known_extends and frame.toplevel:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200505 return
Armin Ronachere791c2a2008-04-07 18:39:54 +0200506
Armin Ronacher75cfb862008-04-11 13:47:22 +0200507 self.newline(node)
Armin Ronacher8edbe492008-04-10 20:43:43 +0200508 if self.environment.finalize is unicode:
509 finalizer = 'unicode'
510 else:
511 finalizer = 'context.finalize'
512
Armin Ronacher7fb38972008-04-11 13:54:28 +0200513 # if we are in the toplevel scope and there was already an extends
514 # statement we have to add a check that disables our yield(s) here
515 # so that they don't appear in the output.
516 outdent_later = False
517 if frame.toplevel and self.extends_so_far != 0:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200518 self.writeline('if parent_root is None:')
519 self.indent()
Armin Ronacher7fb38972008-04-11 13:54:28 +0200520 outdent_later = True
Armin Ronacher75cfb862008-04-11 13:47:22 +0200521
Armin Ronachere791c2a2008-04-07 18:39:54 +0200522 # try to evaluate as many chunks as possible into a static
523 # string at compile time.
524 body = []
525 for child in node.nodes:
526 try:
527 const = unicode(child.as_const())
528 except:
529 body.append(child)
530 continue
531 if body and isinstance(body[-1], list):
532 body[-1].append(const)
533 else:
534 body.append([const])
535
536 # if we have less than 3 nodes we just yield them
537 if len(body) < 3:
538 for item in body:
539 if isinstance(item, list):
540 self.writeline('yield %s' % repr(u''.join(item)))
541 else:
542 self.newline(item)
Armin Ronacher8edbe492008-04-10 20:43:43 +0200543 self.write('yield %s(' % finalizer)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200544 self.visit(item, frame)
545 self.write(')')
546
547 # otherwise we create a format string as this is faster in that case
548 else:
549 format = []
550 arguments = []
551 for item in body:
552 if isinstance(item, list):
553 format.append(u''.join(item).replace('%', '%%'))
554 else:
555 format.append('%s')
556 arguments.append(item)
557 self.writeline('yield %r %% (' % u''.join(format))
558 idx = -1
559 for idx, argument in enumerate(arguments):
560 if idx:
561 self.write(', ')
Armin Ronacher8edbe492008-04-10 20:43:43 +0200562 if finalizer != 'unicode':
563 self.write('(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200564 self.visit(argument, frame)
Armin Ronacher8edbe492008-04-10 20:43:43 +0200565 if finalizer != 'unicode':
566 self.write(')')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200567 self.write(idx == 0 and ',)' or ')')
568
Armin Ronacher7fb38972008-04-11 13:54:28 +0200569 if outdent_later:
Armin Ronacher75cfb862008-04-11 13:47:22 +0200570 self.outdent()
571
Armin Ronacher8efc5222008-04-08 14:47:40 +0200572 def visit_Assign(self, node, frame):
573 self.newline(node)
574 # toplevel assignments however go into the local namespace and
575 # the current template's context. We create a copy of the frame
576 # here and add a set so that the Name visitor can add the assigned
577 # names here.
578 if frame.toplevel:
579 assignment_frame = frame.copy()
580 assignment_frame.assigned_names = set()
581 else:
582 assignment_frame = frame
583 self.visit(node.target, assignment_frame)
584 self.write(' = ')
585 self.visit(node.node, frame)
Armin Ronacher9706fab2008-04-08 18:49:56 +0200586
587 # make sure toplevel assignments are added to the context.
Armin Ronacher8efc5222008-04-08 14:47:40 +0200588 if frame.toplevel:
589 for name in assignment_frame.assigned_names:
590 self.writeline('context[%r] = l_%s' % (name, name))
591
Armin Ronachere791c2a2008-04-07 18:39:54 +0200592 def visit_Name(self, node, frame):
Armin Ronacher8efc5222008-04-08 14:47:40 +0200593 if frame.toplevel and node.ctx == 'store':
594 frame.assigned_names.add(node.name)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200595 self.write('l_' + node.name)
596
597 def visit_Const(self, node, frame):
598 val = node.value
599 if isinstance(val, float):
600 # XXX: add checks for infinity and nan
601 self.write(str(val))
602 else:
603 self.write(repr(val))
604
Armin Ronacher8efc5222008-04-08 14:47:40 +0200605 def visit_Tuple(self, node, frame):
606 self.write('(')
607 idx = -1
608 for idx, item in enumerate(node.items):
609 if idx:
610 self.write(', ')
611 self.visit(item, frame)
612 self.write(idx == 0 and ',)' or ')')
613
Armin Ronacher8edbe492008-04-10 20:43:43 +0200614 def visit_List(self, node, frame):
615 self.write('[')
616 for idx, item in enumerate(node.items):
617 if idx:
618 self.write(', ')
619 self.visit(item, frame)
620 self.write(']')
621
622 def visit_Dict(self, node, frame):
623 self.write('{')
624 for idx, item in enumerate(node.items):
625 if idx:
626 self.write(', ')
627 self.visit(item.key, frame)
628 self.write(': ')
629 self.visit(item.value, frame)
630 self.write('}')
631
Armin Ronachere791c2a2008-04-07 18:39:54 +0200632 def binop(operator):
633 def visitor(self, node, frame):
634 self.write('(')
635 self.visit(node.left, frame)
636 self.write(' %s ' % operator)
637 self.visit(node.right, frame)
638 self.write(')')
639 return visitor
640
641 def uaop(operator):
642 def visitor(self, node, frame):
643 self.write('(' + operator)
644 self.visit(node.node)
645 self.write(')')
646 return visitor
647
648 visit_Add = binop('+')
649 visit_Sub = binop('-')
650 visit_Mul = binop('*')
651 visit_Div = binop('/')
652 visit_FloorDiv = binop('//')
653 visit_Pow = binop('**')
654 visit_Mod = binop('%')
655 visit_And = binop('and')
656 visit_Or = binop('or')
657 visit_Pos = uaop('+')
658 visit_Neg = uaop('-')
659 visit_Not = uaop('not ')
660 del binop, uaop
661
662 def visit_Compare(self, node, frame):
663 self.visit(node.expr, frame)
664 for op in node.ops:
665 self.visit(op, frame)
666
667 def visit_Operand(self, node, frame):
668 self.write(' %s ' % operators[node.op])
669 self.visit(node.expr, frame)
670
671 def visit_Subscript(self, node, frame):
Armin Ronacher8efc5222008-04-08 14:47:40 +0200672 if isinstance(node.arg, nodes.Slice):
673 self.visit(node.node, frame)
674 self.write('[')
675 self.visit(node.arg, frame)
676 self.write(']')
677 return
678 try:
679 const = node.arg.as_const()
680 have_const = True
681 except nodes.Impossible:
682 have_const = False
683 if have_const:
684 if isinstance(const, (int, long, float)):
685 self.visit(node.node, frame)
686 self.write('[%s]' % const)
687 return
688 self.write('subscribe(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200689 self.visit(node.node, frame)
690 self.write(', ')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200691 if have_const:
692 self.write(repr(const))
693 else:
694 self.visit(node.arg, frame)
Armin Ronacher4dfc9752008-04-09 15:03:29 +0200695 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200696
697 def visit_Slice(self, node, frame):
698 if node.start is not None:
699 self.visit(node.start, frame)
700 self.write(':')
701 if node.stop is not None:
702 self.visit(node.stop, frame)
703 if node.step is not None:
704 self.write(':')
705 self.visit(node.step, frame)
706
707 def visit_Filter(self, node, frame):
Armin Ronacherd4c64f72008-04-11 17:15:29 +0200708 self.write('f_%s(' % node.name)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200709 self.visit(node.node, frame)
Armin Ronacherd55ab532008-04-09 16:13:39 +0200710 self.signature(node, frame)
711 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200712
713 def visit_Test(self, node, frame):
Armin Ronacher46f5f982008-04-11 16:40:09 +0200714 self.write('environment.tests[%r](')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200715 self.visit(node.node, frame)
716 self.signature(node, frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200717 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200718
719 def visit_Call(self, node, frame):
720 self.visit(node.node, frame)
721 self.write('(')
722 self.signature(node, frame, False)
723 self.write(')')
724
725 def visit_Keyword(self, node, frame):
726 self.visit(node.key, frame)
727 self.write('=')
728 self.visit(node.value, frame)