# Path: jinja2/nodes.py
# class Impossible(Exception):
# class NodeType(type):
# class Node(object):
# class Stmt(Node):
# class Helper(Node):
# class Template(Node):
# class Output(Stmt):
# class Extends(Stmt):
# class For(Stmt):
# class If(Stmt):
# class Macro(Stmt):
# class CallBlock(Stmt):
# class FilterBlock(Stmt):
# class Block(Stmt):
# class Include(Stmt):
# class Import(Stmt):
# class FromImport(Stmt):
# class ExprStmt(Stmt):
# class Assign(Stmt):
# class Expr(Node):
# class BinExpr(Expr):
# class UnaryExpr(Expr):
# class Name(Expr):
# class Literal(Expr):
# class Const(Literal):
# class TemplateData(Literal):
# class Tuple(Literal):
# class List(Literal):
# class Dict(Literal):
# class Pair(Helper):
# class Keyword(Helper):
# class CondExpr(Expr):
# class Filter(Expr):
# class Test(Expr):
# class Call(Expr):
# class Getitem(Expr):
# class Getattr(Expr):
# class Slice(Expr):
# class Concat(Expr):
# class Compare(Expr):
# class Operand(Helper):
# class Mul(BinExpr):
# class Div(BinExpr):
# class FloorDiv(BinExpr):
# class Add(BinExpr):
# class Sub(BinExpr):
# class Mod(BinExpr):
# class Pow(BinExpr):
# class And(BinExpr):
# class Or(BinExpr):
# class Not(UnaryExpr):
# class Neg(UnaryExpr):
# class Pos(UnaryExpr):
# class EnvironmentAttribute(Expr):
# class ExtensionAttribute(Expr):
# class ImportedName(Expr):
# class InternalName(Expr):
# class MarkSafe(Expr):
# class ContextReference(Expr):
# class Continue(Stmt):
# class Break(Stmt):
#     def __new__(cls, name, bases, d):
#     def __init__(self, *fields, **attributes):
#     def iter_fields(self, exclude=None, only=None):
#     def iter_child_nodes(self, exclude=None, only=None):
#     def find(self, node_type):
#     def find_all(self, node_type):
#     def set_ctx(self, ctx):
#     def set_lineno(self, lineno, override=False):
#     def set_environment(self, environment):
#     def __eq__(self, other):
#     def __ne__(self, other):
#     def __repr__(self):
#     def as_const(self):
#     def can_assign(self):
#     def as_const(self):
#     def as_const(self):
#     def can_assign(self):
#     def as_const(self):
#     def from_untrusted(cls, value, lineno=None, environment=None):
#     def as_const(self):
#     def as_const(self):
#     def can_assign(self):
#     def as_const(self):
#     def as_const(self):
#     def as_const(self):
#     def as_const(self):
#     def as_const(self):
#     def as_const(self, obj=None):
#     def as_const(self):
#     def as_const(self):
#     def can_assign(self):
#     def as_const(self):
#     def can_assign(self):
#     def as_const(self):
#         def const(obj):
#     def as_const(self):
#     def as_const(self):
#     def as_const(self):
#     def as_const(self):
#     def __init__(self):
#     def as_const(self):
# def _failing_new(*args, **kwargs):
# 
# Path: jinja2/visitor.py
# class NodeTransformer(NodeVisitor):
#     """Walks the abstract syntax tree and allows modifications of nodes.
# 
#     The `NodeTransformer` will walk the AST and use the return value of the
#     visitor functions to replace or remove the old node.  If the return
#     value of the visitor function is `None` the node will be removed
#     from the previous location otherwise it's replaced with the return
#     value.  The return value may be the original node in which case no
#     replacement takes place.
#     """
# 
#     def generic_visit(self, node, *args, **kwargs):
#         for field, old_value in node.iter_fields():
#             if isinstance(old_value, list):
#                 new_values = []
#                 for value in old_value:
#                     if isinstance(value, Node):
#                         value = self.visit(value, *args, **kwargs)
#                         if value is None:
#                             continue
#                         elif not isinstance(value, Node):
#                             new_values.extend(value)
#                             continue
#                     new_values.append(value)
#                 old_value[:] = new_values
#             elif isinstance(old_value, Node):
#                 new_node = self.visit(old_value, *args, **kwargs)
#                 if new_node is None:
#                     delattr(node, field)
#                 else:
#                     setattr(node, field, new_node)
#         return node
# 
#     def visit_list(self, node, *args, **kwargs):
#         """As transformers may return lists in some places this method
#         can be used to enforce a list as return value.
#         """
#         rv = self.visit(node, *args, **kwargs)
#         if not isinstance(rv, list):
#             rv = [rv]
#         return rv


from jinja2 import nodes
from jinja2.visitor import NodeTransformer
    and modify the AST in place so that it should be easier to evaluate it.

    Because the AST does not contain all the scoping information and the
    compiler has to find that out, we cannot do all the optimizations we
    want.  For example loop unrolling doesn't work because unrolled loops would
    have a different scoping.

    The solution would be a second syntax tree that has the scoping rules stored.

    :copyright: Copyright 2008 by Christoph Hack, Armin Ronacher.
    :license: BSD.
