loki.ir.transformer
Visitor classes for transforming the IR
Classes
|
An enriched |
|
A |
|
A |
|
Visitor class to rebuild the tree and replace nodes according to a mapper. |
- 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 attributeTransformer.rebuilt
provides a mapping from original to rebuilt nodes. Alternatively, withinplace
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)\) hassource=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 updatingTypedSymbol.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:
- 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
bymapper[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
bymapper[o]
, if it is in the mapper, otherwise its behaviour differs slightly from the defaultvisit_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 asScopedNode.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.
- 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 asScopedNode.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 fromstart
orstop
, 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 onestart
andstop
node, respectively.The sets
start
andstop
are to be understood in a Pythonic way, i.e.,start
nodes will be included in the result andstop
excluded.Important
When recursing down a tree, any
InternalNode
are only included in the tree if theMaskedTransformer
was switched on before visiting thatInternalNode
. 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 settingrequire_all_start
to True.Optionally, traversal can be terminated early with
greedy_stop
. If enabled, theMaskedTransformer
will stop completely to traverse the tree as soon as encountering a node fromstop
.Note
Enabling
require_all_start
andgreedy_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 alsoScopedNode
by default (i.e., it calls the parent constructor withrebuild_scopes=True
).- Parameters:
start ((iterable of)
Node
, optional) – Encountering a node fromstart
during traversal switches theMaskedTransformer
on and includes that node and all subsequently traversed nodes in the produced tree.stop ((iterable of)
Node
, optional) – Encountering a node fromstop
during traversal switches theMaskedTransformer
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 fromstart
.require_all_start (bool, optional) – Switch the
MaskedTransformer
on only after encountering all nodes fromstart
. By default, it is switched on after encountering any node fromstart
.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 fromstart
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.
- visit_object(o, **kwargs)
Return the object unchanged.
- visit_Node(o, **kwargs)
Handler for
Node
objects.It replaces
o
bymapper[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
bymapper[o]
, if it is in the mapper, otherwise its behaviour differs slightly from the defaultvisit_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 asScopedNode.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 encounteredInternalNode
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 theNestedMaskedTransformer
is active.
- visit_InternalNode(o, **kwargs)
Handler for
InternalNode
that are included in the tree as long as anybody
node is included.
- visit_Conditional(o, **kwargs)
Handler for
Conditional
to account for theelse_body
.Note
This removes the
Conditional
ifbody
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 thebodies
are empty. In that case,else_body
is returned (which can be empty, too).