11cb0ef41Sopenharmony_ci# -*- coding: utf-8 -*-
21cb0ef41Sopenharmony_ci"""
31cb0ef41Sopenharmony_ci    jinja2.visitor
41cb0ef41Sopenharmony_ci    ~~~~~~~~~~~~~~
51cb0ef41Sopenharmony_ci
61cb0ef41Sopenharmony_ci    This module implements a visitor for the nodes.
71cb0ef41Sopenharmony_ci
81cb0ef41Sopenharmony_ci    :copyright: (c) 2017 by the Jinja Team.
91cb0ef41Sopenharmony_ci    :license: BSD.
101cb0ef41Sopenharmony_ci"""
111cb0ef41Sopenharmony_cifrom jinja2.nodes import Node
121cb0ef41Sopenharmony_ci
131cb0ef41Sopenharmony_ci
141cb0ef41Sopenharmony_ciclass NodeVisitor(object):
151cb0ef41Sopenharmony_ci    """Walks the abstract syntax tree and call visitor functions for every
161cb0ef41Sopenharmony_ci    node found.  The visitor functions may return values which will be
171cb0ef41Sopenharmony_ci    forwarded by the `visit` method.
181cb0ef41Sopenharmony_ci
191cb0ef41Sopenharmony_ci    Per default the visitor functions for the nodes are ``'visit_'`` +
201cb0ef41Sopenharmony_ci    class name of the node.  So a `TryFinally` node visit function would
211cb0ef41Sopenharmony_ci    be `visit_TryFinally`.  This behavior can be changed by overriding
221cb0ef41Sopenharmony_ci    the `get_visitor` function.  If no visitor function exists for a node
231cb0ef41Sopenharmony_ci    (return value `None`) the `generic_visit` visitor is used instead.
241cb0ef41Sopenharmony_ci    """
251cb0ef41Sopenharmony_ci
261cb0ef41Sopenharmony_ci    def get_visitor(self, node):
271cb0ef41Sopenharmony_ci        """Return the visitor function for this node or `None` if no visitor
281cb0ef41Sopenharmony_ci        exists for this node.  In that case the generic visit function is
291cb0ef41Sopenharmony_ci        used instead.
301cb0ef41Sopenharmony_ci        """
311cb0ef41Sopenharmony_ci        method = 'visit_' + node.__class__.__name__
321cb0ef41Sopenharmony_ci        return getattr(self, method, None)
331cb0ef41Sopenharmony_ci
341cb0ef41Sopenharmony_ci    def visit(self, node, *args, **kwargs):
351cb0ef41Sopenharmony_ci        """Visit a node."""
361cb0ef41Sopenharmony_ci        f = self.get_visitor(node)
371cb0ef41Sopenharmony_ci        if f is not None:
381cb0ef41Sopenharmony_ci            return f(node, *args, **kwargs)
391cb0ef41Sopenharmony_ci        return self.generic_visit(node, *args, **kwargs)
401cb0ef41Sopenharmony_ci
411cb0ef41Sopenharmony_ci    def generic_visit(self, node, *args, **kwargs):
421cb0ef41Sopenharmony_ci        """Called if no explicit visitor function exists for a node."""
431cb0ef41Sopenharmony_ci        for node in node.iter_child_nodes():
441cb0ef41Sopenharmony_ci            self.visit(node, *args, **kwargs)
451cb0ef41Sopenharmony_ci
461cb0ef41Sopenharmony_ci
471cb0ef41Sopenharmony_ciclass NodeTransformer(NodeVisitor):
481cb0ef41Sopenharmony_ci    """Walks the abstract syntax tree and allows modifications of nodes.
491cb0ef41Sopenharmony_ci
501cb0ef41Sopenharmony_ci    The `NodeTransformer` will walk the AST and use the return value of the
511cb0ef41Sopenharmony_ci    visitor functions to replace or remove the old node.  If the return
521cb0ef41Sopenharmony_ci    value of the visitor function is `None` the node will be removed
531cb0ef41Sopenharmony_ci    from the previous location otherwise it's replaced with the return
541cb0ef41Sopenharmony_ci    value.  The return value may be the original node in which case no
551cb0ef41Sopenharmony_ci    replacement takes place.
561cb0ef41Sopenharmony_ci    """
571cb0ef41Sopenharmony_ci
581cb0ef41Sopenharmony_ci    def generic_visit(self, node, *args, **kwargs):
591cb0ef41Sopenharmony_ci        for field, old_value in node.iter_fields():
601cb0ef41Sopenharmony_ci            if isinstance(old_value, list):
611cb0ef41Sopenharmony_ci                new_values = []
621cb0ef41Sopenharmony_ci                for value in old_value:
631cb0ef41Sopenharmony_ci                    if isinstance(value, Node):
641cb0ef41Sopenharmony_ci                        value = self.visit(value, *args, **kwargs)
651cb0ef41Sopenharmony_ci                        if value is None:
661cb0ef41Sopenharmony_ci                            continue
671cb0ef41Sopenharmony_ci                        elif not isinstance(value, Node):
681cb0ef41Sopenharmony_ci                            new_values.extend(value)
691cb0ef41Sopenharmony_ci                            continue
701cb0ef41Sopenharmony_ci                    new_values.append(value)
711cb0ef41Sopenharmony_ci                old_value[:] = new_values
721cb0ef41Sopenharmony_ci            elif isinstance(old_value, Node):
731cb0ef41Sopenharmony_ci                new_node = self.visit(old_value, *args, **kwargs)
741cb0ef41Sopenharmony_ci                if new_node is None:
751cb0ef41Sopenharmony_ci                    delattr(node, field)
761cb0ef41Sopenharmony_ci                else:
771cb0ef41Sopenharmony_ci                    setattr(node, field, new_node)
781cb0ef41Sopenharmony_ci        return node
791cb0ef41Sopenharmony_ci
801cb0ef41Sopenharmony_ci    def visit_list(self, node, *args, **kwargs):
811cb0ef41Sopenharmony_ci        """As transformers may return lists in some places this method
821cb0ef41Sopenharmony_ci        can be used to enforce a list as return value.
831cb0ef41Sopenharmony_ci        """
841cb0ef41Sopenharmony_ci        rv = self.visit(node, *args, **kwargs)
851cb0ef41Sopenharmony_ci        if not isinstance(rv, list):
861cb0ef41Sopenharmony_ci            rv = [rv]
871cb0ef41Sopenharmony_ci        return rv
88