"""


def optimize(node, environment):
    """The context hint can be used to perform an static optimization
    based on the context given."""
    optimizer = Optimizer(environment)
    return optimizer.visit(node)


class Optimizer(NodeTransformer):

    def __init__(self, environment):
        self.environment = environment

    def visit_If(self, node):
        """Eliminate dead code."""
        # do not optimize ifs that have a block inside so that it doesn't
        # break super().

# Path: jinja2/nodes.py
# class Impossible(Exception):
# class NodeType(type):
# class Node(object):
# class Stmt(Node):
# class Helper(Node):
# class Template(Node):
# class Output(Stmt):
# class Extends(Stmt):
# class For(Stmt):
# class If(Stmt):
# class Macro(Stmt):
# class CallBlock(Stmt):
# class FilterBlock(Stmt):
# class Block(Stmt):
# class Include(Stmt):
# class Import(Stmt):
# class FromImport(Stmt):
# class ExprStmt(Stmt):
# class Assign(Stmt):
# class Expr(Node):
# class BinExpr(Expr):
# class UnaryExpr(Expr):
# class Name(Expr):
# class Literal(Expr):
# class Const(Literal):
# class TemplateData(Literal):
# class Tuple(Literal):
# class List(Literal):
# class Dict(Literal):
# class Pair(Helper):
# class Keyword(Helper):
# class CondExpr(Expr):
# class Filter(Expr):
# class Test(Expr):
# class Call(Expr):
# class Getitem(Expr):
# class Getattr(Expr):
# class Slice(Expr):
# class Concat(Expr):
# class Compare(Expr):
# class Operand(Helper):
# class Mul(BinExpr):
# class Div(BinExpr):
# class FloorDiv(BinExpr):
# class Add(BinExpr):
# class Sub(BinExpr):
# class Mod(BinExpr):
# class Pow(BinExpr):
# class And(BinExpr):
# class Or(BinExpr):
# class Not(UnaryExpr):
# class Neg(UnaryExpr):
# class Pos(UnaryExpr):
# class EnvironmentAttribute(Expr):
# class ExtensionAttribute(Expr):
# class ImportedName(Expr):
# class InternalName(Expr):
# class MarkSafe(Expr):
# class ContextReference(Expr):
# class Continue(Stmt):
# class Break(Stmt):
#     def __new__(cls, name, bases, d):
#     def __init__(self, *fields, **attributes):
#     def iter_fields(self, exclude=None, only=None):
#     def iter_child_nodes(self, exclude=None, only=None):
#     def find(self, node_type):
#     def find_all(self, node_type):
#     def set_ctx(self, ctx):
#     def set_lineno(self, lineno, override=False):
#     def set_environment(self, environment):
#     def __eq__(self, other):
#     def __ne__(self, other):
#     def __repr__(self):
#     def as_const(self):
#     def can_assign(self):
#     def as_const(self):
#     def as_const(self):
#     def can_assign(self):
#     def as_const(self):
#     def from_untrusted(cls, value, lineno=None, environment=None):
#     def as_const(self):
#     def as_const(self):
#     def can_assign(self):
#     def as_const(self):
#     def as_const(self):
#     def as_const(self):
#     def as_const(self):
#     def as_const(self):
#     def as_const(self, obj=None):
#     def as_const(self):
#     def as_const(self):
#     def can_assign(self):
#     def as_const(self):
#     def can_assign(self):
#     def as_const(self):
#         def const(obj):
#     def as_const(self):
#     def as_const(self):
#     def as_const(self):
#     def as_const(self):
#     def __init__(self):
#     def as_const(self):
# def _failing_new(*args, **kwargs):
# 
# Path: jinja2/visitor.py
# class NodeTransformer(NodeVisitor):
#     """Walks the abstract syntax tree and allows modifications of nodes.
# 
#     The `NodeTransformer` will walk the AST and use the return value of the
#     visitor functions to replace or remove the old node.  If the return
#     value of the visitor function is `None` the node will be removed
#     from the previous location otherwise it's replaced with the return
#     value.  The return value may be the original node in which case no
#     replacement takes place.
#     """
# 
#     def generic_visit(self, node, *args, **kwargs):
#         for field, old_value in node.iter_fields():
#             if isinstance(old_value, list):
#                 new_values = []
#                 for value in old_value:
#                     if isinstance(value, Node):
#                         value = self.visit(value, *args, **kwargs)
#                         if value is None:
#                             continue
#                         elif not isinstance(value, Node):
#                             new_values.extend(value)
#                             continue
#                     new_values.append(value)
#                 old_value[:] = new_values
#             elif isinstance(old_value, Node):
#                 new_node = self.visit(old_value, *args, **kwargs)
#                 if new_node is None:
#                     delattr(node, field)
#                 else:
#                     setattr(node, field, new_node)
#         return node
# 
#     def visit_list(self, node, *args, **kwargs):
#         """As transformers may return lists in some places this method
#         can be used to enforce a list as return value.
#         """
#         rv = self.visit(node, *args, **kwargs)
#         if not isinstance(rv, list):
#             rv = [rv]
#         return rv


# Path: jinja2/optimizer.py
from jinja2 import nodes
from jinja2.visitor import NodeTransformer

    and modify the AST in place so that it should be easier to evaluate it.

    Because the AST does not contain all the scoping information and the
    compiler has to find that out, we cannot do all the optimizations we
    want.  For example loop unrolling doesn't work because unrolled loops would
    have a different scoping.

    The solution would be a second syntax tree that has the scoping rules stored.

    :copyright: Copyright 2008 by Christoph Hack, Armin Ronacher.
    :license: BSD.
"""


def optimize(node, environment):
    """The context hint can be used to perform an static optimization
    based on the context given."""
    optimizer = Optimizer(environment)
    return optimizer.visit(node)


class Optimizer(NodeTransformer):

    def __init__(self, environment):
        self.environment = environment

    def visit_If(self, node):
        """Eliminate dead code."""
        # do not optimize ifs that have a block inside so that it doesn't
        # break super().

        if node.find(nodes.Block) is not None: