38 '''This module contains the base class RegionTrans. All transformations which
39 act on a region of code (list of PSyIR nodes) sub-class this one.
46 import TransformationError
55 This abstract class is a base class for all transformations that act
56 on a list of nodes. It gives access to a validate function that
57 makes sure that the nodes in the list are in the same order as in
58 the original AST, no node is duplicated, and that all nodes have
59 the same parent. We also check that all nodes to be enclosed are
60 valid for this transformation - this requires that the sub-class
61 populate the `excluded_node_types` tuple.
66 excluded_node_types = ()
69 '''This is a helper function for region based transformations.
70 The parameter for any of those transformations is either a single
71 node, a schedule, or a list of nodes. This function converts this
72 into a list of nodes according to the parameter type. This function
73 will always return a copy, to avoid issues e.g. if a child list
74 of a node should be provided, and a transformation changes the order
75 in this list (which would then also change the order of the
78 :param nodes: can be a single node, a schedule or a list of nodes.
79 :type nodes: Union[:py:obj:`psyclone.psyir.nodes.Node`,
80 :py:obj:`psyclone.psyir.nodes.Schedule`,
81 List[:py:obj:`psyclone.psyir.nodes.Node`]
82 :param options: a dictionary with options for transformations.
83 :type options: Optional[Dict[str,Any]]
85 :returns: a list of nodes.
86 :rtype: List[:py:class:`psyclone.psyir.nodes.Node`]
88 :raises TransformationError: if the supplied parameter is neither a \
89 single Node, nor a Schedule, nor a list of Nodes.
92 if isinstance(nodes, list)
and \
93 all(isinstance(node, Node)
for node
in nodes):
97 if isinstance(nodes, Schedule):
100 return nodes.children[:]
101 if isinstance(nodes, Node):
105 arg_type = str(type(nodes))
106 raise TransformationError(f
"Error in {self.name}: "
107 f
"Argument must be a single Node in a "
108 f
"Schedule, a Schedule or a list of Nodes "
109 f
"in a Schedule but have been passed an "
110 f
"object of type: {arg_type}")
113 '''Checks that the nodes in node_list are valid for a region
116 :param nodes: can be a single node, a schedule or a list of nodes.
117 :type nodes: Union[:py:obj:`psyclone.psyir.nodes.Node`,
118 :py:obj:`psyclone.psyir.nodes.Schedule`,
119 List[:py:obj:`psyclone.psyir.nodes.Node`]
120 :param options: a dictionary with options for transformations.
121 :type options: Optional[Dict[str,Any]]
122 :param bool options["node-type-check"]: this flag controls if the \
123 type of the nodes enclosed in the region should be tested \
124 to avoid using unsupported nodes inside a region.
126 :raises TransformationError: if the nodes in the list are not \
127 in the original order in which they are in the AST, \
128 a node is duplicated or the nodes have different parents.
129 :raises TransformationError: if any of the nodes to be enclosed in \
130 the region are of an unsupported type.
131 :raises TransformationError: if the parent of the supplied Nodes is \
132 not a Schedule or a Directive.
133 :raises TransformationError: if the nodes are in a NEMO \
134 Schedule and the transformation acts on the child of a \
135 single-line If or Where statment.
136 :raises TransformationError: if the supplied options are not a \
148 if not isinstance(options, dict):
149 raise TransformationError(
150 f
"Transformation apply method options argument must be a "
151 f
"dictionary but found '{type(options).__name__}'.")
152 node_parent = node_list[0].parent
154 for child
in node_list:
155 if child.parent
is not node_parent:
156 raise TransformationError(
157 f
"Error in {self.name} transformation: supplied nodes "
158 f
"are not children of the same parent.")
159 if prev_position >= 0
and prev_position+1 != child.position:
160 raise TransformationError(
161 f
"Children are not consecutive children of one parent: "
162 f
"child '{child}' has position {child.position}, but "
163 f
"previous child had position {prev_position}.")
164 prev_position = child.position
167 if options.get(
"node-type-check",
True):
168 for child
in node_list:
171 flat_list = [item
for item
in child.walk(object, Kern)
172 if not isinstance(item, Schedule)]
173 for item
in flat_list:
175 raise TransformationError(
176 f
"Nodes of type '{type(item).__name__}' cannot be "
177 f
"enclosed by a {self.name} transformation")
184 if (len(node_list) > 1
and
185 any([isinstance(node, Schedule)
for node
in node_list])):
186 raise TransformationError(
187 "Cannot apply a transformation to multiple nodes when one "
188 "or more is a Schedule. Either target a single Schedule "
189 "or the children of a Schedule.")
194 if not isinstance(node_parent, Schedule)
and \
195 not isinstance(node_list[0], Schedule):
199 raise TransformationError(
200 "Cannot apply transformation to the immediate children of a "
201 "Loop/IfBlock unless it is to a single Schedule representing"
202 " the Loop/If/Else body.")