Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 896 lines (805 sloc) 35.177 kB
07bc684 @mitsuhiko a clean restart
authored
1 # -*- coding: utf-8 -*-
2 """
3 jinja2.parser
4 ~~~~~~~~~~~~~
5
6 Implements the template parser.
7
55494e4 @mitsuhiko It's a new year
authored
8 :copyright: (c) 2010 by the Jinja Team.
07bc684 @mitsuhiko a clean restart
authored
9 :license: BSD, see LICENSE for more details.
10 """
82b3f3d @mitsuhiko first version of new parser
authored
11 from jinja2 import nodes
a78d276 @mitsuhiko simplified from imports
authored
12 from jinja2.exceptions import TemplateSyntaxError, TemplateAssertionError
bd35772 @mitsuhiko More Python 3 support.
authored
13 from jinja2.utils import next
5dcb724 @mitsuhiko greatly improved error message reporting. This fixes #339
authored
14 from jinja2.lexer import describe_token, describe_token_expr
07bc684 @mitsuhiko a clean restart
authored
15
16
5dcb724 @mitsuhiko greatly improved error message reporting. This fixes #339
authored
17 #: statements that callinto
e791c2a @mitsuhiko added first working pieces of compiler
authored
18 _statement_keywords = frozenset(['for', 'if', 'block', 'extends', 'print',
0a2ac69 @mitsuhiko switched back to explicit set for assignments. {% foo = 42 %} become…
authored
19 'macro', 'include', 'from', 'import',
20 'set'])
115de2e @mitsuhiko Jinja doesn't have keywords any longer. The reason for this radical …
authored
21 _compare_operators = frozenset(['eq', 'ne', 'lt', 'lteq', 'gt', 'gteq'])
82b3f3d @mitsuhiko first version of new parser
authored
22
07bc684 @mitsuhiko a clean restart
authored
23
24 class Parser(object):
023b5e9 @mitsuhiko First extension interface documentation and updates in that interface
authored
25 """This is the central parsing class Jinja2 uses. It's passed to
26 extensions and can be used to parse expressions or statements.
07bc684 @mitsuhiko a clean restart
authored
27 """
28
ba6e25a @mitsuhiko Added support for `Environment.compile_expression`.
authored
29 def __init__(self, environment, source, name=None, filename=None,
30 state=None):
07bc684 @mitsuhiko a clean restart
authored
31 self.environment = environment
ba6e25a @mitsuhiko Added support for `Environment.compile_expression`.
authored
32 self.stream = environment._tokenize(source, name, filename, state)
7f15ef8 @mitsuhiko improved exception system. now both name (load name) and filename ar…
authored
33 self.name = name
07bc684 @mitsuhiko a clean restart
authored
34 self.filename = filename
35 self.closed = False
0553093 @mitsuhiko refactored extensions a bit
authored
36 self.extensions = {}
5b3f4dc @mitsuhiko Added extension ordering, this fixes #376.
authored
37 for extension in environment.iter_extensions():
0553093 @mitsuhiko refactored extensions a bit
authored
38 for tag in extension.tags:
39 self.extensions[tag] = extension.parse
023b5e9 @mitsuhiko First extension interface documentation and updates in that interface
authored
40 self._last_identifier = 0
5dcb724 @mitsuhiko greatly improved error message reporting. This fixes #339
authored
41 self._tag_stack = []
42 self._end_token_stack = []
115de2e @mitsuhiko Jinja doesn't have keywords any longer. The reason for this radical …
authored
43
7f15ef8 @mitsuhiko improved exception system. now both name (load name) and filename ar…
authored
44 def fail(self, msg, lineno=None, exc=TemplateSyntaxError):
45 """Convenience method that raises `exc` with the message, passed
46 line number or last line number as well as the current name and
47 filename.
48 """
49 if lineno is None:
50 lineno = self.stream.current.lineno
61a5a24 @mitsuhiko fixed a bug in error reporting and some small documentation updates
authored
51 raise exc(msg, lineno, self.name, self.filename)
7f15ef8 @mitsuhiko improved exception system. now both name (load name) and filename ar…
authored
52
5dcb724 @mitsuhiko greatly improved error message reporting. This fixes #339
authored
53 def _fail_ut_eof(self, name, end_token_stack, lineno):
54 expected = []
55 for exprs in end_token_stack:
56 expected.extend(map(describe_token_expr, exprs))
28decdb @mitsuhiko This fixes #368.
authored
57 if end_token_stack:
58 currently_looking = ' or '.join(
59 "'%s'" % describe_token_expr(expr)
60 for expr in end_token_stack[-1])
61 else:
62 currently_looking = None
5dcb724 @mitsuhiko greatly improved error message reporting. This fixes #339
authored
63
64 if name is None:
65 message = ['Unexpected end of template.']
66 else:
67 message = ['Encountered unknown tag \'%s\'.' % name]
68
28decdb @mitsuhiko This fixes #368.
authored
69 if currently_looking:
70 if name is not None and name in expected:
71 message.append('You probably made a nesting mistake. Jinja '
72 'is expecting this tag, but currently looking '
73 'for %s.' % currently_looking)
74 else:
75 message.append('Jinja was looking for the following tags: '
76 '%s.' % currently_looking)
5dcb724 @mitsuhiko greatly improved error message reporting. This fixes #339
authored
77
78 if self._tag_stack:
79 message.append('The innermost block that needs to be '
80 'closed is \'%s\'.' % self._tag_stack[-1])
81
82 self.fail(' '.join(message), lineno)
83
84 def fail_unknown_tag(self, name, lineno=None):
85 """Called if the parser encounters an unknown tag. Tries to fail
86 with a human readable error message that could help to identify
87 the problem.
88 """
89 return self._fail_ut_eof(name, self._end_token_stack, lineno)
90
91 def fail_eof(self, end_tokens=None, lineno=None):
92 """Like fail_unknown_tag but for end of template situations."""
93 stack = list(self._end_token_stack)
94 if end_tokens is not None:
95 stack.append(end_tokens)
96 return self._fail_ut_eof(None, stack, lineno)
97
fdf9530 @mitsuhiko added parsing code for "for item in seq recursive" and improved parse…
authored
98 def is_tuple_end(self, extra_end_rules=None):
115de2e @mitsuhiko Jinja doesn't have keywords any longer. The reason for this radical …
authored
99 """Are we at the end of a tuple?"""
fdf9530 @mitsuhiko added parsing code for "for item in seq recursive" and improved parse…
authored
100 if self.stream.current.type in ('variable_end', 'block_end', 'rparen'):
762079c @mitsuhiko more updates on the extension API
authored
101 return True
fdf9530 @mitsuhiko added parsing code for "for item in seq recursive" and improved parse…
authored
102 elif extra_end_rules is not None:
103 return self.stream.current.test_any(extra_end_rules)
762079c @mitsuhiko more updates on the extension API
authored
104 return False
105
023b5e9 @mitsuhiko First extension interface documentation and updates in that interface
authored
106 def free_identifier(self, lineno=None):
107 """Return a new free identifier as :class:`~jinja2.nodes.InternalName`."""
108 self._last_identifier += 1
109 rv = object.__new__(nodes.InternalName)
110 nodes.Node.__init__(rv, 'fi%d' % self._last_identifier, lineno=lineno)
111 return rv
115de2e @mitsuhiko Jinja doesn't have keywords any longer. The reason for this radical …
authored
112
82b3f3d @mitsuhiko first version of new parser
authored
113 def parse_statement(self):
114 """Parse a single statement."""
0a2ac69 @mitsuhiko switched back to explicit set for assignments. {% foo = 42 %} become…
authored
115 token = self.stream.current
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
116 if token.type != 'name':
7f15ef8 @mitsuhiko improved exception system. now both name (load name) and filename ar…
authored
117 self.fail('tag name expected', token.lineno)
5dcb724 @mitsuhiko greatly improved error message reporting. This fixes #339
authored
118 self._tag_stack.append(token.value)
119 pop_tag = True
120 try:
121 if token.value in _statement_keywords:
122 return getattr(self, 'parse_' + self.stream.current.value)()
123 if token.value == 'call':
124 return self.parse_call_block()
125 if token.value == 'filter':
126 return self.parse_filter_block()
127 ext = self.extensions.get(token.value)
128 if ext is not None:
129 return ext(self)
130
131 # did not work out, remove the token we pushed by accident
132 # from the stack so that the unknown tag fail function can
133 # produce a proper error message.
134 self._tag_stack.pop()
135 pop_tag = False
136 self.fail_unknown_tag(token.value, token.lineno)
137 finally:
138 if pop_tag:
139 self._tag_stack.pop()
82b3f3d @mitsuhiko first version of new parser
authored
140
141 def parse_statements(self, end_tokens, drop_needle=False):
f59bac2 @mitsuhiko added first code for parser extensions and moved some code in speedup…
authored
142 """Parse multiple statements into a list until one of the end tokens
143 is reached. This is used to parse the body of statements as it also
023b5e9 @mitsuhiko First extension interface documentation and updates in that interface
authored
144 parses template data if appropriate. The parser checks first if the
145 current token is a colon and skips it if there is one. Then it checks
146 for the block end and parses until if one of the `end_tokens` is
147 reached. Per default the active token in the stream at the end of
148 the call is the matched end token. If this is not wanted `drop_needle`
149 can be set to `True` and the end token is removed.
82b3f3d @mitsuhiko first version of new parser
authored
150 """
151 # the first token may be a colon for python compatibility
fdf9530 @mitsuhiko added parsing code for "for item in seq recursive" and improved parse…
authored
152 self.stream.skip_if('colon')
82b3f3d @mitsuhiko first version of new parser
authored
153
2b60fe5 @mitsuhiko fixed setup.py, a type and removed the possibility to use multiple st…
authored
154 # in the future it would be possible to add whole code sections
155 # by adding some sort of end of statement token and parsing those here.
156 self.stream.expect('block_end')
157 result = self.subparse(end_tokens)
158
5dcb724 @mitsuhiko greatly improved error message reporting. This fixes #339
authored
159 # we reached the end of the template too early, the subparser
160 # does not check for this, so we do that now
161 if self.stream.current.type == 'eof':
162 self.fail_eof(end_tokens)
163
82b3f3d @mitsuhiko first version of new parser
authored
164 if drop_needle:
bd35772 @mitsuhiko More Python 3 support.
authored
165 next(self.stream)
82b3f3d @mitsuhiko first version of new parser
authored
166 return result
167
0a2ac69 @mitsuhiko switched back to explicit set for assignments. {% foo = 42 %} become…
authored
168 def parse_set(self):
169 """Parse an assign statement."""
bd35772 @mitsuhiko More Python 3 support.
authored
170 lineno = next(self.stream).lineno
0a2ac69 @mitsuhiko switched back to explicit set for assignments. {% foo = 42 %} become…
authored
171 target = self.parse_assign_target()
172 self.stream.expect('assign')
173 expr = self.parse_tuple()
174 return nodes.Assign(target, expr, lineno=lineno)
175
82b3f3d @mitsuhiko first version of new parser
authored
176 def parse_for(self):
177 """Parse a for loop."""
115de2e @mitsuhiko Jinja doesn't have keywords any longer. The reason for this radical …
authored
178 lineno = self.stream.expect('name:for').lineno
fdf9530 @mitsuhiko added parsing code for "for item in seq recursive" and improved parse…
authored
179 target = self.parse_assign_target(extra_end_rules=('name:in',))
115de2e @mitsuhiko Jinja doesn't have keywords any longer. The reason for this radical …
authored
180 self.stream.expect('name:in')
fdf9530 @mitsuhiko added parsing code for "for item in seq recursive" and improved parse…
authored
181 iter = self.parse_tuple(with_condexpr=False,
182 extra_end_rules=('name:recursive',))
3d8b784 @mitsuhiko added loop filtering
authored
183 test = None
fdf9530 @mitsuhiko added parsing code for "for item in seq recursive" and improved parse…
authored
184 if self.stream.skip_if('name:if'):
3d8b784 @mitsuhiko added loop filtering
authored
185 test = self.parse_expression()
fdf9530 @mitsuhiko added parsing code for "for item in seq recursive" and improved parse…
authored
186 recursive = self.stream.skip_if('name:recursive')
115de2e @mitsuhiko Jinja doesn't have keywords any longer. The reason for this radical …
authored
187 body = self.parse_statements(('name:endfor', 'name:else'))
bd35772 @mitsuhiko More Python 3 support.
authored
188 if next(self.stream).value == 'endfor':
82b3f3d @mitsuhiko first version of new parser
authored
189 else_ = []
190 else:
115de2e @mitsuhiko Jinja doesn't have keywords any longer. The reason for this radical …
authored
191 else_ = self.parse_statements(('name:endfor',), drop_needle=True)
fdf9530 @mitsuhiko added parsing code for "for item in seq recursive" and improved parse…
authored
192 return nodes.For(target, iter, body, else_, test,
193 recursive, lineno=lineno)
82b3f3d @mitsuhiko first version of new parser
authored
194
195 def parse_if(self):
e791c2a @mitsuhiko added first working pieces of compiler
authored
196 """Parse an if construct."""
115de2e @mitsuhiko Jinja doesn't have keywords any longer. The reason for this radical …
authored
197 node = result = nodes.If(lineno=self.stream.expect('name:if').lineno)
e791c2a @mitsuhiko added first working pieces of compiler
authored
198 while 1:
09c002e @mitsuhiko added a function to parse assign targes and documented it for the ext…
authored
199 node.test = self.parse_tuple(with_condexpr=False)
115de2e @mitsuhiko Jinja doesn't have keywords any longer. The reason for this radical …
authored
200 node.body = self.parse_statements(('name:elif', 'name:else',
201 'name:endif'))
bd35772 @mitsuhiko More Python 3 support.
authored
202 token = next(self.stream)
115de2e @mitsuhiko Jinja doesn't have keywords any longer. The reason for this radical …
authored
203 if token.test('name:elif'):
e791c2a @mitsuhiko added first working pieces of compiler
authored
204 new_node = nodes.If(lineno=self.stream.current.lineno)
205 node.else_ = [new_node]
206 node = new_node
207 continue
115de2e @mitsuhiko Jinja doesn't have keywords any longer. The reason for this radical …
authored
208 elif token.test('name:else'):
209 node.else_ = self.parse_statements(('name:endif',),
e791c2a @mitsuhiko added first working pieces of compiler
authored
210 drop_needle=True)
211 else:
212 node.else_ = []
213 break
214 return result
82b3f3d @mitsuhiko first version of new parser
authored
215
216 def parse_block(self):
bd35772 @mitsuhiko More Python 3 support.
authored
217 node = nodes.Block(lineno=next(self.stream).lineno)
e791c2a @mitsuhiko added first working pieces of compiler
authored
218 node.name = self.stream.expect('name').value
74a0cd9 @mitsuhiko Added support for optional `scoped` modifier to blocks.
authored
219 node.scoped = self.stream.skip_if('name:scoped')
92622e9 @mitsuhiko Further error message improvement, this time for #341.
authored
220
221 # common problem people encounter when switching from django
222 # to jinja. we do not support hyphens in block names, so let's
223 # raise a nicer error message in that case.
224 if self.stream.current.type == 'sub':
225 self.fail('Block names in Jinja have to be valid Python '
5c5d061 @yole couple of typos in comments
yole authored
226 'identifiers and may not contain hyphens, use an '
92622e9 @mitsuhiko Further error message improvement, this time for #341.
authored
227 'underscore instead.')
228
115de2e @mitsuhiko Jinja doesn't have keywords any longer. The reason for this radical …
authored
229 node.body = self.parse_statements(('name:endblock',), drop_needle=True)
fd31049 @mitsuhiko all calls are proxied by context.call now so that we can inject envir…
authored
230 self.stream.skip_if('name:' + node.name)
e791c2a @mitsuhiko added first working pieces of compiler
authored
231 return node
82b3f3d @mitsuhiko first version of new parser
authored
232
233 def parse_extends(self):
bd35772 @mitsuhiko More Python 3 support.
authored
234 node = nodes.Extends(lineno=next(self.stream).lineno)
e791c2a @mitsuhiko added first working pieces of compiler
authored
235 node.template = self.parse_expression()
236 return node
82b3f3d @mitsuhiko first version of new parser
authored
237
ea847c5 @mitsuhiko added "with context" or "without context" import/include modifiers
authored
238 def parse_import_context(self, node, default):
cda43df @mitsuhiko updated filters: wordwraps uses the wordwrap module and urlize marks …
authored
239 if self.stream.current.test_any('name:with', 'name:without') and \
ea847c5 @mitsuhiko added "with context" or "without context" import/include modifiers
authored
240 self.stream.look().test('name:context'):
bd35772 @mitsuhiko More Python 3 support.
authored
241 node.with_context = next(self.stream).value == 'with'
ea847c5 @mitsuhiko added "with context" or "without context" import/include modifiers
authored
242 self.stream.skip()
243 else:
244 node.with_context = default
245 return node
246
82b3f3d @mitsuhiko first version of new parser
authored
247 def parse_include(self):
bd35772 @mitsuhiko More Python 3 support.
authored
248 node = nodes.Include(lineno=next(self.stream).lineno)
0611e49 @mitsuhiko revamped jinja2 import system. the behavior is less confusing now, b…
authored
249 node.template = self.parse_expression()
37f58ce @mitsuhiko Include statements can now be marked with ``ignore missing`` to skip
authored
250 if self.stream.current.test('name:ignore') and \
251 self.stream.look().test('name:missing'):
252 node.ignore_missing = True
253 self.stream.skip(2)
254 else:
255 node.ignore_missing = False
ea847c5 @mitsuhiko added "with context" or "without context" import/include modifiers
authored
256 return self.parse_import_context(node, True)
0611e49 @mitsuhiko revamped jinja2 import system. the behavior is less confusing now, b…
authored
257
258 def parse_import(self):
bd35772 @mitsuhiko More Python 3 support.
authored
259 node = nodes.Import(lineno=next(self.stream).lineno)
0611e49 @mitsuhiko revamped jinja2 import system. the behavior is less confusing now, b…
authored
260 node.template = self.parse_expression()
261 self.stream.expect('name:as')
09c002e @mitsuhiko added a function to parse assign targes and documented it for the ext…
authored
262 node.target = self.parse_assign_target(name_only=True).name
ea847c5 @mitsuhiko added "with context" or "without context" import/include modifiers
authored
263 return self.parse_import_context(node, False)
0611e49 @mitsuhiko revamped jinja2 import system. the behavior is less confusing now, b…
authored
264
265 def parse_from(self):
bd35772 @mitsuhiko More Python 3 support.
authored
266 node = nodes.FromImport(lineno=next(self.stream).lineno)
0611e49 @mitsuhiko revamped jinja2 import system. the behavior is less confusing now, b…
authored
267 node.template = self.parse_expression()
115de2e @mitsuhiko Jinja doesn't have keywords any longer. The reason for this radical …
authored
268 self.stream.expect('name:import')
0611e49 @mitsuhiko revamped jinja2 import system. the behavior is less confusing now, b…
authored
269 node.names = []
ea847c5 @mitsuhiko added "with context" or "without context" import/include modifiers
authored
270
271 def parse_context():
272 if self.stream.current.value in ('with', 'without') and \
273 self.stream.look().test('name:context'):
bd35772 @mitsuhiko More Python 3 support.
authored
274 node.with_context = next(self.stream).value == 'with'
ea847c5 @mitsuhiko added "with context" or "without context" import/include modifiers
authored
275 self.stream.skip()
276 return True
277 return False
278
0611e49 @mitsuhiko revamped jinja2 import system. the behavior is less confusing now, b…
authored
279 while 1:
280 if node.names:
281 self.stream.expect('comma')
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
282 if self.stream.current.type == 'name':
ea847c5 @mitsuhiko added "with context" or "without context" import/include modifiers
authored
283 if parse_context():
284 break
09c002e @mitsuhiko added a function to parse assign targes and documented it for the ext…
authored
285 target = self.parse_assign_target(name_only=True)
903d168 @mitsuhiko variables starting with one or more underscores are not exported
authored
286 if target.name.startswith('_'):
287 self.fail('names starting with an underline can not '
7f15ef8 @mitsuhiko improved exception system. now both name (load name) and filename ar…
authored
288 'be imported', target.lineno,
289 exc=TemplateAssertionError)
fdf9530 @mitsuhiko added parsing code for "for item in seq recursive" and improved parse…
authored
290 if self.stream.skip_if('name:as'):
09c002e @mitsuhiko added a function to parse assign targes and documented it for the ext…
authored
291 alias = self.parse_assign_target(name_only=True)
292 node.names.append((target.name, alias.name))
2feed1d @mitsuhiko there is now a workaround in the compiler that makes sure it's possib…
authored
293 else:
294 node.names.append(target.name)
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
295 if parse_context() or self.stream.current.type != 'comma':
0611e49 @mitsuhiko revamped jinja2 import system. the behavior is less confusing now, b…
authored
296 break
297 else:
298 break
ea847c5 @mitsuhiko added "with context" or "without context" import/include modifiers
authored
299 if not hasattr(node, 'with_context'):
300 node.with_context = False
fdf9530 @mitsuhiko added parsing code for "for item in seq recursive" and improved parse…
authored
301 self.stream.skip_if('comma')
e791c2a @mitsuhiko added first working pieces of compiler
authored
302 return node
82b3f3d @mitsuhiko first version of new parser
authored
303
7108207 @mitsuhiko added support for new call statement
authored
304 def parse_signature(self, node):
305 node.args = args = []
306 node.defaults = defaults = []
307 self.stream.expect('lparen')
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
308 while self.stream.current.type != 'rparen':
7108207 @mitsuhiko added support for new call statement
authored
309 if args:
310 self.stream.expect('comma')
09c002e @mitsuhiko added a function to parse assign targes and documented it for the ext…
authored
311 arg = self.parse_assign_target(name_only=True)
e9411b4 @mitsuhiko fixed bug with parameter parsing
authored
312 arg.set_ctx('param')
fdf9530 @mitsuhiko added parsing code for "for item in seq recursive" and improved parse…
authored
313 if self.stream.skip_if('assign'):
7108207 @mitsuhiko added support for new call statement
authored
314 defaults.append(self.parse_expression())
315 args.append(arg)
316 self.stream.expect('rparen')
317
82b3f3d @mitsuhiko first version of new parser
authored
318 def parse_call_block(self):
bd35772 @mitsuhiko More Python 3 support.
authored
319 node = nodes.CallBlock(lineno=next(self.stream).lineno)
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
320 if self.stream.current.type == 'lparen':
7108207 @mitsuhiko added support for new call statement
authored
321 self.parse_signature(node)
c9705c2 @mitsuhiko all unittests pass, the special and dependency lookups have their own…
authored
322 else:
323 node.args = []
324 node.defaults = []
7108207 @mitsuhiko added support for new call statement
authored
325
8edbe49 @mitsuhiko fixed one bug with blocks, one to go
authored
326 node.call = self.parse_expression()
327 if not isinstance(node.call, nodes.Call):
7f15ef8 @mitsuhiko improved exception system. now both name (load name) and filename ar…
authored
328 self.fail('expected call', node.lineno)
115de2e @mitsuhiko Jinja doesn't have keywords any longer. The reason for this radical …
authored
329 node.body = self.parse_statements(('name:endcall',), drop_needle=True)
e791c2a @mitsuhiko added first working pieces of compiler
authored
330 return node
331
fa865fb @mitsuhiko filter tag works now
authored
332 def parse_filter_block(self):
bd35772 @mitsuhiko More Python 3 support.
authored
333 node = nodes.FilterBlock(lineno=next(self.stream).lineno)
fa865fb @mitsuhiko filter tag works now
authored
334 node.filter = self.parse_filter(None, start_inline=True)
115de2e @mitsuhiko Jinja doesn't have keywords any longer. The reason for this radical …
authored
335 node.body = self.parse_statements(('name:endfilter',),
336 drop_needle=True)
fa865fb @mitsuhiko filter tag works now
authored
337 return node
338
e791c2a @mitsuhiko added first working pieces of compiler
authored
339 def parse_macro(self):
bd35772 @mitsuhiko More Python 3 support.
authored
340 node = nodes.Macro(lineno=next(self.stream).lineno)
09c002e @mitsuhiko added a function to parse assign targes and documented it for the ext…
authored
341 node.name = self.parse_assign_target(name_only=True).name
7108207 @mitsuhiko added support for new call statement
authored
342 self.parse_signature(node)
115de2e @mitsuhiko Jinja doesn't have keywords any longer. The reason for this radical …
authored
343 node.body = self.parse_statements(('name:endmacro',),
344 drop_needle=True)
e791c2a @mitsuhiko added first working pieces of compiler
authored
345 return node
346
347 def parse_print(self):
bd35772 @mitsuhiko More Python 3 support.
authored
348 node = nodes.Output(lineno=next(self.stream).lineno)
e791c2a @mitsuhiko added first working pieces of compiler
authored
349 node.nodes = []
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
350 while self.stream.current.type != 'block_end':
e791c2a @mitsuhiko added first working pieces of compiler
authored
351 if node.nodes:
352 self.stream.expect('comma')
353 node.nodes.append(self.parse_expression())
354 return node
82b3f3d @mitsuhiko first version of new parser
authored
355
fdf9530 @mitsuhiko added parsing code for "for item in seq recursive" and improved parse…
authored
356 def parse_assign_target(self, with_tuple=True, name_only=False,
357 extra_end_rules=None):
09c002e @mitsuhiko added a function to parse assign targes and documented it for the ext…
authored
358 """Parse an assignment target. As Jinja2 allows assignments to
359 tuples, this function can parse all allowed assignment targets. Per
360 default assignments to tuples are parsed, that can be disable however
361 by setting `with_tuple` to `False`. If only assignments to names are
fdf9530 @mitsuhiko added parsing code for "for item in seq recursive" and improved parse…
authored
362 wanted `name_only` can be set to `True`. The `extra_end_rules`
363 parameter is forwarded to the tuple parsing function.
09c002e @mitsuhiko added a function to parse assign targes and documented it for the ext…
authored
364 """
365 if name_only:
366 token = self.stream.expect('name')
367 target = nodes.Name(token.value, 'store', lineno=token.lineno)
368 else:
369 if with_tuple:
fdf9530 @mitsuhiko added parsing code for "for item in seq recursive" and improved parse…
authored
370 target = self.parse_tuple(simplified=True,
371 extra_end_rules=extra_end_rules)
09c002e @mitsuhiko added a function to parse assign targes and documented it for the ext…
authored
372 else:
2ee64bc @mitsuhiko fixed an issue with unary operators having the wrong precendence.
authored
373 target = self.parse_primary()
09c002e @mitsuhiko added a function to parse assign targes and documented it for the ext…
authored
374 target.set_ctx('store')
375 if not target.can_assign():
7f15ef8 @mitsuhiko improved exception system. now both name (load name) and filename ar…
authored
376 self.fail('can\'t assign to %r' % target.__class__.
377 __name__.lower(), target.lineno)
09c002e @mitsuhiko added a function to parse assign targes and documented it for the ext…
authored
378 return target
379
380 def parse_expression(self, with_condexpr=True):
023b5e9 @mitsuhiko First extension interface documentation and updates in that interface
authored
381 """Parse an expression. Per default all expressions are parsed, if
09c002e @mitsuhiko added a function to parse assign targes and documented it for the ext…
authored
382 the optional `with_condexpr` parameter is set to `False` conditional
023b5e9 @mitsuhiko First extension interface documentation and updates in that interface
authored
383 expressions are not parsed.
384 """
09c002e @mitsuhiko added a function to parse assign targes and documented it for the ext…
authored
385 if with_condexpr:
386 return self.parse_condexpr()
387 return self.parse_or()
82b3f3d @mitsuhiko first version of new parser
authored
388
389 def parse_condexpr(self):
390 lineno = self.stream.current.lineno
391 expr1 = self.parse_or()
fdf9530 @mitsuhiko added parsing code for "for item in seq recursive" and improved parse…
authored
392 while self.stream.skip_if('name:if'):
82b3f3d @mitsuhiko first version of new parser
authored
393 expr2 = self.parse_or()
547d0b6 @mitsuhiko Fixed a bug with the loop context of a for loop if the iterator passe…
authored
394 if self.stream.skip_if('name:else'):
395 expr3 = self.parse_condexpr()
396 else:
397 expr3 = None
82b3f3d @mitsuhiko first version of new parser
authored
398 expr1 = nodes.CondExpr(expr2, expr1, expr3, lineno=lineno)
399 lineno = self.stream.current.lineno
400 return expr1
401
402 def parse_or(self):
403 lineno = self.stream.current.lineno
404 left = self.parse_and()
fdf9530 @mitsuhiko added parsing code for "for item in seq recursive" and improved parse…
authored
405 while self.stream.skip_if('name:or'):
82b3f3d @mitsuhiko first version of new parser
authored
406 right = self.parse_and()
407 left = nodes.Or(left, right, lineno=lineno)
408 lineno = self.stream.current.lineno
409 return left
410
411 def parse_and(self):
412 lineno = self.stream.current.lineno
d89f0f3 @mitsuhiko Priority of `not` raised. It's now possible to write `not foo in bar`
authored
413 left = self.parse_not()
fdf9530 @mitsuhiko added parsing code for "for item in seq recursive" and improved parse…
authored
414 while self.stream.skip_if('name:and'):
d89f0f3 @mitsuhiko Priority of `not` raised. It's now possible to write `not foo in bar`
authored
415 right = self.parse_not()
82b3f3d @mitsuhiko first version of new parser
authored
416 left = nodes.And(left, right, lineno=lineno)
417 lineno = self.stream.current.lineno
418 return left
419
d89f0f3 @mitsuhiko Priority of `not` raised. It's now possible to write `not foo in bar`
authored
420 def parse_not(self):
421 if self.stream.current.test('name:not'):
bd35772 @mitsuhiko More Python 3 support.
authored
422 lineno = next(self.stream).lineno
d89f0f3 @mitsuhiko Priority of `not` raised. It's now possible to write `not foo in bar`
authored
423 return nodes.Not(self.parse_not(), lineno=lineno)
424 return self.parse_compare()
425
82b3f3d @mitsuhiko first version of new parser
authored
426 def parse_compare(self):
427 lineno = self.stream.current.lineno
428 expr = self.parse_add()
429 ops = []
430 while 1:
431 token_type = self.stream.current.type
432 if token_type in _compare_operators:
bd35772 @mitsuhiko More Python 3 support.
authored
433 next(self.stream)
82b3f3d @mitsuhiko first version of new parser
authored
434 ops.append(nodes.Operand(token_type, self.parse_add()))
fdf9530 @mitsuhiko added parsing code for "for item in seq recursive" and improved parse…
authored
435 elif self.stream.skip_if('name:in'):
115de2e @mitsuhiko Jinja doesn't have keywords any longer. The reason for this radical …
authored
436 ops.append(nodes.Operand('in', self.parse_add()))
437 elif self.stream.current.test('name:not') and \
438 self.stream.look().test('name:in'):
82b3f3d @mitsuhiko first version of new parser
authored
439 self.stream.skip(2)
440 ops.append(nodes.Operand('notin', self.parse_add()))
441 else:
442 break
443 lineno = self.stream.current.lineno
444 if not ops:
445 return expr
446 return nodes.Compare(expr, ops, lineno=lineno)
447
448 def parse_add(self):
449 lineno = self.stream.current.lineno
450 left = self.parse_sub()
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
451 while self.stream.current.type == 'add':
bd35772 @mitsuhiko More Python 3 support.
authored
452 next(self.stream)
82b3f3d @mitsuhiko first version of new parser
authored
453 right = self.parse_sub()
454 left = nodes.Add(left, right, lineno=lineno)
455 lineno = self.stream.current.lineno
456 return left
457
458 def parse_sub(self):
459 lineno = self.stream.current.lineno
460 left = self.parse_concat()
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
461 while self.stream.current.type == 'sub':
bd35772 @mitsuhiko More Python 3 support.
authored
462 next(self.stream)
82b3f3d @mitsuhiko first version of new parser
authored
463 right = self.parse_concat()
464 left = nodes.Sub(left, right, lineno=lineno)
465 lineno = self.stream.current.lineno
466 return left
467
468 def parse_concat(self):
469 lineno = self.stream.current.lineno
470 args = [self.parse_mul()]
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
471 while self.stream.current.type == 'tilde':
bd35772 @mitsuhiko More Python 3 support.
authored
472 next(self.stream)
82b3f3d @mitsuhiko first version of new parser
authored
473 args.append(self.parse_mul())
474 if len(args) == 1:
475 return args[0]
476 return nodes.Concat(args, lineno=lineno)
477
478 def parse_mul(self):
479 lineno = self.stream.current.lineno
480 left = self.parse_div()
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
481 while self.stream.current.type == 'mul':
bd35772 @mitsuhiko More Python 3 support.
authored
482 next(self.stream)
82b3f3d @mitsuhiko first version of new parser
authored
483 right = self.parse_div()
484 left = nodes.Mul(left, right, lineno=lineno)
485 lineno = self.stream.current.lineno
486 return left
487
488 def parse_div(self):
489 lineno = self.stream.current.lineno
490 left = self.parse_floordiv()
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
491 while self.stream.current.type == 'div':
bd35772 @mitsuhiko More Python 3 support.
authored
492 next(self.stream)
82b3f3d @mitsuhiko first version of new parser
authored
493 right = self.parse_floordiv()
e791c2a @mitsuhiko added first working pieces of compiler
authored
494 left = nodes.Div(left, right, lineno=lineno)
82b3f3d @mitsuhiko first version of new parser
authored
495 lineno = self.stream.current.lineno
496 return left
497
498 def parse_floordiv(self):
499 lineno = self.stream.current.lineno
500 left = self.parse_mod()
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
501 while self.stream.current.type == 'floordiv':
bd35772 @mitsuhiko More Python 3 support.
authored
502 next(self.stream)
82b3f3d @mitsuhiko first version of new parser
authored
503 right = self.parse_mod()
504 left = nodes.FloorDiv(left, right, lineno=lineno)
505 lineno = self.stream.current.lineno
506 return left
507
508 def parse_mod(self):
509 lineno = self.stream.current.lineno
510 left = self.parse_pow()
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
511 while self.stream.current.type == 'mod':
bd35772 @mitsuhiko More Python 3 support.
authored
512 next(self.stream)
82b3f3d @mitsuhiko first version of new parser
authored
513 right = self.parse_pow()
514 left = nodes.Mod(left, right, lineno=lineno)
515 lineno = self.stream.current.lineno
516 return left
517
518 def parse_pow(self):
519 lineno = self.stream.current.lineno
520 left = self.parse_unary()
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
521 while self.stream.current.type == 'pow':
bd35772 @mitsuhiko More Python 3 support.
authored
522 next(self.stream)
82b3f3d @mitsuhiko first version of new parser
authored
523 right = self.parse_unary()
524 left = nodes.Pow(left, right, lineno=lineno)
525 lineno = self.stream.current.lineno
526 return left
527
613912d @mitsuhiko fixed an operator precedence error introduced in 2.5.2.
authored
528 def parse_unary(self, with_filter=True):
82b3f3d @mitsuhiko first version of new parser
authored
529 token_type = self.stream.current.type
530 lineno = self.stream.current.lineno
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
531 if token_type == 'sub':
bd35772 @mitsuhiko More Python 3 support.
authored
532 next(self.stream)
2ee64bc @mitsuhiko fixed an issue with unary operators having the wrong precendence.
authored
533 node = nodes.Neg(self.parse_unary(False), lineno=lineno)
534 elif token_type == 'add':
bd35772 @mitsuhiko More Python 3 support.
authored
535 next(self.stream)
2ee64bc @mitsuhiko fixed an issue with unary operators having the wrong precendence.
authored
536 node = nodes.Pos(self.parse_unary(False), lineno=lineno)
537 else:
538 node = self.parse_primary()
613912d @mitsuhiko fixed an operator precedence error introduced in 2.5.2.
authored
539 node = self.parse_postfix(node)
540 if with_filter:
541 node = self.parse_filter_expr(node)
2ee64bc @mitsuhiko fixed an issue with unary operators having the wrong precendence.
authored
542 return node
82b3f3d @mitsuhiko first version of new parser
authored
543
2ee64bc @mitsuhiko fixed an issue with unary operators having the wrong precendence.
authored
544 def parse_primary(self):
82b3f3d @mitsuhiko first version of new parser
authored
545 token = self.stream.current
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
546 if token.type == 'name':
9bb7e47 @mitsuhiko some more documentation updates and minor code cleanups. Additionall…
authored
547 if token.value in ('true', 'false', 'True', 'False'):
548 node = nodes.Const(token.value in ('true', 'True'),
549 lineno=token.lineno)
550 elif token.value in ('none', 'None'):
82b3f3d @mitsuhiko first version of new parser
authored
551 node = nodes.Const(None, lineno=token.lineno)
552 else:
553 node = nodes.Name(token.value, 'load', lineno=token.lineno)
bd35772 @mitsuhiko More Python 3 support.
authored
554 next(self.stream)
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
555 elif token.type == 'string':
bd35772 @mitsuhiko More Python 3 support.
authored
556 next(self.stream)
4778bda @mitsuhiko added support for implicit string literal concatenation
authored
557 buf = [token.value]
558 lineno = token.lineno
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
559 while self.stream.current.type == 'string':
4778bda @mitsuhiko added support for implicit string literal concatenation
authored
560 buf.append(self.stream.current.value)
bd35772 @mitsuhiko More Python 3 support.
authored
561 next(self.stream)
4778bda @mitsuhiko added support for implicit string literal concatenation
authored
562 node = nodes.Const(''.join(buf), lineno=lineno)
563 elif token.type in ('integer', 'float'):
bd35772 @mitsuhiko More Python 3 support.
authored
564 next(self.stream)
82b3f3d @mitsuhiko first version of new parser
authored
565 node = nodes.Const(token.value, lineno=token.lineno)
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
566 elif token.type == 'lparen':
bd35772 @mitsuhiko More Python 3 support.
authored
567 next(self.stream)
e614e88 @mitsuhiko implicit tuple expressions can no longer be totally empty.
authored
568 node = self.parse_tuple(explicit_parentheses=True)
82b3f3d @mitsuhiko first version of new parser
authored
569 self.stream.expect('rparen')
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
570 elif token.type == 'lbracket':
82b3f3d @mitsuhiko first version of new parser
authored
571 node = self.parse_list()
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
572 elif token.type == 'lbrace':
82b3f3d @mitsuhiko first version of new parser
authored
573 node = self.parse_dict()
574 else:
5dcb724 @mitsuhiko greatly improved error message reporting. This fixes #339
authored
575 self.fail("unexpected '%s'" % describe_token(token), token.lineno)
82b3f3d @mitsuhiko first version of new parser
authored
576 return node
577
fdf9530 @mitsuhiko added parsing code for "for item in seq recursive" and improved parse…
authored
578 def parse_tuple(self, simplified=False, with_condexpr=True,
e614e88 @mitsuhiko implicit tuple expressions can no longer be totally empty.
authored
579 extra_end_rules=None, explicit_parentheses=False):
023b5e9 @mitsuhiko First extension interface documentation and updates in that interface
authored
580 """Works like `parse_expression` but if multiple expressions are
581 delimited by a comma a :class:`~jinja2.nodes.Tuple` node is created.
582 This method could also return a regular expression instead of a tuple
583 if no commas where found.
584
585 The default parsing mode is a full tuple. If `simplified` is `True`
586 only names and literals are parsed. The `no_condexpr` parameter is
587 forwarded to :meth:`parse_expression`.
fdf9530 @mitsuhiko added parsing code for "for item in seq recursive" and improved parse…
authored
588
589 Because tuples do not require delimiters and may end in a bogus comma
590 an extra hint is needed that marks the end of a tuple. For example
591 for loops support tuples between `for` and `in`. In that case the
592 `extra_end_rules` is set to ``['name:in']``.
e614e88 @mitsuhiko implicit tuple expressions can no longer be totally empty.
authored
593
594 `explicit_parentheses` is true if the parsing was triggered by an
595 expression in parentheses. This is used to figure out if an empty
596 tuple is a valid expression or not.
82b3f3d @mitsuhiko first version of new parser
authored
597 """
598 lineno = self.stream.current.lineno
3d8b784 @mitsuhiko added loop filtering
authored
599 if simplified:
2ee64bc @mitsuhiko fixed an issue with unary operators having the wrong precendence.
authored
600 parse = self.parse_primary
09c002e @mitsuhiko added a function to parse assign targes and documented it for the ext…
authored
601 elif with_condexpr:
3d8b784 @mitsuhiko added loop filtering
authored
602 parse = self.parse_expression
09c002e @mitsuhiko added a function to parse assign targes and documented it for the ext…
authored
603 else:
604 parse = lambda: self.parse_expression(with_condexpr=False)
82b3f3d @mitsuhiko first version of new parser
authored
605 args = []
606 is_tuple = False
607 while 1:
608 if args:
609 self.stream.expect('comma')
fdf9530 @mitsuhiko added parsing code for "for item in seq recursive" and improved parse…
authored
610 if self.is_tuple_end(extra_end_rules):
82b3f3d @mitsuhiko first version of new parser
authored
611 break
612 args.append(parse())
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
613 if self.stream.current.type == 'comma':
b5124e6 @mitsuhiko moved trans extension from jinja2.i18n to jinja2.ext and fixed some m…
authored
614 is_tuple = True
615 else:
82b3f3d @mitsuhiko first version of new parser
authored
616 break
617 lineno = self.stream.current.lineno
e614e88 @mitsuhiko implicit tuple expressions can no longer be totally empty.
authored
618
619 if not is_tuple:
620 if args:
621 return args[0]
622
623 # if we don't have explicit parentheses, an empty tuple is
624 # not a valid expression. This would mean nothing (literally
625 # nothing) in the spot of an expression would be an empty
626 # tuple.
627 if not explicit_parentheses:
628 self.fail('Expected an expression, got \'%s\'' %
629 describe_token(self.stream.current))
630
82b3f3d @mitsuhiko first version of new parser
authored
631 return nodes.Tuple(args, 'load', lineno=lineno)
632
633 def parse_list(self):
634 token = self.stream.expect('lbracket')
635 items = []
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
636 while self.stream.current.type != 'rbracket':
82b3f3d @mitsuhiko first version of new parser
authored
637 if items:
638 self.stream.expect('comma')
639 if self.stream.current.type == 'rbracket':
640 break
641 items.append(self.parse_expression())
642 self.stream.expect('rbracket')
643 return nodes.List(items, lineno=token.lineno)
644
645 def parse_dict(self):
646 token = self.stream.expect('lbrace')
647 items = []
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
648 while self.stream.current.type != 'rbrace':
82b3f3d @mitsuhiko first version of new parser
authored
649 if items:
650 self.stream.expect('comma')
651 if self.stream.current.type == 'rbrace':
652 break
653 key = self.parse_expression()
654 self.stream.expect('colon')
655 value = self.parse_expression()
656 items.append(nodes.Pair(key, value, lineno=key.lineno))
657 self.stream.expect('rbrace')
be4ae24 @mitsuhiko fixed a few bugs from the unittests
authored
658 return nodes.Dict(items, lineno=token.lineno)
82b3f3d @mitsuhiko first version of new parser
authored
659
660 def parse_postfix(self, node):
661 while 1:
662 token_type = self.stream.current.type
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
663 if token_type == 'dot' or token_type == 'lbracket':
82b3f3d @mitsuhiko first version of new parser
authored
664 node = self.parse_subscript(node)
613912d @mitsuhiko fixed an operator precedence error introduced in 2.5.2.
authored
665 # calls are valid both after postfix expressions (getattr
666 # and getitem) as well as filters and tests
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
667 elif token_type == 'lparen':
82b3f3d @mitsuhiko first version of new parser
authored
668 node = self.parse_call(node)
613912d @mitsuhiko fixed an operator precedence error introduced in 2.5.2.
authored
669 else:
670 break
671 return node
672
673 def parse_filter_expr(self, node):
674 while 1:
675 token_type = self.stream.current.type
676 if token_type == 'pipe':
82b3f3d @mitsuhiko first version of new parser
authored
677 node = self.parse_filter(node)
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
678 elif token_type == 'name' and self.stream.current.value == 'is':
82b3f3d @mitsuhiko first version of new parser
authored
679 node = self.parse_test(node)
613912d @mitsuhiko fixed an operator precedence error introduced in 2.5.2.
authored
680 # calls are valid both after postfix expressions (getattr
681 # and getitem) as well as filters and tests
682 elif token_type == 'lparen':
683 node = self.parse_call(node)
82b3f3d @mitsuhiko first version of new parser
authored
684 else:
685 break
686 return node
687
688 def parse_subscript(self, node):
bd35772 @mitsuhiko More Python 3 support.
authored
689 token = next(self.stream)
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
690 if token.type == 'dot':
e791c2a @mitsuhiko added first working pieces of compiler
authored
691 attr_token = self.stream.current
bd35772 @mitsuhiko More Python 3 support.
authored
692 next(self.stream)
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
693 if attr_token.type == 'name':
6dc6f29 @mitsuhiko Improved attribute and item lookup by allowing template designers to …
authored
694 return nodes.Getattr(node, attr_token.value, 'load',
695 lineno=token.lineno)
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
696 elif attr_token.type != 'integer':
7f15ef8 @mitsuhiko improved exception system. now both name (load name) and filename ar…
authored
697 self.fail('expected name or number', attr_token.lineno)
e791c2a @mitsuhiko added first working pieces of compiler
authored
698 arg = nodes.Const(attr_token.value, lineno=attr_token.lineno)
6dc6f29 @mitsuhiko Improved attribute and item lookup by allowing template designers to …
authored
699 return nodes.Getitem(node, arg, 'load', lineno=token.lineno)
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
700 if token.type == 'lbracket':
82b3f3d @mitsuhiko first version of new parser
authored
701 args = []
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
702 while self.stream.current.type != 'rbracket':
82b3f3d @mitsuhiko first version of new parser
authored
703 if args:
704 self.stream.expect('comma')
705 args.append(self.parse_subscribed())
706 self.stream.expect('rbracket')
707 if len(args) == 1:
708 arg = args[0]
709 else:
ee2d3c4 @mitsuhiko Fixed a bug in the parser that made ``{{ foo[1, 2] }}`` impossible.
authored
710 arg = nodes.Tuple(args, 'load', lineno=token.lineno)
6dc6f29 @mitsuhiko Improved attribute and item lookup by allowing template designers to …
authored
711 return nodes.Getitem(node, arg, 'load', lineno=token.lineno)
712 self.fail('expected subscript expression', self.lineno)
82b3f3d @mitsuhiko first version of new parser
authored
713
714 def parse_subscribed(self):
715 lineno = self.stream.current.lineno
716
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
717 if self.stream.current.type == 'colon':
bd35772 @mitsuhiko More Python 3 support.
authored
718 next(self.stream)
82b3f3d @mitsuhiko first version of new parser
authored
719 args = [None]
720 else:
721 node = self.parse_expression()
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
722 if self.stream.current.type != 'colon':
82b3f3d @mitsuhiko first version of new parser
authored
723 return node
bd35772 @mitsuhiko More Python 3 support.
authored
724 next(self.stream)
82b3f3d @mitsuhiko first version of new parser
authored
725 args = [node]
726
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
727 if self.stream.current.type == 'colon':
82b3f3d @mitsuhiko first version of new parser
authored
728 args.append(None)
729 elif self.stream.current.type not in ('rbracket', 'comma'):
730 args.append(self.parse_expression())
731 else:
732 args.append(None)
733
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
734 if self.stream.current.type == 'colon':
bd35772 @mitsuhiko More Python 3 support.
authored
735 next(self.stream)
82b3f3d @mitsuhiko first version of new parser
authored
736 if self.stream.current.type not in ('rbracket', 'comma'):
737 args.append(self.parse_expression())
738 else:
739 args.append(None)
740 else:
741 args.append(None)
742
743 return nodes.Slice(lineno=lineno, *args)
744
745 def parse_call(self, node):
746 token = self.stream.expect('lparen')
747 args = []
748 kwargs = []
749 dyn_args = dyn_kwargs = None
750 require_comma = False
751
752 def ensure(expr):
753 if not expr:
7f15ef8 @mitsuhiko improved exception system. now both name (load name) and filename ar…
authored
754 self.fail('invalid syntax for function call expression',
755 token.lineno)
82b3f3d @mitsuhiko first version of new parser
authored
756
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
757 while self.stream.current.type != 'rparen':
82b3f3d @mitsuhiko first version of new parser
authored
758 if require_comma:
759 self.stream.expect('comma')
760 # support for trailing comma
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
761 if self.stream.current.type == 'rparen':
82b3f3d @mitsuhiko first version of new parser
authored
762 break
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
763 if self.stream.current.type == 'mul':
82b3f3d @mitsuhiko first version of new parser
authored
764 ensure(dyn_args is None and dyn_kwargs is None)
bd35772 @mitsuhiko More Python 3 support.
authored
765 next(self.stream)
82b3f3d @mitsuhiko first version of new parser
authored
766 dyn_args = self.parse_expression()
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
767 elif self.stream.current.type == 'pow':
82b3f3d @mitsuhiko first version of new parser
authored
768 ensure(dyn_kwargs is None)
bd35772 @mitsuhiko More Python 3 support.
authored
769 next(self.stream)
82b3f3d @mitsuhiko first version of new parser
authored
770 dyn_kwargs = self.parse_expression()
771 else:
772 ensure(dyn_args is None and dyn_kwargs is None)
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
773 if self.stream.current.type == 'name' and \
774 self.stream.look().type == 'assign':
82b3f3d @mitsuhiko first version of new parser
authored
775 key = self.stream.current.value
776 self.stream.skip(2)
0611e49 @mitsuhiko revamped jinja2 import system. the behavior is less confusing now, b…
authored
777 value = self.parse_expression()
778 kwargs.append(nodes.Keyword(key, value,
779 lineno=value.lineno))
82b3f3d @mitsuhiko first version of new parser
authored
780 else:
781 ensure(not kwargs)
782 args.append(self.parse_expression())
783
784 require_comma = True
785 self.stream.expect('rparen')
786
787 if node is None:
788 return args, kwargs, dyn_args, dyn_kwargs
789 return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs,
790 lineno=token.lineno)
791
fa865fb @mitsuhiko filter tag works now
authored
792 def parse_filter(self, node, start_inline=False):
793 while self.stream.current.type == 'pipe' or start_inline:
794 if not start_inline:
bd35772 @mitsuhiko More Python 3 support.
authored
795 next(self.stream)
82b3f3d @mitsuhiko first version of new parser
authored
796 token = self.stream.expect('name')
b9e7875 @mitsuhiko added support for dotted names in tests and filters
authored
797 name = token.value
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
798 while self.stream.current.type == 'dot':
bd35772 @mitsuhiko More Python 3 support.
authored
799 next(self.stream)
b9e7875 @mitsuhiko added support for dotted names in tests and filters
authored
800 name += '.' + self.stream.expect('name').value
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
801 if self.stream.current.type == 'lparen':
82b3f3d @mitsuhiko first version of new parser
authored
802 args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
803 else:
804 args = []
805 kwargs = []
806 dyn_args = dyn_kwargs = None
b9e7875 @mitsuhiko added support for dotted names in tests and filters
authored
807 node = nodes.Filter(node, name, args, kwargs, dyn_args,
d55ab53 @mitsuhiko nodes have access to environment now
authored
808 dyn_kwargs, lineno=token.lineno)
fa865fb @mitsuhiko filter tag works now
authored
809 start_inline = False
d55ab53 @mitsuhiko nodes have access to environment now
authored
810 return node
82b3f3d @mitsuhiko first version of new parser
authored
811
812 def parse_test(self, node):
bd35772 @mitsuhiko More Python 3 support.
authored
813 token = next(self.stream)
115de2e @mitsuhiko Jinja doesn't have keywords any longer. The reason for this radical …
authored
814 if self.stream.current.test('name:not'):
bd35772 @mitsuhiko More Python 3 support.
authored
815 next(self.stream)
82b3f3d @mitsuhiko first version of new parser
authored
816 negated = True
817 else:
818 negated = False
819 name = self.stream.expect('name').value
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
820 while self.stream.current.type == 'dot':
bd35772 @mitsuhiko More Python 3 support.
authored
821 next(self.stream)
b9e7875 @mitsuhiko added support for dotted names in tests and filters
authored
822 name += '.' + self.stream.expect('name').value
10f3ba2 @mitsuhiko loops and `tests` tests pass now
authored
823 dyn_args = dyn_kwargs = None
824 kwargs = []
272ca2a @aliafshar This commit makes the parser and lexer use regular string comparison …
aliafshar authored
825 if self.stream.current.type == 'lparen':
82b3f3d @mitsuhiko first version of new parser
authored
826 args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
827 elif self.stream.current.type in ('name', 'string', 'integer',
828 'float', 'lparen', 'lbracket',
e3290ea @mitsuhiko Fixed a broken unittest and fixed a bug that required multiple tests …
authored
829 'lbrace') and not \
830 self.stream.current.test_any('name:else', 'name:or',
831 'name:and'):
832 if self.stream.current.test('name:is'):
833 self.fail('You cannot chain multiple tests with is')
82b3f3d @mitsuhiko first version of new parser
authored
834 args = [self.parse_expression()]
835 else:
836 args = []
837 node = nodes.Test(node, name, args, kwargs, dyn_args,
838 dyn_kwargs, lineno=token.lineno)
839 if negated:
9a82205 @mitsuhiko improved undefined behavior
authored
840 node = nodes.Not(node, lineno=token.lineno)
82b3f3d @mitsuhiko first version of new parser
authored
841 return node
842
843 def subparse(self, end_tokens=None):
844 body = []
845 data_buffer = []
846 add_data = data_buffer.append
847
5dcb724 @mitsuhiko greatly improved error message reporting. This fixes #339
authored
848 if end_tokens is not None:
849 self._end_token_stack.append(end_tokens)
850
82b3f3d @mitsuhiko first version of new parser
authored
851 def flush_data():
852 if data_buffer:
853 lineno = data_buffer[0].lineno
854 body.append(nodes.Output(data_buffer[:], lineno=lineno))
855 del data_buffer[:]
856
5dcb724 @mitsuhiko greatly improved error message reporting. This fixes #339
authored
857 try:
858 while self.stream:
859 token = self.stream.current
860 if token.type == 'data':
861 if token.value:
862 add_data(nodes.TemplateData(token.value,
863 lineno=token.lineno))
864 next(self.stream)
865 elif token.type == 'variable_begin':
866 next(self.stream)
867 add_data(self.parse_tuple(with_condexpr=True))
868 self.stream.expect('variable_end')
869 elif token.type == 'block_begin':
870 flush_data()
871 next(self.stream)
872 if end_tokens is not None and \
873 self.stream.current.test_any(*end_tokens):
874 return body
875 rv = self.parse_statement()
876 if isinstance(rv, list):
877 body.extend(rv)
878 else:
879 body.append(rv)
880 self.stream.expect('block_end')
023b5e9 @mitsuhiko First extension interface documentation and updates in that interface
authored
881 else:
5dcb724 @mitsuhiko greatly improved error message reporting. This fixes #339
authored
882 raise AssertionError('internal parsing error')
883
884 flush_data()
885 finally:
886 if end_tokens is not None:
887 self._end_token_stack.pop()
82b3f3d @mitsuhiko first version of new parser
authored
888
889 return body
890
891 def parse(self):
892 """Parse the whole template into a `Template` node."""
d55ab53 @mitsuhiko nodes have access to environment now
authored
893 result = nodes.Template(self.subparse(), lineno=1)
894 result.set_environment(self.environment)
895 return result
Something went wrong with that request. Please try again.