aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/Jinja2/py2/jinja2/parser.py
diff options
context:
space:
mode:
authorfloatdrop <floatdrop@yandex-team.ru>2022-02-10 16:47:15 +0300
committerDaniil Cherednik <dcherednik@yandex-team.ru>2022-02-10 16:47:15 +0300
commite63b84f1d39557d9e46ac380b1f388271894293c (patch)
tree338cdaff3fb027e030b847db66df06019a0e3149 /contrib/python/Jinja2/py2/jinja2/parser.py
parentf60febb7ea449535e7b073c386c7ff0539637fc0 (diff)
downloadydb-e63b84f1d39557d9e46ac380b1f388271894293c.tar.gz
Restoring authorship annotation for <floatdrop@yandex-team.ru>. Commit 1 of 2.
Diffstat (limited to 'contrib/python/Jinja2/py2/jinja2/parser.py')
-rw-r--r--contrib/python/Jinja2/py2/jinja2/parser.py1278
1 files changed, 639 insertions, 639 deletions
diff --git a/contrib/python/Jinja2/py2/jinja2/parser.py b/contrib/python/Jinja2/py2/jinja2/parser.py
index d5881066f7..55a6de25a8 100644
--- a/contrib/python/Jinja2/py2/jinja2/parser.py
+++ b/contrib/python/Jinja2/py2/jinja2/parser.py
@@ -1,4 +1,4 @@
-# -*- coding: utf-8 -*-
+# -*- coding: utf-8 -*-
"""Parse tokens from the lexer into nodes for the compiler."""
from . import nodes
from ._compat import imap
@@ -6,7 +6,7 @@ from .exceptions import TemplateAssertionError
from .exceptions import TemplateSyntaxError
from .lexer import describe_token
from .lexer import describe_token_expr
-
+
_statement_keywords = frozenset(
[
"for",
@@ -24,313 +24,313 @@ _statement_keywords = frozenset(
]
)
_compare_operators = frozenset(["eq", "ne", "lt", "lteq", "gt", "gteq"])
-
-_math_nodes = {
+
+_math_nodes = {
"add": nodes.Add,
"sub": nodes.Sub,
"mul": nodes.Mul,
"div": nodes.Div,
"floordiv": nodes.FloorDiv,
"mod": nodes.Mod,
-}
-
-
-class Parser(object):
+}
+
+
+class Parser(object):
"""This is the central parsing class Jinja uses. It's passed to
- extensions and can be used to parse expressions or statements.
- """
-
+ extensions and can be used to parse expressions or statements.
+ """
+
def __init__(self, environment, source, name=None, filename=None, state=None):
- self.environment = environment
- self.stream = environment._tokenize(source, name, filename, state)
- self.name = name
- self.filename = filename
- self.closed = False
- self.extensions = {}
- for extension in environment.iter_extensions():
- for tag in extension.tags:
- self.extensions[tag] = extension.parse
- self._last_identifier = 0
- self._tag_stack = []
- self._end_token_stack = []
-
- def fail(self, msg, lineno=None, exc=TemplateSyntaxError):
- """Convenience method that raises `exc` with the message, passed
- line number or last line number as well as the current name and
- filename.
- """
- if lineno is None:
- lineno = self.stream.current.lineno
- raise exc(msg, lineno, self.name, self.filename)
-
- def _fail_ut_eof(self, name, end_token_stack, lineno):
- expected = []
- for exprs in end_token_stack:
- expected.extend(imap(describe_token_expr, exprs))
- if end_token_stack:
+ self.environment = environment
+ self.stream = environment._tokenize(source, name, filename, state)
+ self.name = name
+ self.filename = filename
+ self.closed = False
+ self.extensions = {}
+ for extension in environment.iter_extensions():
+ for tag in extension.tags:
+ self.extensions[tag] = extension.parse
+ self._last_identifier = 0
+ self._tag_stack = []
+ self._end_token_stack = []
+
+ def fail(self, msg, lineno=None, exc=TemplateSyntaxError):
+ """Convenience method that raises `exc` with the message, passed
+ line number or last line number as well as the current name and
+ filename.
+ """
+ if lineno is None:
+ lineno = self.stream.current.lineno
+ raise exc(msg, lineno, self.name, self.filename)
+
+ def _fail_ut_eof(self, name, end_token_stack, lineno):
+ expected = []
+ for exprs in end_token_stack:
+ expected.extend(imap(describe_token_expr, exprs))
+ if end_token_stack:
currently_looking = " or ".join(
"'%s'" % describe_token_expr(expr) for expr in end_token_stack[-1]
)
- else:
- currently_looking = None
-
- if name is None:
+ else:
+ currently_looking = None
+
+ if name is None:
message = ["Unexpected end of template."]
- else:
+ else:
message = ["Encountered unknown tag '%s'." % name]
-
- if currently_looking:
- if name is not None and name in expected:
+
+ if currently_looking:
+ if name is not None and name in expected:
message.append(
"You probably made a nesting mistake. Jinja "
"is expecting this tag, but currently looking "
"for %s." % currently_looking
)
- else:
+ else:
message.append(
"Jinja was looking for the following tags: "
"%s." % currently_looking
)
-
- if self._tag_stack:
+
+ if self._tag_stack:
message.append(
"The innermost block that needs to be "
"closed is '%s'." % self._tag_stack[-1]
)
-
+
self.fail(" ".join(message), lineno)
-
- def fail_unknown_tag(self, name, lineno=None):
- """Called if the parser encounters an unknown tag. Tries to fail
- with a human readable error message that could help to identify
- the problem.
- """
- return self._fail_ut_eof(name, self._end_token_stack, lineno)
-
- def fail_eof(self, end_tokens=None, lineno=None):
- """Like fail_unknown_tag but for end of template situations."""
- stack = list(self._end_token_stack)
- if end_tokens is not None:
- stack.append(end_tokens)
- return self._fail_ut_eof(None, stack, lineno)
-
- def is_tuple_end(self, extra_end_rules=None):
- """Are we at the end of a tuple?"""
+
+ def fail_unknown_tag(self, name, lineno=None):
+ """Called if the parser encounters an unknown tag. Tries to fail
+ with a human readable error message that could help to identify
+ the problem.
+ """
+ return self._fail_ut_eof(name, self._end_token_stack, lineno)
+
+ def fail_eof(self, end_tokens=None, lineno=None):
+ """Like fail_unknown_tag but for end of template situations."""
+ stack = list(self._end_token_stack)
+ if end_tokens is not None:
+ stack.append(end_tokens)
+ return self._fail_ut_eof(None, stack, lineno)
+
+ def is_tuple_end(self, extra_end_rules=None):
+ """Are we at the end of a tuple?"""
if self.stream.current.type in ("variable_end", "block_end", "rparen"):
- return True
- elif extra_end_rules is not None:
- return self.stream.current.test_any(extra_end_rules)
- return False
-
- def free_identifier(self, lineno=None):
- """Return a new free identifier as :class:`~jinja2.nodes.InternalName`."""
- self._last_identifier += 1
- rv = object.__new__(nodes.InternalName)
+ return True
+ elif extra_end_rules is not None:
+ return self.stream.current.test_any(extra_end_rules)
+ return False
+
+ def free_identifier(self, lineno=None):
+ """Return a new free identifier as :class:`~jinja2.nodes.InternalName`."""
+ self._last_identifier += 1
+ rv = object.__new__(nodes.InternalName)
nodes.Node.__init__(rv, "fi%d" % self._last_identifier, lineno=lineno)
- return rv
-
- def parse_statement(self):
- """Parse a single statement."""
- token = self.stream.current
+ return rv
+
+ def parse_statement(self):
+ """Parse a single statement."""
+ token = self.stream.current
if token.type != "name":
self.fail("tag name expected", token.lineno)
- self._tag_stack.append(token.value)
- pop_tag = True
- try:
- if token.value in _statement_keywords:
+ self._tag_stack.append(token.value)
+ pop_tag = True
+ try:
+ if token.value in _statement_keywords:
return getattr(self, "parse_" + self.stream.current.value)()
if token.value == "call":
- return self.parse_call_block()
+ return self.parse_call_block()
if token.value == "filter":
- return self.parse_filter_block()
- ext = self.extensions.get(token.value)
- if ext is not None:
- return ext(self)
-
- # did not work out, remove the token we pushed by accident
- # from the stack so that the unknown tag fail function can
- # produce a proper error message.
- self._tag_stack.pop()
- pop_tag = False
- self.fail_unknown_tag(token.value, token.lineno)
- finally:
- if pop_tag:
- self._tag_stack.pop()
-
- def parse_statements(self, end_tokens, drop_needle=False):
- """Parse multiple statements into a list until one of the end tokens
- is reached. This is used to parse the body of statements as it also
- parses template data if appropriate. The parser checks first if the
- current token is a colon and skips it if there is one. Then it checks
- for the block end and parses until if one of the `end_tokens` is
- reached. Per default the active token in the stream at the end of
- the call is the matched end token. If this is not wanted `drop_needle`
- can be set to `True` and the end token is removed.
- """
- # the first token may be a colon for python compatibility
+ return self.parse_filter_block()
+ ext = self.extensions.get(token.value)
+ if ext is not None:
+ return ext(self)
+
+ # did not work out, remove the token we pushed by accident
+ # from the stack so that the unknown tag fail function can
+ # produce a proper error message.
+ self._tag_stack.pop()
+ pop_tag = False
+ self.fail_unknown_tag(token.value, token.lineno)
+ finally:
+ if pop_tag:
+ self._tag_stack.pop()
+
+ def parse_statements(self, end_tokens, drop_needle=False):
+ """Parse multiple statements into a list until one of the end tokens
+ is reached. This is used to parse the body of statements as it also
+ parses template data if appropriate. The parser checks first if the
+ current token is a colon and skips it if there is one. Then it checks
+ for the block end and parses until if one of the `end_tokens` is
+ reached. Per default the active token in the stream at the end of
+ the call is the matched end token. If this is not wanted `drop_needle`
+ can be set to `True` and the end token is removed.
+ """
+ # the first token may be a colon for python compatibility
self.stream.skip_if("colon")
-
- # in the future it would be possible to add whole code sections
- # by adding some sort of end of statement token and parsing those here.
+
+ # in the future it would be possible to add whole code sections
+ # by adding some sort of end of statement token and parsing those here.
self.stream.expect("block_end")
- result = self.subparse(end_tokens)
-
- # we reached the end of the template too early, the subparser
- # does not check for this, so we do that now
+ result = self.subparse(end_tokens)
+
+ # we reached the end of the template too early, the subparser
+ # does not check for this, so we do that now
if self.stream.current.type == "eof":
- self.fail_eof(end_tokens)
-
- if drop_needle:
- next(self.stream)
- return result
-
- def parse_set(self):
- """Parse an assign statement."""
- lineno = next(self.stream).lineno
- target = self.parse_assign_target(with_namespace=True)
+ self.fail_eof(end_tokens)
+
+ if drop_needle:
+ next(self.stream)
+ return result
+
+ def parse_set(self):
+ """Parse an assign statement."""
+ lineno = next(self.stream).lineno
+ target = self.parse_assign_target(with_namespace=True)
if self.stream.skip_if("assign"):
- expr = self.parse_tuple()
- return nodes.Assign(target, expr, lineno=lineno)
- filter_node = self.parse_filter(None)
+ expr = self.parse_tuple()
+ return nodes.Assign(target, expr, lineno=lineno)
+ filter_node = self.parse_filter(None)
body = self.parse_statements(("name:endset",), drop_needle=True)
- return nodes.AssignBlock(target, filter_node, body, lineno=lineno)
-
- def parse_for(self):
- """Parse a for loop."""
+ return nodes.AssignBlock(target, filter_node, body, lineno=lineno)
+
+ def parse_for(self):
+ """Parse a for loop."""
lineno = self.stream.expect("name:for").lineno
target = self.parse_assign_target(extra_end_rules=("name:in",))
self.stream.expect("name:in")
iter = self.parse_tuple(
with_condexpr=False, extra_end_rules=("name:recursive",)
)
- test = None
+ test = None
if self.stream.skip_if("name:if"):
- test = self.parse_expression()
+ test = self.parse_expression()
recursive = self.stream.skip_if("name:recursive")
body = self.parse_statements(("name:endfor", "name:else"))
if next(self.stream).value == "endfor":
- else_ = []
- else:
+ else_ = []
+ else:
else_ = self.parse_statements(("name:endfor",), drop_needle=True)
return nodes.For(target, iter, body, else_, test, recursive, lineno=lineno)
-
- def parse_if(self):
- """Parse an if construct."""
+
+ def parse_if(self):
+ """Parse an if construct."""
node = result = nodes.If(lineno=self.stream.expect("name:if").lineno)
- while 1:
- node.test = self.parse_tuple(with_condexpr=False)
+ while 1:
+ node.test = self.parse_tuple(with_condexpr=False)
node.body = self.parse_statements(("name:elif", "name:else", "name:endif"))
- node.elif_ = []
- node.else_ = []
- token = next(self.stream)
+ node.elif_ = []
+ node.else_ = []
+ token = next(self.stream)
if token.test("name:elif"):
- node = nodes.If(lineno=self.stream.current.lineno)
- result.elif_.append(node)
- continue
+ node = nodes.If(lineno=self.stream.current.lineno)
+ result.elif_.append(node)
+ continue
elif token.test("name:else"):
result.else_ = self.parse_statements(("name:endif",), drop_needle=True)
- break
- return result
-
- def parse_with(self):
- node = nodes.With(lineno=next(self.stream).lineno)
- targets = []
- values = []
+ break
+ return result
+
+ def parse_with(self):
+ node = nodes.With(lineno=next(self.stream).lineno)
+ targets = []
+ values = []
while self.stream.current.type != "block_end":
- if targets:
+ if targets:
self.stream.expect("comma")
- target = self.parse_assign_target()
+ target = self.parse_assign_target()
target.set_ctx("param")
- targets.append(target)
+ targets.append(target)
self.stream.expect("assign")
- values.append(self.parse_expression())
- node.targets = targets
- node.values = values
+ values.append(self.parse_expression())
+ node.targets = targets
+ node.values = values
node.body = self.parse_statements(("name:endwith",), drop_needle=True)
- return node
-
- def parse_autoescape(self):
- node = nodes.ScopedEvalContextModifier(lineno=next(self.stream).lineno)
+ return node
+
+ def parse_autoescape(self):
+ node = nodes.ScopedEvalContextModifier(lineno=next(self.stream).lineno)
node.options = [nodes.Keyword("autoescape", self.parse_expression())]
node.body = self.parse_statements(("name:endautoescape",), drop_needle=True)
- return nodes.Scope([node])
-
- def parse_block(self):
- node = nodes.Block(lineno=next(self.stream).lineno)
+ return nodes.Scope([node])
+
+ def parse_block(self):
+ node = nodes.Block(lineno=next(self.stream).lineno)
node.name = self.stream.expect("name").value
node.scoped = self.stream.skip_if("name:scoped")
-
- # common problem people encounter when switching from django
- # to jinja. we do not support hyphens in block names, so let's
- # raise a nicer error message in that case.
+
+ # common problem people encounter when switching from django
+ # to jinja. we do not support hyphens in block names, so let's
+ # raise a nicer error message in that case.
if self.stream.current.type == "sub":
self.fail(
"Block names in Jinja have to be valid Python "
"identifiers and may not contain hyphens, use an "
"underscore instead."
)
-
+
node.body = self.parse_statements(("name:endblock",), drop_needle=True)
self.stream.skip_if("name:" + node.name)
- return node
-
- def parse_extends(self):
- node = nodes.Extends(lineno=next(self.stream).lineno)
- node.template = self.parse_expression()
- return node
-
- def parse_import_context(self, node, default):
+ return node
+
+ def parse_extends(self):
+ node = nodes.Extends(lineno=next(self.stream).lineno)
+ node.template = self.parse_expression()
+ return node
+
+ def parse_import_context(self, node, default):
if self.stream.current.test_any(
"name:with", "name:without"
) and self.stream.look().test("name:context"):
node.with_context = next(self.stream).value == "with"
- self.stream.skip()
- else:
- node.with_context = default
- return node
-
- def parse_include(self):
- node = nodes.Include(lineno=next(self.stream).lineno)
- node.template = self.parse_expression()
+ self.stream.skip()
+ else:
+ node.with_context = default
+ return node
+
+ def parse_include(self):
+ node = nodes.Include(lineno=next(self.stream).lineno)
+ node.template = self.parse_expression()
if self.stream.current.test("name:ignore") and self.stream.look().test(
"name:missing"
):
- node.ignore_missing = True
- self.stream.skip(2)
- else:
- node.ignore_missing = False
- return self.parse_import_context(node, True)
-
- def parse_import(self):
- node = nodes.Import(lineno=next(self.stream).lineno)
- node.template = self.parse_expression()
+ node.ignore_missing = True
+ self.stream.skip(2)
+ else:
+ node.ignore_missing = False
+ return self.parse_import_context(node, True)
+
+ def parse_import(self):
+ node = nodes.Import(lineno=next(self.stream).lineno)
+ node.template = self.parse_expression()
self.stream.expect("name:as")
- node.target = self.parse_assign_target(name_only=True).name
- return self.parse_import_context(node, False)
-
- def parse_from(self):
- node = nodes.FromImport(lineno=next(self.stream).lineno)
- node.template = self.parse_expression()
+ node.target = self.parse_assign_target(name_only=True).name
+ return self.parse_import_context(node, False)
+
+ def parse_from(self):
+ node = nodes.FromImport(lineno=next(self.stream).lineno)
+ node.template = self.parse_expression()
self.stream.expect("name:import")
- node.names = []
-
- def parse_context():
+ node.names = []
+
+ def parse_context():
if self.stream.current.value in (
"with",
"without",
) and self.stream.look().test("name:context"):
node.with_context = next(self.stream).value == "with"
- self.stream.skip()
- return True
- return False
-
- while 1:
- if node.names:
+ self.stream.skip()
+ return True
+ return False
+
+ while 1:
+ if node.names:
self.stream.expect("comma")
if self.stream.current.type == "name":
- if parse_context():
- break
- target = self.parse_assign_target(name_only=True)
+ if parse_context():
+ break
+ target = self.parse_assign_target(name_only=True)
if target.name.startswith("_"):
self.fail(
"names starting with an underline can not be imported",
@@ -338,70 +338,70 @@ class Parser(object):
exc=TemplateAssertionError,
)
if self.stream.skip_if("name:as"):
- alias = self.parse_assign_target(name_only=True)
- node.names.append((target.name, alias.name))
- else:
- node.names.append(target.name)
+ alias = self.parse_assign_target(name_only=True)
+ node.names.append((target.name, alias.name))
+ else:
+ node.names.append(target.name)
if parse_context() or self.stream.current.type != "comma":
- break
- else:
+ break
+ else:
self.stream.expect("name")
if not hasattr(node, "with_context"):
- node.with_context = False
- return node
-
- def parse_signature(self, node):
- node.args = args = []
- node.defaults = defaults = []
+ node.with_context = False
+ return node
+
+ def parse_signature(self, node):
+ node.args = args = []
+ node.defaults = defaults = []
self.stream.expect("lparen")
while self.stream.current.type != "rparen":
- if args:
+ if args:
self.stream.expect("comma")
- arg = self.parse_assign_target(name_only=True)
+ arg = self.parse_assign_target(name_only=True)
arg.set_ctx("param")
if self.stream.skip_if("assign"):
- defaults.append(self.parse_expression())
- elif defaults:
+ defaults.append(self.parse_expression())
+ elif defaults:
self.fail("non-default argument follows default argument")
- args.append(arg)
+ args.append(arg)
self.stream.expect("rparen")
-
- def parse_call_block(self):
- node = nodes.CallBlock(lineno=next(self.stream).lineno)
+
+ def parse_call_block(self):
+ node = nodes.CallBlock(lineno=next(self.stream).lineno)
if self.stream.current.type == "lparen":
- self.parse_signature(node)
- else:
- node.args = []
- node.defaults = []
-
- node.call = self.parse_expression()
- if not isinstance(node.call, nodes.Call):
+ self.parse_signature(node)
+ else:
+ node.args = []
+ node.defaults = []
+
+ node.call = self.parse_expression()
+ if not isinstance(node.call, nodes.Call):
self.fail("expected call", node.lineno)
node.body = self.parse_statements(("name:endcall",), drop_needle=True)
- return node
-
- def parse_filter_block(self):
- node = nodes.FilterBlock(lineno=next(self.stream).lineno)
- node.filter = self.parse_filter(None, start_inline=True)
+ return node
+
+ def parse_filter_block(self):
+ node = nodes.FilterBlock(lineno=next(self.stream).lineno)
+ node.filter = self.parse_filter(None, start_inline=True)
node.body = self.parse_statements(("name:endfilter",), drop_needle=True)
- return node
-
- def parse_macro(self):
- node = nodes.Macro(lineno=next(self.stream).lineno)
- node.name = self.parse_assign_target(name_only=True).name
- self.parse_signature(node)
+ return node
+
+ def parse_macro(self):
+ node = nodes.Macro(lineno=next(self.stream).lineno)
+ node.name = self.parse_assign_target(name_only=True).name
+ self.parse_signature(node)
node.body = self.parse_statements(("name:endmacro",), drop_needle=True)
- return node
-
- def parse_print(self):
- node = nodes.Output(lineno=next(self.stream).lineno)
- node.nodes = []
+ return node
+
+ def parse_print(self):
+ node = nodes.Output(lineno=next(self.stream).lineno)
+ node.nodes = []
while self.stream.current.type != "block_end":
- if node.nodes:
+ if node.nodes:
self.stream.expect("comma")
- node.nodes.append(self.parse_expression())
- return node
-
+ node.nodes.append(self.parse_expression())
+ return node
+
def parse_assign_target(
self,
with_tuple=True,
@@ -410,195 +410,195 @@ class Parser(object):
with_namespace=False,
):
"""Parse an assignment target. As Jinja allows assignments to
- tuples, this function can parse all allowed assignment targets. Per
- default assignments to tuples are parsed, that can be disable however
- by setting `with_tuple` to `False`. If only assignments to names are
- wanted `name_only` can be set to `True`. The `extra_end_rules`
- parameter is forwarded to the tuple parsing function. If
- `with_namespace` is enabled, a namespace assignment may be parsed.
- """
+ tuples, this function can parse all allowed assignment targets. Per
+ default assignments to tuples are parsed, that can be disable however
+ by setting `with_tuple` to `False`. If only assignments to names are
+ wanted `name_only` can be set to `True`. The `extra_end_rules`
+ parameter is forwarded to the tuple parsing function. If
+ `with_namespace` is enabled, a namespace assignment may be parsed.
+ """
if with_namespace and self.stream.look().type == "dot":
token = self.stream.expect("name")
- next(self.stream) # dot
+ next(self.stream) # dot
attr = self.stream.expect("name")
- target = nodes.NSRef(token.value, attr.value, lineno=token.lineno)
- elif name_only:
+ target = nodes.NSRef(token.value, attr.value, lineno=token.lineno)
+ elif name_only:
token = self.stream.expect("name")
target = nodes.Name(token.value, "store", lineno=token.lineno)
- else:
- if with_tuple:
+ else:
+ if with_tuple:
target = self.parse_tuple(
simplified=True, extra_end_rules=extra_end_rules
)
- else:
- target = self.parse_primary()
+ else:
+ target = self.parse_primary()
target.set_ctx("store")
- if not target.can_assign():
+ if not target.can_assign():
self.fail(
"can't assign to %r" % target.__class__.__name__.lower(), target.lineno
)
- return target
-
- def parse_expression(self, with_condexpr=True):
- """Parse an expression. Per default all expressions are parsed, if
- the optional `with_condexpr` parameter is set to `False` conditional
- expressions are not parsed.
- """
- if with_condexpr:
- return self.parse_condexpr()
- return self.parse_or()
-
- def parse_condexpr(self):
- lineno = self.stream.current.lineno
- expr1 = self.parse_or()
+ return target
+
+ def parse_expression(self, with_condexpr=True):
+ """Parse an expression. Per default all expressions are parsed, if
+ the optional `with_condexpr` parameter is set to `False` conditional
+ expressions are not parsed.
+ """
+ if with_condexpr:
+ return self.parse_condexpr()
+ return self.parse_or()
+
+ def parse_condexpr(self):
+ lineno = self.stream.current.lineno
+ expr1 = self.parse_or()
while self.stream.skip_if("name:if"):
- expr2 = self.parse_or()
+ expr2 = self.parse_or()
if self.stream.skip_if("name:else"):
- expr3 = self.parse_condexpr()
- else:
- expr3 = None
- expr1 = nodes.CondExpr(expr2, expr1, expr3, lineno=lineno)
- lineno = self.stream.current.lineno
- return expr1
-
- def parse_or(self):
- lineno = self.stream.current.lineno
- left = self.parse_and()
+ expr3 = self.parse_condexpr()
+ else:
+ expr3 = None
+ expr1 = nodes.CondExpr(expr2, expr1, expr3, lineno=lineno)
+ lineno = self.stream.current.lineno
+ return expr1
+
+ def parse_or(self):
+ lineno = self.stream.current.lineno
+ left = self.parse_and()
while self.stream.skip_if("name:or"):
- right = self.parse_and()
- left = nodes.Or(left, right, lineno=lineno)
- lineno = self.stream.current.lineno
- return left
-
- def parse_and(self):
- lineno = self.stream.current.lineno
- left = self.parse_not()
+ right = self.parse_and()
+ left = nodes.Or(left, right, lineno=lineno)
+ lineno = self.stream.current.lineno
+ return left
+
+ def parse_and(self):
+ lineno = self.stream.current.lineno
+ left = self.parse_not()
while self.stream.skip_if("name:and"):
- right = self.parse_not()
- left = nodes.And(left, right, lineno=lineno)
- lineno = self.stream.current.lineno
- return left
-
- def parse_not(self):
+ right = self.parse_not()
+ left = nodes.And(left, right, lineno=lineno)
+ lineno = self.stream.current.lineno
+ return left
+
+ def parse_not(self):
if self.stream.current.test("name:not"):
- lineno = next(self.stream).lineno
- return nodes.Not(self.parse_not(), lineno=lineno)
- return self.parse_compare()
-
- def parse_compare(self):
- lineno = self.stream.current.lineno
- expr = self.parse_math1()
- ops = []
- while 1:
- token_type = self.stream.current.type
- if token_type in _compare_operators:
- next(self.stream)
- ops.append(nodes.Operand(token_type, self.parse_math1()))
+ lineno = next(self.stream).lineno
+ return nodes.Not(self.parse_not(), lineno=lineno)
+ return self.parse_compare()
+
+ def parse_compare(self):
+ lineno = self.stream.current.lineno
+ expr = self.parse_math1()
+ ops = []
+ while 1:
+ token_type = self.stream.current.type
+ if token_type in _compare_operators:
+ next(self.stream)
+ ops.append(nodes.Operand(token_type, self.parse_math1()))
elif self.stream.skip_if("name:in"):
ops.append(nodes.Operand("in", self.parse_math1()))
elif self.stream.current.test("name:not") and self.stream.look().test(
"name:in"
):
- self.stream.skip(2)
+ self.stream.skip(2)
ops.append(nodes.Operand("notin", self.parse_math1()))
- else:
- break
- lineno = self.stream.current.lineno
- if not ops:
- return expr
- return nodes.Compare(expr, ops, lineno=lineno)
-
- def parse_math1(self):
- lineno = self.stream.current.lineno
- left = self.parse_concat()
+ else:
+ break
+ lineno = self.stream.current.lineno
+ if not ops:
+ return expr
+ return nodes.Compare(expr, ops, lineno=lineno)
+
+ def parse_math1(self):
+ lineno = self.stream.current.lineno
+ left = self.parse_concat()
while self.stream.current.type in ("add", "sub"):
- cls = _math_nodes[self.stream.current.type]
- next(self.stream)
- right = self.parse_concat()
- left = cls(left, right, lineno=lineno)
- lineno = self.stream.current.lineno
- return left
-
- def parse_concat(self):
- lineno = self.stream.current.lineno
- args = [self.parse_math2()]
+ cls = _math_nodes[self.stream.current.type]
+ next(self.stream)
+ right = self.parse_concat()
+ left = cls(left, right, lineno=lineno)
+ lineno = self.stream.current.lineno
+ return left
+
+ def parse_concat(self):
+ lineno = self.stream.current.lineno
+ args = [self.parse_math2()]
while self.stream.current.type == "tilde":
- next(self.stream)
- args.append(self.parse_math2())
- if len(args) == 1:
- return args[0]
- return nodes.Concat(args, lineno=lineno)
-
- def parse_math2(self):
- lineno = self.stream.current.lineno
- left = self.parse_pow()
+ next(self.stream)
+ args.append(self.parse_math2())
+ if len(args) == 1:
+ return args[0]
+ return nodes.Concat(args, lineno=lineno)
+
+ def parse_math2(self):
+ lineno = self.stream.current.lineno
+ left = self.parse_pow()
while self.stream.current.type in ("mul", "div", "floordiv", "mod"):
- cls = _math_nodes[self.stream.current.type]
- next(self.stream)
- right = self.parse_pow()
- left = cls(left, right, lineno=lineno)
- lineno = self.stream.current.lineno
- return left
-
- def parse_pow(self):
- lineno = self.stream.current.lineno
- left = self.parse_unary()
+ cls = _math_nodes[self.stream.current.type]
+ next(self.stream)
+ right = self.parse_pow()
+ left = cls(left, right, lineno=lineno)
+ lineno = self.stream.current.lineno
+ return left
+
+ def parse_pow(self):
+ lineno = self.stream.current.lineno
+ left = self.parse_unary()
while self.stream.current.type == "pow":
- next(self.stream)
- right = self.parse_unary()
- left = nodes.Pow(left, right, lineno=lineno)
- lineno = self.stream.current.lineno
- return left
-
- def parse_unary(self, with_filter=True):
- token_type = self.stream.current.type
- lineno = self.stream.current.lineno
+ next(self.stream)
+ right = self.parse_unary()
+ left = nodes.Pow(left, right, lineno=lineno)
+ lineno = self.stream.current.lineno
+ return left
+
+ def parse_unary(self, with_filter=True):
+ token_type = self.stream.current.type
+ lineno = self.stream.current.lineno
if token_type == "sub":
- next(self.stream)
- node = nodes.Neg(self.parse_unary(False), lineno=lineno)
+ next(self.stream)
+ node = nodes.Neg(self.parse_unary(False), lineno=lineno)
elif token_type == "add":
- next(self.stream)
- node = nodes.Pos(self.parse_unary(False), lineno=lineno)
- else:
- node = self.parse_primary()
- node = self.parse_postfix(node)
- if with_filter:
- node = self.parse_filter_expr(node)
- return node
-
- def parse_primary(self):
- token = self.stream.current
+ next(self.stream)
+ node = nodes.Pos(self.parse_unary(False), lineno=lineno)
+ else:
+ node = self.parse_primary()
+ node = self.parse_postfix(node)
+ if with_filter:
+ node = self.parse_filter_expr(node)
+ return node
+
+ def parse_primary(self):
+ token = self.stream.current
if token.type == "name":
if token.value in ("true", "false", "True", "False"):
node = nodes.Const(token.value in ("true", "True"), lineno=token.lineno)
elif token.value in ("none", "None"):
- node = nodes.Const(None, lineno=token.lineno)
- else:
+ node = nodes.Const(None, lineno=token.lineno)
+ else:
node = nodes.Name(token.value, "load", lineno=token.lineno)
- next(self.stream)
+ next(self.stream)
elif token.type == "string":
- next(self.stream)
- buf = [token.value]
- lineno = token.lineno
+ next(self.stream)
+ buf = [token.value]
+ lineno = token.lineno
while self.stream.current.type == "string":
- buf.append(self.stream.current.value)
- next(self.stream)
+ buf.append(self.stream.current.value)
+ next(self.stream)
node = nodes.Const("".join(buf), lineno=lineno)
elif token.type in ("integer", "float"):
- next(self.stream)
- node = nodes.Const(token.value, lineno=token.lineno)
+ next(self.stream)
+ node = nodes.Const(token.value, lineno=token.lineno)
elif token.type == "lparen":
- next(self.stream)
- node = self.parse_tuple(explicit_parentheses=True)
+ next(self.stream)
+ node = self.parse_tuple(explicit_parentheses=True)
self.stream.expect("rparen")
elif token.type == "lbracket":
- node = self.parse_list()
+ node = self.parse_list()
elif token.type == "lbrace":
- node = self.parse_dict()
- else:
- self.fail("unexpected '%s'" % describe_token(token), token.lineno)
- return node
-
+ node = self.parse_dict()
+ else:
+ self.fail("unexpected '%s'" % describe_token(token), token.lineno)
+ return node
+
def parse_tuple(
self,
simplified=False,
@@ -606,261 +606,261 @@ class Parser(object):
extra_end_rules=None,
explicit_parentheses=False,
):
- """Works like `parse_expression` but if multiple expressions are
- delimited by a comma a :class:`~jinja2.nodes.Tuple` node is created.
- This method could also return a regular expression instead of a tuple
- if no commas where found.
-
- The default parsing mode is a full tuple. If `simplified` is `True`
- only names and literals are parsed. The `no_condexpr` parameter is
- forwarded to :meth:`parse_expression`.
-
- Because tuples do not require delimiters and may end in a bogus comma
- an extra hint is needed that marks the end of a tuple. For example
- for loops support tuples between `for` and `in`. In that case the
- `extra_end_rules` is set to ``['name:in']``.
-
- `explicit_parentheses` is true if the parsing was triggered by an
- expression in parentheses. This is used to figure out if an empty
- tuple is a valid expression or not.
- """
- lineno = self.stream.current.lineno
- if simplified:
- parse = self.parse_primary
- elif with_condexpr:
- parse = self.parse_expression
- else:
+ """Works like `parse_expression` but if multiple expressions are
+ delimited by a comma a :class:`~jinja2.nodes.Tuple` node is created.
+ This method could also return a regular expression instead of a tuple
+ if no commas where found.
+
+ The default parsing mode is a full tuple. If `simplified` is `True`
+ only names and literals are parsed. The `no_condexpr` parameter is
+ forwarded to :meth:`parse_expression`.
+
+ Because tuples do not require delimiters and may end in a bogus comma
+ an extra hint is needed that marks the end of a tuple. For example
+ for loops support tuples between `for` and `in`. In that case the
+ `extra_end_rules` is set to ``['name:in']``.
+
+ `explicit_parentheses` is true if the parsing was triggered by an
+ expression in parentheses. This is used to figure out if an empty
+ tuple is a valid expression or not.
+ """
+ lineno = self.stream.current.lineno
+ if simplified:
+ parse = self.parse_primary
+ elif with_condexpr:
+ parse = self.parse_expression
+ else:
def parse():
return self.parse_expression(with_condexpr=False)
- args = []
- is_tuple = False
- while 1:
- if args:
+ args = []
+ is_tuple = False
+ while 1:
+ if args:
self.stream.expect("comma")
- if self.is_tuple_end(extra_end_rules):
- break
- args.append(parse())
+ if self.is_tuple_end(extra_end_rules):
+ break
+ args.append(parse())
if self.stream.current.type == "comma":
- is_tuple = True
- else:
- break
- lineno = self.stream.current.lineno
-
- if not is_tuple:
- if args:
- return args[0]
-
- # if we don't have explicit parentheses, an empty tuple is
- # not a valid expression. This would mean nothing (literally
- # nothing) in the spot of an expression would be an empty
- # tuple.
- if not explicit_parentheses:
+ is_tuple = True
+ else:
+ break
+ lineno = self.stream.current.lineno
+
+ if not is_tuple:
+ if args:
+ return args[0]
+
+ # if we don't have explicit parentheses, an empty tuple is
+ # not a valid expression. This would mean nothing (literally
+ # nothing) in the spot of an expression would be an empty
+ # tuple.
+ if not explicit_parentheses:
self.fail(
"Expected an expression, got '%s'"
% describe_token(self.stream.current)
)
-
+
return nodes.Tuple(args, "load", lineno=lineno)
-
- def parse_list(self):
+
+ def parse_list(self):
token = self.stream.expect("lbracket")
- items = []
+ items = []
while self.stream.current.type != "rbracket":
- if items:
+ if items:
self.stream.expect("comma")
if self.stream.current.type == "rbracket":
- break
- items.append(self.parse_expression())
+ break
+ items.append(self.parse_expression())
self.stream.expect("rbracket")
- return nodes.List(items, lineno=token.lineno)
-
- def parse_dict(self):
+ return nodes.List(items, lineno=token.lineno)
+
+ def parse_dict(self):
token = self.stream.expect("lbrace")
- items = []
+ items = []
while self.stream.current.type != "rbrace":
- if items:
+ if items:
self.stream.expect("comma")
if self.stream.current.type == "rbrace":
- break
- key = self.parse_expression()
+ break
+ key = self.parse_expression()
self.stream.expect("colon")
- value = self.parse_expression()
- items.append(nodes.Pair(key, value, lineno=key.lineno))
+ value = self.parse_expression()
+ items.append(nodes.Pair(key, value, lineno=key.lineno))
self.stream.expect("rbrace")
- return nodes.Dict(items, lineno=token.lineno)
-
- def parse_postfix(self, node):
- while 1:
- token_type = self.stream.current.type
+ return nodes.Dict(items, lineno=token.lineno)
+
+ def parse_postfix(self, node):
+ while 1:
+ token_type = self.stream.current.type
if token_type == "dot" or token_type == "lbracket":
- node = self.parse_subscript(node)
- # calls are valid both after postfix expressions (getattr
- # and getitem) as well as filters and tests
+ node = self.parse_subscript(node)
+ # calls are valid both after postfix expressions (getattr
+ # and getitem) as well as filters and tests
elif token_type == "lparen":
- node = self.parse_call(node)
- else:
- break
- return node
-
- def parse_filter_expr(self, node):
- while 1:
- token_type = self.stream.current.type
+ node = self.parse_call(node)
+ else:
+ break
+ return node
+
+ def parse_filter_expr(self, node):
+ while 1:
+ token_type = self.stream.current.type
if token_type == "pipe":
- node = self.parse_filter(node)
+ node = self.parse_filter(node)
elif token_type == "name" and self.stream.current.value == "is":
- node = self.parse_test(node)
- # calls are valid both after postfix expressions (getattr
- # and getitem) as well as filters and tests
+ node = self.parse_test(node)
+ # calls are valid both after postfix expressions (getattr
+ # and getitem) as well as filters and tests
elif token_type == "lparen":
- node = self.parse_call(node)
- else:
- break
- return node
-
- def parse_subscript(self, node):
- token = next(self.stream)
+ node = self.parse_call(node)
+ else:
+ break
+ return node
+
+ def parse_subscript(self, node):
+ token = next(self.stream)
if token.type == "dot":
- attr_token = self.stream.current
- next(self.stream)
+ attr_token = self.stream.current
+ next(self.stream)
if attr_token.type == "name":
return nodes.Getattr(
node, attr_token.value, "load", lineno=token.lineno
)
elif attr_token.type != "integer":
self.fail("expected name or number", attr_token.lineno)
- arg = nodes.Const(attr_token.value, lineno=attr_token.lineno)
+ arg = nodes.Const(attr_token.value, lineno=attr_token.lineno)
return nodes.Getitem(node, arg, "load", lineno=token.lineno)
if token.type == "lbracket":
- args = []
+ args = []
while self.stream.current.type != "rbracket":
- if args:
+ if args:
self.stream.expect("comma")
- args.append(self.parse_subscribed())
+ args.append(self.parse_subscribed())
self.stream.expect("rbracket")
- if len(args) == 1:
- arg = args[0]
- else:
+ if len(args) == 1:
+ arg = args[0]
+ else:
arg = nodes.Tuple(args, "load", lineno=token.lineno)
return nodes.Getitem(node, arg, "load", lineno=token.lineno)
self.fail("expected subscript expression", token.lineno)
-
- def parse_subscribed(self):
- lineno = self.stream.current.lineno
-
+
+ def parse_subscribed(self):
+ lineno = self.stream.current.lineno
+
if self.stream.current.type == "colon":
- next(self.stream)
- args = [None]
- else:
- node = self.parse_expression()
+ next(self.stream)
+ args = [None]
+ else:
+ node = self.parse_expression()
if self.stream.current.type != "colon":
- return node
- next(self.stream)
- args = [node]
-
+ return node
+ next(self.stream)
+ args = [node]
+
if self.stream.current.type == "colon":
- args.append(None)
+ args.append(None)
elif self.stream.current.type not in ("rbracket", "comma"):
- args.append(self.parse_expression())
- else:
- args.append(None)
-
+ args.append(self.parse_expression())
+ else:
+ args.append(None)
+
if self.stream.current.type == "colon":
- next(self.stream)
+ next(self.stream)
if self.stream.current.type not in ("rbracket", "comma"):
- args.append(self.parse_expression())
- else:
- args.append(None)
- else:
- args.append(None)
-
- return nodes.Slice(lineno=lineno, *args)
-
- def parse_call(self, node):
+ args.append(self.parse_expression())
+ else:
+ args.append(None)
+ else:
+ args.append(None)
+
+ return nodes.Slice(lineno=lineno, *args)
+
+ def parse_call(self, node):
token = self.stream.expect("lparen")
- args = []
- kwargs = []
- dyn_args = dyn_kwargs = None
- require_comma = False
-
- def ensure(expr):
- if not expr:
+ args = []
+ kwargs = []
+ dyn_args = dyn_kwargs = None
+ require_comma = False
+
+ def ensure(expr):
+ if not expr:
self.fail("invalid syntax for function call expression", token.lineno)
-
+
while self.stream.current.type != "rparen":
- if require_comma:
+ if require_comma:
self.stream.expect("comma")
- # support for trailing comma
+ # support for trailing comma
if self.stream.current.type == "rparen":
- break
+ break
if self.stream.current.type == "mul":
- ensure(dyn_args is None and dyn_kwargs is None)
- next(self.stream)
- dyn_args = self.parse_expression()
+ ensure(dyn_args is None and dyn_kwargs is None)
+ next(self.stream)
+ dyn_args = self.parse_expression()
elif self.stream.current.type == "pow":
- ensure(dyn_kwargs is None)
- next(self.stream)
- dyn_kwargs = self.parse_expression()
- else:
+ ensure(dyn_kwargs is None)
+ next(self.stream)
+ dyn_kwargs = self.parse_expression()
+ else:
if (
self.stream.current.type == "name"
and self.stream.look().type == "assign"
):
# Parsing a kwarg
ensure(dyn_kwargs is None)
- key = self.stream.current.value
- self.stream.skip(2)
- value = self.parse_expression()
+ key = self.stream.current.value
+ self.stream.skip(2)
+ value = self.parse_expression()
kwargs.append(nodes.Keyword(key, value, lineno=value.lineno))
- else:
+ else:
# Parsing an arg
ensure(dyn_args is None and dyn_kwargs is None and not kwargs)
- args.append(self.parse_expression())
-
- require_comma = True
+ args.append(self.parse_expression())
+
+ require_comma = True
self.stream.expect("rparen")
-
- if node is None:
- return args, kwargs, dyn_args, dyn_kwargs
+
+ if node is None:
+ return args, kwargs, dyn_args, dyn_kwargs
return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno)
-
- def parse_filter(self, node, start_inline=False):
+
+ def parse_filter(self, node, start_inline=False):
while self.stream.current.type == "pipe" or start_inline:
- if not start_inline:
- next(self.stream)
+ if not start_inline:
+ next(self.stream)
token = self.stream.expect("name")
- name = token.value
+ name = token.value
while self.stream.current.type == "dot":
- next(self.stream)
+ next(self.stream)
name += "." + self.stream.expect("name").value
if self.stream.current.type == "lparen":
- args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
- else:
- args = []
- kwargs = []
- dyn_args = dyn_kwargs = None
+ args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
+ else:
+ args = []
+ kwargs = []
+ dyn_args = dyn_kwargs = None
node = nodes.Filter(
node, name, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno
)
- start_inline = False
- return node
-
- def parse_test(self, node):
- token = next(self.stream)
+ start_inline = False
+ return node
+
+ def parse_test(self, node):
+ token = next(self.stream)
if self.stream.current.test("name:not"):
- next(self.stream)
- negated = True
- else:
- negated = False
+ next(self.stream)
+ negated = True
+ else:
+ negated = False
name = self.stream.expect("name").value
while self.stream.current.type == "dot":
- next(self.stream)
+ next(self.stream)
name += "." + self.stream.expect("name").value
- dyn_args = dyn_kwargs = None
- kwargs = []
+ dyn_args = dyn_kwargs = None
+ kwargs = []
if self.stream.current.type == "lparen":
- args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
+ args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
elif self.stream.current.type in (
"name",
"string",
@@ -875,65 +875,65 @@ class Parser(object):
arg_node = self.parse_primary()
arg_node = self.parse_postfix(arg_node)
args = [arg_node]
- else:
- args = []
+ else:
+ args = []
node = nodes.Test(
node, name, args, kwargs, dyn_args, dyn_kwargs, lineno=token.lineno
)
- if negated:
- node = nodes.Not(node, lineno=token.lineno)
- return node
-
- def subparse(self, end_tokens=None):
- body = []
- data_buffer = []
- add_data = data_buffer.append
-
- if end_tokens is not None:
- self._end_token_stack.append(end_tokens)
-
- def flush_data():
- if data_buffer:
- lineno = data_buffer[0].lineno
- body.append(nodes.Output(data_buffer[:], lineno=lineno))
- del data_buffer[:]
-
- try:
- while self.stream:
- token = self.stream.current
+ if negated:
+ node = nodes.Not(node, lineno=token.lineno)
+ return node
+
+ def subparse(self, end_tokens=None):
+ body = []
+ data_buffer = []
+ add_data = data_buffer.append
+
+ if end_tokens is not None:
+ self._end_token_stack.append(end_tokens)
+
+ def flush_data():
+ if data_buffer:
+ lineno = data_buffer[0].lineno
+ body.append(nodes.Output(data_buffer[:], lineno=lineno))
+ del data_buffer[:]
+
+ try:
+ while self.stream:
+ token = self.stream.current
if token.type == "data":
- if token.value:
+ if token.value:
add_data(nodes.TemplateData(token.value, lineno=token.lineno))
- next(self.stream)
+ next(self.stream)
elif token.type == "variable_begin":
- next(self.stream)
- add_data(self.parse_tuple(with_condexpr=True))
+ next(self.stream)
+ add_data(self.parse_tuple(with_condexpr=True))
self.stream.expect("variable_end")
elif token.type == "block_begin":
- flush_data()
- next(self.stream)
+ flush_data()
+ next(self.stream)
if end_tokens is not None and self.stream.current.test_any(
*end_tokens
):
- return body
- rv = self.parse_statement()
- if isinstance(rv, list):
- body.extend(rv)
- else:
- body.append(rv)
+ return body
+ rv = self.parse_statement()
+ if isinstance(rv, list):
+ body.extend(rv)
+ else:
+ body.append(rv)
self.stream.expect("block_end")
- else:
+ else:
raise AssertionError("internal parsing error")
-
- flush_data()
- finally:
- if end_tokens is not None:
- self._end_token_stack.pop()
-
- return body
-
- def parse(self):
- """Parse the whole template into a `Template` node."""
- result = nodes.Template(self.subparse(), lineno=1)
- result.set_environment(self.environment)
- return result
+
+ flush_data()
+ finally:
+ if end_tokens is not None:
+ self._end_token_stack.pop()
+
+ return body
+
+ def parse(self):
+ """Parse the whole template into a `Template` node."""
+ result = nodes.Template(self.subparse(), lineno=1)
+ result.set_environment(self.environment)
+ return result