comparison venv/lib/python2.7/site-packages/jinja2/parser.py @ 0:d67268158946 draft

planemo upload commit a3f181f5f126803c654b3a66dd4e83a48f7e203b
author bcclaywell
date Mon, 12 Oct 2015 17:43:33 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:d67268158946
1 # -*- coding: utf-8 -*-
2 """
3 jinja2.parser
4 ~~~~~~~~~~~~~
5
6 Implements the template parser.
7
8 :copyright: (c) 2010 by the Jinja Team.
9 :license: BSD, see LICENSE for more details.
10 """
11 from jinja2 import nodes
12 from jinja2.exceptions import TemplateSyntaxError, TemplateAssertionError
13 from jinja2.lexer import describe_token, describe_token_expr
14 from jinja2._compat import imap
15
16
17 _statement_keywords = frozenset(['for', 'if', 'block', 'extends', 'print',
18 'macro', 'include', 'from', 'import',
19 'set'])
20 _compare_operators = frozenset(['eq', 'ne', 'lt', 'lteq', 'gt', 'gteq'])
21
22
23 class Parser(object):
24 """This is the central parsing class Jinja2 uses. It's passed to
25 extensions and can be used to parse expressions or statements.
26 """
27
28 def __init__(self, environment, source, name=None, filename=None,
29 state=None):
30 self.environment = environment
31 self.stream = environment._tokenize(source, name, filename, state)
32 self.name = name
33 self.filename = filename
34 self.closed = False
35 self.extensions = {}
36 for extension in environment.iter_extensions():
37 for tag in extension.tags:
38 self.extensions[tag] = extension.parse
39 self._last_identifier = 0
40 self._tag_stack = []
41 self._end_token_stack = []
42
43 def fail(self, msg, lineno=None, exc=TemplateSyntaxError):
44 """Convenience method that raises `exc` with the message, passed
45 line number or last line number as well as the current name and
46 filename.
47 """
48 if lineno is None:
49 lineno = self.stream.current.lineno
50 raise exc(msg, lineno, self.name, self.filename)
51
52 def _fail_ut_eof(self, name, end_token_stack, lineno):
53 expected = []
54 for exprs in end_token_stack:
55 expected.extend(imap(describe_token_expr, exprs))
56 if end_token_stack:
57 currently_looking = ' or '.join(
58 "'%s'" % describe_token_expr(expr)
59 for expr in end_token_stack[-1])
60 else:
61 currently_looking = None
62
63 if name is None:
64 message = ['Unexpected end of template.']
65 else:
66 message = ['Encountered unknown tag \'%s\'.' % name]
67
68 if currently_looking:
69 if name is not None and name in expected:
70 message.append('You probably made a nesting mistake. Jinja '
71 'is expecting this tag, but currently looking '
72 'for %s.' % currently_looking)
73 else:
74 message.append('Jinja was looking for the following tags: '
75 '%s.' % currently_looking)
76
77 if self._tag_stack:
78 message.append('The innermost block that needs to be '
79 'closed is \'%s\'.' % self._tag_stack[-1])
80
81 self.fail(' '.join(message), lineno)
82
83 def fail_unknown_tag(self, name, lineno=None):
84 """Called if the parser encounters an unknown tag. Tries to fail
85 with a human readable error message that could help to identify
86 the problem.
87 """
88 return self._fail_ut_eof(name, self._end_token_stack, lineno)
89
90 def fail_eof(self, end_tokens=None, lineno=None):
91 """Like fail_unknown_tag but for end of template situations."""
92 stack = list(self._end_token_stack)
93 if end_tokens is not None:
94 stack.append(end_tokens)
95 return self._fail_ut_eof(None, stack, lineno)
96
97 def is_tuple_end(self, extra_end_rules=None):
98 """Are we at the end of a tuple?"""
99 if self.stream.current.type in ('variable_end', 'block_end', 'rparen'):
100 return True
101 elif extra_end_rules is not None:
102 return self.stream.current.test_any(extra_end_rules)
103 return False
104
105 def free_identifier(self, lineno=None):
106 """Return a new free identifier as :class:`~jinja2.nodes.InternalName`."""
107 self._last_identifier += 1
108 rv = object.__new__(nodes.InternalName)
109 nodes.Node.__init__(rv, 'fi%d' % self._last_identifier, lineno=lineno)
110 return rv
111
112 def parse_statement(self):
113 """Parse a single statement."""
114 token = self.stream.current
115 if token.type != 'name':
116 self.fail('tag name expected', token.lineno)
117 self._tag_stack.append(token.value)
118 pop_tag = True
119 try:
120 if token.value in _statement_keywords:
121 return getattr(self, 'parse_' + self.stream.current.value)()
122 if token.value == 'call':
123 return self.parse_call_block()
124 if token.value == 'filter':
125 return self.parse_filter_block()
126 ext = self.extensions.get(token.value)
127 if ext is not None:
128 return ext(self)
129
130 # did not work out, remove the token we pushed by accident
131 # from the stack so that the unknown tag fail function can
132 # produce a proper error message.
133 self._tag_stack.pop()
134 pop_tag = False
135 self.fail_unknown_tag(token.value, token.lineno)
136 finally:
137 if pop_tag:
138 self._tag_stack.pop()
139
140 def parse_statements(self, end_tokens, drop_needle=False):
141 """Parse multiple statements into a list until one of the end tokens
142 is reached. This is used to parse the body of statements as it also
143 parses template data if appropriate. The parser checks first if the
144 current token is a colon and skips it if there is one. Then it checks
145 for the block end and parses until if one of the `end_tokens` is
146 reached. Per default the active token in the stream at the end of
147 the call is the matched end token. If this is not wanted `drop_needle`
148 can be set to `True` and the end token is removed.
149 """
150 # the first token may be a colon for python compatibility
151 self.stream.skip_if('colon')
152
153 # in the future it would be possible to add whole code sections
154 # by adding some sort of end of statement token and parsing those here.
155 self.stream.expect('block_end')
156 result = self.subparse(end_tokens)
157
158 # we reached the end of the template too early, the subparser
159 # does not check for this, so we do that now
160 if self.stream.current.type == 'eof':
161 self.fail_eof(end_tokens)
162
163 if drop_needle:
164 next(self.stream)
165 return result
166
167 def parse_set(self):
168 """Parse an assign statement."""
169 lineno = next(self.stream).lineno
170 target = self.parse_assign_target()
171 if self.stream.skip_if('assign'):
172 expr = self.parse_tuple()
173 return nodes.Assign(target, expr, lineno=lineno)
174 body = self.parse_statements(('name:endset',),
175 drop_needle=True)
176 return nodes.AssignBlock(target, body, lineno=lineno)
177
178 def parse_for(self):
179 """Parse a for loop."""
180 lineno = self.stream.expect('name:for').lineno
181 target = self.parse_assign_target(extra_end_rules=('name:in',))
182 self.stream.expect('name:in')
183 iter = self.parse_tuple(with_condexpr=False,
184 extra_end_rules=('name:recursive',))
185 test = None
186 if self.stream.skip_if('name:if'):
187 test = self.parse_expression()
188 recursive = self.stream.skip_if('name:recursive')
189 body = self.parse_statements(('name:endfor', 'name:else'))
190 if next(self.stream).value == 'endfor':
191 else_ = []
192 else:
193 else_ = self.parse_statements(('name:endfor',), drop_needle=True)
194 return nodes.For(target, iter, body, else_, test,
195 recursive, lineno=lineno)
196
197 def parse_if(self):
198 """Parse an if construct."""
199 node = result = nodes.If(lineno=self.stream.expect('name:if').lineno)
200 while 1:
201 node.test = self.parse_tuple(with_condexpr=False)
202 node.body = self.parse_statements(('name:elif', 'name:else',
203 'name:endif'))
204 token = next(self.stream)
205 if token.test('name:elif'):
206 new_node = nodes.If(lineno=self.stream.current.lineno)
207 node.else_ = [new_node]
208 node = new_node
209 continue
210 elif token.test('name:else'):
211 node.else_ = self.parse_statements(('name:endif',),
212 drop_needle=True)
213 else:
214 node.else_ = []
215 break
216 return result
217
218 def parse_block(self):
219 node = nodes.Block(lineno=next(self.stream).lineno)
220 node.name = self.stream.expect('name').value
221 node.scoped = self.stream.skip_if('name:scoped')
222
223 # common problem people encounter when switching from django
224 # to jinja. we do not support hyphens in block names, so let's
225 # raise a nicer error message in that case.
226 if self.stream.current.type == 'sub':
227 self.fail('Block names in Jinja have to be valid Python '
228 'identifiers and may not contain hyphens, use an '
229 'underscore instead.')
230
231 node.body = self.parse_statements(('name:endblock',), drop_needle=True)
232 self.stream.skip_if('name:' + node.name)
233 return node
234
235 def parse_extends(self):
236 node = nodes.Extends(lineno=next(self.stream).lineno)
237 node.template = self.parse_expression()
238 return node
239
240 def parse_import_context(self, node, default):
241 if self.stream.current.test_any('name:with', 'name:without') and \
242 self.stream.look().test('name:context'):
243 node.with_context = next(self.stream).value == 'with'
244 self.stream.skip()
245 else:
246 node.with_context = default
247 return node
248
249 def parse_include(self):
250 node = nodes.Include(lineno=next(self.stream).lineno)
251 node.template = self.parse_expression()
252 if self.stream.current.test('name:ignore') and \
253 self.stream.look().test('name:missing'):
254 node.ignore_missing = True
255 self.stream.skip(2)
256 else:
257 node.ignore_missing = False
258 return self.parse_import_context(node, True)
259
260 def parse_import(self):
261 node = nodes.Import(lineno=next(self.stream).lineno)
262 node.template = self.parse_expression()
263 self.stream.expect('name:as')
264 node.target = self.parse_assign_target(name_only=True).name
265 return self.parse_import_context(node, False)
266
267 def parse_from(self):
268 node = nodes.FromImport(lineno=next(self.stream).lineno)
269 node.template = self.parse_expression()
270 self.stream.expect('name:import')
271 node.names = []
272
273 def parse_context():
274 if self.stream.current.value in ('with', 'without') and \
275 self.stream.look().test('name:context'):
276 node.with_context = next(self.stream).value == 'with'
277 self.stream.skip()
278 return True
279 return False
280
281 while 1:
282 if node.names:
283 self.stream.expect('comma')
284 if self.stream.current.type == 'name':
285 if parse_context():
286 break
287 target = self.parse_assign_target(name_only=True)
288 if target.name.startswith('_'):
289 self.fail('names starting with an underline can not '
290 'be imported', target.lineno,
291 exc=TemplateAssertionError)
292 if self.stream.skip_if('name:as'):
293 alias = self.parse_assign_target(name_only=True)
294 node.names.append((target.name, alias.name))
295 else:
296 node.names.append(target.name)
297 if parse_context() or self.stream.current.type != 'comma':
298 break
299 else:
300 break
301 if not hasattr(node, 'with_context'):
302 node.with_context = False
303 self.stream.skip_if('comma')
304 return node
305
306 def parse_signature(self, node):
307 node.args = args = []
308 node.defaults = defaults = []
309 self.stream.expect('lparen')
310 while self.stream.current.type != 'rparen':
311 if args:
312 self.stream.expect('comma')
313 arg = self.parse_assign_target(name_only=True)
314 arg.set_ctx('param')
315 if self.stream.skip_if('assign'):
316 defaults.append(self.parse_expression())
317 elif defaults:
318 self.fail('non-default argument follows default argument')
319 args.append(arg)
320 self.stream.expect('rparen')
321
322 def parse_call_block(self):
323 node = nodes.CallBlock(lineno=next(self.stream).lineno)
324 if self.stream.current.type == 'lparen':
325 self.parse_signature(node)
326 else:
327 node.args = []
328 node.defaults = []
329
330 node.call = self.parse_expression()
331 if not isinstance(node.call, nodes.Call):
332 self.fail('expected call', node.lineno)
333 node.body = self.parse_statements(('name:endcall',), drop_needle=True)
334 return node
335
336 def parse_filter_block(self):
337 node = nodes.FilterBlock(lineno=next(self.stream).lineno)
338 node.filter = self.parse_filter(None, start_inline=True)
339 node.body = self.parse_statements(('name:endfilter',),
340 drop_needle=True)
341 return node
342
343 def parse_macro(self):
344 node = nodes.Macro(lineno=next(self.stream).lineno)
345 node.name = self.parse_assign_target(name_only=True).name
346 self.parse_signature(node)
347 node.body = self.parse_statements(('name:endmacro',),
348 drop_needle=True)
349 return node
350
351 def parse_print(self):
352 node = nodes.Output(lineno=next(self.stream).lineno)
353 node.nodes = []
354 while self.stream.current.type != 'block_end':
355 if node.nodes:
356 self.stream.expect('comma')
357 node.nodes.append(self.parse_expression())
358 return node
359
360 def parse_assign_target(self, with_tuple=True, name_only=False,
361 extra_end_rules=None):
362 """Parse an assignment target. As Jinja2 allows assignments to
363 tuples, this function can parse all allowed assignment targets. Per
364 default assignments to tuples are parsed, that can be disable however
365 by setting `with_tuple` to `False`. If only assignments to names are
366 wanted `name_only` can be set to `True`. The `extra_end_rules`
367 parameter is forwarded to the tuple parsing function.
368 """
369 if name_only:
370 token = self.stream.expect('name')
371 target = nodes.Name(token.value, 'store', lineno=token.lineno)
372 else:
373 if with_tuple:
374 target = self.parse_tuple(simplified=True,
375 extra_end_rules=extra_end_rules)
376 else:
377 target = self.parse_primary()
378 target.set_ctx('store')
379 if not target.can_assign():
380 self.fail('can\'t assign to %r' % target.__class__.
381 __name__.lower(), target.lineno)
382 return target
383
384 def parse_expression(self, with_condexpr=True):
385 """Parse an expression. Per default all expressions are parsed, if
386 the optional `with_condexpr` parameter is set to `False` conditional
387 expressions are not parsed.
388 """
389 if with_condexpr:
390 return self.parse_condexpr()
391 return self.parse_or()
392
393 def parse_condexpr(self):
394 lineno = self.stream.current.lineno
395 expr1 = self.parse_or()
396 while self.stream.skip_if('name:if'):
397 expr2 = self.parse_or()
398 if self.stream.skip_if('name:else'):
399 expr3 = self.parse_condexpr()
400 else:
401 expr3 = None
402 expr1 = nodes.CondExpr(expr2, expr1, expr3, lineno=lineno)
403 lineno = self.stream.current.lineno
404 return expr1
405
406 def parse_or(self):
407 lineno = self.stream.current.lineno
408 left = self.parse_and()
409 while self.stream.skip_if('name:or'):
410 right = self.parse_and()
411 left = nodes.Or(left, right, lineno=lineno)
412 lineno = self.stream.current.lineno
413 return left
414
415 def parse_and(self):
416 lineno = self.stream.current.lineno
417 left = self.parse_not()
418 while self.stream.skip_if('name:and'):
419 right = self.parse_not()
420 left = nodes.And(left, right, lineno=lineno)
421 lineno = self.stream.current.lineno
422 return left
423
424 def parse_not(self):
425 if self.stream.current.test('name:not'):
426 lineno = next(self.stream).lineno
427 return nodes.Not(self.parse_not(), lineno=lineno)
428 return self.parse_compare()
429
430 def parse_compare(self):
431 lineno = self.stream.current.lineno
432 expr = self.parse_add()
433 ops = []
434 while 1:
435 token_type = self.stream.current.type
436 if token_type in _compare_operators:
437 next(self.stream)
438 ops.append(nodes.Operand(token_type, self.parse_add()))
439 elif self.stream.skip_if('name:in'):
440 ops.append(nodes.Operand('in', self.parse_add()))
441 elif (self.stream.current.test('name:not') and
442 self.stream.look().test('name:in')):
443 self.stream.skip(2)
444 ops.append(nodes.Operand('notin', self.parse_add()))
445 else:
446 break
447 lineno = self.stream.current.lineno
448 if not ops:
449 return expr
450 return nodes.Compare(expr, ops, lineno=lineno)
451
452 def parse_add(self):
453 lineno = self.stream.current.lineno
454 left = self.parse_sub()
455 while self.stream.current.type == 'add':
456 next(self.stream)
457 right = self.parse_sub()
458 left = nodes.Add(left, right, lineno=lineno)
459 lineno = self.stream.current.lineno
460 return left
461
462 def parse_sub(self):
463 lineno = self.stream.current.lineno
464 left = self.parse_concat()
465 while self.stream.current.type == 'sub':
466 next(self.stream)
467 right = self.parse_concat()
468 left = nodes.Sub(left, right, lineno=lineno)
469 lineno = self.stream.current.lineno
470 return left
471
472 def parse_concat(self):
473 lineno = self.stream.current.lineno
474 args = [self.parse_mul()]
475 while self.stream.current.type == 'tilde':
476 next(self.stream)
477 args.append(self.parse_mul())
478 if len(args) == 1:
479 return args[0]
480 return nodes.Concat(args, lineno=lineno)
481
482 def parse_mul(self):
483 lineno = self.stream.current.lineno
484 left = self.parse_div()
485 while self.stream.current.type == 'mul':
486 next(self.stream)
487 right = self.parse_div()
488 left = nodes.Mul(left, right, lineno=lineno)
489 lineno = self.stream.current.lineno
490 return left
491
492 def parse_div(self):
493 lineno = self.stream.current.lineno
494 left = self.parse_floordiv()
495 while self.stream.current.type == 'div':
496 next(self.stream)
497 right = self.parse_floordiv()
498 left = nodes.Div(left, right, lineno=lineno)
499 lineno = self.stream.current.lineno
500 return left
501
502 def parse_floordiv(self):
503 lineno = self.stream.current.lineno
504 left = self.parse_mod()
505 while self.stream.current.type == 'floordiv':
506 next(self.stream)
507 right = self.parse_mod()
508 left = nodes.FloorDiv(left, right, lineno=lineno)
509 lineno = self.stream.current.lineno
510 return left
511
512 def parse_mod(self):
513 lineno = self.stream.current.lineno
514 left = self.parse_pow()
515 while self.stream.current.type == 'mod':
516 next(self.stream)
517 right = self.parse_pow()
518 left = nodes.Mod(left, right, lineno=lineno)
519 lineno = self.stream.current.lineno
520 return left
521
522 def parse_pow(self):
523 lineno = self.stream.current.lineno
524 left = self.parse_unary()
525 while self.stream.current.type == 'pow':
526 next(self.stream)
527 right = self.parse_unary()
528 left = nodes.Pow(left, right, lineno=lineno)
529 lineno = self.stream.current.lineno
530 return left
531
532 def parse_unary(self, with_filter=True):
533 token_type = self.stream.current.type
534 lineno = self.stream.current.lineno
535 if token_type == 'sub':
536 next(self.stream)
537 node = nodes.Neg(self.parse_unary(False), lineno=lineno)
538 elif token_type == 'add':
539 next(self.stream)
540 node = nodes.Pos(self.parse_unary(False), lineno=lineno)
541 else:
542 node = self.parse_primary()
543 node = self.parse_postfix(node)
544 if with_filter:
545 node = self.parse_filter_expr(node)
546 return node
547
548 def parse_primary(self):
549 token = self.stream.current
550 if token.type == 'name':
551 if token.value in ('true', 'false', 'True', 'False'):
552 node = nodes.Const(token.value in ('true', 'True'),
553 lineno=token.lineno)
554 elif token.value in ('none', 'None'):
555 node = nodes.Const(None, lineno=token.lineno)
556 else:
557 node = nodes.Name(token.value, 'load', lineno=token.lineno)
558 next(self.stream)
559 elif token.type == 'string':
560 next(self.stream)
561 buf = [token.value]
562 lineno = token.lineno
563 while self.stream.current.type == 'string':
564 buf.append(self.stream.current.value)
565 next(self.stream)
566 node = nodes.Const(''.join(buf), lineno=lineno)
567 elif token.type in ('integer', 'float'):
568 next(self.stream)
569 node = nodes.Const(token.value, lineno=token.lineno)
570 elif token.type == 'lparen':
571 next(self.stream)
572 node = self.parse_tuple(explicit_parentheses=True)
573 self.stream.expect('rparen')
574 elif token.type == 'lbracket':
575 node = self.parse_list()
576 elif token.type == 'lbrace':
577 node = self.parse_dict()
578 else:
579 self.fail("unexpected '%s'" % describe_token(token), token.lineno)
580 return node
581
582 def parse_tuple(self, simplified=False, with_condexpr=True,
583 extra_end_rules=None, explicit_parentheses=False):
584 """Works like `parse_expression` but if multiple expressions are
585 delimited by a comma a :class:`~jinja2.nodes.Tuple` node is created.
586 This method could also return a regular expression instead of a tuple
587 if no commas where found.
588
589 The default parsing mode is a full tuple. If `simplified` is `True`
590 only names and literals are parsed. The `no_condexpr` parameter is
591 forwarded to :meth:`parse_expression`.
592
593 Because tuples do not require delimiters and may end in a bogus comma
594 an extra hint is needed that marks the end of a tuple. For example
595 for loops support tuples between `for` and `in`. In that case the
596 `extra_end_rules` is set to ``['name:in']``.
597
598 `explicit_parentheses` is true if the parsing was triggered by an
599 expression in parentheses. This is used to figure out if an empty
600 tuple is a valid expression or not.
601 """
602 lineno = self.stream.current.lineno
603 if simplified:
604 parse = self.parse_primary
605 elif with_condexpr:
606 parse = self.parse_expression
607 else:
608 parse = lambda: self.parse_expression(with_condexpr=False)
609 args = []
610 is_tuple = False
611 while 1:
612 if args:
613 self.stream.expect('comma')
614 if self.is_tuple_end(extra_end_rules):
615 break
616 args.append(parse())
617 if self.stream.current.type == 'comma':
618 is_tuple = True
619 else:
620 break
621 lineno = self.stream.current.lineno
622
623 if not is_tuple:
624 if args:
625 return args[0]
626
627 # if we don't have explicit parentheses, an empty tuple is
628 # not a valid expression. This would mean nothing (literally
629 # nothing) in the spot of an expression would be an empty
630 # tuple.
631 if not explicit_parentheses:
632 self.fail('Expected an expression, got \'%s\'' %
633 describe_token(self.stream.current))
634
635 return nodes.Tuple(args, 'load', lineno=lineno)
636
637 def parse_list(self):
638 token = self.stream.expect('lbracket')
639 items = []
640 while self.stream.current.type != 'rbracket':
641 if items:
642 self.stream.expect('comma')
643 if self.stream.current.type == 'rbracket':
644 break
645 items.append(self.parse_expression())
646 self.stream.expect('rbracket')
647 return nodes.List(items, lineno=token.lineno)
648
649 def parse_dict(self):
650 token = self.stream.expect('lbrace')
651 items = []
652 while self.stream.current.type != 'rbrace':
653 if items:
654 self.stream.expect('comma')
655 if self.stream.current.type == 'rbrace':
656 break
657 key = self.parse_expression()
658 self.stream.expect('colon')
659 value = self.parse_expression()
660 items.append(nodes.Pair(key, value, lineno=key.lineno))
661 self.stream.expect('rbrace')
662 return nodes.Dict(items, lineno=token.lineno)
663
664 def parse_postfix(self, node):
665 while 1:
666 token_type = self.stream.current.type
667 if token_type == 'dot' or token_type == 'lbracket':
668 node = self.parse_subscript(node)
669 # calls are valid both after postfix expressions (getattr
670 # and getitem) as well as filters and tests
671 elif token_type == 'lparen':
672 node = self.parse_call(node)
673 else:
674 break
675 return node
676
677 def parse_filter_expr(self, node):
678 while 1:
679 token_type = self.stream.current.type
680 if token_type == 'pipe':
681 node = self.parse_filter(node)
682 elif token_type == 'name' and self.stream.current.value == 'is':
683 node = self.parse_test(node)
684 # calls are valid both after postfix expressions (getattr
685 # and getitem) as well as filters and tests
686 elif token_type == 'lparen':
687 node = self.parse_call(node)
688 else:
689 break
690 return node
691
692 def parse_subscript(self, node):
693 token = next(self.stream)
694 if token.type == 'dot':
695 attr_token = self.stream.current
696 next(self.stream)
697 if attr_token.type == 'name':
698 return nodes.Getattr(node, attr_token.value, 'load',
699 lineno=token.lineno)
700 elif attr_token.type != 'integer':
701 self.fail('expected name or number', attr_token.lineno)
702 arg = nodes.Const(attr_token.value, lineno=attr_token.lineno)
703 return nodes.Getitem(node, arg, 'load', lineno=token.lineno)
704 if token.type == 'lbracket':
705 args = []
706 while self.stream.current.type != 'rbracket':
707 if args:
708 self.stream.expect('comma')
709 args.append(self.parse_subscribed())
710 self.stream.expect('rbracket')
711 if len(args) == 1:
712 arg = args[0]
713 else:
714 arg = nodes.Tuple(args, 'load', lineno=token.lineno)
715 return nodes.Getitem(node, arg, 'load', lineno=token.lineno)
716 self.fail('expected subscript expression', self.lineno)
717
718 def parse_subscribed(self):
719 lineno = self.stream.current.lineno
720
721 if self.stream.current.type == 'colon':
722 next(self.stream)
723 args = [None]
724 else:
725 node = self.parse_expression()
726 if self.stream.current.type != 'colon':
727 return node
728 next(self.stream)
729 args = [node]
730
731 if self.stream.current.type == 'colon':
732 args.append(None)
733 elif self.stream.current.type not in ('rbracket', 'comma'):
734 args.append(self.parse_expression())
735 else:
736 args.append(None)
737
738 if self.stream.current.type == 'colon':
739 next(self.stream)
740 if self.stream.current.type not in ('rbracket', 'comma'):
741 args.append(self.parse_expression())
742 else:
743 args.append(None)
744 else:
745 args.append(None)
746
747 return nodes.Slice(lineno=lineno, *args)
748
749 def parse_call(self, node):
750 token = self.stream.expect('lparen')
751 args = []
752 kwargs = []
753 dyn_args = dyn_kwargs = None
754 require_comma = False
755
756 def ensure(expr):
757 if not expr:
758 self.fail('invalid syntax for function call expression',
759 token.lineno)
760
761 while self.stream.current.type != 'rparen':
762 if require_comma:
763 self.stream.expect('comma')
764 # support for trailing comma
765 if self.stream.current.type == 'rparen':
766 break
767 if self.stream.current.type == 'mul':
768 ensure(dyn_args is None and dyn_kwargs is None)
769 next(self.stream)
770 dyn_args = self.parse_expression()
771 elif self.stream.current.type == 'pow':
772 ensure(dyn_kwargs is None)
773 next(self.stream)
774 dyn_kwargs = self.parse_expression()
775 else:
776 ensure(dyn_args is None and dyn_kwargs is None)
777 if self.stream.current.type == 'name' and \
778 self.stream.look().type == 'assign':
779 key = self.stream.current.value
780 self.stream.skip(2)
781 value = self.parse_expression()
782 kwargs.append(nodes.Keyword(key, value,
783 lineno=value.lineno))
784 else:
785 ensure(not kwargs)
786 args.append(self.parse_expression())
787
788 require_comma = True
789 self.stream.expect('rparen')
790
791 if node is None:
792 return args, kwargs, dyn_args, dyn_kwargs
793 return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs,
794 lineno=token.lineno)
795
796 def parse_filter(self, node, start_inline=False):
797 while self.stream.current.type == 'pipe' or start_inline:
798 if not start_inline:
799 next(self.stream)
800 token = self.stream.expect('name')
801 name = token.value
802 while self.stream.current.type == 'dot':
803 next(self.stream)
804 name += '.' + self.stream.expect('name').value
805 if self.stream.current.type == 'lparen':
806 args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
807 else:
808 args = []
809 kwargs = []
810 dyn_args = dyn_kwargs = None
811 node = nodes.Filter(node, name, args, kwargs, dyn_args,
812 dyn_kwargs, lineno=token.lineno)
813 start_inline = False
814 return node
815
816 def parse_test(self, node):
817 token = next(self.stream)
818 if self.stream.current.test('name:not'):
819 next(self.stream)
820 negated = True
821 else:
822 negated = False
823 name = self.stream.expect('name').value
824 while self.stream.current.type == 'dot':
825 next(self.stream)
826 name += '.' + self.stream.expect('name').value
827 dyn_args = dyn_kwargs = None
828 kwargs = []
829 if self.stream.current.type == 'lparen':
830 args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
831 elif (self.stream.current.type in ('name', 'string', 'integer',
832 'float', 'lparen', 'lbracket',
833 'lbrace') and not
834 self.stream.current.test_any('name:else', 'name:or',
835 'name:and')):
836 if self.stream.current.test('name:is'):
837 self.fail('You cannot chain multiple tests with is')
838 args = [self.parse_expression()]
839 else:
840 args = []
841 node = nodes.Test(node, name, args, kwargs, dyn_args,
842 dyn_kwargs, lineno=token.lineno)
843 if negated:
844 node = nodes.Not(node, lineno=token.lineno)
845 return node
846
847 def subparse(self, end_tokens=None):
848 body = []
849 data_buffer = []
850 add_data = data_buffer.append
851
852 if end_tokens is not None:
853 self._end_token_stack.append(end_tokens)
854
855 def flush_data():
856 if data_buffer:
857 lineno = data_buffer[0].lineno
858 body.append(nodes.Output(data_buffer[:], lineno=lineno))
859 del data_buffer[:]
860
861 try:
862 while self.stream:
863 token = self.stream.current
864 if token.type == 'data':
865 if token.value:
866 add_data(nodes.TemplateData(token.value,
867 lineno=token.lineno))
868 next(self.stream)
869 elif token.type == 'variable_begin':
870 next(self.stream)
871 add_data(self.parse_tuple(with_condexpr=True))
872 self.stream.expect('variable_end')
873 elif token.type == 'block_begin':
874 flush_data()
875 next(self.stream)
876 if end_tokens is not None and \
877 self.stream.current.test_any(*end_tokens):
878 return body
879 rv = self.parse_statement()
880 if isinstance(rv, list):
881 body.extend(rv)
882 else:
883 body.append(rv)
884 self.stream.expect('block_end')
885 else:
886 raise AssertionError('internal parsing error')
887
888 flush_data()
889 finally:
890 if end_tokens is not None:
891 self._end_token_stack.pop()
892
893 return body
894
895 def parse(self):
896 """Parse the whole template into a `Template` node."""
897 result = nodes.Template(self.subparse(), lineno=1)
898 result.set_environment(self.environment)
899 return result