# 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
    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:
            return self.generic_visit(node)
        try:
            val = self.visit(node.test).as_const()

# 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

    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:
            return self.generic_visit(node)
        try:
            val = self.visit(node.test).as_const()

        except nodes.Impossible: