def outdent(self, step=1):
self.indentation -= step
- def blockvisit(self, nodes, frame, force_generator=False):
- self.indent()
- if force_generator and frame.buffer is None:
+ def blockvisit(self, nodes, frame, indent=True, force_generator=True):
+ if indent:
+ self.indent()
+ if frame.buffer is None and force_generator:
self.writeline('if 0: yield None')
try:
for node in nodes:
self.visit(node, frame)
except CompilerExit:
pass
- self.outdent()
+ if indent:
+ self.outdent()
def write(self, x):
if self.new_lines:
self.write('**')
self.visit(node.dyn_kwargs, frame)
- def pull_locals(self, frame, no_indent=False):
- if not no_indent:
+ def pull_locals(self, frame, indent=True):
+ if indent:
self.indent()
for name in frame.identifiers.undeclared:
self.writeline('l_%s = context[%r]' % (name, name))
self.writeline('f_%s = environment.filters[%r]' % (name, name))
for name in frame.identifiers.tests:
self.writeline('t_%s = environment.tests[%r]' % (name, name))
- if not no_indent:
+ if indent:
self.outdent()
def collect_shadowed(self, frame):
frame = Frame()
frame.inspect(node.body)
frame.toplevel = frame.rootlevel = True
- self.pull_locals(frame)
self.indent()
+ self.pull_locals(frame, indent=False)
self.writeline('yield context')
+ self.blockvisit(node.body, frame, indent=False)
self.outdent()
- self.blockvisit(node.body, frame)
# make sure that the parent root is called.
if have_extends:
self.writeline('def block_%s(context, environment=environment):'
% name, block, 1)
self.pull_locals(block_frame)
- self.blockvisit(block.body, block_frame, True)
+ self.blockvisit(block.body, block_frame)
self.writeline('blocks = {%s}' % ', '.join('%r: block_%s' % (x, x)
for x in self.blocks), extra=1)
loop_frame.identifiers.add_special('loop')
aliases = self.collect_shadowed(loop_frame)
- self.pull_locals(loop_frame, True)
+ self.pull_locals(loop_frame, indent=False)
if node.else_:
self.writeline('l_loop = None')
self.writeline('continue')
self.outdent(2)
- self.blockvisit(node.body, loop_frame)
+ self.blockvisit(node.body, loop_frame, force_generator=False)
if node.else_:
self.writeline('if l_loop is None:')
self.indent()
self.writeline('l_loop = ' + parent_loop)
self.outdent()
- self.blockvisit(node.else_, loop_frame)
+ self.blockvisit(node.else_, loop_frame, force_generator=False)
# reset the aliases if there are any.
for name, alias in aliases.iteritems():
macro_frame = self.function_scoping(node, frame)
args = macro_frame.arguments
self.writeline('def macro(%s):' % ', '.join(args), node)
- self.pull_locals(macro_frame)
- self.blockvisit(node.body, macro_frame, True)
+ macro_frame.buffer = buf = self.temporary_identifier()
+ self.indent()
+ self.pull_locals(macro_frame, indent=False)
+ self.writeline('%s = []' % buf)
+ self.blockvisit(node.body, macro_frame, indent=False)
+ self.writeline("return TemplateData(u''.join(%s))" % buf)
+ self.outdent()
self.newline()
if frame.toplevel:
self.write('context[%r] = ' % node.name)
self.write('l_%s = Macro(macro, %r, (%s), (' % (node.name, node.name,
arg_tuple))
for arg in node.defaults:
- self.visit(arg)
+ self.visit(arg, macro_frame)
self.write(', ')
self.write('), %s, %s)' % (
macro_frame.accesses_arguments and '1' or '0',
call_frame = self.function_scoping(node, frame)
args = call_frame.arguments
self.writeline('def call(%s):' % ', '.join(args), node)
- self.blockvisit(node.body, call_frame, node)
+ call_frame.buffer = buf = self.temporary_identifier()
+ self.indent()
+ self.pull_locals(call_frame, indent=False)
+ self.writeline('%s = []' % buf)
+ self.blockvisit(node.body, call_frame, indent=False)
+ self.writeline("return TemplateData(u''.join(%s))" % buf)
+ self.outdent()
arg_tuple = ', '.join(repr(x.name) for x in node.args)
if len(node.args) == 1:
arg_tuple += ','
filter_frame.inspect(node.iter_child_nodes())
aliases = self.collect_shadowed(filter_frame)
- self.pull_locals(filter_frame, True)
+ self.pull_locals(filter_frame, indent=False)
filter_frame.buffer = buf = self.temporary_identifier()
self.writeline('%s = []' % buf, node)
def visit_For(self, node, context):
"""Loop unrolling for iterable constant values."""
+ fallback = self.generic_visit(node.copy(), context)
try:
iterable = self.visit(node.iter, context).as_const()
# we only unroll them if they have a length and are iterable
# we also don't want unrolling if macros are defined in it
if node.find(nodes.Macro) is not None:
raise TypeError()
- # XXX: add support for loop test clauses in the optimizer
- if node.test is not None:
- raise TypeError()
except (nodes.Impossible, TypeError):
- return self.generic_visit(node, context)
+ return fallback
parent = context.get('loop')
context.push()
else:
raise AssertionError('unexpected assignable node')
+ if node.test is not None:
+ filtered_sequence = []
+ for item in iterable:
+ context.push()
+ assign(node.target, item)
+ try:
+ rv = self.visit(node.test.copy(), context).as_const()
+ except:
+ return fallback
+ context.pop()
+ if rv:
+ filtered_sequence.append(item)
+ iterable = filtered_sequence
+
try:
try:
for item, loop in LoopContext(iterable, parent, True):