blob: 91a848b6d9338087808bd4dfb18df6bcecb1a6d4 [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 Ronacher023b5e92008-05-08 11:03:10 +020021 """This is the central parsing class Jinja2 uses. It's passed to
22 extensions and can be used to parse expressions or statements.
Armin Ronacher07bc6842008-03-31 14:18:49 +020023 """
24
25 def __init__(self, environment, source, filename=None):
26 self.environment = environment
Armin Ronacher07bc6842008-03-31 14:18:49 +020027 if isinstance(filename, unicode):
28 filename = filename.encode('utf-8')
Armin Ronacherbf7c4ad2008-04-12 12:02:36 +020029 self.source = unicode(source)
Armin Ronacher07bc6842008-03-31 14:18:49 +020030 self.filename = filename
31 self.closed = False
Armin Ronacher023b5e92008-05-08 11:03:10 +020032 self.stream = environment.lexer.tokenize(self.source, filename)
Armin Ronacher05530932008-04-20 13:27:49 +020033 self.extensions = {}
Armin Ronacher023b5e92008-05-08 11:03:10 +020034 for extension in environment.extensions.itervalues():
Armin Ronacher05530932008-04-20 13:27:49 +020035 for tag in extension.tags:
36 self.extensions[tag] = extension.parse
Armin Ronacher023b5e92008-05-08 11:03:10 +020037 self._last_identifier = 0
Armin Ronacher115de2e2008-05-01 22:20:05 +020038
39 def is_tuple_end(self):
40 """Are we at the end of a tuple?"""
Armin Ronacher023b5e92008-05-08 11:03:10 +020041 return self.stream.current.type in ('variable_end', 'block_end',
42 'rparen') or \
43 self.stream.current.test('name:in')
44
45 def ignore_colon(self):
46 """If there is a colon, skip it and return `True`, else `False`."""
47 if self.stream.current.type is 'colon':
48 self.stream.next()
49 return True
50 return False
51
52 def free_identifier(self, lineno=None):
53 """Return a new free identifier as :class:`~jinja2.nodes.InternalName`."""
54 self._last_identifier += 1
55 rv = object.__new__(nodes.InternalName)
56 nodes.Node.__init__(rv, 'fi%d' % self._last_identifier, lineno=lineno)
57 return rv
Armin Ronacher115de2e2008-05-01 22:20:05 +020058
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020059 def parse_statement(self):
60 """Parse a single statement."""
61 token_type = self.stream.current.type
Armin Ronacher115de2e2008-05-01 22:20:05 +020062 if self.stream.current.type is 'name':
63 if self.stream.current.value in _statement_keywords:
64 return getattr(self, 'parse_' + self.stream.current.value)()
65 elif self.stream.current.value == 'call':
66 return self.parse_call_block()
67 elif self.stream.current.value == 'filter':
68 return self.parse_filter_block()
69 else:
70 ext = self.extensions.get(self.stream.current.value)
71 if ext is not None:
72 return ext(self)
Armin Ronacher157531b2008-04-28 16:14:03 +020073 lineno = self.stream.current.lineno
Armin Ronacher8efc5222008-04-08 14:47:40 +020074 expr = self.parse_tuple()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020075 if self.stream.current.type == 'assign':
Armin Ronachere791c2a2008-04-07 18:39:54 +020076 result = self.parse_assign(expr)
77 else:
78 result = nodes.ExprStmt(expr, lineno=lineno)
Armin Ronachere791c2a2008-04-07 18:39:54 +020079 return result
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020080
81 def parse_assign(self, target):
82 """Parse an assign statement."""
83 lineno = self.stream.expect('assign').lineno
84 if not target.can_assign():
85 raise TemplateSyntaxError("can't assign to '%s'" %
Armin Ronacher4f7d2d52008-04-22 10:40:26 +020086 target.__class__.__name__.lower(),
87 target.lineno, self.filename)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020088 expr = self.parse_tuple()
Armin Ronachere791c2a2008-04-07 18:39:54 +020089 target.set_ctx('store')
Armin Ronacher82b3f3d2008-03-31 20:01:08 +020090 return nodes.Assign(target, expr, lineno=lineno)
91
92 def parse_statements(self, end_tokens, drop_needle=False):
Armin Ronacherf59bac22008-04-20 13:11:43 +020093 """Parse multiple statements into a list until one of the end tokens
94 is reached. This is used to parse the body of statements as it also
Armin Ronacher023b5e92008-05-08 11:03:10 +020095 parses template data if appropriate. The parser checks first if the
96 current token is a colon and skips it if there is one. Then it checks
97 for the block end and parses until if one of the `end_tokens` is
98 reached. Per default the active token in the stream at the end of
99 the call is the matched end token. If this is not wanted `drop_needle`
100 can be set to `True` and the end token is removed.
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200101 """
102 # the first token may be a colon for python compatibility
Armin Ronacher023b5e92008-05-08 11:03:10 +0200103 self.ignore_colon()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200104
Armin Ronacher2b60fe52008-04-21 08:23:59 +0200105 # in the future it would be possible to add whole code sections
106 # by adding some sort of end of statement token and parsing those here.
107 self.stream.expect('block_end')
108 result = self.subparse(end_tokens)
109
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200110 if drop_needle:
111 self.stream.next()
112 return result
113
114 def parse_for(self):
115 """Parse a for loop."""
Armin Ronacher115de2e2008-05-01 22:20:05 +0200116 lineno = self.stream.expect('name:for').lineno
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200117 target = self.parse_tuple(simplified=True)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200118 if not target.can_assign():
119 raise TemplateSyntaxError("can't assign to '%s'" %
Armin Ronacher4f7d2d52008-04-22 10:40:26 +0200120 target.__class__.__name__.lower(),
121 target.lineno, self.filename)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200122 target.set_ctx('store')
Armin Ronacher115de2e2008-05-01 22:20:05 +0200123 self.stream.expect('name:in')
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200124 iter = self.parse_tuple(no_condexpr=True)
125 test = None
Armin Ronacherc0725642008-05-04 22:43:19 +0200126 if self.stream.current.test('name:if'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200127 self.stream.next()
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200128 test = self.parse_expression()
Armin Ronacher115de2e2008-05-01 22:20:05 +0200129 body = self.parse_statements(('name:endfor', 'name:else'))
130 if self.stream.next().value == 'endfor':
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200131 else_ = []
132 else:
Armin Ronacher115de2e2008-05-01 22:20:05 +0200133 else_ = self.parse_statements(('name:endfor',), drop_needle=True)
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200134 return nodes.For(target, iter, body, else_, test, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200135
136 def parse_if(self):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200137 """Parse an if construct."""
Armin Ronacher115de2e2008-05-01 22:20:05 +0200138 node = result = nodes.If(lineno=self.stream.expect('name:if').lineno)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200139 while 1:
Armin Ronacherea847c52008-05-02 20:04:32 +0200140 node.test = self.parse_tuple(no_condexpr=True)
Armin Ronacher115de2e2008-05-01 22:20:05 +0200141 node.body = self.parse_statements(('name:elif', 'name:else',
142 'name:endif'))
143 token = self.stream.next()
144 if token.test('name:elif'):
Armin Ronachere791c2a2008-04-07 18:39:54 +0200145 new_node = nodes.If(lineno=self.stream.current.lineno)
146 node.else_ = [new_node]
147 node = new_node
148 continue
Armin Ronacher115de2e2008-05-01 22:20:05 +0200149 elif token.test('name:else'):
150 node.else_ = self.parse_statements(('name:endif',),
Armin Ronachere791c2a2008-04-07 18:39:54 +0200151 drop_needle=True)
152 else:
153 node.else_ = []
154 break
155 return result
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200156
157 def parse_block(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200158 node = nodes.Block(lineno=self.stream.next().lineno)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200159 node.name = self.stream.expect('name').value
Armin Ronacher115de2e2008-05-01 22:20:05 +0200160 node.body = self.parse_statements(('name:endblock',), drop_needle=True)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200161 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200162
163 def parse_extends(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200164 node = nodes.Extends(lineno=self.stream.next().lineno)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200165 node.template = self.parse_expression()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200166 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200167
Armin Ronacherea847c52008-05-02 20:04:32 +0200168 def parse_import_context(self, node, default):
Armin Ronachercda43df2008-05-03 17:10:05 +0200169 if self.stream.current.test_any('name:with', 'name:without') and \
Armin Ronacherea847c52008-05-02 20:04:32 +0200170 self.stream.look().test('name:context'):
171 node.with_context = self.stream.next().value == 'with'
172 self.stream.skip()
173 else:
174 node.with_context = default
175 return node
176
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200177 def parse_include(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200178 node = nodes.Include(lineno=self.stream.next().lineno)
Armin Ronacher0611e492008-04-25 23:44:14 +0200179 node.template = self.parse_expression()
Armin Ronacherea847c52008-05-02 20:04:32 +0200180 return self.parse_import_context(node, True)
Armin Ronacher0611e492008-04-25 23:44:14 +0200181
182 def parse_import(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200183 node = nodes.Import(lineno=self.stream.next().lineno)
Armin Ronacher0611e492008-04-25 23:44:14 +0200184 node.template = self.parse_expression()
185 self.stream.expect('name:as')
186 node.target = self.stream.expect('name').value
187 if not nodes.Name(node.target, 'store').can_assign():
188 raise TemplateSyntaxError('can\'t assign imported template '
189 'to %r' % node.target, node.lineno,
190 self.filename)
Armin Ronacherea847c52008-05-02 20:04:32 +0200191 return self.parse_import_context(node, False)
Armin Ronacher0611e492008-04-25 23:44:14 +0200192
193 def parse_from(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200194 node = nodes.FromImport(lineno=self.stream.next().lineno)
Armin Ronacher0611e492008-04-25 23:44:14 +0200195 node.template = self.parse_expression()
Armin Ronacher115de2e2008-05-01 22:20:05 +0200196 self.stream.expect('name:import')
Armin Ronacher0611e492008-04-25 23:44:14 +0200197 node.names = []
Armin Ronacherea847c52008-05-02 20:04:32 +0200198
199 def parse_context():
200 if self.stream.current.value in ('with', 'without') and \
201 self.stream.look().test('name:context'):
202 node.with_context = self.stream.next().value == 'with'
203 self.stream.skip()
204 return True
205 return False
206
Armin Ronacher0611e492008-04-25 23:44:14 +0200207 while 1:
208 if node.names:
209 self.stream.expect('comma')
210 if self.stream.current.type is 'name':
Armin Ronacherea847c52008-05-02 20:04:32 +0200211 if parse_context():
212 break
Armin Ronacher0611e492008-04-25 23:44:14 +0200213 target = nodes.Name(self.stream.current.value, 'store')
214 if not target.can_assign():
215 raise TemplateSyntaxError('can\'t import object named %r'
216 % target.name, target.lineno,
217 self.filename)
218 elif target.name.startswith('__'):
219 raise TemplateAssertionError('names starting with two '
220 'underscores can not be '
221 'imported', target.lineno,
222 self.filename)
Armin Ronacher0611e492008-04-25 23:44:14 +0200223 self.stream.next()
Armin Ronacher2feed1d2008-04-26 16:26:52 +0200224 if self.stream.current.test('name:as'):
225 self.stream.next()
226 alias = self.stream.expect('name')
227 if not nodes.Name(alias.value, 'store').can_assign():
228 raise TemplateSyntaxError('can\'t name imported '
229 'object %r.' % alias.value,
230 alias.lineno, self.filename)
231 node.names.append((target.name, alias.value))
232 else:
233 node.names.append(target.name)
Armin Ronacherea847c52008-05-02 20:04:32 +0200234 if parse_context() or self.stream.current.type is not 'comma':
Armin Ronacher0611e492008-04-25 23:44:14 +0200235 break
236 else:
237 break
Armin Ronacherea847c52008-05-02 20:04:32 +0200238 if not hasattr(node, 'with_context'):
239 node.with_context = False
240 if self.stream.current.type is 'comma':
241 self.stream.next()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200242 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200243
Armin Ronacher71082072008-04-12 14:19:36 +0200244 def parse_signature(self, node):
245 node.args = args = []
246 node.defaults = defaults = []
247 self.stream.expect('lparen')
248 while self.stream.current.type is not 'rparen':
249 if args:
250 self.stream.expect('comma')
251 token = self.stream.expect('name')
252 arg = nodes.Name(token.value, 'param', lineno=token.lineno)
253 if not arg.can_assign():
254 raise TemplateSyntaxError("can't assign to '%s'" %
255 arg.name, arg.lineno,
256 self.filename)
257 if self.stream.current.type is 'assign':
258 self.stream.next()
259 defaults.append(self.parse_expression())
260 args.append(arg)
261 self.stream.expect('rparen')
262
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200263 def parse_call_block(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200264 node = nodes.CallBlock(lineno=self.stream.next().lineno)
Armin Ronacher71082072008-04-12 14:19:36 +0200265 if self.stream.current.type is 'lparen':
266 self.parse_signature(node)
Armin Ronacherc9705c22008-04-27 21:28:03 +0200267 else:
268 node.args = []
269 node.defaults = []
Armin Ronacher71082072008-04-12 14:19:36 +0200270
Armin Ronacher8edbe492008-04-10 20:43:43 +0200271 node.call = self.parse_expression()
272 if not isinstance(node.call, nodes.Call):
273 raise TemplateSyntaxError('expected call', node.lineno,
274 self.filename)
Armin Ronacher115de2e2008-05-01 22:20:05 +0200275 node.body = self.parse_statements(('name:endcall',), drop_needle=True)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200276 return node
277
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200278 def parse_filter_block(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200279 node = nodes.FilterBlock(lineno=self.stream.next().lineno)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200280 node.filter = self.parse_filter(None, start_inline=True)
Armin Ronacher115de2e2008-05-01 22:20:05 +0200281 node.body = self.parse_statements(('name:endfilter',),
282 drop_needle=True)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200283 return node
284
Armin Ronachere791c2a2008-04-07 18:39:54 +0200285 def parse_macro(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200286 node = nodes.Macro(lineno=self.stream.next().lineno)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200287 node.name = self.stream.expect('name').value
Armin Ronacherf059ec12008-04-11 22:21:00 +0200288 # make sure that assignments to that name are allowed
289 if not nodes.Name(node.name, 'store').can_assign():
290 raise TemplateSyntaxError('can\'t assign macro to %r' %
291 node.target, node.lineno,
292 self.filename)
Armin Ronacher71082072008-04-12 14:19:36 +0200293 self.parse_signature(node)
Armin Ronacher115de2e2008-05-01 22:20:05 +0200294 node.body = self.parse_statements(('name:endmacro',),
295 drop_needle=True)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200296 return node
297
298 def parse_print(self):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200299 node = nodes.Output(lineno=self.stream.next().lineno)
Armin Ronachere791c2a2008-04-07 18:39:54 +0200300 node.nodes = []
Armin Ronacher023b5e92008-05-08 11:03:10 +0200301 while self.stream.current.type is not 'block_end':
Armin Ronachere791c2a2008-04-07 18:39:54 +0200302 if node.nodes:
303 self.stream.expect('comma')
304 node.nodes.append(self.parse_expression())
Armin Ronachere791c2a2008-04-07 18:39:54 +0200305 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200306
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200307 def parse_expression(self, no_condexpr=False):
Armin Ronacher023b5e92008-05-08 11:03:10 +0200308 """Parse an expression. Per default all expressions are parsed, if
309 the optional `no_condexpr` parameter is set to `True` conditional
310 expressions are not parsed.
311 """
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200312 if no_condexpr:
313 return self.parse_or()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200314 return self.parse_condexpr()
315
316 def parse_condexpr(self):
317 lineno = self.stream.current.lineno
318 expr1 = self.parse_or()
Armin Ronacher115de2e2008-05-01 22:20:05 +0200319 while self.stream.current.test('name:if'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200320 self.stream.next()
321 expr2 = self.parse_or()
Armin Ronacher115de2e2008-05-01 22:20:05 +0200322 self.stream.expect('name:else')
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200323 expr3 = self.parse_condexpr()
324 expr1 = nodes.CondExpr(expr2, expr1, expr3, lineno=lineno)
325 lineno = self.stream.current.lineno
326 return expr1
327
328 def parse_or(self):
329 lineno = self.stream.current.lineno
330 left = self.parse_and()
Armin Ronacher115de2e2008-05-01 22:20:05 +0200331 while self.stream.current.test('name:or'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200332 self.stream.next()
333 right = self.parse_and()
334 left = nodes.Or(left, right, lineno=lineno)
335 lineno = self.stream.current.lineno
336 return left
337
338 def parse_and(self):
339 lineno = self.stream.current.lineno
340 left = self.parse_compare()
Armin Ronacher115de2e2008-05-01 22:20:05 +0200341 while self.stream.current.test('name:and'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200342 self.stream.next()
343 right = self.parse_compare()
344 left = nodes.And(left, right, lineno=lineno)
345 lineno = self.stream.current.lineno
346 return left
347
348 def parse_compare(self):
349 lineno = self.stream.current.lineno
350 expr = self.parse_add()
351 ops = []
352 while 1:
353 token_type = self.stream.current.type
354 if token_type in _compare_operators:
355 self.stream.next()
356 ops.append(nodes.Operand(token_type, self.parse_add()))
Armin Ronacher115de2e2008-05-01 22:20:05 +0200357 elif self.stream.current.test('name:in'):
358 self.stream.next()
359 ops.append(nodes.Operand('in', self.parse_add()))
360 elif self.stream.current.test('name:not') and \
361 self.stream.look().test('name:in'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200362 self.stream.skip(2)
363 ops.append(nodes.Operand('notin', self.parse_add()))
364 else:
365 break
366 lineno = self.stream.current.lineno
367 if not ops:
368 return expr
369 return nodes.Compare(expr, ops, lineno=lineno)
370
371 def parse_add(self):
372 lineno = self.stream.current.lineno
373 left = self.parse_sub()
374 while self.stream.current.type is 'add':
375 self.stream.next()
376 right = self.parse_sub()
377 left = nodes.Add(left, right, lineno=lineno)
378 lineno = self.stream.current.lineno
379 return left
380
381 def parse_sub(self):
382 lineno = self.stream.current.lineno
383 left = self.parse_concat()
384 while self.stream.current.type is 'sub':
385 self.stream.next()
386 right = self.parse_concat()
387 left = nodes.Sub(left, right, lineno=lineno)
388 lineno = self.stream.current.lineno
389 return left
390
391 def parse_concat(self):
392 lineno = self.stream.current.lineno
393 args = [self.parse_mul()]
394 while self.stream.current.type is 'tilde':
395 self.stream.next()
396 args.append(self.parse_mul())
397 if len(args) == 1:
398 return args[0]
399 return nodes.Concat(args, lineno=lineno)
400
401 def parse_mul(self):
402 lineno = self.stream.current.lineno
403 left = self.parse_div()
404 while self.stream.current.type is 'mul':
405 self.stream.next()
406 right = self.parse_div()
407 left = nodes.Mul(left, right, lineno=lineno)
408 lineno = self.stream.current.lineno
409 return left
410
411 def parse_div(self):
412 lineno = self.stream.current.lineno
413 left = self.parse_floordiv()
414 while self.stream.current.type is 'div':
415 self.stream.next()
416 right = self.parse_floordiv()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200417 left = nodes.Div(left, right, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200418 lineno = self.stream.current.lineno
419 return left
420
421 def parse_floordiv(self):
422 lineno = self.stream.current.lineno
423 left = self.parse_mod()
424 while self.stream.current.type is 'floordiv':
425 self.stream.next()
426 right = self.parse_mod()
427 left = nodes.FloorDiv(left, right, lineno=lineno)
428 lineno = self.stream.current.lineno
429 return left
430
431 def parse_mod(self):
432 lineno = self.stream.current.lineno
433 left = self.parse_pow()
434 while self.stream.current.type is 'mod':
435 self.stream.next()
436 right = self.parse_pow()
437 left = nodes.Mod(left, right, lineno=lineno)
438 lineno = self.stream.current.lineno
439 return left
440
441 def parse_pow(self):
442 lineno = self.stream.current.lineno
443 left = self.parse_unary()
444 while self.stream.current.type is 'pow':
445 self.stream.next()
446 right = self.parse_unary()
447 left = nodes.Pow(left, right, lineno=lineno)
448 lineno = self.stream.current.lineno
449 return left
450
451 def parse_unary(self):
452 token_type = self.stream.current.type
453 lineno = self.stream.current.lineno
Armin Ronacher115de2e2008-05-01 22:20:05 +0200454 if token_type is 'name' and self.stream.current.value == 'not':
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200455 self.stream.next()
456 node = self.parse_unary()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200457 return nodes.Not(node, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200458 if token_type is 'sub':
459 self.stream.next()
460 node = self.parse_unary()
Armin Ronachere791c2a2008-04-07 18:39:54 +0200461 return nodes.Neg(node, lineno=lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200462 if token_type is 'add':
463 self.stream.next()
464 node = self.parse_unary()
465 return nodes.Pos(node, lineno=lineno)
466 return self.parse_primary()
467
468 def parse_primary(self, parse_postfix=True):
469 token = self.stream.current
470 if token.type is 'name':
471 if token.value in ('true', 'false'):
472 node = nodes.Const(token.value == 'true', lineno=token.lineno)
473 elif token.value == 'none':
474 node = nodes.Const(None, lineno=token.lineno)
475 else:
476 node = nodes.Name(token.value, 'load', lineno=token.lineno)
477 self.stream.next()
478 elif token.type in ('integer', 'float', 'string'):
479 self.stream.next()
480 node = nodes.Const(token.value, lineno=token.lineno)
481 elif token.type is 'lparen':
482 self.stream.next()
483 node = self.parse_tuple()
484 self.stream.expect('rparen')
485 elif token.type is 'lbracket':
486 node = self.parse_list()
487 elif token.type is 'lbrace':
488 node = self.parse_dict()
489 else:
490 raise TemplateSyntaxError("unexpected token '%s'" %
491 (token,), token.lineno,
492 self.filename)
493 if parse_postfix:
494 node = self.parse_postfix(node)
495 return node
496
Armin Ronacher023b5e92008-05-08 11:03:10 +0200497 def parse_tuple(self, simplified=False, no_condexpr=False):
498 """Works like `parse_expression` but if multiple expressions are
499 delimited by a comma a :class:`~jinja2.nodes.Tuple` node is created.
500 This method could also return a regular expression instead of a tuple
501 if no commas where found.
502
503 The default parsing mode is a full tuple. If `simplified` is `True`
504 only names and literals are parsed. The `no_condexpr` parameter is
505 forwarded to :meth:`parse_expression`.
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200506 """
507 lineno = self.stream.current.lineno
Armin Ronacher3d8b7842008-04-13 13:16:50 +0200508 if simplified:
509 parse = self.parse_primary
510 elif no_condexpr:
511 parse = lambda: self.parse_expression(no_condexpr=True)
512 else:
513 parse = self.parse_expression
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200514 args = []
515 is_tuple = False
516 while 1:
517 if args:
518 self.stream.expect('comma')
Armin Ronacher115de2e2008-05-01 22:20:05 +0200519 if self.is_tuple_end():
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200520 break
521 args.append(parse())
Armin Ronacherb5124e62008-04-25 00:36:14 +0200522 if self.stream.current.type is 'comma':
523 is_tuple = True
524 else:
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200525 break
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200526 lineno = self.stream.current.lineno
527 if not is_tuple and args:
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200528 return args[0]
529 return nodes.Tuple(args, 'load', lineno=lineno)
530
531 def parse_list(self):
532 token = self.stream.expect('lbracket')
533 items = []
534 while self.stream.current.type is not 'rbracket':
535 if items:
536 self.stream.expect('comma')
537 if self.stream.current.type == 'rbracket':
538 break
539 items.append(self.parse_expression())
540 self.stream.expect('rbracket')
541 return nodes.List(items, lineno=token.lineno)
542
543 def parse_dict(self):
544 token = self.stream.expect('lbrace')
545 items = []
546 while self.stream.current.type is not 'rbrace':
547 if items:
548 self.stream.expect('comma')
549 if self.stream.current.type == 'rbrace':
550 break
551 key = self.parse_expression()
552 self.stream.expect('colon')
553 value = self.parse_expression()
554 items.append(nodes.Pair(key, value, lineno=key.lineno))
555 self.stream.expect('rbrace')
Armin Ronacherbe4ae242008-04-18 09:49:08 +0200556 return nodes.Dict(items, lineno=token.lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200557
558 def parse_postfix(self, node):
559 while 1:
560 token_type = self.stream.current.type
561 if token_type is 'dot' or token_type is 'lbracket':
562 node = self.parse_subscript(node)
563 elif token_type is 'lparen':
564 node = self.parse_call(node)
565 elif token_type is 'pipe':
566 node = self.parse_filter(node)
Armin Ronacher115de2e2008-05-01 22:20:05 +0200567 elif token_type is 'name' and self.stream.current.value == 'is':
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200568 node = self.parse_test(node)
569 else:
570 break
571 return node
572
573 def parse_subscript(self, node):
574 token = self.stream.next()
575 if token.type is 'dot':
Armin Ronachere791c2a2008-04-07 18:39:54 +0200576 attr_token = self.stream.current
577 if attr_token.type not in ('name', 'integer'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200578 raise TemplateSyntaxError('expected name or number',
Armin Ronachere791c2a2008-04-07 18:39:54 +0200579 attr_token.lineno, self.filename)
580 arg = nodes.Const(attr_token.value, lineno=attr_token.lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200581 self.stream.next()
582 elif token.type is 'lbracket':
583 args = []
584 while self.stream.current.type is not 'rbracket':
585 if args:
586 self.stream.expect('comma')
587 args.append(self.parse_subscribed())
588 self.stream.expect('rbracket')
589 if len(args) == 1:
590 arg = args[0]
591 else:
Benjamin Wieganda3152742008-04-28 18:07:52 +0200592 arg = nodes.Tuple(args, self.lineno, self.filename)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200593 else:
594 raise TemplateSyntaxError('expected subscript expression',
595 self.lineno, self.filename)
596 return nodes.Subscript(node, arg, 'load', lineno=token.lineno)
597
598 def parse_subscribed(self):
599 lineno = self.stream.current.lineno
600
601 if self.stream.current.type is 'colon':
602 self.stream.next()
603 args = [None]
604 else:
605 node = self.parse_expression()
606 if self.stream.current.type is not 'colon':
607 return node
608 self.stream.next()
609 args = [node]
610
611 if self.stream.current.type is 'colon':
612 args.append(None)
613 elif self.stream.current.type not in ('rbracket', 'comma'):
614 args.append(self.parse_expression())
615 else:
616 args.append(None)
617
618 if self.stream.current.type is 'colon':
619 self.stream.next()
620 if self.stream.current.type not in ('rbracket', 'comma'):
621 args.append(self.parse_expression())
622 else:
623 args.append(None)
624 else:
625 args.append(None)
626
627 return nodes.Slice(lineno=lineno, *args)
628
629 def parse_call(self, node):
630 token = self.stream.expect('lparen')
631 args = []
632 kwargs = []
633 dyn_args = dyn_kwargs = None
634 require_comma = False
635
636 def ensure(expr):
637 if not expr:
638 raise TemplateSyntaxError('invalid syntax for function '
639 'call expression', token.lineno,
640 self.filename)
641
642 while self.stream.current.type is not 'rparen':
643 if require_comma:
644 self.stream.expect('comma')
645 # support for trailing comma
646 if self.stream.current.type is 'rparen':
647 break
648 if self.stream.current.type is 'mul':
649 ensure(dyn_args is None and dyn_kwargs is None)
650 self.stream.next()
651 dyn_args = self.parse_expression()
652 elif self.stream.current.type is 'pow':
653 ensure(dyn_kwargs is None)
654 self.stream.next()
655 dyn_kwargs = self.parse_expression()
656 else:
657 ensure(dyn_args is None and dyn_kwargs is None)
658 if self.stream.current.type is 'name' and \
659 self.stream.look().type is 'assign':
660 key = self.stream.current.value
661 self.stream.skip(2)
Armin Ronacher0611e492008-04-25 23:44:14 +0200662 value = self.parse_expression()
663 kwargs.append(nodes.Keyword(key, value,
664 lineno=value.lineno))
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200665 else:
666 ensure(not kwargs)
667 args.append(self.parse_expression())
668
669 require_comma = True
670 self.stream.expect('rparen')
671
672 if node is None:
673 return args, kwargs, dyn_args, dyn_kwargs
674 return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs,
675 lineno=token.lineno)
676
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200677 def parse_filter(self, node, start_inline=False):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200678 lineno = self.stream.current.type
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200679 while self.stream.current.type == 'pipe' or start_inline:
680 if not start_inline:
681 self.stream.next()
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200682 token = self.stream.expect('name')
683 if self.stream.current.type is 'lparen':
684 args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
685 else:
686 args = []
687 kwargs = []
688 dyn_args = dyn_kwargs = None
Armin Ronacherd55ab532008-04-09 16:13:39 +0200689 node = nodes.Filter(node, token.value, args, kwargs, dyn_args,
690 dyn_kwargs, lineno=token.lineno)
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200691 start_inline = False
Armin Ronacherd55ab532008-04-09 16:13:39 +0200692 return node
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200693
694 def parse_test(self, node):
Armin Ronacher115de2e2008-05-01 22:20:05 +0200695 token = self.stream.next()
696 if self.stream.current.test('name:not'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200697 self.stream.next()
698 negated = True
699 else:
700 negated = False
701 name = self.stream.expect('name').value
Armin Ronacher10f3ba22008-04-18 11:30:37 +0200702 dyn_args = dyn_kwargs = None
703 kwargs = []
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200704 if self.stream.current.type is 'lparen':
705 args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
706 elif self.stream.current.type in ('name', 'string', 'integer',
707 'float', 'lparen', 'lbracket',
Armin Ronacher115de2e2008-05-01 22:20:05 +0200708 'lbrace'):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200709 args = [self.parse_expression()]
710 else:
711 args = []
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200712 node = nodes.Test(node, name, args, kwargs, dyn_args,
713 dyn_kwargs, lineno=token.lineno)
714 if negated:
Armin Ronacher9a822052008-04-17 18:44:07 +0200715 node = nodes.Not(node, lineno=token.lineno)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200716 return node
717
718 def subparse(self, end_tokens=None):
719 body = []
720 data_buffer = []
721 add_data = data_buffer.append
722
723 def flush_data():
724 if data_buffer:
725 lineno = data_buffer[0].lineno
726 body.append(nodes.Output(data_buffer[:], lineno=lineno))
727 del data_buffer[:]
728
729 while self.stream:
730 token = self.stream.current
731 if token.type is 'data':
Armin Ronacherfa865fb2008-04-12 22:11:53 +0200732 if token.value:
733 add_data(nodes.Const(token.value, lineno=token.lineno))
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200734 self.stream.next()
735 elif token.type is 'variable_begin':
736 self.stream.next()
Armin Ronacherb5124e62008-04-25 00:36:14 +0200737 add_data(self.parse_tuple())
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200738 self.stream.expect('variable_end')
739 elif token.type is 'block_begin':
740 flush_data()
741 self.stream.next()
742 if end_tokens is not None and \
Armin Ronachercda43df2008-05-03 17:10:05 +0200743 self.stream.current.test_any(*end_tokens):
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200744 return body
Armin Ronacher023b5e92008-05-08 11:03:10 +0200745 rv = self.parse_statement()
746 if isinstance(rv, list):
747 body.extend(rv)
748 else:
749 body.append(rv)
Armin Ronacher82b3f3d2008-03-31 20:01:08 +0200750 self.stream.expect('block_end')
751 else:
752 raise AssertionError('internal parsing error')
753
754 flush_data()
755 return body
756
757 def parse(self):
758 """Parse the whole template into a `Template` node."""
Armin Ronacherd55ab532008-04-09 16:13:39 +0200759 result = nodes.Template(self.subparse(), lineno=1)
760 result.set_environment(self.environment)
761 return result