blob: 39e14320ebc0a359d059ab2feb7ce3307099b52d [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 Ronachere791c2a2008-04-07 18:39:54 +020033 is_child = node.find(nodes.Extends) is not None
Christoph Hack65642a52008-04-08 14:46:56 +020034 generator = CodeGenerator(environment, is_child, filename, stream)
Armin Ronachere791c2a2008-04-07 18:39:54 +020035 generator.visit(node)
36 if stream is None:
37 return generator.stream.getvalue()
38
39
Armin Ronacher4dfc9752008-04-09 15:03:29 +020040def has_safe_repr(value):
41 """Does the node have a safe representation?"""
Armin Ronacherd55ab532008-04-09 16:13:39 +020042 if value is None or value is NotImplemented or value is Ellipsis:
Armin Ronacher4dfc9752008-04-09 15:03:29 +020043 return True
Armin Ronacherd55ab532008-04-09 16:13:39 +020044 if isinstance(value, (bool, int, long, float, complex, basestring,
45 StaticLoopContext)):
Armin Ronacher4dfc9752008-04-09 15:03:29 +020046 return True
Armin Ronacherd55ab532008-04-09 16:13:39 +020047 if isinstance(value, (tuple, list, set, frozenset)):
Armin Ronacher4dfc9752008-04-09 15:03:29 +020048 for item in value:
49 if not has_safe_repr(item):
50 return False
51 return True
52 elif isinstance(value, dict):
53 for key, value in value.iteritems():
54 if not has_safe_repr(key):
55 return False
56 if not has_safe_repr(value):
57 return False
58 return True
59 return False
60
61
Armin Ronachere791c2a2008-04-07 18:39:54 +020062class Identifiers(object):
63 """Tracks the status of identifiers in frames."""
64
65 def __init__(self):
66 # variables that are known to be declared (probably from outer
67 # frames or because they are special for the frame)
68 self.declared = set()
69
70 # names that are accessed without being explicitly declared by
71 # this one or any of the outer scopes. Names can appear both in
72 # declared and undeclared.
73 self.undeclared = set()
74
75 # names that are declared locally
76 self.declared_locally = set()
77
78 # names that are declared by parameters
79 self.declared_parameter = set()
80
Christoph Hack65642a52008-04-08 14:46:56 +020081 # filters that are declared locally
82 self.declared_filter = set()
Christoph Hackacb130e2008-04-08 15:21:53 +020083 self.undeclared_filter = dict()
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):
Christoph Hack65642a52008-04-08 14:46:56 +0200168 if not node.name in self.identifiers.declared_filter:
Christoph Hackacb130e2008-04-08 15:21:53 +0200169 uf = self.identifiers.undeclared_filter.get(node.name, 0) + 1
170 if uf > 1:
171 self.identifiers.declared_filter.add(node.name)
172 self.identifiers.undeclared_filter[node.name] = uf
Christoph Hack65642a52008-04-08 14:46:56 +0200173
Armin Ronachere791c2a2008-04-07 18:39:54 +0200174 def visit_Macro(self, node):
175 """Macros set local."""
176 self.identifiers.declared_locally.add(node.name)
177
Armin Ronacherebe55aa2008-04-10 20:51:23 +0200178 def visit_Assign(self, node):
179 """Visit assignments in the correct order."""
180 self.visit(node.node)
181 self.visit(node.target)
182
Armin Ronachere791c2a2008-04-07 18:39:54 +0200183 # stop traversing at instructions that have their own scope.
Armin Ronacher8efc5222008-04-08 14:47:40 +0200184 visit_Block = visit_CallBlock = visit_FilterBlock = \
Armin Ronachere791c2a2008-04-07 18:39:54 +0200185 visit_For = lambda s, n: None
186
187
Armin Ronacher75cfb862008-04-11 13:47:22 +0200188class CompilerExit(Exception):
189 """Raised if the compiler encountered a situation where it just
190 doesn't make sense to further process the code. Any block that
191 raises such an exception is not further processed."""
192
193
Armin Ronachere791c2a2008-04-07 18:39:54 +0200194class CodeGenerator(NodeVisitor):
195
Christoph Hack65642a52008-04-08 14:46:56 +0200196 def __init__(self, environment, is_child, filename, stream=None):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200197 if stream is None:
198 stream = StringIO()
Christoph Hack65642a52008-04-08 14:46:56 +0200199 self.environment = environment
Armin Ronachere791c2a2008-04-07 18:39:54 +0200200 self.is_child = is_child
201 self.filename = filename
202 self.stream = stream
203 self.blocks = {}
204 self.indentation = 0
205 self.new_lines = 0
206 self.last_identifier = 0
Armin Ronacher75cfb862008-04-11 13:47:22 +0200207 self.has_known_extends = False
Armin Ronachere791c2a2008-04-07 18:39:54 +0200208 self._last_line = 0
209 self._first_write = True
210
211 def temporary_identifier(self):
212 self.last_identifier += 1
213 return 't%d' % self.last_identifier
214
215 def indent(self):
216 self.indentation += 1
217
Armin Ronacher8efc5222008-04-08 14:47:40 +0200218 def outdent(self, step=1):
219 self.indentation -= step
Armin Ronachere791c2a2008-04-07 18:39:54 +0200220
221 def blockvisit(self, nodes, frame, force_generator=False):
222 self.indent()
223 if force_generator:
224 self.writeline('if 0: yield None')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200225 try:
226 for node in nodes:
227 self.visit(node, frame)
228 except CompilerExit:
229 pass
Armin Ronachere791c2a2008-04-07 18:39:54 +0200230 self.outdent()
231
232 def write(self, x):
233 if self.new_lines:
234 if not self._first_write:
235 self.stream.write('\n' * self.new_lines)
236 self._first_write = False
237 self.stream.write(' ' * self.indentation)
238 self.new_lines = 0
239 self.stream.write(x)
240
241 def writeline(self, x, node=None, extra=0):
242 self.newline(node, extra)
243 self.write(x)
244
245 def newline(self, node=None, extra=0):
246 self.new_lines = max(self.new_lines, 1 + extra)
247 if node is not None and node.lineno != self._last_line:
248 self.write('# line: %s' % node.lineno)
249 self.new_lines = 1
250 self._last_line = node.lineno
251
Armin Ronacher8efc5222008-04-08 14:47:40 +0200252 def signature(self, node, frame, have_comma=True):
253 have_comma = have_comma and [True] or []
254 def touch_comma():
255 if have_comma:
256 self.write(', ')
257 else:
258 have_comma.append(True)
259
260 for arg in node.args:
261 touch_comma()
262 self.visit(arg, frame)
263 for kwarg in node.kwargs:
264 touch_comma()
265 self.visit(kwarg, frame)
266 if node.dyn_args:
267 touch_comma()
268 self.visit(node.dyn_args, frame)
269 if node.dyn_kwargs:
270 touch_comma()
271 self.visit(node.dyn_kwargs, frame)
272
Armin Ronachere791c2a2008-04-07 18:39:54 +0200273 def pull_locals(self, frame, no_indent=False):
274 if not no_indent:
275 self.indent()
276 for name in frame.identifiers.undeclared:
277 self.writeline('l_%s = context[%r]' % (name, name))
Christoph Hackacb130e2008-04-08 15:21:53 +0200278 for name, count in frame.identifiers.undeclared_filter.iteritems():
279 if count > 1:
280 self.writeline('f_%s = context[%r]' % (name, name))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200281 if not no_indent:
282 self.outdent()
283
284 # -- Visitors
285
286 def visit_Template(self, node, frame=None):
287 assert frame is None, 'no root frame allowed'
288 self.writeline('from jinja2.runtime import *')
289 self.writeline('filename = %r' % self.filename)
Armin Ronacher8edbe492008-04-10 20:43:43 +0200290
Armin Ronacher75cfb862008-04-11 13:47:22 +0200291 # do we have an extends tag at all? If not, we can save some
292 # overhead by just not processing any inheritance code.
293 have_extends = node.find(nodes.Extends) is not None
294
Armin Ronacher8edbe492008-04-10 20:43:43 +0200295 # find all blocks
296 for block in node.find_all(nodes.Block):
297 if block.name in self.blocks:
298 raise TemplateAssertionError('block %r defined twice' %
299 block.name, block.lineno,
300 self.filename)
301 self.blocks[block.name] = block
Armin Ronachere791c2a2008-04-07 18:39:54 +0200302
Armin Ronacher8efc5222008-04-08 14:47:40 +0200303 # generate the root render function.
Armin Ronacher75cfb862008-04-11 13:47:22 +0200304 self.writeline('def root(globals):', extra=1)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200305 self.indent()
Armin Ronacher75cfb862008-04-11 13:47:22 +0200306 self.writeline('context = TemplateContext(globals, filename, blocks)')
307 if have_extends:
308 self.writeline('parent_root = None')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200309 self.outdent()
Armin Ronacher75cfb862008-04-11 13:47:22 +0200310
311 # process the root
Armin Ronachere791c2a2008-04-07 18:39:54 +0200312 frame = Frame()
313 frame.inspect(node.body)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200314 frame.toplevel = frame.rootlevel = True
Armin Ronachere791c2a2008-04-07 18:39:54 +0200315 self.pull_locals(frame)
316 self.blockvisit(node.body, frame, True)
317
Armin Ronacher8efc5222008-04-08 14:47:40 +0200318 # make sure that the parent root is called.
Armin Ronacher75cfb862008-04-11 13:47:22 +0200319 if have_extends:
320 if not self.has_known_extends:
321 self.indent()
322 self.writeline('if parent_root is not None:')
323 self.indent()
324 self.writeline('for event in parent_root(context):')
325 self.indent()
326 self.writeline('yield event')
327 self.outdent(2 + self.has_known_extends)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200328
329 # at this point we now have the blocks collected and can visit them too.
330 for name, block in self.blocks.iteritems():
331 block_frame = Frame()
332 block_frame.inspect(block.body)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200333 block_frame.block = name
334 self.writeline('def block_%s(context):' % 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 Ronacher75cfb862008-04-11 13:47:22 +0200343 # if we know that we are a child template, there is no need to
344 # check if we are one
345 if self.has_known_extends:
346 return
347 if frame.toplevel:
348 self.writeline('if parent_root is None:')
349 self.indent()
Armin Ronacher8efc5222008-04-08 14:47:40 +0200350 self.writeline('for event in block_%s(context):' % node.name)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200351 self.indent()
352 self.writeline('yield event')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200353 self.outdent(1 + frame.toplevel)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200354
355 def visit_Extends(self, node, frame):
356 """Calls the extender."""
Armin Ronacher8efc5222008-04-08 14:47:40 +0200357 if not frame.toplevel:
358 raise TemplateAssertionError('cannot use extend from a non '
359 'top-level scope', node.lineno,
360 self.filename)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200361
362 # if we have a known extends we just add a template runtime
363 # error into the generated code. We could catch that at compile
364 # time too, but i welcome it not to confuse users by throwing the
365 # same error at different times just "because we can".
366 if not self.has_known_extends:
367 self.writeline('if parent_root is not None:')
368 self.indent()
Armin Ronacher8efc5222008-04-08 14:47:40 +0200369 self.writeline('raise TemplateRuntimeError(%r)' %
370 'extended multiple times')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200371
372 # if we have a known extends already we don't need that code here
373 # as we know that the template execution will end here.
374 if self.has_known_extends:
375 raise CompilerExit()
376
Armin Ronacher8efc5222008-04-08 14:47:40 +0200377 self.outdent()
378 self.writeline('parent_root = extends(', node, 1)
379 self.visit(node.template, frame)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200380 self.write(', context)')
381
382 # if this extends statement was in the root level we can take
383 # advantage of that information and simplify the generated code
384 # in the top level from this point onwards
385 self.has_known_extends = True
Armin Ronachere791c2a2008-04-07 18:39:54 +0200386
387 def visit_For(self, node, frame):
388 loop_frame = frame.inner()
389 loop_frame.inspect(node.iter_child_nodes())
Armin Ronachere791c2a2008-04-07 18:39:54 +0200390 extended_loop = bool(node.else_) or \
391 'loop' in loop_frame.identifiers.undeclared
Armin Ronacher8efc5222008-04-08 14:47:40 +0200392 loop_frame.identifiers.add_special('loop')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200393
394 # make sure we "backup" overridden, local identifiers
395 # TODO: we should probably optimize this and check if the
396 # identifier is in use afterwards.
397 aliases = {}
398 for name in loop_frame.identifiers.find_shadowed():
399 aliases[name] = ident = self.temporary_identifier()
400 self.writeline('%s = l_%s' % (ident, name))
401
402 self.pull_locals(loop_frame, True)
403
404 self.newline(node)
405 if node.else_:
406 self.writeline('l_loop = None')
407 self.write('for ')
408 self.visit(node.target, loop_frame)
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200409 self.write(extended_loop and ', l_loop in LoopContext(' or ' in ')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200410 self.visit(node.iter, loop_frame)
Armin Ronacher180a1bd2008-04-09 12:14:24 +0200411 if 'loop' in aliases:
412 self.write(', ' + aliases['loop'])
Armin Ronachere791c2a2008-04-07 18:39:54 +0200413 self.write(extended_loop and '):' or ':')
414 self.blockvisit(node.body, loop_frame)
415
416 if node.else_:
417 self.writeline('if l_loop is None:')
418 self.blockvisit(node.else_, loop_frame)
419
Armin Ronacher8efc5222008-04-08 14:47:40 +0200420 # reset the aliases and clean up
421 delete = set('l_' + x for x in loop_frame.identifiers.declared_locally
422 | loop_frame.identifiers.declared_parameter)
423 if extended_loop:
424 delete.add('l_loop')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200425 for name, alias in aliases.iteritems():
Armin Ronacher8efc5222008-04-08 14:47:40 +0200426 self.writeline('l_%s = %s' % (name, alias))
427 delete.add(alias)
428 delete.discard('l_' + name)
429 self.writeline('del %s' % ', '.join(delete))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200430
431 def visit_If(self, node, frame):
Armin Ronacher75cfb862008-04-11 13:47:22 +0200432 if_frame = frame.soft()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200433 self.writeline('if ', node)
Armin Ronacher75cfb862008-04-11 13:47:22 +0200434 self.visit(node.test, if_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200435 self.write(':')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200436 self.blockvisit(node.body, if_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200437 if node.else_:
438 self.writeline('else:')
Armin Ronacher75cfb862008-04-11 13:47:22 +0200439 self.blockvisit(node.else_, if_frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200440
Armin Ronacher8efc5222008-04-08 14:47:40 +0200441 def visit_Macro(self, node, frame):
442 macro_frame = frame.inner()
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200443 macro_frame.inspect(node.iter_child_nodes(), hard_scope=True)
444
445 # variables that are undeclared (accessed before declaration) and
446 # declared locally *and* part of an outside scope raise a template
447 # assertion error. Reason: we can't generate reasonable code from
448 # it without aliasing all the variables. XXX: alias them ^^
449 overriden_closure_vars = (
450 macro_frame.identifiers.undeclared &
451 macro_frame.identifiers.declared &
452 (macro_frame.identifiers.declared_locally |
453 macro_frame.identifiers.declared_parameter)
454 )
455 if overriden_closure_vars:
456 vars = ', '.join(sorted(overriden_closure_vars))
457 raise TemplateAssertionError('It\'s not possible to set and '
458 'access variables derived from '
459 'an outer scope! (affects: %s' %
460 vars, node.lineno, self.filename)
461
462 # remove variables from a closure from the frame's undeclared
463 # identifiers.
464 macro_frame.identifiers.undeclared -= (
465 macro_frame.identifiers.undeclared &
466 macro_frame.identifiers.declared
467 )
468
Armin Ronacher8efc5222008-04-08 14:47:40 +0200469 args = ['l_' + x.name for x in node.args]
470 if 'arguments' in macro_frame.identifiers.undeclared:
471 accesses_arguments = True
472 args.append('l_arguments')
473 else:
474 accesses_arguments = False
475 self.writeline('def macro(%s):' % ', '.join(args), node)
476 self.indent()
477 self.writeline('if 0: yield None')
478 self.outdent()
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200479 self.pull_locals(macro_frame)
480 self.blockvisit(node.body, macro_frame)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200481 self.newline()
482 if frame.toplevel:
483 self.write('context[%r] = ' % node.name)
484 arg_tuple = ', '.join(repr(x.name) for x in node.args)
485 if len(node.args) == 1:
486 arg_tuple += ','
Armin Ronacher4f62a9f2008-04-08 18:09:13 +0200487 self.write('l_%s = Macro(macro, %r, (%s), (' % (node.name, node.name,
488 arg_tuple))
489 for arg in node.defaults:
490 self.visit(arg)
491 self.write(', ')
Armin Ronacher4dfc9752008-04-09 15:03:29 +0200492 self.write('), %r)' % accesses_arguments)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200493
Armin Ronachere791c2a2008-04-07 18:39:54 +0200494 def visit_ExprStmt(self, node, frame):
495 self.newline(node)
496 self.visit(node, frame)
497
498 def visit_Output(self, node, frame):
Armin Ronacher75cfb862008-04-11 13:47:22 +0200499 # if we have a known extends statement, we don't output anything
500 if self.has_known_extends:
501 return
Armin Ronachere791c2a2008-04-07 18:39:54 +0200502
Armin Ronacher75cfb862008-04-11 13:47:22 +0200503 self.newline(node)
Armin Ronacher8edbe492008-04-10 20:43:43 +0200504 if self.environment.finalize is unicode:
505 finalizer = 'unicode'
506 else:
507 finalizer = 'context.finalize'
508
Armin Ronacher75cfb862008-04-11 13:47:22 +0200509 if frame.toplevel:
510 self.writeline('if parent_root is None:')
511 self.indent()
512
Armin Ronachere791c2a2008-04-07 18:39:54 +0200513 # try to evaluate as many chunks as possible into a static
514 # string at compile time.
515 body = []
516 for child in node.nodes:
517 try:
518 const = unicode(child.as_const())
519 except:
520 body.append(child)
521 continue
522 if body and isinstance(body[-1], list):
523 body[-1].append(const)
524 else:
525 body.append([const])
526
527 # if we have less than 3 nodes we just yield them
528 if len(body) < 3:
529 for item in body:
530 if isinstance(item, list):
531 self.writeline('yield %s' % repr(u''.join(item)))
532 else:
533 self.newline(item)
Armin Ronacher8edbe492008-04-10 20:43:43 +0200534 self.write('yield %s(' % finalizer)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200535 self.visit(item, frame)
536 self.write(')')
537
538 # otherwise we create a format string as this is faster in that case
539 else:
540 format = []
541 arguments = []
542 for item in body:
543 if isinstance(item, list):
544 format.append(u''.join(item).replace('%', '%%'))
545 else:
546 format.append('%s')
547 arguments.append(item)
548 self.writeline('yield %r %% (' % u''.join(format))
549 idx = -1
550 for idx, argument in enumerate(arguments):
551 if idx:
552 self.write(', ')
Armin Ronacher8edbe492008-04-10 20:43:43 +0200553 if finalizer != 'unicode':
554 self.write('(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200555 self.visit(argument, frame)
Armin Ronacher8edbe492008-04-10 20:43:43 +0200556 if finalizer != 'unicode':
557 self.write(')')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200558 self.write(idx == 0 and ',)' or ')')
559
Armin Ronacher75cfb862008-04-11 13:47:22 +0200560 if frame.toplevel:
561 self.outdent()
562
Armin Ronacher8efc5222008-04-08 14:47:40 +0200563 def visit_Assign(self, node, frame):
564 self.newline(node)
565 # toplevel assignments however go into the local namespace and
566 # the current template's context. We create a copy of the frame
567 # here and add a set so that the Name visitor can add the assigned
568 # names here.
569 if frame.toplevel:
570 assignment_frame = frame.copy()
571 assignment_frame.assigned_names = set()
572 else:
573 assignment_frame = frame
574 self.visit(node.target, assignment_frame)
575 self.write(' = ')
576 self.visit(node.node, frame)
Armin Ronacher9706fab2008-04-08 18:49:56 +0200577
578 # make sure toplevel assignments are added to the context.
Armin Ronacher8efc5222008-04-08 14:47:40 +0200579 if frame.toplevel:
580 for name in assignment_frame.assigned_names:
581 self.writeline('context[%r] = l_%s' % (name, name))
582
Armin Ronachere791c2a2008-04-07 18:39:54 +0200583 def visit_Name(self, node, frame):
Armin Ronacher8efc5222008-04-08 14:47:40 +0200584 if frame.toplevel and node.ctx == 'store':
585 frame.assigned_names.add(node.name)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200586 self.write('l_' + node.name)
587
588 def visit_Const(self, node, frame):
589 val = node.value
590 if isinstance(val, float):
591 # XXX: add checks for infinity and nan
592 self.write(str(val))
593 else:
594 self.write(repr(val))
595
Armin Ronacher8efc5222008-04-08 14:47:40 +0200596 def visit_Tuple(self, node, frame):
597 self.write('(')
598 idx = -1
599 for idx, item in enumerate(node.items):
600 if idx:
601 self.write(', ')
602 self.visit(item, frame)
603 self.write(idx == 0 and ',)' or ')')
604
Armin Ronacher8edbe492008-04-10 20:43:43 +0200605 def visit_List(self, node, frame):
606 self.write('[')
607 for idx, item in enumerate(node.items):
608 if idx:
609 self.write(', ')
610 self.visit(item, frame)
611 self.write(']')
612
613 def visit_Dict(self, node, frame):
614 self.write('{')
615 for idx, item in enumerate(node.items):
616 if idx:
617 self.write(', ')
618 self.visit(item.key, frame)
619 self.write(': ')
620 self.visit(item.value, frame)
621 self.write('}')
622
Armin Ronachere791c2a2008-04-07 18:39:54 +0200623 def binop(operator):
624 def visitor(self, node, frame):
625 self.write('(')
626 self.visit(node.left, frame)
627 self.write(' %s ' % operator)
628 self.visit(node.right, frame)
629 self.write(')')
630 return visitor
631
632 def uaop(operator):
633 def visitor(self, node, frame):
634 self.write('(' + operator)
635 self.visit(node.node)
636 self.write(')')
637 return visitor
638
639 visit_Add = binop('+')
640 visit_Sub = binop('-')
641 visit_Mul = binop('*')
642 visit_Div = binop('/')
643 visit_FloorDiv = binop('//')
644 visit_Pow = binop('**')
645 visit_Mod = binop('%')
646 visit_And = binop('and')
647 visit_Or = binop('or')
648 visit_Pos = uaop('+')
649 visit_Neg = uaop('-')
650 visit_Not = uaop('not ')
651 del binop, uaop
652
653 def visit_Compare(self, node, frame):
654 self.visit(node.expr, frame)
655 for op in node.ops:
656 self.visit(op, frame)
657
658 def visit_Operand(self, node, frame):
659 self.write(' %s ' % operators[node.op])
660 self.visit(node.expr, frame)
661
662 def visit_Subscript(self, node, frame):
Armin Ronacher8efc5222008-04-08 14:47:40 +0200663 if isinstance(node.arg, nodes.Slice):
664 self.visit(node.node, frame)
665 self.write('[')
666 self.visit(node.arg, frame)
667 self.write(']')
668 return
669 try:
670 const = node.arg.as_const()
671 have_const = True
672 except nodes.Impossible:
673 have_const = False
674 if have_const:
675 if isinstance(const, (int, long, float)):
676 self.visit(node.node, frame)
677 self.write('[%s]' % const)
678 return
679 self.write('subscribe(')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200680 self.visit(node.node, frame)
681 self.write(', ')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200682 if have_const:
683 self.write(repr(const))
684 else:
685 self.visit(node.arg, frame)
Armin Ronacher4dfc9752008-04-09 15:03:29 +0200686 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200687
688 def visit_Slice(self, node, frame):
689 if node.start is not None:
690 self.visit(node.start, frame)
691 self.write(':')
692 if node.stop is not None:
693 self.visit(node.stop, frame)
694 if node.step is not None:
695 self.write(':')
696 self.visit(node.step, frame)
697
698 def visit_Filter(self, node, frame):
Armin Ronacherd55ab532008-04-09 16:13:39 +0200699 if node.name in frame.identifiers.declared_filter:
700 self.write('f_%s(' % node.name)
701 else:
702 self.write('context.filter[%r](' % node.name)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200703 self.visit(node.node, frame)
Armin Ronacherd55ab532008-04-09 16:13:39 +0200704 self.signature(node, frame)
705 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200706
707 def visit_Test(self, node, frame):
708 self.write('context.tests[%r](')
709 self.visit(node.node, frame)
710 self.signature(node, frame)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200711 self.write(')')
Armin Ronacher8efc5222008-04-08 14:47:40 +0200712
713 def visit_Call(self, node, frame):
714 self.visit(node.node, frame)
715 self.write('(')
716 self.signature(node, frame, False)
717 self.write(')')
718
719 def visit_Keyword(self, node, frame):
720 self.visit(node.key, frame)
721 self.write('=')
722 self.visit(node.value, frame)