there is now a workaround in the compiler that makes sure it's possible to call thing...
[jinja2.git] / jinja2 / parser.py
1 # -*- 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 """
11 from operator import itemgetter
12 from jinja2 import nodes
13 from jinja2.exceptions import TemplateSyntaxError
14
15
16 statement_end_tokens = set(['variable_end', 'block_end', 'in'])
17 _statement_keywords = frozenset(['for', 'if', 'block', 'extends', 'print',
18                                  'macro', 'include', 'from', 'import'])
19 _compare_operators = frozenset(['eq', 'ne', 'lt', 'lteq', 'gt', 'gteq', 'in'])
20 _tuple_edge_tokens = set(['rparen']) | statement_end_tokens
21
22
23 class Parser(object):
24     """The template parser class.
25
26     Transforms sourcecode into an abstract syntax tree.
27     """
28
29     def __init__(self, environment, source, filename=None):
30         self.environment = environment
31         if isinstance(filename, unicode):
32             filename = filename.encode('utf-8')
33         self.source = unicode(source)
34         self.filename = filename
35         self.closed = False
36         self.stream = environment.lexer.tokenize(source, filename)
37         self.extensions = {}
38         for extension in environment.extensions:
39             for tag in extension.tags:
40                 self.extensions[tag] = extension.parse
41
42     def parse_statement(self):
43         """Parse a single statement."""
44         token_type = self.stream.current.type
45         if token_type in _statement_keywords:
46             return getattr(self, 'parse_' + token_type)()
47         elif token_type is 'call':
48             return self.parse_call_block()
49         elif token_type is 'filter':
50             return self.parse_filter_block()
51         elif token_type is 'name':
52             ext = self.extensions.get(self.stream.current.value)
53             if ext is not None:
54                 return ext(self)
55         lineno = self.stream.current
56         expr = self.parse_tuple()
57         if self.stream.current.type == 'assign':
58             result = self.parse_assign(expr)
59         else:
60             result = nodes.ExprStmt(expr, lineno=lineno)
61         return result
62
63     def parse_assign(self, target):
64         """Parse an assign statement."""
65         lineno = self.stream.expect('assign').lineno
66         if not target.can_assign():
67             raise TemplateSyntaxError("can't assign to '%s'" %
68                                       target.__class__.__name__.lower(),
69                                       target.lineno, self.filename)
70         expr = self.parse_tuple()
71         target.set_ctx('store')
72         return nodes.Assign(target, expr, lineno=lineno)
73
74     def parse_statements(self, end_tokens, drop_needle=False):
75         """Parse multiple statements into a list until one of the end tokens
76         is reached.  This is used to parse the body of statements as it also
77         parses template data if appropriate.
78         """
79         # the first token may be a colon for python compatibility
80         if self.stream.current.type is 'colon':
81             self.stream.next()
82
83         # in the future it would be possible to add whole code sections
84         # by adding some sort of end of statement token and parsing those here.
85         self.stream.expect('block_end')
86         result = self.subparse(end_tokens)
87
88         if drop_needle:
89             self.stream.next()
90         return result
91
92     def parse_for(self):
93         """Parse a for loop."""
94         lineno = self.stream.expect('for').lineno
95         target = self.parse_tuple(simplified=True)
96         if not target.can_assign():
97             raise TemplateSyntaxError("can't assign to '%s'" %
98                                       target.__class__.__name__.lower(),
99                                       target.lineno, self.filename)
100         target.set_ctx('store')
101         self.stream.expect('in')
102         iter = self.parse_tuple(no_condexpr=True)
103         test = None
104         if self.stream.current.type is 'if':
105             self.stream.next()
106             test = self.parse_expression()
107         body = self.parse_statements(('endfor', 'else'))
108         if self.stream.next().type is 'endfor':
109             else_ = []
110         else:
111             else_ = self.parse_statements(('endfor',), drop_needle=True)
112         return nodes.For(target, iter, body, else_, test, lineno=lineno)
113
114     def parse_if(self):
115         """Parse an if construct."""
116         node = result = nodes.If(lineno=self.stream.expect('if').lineno)
117         while 1:
118             # TODO: exclude conditional expressions here
119             node.test = self.parse_tuple()
120             node.body = self.parse_statements(('elif', 'else', 'endif'))
121             token_type = self.stream.next().type
122             if token_type is 'elif':
123                 new_node = nodes.If(lineno=self.stream.current.lineno)
124                 node.else_ = [new_node]
125                 node = new_node
126                 continue
127             elif token_type is 'else':
128                 node.else_ = self.parse_statements(('endif',),
129                                                    drop_needle=True)
130             else:
131                 node.else_ = []
132             break
133         return result
134
135     def parse_block(self):
136         node = nodes.Block(lineno=self.stream.expect('block').lineno)
137         node.name = self.stream.expect('name').value
138         node.body = self.parse_statements(('endblock',), drop_needle=True)
139         return node
140
141     def parse_extends(self):
142         node = nodes.Extends(lineno=self.stream.expect('extends').lineno)
143         node.template = self.parse_expression()
144         return node
145
146     def parse_include(self):
147         node = nodes.Include(lineno=self.stream.expect('include').lineno)
148         node.template = self.parse_expression()
149         return node
150
151     def parse_import(self):
152         node = nodes.Import(lineno=self.stream.expect('import').lineno)
153         node.template = self.parse_expression()
154         self.stream.expect('name:as')
155         node.target = self.stream.expect('name').value
156         if not nodes.Name(node.target, 'store').can_assign():
157             raise TemplateSyntaxError('can\'t assign imported template '
158                                       'to %r' % node.target, node.lineno,
159                                       self.filename)
160         return node
161
162     def parse_from(self):
163         node = nodes.FromImport(lineno=self.stream.expect('from').lineno)
164         node.template = self.parse_expression()
165         self.stream.expect('import')
166         node.names = []
167         while 1:
168             if node.names:
169                 self.stream.expect('comma')
170             if self.stream.current.type is 'name':
171                 target = nodes.Name(self.stream.current.value, 'store')
172                 if not target.can_assign():
173                     raise TemplateSyntaxError('can\'t import object named %r'
174                                               % target.name, target.lineno,
175                                               self.filename)
176                 elif target.name.startswith('__'):
177                     raise TemplateAssertionError('names starting with two '
178                                                  'underscores can not be '
179                                                  'imported', target.lineno,
180                                                  self.filename)
181                 self.stream.next()
182                 if self.stream.current.test('name:as'):
183                     self.stream.next()
184                     alias = self.stream.expect('name')
185                     if not nodes.Name(alias.value, 'store').can_assign():
186                         raise TemplateSyntaxError('can\'t name imported '
187                                                   'object %r.' % alias.value,
188                                                   alias.lineno, self.filename)
189                     node.names.append((target.name, alias.value))
190                 else:
191                     node.names.append(target.name)
192                 if self.stream.current.type is not 'comma':
193                     break
194             else:
195                 break
196         if self.stream.current.type is 'comma':
197             self.stream.next()
198         return node
199
200     def parse_signature(self, node):
201         node.args = args = []
202         node.defaults = defaults = []
203         self.stream.expect('lparen')
204         while self.stream.current.type is not 'rparen':
205             if args:
206                 self.stream.expect('comma')
207             token = self.stream.expect('name')
208             arg = nodes.Name(token.value, 'param', lineno=token.lineno)
209             if not arg.can_assign():
210                 raise TemplateSyntaxError("can't assign to '%s'" %
211                                           arg.name, arg.lineno,
212                                           self.filename)
213             if self.stream.current.type is 'assign':
214                 self.stream.next()
215                 defaults.append(self.parse_expression())
216             args.append(arg)
217         self.stream.expect('rparen')
218
219     def parse_call_block(self):
220         node = nodes.CallBlock(lineno=self.stream.expect('call').lineno)
221         if self.stream.current.type is 'lparen':
222             self.parse_signature(node)
223
224         node.call = self.parse_expression()
225         if not isinstance(node.call, nodes.Call):
226             raise TemplateSyntaxError('expected call', node.lineno,
227                                       self.filename)
228         node.body = self.parse_statements(('endcall',), drop_needle=True)
229         return node
230
231     def parse_filter_block(self):
232         node = nodes.FilterBlock(lineno=self.stream.expect('filter').lineno)
233         node.filter = self.parse_filter(None, start_inline=True)
234         node.body = self.parse_statements(('endfilter',), drop_needle=True)
235         return node
236
237     def parse_macro(self):
238         node = nodes.Macro(lineno=self.stream.expect('macro').lineno)
239         node.name = self.stream.expect('name').value
240         # make sure that assignments to that name are allowed
241         if not nodes.Name(node.name, 'store').can_assign():
242             raise TemplateSyntaxError('can\'t assign macro to %r' %
243                                       node.target, node.lineno,
244                                       self.filename)
245         self.parse_signature(node)
246         node.body = self.parse_statements(('endmacro',), drop_needle=True)
247         return node
248
249     def parse_print(self):
250         node = nodes.Output(lineno=self.stream.expect('print').lineno)
251         node.nodes = []
252         while self.stream.current.type not in statement_end_tokens:
253             if node.nodes:
254                 self.stream.expect('comma')
255             node.nodes.append(self.parse_expression())
256         return node
257
258     def parse_expression(self, no_condexpr=False):
259         """Parse an expression."""
260         if no_condexpr:
261             return self.parse_or()
262         return self.parse_condexpr()
263
264     def parse_condexpr(self):
265         lineno = self.stream.current.lineno
266         expr1 = self.parse_or()
267         while self.stream.current.type is 'if':
268             self.stream.next()
269             expr2 = self.parse_or()
270             self.stream.expect('else')
271             expr3 = self.parse_condexpr()
272             expr1 = nodes.CondExpr(expr2, expr1, expr3, lineno=lineno)
273             lineno = self.stream.current.lineno
274         return expr1
275
276     def parse_or(self):
277         lineno = self.stream.current.lineno
278         left = self.parse_and()
279         while self.stream.current.type is 'or':
280             self.stream.next()
281             right = self.parse_and()
282             left = nodes.Or(left, right, lineno=lineno)
283             lineno = self.stream.current.lineno
284         return left
285
286     def parse_and(self):
287         lineno = self.stream.current.lineno
288         left = self.parse_compare()
289         while self.stream.current.type is 'and':
290             self.stream.next()
291             right = self.parse_compare()
292             left = nodes.And(left, right, lineno=lineno)
293             lineno = self.stream.current.lineno
294         return left
295
296     def parse_compare(self):
297         lineno = self.stream.current.lineno
298         expr = self.parse_add()
299         ops = []
300         while 1:
301             token_type = self.stream.current.type
302             if token_type in _compare_operators:
303                 self.stream.next()
304                 ops.append(nodes.Operand(token_type, self.parse_add()))
305             elif token_type is 'not' and self.stream.look().type is 'in':
306                 self.stream.skip(2)
307                 ops.append(nodes.Operand('notin', self.parse_add()))
308             else:
309                 break
310             lineno = self.stream.current.lineno
311         if not ops:
312             return expr
313         return nodes.Compare(expr, ops, lineno=lineno)
314
315     def parse_add(self):
316         lineno = self.stream.current.lineno
317         left = self.parse_sub()
318         while self.stream.current.type is 'add':
319             self.stream.next()
320             right = self.parse_sub()
321             left = nodes.Add(left, right, lineno=lineno)
322             lineno = self.stream.current.lineno
323         return left
324
325     def parse_sub(self):
326         lineno = self.stream.current.lineno
327         left = self.parse_concat()
328         while self.stream.current.type is 'sub':
329             self.stream.next()
330             right = self.parse_concat()
331             left = nodes.Sub(left, right, lineno=lineno)
332             lineno = self.stream.current.lineno
333         return left
334
335     def parse_concat(self):
336         lineno = self.stream.current.lineno
337         args = [self.parse_mul()]
338         while self.stream.current.type is 'tilde':
339             self.stream.next()
340             args.append(self.parse_mul())
341         if len(args) == 1:
342             return args[0]
343         return nodes.Concat(args, lineno=lineno)
344
345     def parse_mul(self):
346         lineno = self.stream.current.lineno
347         left = self.parse_div()
348         while self.stream.current.type is 'mul':
349             self.stream.next()
350             right = self.parse_div()
351             left = nodes.Mul(left, right, lineno=lineno)
352             lineno = self.stream.current.lineno
353         return left
354
355     def parse_div(self):
356         lineno = self.stream.current.lineno
357         left = self.parse_floordiv()
358         while self.stream.current.type is 'div':
359             self.stream.next()
360             right = self.parse_floordiv()
361             left = nodes.Div(left, right, lineno=lineno)
362             lineno = self.stream.current.lineno
363         return left
364
365     def parse_floordiv(self):
366         lineno = self.stream.current.lineno
367         left = self.parse_mod()
368         while self.stream.current.type is 'floordiv':
369             self.stream.next()
370             right = self.parse_mod()
371             left = nodes.FloorDiv(left, right, lineno=lineno)
372             lineno = self.stream.current.lineno
373         return left
374
375     def parse_mod(self):
376         lineno = self.stream.current.lineno
377         left = self.parse_pow()
378         while self.stream.current.type is 'mod':
379             self.stream.next()
380             right = self.parse_pow()
381             left = nodes.Mod(left, right, lineno=lineno)
382             lineno = self.stream.current.lineno
383         return left
384
385     def parse_pow(self):
386         lineno = self.stream.current.lineno
387         left = self.parse_unary()
388         while self.stream.current.type is 'pow':
389             self.stream.next()
390             right = self.parse_unary()
391             left = nodes.Pow(left, right, lineno=lineno)
392             lineno = self.stream.current.lineno
393         return left
394
395     def parse_unary(self):
396         token_type = self.stream.current.type
397         lineno = self.stream.current.lineno
398         if token_type is 'not':
399             self.stream.next()
400             node = self.parse_unary()
401             return nodes.Not(node, lineno=lineno)
402         if token_type is 'sub':
403             self.stream.next()
404             node = self.parse_unary()
405             return nodes.Neg(node, lineno=lineno)
406         if token_type is 'add':
407             self.stream.next()
408             node = self.parse_unary()
409             return nodes.Pos(node, lineno=lineno)
410         return self.parse_primary()
411
412     def parse_primary(self, parse_postfix=True):
413         token = self.stream.current
414         if token.type is 'name':
415             if token.value in ('true', 'false'):
416                 node = nodes.Const(token.value == 'true', lineno=token.lineno)
417             elif token.value == 'none':
418                 node = nodes.Const(None, lineno=token.lineno)
419             else:
420                 node = nodes.Name(token.value, 'load', lineno=token.lineno)
421             self.stream.next()
422         elif token.type in ('integer', 'float', 'string'):
423             self.stream.next()
424             node = nodes.Const(token.value, lineno=token.lineno)
425         elif token.type is 'lparen':
426             self.stream.next()
427             node = self.parse_tuple()
428             self.stream.expect('rparen')
429         elif token.type is 'lbracket':
430             node = self.parse_list()
431         elif token.type is 'lbrace':
432             node = self.parse_dict()
433         else:
434             raise TemplateSyntaxError("unexpected token '%s'" %
435                                       (token,), token.lineno,
436                                       self.filename)
437         if parse_postfix:
438             node = self.parse_postfix(node)
439         return node
440
441     def parse_tuple(self, enforce=False, simplified=False, no_condexpr=False):
442         """
443         Parse multiple expressions into a tuple. This can also return
444         just one expression which is not a tuple. If you want to enforce
445         a tuple, pass it enforce=True (currently unused).
446         """
447         lineno = self.stream.current.lineno
448         if simplified:
449             parse = self.parse_primary
450         elif no_condexpr:
451             parse = lambda: self.parse_expression(no_condexpr=True)
452         else:
453             parse = self.parse_expression
454         args = []
455         is_tuple = False
456         while 1:
457             if args:
458                 self.stream.expect('comma')
459             if self.stream.current.type in _tuple_edge_tokens:
460                 break
461             args.append(parse())
462             if self.stream.current.type is 'comma':
463                 is_tuple = True
464             else:
465                 break
466             lineno = self.stream.current.lineno
467         if not is_tuple and args:
468             if enforce:
469                 raise TemplateSyntaxError('tuple expected', lineno,
470                                           self.filename)
471             return args[0]
472         return nodes.Tuple(args, 'load', lineno=lineno)
473
474     def parse_list(self):
475         token = self.stream.expect('lbracket')
476         items = []
477         while self.stream.current.type is not 'rbracket':
478             if items:
479                 self.stream.expect('comma')
480             if self.stream.current.type == 'rbracket':
481                 break
482             items.append(self.parse_expression())
483         self.stream.expect('rbracket')
484         return nodes.List(items, lineno=token.lineno)
485
486     def parse_dict(self):
487         token = self.stream.expect('lbrace')
488         items = []
489         while self.stream.current.type is not 'rbrace':
490             if items:
491                 self.stream.expect('comma')
492             if self.stream.current.type == 'rbrace':
493                 break
494             key = self.parse_expression()
495             self.stream.expect('colon')
496             value = self.parse_expression()
497             items.append(nodes.Pair(key, value, lineno=key.lineno))
498         self.stream.expect('rbrace')
499         return nodes.Dict(items, lineno=token.lineno)
500
501     def parse_postfix(self, node):
502         while 1:
503             token_type = self.stream.current.type
504             if token_type is 'dot' or token_type is 'lbracket':
505                 node = self.parse_subscript(node)
506             elif token_type is 'lparen':
507                 node = self.parse_call(node)
508             elif token_type is 'pipe':
509                 node = self.parse_filter(node)
510             elif token_type is 'is':
511                 node = self.parse_test(node)
512             else:
513                 break
514         return node
515
516     def parse_subscript(self, node):
517         token = self.stream.next()
518         if token.type is 'dot':
519             attr_token = self.stream.current
520             if attr_token.type not in ('name', 'integer'):
521                 raise TemplateSyntaxError('expected name or number',
522                                           attr_token.lineno, self.filename)
523             arg = nodes.Const(attr_token.value, lineno=attr_token.lineno)
524             self.stream.next()
525         elif token.type is 'lbracket':
526             args = []
527             while self.stream.current.type is not 'rbracket':
528                 if args:
529                     self.stream.expect('comma')
530                 args.append(self.parse_subscribed())
531             self.stream.expect('rbracket')
532             if len(args) == 1:
533                 arg = args[0]
534             else:
535                 arg = nodes.Tuple(args, lineno, self.filename)
536         else:
537             raise TemplateSyntaxError('expected subscript expression',
538                                       self.lineno, self.filename)
539         return nodes.Subscript(node, arg, 'load', lineno=token.lineno)
540
541     def parse_subscribed(self):
542         lineno = self.stream.current.lineno
543
544         if self.stream.current.type is 'colon':
545             self.stream.next()
546             args = [None]
547         else:
548             node = self.parse_expression()
549             if self.stream.current.type is not 'colon':
550                 return node
551             self.stream.next()
552             args = [node]
553
554         if self.stream.current.type is 'colon':
555             args.append(None)
556         elif self.stream.current.type not in ('rbracket', 'comma'):
557             args.append(self.parse_expression())
558         else:
559             args.append(None)
560
561         if self.stream.current.type is 'colon':
562             self.stream.next()
563             if self.stream.current.type not in ('rbracket', 'comma'):
564                 args.append(self.parse_expression())
565             else:
566                 args.append(None)
567         else:
568             args.append(None)
569
570         return nodes.Slice(lineno=lineno, *args)
571
572     def parse_call(self, node):
573         token = self.stream.expect('lparen')
574         args = []
575         kwargs = []
576         dyn_args = dyn_kwargs = None
577         require_comma = False
578
579         def ensure(expr):
580             if not expr:
581                 raise TemplateSyntaxError('invalid syntax for function '
582                                           'call expression', token.lineno,
583                                           self.filename)
584
585         while self.stream.current.type is not 'rparen':
586             if require_comma:
587                 self.stream.expect('comma')
588                 # support for trailing comma
589                 if self.stream.current.type is 'rparen':
590                     break
591             if self.stream.current.type is 'mul':
592                 ensure(dyn_args is None and dyn_kwargs is None)
593                 self.stream.next()
594                 dyn_args = self.parse_expression()
595             elif self.stream.current.type is 'pow':
596                 ensure(dyn_kwargs is None)
597                 self.stream.next()
598                 dyn_kwargs = self.parse_expression()
599             else:
600                 ensure(dyn_args is None and dyn_kwargs is None)
601                 if self.stream.current.type is 'name' and \
602                     self.stream.look().type is 'assign':
603                     key = self.stream.current.value
604                     self.stream.skip(2)
605                     value = self.parse_expression()
606                     kwargs.append(nodes.Keyword(key, value,
607                                                 lineno=value.lineno))
608                 else:
609                     ensure(not kwargs)
610                     args.append(self.parse_expression())
611
612             require_comma = True
613         self.stream.expect('rparen')
614
615         if node is None:
616             return args, kwargs, dyn_args, dyn_kwargs
617         return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs,
618                           lineno=token.lineno)
619
620     def parse_filter(self, node, start_inline=False):
621         lineno = self.stream.current.type
622         while self.stream.current.type == 'pipe' or start_inline:
623             if not start_inline:
624                 self.stream.next()
625             token = self.stream.expect('name')
626             if self.stream.current.type is 'lparen':
627                 args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
628             else:
629                 args = []
630                 kwargs = []
631                 dyn_args = dyn_kwargs = None
632             node = nodes.Filter(node, token.value, args, kwargs, dyn_args,
633                                 dyn_kwargs, lineno=token.lineno)
634             start_inline = False
635         return node
636
637     def parse_test(self, node):
638         token = self.stream.expect('is')
639         if self.stream.current.type is 'not':
640             self.stream.next()
641             negated = True
642         else:
643             negated = False
644         name = self.stream.expect('name').value
645         dyn_args = dyn_kwargs = None
646         kwargs = []
647         if self.stream.current.type is 'lparen':
648             args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
649         elif self.stream.current.type in ('name', 'string', 'integer',
650                                           'float', 'lparen', 'lbracket',
651                                           'lbrace', 'regex'):
652             args = [self.parse_expression()]
653         else:
654             args = []
655         node = nodes.Test(node, name, args, kwargs, dyn_args,
656                           dyn_kwargs, lineno=token.lineno)
657         if negated:
658             node = nodes.Not(node, lineno=token.lineno)
659         return node
660
661     def subparse(self, end_tokens=None):
662         body = []
663         data_buffer = []
664         add_data = data_buffer.append
665
666         def flush_data():
667             if data_buffer:
668                 lineno = data_buffer[0].lineno
669                 body.append(nodes.Output(data_buffer[:], lineno=lineno))
670                 del data_buffer[:]
671
672         while self.stream:
673             token = self.stream.current
674             if token.type is 'data':
675                 if token.value:
676                     add_data(nodes.Const(token.value, lineno=token.lineno))
677                 self.stream.next()
678             elif token.type is 'variable_begin':
679                 self.stream.next()
680                 add_data(self.parse_tuple())
681                 self.stream.expect('variable_end')
682             elif token.type is 'block_begin':
683                 flush_data()
684                 self.stream.next()
685                 if end_tokens is not None and \
686                    self.stream.current.test_many(end_tokens):
687                     return body
688                 while self.stream.current.type is not 'block_end':
689                     body.append(self.parse_statement())
690                 self.stream.expect('block_end')
691             else:
692                 raise AssertionError('internal parsing error')
693
694         flush_data()
695         return body
696
697     def parse(self):
698         """Parse the whole template into a `Template` node."""
699         result = nodes.Template(self.subparse(), lineno=1)
700         result.set_environment(self.environment)
701         return result