diff options
author | floatdrop <floatdrop@yandex-team.ru> | 2022-02-10 16:47:15 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:47:15 +0300 |
commit | e63b84f1d39557d9e46ac380b1f388271894293c (patch) | |
tree | 338cdaff3fb027e030b847db66df06019a0e3149 /contrib/python/Jinja2/py2/jinja2/parser.py | |
parent | f60febb7ea449535e7b073c386c7ff0539637fc0 (diff) | |
download | ydb-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.py | 1278 |
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 |