diff env/lib/python3.7/site-packages/jinja2/parser.py @ 5:9b1c78e6ba9c draft default tip

"planemo upload commit 6c0a8142489327ece472c84e558c47da711a9142"
author shellac
date Mon, 01 Jun 2020 08:59:25 -0400
parents 79f47841a781
children
line wrap: on
line diff
--- a/env/lib/python3.7/site-packages/jinja2/parser.py	Thu May 14 16:47:39 2020 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,939 +0,0 @@
-# -*- coding: utf-8 -*-
-"""Parse tokens from the lexer into nodes for the compiler."""
-from . import nodes
-from ._compat import imap
-from .exceptions import TemplateAssertionError
-from .exceptions import TemplateSyntaxError
-from .lexer import describe_token
-from .lexer import describe_token_expr
-
-_statement_keywords = frozenset(
-    [
-        "for",
-        "if",
-        "block",
-        "extends",
-        "print",
-        "macro",
-        "include",
-        "from",
-        "import",
-        "set",
-        "with",
-        "autoescape",
-    ]
-)
-_compare_operators = frozenset(["eq", "ne", "lt", "lteq", "gt", "gteq"])
-
-_math_nodes = {
-    "add": nodes.Add,
-    "sub": nodes.Sub,
-    "mul": nodes.Mul,
-    "div": nodes.Div,
-    "floordiv": nodes.FloorDiv,
-    "mod": nodes.Mod,
-}
-
-
-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.
-    """
-
-    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:
-            currently_looking = " or ".join(
-                "'%s'" % describe_token_expr(expr) for expr in end_token_stack[-1]
-            )
-        else:
-            currently_looking = None
-
-        if name is None:
-            message = ["Unexpected end of template."]
-        else:
-            message = ["Encountered unknown tag '%s'." % name]
-
-        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:
-                message.append(
-                    "Jinja was looking for the following tags: "
-                    "%s." % currently_looking
-                )
-
-        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?"""
-        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)
-        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
-        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:
-                return getattr(self, "parse_" + self.stream.current.value)()
-            if token.value == "call":
-                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
-        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.
-        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
-        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)
-        if self.stream.skip_if("assign"):
-            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."""
-        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
-        if self.stream.skip_if("name:if"):
-            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_ = 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."""
-        node = result = nodes.If(lineno=self.stream.expect("name:if").lineno)
-        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)
-            if token.test("name:elif"):
-                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 = []
-        while self.stream.current.type != "block_end":
-            if targets:
-                self.stream.expect("comma")
-            target = self.parse_assign_target()
-            target.set_ctx("param")
-            targets.append(target)
-            self.stream.expect("assign")
-            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)
-        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)
-        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.
-        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):
-        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()
-        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()
-        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()
-        self.stream.expect("name:import")
-        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.expect("comma")
-            if self.stream.current.type == "name":
-                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",
-                        target.lineno,
-                        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)
-                if parse_context() or self.stream.current.type != "comma":
-                    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 = []
-        self.stream.expect("lparen")
-        while self.stream.current.type != "rparen":
-            if args:
-                self.stream.expect("comma")
-            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:
-                self.fail("non-default argument follows default argument")
-            args.append(arg)
-        self.stream.expect("rparen")
-
-    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.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)
-        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)
-        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 = []
-        while self.stream.current.type != "block_end":
-            if node.nodes:
-                self.stream.expect("comma")
-            node.nodes.append(self.parse_expression())
-        return node
-
-    def parse_assign_target(
-        self,
-        with_tuple=True,
-        name_only=False,
-        extra_end_rules=None,
-        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.
-        """
-        if with_namespace and self.stream.look().type == "dot":
-            token = self.stream.expect("name")
-            next(self.stream)  # dot
-            attr = self.stream.expect("name")
-            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:
-                target = self.parse_tuple(
-                    simplified=True, extra_end_rules=extra_end_rules
-                )
-            else:
-                target = self.parse_primary()
-            target.set_ctx("store")
-        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()
-        while self.stream.skip_if("name:if"):
-            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()
-        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()
-        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):
-        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()))
-            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)
-                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()
-        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()]
-        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()
-        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()
-        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
-        if token_type == "sub":
-            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
-        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.Name(token.value, "load", lineno=token.lineno)
-            next(self.stream)
-        elif token.type == "string":
-            next(self.stream)
-            buf = [token.value]
-            lineno = token.lineno
-            while self.stream.current.type == "string":
-                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)
-        elif token.type == "lparen":
-            next(self.stream)
-            node = self.parse_tuple(explicit_parentheses=True)
-            self.stream.expect("rparen")
-        elif token.type == "lbracket":
-            node = self.parse_list()
-        elif token.type == "lbrace":
-            node = self.parse_dict()
-        else:
-            self.fail("unexpected '%s'" % describe_token(token), token.lineno)
-        return node
-
-    def parse_tuple(
-        self,
-        simplified=False,
-        with_condexpr=True,
-        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:
-
-            def parse():
-                return self.parse_expression(with_condexpr=False)
-
-        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.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:
-                self.fail(
-                    "Expected an expression, got '%s'"
-                    % describe_token(self.stream.current)
-                )
-
-        return nodes.Tuple(args, "load", lineno=lineno)
-
-    def parse_list(self):
-        token = self.stream.expect("lbracket")
-        items = []
-        while self.stream.current.type != "rbracket":
-            if items:
-                self.stream.expect("comma")
-            if self.stream.current.type == "rbracket":
-                break
-            items.append(self.parse_expression())
-        self.stream.expect("rbracket")
-        return nodes.List(items, lineno=token.lineno)
-
-    def parse_dict(self):
-        token = self.stream.expect("lbrace")
-        items = []
-        while self.stream.current.type != "rbrace":
-            if items:
-                self.stream.expect("comma")
-            if self.stream.current.type == "rbrace":
-                break
-            key = self.parse_expression()
-            self.stream.expect("colon")
-            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
-            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
-            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
-            if token_type == "pipe":
-                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
-            elif token_type == "lparen":
-                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)
-            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)
-            return nodes.Getitem(node, arg, "load", lineno=token.lineno)
-        if token.type == "lbracket":
-            args = []
-            while self.stream.current.type != "rbracket":
-                if args:
-                    self.stream.expect("comma")
-                args.append(self.parse_subscribed())
-            self.stream.expect("rbracket")
-            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
-
-        if self.stream.current.type == "colon":
-            next(self.stream)
-            args = [None]
-        else:
-            node = self.parse_expression()
-            if self.stream.current.type != "colon":
-                return node
-            next(self.stream)
-            args = [node]
-
-        if self.stream.current.type == "colon":
-            args.append(None)
-        elif self.stream.current.type not in ("rbracket", "comma"):
-            args.append(self.parse_expression())
-        else:
-            args.append(None)
-
-        if self.stream.current.type == "colon":
-            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):
-        token = self.stream.expect("lparen")
-        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:
-                self.stream.expect("comma")
-                # support for trailing comma
-                if self.stream.current.type == "rparen":
-                    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()
-            elif self.stream.current.type == "pow":
-                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()
-                    kwargs.append(nodes.Keyword(key, value, lineno=value.lineno))
-                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
-        self.stream.expect("rparen")
-
-        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):
-        while self.stream.current.type == "pipe" or start_inline:
-            if not start_inline:
-                next(self.stream)
-            token = self.stream.expect("name")
-            name = token.value
-            while self.stream.current.type == "dot":
-                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
-            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)
-        if self.stream.current.test("name:not"):
-            next(self.stream)
-            negated = True
-        else:
-            negated = False
-        name = self.stream.expect("name").value
-        while self.stream.current.type == "dot":
-            next(self.stream)
-            name += "." + self.stream.expect("name").value
-        dyn_args = dyn_kwargs = None
-        kwargs = []
-        if self.stream.current.type == "lparen":
-            args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
-        elif self.stream.current.type in (
-            "name",
-            "string",
-            "integer",
-            "float",
-            "lparen",
-            "lbracket",
-            "lbrace",
-        ) and not self.stream.current.test_any("name:else", "name:or", "name:and"):
-            if self.stream.current.test("name:is"):
-                self.fail("You cannot chain multiple tests with is")
-            arg_node = self.parse_primary()
-            arg_node = self.parse_postfix(arg_node)
-            args = [arg_node]
-        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 token.type == "data":
-                    if token.value:
-                        add_data(nodes.TemplateData(token.value, lineno=token.lineno))
-                    next(self.stream)
-                elif token.type == "variable_begin":
-                    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)
-                    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)
-                    self.stream.expect("block_end")
-                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