1 # -*- coding: utf-8 -*-
6 Implements the template parser.
8 :copyright: 2008 by Armin Ronacher.
9 :license: BSD, see LICENSE for more details.
11 from jinja2 import nodes
12 from jinja2.exceptions import TemplateSyntaxError, TemplateAssertionError
15 _statement_keywords = frozenset(['for', 'if', 'block', 'extends', 'print',
16 'macro', 'include', 'from', 'import',
18 _compare_operators = frozenset(['eq', 'ne', 'lt', 'lteq', 'gt', 'gteq'])
22 """This is the central parsing class Jinja2 uses. It's passed to
23 extensions and can be used to parse expressions or statements.
26 def __init__(self, environment, source, name=None, filename=None):
27 self.environment = environment
28 self.source = unicode(source)
30 self.filename = filename
32 self.stream = environment.lexer.tokenize(self.source, name, filename)
34 for extension in environment.extensions.itervalues():
35 for tag in extension.tags:
36 self.extensions[tag] = extension.parse
37 self._last_identifier = 0
39 def fail(self, msg, lineno=None, exc=TemplateSyntaxError):
40 """Convenience method that raises `exc` with the message, passed
41 line number or last line number as well as the current name and
45 lineno = self.stream.current.lineno
46 raise exc(msg, lineno, self.name, self.filename)
48 def is_tuple_end(self, extra_end_rules=None):
49 """Are we at the end of a tuple?"""
50 if self.stream.current.type in ('variable_end', 'block_end', 'rparen'):
52 elif extra_end_rules is not None:
53 return self.stream.current.test_any(extra_end_rules)
56 def free_identifier(self, lineno=None):
57 """Return a new free identifier as :class:`~jinja2.nodes.InternalName`."""
58 self._last_identifier += 1
59 rv = object.__new__(nodes.InternalName)
60 nodes.Node.__init__(rv, 'fi%d' % self._last_identifier, lineno=lineno)
63 def parse_statement(self):
64 """Parse a single statement."""
65 token = self.stream.current
66 if token.type is not 'name':
67 self.fail('tag name expected', token.lineno)
68 if token.value in _statement_keywords:
69 return getattr(self, 'parse_' + self.stream.current.value)()
70 if token.value == 'call':
71 return self.parse_call_block()
72 if token.value == 'filter':
73 return self.parse_filter_block()
74 ext = self.extensions.get(token.value)
77 self.fail('unknown tag %r' % token.value, token.lineno)
79 def parse_statements(self, end_tokens, drop_needle=False):
80 """Parse multiple statements into a list until one of the end tokens
81 is reached. This is used to parse the body of statements as it also
82 parses template data if appropriate. The parser checks first if the
83 current token is a colon and skips it if there is one. Then it checks
84 for the block end and parses until if one of the `end_tokens` is
85 reached. Per default the active token in the stream at the end of
86 the call is the matched end token. If this is not wanted `drop_needle`
87 can be set to `True` and the end token is removed.
89 # the first token may be a colon for python compatibility
90 self.stream.skip_if('colon')
92 # in the future it would be possible to add whole code sections
93 # by adding some sort of end of statement token and parsing those here.
94 self.stream.expect('block_end')
95 result = self.subparse(end_tokens)
102 """Parse an assign statement."""
103 lineno = self.stream.next().lineno
104 target = self.parse_assign_target()
105 self.stream.expect('assign')
106 expr = self.parse_tuple()
107 return nodes.Assign(target, expr, lineno=lineno)
110 """Parse a for loop."""
111 lineno = self.stream.expect('name:for').lineno
112 target = self.parse_assign_target(extra_end_rules=('name:in',))
113 self.stream.expect('name:in')
114 iter = self.parse_tuple(with_condexpr=False,
115 extra_end_rules=('name:recursive',))
117 if self.stream.skip_if('name:if'):
118 test = self.parse_expression()
119 recursive = self.stream.skip_if('name:recursive')
120 body = self.parse_statements(('name:endfor', 'name:else'))
121 if self.stream.next().value == 'endfor':
124 else_ = self.parse_statements(('name:endfor',), drop_needle=True)
125 return nodes.For(target, iter, body, else_, test,
126 recursive, lineno=lineno)
129 """Parse an if construct."""
130 node = result = nodes.If(lineno=self.stream.expect('name:if').lineno)
132 node.test = self.parse_tuple(with_condexpr=False)
133 node.body = self.parse_statements(('name:elif', 'name:else',
135 token = self.stream.next()
136 if token.test('name:elif'):
137 new_node = nodes.If(lineno=self.stream.current.lineno)
138 node.else_ = [new_node]
141 elif token.test('name:else'):
142 node.else_ = self.parse_statements(('name:endif',),
149 def parse_block(self):
150 node = nodes.Block(lineno=self.stream.next().lineno)
151 node.name = self.stream.expect('name').value
152 node.body = self.parse_statements(('name:endblock',), drop_needle=True)
153 self.stream.skip_if('name:' + node.name)
156 def parse_extends(self):
157 node = nodes.Extends(lineno=self.stream.next().lineno)
158 node.template = self.parse_expression()
161 def parse_import_context(self, node, default):
162 if self.stream.current.test_any('name:with', 'name:without') and \
163 self.stream.look().test('name:context'):
164 node.with_context = self.stream.next().value == 'with'
167 node.with_context = default
170 def parse_include(self):
171 node = nodes.Include(lineno=self.stream.next().lineno)
172 node.template = self.parse_expression()
173 return self.parse_import_context(node, True)
175 def parse_import(self):
176 node = nodes.Import(lineno=self.stream.next().lineno)
177 node.template = self.parse_expression()
178 self.stream.expect('name:as')
179 node.target = self.parse_assign_target(name_only=True).name
180 return self.parse_import_context(node, False)
182 def parse_from(self):
183 node = nodes.FromImport(lineno=self.stream.next().lineno)
184 node.template = self.parse_expression()
185 self.stream.expect('name:import')
189 if self.stream.current.value in ('with', 'without') and \
190 self.stream.look().test('name:context'):
191 node.with_context = self.stream.next().value == 'with'
198 self.stream.expect('comma')
199 if self.stream.current.type is 'name':
202 target = self.parse_assign_target(name_only=True)
203 if target.name.startswith('_'):
204 self.fail('names starting with an underline can not '
205 'be imported', target.lineno,
206 exc=TemplateAssertionError)
207 if self.stream.skip_if('name:as'):
208 alias = self.parse_assign_target(name_only=True)
209 node.names.append((target.name, alias.name))
211 node.names.append(target.name)
212 if parse_context() or self.stream.current.type is not 'comma':
216 if not hasattr(node, 'with_context'):
217 node.with_context = False
218 self.stream.skip_if('comma')
221 def parse_signature(self, node):
222 node.args = args = []
223 node.defaults = defaults = []
224 self.stream.expect('lparen')
225 while self.stream.current.type is not 'rparen':
227 self.stream.expect('comma')
228 arg = self.parse_assign_target(name_only=True)
230 if self.stream.skip_if('assign'):
231 defaults.append(self.parse_expression())
233 self.stream.expect('rparen')
235 def parse_call_block(self):
236 node = nodes.CallBlock(lineno=self.stream.next().lineno)
237 if self.stream.current.type is 'lparen':
238 self.parse_signature(node)
243 node.call = self.parse_expression()
244 if not isinstance(node.call, nodes.Call):
245 self.fail('expected call', node.lineno)
246 node.body = self.parse_statements(('name:endcall',), drop_needle=True)
249 def parse_filter_block(self):
250 node = nodes.FilterBlock(lineno=self.stream.next().lineno)
251 node.filter = self.parse_filter(None, start_inline=True)
252 node.body = self.parse_statements(('name:endfilter',),
256 def parse_macro(self):
257 node = nodes.Macro(lineno=self.stream.next().lineno)
258 node.name = self.parse_assign_target(name_only=True).name
259 self.parse_signature(node)
260 node.body = self.parse_statements(('name:endmacro',),
264 def parse_print(self):
265 node = nodes.Output(lineno=self.stream.next().lineno)
267 while self.stream.current.type is not 'block_end':
269 self.stream.expect('comma')
270 node.nodes.append(self.parse_expression())
273 def parse_assign_target(self, with_tuple=True, name_only=False,
274 extra_end_rules=None):
275 """Parse an assignment target. As Jinja2 allows assignments to
276 tuples, this function can parse all allowed assignment targets. Per
277 default assignments to tuples are parsed, that can be disable however
278 by setting `with_tuple` to `False`. If only assignments to names are
279 wanted `name_only` can be set to `True`. The `extra_end_rules`
280 parameter is forwarded to the tuple parsing function.
283 token = self.stream.expect('name')
284 target = nodes.Name(token.value, 'store', lineno=token.lineno)
287 target = self.parse_tuple(simplified=True,
288 extra_end_rules=extra_end_rules)
290 target = self.parse_primary(with_postfix=False)
291 target.set_ctx('store')
292 if not target.can_assign():
293 self.fail('can\'t assign to %r' % target.__class__.
294 __name__.lower(), target.lineno)
297 def parse_expression(self, with_condexpr=True):
298 """Parse an expression. Per default all expressions are parsed, if
299 the optional `with_condexpr` parameter is set to `False` conditional
300 expressions are not parsed.
303 return self.parse_condexpr()
304 return self.parse_or()
306 def parse_condexpr(self):
307 lineno = self.stream.current.lineno
308 expr1 = self.parse_or()
309 while self.stream.skip_if('name:if'):
310 expr2 = self.parse_or()
311 self.stream.expect('name:else')
312 expr3 = self.parse_condexpr()
313 expr1 = nodes.CondExpr(expr2, expr1, expr3, lineno=lineno)
314 lineno = self.stream.current.lineno
318 lineno = self.stream.current.lineno
319 left = self.parse_and()
320 while self.stream.skip_if('name:or'):
321 right = self.parse_and()
322 left = nodes.Or(left, right, lineno=lineno)
323 lineno = self.stream.current.lineno
327 lineno = self.stream.current.lineno
328 left = self.parse_compare()
329 while self.stream.skip_if('name:and'):
330 right = self.parse_compare()
331 left = nodes.And(left, right, lineno=lineno)
332 lineno = self.stream.current.lineno
335 def parse_compare(self):
336 lineno = self.stream.current.lineno
337 expr = self.parse_add()
340 token_type = self.stream.current.type
341 if token_type in _compare_operators:
343 ops.append(nodes.Operand(token_type, self.parse_add()))
344 elif self.stream.skip_if('name:in'):
345 ops.append(nodes.Operand('in', self.parse_add()))
346 elif self.stream.current.test('name:not') and \
347 self.stream.look().test('name:in'):
349 ops.append(nodes.Operand('notin', self.parse_add()))
352 lineno = self.stream.current.lineno
355 return nodes.Compare(expr, ops, lineno=lineno)
358 lineno = self.stream.current.lineno
359 left = self.parse_sub()
360 while self.stream.current.type is 'add':
362 right = self.parse_sub()
363 left = nodes.Add(left, right, lineno=lineno)
364 lineno = self.stream.current.lineno
368 lineno = self.stream.current.lineno
369 left = self.parse_concat()
370 while self.stream.current.type is 'sub':
372 right = self.parse_concat()
373 left = nodes.Sub(left, right, lineno=lineno)
374 lineno = self.stream.current.lineno
377 def parse_concat(self):
378 lineno = self.stream.current.lineno
379 args = [self.parse_mul()]
380 while self.stream.current.type is 'tilde':
382 args.append(self.parse_mul())
385 return nodes.Concat(args, lineno=lineno)
388 lineno = self.stream.current.lineno
389 left = self.parse_div()
390 while self.stream.current.type is 'mul':
392 right = self.parse_div()
393 left = nodes.Mul(left, right, lineno=lineno)
394 lineno = self.stream.current.lineno
398 lineno = self.stream.current.lineno
399 left = self.parse_floordiv()
400 while self.stream.current.type is 'div':
402 right = self.parse_floordiv()
403 left = nodes.Div(left, right, lineno=lineno)
404 lineno = self.stream.current.lineno
407 def parse_floordiv(self):
408 lineno = self.stream.current.lineno
409 left = self.parse_mod()
410 while self.stream.current.type is 'floordiv':
412 right = self.parse_mod()
413 left = nodes.FloorDiv(left, right, lineno=lineno)
414 lineno = self.stream.current.lineno
418 lineno = self.stream.current.lineno
419 left = self.parse_pow()
420 while self.stream.current.type is 'mod':
422 right = self.parse_pow()
423 left = nodes.Mod(left, right, lineno=lineno)
424 lineno = self.stream.current.lineno
428 lineno = self.stream.current.lineno
429 left = self.parse_unary()
430 while self.stream.current.type is 'pow':
432 right = self.parse_unary()
433 left = nodes.Pow(left, right, lineno=lineno)
434 lineno = self.stream.current.lineno
437 def parse_unary(self):
438 token_type = self.stream.current.type
439 lineno = self.stream.current.lineno
440 if token_type is 'name' and self.stream.current.value == 'not':
442 node = self.parse_unary()
443 return nodes.Not(node, lineno=lineno)
444 if token_type is 'sub':
446 node = self.parse_unary()
447 return nodes.Neg(node, lineno=lineno)
448 if token_type is 'add':
450 node = self.parse_unary()
451 return nodes.Pos(node, lineno=lineno)
452 return self.parse_primary()
454 def parse_primary(self, with_postfix=True):
455 token = self.stream.current
456 if token.type is 'name':
457 if token.value in ('true', 'false', 'True', 'False'):
458 node = nodes.Const(token.value in ('true', 'True'),
460 elif token.value in ('none', 'None'):
461 node = nodes.Const(None, lineno=token.lineno)
463 node = nodes.Name(token.value, 'load', lineno=token.lineno)
465 elif token.type in ('integer', 'float', 'string'):
467 node = nodes.Const(token.value, lineno=token.lineno)
468 elif token.type is 'lparen':
470 node = self.parse_tuple()
471 self.stream.expect('rparen')
472 elif token.type is 'lbracket':
473 node = self.parse_list()
474 elif token.type is 'lbrace':
475 node = self.parse_dict()
477 self.fail("unexpected token '%s'" % (token,), token.lineno)
479 node = self.parse_postfix(node)
482 def parse_tuple(self, simplified=False, with_condexpr=True,
483 extra_end_rules=None):
484 """Works like `parse_expression` but if multiple expressions are
485 delimited by a comma a :class:`~jinja2.nodes.Tuple` node is created.
486 This method could also return a regular expression instead of a tuple
487 if no commas where found.
489 The default parsing mode is a full tuple. If `simplified` is `True`
490 only names and literals are parsed. The `no_condexpr` parameter is
491 forwarded to :meth:`parse_expression`.
493 Because tuples do not require delimiters and may end in a bogus comma
494 an extra hint is needed that marks the end of a tuple. For example
495 for loops support tuples between `for` and `in`. In that case the
496 `extra_end_rules` is set to ``['name:in']``.
498 lineno = self.stream.current.lineno
500 parse = lambda: self.parse_primary(with_postfix=False)
502 parse = self.parse_expression
504 parse = lambda: self.parse_expression(with_condexpr=False)
509 self.stream.expect('comma')
510 if self.is_tuple_end(extra_end_rules):
513 if self.stream.current.type is 'comma':
517 lineno = self.stream.current.lineno
518 if not is_tuple and args:
520 return nodes.Tuple(args, 'load', lineno=lineno)
522 def parse_list(self):
523 token = self.stream.expect('lbracket')
525 while self.stream.current.type is not 'rbracket':
527 self.stream.expect('comma')
528 if self.stream.current.type == 'rbracket':
530 items.append(self.parse_expression())
531 self.stream.expect('rbracket')
532 return nodes.List(items, lineno=token.lineno)
534 def parse_dict(self):
535 token = self.stream.expect('lbrace')
537 while self.stream.current.type is not 'rbrace':
539 self.stream.expect('comma')
540 if self.stream.current.type == 'rbrace':
542 key = self.parse_expression()
543 self.stream.expect('colon')
544 value = self.parse_expression()
545 items.append(nodes.Pair(key, value, lineno=key.lineno))
546 self.stream.expect('rbrace')
547 return nodes.Dict(items, lineno=token.lineno)
549 def parse_postfix(self, node):
551 token_type = self.stream.current.type
552 if token_type is 'dot' or token_type is 'lbracket':
553 node = self.parse_subscript(node)
554 elif token_type is 'lparen':
555 node = self.parse_call(node)
556 elif token_type is 'pipe':
557 node = self.parse_filter(node)
558 elif token_type is 'name' and self.stream.current.value == 'is':
559 node = self.parse_test(node)
564 def parse_subscript(self, node):
565 token = self.stream.next()
566 if token.type is 'dot':
567 attr_token = self.stream.current
568 if attr_token.type not in ('name', 'integer'):
569 self.fail('expected name or number', attr_token.lineno)
570 arg = nodes.Const(attr_token.value, lineno=attr_token.lineno)
572 elif token.type is 'lbracket':
574 while self.stream.current.type is not 'rbracket':
576 self.stream.expect('comma')
577 args.append(self.parse_subscribed())
578 self.stream.expect('rbracket')
582 arg = nodes.Tuple(args, self.lineno, self.filename)
584 self.fail('expected subscript expression', self.lineno)
585 return nodes.Subscript(node, arg, 'load', lineno=token.lineno)
587 def parse_subscribed(self):
588 lineno = self.stream.current.lineno
590 if self.stream.current.type is 'colon':
594 node = self.parse_expression()
595 if self.stream.current.type is not 'colon':
600 if self.stream.current.type is 'colon':
602 elif self.stream.current.type not in ('rbracket', 'comma'):
603 args.append(self.parse_expression())
607 if self.stream.current.type is 'colon':
609 if self.stream.current.type not in ('rbracket', 'comma'):
610 args.append(self.parse_expression())
616 return nodes.Slice(lineno=lineno, *args)
618 def parse_call(self, node):
619 token = self.stream.expect('lparen')
622 dyn_args = dyn_kwargs = None
623 require_comma = False
627 self.fail('invalid syntax for function call expression',
630 while self.stream.current.type is not 'rparen':
632 self.stream.expect('comma')
633 # support for trailing comma
634 if self.stream.current.type is 'rparen':
636 if self.stream.current.type is 'mul':
637 ensure(dyn_args is None and dyn_kwargs is None)
639 dyn_args = self.parse_expression()
640 elif self.stream.current.type is 'pow':
641 ensure(dyn_kwargs is None)
643 dyn_kwargs = self.parse_expression()
645 ensure(dyn_args is None and dyn_kwargs is None)
646 if self.stream.current.type is 'name' and \
647 self.stream.look().type is 'assign':
648 key = self.stream.current.value
650 value = self.parse_expression()
651 kwargs.append(nodes.Keyword(key, value,
652 lineno=value.lineno))
655 args.append(self.parse_expression())
658 self.stream.expect('rparen')
661 return args, kwargs, dyn_args, dyn_kwargs
662 return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs,
665 def parse_filter(self, node, start_inline=False):
666 while self.stream.current.type == 'pipe' or start_inline:
669 token = self.stream.expect('name')
671 while self.stream.current.type is 'dot':
673 name += '.' + self.stream.expect('name').value
674 if self.stream.current.type is 'lparen':
675 args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
679 dyn_args = dyn_kwargs = None
680 node = nodes.Filter(node, name, args, kwargs, dyn_args,
681 dyn_kwargs, lineno=token.lineno)
685 def parse_test(self, node):
686 token = self.stream.next()
687 if self.stream.current.test('name:not'):
692 name = self.stream.expect('name').value
693 while self.stream.current.type is 'dot':
695 name += '.' + self.stream.expect('name').value
696 dyn_args = dyn_kwargs = None
698 if self.stream.current.type is 'lparen':
699 args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
700 elif self.stream.current.type in ('name', 'string', 'integer',
701 'float', 'lparen', 'lbracket',
703 args = [self.parse_expression()]
706 node = nodes.Test(node, name, args, kwargs, dyn_args,
707 dyn_kwargs, lineno=token.lineno)
709 node = nodes.Not(node, lineno=token.lineno)
712 def subparse(self, end_tokens=None):
715 add_data = data_buffer.append
719 lineno = data_buffer[0].lineno
720 body.append(nodes.Output(data_buffer[:], lineno=lineno))
724 token = self.stream.current
725 if token.type is 'data':
727 add_data(nodes.TemplateData(token.value,
728 lineno=token.lineno))
730 elif token.type is 'variable_begin':
732 add_data(self.parse_tuple(with_condexpr=True))
733 self.stream.expect('variable_end')
734 elif token.type is 'block_begin':
737 if end_tokens is not None and \
738 self.stream.current.test_any(*end_tokens):
740 rv = self.parse_statement()
741 if isinstance(rv, list):
745 self.stream.expect('block_end')
747 raise AssertionError('internal parsing error')
753 """Parse the whole template into a `Template` node."""
754 result = nodes.Template(self.subparse(), lineno=1)
755 result.set_environment(self.environment)