blob: 05d2e3293d31d4d6bb73bb1c8027d40c7c7a62a0 [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',
Armin Ronacher0611e492008-04-25 23:44:14 +020016 'macro', 'include', 'from', 'import'])
Armin Ronacher115de2e2008-05-01 22:20:05 +020017_compare_operators = frozenset(['eq', 'ne', 'lt', 'lteq', 'gt', 'gteq'])
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020018
Armin Ronacher07bc6842008-03-31 14:18:49 +020019
20class Parser(object):
Armin Ronacherd55ab532008-04-09 16:13:39 +020021 """The template parser class.
Armin Ronacher07bc6842008-03-31 14:18:49 +020022
23 Transforms sourcecode into an abstract syntax tree.
24 """
25
26 def __init__(self, environment, source, filename=None):
27 self.environment = environment
Armin Ronacher07bc6842008-03-31 14:18:49 +020028 if isinstance(filename, unicode):
29 filename = filename.encode('utf-8')
Armin Ronacherbf7c4ad2008-04-12 12:02:36 +020030 self.source = unicode(source)
Armin Ronacher07bc6842008-03-31 14:18:49 +020031 self.filename = filename
32 self.closed = False
Armin Ronacher07bc6842008-03-31 14:18:49 +020033 self.stream = environment.lexer.tokenize(source, filename)
Armin Ronacher05530932008-04-20 13:27:49 +020034 self.extensions = {}
35 for extension in environment.extensions:
36 for tag in extension.tags:
37 self.extensions[tag] = extension.parse
Armin Ronacher07bc6842008-03-31 14:18:49 +020038
Armin Ronacher115de2e2008-05-01 22:20:05 +020039 def is_statement_end(self):
40 """Are we at the end of a statement?"""
41 if self.stream.current.type in ('variable_end', 'block_end'):
42 return True
43 return self.stream.current.test('name:in')
44
45 def is_tuple_end(self):
46 """Are we at the end of a tuple?"""
47 return self.stream.current.type is 'rparen' or self.is_statement_end()
48
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020049 def parse_statement(self):
50 """Parse a single statement."""
51 token_type = self.stream.current.type
Armin Ronacher115de2e2008-05-01 22:20:05 +020052 if self.stream.current.type is 'name':
53 if self.stream.current.value in _statement_keywords:
54 return getattr(self, 'parse_' + self.stream.current.value)()
55 elif self.stream.current.value == 'call':
56 return self.parse_call_block()
57 elif self.stream.current.value == 'filter':
58 return self.parse_filter_block()
59 else:
60 ext = self.extensions.get(self.stream.current.value)
61 if ext is not None:
62 return ext(self)
Armin Ronacher157531b2008-04-28 16:14:03 +020063 lineno = self.stream.current.lineno
Armin Ronacher8efc5222008-04-08 14:47:40 +020064 expr = self.parse_tuple()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020065 if self.stream.current.type == 'assign':
Armin Ronachere791c2a2008-04-07 18:39:54 +020066 result = self.parse_assign(expr)
67 else:
68 result = nodes.ExprStmt(expr, lineno=lineno)
Armin Ronachere791c2a2008-04-07 18:39:54 +020069 return result
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020070
71 def parse_assign(self, target):
72 """Parse an assign statement."""
73 lineno = self.stream.expect('assign').lineno
74 if not target.can_assign():
75 raise TemplateSyntaxError("can't assign to '%s'" %
Armin Ronacher4f7d2d52008-04-22 10:40:26 +020076 target.__class__.__name__.lower(),
77 target.lineno, self.filename)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020078 expr = self.parse_tuple()
Armin Ronachere791c2a2008-04-07 18:39:54 +020079 target.set_ctx('store')
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020080 return nodes.Assign(target, expr, lineno=lineno)
81
82 def parse_statements(self, end_tokens, drop_needle=False):
Armin Ronacherf59bac22008-04-20 13:11:43 +020083 """Parse multiple statements into a list until one of the end tokens
84 is reached. This is used to parse the body of statements as it also
85 parses template data if appropriate.
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020086 """
87 # the first token may be a colon for python compatibility
88 if self.stream.current.type is 'colon':
89 self.stream.next()
90
Armin Ronacher2b60fe52008-04-21 08:23:59 +020091 # in the future it would be possible to add whole code sections
92 # by adding some sort of end of statement token and parsing those here.
93 self.stream.expect('block_end')
94 result = self.subparse(end_tokens)
95
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020096 if drop_needle:
97 self.stream.next()
98 return result
99
100 def parse_for(self):
101 """Parse a for loop."""
Armin Ronacher115de2e2008-05-01 22:20:05 +0200102 lineno = self.stream.expect('name:for').lineno
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200103 target = self.parse_tuple(simplified=True)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200104 if not target.can_assign():
105 raise TemplateSyntaxError("can't assign to '%s'" %
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200106 target.__class__.__name__.lower(),
107 target.lineno, self.filename)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200108 target.set_ctx('store')
Armin Ronacher115de2e2008-05-01 22:20:05 +0200109 self.stream.expect('name:in')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200110 iter = self.parse_tuple(no_condexpr=True)
111 test = None
Armin Ronacher115de2e2008-05-01 22:20:05 +0200112 if self.stream.current.type is 'name:if':
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200113 self.stream.next()
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200114 test = self.parse_expression()
Armin Ronacher115de2e2008-05-01 22:20:05 +0200115 body = self.parse_statements(('name:endfor', 'name:else'))
116 if self.stream.next().value == 'endfor':
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200117 else_ = []
118 else:
Armin Ronacher115de2e2008-05-01 22:20:05 +0200119 else_ = self.parse_statements(('name:endfor',), drop_needle=True)
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200120 return nodes.For(target, iter, body, else_, test, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200121
122 def parse_if(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200123 """Parse an if construct."""
Armin Ronacher115de2e2008-05-01 22:20:05 +0200124 node = result = nodes.If(lineno=self.stream.expect('name:if').lineno)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200125 while 1:
Armin Ronacherea847c52008-05-02 20:04:32 +0200126 node.test = self.parse_tuple(no_condexpr=True)
Armin Ronacher115de2e2008-05-01 22:20:05 +0200127 node.body = self.parse_statements(('name:elif', 'name:else',
128 'name:endif'))
129 token = self.stream.next()
130 if token.test('name:elif'):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200131 new_node = nodes.If(lineno=self.stream.current.lineno)
132 node.else_ = [new_node]
133 node = new_node
134 continue
Armin Ronacher115de2e2008-05-01 22:20:05 +0200135 elif token.test('name:else'):
136 node.else_ = self.parse_statements(('name:endif',),
Armin Ronachere791c2a2008-04-07 18:39:54 +0200137 drop_needle=True)
138 else:
139 node.else_ = []
140 break
141 return result
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200142
143 def parse_block(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200144 node = nodes.Block(lineno=self.stream.next().lineno)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200145 node.name = self.stream.expect('name').value
Armin Ronacher115de2e2008-05-01 22:20:05 +0200146 node.body = self.parse_statements(('name:endblock',), drop_needle=True)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200147 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200148
149 def parse_extends(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200150 node = nodes.Extends(lineno=self.stream.next().lineno)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200151 node.template = self.parse_expression()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200152 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200153
Armin Ronacherea847c52008-05-02 20:04:32 +0200154 def parse_import_context(self, node, default):
Armin Ronachercda43df2008-05-03 17:10:05 +0200155 if self.stream.current.test_any('name:with', 'name:without') and \
Armin Ronacherea847c52008-05-02 20:04:32 +0200156 self.stream.look().test('name:context'):
157 node.with_context = self.stream.next().value == 'with'
158 self.stream.skip()
159 else:
160 node.with_context = default
161 return node
162
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200163 def parse_include(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200164 node = nodes.Include(lineno=self.stream.next().lineno)
Armin Ronacher0611e492008-04-25 23:44:14 +0200165 node.template = self.parse_expression()
Armin Ronacherea847c52008-05-02 20:04:32 +0200166 return self.parse_import_context(node, True)
Armin Ronacher0611e492008-04-25 23:44:14 +0200167
168 def parse_import(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200169 node = nodes.Import(lineno=self.stream.next().lineno)
Armin Ronacher0611e492008-04-25 23:44:14 +0200170 node.template = self.parse_expression()
171 self.stream.expect('name:as')
172 node.target = self.stream.expect('name').value
173 if not nodes.Name(node.target, 'store').can_assign():
174 raise TemplateSyntaxError('can\'t assign imported template '
175 'to %r' % node.target, node.lineno,
176 self.filename)
Armin Ronacherea847c52008-05-02 20:04:32 +0200177 return self.parse_import_context(node, False)
Armin Ronacher0611e492008-04-25 23:44:14 +0200178
179 def parse_from(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200180 node = nodes.FromImport(lineno=self.stream.next().lineno)
Armin Ronacher0611e492008-04-25 23:44:14 +0200181 node.template = self.parse_expression()
Armin Ronacher115de2e2008-05-01 22:20:05 +0200182 self.stream.expect('name:import')
Armin Ronacher0611e492008-04-25 23:44:14 +0200183 node.names = []
Armin Ronacherea847c52008-05-02 20:04:32 +0200184
185 def parse_context():
186 if self.stream.current.value in ('with', 'without') and \
187 self.stream.look().test('name:context'):
188 node.with_context = self.stream.next().value == 'with'
189 self.stream.skip()
190 return True
191 return False
192
Armin Ronacher0611e492008-04-25 23:44:14 +0200193 while 1:
194 if node.names:
195 self.stream.expect('comma')
196 if self.stream.current.type is 'name':
Armin Ronacherea847c52008-05-02 20:04:32 +0200197 if parse_context():
198 break
Armin Ronacher0611e492008-04-25 23:44:14 +0200199 target = nodes.Name(self.stream.current.value, 'store')
200 if not target.can_assign():
201 raise TemplateSyntaxError('can\'t import object named %r'
202 % target.name, target.lineno,
203 self.filename)
204 elif target.name.startswith('__'):
205 raise TemplateAssertionError('names starting with two '
206 'underscores can not be '
207 'imported', target.lineno,
208 self.filename)
Armin Ronacher0611e492008-04-25 23:44:14 +0200209 self.stream.next()
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200210 if self.stream.current.test('name:as'):
211 self.stream.next()
212 alias = self.stream.expect('name')
213 if not nodes.Name(alias.value, 'store').can_assign():
214 raise TemplateSyntaxError('can\'t name imported '
215 'object %r.' % alias.value,
216 alias.lineno, self.filename)
217 node.names.append((target.name, alias.value))
218 else:
219 node.names.append(target.name)
Armin Ronacherea847c52008-05-02 20:04:32 +0200220 if parse_context() or self.stream.current.type is not 'comma':
Armin Ronacher0611e492008-04-25 23:44:14 +0200221 break
222 else:
223 break
Armin Ronacherea847c52008-05-02 20:04:32 +0200224 if not hasattr(node, 'with_context'):
225 node.with_context = False
226 if self.stream.current.type is 'comma':
227 self.stream.next()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200228 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200229
Armin Ronacher71082072008-04-12 14:19:36 +0200230 def parse_signature(self, node):
231 node.args = args = []
232 node.defaults = defaults = []
233 self.stream.expect('lparen')
234 while self.stream.current.type is not 'rparen':
235 if args:
236 self.stream.expect('comma')
237 token = self.stream.expect('name')
238 arg = nodes.Name(token.value, 'param', lineno=token.lineno)
239 if not arg.can_assign():
240 raise TemplateSyntaxError("can't assign to '%s'" %
241 arg.name, arg.lineno,
242 self.filename)
243 if self.stream.current.type is 'assign':
244 self.stream.next()
245 defaults.append(self.parse_expression())
246 args.append(arg)
247 self.stream.expect('rparen')
248
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200249 def parse_call_block(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200250 node = nodes.CallBlock(lineno=self.stream.next().lineno)
Armin Ronacher71082072008-04-12 14:19:36 +0200251 if self.stream.current.type is 'lparen':
252 self.parse_signature(node)
Armin Ronacherc9705c22008-04-27 21:28:03 +0200253 else:
254 node.args = []
255 node.defaults = []
Armin Ronacher71082072008-04-12 14:19:36 +0200256
Armin Ronacher8edbe492008-04-10 20:43:43 +0200257 node.call = self.parse_expression()
258 if not isinstance(node.call, nodes.Call):
259 raise TemplateSyntaxError('expected call', node.lineno,
260 self.filename)
Armin Ronacher115de2e2008-05-01 22:20:05 +0200261 node.body = self.parse_statements(('name:endcall',), drop_needle=True)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200262 return node
263
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200264 def parse_filter_block(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200265 node = nodes.FilterBlock(lineno=self.stream.next().lineno)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200266 node.filter = self.parse_filter(None, start_inline=True)
Armin Ronacher115de2e2008-05-01 22:20:05 +0200267 node.body = self.parse_statements(('name:endfilter',),
268 drop_needle=True)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200269 return node
270
Armin Ronachere791c2a2008-04-07 18:39:54 +0200271 def parse_macro(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200272 node = nodes.Macro(lineno=self.stream.next().lineno)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200273 node.name = self.stream.expect('name').value
Armin Ronacherf059ec12008-04-11 22:21:00 +0200274 # make sure that assignments to that name are allowed
275 if not nodes.Name(node.name, 'store').can_assign():
276 raise TemplateSyntaxError('can\'t assign macro to %r' %
277 node.target, node.lineno,
278 self.filename)
Armin Ronacher71082072008-04-12 14:19:36 +0200279 self.parse_signature(node)
Armin Ronacher115de2e2008-05-01 22:20:05 +0200280 node.body = self.parse_statements(('name:endmacro',),
281 drop_needle=True)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200282 return node
283
284 def parse_print(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200285 node = nodes.Output(lineno=self.stream.next().lineno)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200286 node.nodes = []
Armin Ronacher115de2e2008-05-01 22:20:05 +0200287 while not self.is_statement_end():
Armin Ronachere791c2a2008-04-07 18:39:54 +0200288 if node.nodes:
289 self.stream.expect('comma')
290 node.nodes.append(self.parse_expression())
Armin Ronachere791c2a2008-04-07 18:39:54 +0200291 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200292
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200293 def parse_expression(self, no_condexpr=False):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200294 """Parse an expression."""
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200295 if no_condexpr:
296 return self.parse_or()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200297 return self.parse_condexpr()
298
299 def parse_condexpr(self):
300 lineno = self.stream.current.lineno
301 expr1 = self.parse_or()
Armin Ronacher115de2e2008-05-01 22:20:05 +0200302 while self.stream.current.test('name:if'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200303 self.stream.next()
304 expr2 = self.parse_or()
Armin Ronacher115de2e2008-05-01 22:20:05 +0200305 self.stream.expect('name:else')
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200306 expr3 = self.parse_condexpr()
307 expr1 = nodes.CondExpr(expr2, expr1, expr3, lineno=lineno)
308 lineno = self.stream.current.lineno
309 return expr1
310
311 def parse_or(self):
312 lineno = self.stream.current.lineno
313 left = self.parse_and()
Armin Ronacher115de2e2008-05-01 22:20:05 +0200314 while self.stream.current.test('name:or'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200315 self.stream.next()
316 right = self.parse_and()
317 left = nodes.Or(left, right, lineno=lineno)
318 lineno = self.stream.current.lineno
319 return left
320
321 def parse_and(self):
322 lineno = self.stream.current.lineno
323 left = self.parse_compare()
Armin Ronacher115de2e2008-05-01 22:20:05 +0200324 while self.stream.current.test('name:and'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200325 self.stream.next()
326 right = self.parse_compare()
327 left = nodes.And(left, right, lineno=lineno)
328 lineno = self.stream.current.lineno
329 return left
330
331 def parse_compare(self):
332 lineno = self.stream.current.lineno
333 expr = self.parse_add()
334 ops = []
335 while 1:
336 token_type = self.stream.current.type
337 if token_type in _compare_operators:
338 self.stream.next()
339 ops.append(nodes.Operand(token_type, self.parse_add()))
Armin Ronacher115de2e2008-05-01 22:20:05 +0200340 elif self.stream.current.test('name:in'):
341 self.stream.next()
342 ops.append(nodes.Operand('in', self.parse_add()))
343 elif self.stream.current.test('name:not') and \
344 self.stream.look().test('name:in'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200345 self.stream.skip(2)
346 ops.append(nodes.Operand('notin', self.parse_add()))
347 else:
348 break
349 lineno = self.stream.current.lineno
350 if not ops:
351 return expr
352 return nodes.Compare(expr, ops, lineno=lineno)
353
354 def parse_add(self):
355 lineno = self.stream.current.lineno
356 left = self.parse_sub()
357 while self.stream.current.type is 'add':
358 self.stream.next()
359 right = self.parse_sub()
360 left = nodes.Add(left, right, lineno=lineno)
361 lineno = self.stream.current.lineno
362 return left
363
364 def parse_sub(self):
365 lineno = self.stream.current.lineno
366 left = self.parse_concat()
367 while self.stream.current.type is 'sub':
368 self.stream.next()
369 right = self.parse_concat()
370 left = nodes.Sub(left, right, lineno=lineno)
371 lineno = self.stream.current.lineno
372 return left
373
374 def parse_concat(self):
375 lineno = self.stream.current.lineno
376 args = [self.parse_mul()]
377 while self.stream.current.type is 'tilde':
378 self.stream.next()
379 args.append(self.parse_mul())
380 if len(args) == 1:
381 return args[0]
382 return nodes.Concat(args, lineno=lineno)
383
384 def parse_mul(self):
385 lineno = self.stream.current.lineno
386 left = self.parse_div()
387 while self.stream.current.type is 'mul':
388 self.stream.next()
389 right = self.parse_div()
390 left = nodes.Mul(left, right, lineno=lineno)
391 lineno = self.stream.current.lineno
392 return left
393
394 def parse_div(self):
395 lineno = self.stream.current.lineno
396 left = self.parse_floordiv()
397 while self.stream.current.type is 'div':
398 self.stream.next()
399 right = self.parse_floordiv()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200400 left = nodes.Div(left, right, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200401 lineno = self.stream.current.lineno
402 return left
403
404 def parse_floordiv(self):
405 lineno = self.stream.current.lineno
406 left = self.parse_mod()
407 while self.stream.current.type is 'floordiv':
408 self.stream.next()
409 right = self.parse_mod()
410 left = nodes.FloorDiv(left, right, lineno=lineno)
411 lineno = self.stream.current.lineno
412 return left
413
414 def parse_mod(self):
415 lineno = self.stream.current.lineno
416 left = self.parse_pow()
417 while self.stream.current.type is 'mod':
418 self.stream.next()
419 right = self.parse_pow()
420 left = nodes.Mod(left, right, lineno=lineno)
421 lineno = self.stream.current.lineno
422 return left
423
424 def parse_pow(self):
425 lineno = self.stream.current.lineno
426 left = self.parse_unary()
427 while self.stream.current.type is 'pow':
428 self.stream.next()
429 right = self.parse_unary()
430 left = nodes.Pow(left, right, lineno=lineno)
431 lineno = self.stream.current.lineno
432 return left
433
434 def parse_unary(self):
435 token_type = self.stream.current.type
436 lineno = self.stream.current.lineno
Armin Ronacher115de2e2008-05-01 22:20:05 +0200437 if token_type is 'name' and self.stream.current.value == 'not':
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200438 self.stream.next()
439 node = self.parse_unary()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200440 return nodes.Not(node, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200441 if token_type is 'sub':
442 self.stream.next()
443 node = self.parse_unary()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200444 return nodes.Neg(node, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200445 if token_type is 'add':
446 self.stream.next()
447 node = self.parse_unary()
448 return nodes.Pos(node, lineno=lineno)
449 return self.parse_primary()
450
451 def parse_primary(self, parse_postfix=True):
452 token = self.stream.current
453 if token.type is 'name':
454 if token.value in ('true', 'false'):
455 node = nodes.Const(token.value == 'true', lineno=token.lineno)
456 elif token.value == 'none':
457 node = nodes.Const(None, lineno=token.lineno)
458 else:
459 node = nodes.Name(token.value, 'load', lineno=token.lineno)
460 self.stream.next()
461 elif token.type in ('integer', 'float', 'string'):
462 self.stream.next()
463 node = nodes.Const(token.value, lineno=token.lineno)
464 elif token.type is 'lparen':
465 self.stream.next()
466 node = self.parse_tuple()
467 self.stream.expect('rparen')
468 elif token.type is 'lbracket':
469 node = self.parse_list()
470 elif token.type is 'lbrace':
471 node = self.parse_dict()
472 else:
473 raise TemplateSyntaxError("unexpected token '%s'" %
474 (token,), token.lineno,
475 self.filename)
476 if parse_postfix:
477 node = self.parse_postfix(node)
478 return node
479
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200480 def parse_tuple(self, enforce=False, simplified=False, no_condexpr=False):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200481 """Parse multiple expressions into a tuple. This can also return
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200482 just one expression which is not a tuple. If you want to enforce
483 a tuple, pass it enforce=True (currently unused).
484 """
485 lineno = self.stream.current.lineno
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200486 if simplified:
487 parse = self.parse_primary
488 elif no_condexpr:
489 parse = lambda: self.parse_expression(no_condexpr=True)
490 else:
491 parse = self.parse_expression
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200492 args = []
493 is_tuple = False
494 while 1:
495 if args:
496 self.stream.expect('comma')
Armin Ronacher115de2e2008-05-01 22:20:05 +0200497 if self.is_tuple_end():
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200498 break
499 args.append(parse())
Armin Ronacherb5124e62008-04-25 00:36:14 +0200500 if self.stream.current.type is 'comma':
501 is_tuple = True
502 else:
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200503 break
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200504 lineno = self.stream.current.lineno
505 if not is_tuple and args:
506 if enforce:
507 raise TemplateSyntaxError('tuple expected', lineno,
508 self.filename)
509 return args[0]
510 return nodes.Tuple(args, 'load', lineno=lineno)
511
512 def parse_list(self):
513 token = self.stream.expect('lbracket')
514 items = []
515 while self.stream.current.type is not 'rbracket':
516 if items:
517 self.stream.expect('comma')
518 if self.stream.current.type == 'rbracket':
519 break
520 items.append(self.parse_expression())
521 self.stream.expect('rbracket')
522 return nodes.List(items, lineno=token.lineno)
523
524 def parse_dict(self):
525 token = self.stream.expect('lbrace')
526 items = []
527 while self.stream.current.type is not 'rbrace':
528 if items:
529 self.stream.expect('comma')
530 if self.stream.current.type == 'rbrace':
531 break
532 key = self.parse_expression()
533 self.stream.expect('colon')
534 value = self.parse_expression()
535 items.append(nodes.Pair(key, value, lineno=key.lineno))
536 self.stream.expect('rbrace')
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200537 return nodes.Dict(items, lineno=token.lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200538
539 def parse_postfix(self, node):
540 while 1:
541 token_type = self.stream.current.type
542 if token_type is 'dot' or token_type is 'lbracket':
543 node = self.parse_subscript(node)
544 elif token_type is 'lparen':
545 node = self.parse_call(node)
546 elif token_type is 'pipe':
547 node = self.parse_filter(node)
Armin Ronacher115de2e2008-05-01 22:20:05 +0200548 elif token_type is 'name' and self.stream.current.value == 'is':
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200549 node = self.parse_test(node)
550 else:
551 break
552 return node
553
554 def parse_subscript(self, node):
555 token = self.stream.next()
556 if token.type is 'dot':
Armin Ronachere791c2a2008-04-07 18:39:54 +0200557 attr_token = self.stream.current
558 if attr_token.type not in ('name', 'integer'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200559 raise TemplateSyntaxError('expected name or number',
Armin Ronachere791c2a2008-04-07 18:39:54 +0200560 attr_token.lineno, self.filename)
561 arg = nodes.Const(attr_token.value, lineno=attr_token.lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200562 self.stream.next()
563 elif token.type is 'lbracket':
564 args = []
565 while self.stream.current.type is not 'rbracket':
566 if args:
567 self.stream.expect('comma')
568 args.append(self.parse_subscribed())
569 self.stream.expect('rbracket')
570 if len(args) == 1:
571 arg = args[0]
572 else:
Benjamin Wieganda3152742008-04-28 18:07:52 +0200573 arg = nodes.Tuple(args, self.lineno, self.filename)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200574 else:
575 raise TemplateSyntaxError('expected subscript expression',
576 self.lineno, self.filename)
577 return nodes.Subscript(node, arg, 'load', lineno=token.lineno)
578
579 def parse_subscribed(self):
580 lineno = self.stream.current.lineno
581
582 if self.stream.current.type is 'colon':
583 self.stream.next()
584 args = [None]
585 else:
586 node = self.parse_expression()
587 if self.stream.current.type is not 'colon':
588 return node
589 self.stream.next()
590 args = [node]
591
592 if self.stream.current.type is 'colon':
593 args.append(None)
594 elif self.stream.current.type not in ('rbracket', 'comma'):
595 args.append(self.parse_expression())
596 else:
597 args.append(None)
598
599 if self.stream.current.type is 'colon':
600 self.stream.next()
601 if self.stream.current.type not in ('rbracket', 'comma'):
602 args.append(self.parse_expression())
603 else:
604 args.append(None)
605 else:
606 args.append(None)
607
608 return nodes.Slice(lineno=lineno, *args)
609
610 def parse_call(self, node):
611 token = self.stream.expect('lparen')
612 args = []
613 kwargs = []
614 dyn_args = dyn_kwargs = None
615 require_comma = False
616
617 def ensure(expr):
618 if not expr:
619 raise TemplateSyntaxError('invalid syntax for function '
620 'call expression', token.lineno,
621 self.filename)
622
623 while self.stream.current.type is not 'rparen':
624 if require_comma:
625 self.stream.expect('comma')
626 # support for trailing comma
627 if self.stream.current.type is 'rparen':
628 break
629 if self.stream.current.type is 'mul':
630 ensure(dyn_args is None and dyn_kwargs is None)
631 self.stream.next()
632 dyn_args = self.parse_expression()
633 elif self.stream.current.type is 'pow':
634 ensure(dyn_kwargs is None)
635 self.stream.next()
636 dyn_kwargs = self.parse_expression()
637 else:
638 ensure(dyn_args is None and dyn_kwargs is None)
639 if self.stream.current.type is 'name' and \
640 self.stream.look().type is 'assign':
641 key = self.stream.current.value
642 self.stream.skip(2)
Armin Ronacher0611e492008-04-25 23:44:14 +0200643 value = self.parse_expression()
644 kwargs.append(nodes.Keyword(key, value,
645 lineno=value.lineno))
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200646 else:
647 ensure(not kwargs)
648 args.append(self.parse_expression())
649
650 require_comma = True
651 self.stream.expect('rparen')
652
653 if node is None:
654 return args, kwargs, dyn_args, dyn_kwargs
655 return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs,
656 lineno=token.lineno)
657
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200658 def parse_filter(self, node, start_inline=False):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200659 lineno = self.stream.current.type
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200660 while self.stream.current.type == 'pipe' or start_inline:
661 if not start_inline:
662 self.stream.next()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200663 token = self.stream.expect('name')
664 if self.stream.current.type is 'lparen':
665 args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
666 else:
667 args = []
668 kwargs = []
669 dyn_args = dyn_kwargs = None
Armin Ronacherd55ab532008-04-09 16:13:39 +0200670 node = nodes.Filter(node, token.value, args, kwargs, dyn_args,
671 dyn_kwargs, lineno=token.lineno)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200672 start_inline = False
Armin Ronacherd55ab532008-04-09 16:13:39 +0200673 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200674
675 def parse_test(self, node):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200676 token = self.stream.next()
677 if self.stream.current.test('name:not'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200678 self.stream.next()
679 negated = True
680 else:
681 negated = False
682 name = self.stream.expect('name').value
Armin Ronacher10f3ba22008-04-18 11:30:37 +0200683 dyn_args = dyn_kwargs = None
684 kwargs = []
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200685 if self.stream.current.type is 'lparen':
686 args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
687 elif self.stream.current.type in ('name', 'string', 'integer',
688 'float', 'lparen', 'lbracket',
Armin Ronacher115de2e2008-05-01 22:20:05 +0200689 'lbrace'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200690 args = [self.parse_expression()]
691 else:
692 args = []
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200693 node = nodes.Test(node, name, args, kwargs, dyn_args,
694 dyn_kwargs, lineno=token.lineno)
695 if negated:
Armin Ronacher9a822052008-04-17 18:44:07 +0200696 node = nodes.Not(node, lineno=token.lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200697 return node
698
699 def subparse(self, end_tokens=None):
700 body = []
701 data_buffer = []
702 add_data = data_buffer.append
703
704 def flush_data():
705 if data_buffer:
706 lineno = data_buffer[0].lineno
707 body.append(nodes.Output(data_buffer[:], lineno=lineno))
708 del data_buffer[:]
709
710 while self.stream:
711 token = self.stream.current
712 if token.type is 'data':
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200713 if token.value:
714 add_data(nodes.Const(token.value, lineno=token.lineno))
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200715 self.stream.next()
716 elif token.type is 'variable_begin':
717 self.stream.next()
Armin Ronacherb5124e62008-04-25 00:36:14 +0200718 add_data(self.parse_tuple())
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200719 self.stream.expect('variable_end')
720 elif token.type is 'block_begin':
721 flush_data()
722 self.stream.next()
723 if end_tokens is not None and \
Armin Ronachercda43df2008-05-03 17:10:05 +0200724 self.stream.current.test_any(*end_tokens):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200725 return body
Armin Ronacher32a910f2008-04-26 23:21:03 +0200726 body.append(self.parse_statement())
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200727 self.stream.expect('block_end')
728 else:
729 raise AssertionError('internal parsing error')
730
731 flush_data()
732 return body
733
734 def parse(self):
735 """Parse the whole template into a `Template` node."""
Armin Ronacherd55ab532008-04-09 16:13:39 +0200736 result = nodes.Template(self.subparse(), lineno=1)
737 result.set_environment(self.environment)
738 return result