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