blob: 5185df37221b57f99272b901f41eaf81f97ed8de [file] [log] [blame]
Armin Ronacher07bc6842008-03-31 14:18:49 +02001# -*- coding: utf-8 -*-
2"""
3 jinja2.parser
4 ~~~~~~~~~~~~~
5
6 Implements the template parser.
7
8 :copyright: 2008 by Armin Ronacher.
9 :license: BSD, see LICENSE for more details.
10"""
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020011from jinja2 import nodes
12from jinja2.exceptions import TemplateSyntaxError
Armin Ronacher07bc6842008-03-31 14:18:49 +020013
14
Armin Ronachere791c2a2008-04-07 18:39:54 +020015_statement_keywords = frozenset(['for', 'if', 'block', 'extends', 'print',
16 'macro', 'include'])
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020017_compare_operators = frozenset(['eq', 'ne', 'lt', 'lteq', 'gt', 'gteq', 'in'])
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020018_statement_end_tokens = set(['elif', 'else', 'endblock', 'endfilter',
Armin Ronachere791c2a2008-04-07 18:39:54 +020019 'endfor', 'endif', 'endmacro', 'variable_end',
20 'in', 'recursive', 'endcall', 'block_end'])
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020021
Armin Ronacher07bc6842008-03-31 14:18:49 +020022
23class Parser(object):
Armin Ronacherd55ab532008-04-09 16:13:39 +020024 """The template parser class.
Armin Ronacher07bc6842008-03-31 14:18:49 +020025
26 Transforms sourcecode into an abstract syntax tree.
27 """
28
29 def __init__(self, environment, source, filename=None):
30 self.environment = environment
Armin Ronacher07bc6842008-03-31 14:18:49 +020031 if isinstance(filename, unicode):
32 filename = filename.encode('utf-8')
Armin Ronacherbf7c4ad2008-04-12 12:02:36 +020033 self.source = unicode(source)
Armin Ronacher07bc6842008-03-31 14:18:49 +020034 self.filename = filename
35 self.closed = False
Armin Ronacher07bc6842008-03-31 14:18:49 +020036 self.no_variable_block = self.environment.lexer.no_variable_block
37 self.stream = environment.lexer.tokenize(source, filename)
38
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020039 def end_statement(self):
40 """Make sure that the statement ends properly."""
41 if self.stream.current.type is 'semicolon':
42 self.stream.next()
43 elif self.stream.current.type not in _statement_end_tokens:
44 raise TemplateSyntaxError('ambigous end of statement',
45 self.stream.current.lineno,
46 self.filename)
47
48 def parse_statement(self):
49 """Parse a single statement."""
50 token_type = self.stream.current.type
51 if token_type in _statement_keywords:
52 return getattr(self, 'parse_' + token_type)()
53 elif token_type is 'call':
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020054 return self.parse_call_block()
Armin Ronacherfa865fb2008-04-12 22:11:53 +020055 elif token_type is 'filter':
56 return self.parse_filter_block()
Armin Ronachere791c2a2008-04-07 18:39:54 +020057 lineno = self.stream.current
Armin Ronacher8efc5222008-04-08 14:47:40 +020058 expr = self.parse_tuple()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020059 if self.stream.current.type == 'assign':
Armin Ronachere791c2a2008-04-07 18:39:54 +020060 result = self.parse_assign(expr)
61 else:
62 result = nodes.ExprStmt(expr, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020063 self.end_statement()
Armin Ronachere791c2a2008-04-07 18:39:54 +020064 return result
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020065
66 def parse_assign(self, target):
67 """Parse an assign statement."""
68 lineno = self.stream.expect('assign').lineno
69 if not target.can_assign():
70 raise TemplateSyntaxError("can't assign to '%s'" %
71 target, target.lineno,
72 self.filename)
73 expr = self.parse_tuple()
74 self.end_statement()
Armin Ronachere791c2a2008-04-07 18:39:54 +020075 target.set_ctx('store')
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020076 return nodes.Assign(target, expr, lineno=lineno)
77
78 def parse_statements(self, end_tokens, drop_needle=False):
79 """
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
82 also parses template data if appropriate.
83 """
84 # the first token may be a colon for python compatibility
85 if self.stream.current.type is 'colon':
86 self.stream.next()
87
88 if self.stream.current.type is 'block_end':
89 self.stream.next()
90 result = self.subparse(end_tokens)
91 else:
92 result = []
93 while self.stream.current.type not in end_tokens:
Armin Ronachere791c2a2008-04-07 18:39:54 +020094 if self.stream.current.type is 'block_end':
95 self.stream.next()
96 result.extend(self.subparse(end_tokens))
97 break
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020098 result.append(self.parse_statement())
99 if drop_needle:
100 self.stream.next()
101 return result
102
103 def parse_for(self):
104 """Parse a for loop."""
105 lineno = self.stream.expect('for').lineno
106 target = self.parse_tuple(simplified=True)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200107 if not target.can_assign():
108 raise TemplateSyntaxError("can't assign to '%s'" %
109 target, target.lineno,
110 self.filename)
111 target.set_ctx('store')
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200112 self.stream.expect('in')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200113 iter = self.parse_tuple(no_condexpr=True)
114 test = None
115 if self.stream.current.type is 'if':
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200116 self.stream.next()
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200117 test = self.parse_expression()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200118 body = self.parse_statements(('endfor', 'else'))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200119 if self.stream.next().type is 'endfor':
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200120 else_ = []
121 else:
122 else_ = self.parse_statements(('endfor',), drop_needle=True)
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200123 return nodes.For(target, iter, body, else_, test, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200124
125 def parse_if(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200126 """Parse an if construct."""
127 node = result = nodes.If(lineno=self.stream.expect('if').lineno)
128 while 1:
129 # TODO: exclude conditional expressions here
130 node.test = self.parse_tuple()
131 node.body = self.parse_statements(('elif', 'else', 'endif'))
132 token_type = self.stream.next().type
133 if token_type is 'elif':
134 new_node = nodes.If(lineno=self.stream.current.lineno)
135 node.else_ = [new_node]
136 node = new_node
137 continue
138 elif token_type is 'else':
139 node.else_ = self.parse_statements(('endif',),
140 drop_needle=True)
141 else:
142 node.else_ = []
143 break
144 return result
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200145
146 def parse_block(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200147 node = nodes.Block(lineno=self.stream.expect('block').lineno)
148 node.name = self.stream.expect('name').value
149 node.body = self.parse_statements(('endblock',), drop_needle=True)
150 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200151
152 def parse_extends(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200153 node = nodes.Extends(lineno=self.stream.expect('extends').lineno)
154 node.template = self.parse_expression()
155 self.end_statement()
156 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200157
158 def parse_include(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200159 node = nodes.Include(lineno=self.stream.expect('include').lineno)
160 expr = self.parse_expression()
161 if self.stream.current.type is 'assign':
162 self.stream.next()
Armin Ronacher7c0116f2008-04-12 00:06:19 +0200163 if not isinstance(expr, nodes.Name):
164 raise TemplateSyntaxError('must assign imported template to '
165 'variable or current scope',
166 expr.lineno, self.filename)
167 if not expr.can_assign():
Armin Ronachere791c2a2008-04-07 18:39:54 +0200168 raise TemplateSyntaxError('can\'t assign imported template '
Armin Ronacher7c0116f2008-04-12 00:06:19 +0200169 'to %r' % expr, expr.lineno,
Armin Ronachere791c2a2008-04-07 18:39:54 +0200170 self.filename)
Armin Ronacher7c0116f2008-04-12 00:06:19 +0200171 node.target = expr.name
Armin Ronachere791c2a2008-04-07 18:39:54 +0200172 node.template = self.parse_expression()
173 else:
174 node.target = None
175 node.template = expr
176 self.end_statement()
177 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200178
Armin Ronacher71082072008-04-12 14:19:36 +0200179 def parse_signature(self, node):
180 node.args = args = []
181 node.defaults = defaults = []
182 self.stream.expect('lparen')
183 while self.stream.current.type is not 'rparen':
184 if args:
185 self.stream.expect('comma')
186 token = self.stream.expect('name')
187 arg = nodes.Name(token.value, 'param', lineno=token.lineno)
188 if not arg.can_assign():
189 raise TemplateSyntaxError("can't assign to '%s'" %
190 arg.name, arg.lineno,
191 self.filename)
192 if self.stream.current.type is 'assign':
193 self.stream.next()
194 defaults.append(self.parse_expression())
195 args.append(arg)
196 self.stream.expect('rparen')
197
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200198 def parse_call_block(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200199 node = nodes.CallBlock(lineno=self.stream.expect('call').lineno)
Armin Ronacher71082072008-04-12 14:19:36 +0200200 if self.stream.current.type is 'lparen':
201 self.parse_signature(node)
202
Armin Ronacher8edbe492008-04-10 20:43:43 +0200203 node.call = self.parse_expression()
204 if not isinstance(node.call, nodes.Call):
205 raise TemplateSyntaxError('expected call', node.lineno,
206 self.filename)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200207 node.body = self.parse_statements(('endcall',), drop_needle=True)
208 return node
209
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200210 def parse_filter_block(self):
211 node = nodes.FilterBlock(lineno=self.stream.expect('filter').lineno)
212 node.filter = self.parse_filter(None, start_inline=True)
213 node.body = self.parse_statements(('endfilter',), drop_needle=True)
214 return node
215
Armin Ronachere791c2a2008-04-07 18:39:54 +0200216 def parse_macro(self):
217 node = nodes.Macro(lineno=self.stream.expect('macro').lineno)
218 node.name = self.stream.expect('name').value
Armin Ronacherf059ec12008-04-11 22:21:00 +0200219 # make sure that assignments to that name are allowed
220 if not nodes.Name(node.name, 'store').can_assign():
221 raise TemplateSyntaxError('can\'t assign macro to %r' %
222 node.target, node.lineno,
223 self.filename)
Armin Ronacher71082072008-04-12 14:19:36 +0200224 self.parse_signature(node)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200225 node.body = self.parse_statements(('endmacro',), drop_needle=True)
226 return node
227
228 def parse_print(self):
229 node = nodes.Output(lineno=self.stream.expect('print').lineno)
230 node.nodes = []
231 while self.stream.current.type not in _statement_end_tokens:
232 if node.nodes:
233 self.stream.expect('comma')
234 node.nodes.append(self.parse_expression())
235 self.end_statement()
236 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200237
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200238 def parse_expression(self, no_condexpr=False):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200239 """Parse an expression."""
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200240 if no_condexpr:
241 return self.parse_or()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200242 return self.parse_condexpr()
243
244 def parse_condexpr(self):
245 lineno = self.stream.current.lineno
246 expr1 = self.parse_or()
247 while self.stream.current.type is 'if':
248 self.stream.next()
249 expr2 = self.parse_or()
250 self.stream.expect('else')
251 expr3 = self.parse_condexpr()
252 expr1 = nodes.CondExpr(expr2, expr1, expr3, lineno=lineno)
253 lineno = self.stream.current.lineno
254 return expr1
255
256 def parse_or(self):
257 lineno = self.stream.current.lineno
258 left = self.parse_and()
259 while self.stream.current.type is 'or':
260 self.stream.next()
261 right = self.parse_and()
262 left = nodes.Or(left, right, lineno=lineno)
263 lineno = self.stream.current.lineno
264 return left
265
266 def parse_and(self):
267 lineno = self.stream.current.lineno
268 left = self.parse_compare()
269 while self.stream.current.type is 'and':
270 self.stream.next()
271 right = self.parse_compare()
272 left = nodes.And(left, right, lineno=lineno)
273 lineno = self.stream.current.lineno
274 return left
275
276 def parse_compare(self):
277 lineno = self.stream.current.lineno
278 expr = self.parse_add()
279 ops = []
280 while 1:
281 token_type = self.stream.current.type
282 if token_type in _compare_operators:
283 self.stream.next()
284 ops.append(nodes.Operand(token_type, self.parse_add()))
285 elif token_type is 'not' and self.stream.look().type is 'in':
286 self.stream.skip(2)
287 ops.append(nodes.Operand('notin', self.parse_add()))
288 else:
289 break
290 lineno = self.stream.current.lineno
291 if not ops:
292 return expr
293 return nodes.Compare(expr, ops, lineno=lineno)
294
295 def parse_add(self):
296 lineno = self.stream.current.lineno
297 left = self.parse_sub()
298 while self.stream.current.type is 'add':
299 self.stream.next()
300 right = self.parse_sub()
301 left = nodes.Add(left, right, lineno=lineno)
302 lineno = self.stream.current.lineno
303 return left
304
305 def parse_sub(self):
306 lineno = self.stream.current.lineno
307 left = self.parse_concat()
308 while self.stream.current.type is 'sub':
309 self.stream.next()
310 right = self.parse_concat()
311 left = nodes.Sub(left, right, lineno=lineno)
312 lineno = self.stream.current.lineno
313 return left
314
315 def parse_concat(self):
316 lineno = self.stream.current.lineno
317 args = [self.parse_mul()]
318 while self.stream.current.type is 'tilde':
319 self.stream.next()
320 args.append(self.parse_mul())
321 if len(args) == 1:
322 return args[0]
323 return nodes.Concat(args, lineno=lineno)
324
325 def parse_mul(self):
326 lineno = self.stream.current.lineno
327 left = self.parse_div()
328 while self.stream.current.type is 'mul':
329 self.stream.next()
330 right = self.parse_div()
331 left = nodes.Mul(left, right, lineno=lineno)
332 lineno = self.stream.current.lineno
333 return left
334
335 def parse_div(self):
336 lineno = self.stream.current.lineno
337 left = self.parse_floordiv()
338 while self.stream.current.type is 'div':
339 self.stream.next()
340 right = self.parse_floordiv()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200341 left = nodes.Div(left, right, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200342 lineno = self.stream.current.lineno
343 return left
344
345 def parse_floordiv(self):
346 lineno = self.stream.current.lineno
347 left = self.parse_mod()
348 while self.stream.current.type is 'floordiv':
349 self.stream.next()
350 right = self.parse_mod()
351 left = nodes.FloorDiv(left, right, lineno=lineno)
352 lineno = self.stream.current.lineno
353 return left
354
355 def parse_mod(self):
356 lineno = self.stream.current.lineno
357 left = self.parse_pow()
358 while self.stream.current.type is 'mod':
359 self.stream.next()
360 right = self.parse_pow()
361 left = nodes.Mod(left, right, lineno=lineno)
362 lineno = self.stream.current.lineno
363 return left
364
365 def parse_pow(self):
366 lineno = self.stream.current.lineno
367 left = self.parse_unary()
368 while self.stream.current.type is 'pow':
369 self.stream.next()
370 right = self.parse_unary()
371 left = nodes.Pow(left, right, lineno=lineno)
372 lineno = self.stream.current.lineno
373 return left
374
375 def parse_unary(self):
376 token_type = self.stream.current.type
377 lineno = self.stream.current.lineno
378 if token_type is 'not':
379 self.stream.next()
380 node = self.parse_unary()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200381 return nodes.Not(node, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200382 if token_type is 'sub':
383 self.stream.next()
384 node = self.parse_unary()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200385 return nodes.Neg(node, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200386 if token_type is 'add':
387 self.stream.next()
388 node = self.parse_unary()
389 return nodes.Pos(node, lineno=lineno)
390 return self.parse_primary()
391
392 def parse_primary(self, parse_postfix=True):
393 token = self.stream.current
394 if token.type is 'name':
395 if token.value in ('true', 'false'):
396 node = nodes.Const(token.value == 'true', lineno=token.lineno)
397 elif token.value == 'none':
398 node = nodes.Const(None, lineno=token.lineno)
399 else:
400 node = nodes.Name(token.value, 'load', lineno=token.lineno)
401 self.stream.next()
402 elif token.type in ('integer', 'float', 'string'):
403 self.stream.next()
404 node = nodes.Const(token.value, lineno=token.lineno)
405 elif token.type is 'lparen':
406 self.stream.next()
407 node = self.parse_tuple()
408 self.stream.expect('rparen')
409 elif token.type is 'lbracket':
410 node = self.parse_list()
411 elif token.type is 'lbrace':
412 node = self.parse_dict()
413 else:
414 raise TemplateSyntaxError("unexpected token '%s'" %
415 (token,), token.lineno,
416 self.filename)
417 if parse_postfix:
418 node = self.parse_postfix(node)
419 return node
420
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200421 def parse_tuple(self, enforce=False, simplified=False, no_condexpr=False):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200422 """
423 Parse multiple expressions into a tuple. This can also return
424 just one expression which is not a tuple. If you want to enforce
425 a tuple, pass it enforce=True (currently unused).
426 """
427 lineno = self.stream.current.lineno
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200428 if simplified:
429 parse = self.parse_primary
430 elif no_condexpr:
431 parse = lambda: self.parse_expression(no_condexpr=True)
432 else:
433 parse = self.parse_expression
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200434 args = []
435 is_tuple = False
436 while 1:
437 if args:
438 self.stream.expect('comma')
Armin Ronachere791c2a2008-04-07 18:39:54 +0200439 if self.stream.current.type in _statement_end_tokens:
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200440 break
441 args.append(parse())
442 if self.stream.current.type is not 'comma':
443 break
444 is_tuple = True
445 lineno = self.stream.current.lineno
446 if not is_tuple and args:
447 if enforce:
448 raise TemplateSyntaxError('tuple expected', lineno,
449 self.filename)
450 return args[0]
451 return nodes.Tuple(args, 'load', lineno=lineno)
452
453 def parse_list(self):
454 token = self.stream.expect('lbracket')
455 items = []
456 while self.stream.current.type is not 'rbracket':
457 if items:
458 self.stream.expect('comma')
459 if self.stream.current.type == 'rbracket':
460 break
461 items.append(self.parse_expression())
462 self.stream.expect('rbracket')
463 return nodes.List(items, lineno=token.lineno)
464
465 def parse_dict(self):
466 token = self.stream.expect('lbrace')
467 items = []
468 while self.stream.current.type is not 'rbrace':
469 if items:
470 self.stream.expect('comma')
471 if self.stream.current.type == 'rbrace':
472 break
473 key = self.parse_expression()
474 self.stream.expect('colon')
475 value = self.parse_expression()
476 items.append(nodes.Pair(key, value, lineno=key.lineno))
477 self.stream.expect('rbrace')
478 return nodes.Dict(items, token.lineno, self.filename)
479
480 def parse_postfix(self, node):
481 while 1:
482 token_type = self.stream.current.type
483 if token_type is 'dot' or token_type is 'lbracket':
484 node = self.parse_subscript(node)
485 elif token_type is 'lparen':
486 node = self.parse_call(node)
487 elif token_type is 'pipe':
488 node = self.parse_filter(node)
489 elif token_type is 'is':
490 node = self.parse_test(node)
491 else:
492 break
493 return node
494
495 def parse_subscript(self, node):
496 token = self.stream.next()
497 if token.type is 'dot':
Armin Ronachere791c2a2008-04-07 18:39:54 +0200498 attr_token = self.stream.current
499 if attr_token.type not in ('name', 'integer'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200500 raise TemplateSyntaxError('expected name or number',
Armin Ronachere791c2a2008-04-07 18:39:54 +0200501 attr_token.lineno, self.filename)
502 arg = nodes.Const(attr_token.value, lineno=attr_token.lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200503 self.stream.next()
504 elif token.type is 'lbracket':
505 args = []
506 while self.stream.current.type is not 'rbracket':
507 if args:
508 self.stream.expect('comma')
509 args.append(self.parse_subscribed())
510 self.stream.expect('rbracket')
511 if len(args) == 1:
512 arg = args[0]
513 else:
514 arg = nodes.Tuple(args, lineno, self.filename)
515 else:
516 raise TemplateSyntaxError('expected subscript expression',
517 self.lineno, self.filename)
518 return nodes.Subscript(node, arg, 'load', lineno=token.lineno)
519
520 def parse_subscribed(self):
521 lineno = self.stream.current.lineno
522
523 if self.stream.current.type is 'colon':
524 self.stream.next()
525 args = [None]
526 else:
527 node = self.parse_expression()
528 if self.stream.current.type is not 'colon':
529 return node
530 self.stream.next()
531 args = [node]
532
533 if self.stream.current.type is 'colon':
534 args.append(None)
535 elif self.stream.current.type not in ('rbracket', 'comma'):
536 args.append(self.parse_expression())
537 else:
538 args.append(None)
539
540 if self.stream.current.type is 'colon':
541 self.stream.next()
542 if self.stream.current.type not in ('rbracket', 'comma'):
543 args.append(self.parse_expression())
544 else:
545 args.append(None)
546 else:
547 args.append(None)
548
549 return nodes.Slice(lineno=lineno, *args)
550
551 def parse_call(self, node):
552 token = self.stream.expect('lparen')
553 args = []
554 kwargs = []
555 dyn_args = dyn_kwargs = None
556 require_comma = False
557
558 def ensure(expr):
559 if not expr:
560 raise TemplateSyntaxError('invalid syntax for function '
561 'call expression', token.lineno,
562 self.filename)
563
564 while self.stream.current.type is not 'rparen':
565 if require_comma:
566 self.stream.expect('comma')
567 # support for trailing comma
568 if self.stream.current.type is 'rparen':
569 break
570 if self.stream.current.type is 'mul':
571 ensure(dyn_args is None and dyn_kwargs is None)
572 self.stream.next()
573 dyn_args = self.parse_expression()
574 elif self.stream.current.type is 'pow':
575 ensure(dyn_kwargs is None)
576 self.stream.next()
577 dyn_kwargs = self.parse_expression()
578 else:
579 ensure(dyn_args is None and dyn_kwargs is None)
580 if self.stream.current.type is 'name' and \
581 self.stream.look().type is 'assign':
582 key = self.stream.current.value
583 self.stream.skip(2)
Armin Ronacher8efc5222008-04-08 14:47:40 +0200584 kwargs.append(nodes.Keyword(key, self.parse_expression(),
585 lineno=key.lineno))
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200586 else:
587 ensure(not kwargs)
588 args.append(self.parse_expression())
589
590 require_comma = True
591 self.stream.expect('rparen')
592
593 if node is None:
594 return args, kwargs, dyn_args, dyn_kwargs
595 return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs,
596 lineno=token.lineno)
597
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200598 def parse_filter(self, node, start_inline=False):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200599 lineno = self.stream.current.type
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200600 while self.stream.current.type == 'pipe' or start_inline:
601 if not start_inline:
602 self.stream.next()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200603 token = self.stream.expect('name')
604 if self.stream.current.type is 'lparen':
605 args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
606 else:
607 args = []
608 kwargs = []
609 dyn_args = dyn_kwargs = None
Armin Ronacherd55ab532008-04-09 16:13:39 +0200610 node = nodes.Filter(node, token.value, args, kwargs, dyn_args,
611 dyn_kwargs, lineno=token.lineno)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200612 start_inline = False
Armin Ronacherd55ab532008-04-09 16:13:39 +0200613 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200614
615 def parse_test(self, node):
616 token = self.stream.expect('is')
617 if self.stream.current.type is 'not':
618 self.stream.next()
619 negated = True
620 else:
621 negated = False
622 name = self.stream.expect('name').value
623 if self.stream.current.type is 'lparen':
624 args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
625 elif self.stream.current.type in ('name', 'string', 'integer',
626 'float', 'lparen', 'lbracket',
627 'lbrace', 'regex'):
628 args = [self.parse_expression()]
629 else:
630 args = []
631 kwargs = []
632 dyn_args = dyn_kwargs = None
633 node = nodes.Test(node, name, args, kwargs, dyn_args,
634 dyn_kwargs, lineno=token.lineno)
635 if negated:
636 node = nodes.NotExpression(node, lineno=token.lineno)
637 return node
638
639 def subparse(self, end_tokens=None):
640 body = []
641 data_buffer = []
642 add_data = data_buffer.append
643
644 def flush_data():
645 if data_buffer:
646 lineno = data_buffer[0].lineno
647 body.append(nodes.Output(data_buffer[:], lineno=lineno))
648 del data_buffer[:]
649
650 while self.stream:
651 token = self.stream.current
652 if token.type is 'data':
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200653 if token.value:
654 add_data(nodes.Const(token.value, lineno=token.lineno))
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200655 self.stream.next()
656 elif token.type is 'variable_begin':
657 self.stream.next()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200658 want_comma = False
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200659 while self.stream.current.type not in _statement_end_tokens:
Armin Ronachere791c2a2008-04-07 18:39:54 +0200660 if want_comma:
661 self.stream.expect('comma')
662 add_data(self.parse_expression())
663 want_comma = True
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200664 self.stream.expect('variable_end')
665 elif token.type is 'block_begin':
666 flush_data()
667 self.stream.next()
668 if end_tokens is not None and \
669 self.stream.current.type in end_tokens:
670 return body
671 while self.stream.current.type is not 'block_end':
672 body.append(self.parse_statement())
673 self.stream.expect('block_end')
674 else:
675 raise AssertionError('internal parsing error')
676
677 flush_data()
678 return body
679
680 def parse(self):
681 """Parse the whole template into a `Template` node."""
Armin Ronacherd55ab532008-04-09 16:13:39 +0200682 result = nodes.Template(self.subparse(), lineno=1)
683 result.set_environment(self.environment)
684 return result