blob: 426e5541aaac99c45de85c705f6dd0f9ff2740d6 [file] [log] [blame]
Armin Ronacher07bc6842008-03-31 14:18:49 +02001# -*- coding: utf-8 -*-
2"""
3 jinja2.parser
4 ~~~~~~~~~~~~~
5
6 Implements the template parser.
7
8 :copyright: 2008 by Armin Ronacher.
9 :license: BSD, see LICENSE for more details.
10"""
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020011from jinja2 import nodes
12from jinja2.exceptions import TemplateSyntaxError
Armin Ronacher07bc6842008-03-31 14:18:49 +020013
14
15__all__ = ['Parser']
16
Armin Ronachere791c2a2008-04-07 18:39:54 +020017_statement_keywords = frozenset(['for', 'if', 'block', 'extends', 'print',
18 'macro', 'include'])
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020019_compare_operators = frozenset(['eq', 'ne', 'lt', 'lteq', 'gt', 'gteq', 'in'])
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020020_statement_end_tokens = set(['elif', 'else', 'endblock', 'endfilter',
Armin Ronachere791c2a2008-04-07 18:39:54 +020021 'endfor', 'endif', 'endmacro', 'variable_end',
22 'in', 'recursive', 'endcall', 'block_end'])
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020023
Armin Ronacher07bc6842008-03-31 14:18:49 +020024
25class Parser(object):
26 """
27 The template parser class.
28
29 Transforms sourcecode into an abstract syntax tree.
30 """
31
32 def __init__(self, environment, source, filename=None):
33 self.environment = environment
34 if isinstance(source, str):
35 source = source.decode(environment.template_charset, 'ignore')
36 if isinstance(filename, unicode):
37 filename = filename.encode('utf-8')
38 self.source = source
39 self.filename = filename
40 self.closed = False
Armin Ronacher07bc6842008-03-31 14:18:49 +020041 self.no_variable_block = self.environment.lexer.no_variable_block
42 self.stream = environment.lexer.tokenize(source, filename)
43
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020044 def end_statement(self):
45 """Make sure that the statement ends properly."""
46 if self.stream.current.type is 'semicolon':
47 self.stream.next()
48 elif self.stream.current.type not in _statement_end_tokens:
49 raise TemplateSyntaxError('ambigous end of statement',
50 self.stream.current.lineno,
51 self.filename)
52
53 def parse_statement(self):
54 """Parse a single statement."""
55 token_type = self.stream.current.type
56 if token_type in _statement_keywords:
57 return getattr(self, 'parse_' + token_type)()
58 elif token_type is 'call':
59 self.stream.next()
60 return self.parse_call_block()
Armin Ronachere791c2a2008-04-07 18:39:54 +020061 lineno = self.stream.current
Armin Ronacher8efc5222008-04-08 14:47:40 +020062 expr = self.parse_tuple()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020063 if self.stream.current.type == 'assign':
Armin Ronachere791c2a2008-04-07 18:39:54 +020064 result = self.parse_assign(expr)
65 else:
66 result = nodes.ExprStmt(expr, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020067 self.end_statement()
Armin Ronachere791c2a2008-04-07 18:39:54 +020068 return result
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020069
70 def parse_assign(self, target):
71 """Parse an assign statement."""
72 lineno = self.stream.expect('assign').lineno
73 if not target.can_assign():
74 raise TemplateSyntaxError("can't assign to '%s'" %
75 target, target.lineno,
76 self.filename)
77 expr = self.parse_tuple()
78 self.end_statement()
Armin Ronachere791c2a2008-04-07 18:39:54 +020079 target.set_ctx('store')
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020080 return nodes.Assign(target, expr, lineno=lineno)
81
82 def parse_statements(self, end_tokens, drop_needle=False):
83 """
84 Parse multiple statements into a list until one of the end tokens
85 is reached. This is used to parse the body of statements as it
86 also parses template data if appropriate.
87 """
88 # the first token may be a colon for python compatibility
89 if self.stream.current.type is 'colon':
90 self.stream.next()
91
92 if self.stream.current.type is 'block_end':
93 self.stream.next()
94 result = self.subparse(end_tokens)
95 else:
96 result = []
97 while self.stream.current.type not in end_tokens:
Armin Ronachere791c2a2008-04-07 18:39:54 +020098 if self.stream.current.type is 'block_end':
99 self.stream.next()
100 result.extend(self.subparse(end_tokens))
101 break
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200102 result.append(self.parse_statement())
103 if drop_needle:
104 self.stream.next()
105 return result
106
107 def parse_for(self):
108 """Parse a for loop."""
109 lineno = self.stream.expect('for').lineno
110 target = self.parse_tuple(simplified=True)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200111 if not target.can_assign():
112 raise TemplateSyntaxError("can't assign to '%s'" %
113 target, target.lineno,
114 self.filename)
115 target.set_ctx('store')
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200116 self.stream.expect('in')
117 iter = self.parse_tuple()
118 if self.stream.current.type is 'recursive':
119 self.stream.next()
120 recursive = True
121 else:
122 recursive = False
123 body = self.parse_statements(('endfor', 'else'))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200124 if self.stream.next().type is 'endfor':
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200125 else_ = []
126 else:
127 else_ = self.parse_statements(('endfor',), drop_needle=True)
128 return nodes.For(target, iter, body, else_, False, lineno=lineno)
129
130 def parse_if(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200131 """Parse an if construct."""
132 node = result = nodes.If(lineno=self.stream.expect('if').lineno)
133 while 1:
134 # TODO: exclude conditional expressions here
135 node.test = self.parse_tuple()
136 node.body = self.parse_statements(('elif', 'else', 'endif'))
137 token_type = self.stream.next().type
138 if token_type is 'elif':
139 new_node = nodes.If(lineno=self.stream.current.lineno)
140 node.else_ = [new_node]
141 node = new_node
142 continue
143 elif token_type is 'else':
144 node.else_ = self.parse_statements(('endif',),
145 drop_needle=True)
146 else:
147 node.else_ = []
148 break
149 return result
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200150
151 def parse_block(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200152 node = nodes.Block(lineno=self.stream.expect('block').lineno)
153 node.name = self.stream.expect('name').value
154 node.body = self.parse_statements(('endblock',), drop_needle=True)
155 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200156
157 def parse_extends(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200158 node = nodes.Extends(lineno=self.stream.expect('extends').lineno)
159 node.template = self.parse_expression()
160 self.end_statement()
161 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200162
163 def parse_include(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200164 node = nodes.Include(lineno=self.stream.expect('include').lineno)
165 expr = self.parse_expression()
166 if self.stream.current.type is 'assign':
167 self.stream.next()
168 if not expr.can_assign():
169 raise TemplateSyntaxError('can\'t assign imported template '
170 'to this expression.', expr.lineno,
171 self.filename)
172 expr.set_ctx('store')
173 node.target = expr
174 node.template = self.parse_expression()
175 else:
176 node.target = None
177 node.template = expr
178 self.end_statement()
179 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200180
181 def parse_call_block(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200182 node = nodes.CallBlock(lineno=self.stream.expect('call').lineno)
183 node.call = self.parse_call()
184 node.body = self.parse_statements(('endcall',), drop_needle=True)
185 return node
186
187 def parse_macro(self):
188 node = nodes.Macro(lineno=self.stream.expect('macro').lineno)
189 node.name = self.stream.expect('name').value
190 self.stream.expect('lparen')
191 node.args = args = []
192 node.defaults = defaults = []
193 while self.stream.current.type is not 'rparen':
194 if args:
195 self.stream.expect('comma')
196 token = self.stream.expect('name')
197 arg = nodes.Name(token.value, 'param', lineno=token.lineno)
198 if not arg.can_assign():
199 raise TemplateSyntaxError("can't assign to '%s'" %
200 arg.name, arg.lineno,
201 self.filename)
202 if self.stream.current.type is 'assign':
203 self.stream.next()
204 defaults.append(self.parse_expression())
Armin Ronacher8efc5222008-04-08 14:47:40 +0200205 args.append(arg)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200206 self.stream.expect('rparen')
207 node.body = self.parse_statements(('endmacro',), drop_needle=True)
208 return node
209
210 def parse_print(self):
211 node = nodes.Output(lineno=self.stream.expect('print').lineno)
212 node.nodes = []
213 while self.stream.current.type not in _statement_end_tokens:
214 if node.nodes:
215 self.stream.expect('comma')
216 node.nodes.append(self.parse_expression())
217 self.end_statement()
218 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200219
220 def parse_expression(self):
221 """Parse an expression."""
222 return self.parse_condexpr()
223
224 def parse_condexpr(self):
225 lineno = self.stream.current.lineno
226 expr1 = self.parse_or()
227 while self.stream.current.type is 'if':
228 self.stream.next()
229 expr2 = self.parse_or()
230 self.stream.expect('else')
231 expr3 = self.parse_condexpr()
232 expr1 = nodes.CondExpr(expr2, expr1, expr3, lineno=lineno)
233 lineno = self.stream.current.lineno
234 return expr1
235
236 def parse_or(self):
237 lineno = self.stream.current.lineno
238 left = self.parse_and()
239 while self.stream.current.type is 'or':
240 self.stream.next()
241 right = self.parse_and()
242 left = nodes.Or(left, right, lineno=lineno)
243 lineno = self.stream.current.lineno
244 return left
245
246 def parse_and(self):
247 lineno = self.stream.current.lineno
248 left = self.parse_compare()
249 while self.stream.current.type is 'and':
250 self.stream.next()
251 right = self.parse_compare()
252 left = nodes.And(left, right, lineno=lineno)
253 lineno = self.stream.current.lineno
254 return left
255
256 def parse_compare(self):
257 lineno = self.stream.current.lineno
258 expr = self.parse_add()
259 ops = []
260 while 1:
261 token_type = self.stream.current.type
262 if token_type in _compare_operators:
263 self.stream.next()
264 ops.append(nodes.Operand(token_type, self.parse_add()))
265 elif token_type is 'not' and self.stream.look().type is 'in':
266 self.stream.skip(2)
267 ops.append(nodes.Operand('notin', self.parse_add()))
268 else:
269 break
270 lineno = self.stream.current.lineno
271 if not ops:
272 return expr
273 return nodes.Compare(expr, ops, lineno=lineno)
274
275 def parse_add(self):
276 lineno = self.stream.current.lineno
277 left = self.parse_sub()
278 while self.stream.current.type is 'add':
279 self.stream.next()
280 right = self.parse_sub()
281 left = nodes.Add(left, right, lineno=lineno)
282 lineno = self.stream.current.lineno
283 return left
284
285 def parse_sub(self):
286 lineno = self.stream.current.lineno
287 left = self.parse_concat()
288 while self.stream.current.type is 'sub':
289 self.stream.next()
290 right = self.parse_concat()
291 left = nodes.Sub(left, right, lineno=lineno)
292 lineno = self.stream.current.lineno
293 return left
294
295 def parse_concat(self):
296 lineno = self.stream.current.lineno
297 args = [self.parse_mul()]
298 while self.stream.current.type is 'tilde':
299 self.stream.next()
300 args.append(self.parse_mul())
301 if len(args) == 1:
302 return args[0]
303 return nodes.Concat(args, lineno=lineno)
304
305 def parse_mul(self):
306 lineno = self.stream.current.lineno
307 left = self.parse_div()
308 while self.stream.current.type is 'mul':
309 self.stream.next()
310 right = self.parse_div()
311 left = nodes.Mul(left, right, lineno=lineno)
312 lineno = self.stream.current.lineno
313 return left
314
315 def parse_div(self):
316 lineno = self.stream.current.lineno
317 left = self.parse_floordiv()
318 while self.stream.current.type is 'div':
319 self.stream.next()
320 right = self.parse_floordiv()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200321 left = nodes.Div(left, right, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200322 lineno = self.stream.current.lineno
323 return left
324
325 def parse_floordiv(self):
326 lineno = self.stream.current.lineno
327 left = self.parse_mod()
328 while self.stream.current.type is 'floordiv':
329 self.stream.next()
330 right = self.parse_mod()
331 left = nodes.FloorDiv(left, right, lineno=lineno)
332 lineno = self.stream.current.lineno
333 return left
334
335 def parse_mod(self):
336 lineno = self.stream.current.lineno
337 left = self.parse_pow()
338 while self.stream.current.type is 'mod':
339 self.stream.next()
340 right = self.parse_pow()
341 left = nodes.Mod(left, right, lineno=lineno)
342 lineno = self.stream.current.lineno
343 return left
344
345 def parse_pow(self):
346 lineno = self.stream.current.lineno
347 left = self.parse_unary()
348 while self.stream.current.type is 'pow':
349 self.stream.next()
350 right = self.parse_unary()
351 left = nodes.Pow(left, right, lineno=lineno)
352 lineno = self.stream.current.lineno
353 return left
354
355 def parse_unary(self):
356 token_type = self.stream.current.type
357 lineno = self.stream.current.lineno
358 if token_type is 'not':
359 self.stream.next()
360 node = self.parse_unary()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200361 return nodes.Not(node, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200362 if token_type is 'sub':
363 self.stream.next()
364 node = self.parse_unary()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200365 return nodes.Neg(node, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200366 if token_type is 'add':
367 self.stream.next()
368 node = self.parse_unary()
369 return nodes.Pos(node, lineno=lineno)
370 return self.parse_primary()
371
372 def parse_primary(self, parse_postfix=True):
373 token = self.stream.current
374 if token.type is 'name':
375 if token.value in ('true', 'false'):
376 node = nodes.Const(token.value == 'true', lineno=token.lineno)
377 elif token.value == 'none':
378 node = nodes.Const(None, lineno=token.lineno)
379 else:
380 node = nodes.Name(token.value, 'load', lineno=token.lineno)
381 self.stream.next()
382 elif token.type in ('integer', 'float', 'string'):
383 self.stream.next()
384 node = nodes.Const(token.value, lineno=token.lineno)
385 elif token.type is 'lparen':
386 self.stream.next()
387 node = self.parse_tuple()
388 self.stream.expect('rparen')
389 elif token.type is 'lbracket':
390 node = self.parse_list()
391 elif token.type is 'lbrace':
392 node = self.parse_dict()
393 else:
394 raise TemplateSyntaxError("unexpected token '%s'" %
395 (token,), token.lineno,
396 self.filename)
397 if parse_postfix:
398 node = self.parse_postfix(node)
399 return node
400
401 def parse_tuple(self, enforce=False, simplified=False):
402 """
403 Parse multiple expressions into a tuple. This can also return
404 just one expression which is not a tuple. If you want to enforce
405 a tuple, pass it enforce=True (currently unused).
406 """
407 lineno = self.stream.current.lineno
408 parse = simplified and self.parse_primary or self.parse_expression
409 args = []
410 is_tuple = False
411 while 1:
412 if args:
413 self.stream.expect('comma')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200414 if self.stream.current.type in _statement_end_tokens:
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200415 break
416 args.append(parse())
417 if self.stream.current.type is not 'comma':
418 break
419 is_tuple = True
420 lineno = self.stream.current.lineno
421 if not is_tuple and args:
422 if enforce:
423 raise TemplateSyntaxError('tuple expected', lineno,
424 self.filename)
425 return args[0]
426 return nodes.Tuple(args, 'load', lineno=lineno)
427
428 def parse_list(self):
429 token = self.stream.expect('lbracket')
430 items = []
431 while self.stream.current.type is not 'rbracket':
432 if items:
433 self.stream.expect('comma')
434 if self.stream.current.type == 'rbracket':
435 break
436 items.append(self.parse_expression())
437 self.stream.expect('rbracket')
438 return nodes.List(items, lineno=token.lineno)
439
440 def parse_dict(self):
441 token = self.stream.expect('lbrace')
442 items = []
443 while self.stream.current.type is not 'rbrace':
444 if items:
445 self.stream.expect('comma')
446 if self.stream.current.type == 'rbrace':
447 break
448 key = self.parse_expression()
449 self.stream.expect('colon')
450 value = self.parse_expression()
451 items.append(nodes.Pair(key, value, lineno=key.lineno))
452 self.stream.expect('rbrace')
453 return nodes.Dict(items, token.lineno, self.filename)
454
455 def parse_postfix(self, node):
456 while 1:
457 token_type = self.stream.current.type
458 if token_type is 'dot' or token_type is 'lbracket':
459 node = self.parse_subscript(node)
460 elif token_type is 'lparen':
461 node = self.parse_call(node)
462 elif token_type is 'pipe':
463 node = self.parse_filter(node)
464 elif token_type is 'is':
465 node = self.parse_test(node)
466 else:
467 break
468 return node
469
470 def parse_subscript(self, node):
471 token = self.stream.next()
472 if token.type is 'dot':
Armin Ronachere791c2a2008-04-07 18:39:54 +0200473 attr_token = self.stream.current
474 if attr_token.type not in ('name', 'integer'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200475 raise TemplateSyntaxError('expected name or number',
Armin Ronachere791c2a2008-04-07 18:39:54 +0200476 attr_token.lineno, self.filename)
477 arg = nodes.Const(attr_token.value, lineno=attr_token.lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200478 self.stream.next()
479 elif token.type is 'lbracket':
480 args = []
481 while self.stream.current.type is not 'rbracket':
482 if args:
483 self.stream.expect('comma')
484 args.append(self.parse_subscribed())
485 self.stream.expect('rbracket')
486 if len(args) == 1:
487 arg = args[0]
488 else:
489 arg = nodes.Tuple(args, lineno, self.filename)
490 else:
491 raise TemplateSyntaxError('expected subscript expression',
492 self.lineno, self.filename)
493 return nodes.Subscript(node, arg, 'load', lineno=token.lineno)
494
495 def parse_subscribed(self):
496 lineno = self.stream.current.lineno
497
498 if self.stream.current.type is 'colon':
499 self.stream.next()
500 args = [None]
501 else:
502 node = self.parse_expression()
503 if self.stream.current.type is not 'colon':
504 return node
505 self.stream.next()
506 args = [node]
507
508 if self.stream.current.type is 'colon':
509 args.append(None)
510 elif self.stream.current.type not in ('rbracket', 'comma'):
511 args.append(self.parse_expression())
512 else:
513 args.append(None)
514
515 if self.stream.current.type is 'colon':
516 self.stream.next()
517 if self.stream.current.type not in ('rbracket', 'comma'):
518 args.append(self.parse_expression())
519 else:
520 args.append(None)
521 else:
522 args.append(None)
523
524 return nodes.Slice(lineno=lineno, *args)
525
526 def parse_call(self, node):
527 token = self.stream.expect('lparen')
528 args = []
529 kwargs = []
530 dyn_args = dyn_kwargs = None
531 require_comma = False
532
533 def ensure(expr):
534 if not expr:
535 raise TemplateSyntaxError('invalid syntax for function '
536 'call expression', token.lineno,
537 self.filename)
538
539 while self.stream.current.type is not 'rparen':
540 if require_comma:
541 self.stream.expect('comma')
542 # support for trailing comma
543 if self.stream.current.type is 'rparen':
544 break
545 if self.stream.current.type is 'mul':
546 ensure(dyn_args is None and dyn_kwargs is None)
547 self.stream.next()
548 dyn_args = self.parse_expression()
549 elif self.stream.current.type is 'pow':
550 ensure(dyn_kwargs is None)
551 self.stream.next()
552 dyn_kwargs = self.parse_expression()
553 else:
554 ensure(dyn_args is None and dyn_kwargs is None)
555 if self.stream.current.type is 'name' and \
556 self.stream.look().type is 'assign':
557 key = self.stream.current.value
558 self.stream.skip(2)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200559 kwargs.append(nodes.Keyword(key, self.parse_expression(),
560 lineno=key.lineno))
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200561 else:
562 ensure(not kwargs)
563 args.append(self.parse_expression())
564
565 require_comma = True
566 self.stream.expect('rparen')
567
568 if node is None:
569 return args, kwargs, dyn_args, dyn_kwargs
570 return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs,
571 lineno=token.lineno)
572
573 def parse_filter(self, node):
574 lineno = self.stream.current.type
575 filters = []
576 while self.stream.current.type == 'pipe':
577 self.stream.next()
578 token = self.stream.expect('name')
579 if self.stream.current.type is 'lparen':
580 args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
581 else:
582 args = []
583 kwargs = []
584 dyn_args = dyn_kwargs = None
585 filters.append(nodes.FilterCall(token.value, args, kwargs,
586 dyn_args, dyn_kwargs,
587 lineno=token.lineno))
588 return nodes.Filter(node, filters)
589
590 def parse_test(self, node):
591 token = self.stream.expect('is')
592 if self.stream.current.type is 'not':
593 self.stream.next()
594 negated = True
595 else:
596 negated = False
597 name = self.stream.expect('name').value
598 if self.stream.current.type is 'lparen':
599 args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
600 elif self.stream.current.type in ('name', 'string', 'integer',
601 'float', 'lparen', 'lbracket',
602 'lbrace', 'regex'):
603 args = [self.parse_expression()]
604 else:
605 args = []
606 kwargs = []
607 dyn_args = dyn_kwargs = None
608 node = nodes.Test(node, name, args, kwargs, dyn_args,
609 dyn_kwargs, lineno=token.lineno)
610 if negated:
611 node = nodes.NotExpression(node, lineno=token.lineno)
612 return node
613
614 def subparse(self, end_tokens=None):
615 body = []
616 data_buffer = []
617 add_data = data_buffer.append
618
619 def flush_data():
620 if data_buffer:
621 lineno = data_buffer[0].lineno
622 body.append(nodes.Output(data_buffer[:], lineno=lineno))
623 del data_buffer[:]
624
625 while self.stream:
626 token = self.stream.current
627 if token.type is 'data':
628 add_data(nodes.Const(token.value, lineno=token.lineno))
629 self.stream.next()
630 elif token.type is 'variable_begin':
631 self.stream.next()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200632 want_comma = False
633 while not self.stream.current.type in _statement_end_tokens:
634 if want_comma:
635 self.stream.expect('comma')
636 add_data(self.parse_expression())
637 want_comma = True
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200638 self.stream.expect('variable_end')
639 elif token.type is 'block_begin':
640 flush_data()
641 self.stream.next()
642 if end_tokens is not None and \
643 self.stream.current.type in end_tokens:
644 return body
645 while self.stream.current.type is not 'block_end':
646 body.append(self.parse_statement())
647 self.stream.expect('block_end')
648 else:
649 raise AssertionError('internal parsing error')
650
651 flush_data()
652 return body
653
654 def parse(self):
655 """Parse the whole template into a `Template` node."""
656 return nodes.Template(self.subparse(), lineno=1)