blob: 8a0eb6a53ea0f845b330954a8b0d868accd36576 [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 Ronacherf59bac22008-04-20 13:11:43 +020011from operator import itemgetter
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020012from jinja2 import nodes
13from jinja2.exceptions import TemplateSyntaxError
Armin Ronacher07bc6842008-03-31 14:18:49 +020014
15
Armin Ronacher2feed1d2008-04-26 16:26:52 +020016statement_end_tokens = set(['variable_end', 'block_end', 'in'])
Armin Ronachere791c2a2008-04-07 18:39:54 +020017_statement_keywords = frozenset(['for', 'if', 'block', 'extends', 'print',
Armin Ronacher0611e492008-04-25 23:44:14 +020018 'macro', 'include', 'from', 'import'])
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020019_compare_operators = frozenset(['eq', 'ne', 'lt', 'lteq', 'gt', 'gteq', 'in'])
Armin Ronacherb5124e62008-04-25 00:36:14 +020020_tuple_edge_tokens = set(['rparen']) | statement_end_tokens
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.stream = environment.lexer.tokenize(source, filename)
Armin Ronacher05530932008-04-20 13:27:49 +020037 self.extensions = {}
38 for extension in environment.extensions:
39 for tag in extension.tags:
40 self.extensions[tag] = extension.parse
Armin Ronacher07bc6842008-03-31 14:18:49 +020041
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020042 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':
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020048 return self.parse_call_block()
Armin Ronacherfa865fb2008-04-12 22:11:53 +020049 elif token_type is 'filter':
50 return self.parse_filter_block()
Armin Ronacherf59bac22008-04-20 13:11:43 +020051 elif token_type is 'name':
52 ext = self.extensions.get(self.stream.current.value)
53 if ext is not None:
54 return ext(self)
Armin Ronachere791c2a2008-04-07 18:39:54 +020055 lineno = self.stream.current
Armin Ronacher8efc5222008-04-08 14:47:40 +020056 expr = self.parse_tuple()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020057 if self.stream.current.type == 'assign':
Armin Ronachere791c2a2008-04-07 18:39:54 +020058 result = self.parse_assign(expr)
59 else:
60 result = nodes.ExprStmt(expr, lineno=lineno)
Armin Ronachere791c2a2008-04-07 18:39:54 +020061 return result
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020062
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'" %
Armin Ronacher4f7d2d52008-04-22 10:40:26 +020068 target.__class__.__name__.lower(),
69 target.lineno, self.filename)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020070 expr = self.parse_tuple()
Armin Ronachere791c2a2008-04-07 18:39:54 +020071 target.set_ctx('store')
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020072 return nodes.Assign(target, expr, lineno=lineno)
73
74 def parse_statements(self, end_tokens, drop_needle=False):
Armin Ronacherf59bac22008-04-20 13:11:43 +020075 """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.
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020078 """
79 # the first token may be a colon for python compatibility
80 if self.stream.current.type is 'colon':
81 self.stream.next()
82
Armin Ronacher2b60fe52008-04-21 08:23:59 +020083 # 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
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020088 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)
Armin Ronachere791c2a2008-04-07 18:39:54 +020096 if not target.can_assign():
97 raise TemplateSyntaxError("can't assign to '%s'" %
Armin Ronacher4f7d2d52008-04-22 10:40:26 +020098 target.__class__.__name__.lower(),
99 target.lineno, self.filename)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200100 target.set_ctx('store')
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200101 self.stream.expect('in')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200102 iter = self.parse_tuple(no_condexpr=True)
103 test = None
104 if self.stream.current.type is 'if':
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200105 self.stream.next()
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200106 test = self.parse_expression()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200107 body = self.parse_statements(('endfor', 'else'))
Armin Ronachere791c2a2008-04-07 18:39:54 +0200108 if self.stream.next().type is 'endfor':
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200109 else_ = []
110 else:
111 else_ = self.parse_statements(('endfor',), drop_needle=True)
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200112 return nodes.For(target, iter, body, else_, test, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200113
114 def parse_if(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200115 """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
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200134
135 def parse_block(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200136 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
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200140
141 def parse_extends(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200142 node = nodes.Extends(lineno=self.stream.expect('extends').lineno)
143 node.template = self.parse_expression()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200144 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200145
146 def parse_include(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200147 node = nodes.Include(lineno=self.stream.expect('include').lineno)
Armin Ronacher0611e492008-04-25 23:44:14 +0200148 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)
Armin Ronacher0611e492008-04-25 23:44:14 +0200181 self.stream.next()
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200182 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)
Armin Ronacher0611e492008-04-25 23:44:14 +0200192 if self.stream.current.type is not 'comma':
193 break
194 else:
195 break
196 if self.stream.current.type is 'comma':
Armin Ronachere791c2a2008-04-07 18:39:54 +0200197 self.stream.next()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200198 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200199
Armin Ronacher71082072008-04-12 14:19:36 +0200200 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
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200219 def parse_call_block(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200220 node = nodes.CallBlock(lineno=self.stream.expect('call').lineno)
Armin Ronacher71082072008-04-12 14:19:36 +0200221 if self.stream.current.type is 'lparen':
222 self.parse_signature(node)
Armin Ronacherc9705c22008-04-27 21:28:03 +0200223 else:
224 node.args = []
225 node.defaults = []
Armin Ronacher71082072008-04-12 14:19:36 +0200226
Armin Ronacher8edbe492008-04-10 20:43:43 +0200227 node.call = self.parse_expression()
228 if not isinstance(node.call, nodes.Call):
229 raise TemplateSyntaxError('expected call', node.lineno,
230 self.filename)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200231 node.body = self.parse_statements(('endcall',), drop_needle=True)
232 return node
233
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200234 def parse_filter_block(self):
235 node = nodes.FilterBlock(lineno=self.stream.expect('filter').lineno)
236 node.filter = self.parse_filter(None, start_inline=True)
237 node.body = self.parse_statements(('endfilter',), drop_needle=True)
238 return node
239
Armin Ronachere791c2a2008-04-07 18:39:54 +0200240 def parse_macro(self):
241 node = nodes.Macro(lineno=self.stream.expect('macro').lineno)
242 node.name = self.stream.expect('name').value
Armin Ronacherf059ec12008-04-11 22:21:00 +0200243 # make sure that assignments to that name are allowed
244 if not nodes.Name(node.name, 'store').can_assign():
245 raise TemplateSyntaxError('can\'t assign macro to %r' %
246 node.target, node.lineno,
247 self.filename)
Armin Ronacher71082072008-04-12 14:19:36 +0200248 self.parse_signature(node)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200249 node.body = self.parse_statements(('endmacro',), drop_needle=True)
250 return node
251
252 def parse_print(self):
253 node = nodes.Output(lineno=self.stream.expect('print').lineno)
254 node.nodes = []
Armin Ronacherf59bac22008-04-20 13:11:43 +0200255 while self.stream.current.type not in statement_end_tokens:
Armin Ronachere791c2a2008-04-07 18:39:54 +0200256 if node.nodes:
257 self.stream.expect('comma')
258 node.nodes.append(self.parse_expression())
Armin Ronachere791c2a2008-04-07 18:39:54 +0200259 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200260
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200261 def parse_expression(self, no_condexpr=False):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200262 """Parse an expression."""
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200263 if no_condexpr:
264 return self.parse_or()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200265 return self.parse_condexpr()
266
267 def parse_condexpr(self):
268 lineno = self.stream.current.lineno
269 expr1 = self.parse_or()
270 while self.stream.current.type is 'if':
271 self.stream.next()
272 expr2 = self.parse_or()
273 self.stream.expect('else')
274 expr3 = self.parse_condexpr()
275 expr1 = nodes.CondExpr(expr2, expr1, expr3, lineno=lineno)
276 lineno = self.stream.current.lineno
277 return expr1
278
279 def parse_or(self):
280 lineno = self.stream.current.lineno
281 left = self.parse_and()
282 while self.stream.current.type is 'or':
283 self.stream.next()
284 right = self.parse_and()
285 left = nodes.Or(left, right, lineno=lineno)
286 lineno = self.stream.current.lineno
287 return left
288
289 def parse_and(self):
290 lineno = self.stream.current.lineno
291 left = self.parse_compare()
292 while self.stream.current.type is 'and':
293 self.stream.next()
294 right = self.parse_compare()
295 left = nodes.And(left, right, lineno=lineno)
296 lineno = self.stream.current.lineno
297 return left
298
299 def parse_compare(self):
300 lineno = self.stream.current.lineno
301 expr = self.parse_add()
302 ops = []
303 while 1:
304 token_type = self.stream.current.type
305 if token_type in _compare_operators:
306 self.stream.next()
307 ops.append(nodes.Operand(token_type, self.parse_add()))
308 elif token_type is 'not' and self.stream.look().type is 'in':
309 self.stream.skip(2)
310 ops.append(nodes.Operand('notin', self.parse_add()))
311 else:
312 break
313 lineno = self.stream.current.lineno
314 if not ops:
315 return expr
316 return nodes.Compare(expr, ops, lineno=lineno)
317
318 def parse_add(self):
319 lineno = self.stream.current.lineno
320 left = self.parse_sub()
321 while self.stream.current.type is 'add':
322 self.stream.next()
323 right = self.parse_sub()
324 left = nodes.Add(left, right, lineno=lineno)
325 lineno = self.stream.current.lineno
326 return left
327
328 def parse_sub(self):
329 lineno = self.stream.current.lineno
330 left = self.parse_concat()
331 while self.stream.current.type is 'sub':
332 self.stream.next()
333 right = self.parse_concat()
334 left = nodes.Sub(left, right, lineno=lineno)
335 lineno = self.stream.current.lineno
336 return left
337
338 def parse_concat(self):
339 lineno = self.stream.current.lineno
340 args = [self.parse_mul()]
341 while self.stream.current.type is 'tilde':
342 self.stream.next()
343 args.append(self.parse_mul())
344 if len(args) == 1:
345 return args[0]
346 return nodes.Concat(args, lineno=lineno)
347
348 def parse_mul(self):
349 lineno = self.stream.current.lineno
350 left = self.parse_div()
351 while self.stream.current.type is 'mul':
352 self.stream.next()
353 right = self.parse_div()
354 left = nodes.Mul(left, right, lineno=lineno)
355 lineno = self.stream.current.lineno
356 return left
357
358 def parse_div(self):
359 lineno = self.stream.current.lineno
360 left = self.parse_floordiv()
361 while self.stream.current.type is 'div':
362 self.stream.next()
363 right = self.parse_floordiv()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200364 left = nodes.Div(left, right, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200365 lineno = self.stream.current.lineno
366 return left
367
368 def parse_floordiv(self):
369 lineno = self.stream.current.lineno
370 left = self.parse_mod()
371 while self.stream.current.type is 'floordiv':
372 self.stream.next()
373 right = self.parse_mod()
374 left = nodes.FloorDiv(left, right, lineno=lineno)
375 lineno = self.stream.current.lineno
376 return left
377
378 def parse_mod(self):
379 lineno = self.stream.current.lineno
380 left = self.parse_pow()
381 while self.stream.current.type is 'mod':
382 self.stream.next()
383 right = self.parse_pow()
384 left = nodes.Mod(left, right, lineno=lineno)
385 lineno = self.stream.current.lineno
386 return left
387
388 def parse_pow(self):
389 lineno = self.stream.current.lineno
390 left = self.parse_unary()
391 while self.stream.current.type is 'pow':
392 self.stream.next()
393 right = self.parse_unary()
394 left = nodes.Pow(left, right, lineno=lineno)
395 lineno = self.stream.current.lineno
396 return left
397
398 def parse_unary(self):
399 token_type = self.stream.current.type
400 lineno = self.stream.current.lineno
401 if token_type is 'not':
402 self.stream.next()
403 node = self.parse_unary()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200404 return nodes.Not(node, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200405 if token_type is 'sub':
406 self.stream.next()
407 node = self.parse_unary()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200408 return nodes.Neg(node, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200409 if token_type is 'add':
410 self.stream.next()
411 node = self.parse_unary()
412 return nodes.Pos(node, lineno=lineno)
413 return self.parse_primary()
414
415 def parse_primary(self, parse_postfix=True):
416 token = self.stream.current
417 if token.type is 'name':
418 if token.value in ('true', 'false'):
419 node = nodes.Const(token.value == 'true', lineno=token.lineno)
420 elif token.value == 'none':
421 node = nodes.Const(None, lineno=token.lineno)
422 else:
423 node = nodes.Name(token.value, 'load', lineno=token.lineno)
424 self.stream.next()
425 elif token.type in ('integer', 'float', 'string'):
426 self.stream.next()
427 node = nodes.Const(token.value, lineno=token.lineno)
428 elif token.type is 'lparen':
429 self.stream.next()
430 node = self.parse_tuple()
431 self.stream.expect('rparen')
432 elif token.type is 'lbracket':
433 node = self.parse_list()
434 elif token.type is 'lbrace':
435 node = self.parse_dict()
436 else:
437 raise TemplateSyntaxError("unexpected token '%s'" %
438 (token,), token.lineno,
439 self.filename)
440 if parse_postfix:
441 node = self.parse_postfix(node)
442 return node
443
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200444 def parse_tuple(self, enforce=False, simplified=False, no_condexpr=False):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200445 """
446 Parse multiple expressions into a tuple. This can also return
447 just one expression which is not a tuple. If you want to enforce
448 a tuple, pass it enforce=True (currently unused).
449 """
450 lineno = self.stream.current.lineno
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200451 if simplified:
452 parse = self.parse_primary
453 elif no_condexpr:
454 parse = lambda: self.parse_expression(no_condexpr=True)
455 else:
456 parse = self.parse_expression
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200457 args = []
458 is_tuple = False
459 while 1:
460 if args:
461 self.stream.expect('comma')
Armin Ronacherb5124e62008-04-25 00:36:14 +0200462 if self.stream.current.type in _tuple_edge_tokens:
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200463 break
464 args.append(parse())
Armin Ronacherb5124e62008-04-25 00:36:14 +0200465 if self.stream.current.type is 'comma':
466 is_tuple = True
467 else:
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200468 break
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200469 lineno = self.stream.current.lineno
470 if not is_tuple and args:
471 if enforce:
472 raise TemplateSyntaxError('tuple expected', lineno,
473 self.filename)
474 return args[0]
475 return nodes.Tuple(args, 'load', lineno=lineno)
476
477 def parse_list(self):
478 token = self.stream.expect('lbracket')
479 items = []
480 while self.stream.current.type is not 'rbracket':
481 if items:
482 self.stream.expect('comma')
483 if self.stream.current.type == 'rbracket':
484 break
485 items.append(self.parse_expression())
486 self.stream.expect('rbracket')
487 return nodes.List(items, lineno=token.lineno)
488
489 def parse_dict(self):
490 token = self.stream.expect('lbrace')
491 items = []
492 while self.stream.current.type is not 'rbrace':
493 if items:
494 self.stream.expect('comma')
495 if self.stream.current.type == 'rbrace':
496 break
497 key = self.parse_expression()
498 self.stream.expect('colon')
499 value = self.parse_expression()
500 items.append(nodes.Pair(key, value, lineno=key.lineno))
501 self.stream.expect('rbrace')
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200502 return nodes.Dict(items, lineno=token.lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200503
504 def parse_postfix(self, node):
505 while 1:
506 token_type = self.stream.current.type
507 if token_type is 'dot' or token_type is 'lbracket':
508 node = self.parse_subscript(node)
509 elif token_type is 'lparen':
510 node = self.parse_call(node)
511 elif token_type is 'pipe':
512 node = self.parse_filter(node)
513 elif token_type is 'is':
514 node = self.parse_test(node)
515 else:
516 break
517 return node
518
519 def parse_subscript(self, node):
520 token = self.stream.next()
521 if token.type is 'dot':
Armin Ronachere791c2a2008-04-07 18:39:54 +0200522 attr_token = self.stream.current
523 if attr_token.type not in ('name', 'integer'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200524 raise TemplateSyntaxError('expected name or number',
Armin Ronachere791c2a2008-04-07 18:39:54 +0200525 attr_token.lineno, self.filename)
526 arg = nodes.Const(attr_token.value, lineno=attr_token.lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200527 self.stream.next()
528 elif token.type is 'lbracket':
529 args = []
530 while self.stream.current.type is not 'rbracket':
531 if args:
532 self.stream.expect('comma')
533 args.append(self.parse_subscribed())
534 self.stream.expect('rbracket')
535 if len(args) == 1:
536 arg = args[0]
537 else:
538 arg = nodes.Tuple(args, lineno, self.filename)
539 else:
540 raise TemplateSyntaxError('expected subscript expression',
541 self.lineno, self.filename)
542 return nodes.Subscript(node, arg, 'load', lineno=token.lineno)
543
544 def parse_subscribed(self):
545 lineno = self.stream.current.lineno
546
547 if self.stream.current.type is 'colon':
548 self.stream.next()
549 args = [None]
550 else:
551 node = self.parse_expression()
552 if self.stream.current.type is not 'colon':
553 return node
554 self.stream.next()
555 args = [node]
556
557 if self.stream.current.type is 'colon':
558 args.append(None)
559 elif self.stream.current.type not in ('rbracket', 'comma'):
560 args.append(self.parse_expression())
561 else:
562 args.append(None)
563
564 if self.stream.current.type is 'colon':
565 self.stream.next()
566 if self.stream.current.type not in ('rbracket', 'comma'):
567 args.append(self.parse_expression())
568 else:
569 args.append(None)
570 else:
571 args.append(None)
572
573 return nodes.Slice(lineno=lineno, *args)
574
575 def parse_call(self, node):
576 token = self.stream.expect('lparen')
577 args = []
578 kwargs = []
579 dyn_args = dyn_kwargs = None
580 require_comma = False
581
582 def ensure(expr):
583 if not expr:
584 raise TemplateSyntaxError('invalid syntax for function '
585 'call expression', token.lineno,
586 self.filename)
587
588 while self.stream.current.type is not 'rparen':
589 if require_comma:
590 self.stream.expect('comma')
591 # support for trailing comma
592 if self.stream.current.type is 'rparen':
593 break
594 if self.stream.current.type is 'mul':
595 ensure(dyn_args is None and dyn_kwargs is None)
596 self.stream.next()
597 dyn_args = self.parse_expression()
598 elif self.stream.current.type is 'pow':
599 ensure(dyn_kwargs is None)
600 self.stream.next()
601 dyn_kwargs = self.parse_expression()
602 else:
603 ensure(dyn_args is None and dyn_kwargs is None)
604 if self.stream.current.type is 'name' and \
605 self.stream.look().type is 'assign':
606 key = self.stream.current.value
607 self.stream.skip(2)
Armin Ronacher0611e492008-04-25 23:44:14 +0200608 value = self.parse_expression()
609 kwargs.append(nodes.Keyword(key, value,
610 lineno=value.lineno))
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200611 else:
612 ensure(not kwargs)
613 args.append(self.parse_expression())
614
615 require_comma = True
616 self.stream.expect('rparen')
617
618 if node is None:
619 return args, kwargs, dyn_args, dyn_kwargs
620 return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs,
621 lineno=token.lineno)
622
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200623 def parse_filter(self, node, start_inline=False):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200624 lineno = self.stream.current.type
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200625 while self.stream.current.type == 'pipe' or start_inline:
626 if not start_inline:
627 self.stream.next()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200628 token = self.stream.expect('name')
629 if self.stream.current.type is 'lparen':
630 args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
631 else:
632 args = []
633 kwargs = []
634 dyn_args = dyn_kwargs = None
Armin Ronacherd55ab532008-04-09 16:13:39 +0200635 node = nodes.Filter(node, token.value, args, kwargs, dyn_args,
636 dyn_kwargs, lineno=token.lineno)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200637 start_inline = False
Armin Ronacherd55ab532008-04-09 16:13:39 +0200638 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200639
640 def parse_test(self, node):
641 token = self.stream.expect('is')
642 if self.stream.current.type is 'not':
643 self.stream.next()
644 negated = True
645 else:
646 negated = False
647 name = self.stream.expect('name').value
Armin Ronacher10f3ba22008-04-18 11:30:37 +0200648 dyn_args = dyn_kwargs = None
649 kwargs = []
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200650 if self.stream.current.type is 'lparen':
651 args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
652 elif self.stream.current.type in ('name', 'string', 'integer',
653 'float', 'lparen', 'lbracket',
654 'lbrace', 'regex'):
655 args = [self.parse_expression()]
656 else:
657 args = []
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200658 node = nodes.Test(node, name, args, kwargs, dyn_args,
659 dyn_kwargs, lineno=token.lineno)
660 if negated:
Armin Ronacher9a822052008-04-17 18:44:07 +0200661 node = nodes.Not(node, lineno=token.lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200662 return node
663
664 def subparse(self, end_tokens=None):
665 body = []
666 data_buffer = []
667 add_data = data_buffer.append
668
669 def flush_data():
670 if data_buffer:
671 lineno = data_buffer[0].lineno
672 body.append(nodes.Output(data_buffer[:], lineno=lineno))
673 del data_buffer[:]
674
675 while self.stream:
676 token = self.stream.current
677 if token.type is 'data':
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200678 if token.value:
679 add_data(nodes.Const(token.value, lineno=token.lineno))
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200680 self.stream.next()
681 elif token.type is 'variable_begin':
682 self.stream.next()
Armin Ronacherb5124e62008-04-25 00:36:14 +0200683 add_data(self.parse_tuple())
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200684 self.stream.expect('variable_end')
685 elif token.type is 'block_begin':
686 flush_data()
687 self.stream.next()
688 if end_tokens is not None and \
Armin Ronacherf59bac22008-04-20 13:11:43 +0200689 self.stream.current.test_many(end_tokens):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200690 return body
Armin Ronacher32a910f2008-04-26 23:21:03 +0200691 body.append(self.parse_statement())
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200692 self.stream.expect('block_end')
693 else:
694 raise AssertionError('internal parsing error')
695
696 flush_data()
697 return body
698
699 def parse(self):
700 """Parse the whole template into a `Template` node."""
Armin Ronacherd55ab532008-04-09 16:13:39 +0200701 result = nodes.Template(self.subparse(), lineno=1)
702 result.set_environment(self.environment)
703 return result