Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(335)

Side by Side Diff: mojo/public/third_party/jinja2/parser.py

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

Powered by Google App Engine
This is Rietveld 408576698