# loki.ir.transformer module

Visitor classes for transforming the IR

class Transformer(mapper=None, invalidate_source=True, inplace=False, rebuild_scopes=False)

  Bases: `Visitor`

  Visitor class to rebuild the tree and replace nodes according to a mapper.

  Given a control flow tree \(T\) and a mapper from nodes in \(T\) to a set of new nodes \(L, M : N \rightarrow L\), build a new control flow tree \(T'\) where a node \(n \in N\) is replaced with \(M(n)\).

  Important

  The mapping is applied before visiting any children of a node.

  Removing nodes: In the special case in which \(M(n)\) is None, \(n\) is dropped from \(T'\).

  One to many mapping: In the special case in which \(M(n)\) is an iterable of nodes, all nodes in \(M(n)\) are inserted into the tuple containing \(n\).

  Warning

  Applying a `Transformer` to an IR tree rebuilds all nodes by default, which means individual nodes from the original IR are no longer found in the new tree. To update references to IR nodes, the attribute `Transformer.rebuilt` provides a mapping from original to rebuilt nodes. Alternatively, with `inplace` the mapping can be applied without rebuilding the tree, leaving existing references to individual IR nodes intact (as long as the mapping does not replace or remove them in the tree).

  Parameters:

    - mapper (dict) – The mapping \(M : N \rightarrow L\).

    - invalidate_source (bool, optional) – If set to True, this triggers invalidating the `source` property of all parent nodes of a node \(n\) if \(M(n)\) has `source=None`.

    - inplace (bool, optional) – If set to True, all updates are performed on existing `Node` objects, instead of rebuilding them, keeping the original tree intact.

    - rebuild_scopes (bool, optional) – If set to True, this will also rebuild `ScopedNode` in the IR. This requires updating `TypedSymbol.scope` properties, which is expensive and thus carried out only when explicitly requested.

  rebuilt

    After applying the `Transformer` to an IR, this contains a mapping \(n \rightarrow n'\) for every node of the original tree \(n \in T\) to the rebuilt nodes in the new tree \(n' \in T'\).

    Type:

      dict

  visit_object(o, **kwargs)

    Return the object unchanged.

  visit_tuple(o, **kwargs)

    Visit all elements in a tuple, injecting any one-to-many mappings.

  visit_list(o, **kwargs)

    Visit all elements in a tuple, injecting any one-to-many mappings.

  visit_Node(o, **kwargs)

    Handler for `Node` objects.

    It replaces `o` by `mapper[o]`, if it is in the mapper, otherwise visits all children before rebuilding the node.

  visit_ScopedNode(o, **kwargs)

    Handler for `ScopedNode` objects.

    It replaces `o` by `mapper[o]`, if it is in the mapper, otherwise its behaviour differs slightly from the default `visit_Node()` as it rebuilds the node first, then visits all children and then updates in-place the rebuilt node. This is to make sure upwards-pointing references to this scope (such as `ScopedNode.parent` properties) can be updated correctly.

    Additionally, it passes down the currently active scope in `kwargs` when recursing to children.

  visit(o, *args, **kwargs)

    Apply this `Transformer` to an IR tree.

    Parameters:

      - o (`Node`) – The node to visit.

      - *args – Optional arguments to pass to the visit methods.

      - **kwargs – Optional keyword arguments to pass to the visit methods.

    Returns:

      The rebuilt control flow tree.

    Return type:

      `Node` or tuple

class NestedTransformer(mapper=None, invalidate_source=True, inplace=False, rebuild_scopes=False)

  Bases: `Transformer`

  A `Transformer` that applies replacements in a depth-first fashion.

  visit_tuple(o, **kwargs)

    Visit all elements in a tuple, injecting any one-to-many mappings.

  visit_list(o, **kwargs)

    Visit all elements in a tuple, injecting any one-to-many mappings.

  visit_Node(o, **kwargs)

    Handler for `Node` objects.

    It visits all children before applying the `mapper`.

  visit_ScopedNode(o, **kwargs)

    Handler for `ScopedNode` objects.

    Its behaviour differs slightly from the default `visit_Node()` as it rebuilds the node first, then visits all children and then updates in-place the rebuilt node. This is to make sure upwards-pointing references to this scope (such as `ScopedNode.parent` properties) can be updated correctly.

    Additionally, it passes down the currently active scope in `kwargs` when recursing to children.

class MaskedTransformer(start=None, stop=None, active=False, require_all_start=False, greedy_stop=False, **kwargs)

  Bases: `Transformer`

  An enriched `Transformer` that can selectively include or exclude parts of the tree.

  For that `MaskedTransformer` is selectively switched on and off while traversing the tree. Nodes are only included in the new tree while it is “switched on”. The transformer is switched on or off when it encounters nodes from `start` or `stop`, respectively. This can be used, e.g., to extract everything between two nodes, or to create a copy of the entire tree but without all nodes between two nodes. Multiple such ranges can be defined by providing more than one `start` and `stop` node, respectively.

  The sets `start` and `stop` are to be understood in a Pythonic way, i.e., `start` nodes will be included in the result and `stop` excluded.

  Important

  When recursing down a tree, any `InternalNode` are only included in the tree if the `MaskedTransformer` was switched on before visiting that `InternalNode`. Importantly, this means the node is also not included if the transformer is switched on while traversing the internal node’s body. In such a case, only the body nodes that are included are retained.

  Optionally as a variant, switching on can also be delayed until all nodes from `start` have been encountered by setting `require_all_start` to True.

  Optionally, traversal can be terminated early with `greedy_stop`. If enabled, the `MaskedTransformer` will stop completely to traverse the tree as soon as encountering a node from `stop`.

  Note

  Enabling `require_all_start` and `greedy_stop` at the same time can be useful when you require the minimum number of nodes in-between multiple start and end nodes without knowing in which order they appear.

  Note

  `MaskedTransformer` rebuilds also `ScopedNode` by default (i.e., it calls the parent constructor with `rebuild_scopes=True`).

  Parameters:

    - start ((iterable of) `Node`, optional) – Encountering a node from `start` during traversal switches the `MaskedTransformer` on and includes that node and all subsequently traversed nodes in the produced tree.

    - stop ((iterable of) `Node`, optional) – Encountering a node from `stop` during traversal switches the `MaskedTransformer` off and excludes that node and all subsequently traversed nodes from the produced tree.

    - active (bool, optional) – Switch the `MaskedTransformer` on at the beginning of the traversal. By default, it is switched on only after encountering a node from `start`.

    - require_all_start (bool, optional) – Switch the `MaskedTransformer` on only after encountering all nodes from `start`. By default, it is switched on after encountering any node from `start`.

    - greedy_stop (bool, optional) – Stop traversing the tree as soon as any node from `stop` is encountered. By default, traversal continues but nodes are excluded from the new tree until a node from `start` is encountered.

    - **kwargs (optional) – Keyword arguments that are passed to the parent class constructor.

  visit(o, *args, **kwargs)

    Apply this `Transformer` to an IR tree.

    Parameters:

      - o (`Node`) – The node to visit.

      - *args – Optional arguments to pass to the visit methods.

      - **kwargs – Optional keyword arguments to pass to the visit methods.

    Returns:

      The rebuilt control flow tree.

    Return type:

      `Node` or tuple

  visit_object(o, **kwargs)

    Return the object unchanged.

  visit_Node(o, **kwargs)

    Handler for `Node` objects.

    It replaces `o` by `mapper[o]`, if it is in the mapper, otherwise visits all children before rebuilding the node.

  visit_ScopedNode(o, **kwargs)

    Handler for `ScopedNode` objects.

    It replaces `o` by `mapper[o]`, if it is in the mapper, otherwise its behaviour differs slightly from the default `visit_Node()` as it rebuilds the node first, then visits all children and then updates in-place the rebuilt node. This is to make sure upwards-pointing references to this scope (such as `ScopedNode.parent` properties) can be updated correctly.

    Additionally, it passes down the currently active scope in `kwargs` when recursing to children.

class NestedMaskedTransformer(start=None, stop=None, active=False, require_all_start=False, greedy_stop=False, **kwargs)

  Bases: `MaskedTransformer`

  A `MaskedTransformer` that retains parents for children that are included in the produced tree.

  In contrast to `MaskedTransformer`, any encountered `InternalNode` are included in the new tree as long as any of its children are included.

  visit_object(o, **kwargs)

    Return the object unchanged.

    Note that we need to keep them here regardless of the transformer being active because this handler takes care of properties for inactive parents that may still be retained if other children switch on the transformer.

  visit_LeafNode(o, **kwargs)

    Handler for `LeafNode` that are included in the tree if the `NestedMaskedTransformer` is active.

  visit_InternalNode(o, **kwargs)

    Handler for `InternalNode` that are included in the tree as long as any `body` node is included.

  visit_Conditional(o, **kwargs)

    Handler for `Conditional` to account for the `else_body`.

    Note

    This removes the `Conditional` if `body` is empty. In that case, `else_body` is returned (which can be empty, too).

  visit_MultiConditional(o, **kwargs)

    Handler for `MultiConditional` to account for all bodies.

    Note

    This removes the `MultiConditional` if all of the `bodies` are empty. In that case, `else_body` is returned (which can be empty, too).