39 ''' This module provides the fparser2 to PSyIR front-end, it follows a
40 Visitor Pattern to traverse relevant fparser2 nodes and contains the logic
41 to transform each node into the equivalent PSyIR representation.'''
43 from collections
import OrderedDict
44 from dataclasses
import dataclass, field
46 from typing
import Optional, List
48 from fparser.common.readfortran
import FortranStringReader
49 from fparser.two
import C99Preprocessor, Fortran2003, utils
50 from fparser.two.parser
import ParserFactory
51 from fparser.two.utils
import walk, BlockBase, StmtBase
56 ArrayMember, ArrayOfStructuresReference, ArrayReference, Assignment,
57 BinaryOperation, Call, CodeBlock, Container, Directive, FileContainer,
58 IfBlock, IntrinsicCall, Literal, Loop, Member, Node, Range,
59 Reference, Return, Routine, Schedule, StructureReference, UnaryOperation,
63 ArrayOfStructuresMixin)
65 ArgumentInterface, ArrayType, AutomaticInterface, CHARACTER_TYPE,
66 CommonBlockInterface, ContainerSymbol, DataSymbol, DataTypeSymbol,
67 DefaultModuleInterface, GenericInterfaceSymbol, ImportInterface,
68 INTEGER_TYPE, NoType, RoutineSymbol, ScalarType, StaticInterface,
69 StructureType, Symbol, SymbolError, SymbolTable, UnknownInterface,
70 UnresolvedInterface, UnresolvedType, UnsupportedFortranType,
83 FORTRAN_INTRINSICS = Fortran2003.Intrinsic_Name.function_names
86 TYPE_MAP_FROM_FORTRAN = {
"integer": ScalarType.Intrinsic.INTEGER,
87 "character": ScalarType.Intrinsic.CHARACTER,
88 "logical": ScalarType.Intrinsic.BOOLEAN,
89 "real": ScalarType.Intrinsic.REAL,
90 "double precision": ScalarType.Intrinsic.REAL}
93 VISIBILITY_MAP_FROM_FORTRAN = {
"public": Symbol.Visibility.PUBLIC,
94 "private": Symbol.Visibility.PRIVATE}
98 Fortran2003.Real_Literal_Constant: ScalarType.Intrinsic.REAL,
99 Fortran2003.Logical_Literal_Constant: ScalarType.Intrinsic.BOOLEAN,
100 Fortran2003.Char_Literal_Constant: ScalarType.Intrinsic.CHARACTER,
101 Fortran2003.Int_Literal_Constant: ScalarType.Intrinsic.INTEGER}
104 INTENT_MAPPING = {
"in": ArgumentInterface.Access.READ,
105 "out": ArgumentInterface.Access.WRITE,
106 "inout": ArgumentInterface.Access.READWRITE}
109 SUPPORTED_ROUTINE_PREFIXES = [
"ELEMENTAL",
"PURE",
"IMPURE"]
115 def _canonicalise_minmaxsum(arg_nodes, arg_names, node):
116 '''Canonicalise the arguments to any of the minval, maxval or sum
117 intrinsics. These three intrinsics can use the same function as
118 they have the same argument rules:
120 RESULT = [MINVAL, MAXVAL, SUM](ARRAY[, MASK])
121 RESULT = [MINVAL, MAXVAL, SUM](ARRAY, DIM[, MASK])
123 This function re-orderes and modifies the supplied arguments a
124 canonical form so that the PSyIR does not need to support the
125 different forms that are allowed in Fortran.
127 In general Fortran supports all arguments being named, all
128 arguments being positional and everything in-between, as long as
129 all named arguments follow all positional arguments.
131 For example, both SUM(A, DIM, MASK) and SUM(DIM=DIM, MASK=MASK,
132 ARRAY=A) are equivalent in Fortran.
134 The PSyIR canonical form has all required arguments as positional
135 arguments and all optional arguments as named arguments, which
136 would result in SUM(A, DIM=DIM, MASK=MASK) in this case. Note that
137 the canonical form does not constrain the order of named
140 In the case where the argument type needs to be determined in
141 order to create the PSyIR canonical form a CodeBlock is used (by
142 raising NotImplementedError).
144 :param arg_nodes: a list of fparser2 arguments.
145 :type arg_nodes: List[:py:class:`fparser.two.utils.Base`]
146 :param arg_names: a list of named-argument names.
147 :type arg_names: List[Union[str, None]]
148 :param node: the PSyIR Call or IntrinsicCall node.
149 :type node: :py:class:`psyclone.psyir.nodes.Call` or \
150 :py:class:`psyclone.psyir.nodes.IntrinsicCall`
152 :raises InternalError: if the array argument is not found in the \
154 :raises NotImplementedError: if there are two arguments and both \
155 of them are not named as the second argument could be a \
156 dimension or a mask and it is not currently possible to \
166 for name
in arg_names:
167 if name.lower() ==
"array":
172 f
"Invalid intrinsic arguments found. Expecting one "
173 f
"of the named arguments to be 'array', but found "
177 _ = arg_names.pop(arg_name_index)
178 arg_names.insert(0,
None)
180 node = arg_nodes.pop(arg_name_index)
181 arg_nodes.insert(0, node)
184 num_arg_names = len([arg_name
for arg_name
in arg_names
192 if len(arg_nodes) == 2
and num_arg_names == 0:
193 raise NotImplementedError(
194 f
"In '{node}' there are two arguments that are not named. "
195 f
"The second could be a dim or a mask so we need datatype "
196 f
"information to determine which and we do not determine "
197 f
"this information at the moment.")
202 if len(arg_nodes) == 3
and num_arg_names < 2:
207 arg_names[2] =
"mask"
210 def _first_type_match(nodelist, typekind):
211 '''Returns the first instance of the specified type in the given
214 :param list nodelist: list of fparser2 nodes.
215 :param type typekind: the fparser2 Type we are searching for.
217 :returns: the first instance of the specified type.
218 :rtype: instance of typekind
220 :raises ValueError: if the list does not contain an object of type \
224 for node
in nodelist:
225 if isinstance(node, typekind):
230 def _find_or_create_unresolved_symbol(location, name, scope_limit=None,
232 '''Returns the symbol with the given 'name' from a symbol table
233 associated with the 'location' node or one of its ancestors. If a
234 symbol is found then the type of the existing symbol is compared
235 with the specified 'symbol_type' parameter (passed as part of
236 '**kargs'). If it is not already an instance of this type, then
237 the symbol is specialised (in place).
239 If the symbol is not found then a new Symbol with the specified
240 visibility but of unresolved interface is created and inserted in the
241 most local SymbolTable that has a Routine or Container node as
244 The scope_limit variable further limits the symbol table search so
245 that the search through ancestor nodes stops when the scope_limit
246 node is reached i.e. ancestors of the scope_limit node are not
249 :param location: PSyIR node from which to operate.
250 :type location: :py:class:`psyclone.psyir.nodes.Node`
251 :param str name: the name of the symbol.
252 :param scope_limit: optional Node which limits the symbol
253 search space to the symbol tables of the nodes within the
254 given scope. If it is None (the default), the whole
255 scope (all symbol tables in ancestor nodes) is searched
256 otherwise ancestors of the scope_limit node are not
258 :type scope_limit: :py:class:`psyclone.psyir.nodes.Node` or
261 :returns: the matching symbol.
262 :rtype: :py:class:`psyclone.psyir.symbols.Symbol`
264 :raises TypeError: if the supplied scope_limit is not a Node.
265 :raises ValueError: if the supplied scope_limit node is not an
266 ancestor of the supplied node.
269 if not isinstance(location, Node):
271 f
"The location argument '{location}' provided to "
272 f
"_find_or_create_unresolved_symbol() is not of type `Node`.")
274 if scope_limit
is not None:
276 if not isinstance(scope_limit, Node):
278 f
"The scope_limit argument '{scope_limit}' provided to "
279 f
"_find_or_create_unresolved_symbol() is not of type `Node`.")
283 mynode = location.parent
284 while mynode
is not None:
285 if mynode
is scope_limit:
289 mynode = mynode.parent
294 f
"The scope_limit node '{scope_limit}' provided to "
295 f
"_find_or_create_unresolved_symbol() is not an ancestor of "
296 f
"this node '{location}'.")
299 sym = location.scope.symbol_table.lookup(name, scope_limit=scope_limit)
300 if "symbol_type" in kargs:
301 expected_type = kargs.pop(
"symbol_type")
302 if not isinstance(sym, expected_type):
305 sym.specialise(expected_type, **kargs)
317 symbol_table = location.scope.symbol_table
318 while symbol_table.node
and not isinstance(
319 symbol_table.node, (Routine, Container)):
320 symbol_table = symbol_table.parent_symbol_table()
327 return symbol_table.new_symbol(
328 name, interface=UnresolvedInterface(), **kargs)
331 def _find_or_create_psyclone_internal_cmp(node):
333 Utility routine to return a symbol of the generic psyclone comparison
334 interface. If the interface does not exist in the scope it first adds
335 the necessary code to the parent module.
337 :param node: location where the comparison interface is needed.
338 :type node: :py:class:`psyclone.psyir.nodes.Node`
339 :returns: the comparison interface symbol.
340 :rtype: :py:class:`psyclone.psyir.symbols.Symbol`
342 :raises NotImplementedError: if there is no ancestor module container
343 on which to add the interface code into.
346 return node.scope.symbol_table.lookup_with_tag(
"psyclone_internal_cmp")
348 container = node.ancestor(Container)
349 if container
and not isinstance(container, FileContainer):
352 name_interface = node.scope.symbol_table.next_available_name(
353 "psyclone_internal_cmp")
354 name_f_int = node.scope.symbol_table.next_available_name(
356 name_f_logical = node.scope.symbol_table.next_available_name(
357 "psyclone_cmp_logical")
358 name_f_char = node.scope.symbol_table.next_available_name(
360 fortran_reader = FortranReader()
361 dummymod = fortran_reader.psyir_from_source(f
'''
364 interface {name_interface}
365 procedure {name_f_int}
366 procedure {name_f_logical}
367 procedure {name_f_char}
368 end interface {name_interface}
369 private {name_interface}
370 private {name_f_int}, {name_f_logical}, {name_f_char}
372 logical pure function {name_f_int}(op1, op2)
373 integer, intent(in) :: op1, op2
374 {name_f_int} = op1.eq.op2
376 logical pure function {name_f_logical}(op1, op2)
377 logical, intent(in) :: op1, op2
378 {name_f_logical} = op1.eqv.op2
380 logical pure function {name_f_char}(op1, op2)
381 character(*), intent(in) :: op1, op2
382 {name_f_char} = op1.eq.op2
388 container.children.extend(dummymod.pop_all_children())
389 container.symbol_table.merge(dummymod.symbol_table)
390 symbol = container.symbol_table.lookup(name_interface)
392 container.symbol_table.tags_dict[
'psyclone_internal_cmp'] = symbol
395 raise NotImplementedError(
396 "Could not find the generic comparison interface and the scope does "
397 "not have an ancestor container in which to add it.")
400 def _check_args(array, dim):
401 '''Utility routine used by the _check_bound_is_full_extent and
402 _check_array_range_literal functions to check common arguments.
404 This routine is only in fparser2.py until #717 is complete as it
405 is used to check that array syntax in a where statement is for the
406 full extent of the dimension. Once #717 is complete this routine
409 :param array: the node to check.
410 :type array: :py:class:`pysclone.psyir.node.array`
411 :param int dim: the dimension index to use.
413 :raises TypeError: if the supplied arguments are of the wrong type.
414 :raises ValueError: if the value of the supplied dim argument is \
415 less than 1 or greater than the number of dimensions in the \
416 supplied array argument.
419 if not isinstance(array, ArrayMixin):
421 f
"method _check_args 'array' argument should be some sort of "
422 f
"array access (i.e. a sub-class of ArrayMixin) but found "
423 f
"'{type(array).__name__}'.")
425 if not isinstance(dim, int):
427 f
"method _check_args 'dim' argument should be an "
428 f
"int type but found '{type(dim).__name__}'.")
431 f
"method _check_args 'dim' argument should be at "
432 f
"least 1 but found {dim}.")
433 if dim > len(array.children):
435 f
"method _check_args 'dim' argument should be at most the number "
436 f
"of dimensions of the array ({len(array.children)}) but found "
441 if not isinstance(array.indices[dim-1], Range):
443 f
"method _check_args 'array' argument index '{dim-1}' should be a "
444 f
"Range type but found '{type(array.indices[dim-1]).__name__}'.")
447 def _is_bound_full_extent(array, dim, intrinsic):
448 '''A Fortran array section with a missing lower bound implies the
449 access starts at the first element and a missing upper bound
450 implies the access ends at the last element e.g. a(:,:)
451 accesses all elements of array a and is equivalent to
452 a(lbound(a,1):ubound(a,1),lbound(a,2):ubound(a,2)). The PSyIR
453 does not support the shorthand notation, therefore the lbound
454 and ubound operators are used in the PSyIR.
456 This utility function checks that shorthand lower or upper
457 bound Fortran code is captured as longhand lbound and/or
458 ubound functions as expected in the PSyIR.
460 This routine is only in fparser2.py until #717 is complete as it
461 is used to check that array syntax in a where statement is for the
462 full extent of the dimension. Once #717 is complete this routine
463 can be moved into fparser2_test.py as it is used there in a
466 :param array: the node to check.
467 :type array: :py:class:`pysclone.psyir.nodes.ArrayMixin`
468 :param int dim: the dimension index to use.
469 :param intrinsic: the intrinsic to check.
471 :py:class:`psyclone.psyir.nodes.IntrinsicCall.Intrinsic.LBOUND` |
472 :py:class:`psyclone.psyir.nodes.IntrinsicCall.Intrinsic.UBOUND`
474 :returns: True if the supplied array has the expected properties,
475 otherwise returns False.
478 :raises TypeError: if the supplied arguments are of the wrong type.
481 _check_args(array, dim)
483 if intrinsic == IntrinsicCall.Intrinsic.LBOUND:
485 elif intrinsic == IntrinsicCall.Intrinsic.UBOUND:
489 f
"'intrinsic' argument expected to be LBOUND or UBOUND but "
490 f
"found '{type(intrinsic).__name__}'.")
494 bound = array.indices[dim-1].children[index]
496 if not isinstance(bound, IntrinsicCall):
499 reference = bound.arguments[0]
500 literal = bound.arguments[1]
502 if bound.intrinsic != intrinsic:
505 if (
not isinstance(literal, Literal)
or
506 literal.datatype.intrinsic != ScalarType.Intrinsic.INTEGER
or
507 literal.value != str(dim)):
510 return isinstance(reference, Reference)
and array.is_same_array(reference)
513 def _is_array_range_literal(array, dim, index, value):
514 '''Utility function to check that the supplied array has an integer
515 literal at dimension index "dim" and range index "index" with
518 The step part of the range node has an integer literal with
521 This routine is only in fparser2.py until #717 is complete as it
522 is used to check that array syntax in a where statement is for the
523 full extent of the dimension. Once #717 is complete this routine
524 can be moved into fparser2_test.py as it is used there in a
527 :param array: the node to check.
528 :type array: :py:class:`pysclone.psyir.node.ArrayReference`
529 :param int dim: the dimension index to check.
530 :param int index: the index of the range to check (0 is the \
531 lower bound, 1 is the upper bound and 2 is the step).
532 :param int value: the expected value of the literal.
534 :raises NotImplementedError: if the supplied argument does not \
535 have the required properties.
537 :returns: True if the supplied array has the expected properties, \
538 otherwise returns False.
541 :raises TypeError: if the supplied arguments are of the wrong type.
542 :raises ValueError: if the index argument has an incorrect value.
545 _check_args(array, dim)
547 if not isinstance(index, int):
549 f
"method _check_array_range_literal 'index' argument should be an "
550 f
"int type but found '{type(index).__name__}'.")
552 if index < 0
or index > 2:
554 f
"method _check_array_range_literal 'index' argument should be "
555 f
"0, 1 or 2 but found {index}.")
557 if not isinstance(value, int):
559 f
"method _check_array_range_literal 'value' argument should be an "
560 f
"int type but found '{type(value).__name__}'.")
564 literal = array.children[dim-1].children[index]
566 if (isinstance(literal, Literal)
and
567 literal.datatype.intrinsic == ScalarType.Intrinsic.INTEGER
and
568 literal.value == str(value)):
573 def _is_range_full_extent(my_range):
574 '''Utility function to check whether a Range object is equivalent to a
575 ":" in Fortran array notation. The PSyIR representation of "a(:)"
576 is "a(lbound(a,1):ubound(a,1):1). Therefore, for array a index 1,
577 the lower bound is compared with "lbound(a,1)", the upper bound is
578 compared with "ubound(a,1)" and the step is compared with 1.
580 If everything is OK then this routine silently returns, otherwise
581 an exception is raised by one of the functions
582 (_check_bound_is_full_extent or _check_array_range_literal) called by this
585 This routine is only in fparser2.py until #717 is complete as it
586 is used to check that array syntax in a where statement is for the
587 full extent of the dimension. Once #717 is complete this routine
590 :param my_range: the Range node to check.
591 :type my_range: :py:class:`psyclone.psyir.node.Range`
595 array = my_range.parent
599 dim = array.children.index(my_range) + 1
601 is_lower = _is_bound_full_extent(
602 array, dim, IntrinsicCall.Intrinsic.LBOUND)
604 is_upper = _is_bound_full_extent(
605 array, dim, IntrinsicCall.Intrinsic.UBOUND)
607 is_step = _is_array_range_literal(array, dim, 2, 1)
608 return is_lower
and is_upper
and is_step
611 def _copy_full_base_reference(node):
613 Given the supplied node, creates a new node with the same access
614 apart from the final array access. Such a node is then suitable for use
615 as an argument to either e.g. LBOUND or UBOUND.
617 e.g. if `node` is an ArrayMember representing the inner access in
618 'grid%data(:)' then this routine will return a PSyIR node for
621 :param node: the array access. In the case of a structure, this \
622 must be the inner-most part of the access.
623 :type node: :py:class:`psyclone.psyir.nodes.Reference` or \
624 :py:class:`psyclone.psyir.nodes.Member`
626 :returns: the PSyIR for a suitable argument to either LBOUND or \
627 UBOUND applied to the supplied `node`.
628 :rtype: :py:class:`psyclone.psyir.nodes.Node`
630 :raises InternalError: if the supplied node is not an instance of \
631 either Reference or Member.
633 if isinstance(node, Reference):
634 return Reference(node.symbol)
636 if isinstance(node, Member):
642 parent_ref = node.ancestor(Reference, include_self=
True)
647 while hasattr(inner,
"member")
and inner
is not node:
652 arg = parent_ref.copy()
656 for _
in range(depth-1):
659 inner.children[0] = Member(node.name, inner)
663 f
"The supplied node must be an instance of either Reference "
664 f
"or Member but got '{type(node).__name__}'.")
667 def _kind_find_or_create(name, symbol_table):
669 Utility method that returns a Symbol representing the named KIND
670 parameter. If the supplied Symbol Table (or one of its ancestors)
671 does not contain an appropriate entry then one is created. If it does
672 contain a matching entry then it must be either a Symbol or a
675 If it is a DataSymbol then it must have a datatype of 'Integer',
676 'Unresolved' or 'Unsupported'. If it is Unresolved then the fact
677 that we now know that this Symbol represents a KIND parameter means we
678 can change the datatype to be 'integer' and mark it as constant.
680 If the existing symbol is a generic Symbol then it is replaced with
681 a new DataSymbol of type 'integer'.
683 :param str name: the name of the variable holding the KIND value.
684 :param symbol_table: the Symbol Table associated with the code being \
686 :type symbol_table: :py:class:`psyclone.psyir.symbols.SymbolTable`
688 :returns: the Symbol representing the KIND parameter.
689 :rtype: :py:class:`psyclone.psyir.symbols.DataSymbol`
691 :raises TypeError: if the symbol table already contains an entry for \
692 `name` but it is not an instance of Symbol or DataSymbol.
693 :raises TypeError: if the symbol table already contains a DataSymbol \
694 for `name` and its datatype is not 'Integer' or 'Unresolved'.
697 lower_name = name.lower()
700 kind_symbol = symbol_table.lookup(lower_name)
702 if type(kind_symbol)
is Symbol:
705 kind_symbol.specialise(DataSymbol, datatype=default_integer_type(),
707 elif isinstance(kind_symbol, DataSymbol):
709 if not (isinstance(kind_symbol.datatype,
710 (UnsupportedType, UnresolvedType))
or
711 (isinstance(kind_symbol.datatype, ScalarType)
and
712 kind_symbol.datatype.intrinsic ==
713 ScalarType.Intrinsic.INTEGER)):
715 f
"SymbolTable already contains a DataSymbol for variable "
716 f
"'{lower_name}' used as a kind parameter but it is not a "
717 f
"'Unresolved', 'Unsupported' or 'scalar Integer' type.")
721 if isinstance(kind_symbol.datatype, UnresolvedType):
722 kind_symbol.datatype = default_integer_type()
723 kind_symbol.is_constant =
True
726 f
"A symbol representing a kind parameter must be an instance "
727 f
"of either a Symbol or a DataSymbol. However, found an entry "
728 f
"of type '{type(kind_symbol).__name__}' for variable "
733 kind_symbol = _find_or_create_unresolved_symbol(
734 symbol_table.node, lower_name,
735 symbol_type=DataSymbol,
736 datatype=default_integer_type(),
737 visibility=symbol_table.default_visibility,
742 def default_precision(_):
743 '''Returns the default precision specified by the front end. This is
744 currently always set to undefined irrespective of the datatype but
745 could be read from a config file in the future. The unused
746 argument provides the name of the datatype. This name will allow a
747 future implementation of this method to choose different default
748 precisions for different datatypes if required.
750 There are alternative options for setting a default precision,
753 1) The back-end sets the default precision in a similar manner
755 2) A PSyIR transformation is used to set default precision.
757 This routine is primarily here as a placeholder and could be
758 replaced by an alternative solution, see issue #748.
760 :returns: the default precision for the supplied datatype name.
761 :rtype: :py:class:`psyclone.psyir.symbols.scalartype.Precision`
764 return ScalarType.Precision.UNDEFINED
767 def default_integer_type():
768 '''Returns the default integer datatype specified by the front end.
770 :returns: the default integer datatype.
771 :rtype: :py:class:`psyclone.psyir.symbols.ScalarType`
774 return ScalarType(ScalarType.Intrinsic.INTEGER,
775 default_precision(ScalarType.Intrinsic.INTEGER))
778 def default_real_type():
779 '''Returns the default real datatype specified by the front end.
781 :returns: the default real datatype.
782 :rtype: :py:class:`psyclone.psyir.symbols.ScalarType`
785 return ScalarType(ScalarType.Intrinsic.REAL,
786 default_precision(ScalarType.Intrinsic.REAL))
789 def get_literal_precision(fparser2_node, psyir_literal_parent):
790 '''Takes a Fortran2003 literal node as input and returns the appropriat
791 PSyIR precision type for that node. Adds a UnresolvedType DataSymbol in
792 the SymbolTable if the precision is given by an undefined symbol.
794 :param fparser2_node: the fparser2 literal node.
795 :type fparser2_node: :py:class:`Fortran2003.Real_Literal_Constant` or \
796 :py:class:`Fortran2003.Logical_Literal_Constant` or \
797 :py:class:`Fortran2003.Char_Literal_Constant` or \
798 :py:class:`Fortran2003.Int_Literal_Constant`
799 :param psyir_literal_parent: the PSyIR node that will be the \
800 parent of the PSyIR literal node that will be created from the \
801 fparser2 node information.
802 :type psyir_literal_parent: :py:class:`psyclone.psyir.nodes.Node`
804 :returns: the PSyIR Precision of this literal value.
805 :rtype: :py:class:`psyclone.psyir.symbols.DataSymbol`, int or \
806 :py:class:`psyclone.psyir.symbols.ScalarType.Precision`
808 :raises InternalError: if the arguments are of the wrong type.
809 :raises InternalError: if there's no symbol table associated with \
810 `psyir_literal_parent` or one of its ancestors.
813 if not isinstance(fparser2_node,
814 (Fortran2003.Real_Literal_Constant,
815 Fortran2003.Logical_Literal_Constant,
816 Fortran2003.Char_Literal_Constant,
817 Fortran2003.Int_Literal_Constant)):
819 f
"Unsupported literal type '{type(fparser2_node).__name__}' found "
820 f
"in get_literal_precision.")
821 if not isinstance(psyir_literal_parent, Node):
823 f
"Expecting argument psyir_literal_parent to be a PSyIR Node but "
824 f
"found '{type(psyir_literal_parent).__name__}' in "
825 f
"get_literal_precision.")
826 precision_name = fparser2_node.items[1]
827 if not precision_name:
829 if isinstance(fparser2_node, Fortran2003.Real_Literal_Constant):
830 precision_value = fparser2_node.items[0]
831 if "d" in precision_value.lower():
832 return ScalarType.Precision.DOUBLE
833 if "e" in precision_value.lower():
834 return ScalarType.Precision.SINGLE
837 data_name = CONSTANT_TYPE_MAP[type(fparser2_node)]
838 except KeyError
as err:
839 raise NotImplementedError(
840 f
"Could not process {type(fparser2_node).__name__}. Only "
841 f
"'real', 'integer', 'logical' and 'character' intrinsic "
842 f
"types are supported.")
from err
843 return default_precision(data_name)
846 return int(precision_name)
850 precision_name = precision_name.lower()
853 symbol_table = psyir_literal_parent.scope.symbol_table
854 except SymbolError
as err:
859 f
"Failed to find a symbol table to which to add the kind "
860 f
"symbol '{precision_name}'.")
from err
861 return _kind_find_or_create(precision_name, symbol_table)
864 def _process_routine_symbols(module_ast, symbol_table, visibility_map):
866 Examines the supplied fparser2 parse tree for a module and creates
867 RoutineSymbols for every routine (function or subroutine) that it
870 :param module_ast: fparser2 parse tree for module.
871 :type module_ast: :py:class:`fparser.two.Fortran2003.Program`
872 :param symbol_table: the SymbolTable to which to add the symbols.
873 :type symbol_table: :py:class:`psyclone.psyir.symbols.SymbolTable`
874 :param visibility_map: dict of symbol names with explicit visibilities.
875 :type visibility_map: Dict[str, \
876 :py:class:`psyclone.psyir.symbols.Symbol.Visibility`]
879 routines = walk(module_ast, (Fortran2003.Subroutine_Subprogram,
880 Fortran2003.Function_Subprogram))
885 type_map = {Fortran2003.Subroutine_Subprogram: NoType,
886 Fortran2003.Function_Subprogram: UnresolvedType}
888 for routine
in routines:
895 name = str(routine.children[0].children[1]).lower()
897 sym_type = type_map[type(routine)]()
899 vis = visibility_map.get(name, symbol_table.default_visibility)
901 prefix = routine.children[0].children[0]
903 for child
in prefix.children:
904 if isinstance(child, Fortran2003.Prefix_Spec):
905 if child.string ==
"PURE":
907 elif child.string ==
"IMPURE":
909 elif child.string ==
"ELEMENTAL":
912 rsymbol = RoutineSymbol(name, sym_type, visibility=vis,
913 is_pure=is_pure, is_elemental=is_elemental,
914 interface=DefaultModuleInterface())
915 symbol_table.add(rsymbol)
918 def _process_access_spec(attr):
920 Converts from an fparser2 Access_Spec node to a PSyIR visibility.
922 :param attr: the fparser2 AST node to process.
923 :type attr: :py:class:`fparser.two.Fortran2003.Access_Spec`
925 :return: the PSyIR visibility corresponding to the access spec.
926 :rtype: :py:class:`psyclone.psyir.Symbol.Visibility`
928 :raises InternalError: if an invalid access specification is found.
932 return VISIBILITY_MAP_FROM_FORTRAN[attr.string.lower()]
933 except KeyError
as err:
934 raise InternalError(f
"Unexpected Access Spec attribute "
935 f
"'{attr}'.")
from err
938 def _create_struct_reference(parent, base_ref, base_symbol, members,
941 Utility to create a StructureReference or ArrayOfStructuresReference. Any
942 PSyIR nodes in the supplied lists of members and indices are copied
943 when making the new node.
945 :param parent: Parent node of the PSyIR node we are constructing.
946 :type parent: :py:class:`psyclone.psyir.nodes.Node`
947 :param type base_ref: the type of Reference to create.
948 :param base_symbol: the Symbol that the reference is to.
949 :type base_symbol: :py:class:`psyclone.psyir.symbols.Symbol`
950 :param members: the component(s) of the structure that are being accessed.\
951 Any components that are array references must provide the name of the \
952 array and a list of DataNodes describing which part of it is accessed.
953 :type members: list of str or 2-tuples containing (str, \
954 list of nodes describing array access)
955 :param indices: a list of Nodes describing the array indices for \
956 the base reference (if any).
957 :type indices: list of :py:class:`psyclone.psyir.nodes.Node`
959 :raises InternalError: if any element in the `members` list is not a \
960 str or tuple or if `indices` are supplied for a StructureReference \
961 or *not* supplied for an ArrayOfStructuresReference.
962 :raises NotImplementedError: if `base_ref` is not a StructureReference or \
963 an ArrayOfStructuresReference.
969 for member
in members:
970 if isinstance(member, str):
971 new_members.append(member)
972 elif isinstance(member, tuple):
974 new_members.append((member[0], [kid.copy()
for kid
in member[1]]))
977 f
"List of members must contain only strings or tuples "
978 f
"but found entry of type '{type(member).__name__}'")
979 if base_ref
is StructureReference:
982 f
"Creating a StructureReference but array indices have been "
983 f
"supplied ({indices}) which makes no sense.")
984 return base_ref.create(base_symbol, new_members, parent=parent)
985 if base_ref
is ArrayOfStructuresReference:
988 "Cannot create an ArrayOfStructuresReference without one or "
989 "more index expressions but the 'indices' argument is empty.")
990 return base_ref.create(base_symbol, [idx.copy()
for idx
in indices],
991 new_members, parent=parent)
993 raise NotImplementedError(
994 f
"Cannot create structure reference for type '{base_ref}' - expected "
995 f
"either StructureReference or ArrayOfStructuresReference.")
998 def _get_arg_names(node_list):
999 '''Utility function that given an fparser2 argument list returns two
1000 separate lists, one with the arguments themselves and another with
1003 :param node_list: a list of fparser2 argument nodes which could \
1004 be positional or named.
1005 :type node_list: List[:py:class:`fparser.two.utils.Base`]
1007 :returns: a list of fparser2 arguments with any name \
1008 information and a separate list of named argument names.
1009 :rtype: Tuple[List[:py:class:`fparser.two.utils.Base`], \
1010 List[Union[str, None]]
1015 for node
in node_list:
1016 if isinstance(node, Fortran2003.Actual_Arg_Spec):
1017 arg_names.append(node.children[0].string)
1018 arg_nodes.append(node.children[1])
1020 arg_names.append(
None)
1021 arg_nodes.append(node)
1022 return arg_nodes, arg_names
1027 Class to encapsulate the functionality for processing the fparser2 AST and
1028 convert the nodes to PSyIR.
1031 unary_operators = OrderedDict([
1032 (
'+', UnaryOperation.Operator.PLUS),
1033 (
'-', UnaryOperation.Operator.MINUS),
1034 (
'.not.', UnaryOperation.Operator.NOT)])
1036 binary_operators = OrderedDict([
1037 (
'+', BinaryOperation.Operator.ADD),
1038 (
'-', BinaryOperation.Operator.SUB),
1039 (
'*', BinaryOperation.Operator.MUL),
1040 (
'/', BinaryOperation.Operator.DIV),
1041 (
'**', BinaryOperation.Operator.POW),
1042 (
'==', BinaryOperation.Operator.EQ),
1043 (
'.eq.', BinaryOperation.Operator.EQ),
1044 (
'.eqv.', BinaryOperation.Operator.EQV),
1045 (
'/=', BinaryOperation.Operator.NE),
1046 (
'.ne.', BinaryOperation.Operator.NE),
1047 (
'.neqv.', BinaryOperation.Operator.NEQV),
1048 (
'<=', BinaryOperation.Operator.LE),
1049 (
'.le.', BinaryOperation.Operator.LE),
1050 (
'<', BinaryOperation.Operator.LT),
1051 (
'.lt.', BinaryOperation.Operator.LT),
1052 (
'>=', BinaryOperation.Operator.GE),
1053 (
'.ge.', BinaryOperation.Operator.GE),
1054 (
'>', BinaryOperation.Operator.GT),
1055 (
'.gt.', BinaryOperation.Operator.GT),
1056 (
'.and.', BinaryOperation.Operator.AND),
1057 (
'.or.', BinaryOperation.Operator.OR)])
1061 """Class for storing required information from an fparser2
1062 Select_Type_Construct.
1064 :param guard_type: the guard types used by 'type is' and 'class is'
1065 select-type clauses e.g. 'REAL', 'REAL(KIND = 4), or 'mytype'
1066 in 'type_is(REAL)' 'type_is(REAL(KIND = 4)' and 'class
1067 is(mytype)' respectively. These are stored as a list of
1068 str, ordered as found within the select-type
1069 construct's 'type is', 'class is' and 'class default'
1070 clauses with None indicating the 'class default' clause.
1071 :param guard_type_name: a string representation of the guard types used
1072 by 'type is' and 'class is' select-type clauses e.g. 'REAL',
1073 'REAL(KIND = 4)', or 'mytype' are stored as
1074 'REAL', 'REAL_4' and 'mytype' respectively. These are
1075 designed to be used as base variable names in
1076 the code. These are ordered as they are found in the
1077 the select type construct 'type is, 'class is'
1078 and 'class default' clauses with None representing the
1080 :param intrinsic_type_name: the base intrinsic string name for the
1081 particular clause or None if there is no intrinsic type. e.g.
1082 'type is(REAL*4)' becomes 'REAL' and 'type is(mytype)' becomes
1083 None. These are ordered as they occur in the select-type
1084 construct's clauses.
1085 :param clause_type: the name of the clause in the select-type construct
1086 i.e. one of 'type is', 'class is' and 'class default'. These are
1087 ordered as they occur within the select-type construct.
1088 :param stmts: a list of fparser2 statements holding the content of each
1089 of the select-type construct 'type is, 'class is' and
1090 'class default' clauses. These are ordered as they occur within the
1091 select-type construct.
1092 :param selector: the name of the select-type construct selector e.g.
1093 'selector' in 'select type(selector)'.
1094 :param num_clauses: the number of 'type is', 'class is' and
1095 'class default' clauses in the select type construct.
1096 :param default_idx: index of the 'default' clause as it appears within
1097 the select-type construct's 'type is, 'class is' and
1098 'class default' clauses, or -1 if no default clause is found.
1104 guard_type: List[Optional[str]] = field(default_factory=list)
1105 guard_type_name: List[Optional[str]] = field(default_factory=list)
1106 intrinsic_type_name: List[Optional[str]] = field(default_factory=list)
1107 clause_type: List[str] = field(default_factory=list)
1108 stmts: List[List[StmtBase]] = field(default_factory=list)
1110 num_clauses: int = -1
1111 default_idx: int = -1
1116 Fortran2003.Allocate_Stmt: self._allocate_handler,
1117 Fortran2003.Allocate_Shape_Spec: self._allocate_shape_spec_handler,
1118 Fortran2003.Assignment_Stmt: self._assignment_handler,
1119 Fortran2003.Data_Ref: self._structure_accessor_handler,
1120 Fortran2003.Procedure_Designator: self._structure_accessor_handler,
1121 Fortran2003.Deallocate_Stmt: self._deallocate_handler,
1122 Fortran2003.Function_Subprogram: self._subroutine_handler,
1123 Fortran2003.Name: self._name_handler,
1124 Fortran2003.Parenthesis: self._parenthesis_handler,
1125 Fortran2003.Part_Ref: self._part_ref_handler,
1126 Fortran2003.Subscript_Triplet: self._subscript_triplet_handler,
1127 Fortran2003.If_Stmt: self._if_stmt_handler,
1128 utils.NumberBase: self._number_handler,
1129 Fortran2003.Include_Stmt: self._include_handler,
1130 C99Preprocessor.Cpp_Include_Stmt: self._include_handler,
1131 Fortran2003.Int_Literal_Constant: self._number_handler,
1132 Fortran2003.Char_Literal_Constant: self._char_literal_handler,
1133 Fortran2003.Logical_Literal_Constant: self._bool_literal_handler,
1134 utils.BinaryOpBase: self._binary_op_handler,
1135 Fortran2003.End_Do_Stmt: self._ignore_handler,
1136 Fortran2003.End_Subroutine_Stmt: self._ignore_handler,
1137 Fortran2003.If_Construct: self._if_construct_handler,
1138 Fortran2003.Case_Construct: self._case_construct_handler,
1139 Fortran2003.Select_Type_Construct:
1140 self._select_type_construct_handler,
1141 Fortran2003.Return_Stmt: self._return_handler,
1142 Fortran2003.UnaryOpBase: self._unary_op_handler,
1143 Fortran2003.Block_Nonlabel_Do_Construct:
1144 self._do_construct_handler,
1145 Fortran2003.Intrinsic_Function_Reference: self._intrinsic_handler,
1146 Fortran2003.Where_Construct: self._where_construct_handler,
1147 Fortran2003.Where_Stmt: self._where_construct_handler,
1148 Fortran2003.Call_Stmt: self._call_handler,
1149 Fortran2003.Subroutine_Subprogram: self._subroutine_handler,
1150 Fortran2003.Module: self._module_handler,
1151 Fortran2003.Main_Program: self._main_program_handler,
1152 Fortran2003.Program: self._program_handler,
1157 '''Create a CodeBlock for the supplied list of fparser2 nodes and then
1158 wipe the list. A CodeBlock is a node in the PSyIR (Schedule)
1159 that represents a sequence of one or more Fortran statements
1160 and/or expressions which PSyclone does not attempt to handle.
1162 :param parent: Node in the PSyclone AST to which to add this CodeBlock.
1163 :type parent: :py:class:`psyclone.psyir.nodes.Node`
1164 :param fp2_nodes: list of fparser2 AST nodes constituting the
1166 :type fp2_nodes: list of :py:class:`fparser.two.utils.Base`
1167 :param message: Include a preceeding comment attached to the CodeBlock.
1168 :type message: Optional[str]
1170 :returns: a CodeBlock instance.
1171 :rtype: :py:class:`psyclone.CodeBlock`
1183 if isinstance(parent, (Schedule, Container)):
1184 structure = CodeBlock.Structure.STATEMENT
1185 elif isinstance(parent, Directive):
1187 "Fparser2Reader:nodes_to_code_block: A CodeBlock with "
1188 "a Directive as parent is not yet supported.")
1190 structure = CodeBlock.Structure.EXPRESSION
1192 code_block = CodeBlock(fp2_nodes, structure, parent=parent)
1194 code_block.preceding_comment = message
1195 parent.addchild(code_block)
1200 '''Translate the supplied fparser2 parse_tree into PSyIR.
1202 :param parse_tree: the supplied fparser2 parse tree.
1203 :type parse_tree: :py:class:`fparser.two.Fortran2003.Program`
1205 :returns: PSyIR representation of the supplied fparser2 parse_tree.
1206 :rtype: :py:class:`psyclone.psyir.nodes.Container` or \
1207 :py:class:`psyclone.psyir.nodes.Routine`
1209 :raises GenerationError: if the root of the supplied fparser2 \
1210 parse tree is not a Program.
1213 if not isinstance(parse_tree, Fortran2003.Program):
1215 f
"The Fparser2Reader generate_psyir method expects the root "
1216 f
"of the supplied fparser2 tree to be a Program, but found "
1217 f
"'{type(parse_tree).__name__}'")
1219 node = Container(
"dummy")
1221 result = node.children[0]
1222 return result.detach()
1226 Create a Container from the supplied fparser2 module AST.
1228 :param module_ast: fparser2 AST of the full module.
1229 :type module_ast: :py:class:`fparser.two.Fortran2003.Program`
1231 :returns: PSyIR container representing the given module_ast or None \
1232 if there's no module in the parse tree.
1233 :rtype: :py:class:`psyclone.psyir.nodes.Container`
1235 :raises GenerationError: unable to generate a Container from the \
1236 provided fpaser2 parse tree.
1239 modules = walk(module_ast, Fortran2003.Module_Stmt)
1240 if len(modules) > 1:
1242 f
"Could not process {module_ast}. Just one module definition "
1243 f
"per file supported.")
1247 module = modules[0].parent
1248 mod_name = str(modules[0].children[1])
1251 new_container = Container(mod_name)
1258 new_container.symbol_table.default_visibility = default_visibility
1261 _process_routine_symbols(module_ast, new_container.symbol_table,
1265 for child
in module.children:
1266 if isinstance(child, Fortran2003.Specification_Part):
1271 return new_container
1274 '''Create one or more schedules for routines corresponding to the
1275 supplied name in the supplied fparser2 AST. (There can be more than
1276 one routine if the supplied name corresponds to an interface block
1279 :param str name: name of the subroutine represented by the kernel.
1280 :param module_ast: fparser2 AST of the full module where the kernel \
1282 :type module_ast: :py:class:`fparser.two.Fortran2003.Program`
1284 :returns: PSyIR schedules representing the matching subroutine(s).
1285 :rtype: List[:py:class:`psyclone.psyir.nodes.KernelSchedule`]
1287 :raises GenerationError: if supplied parse tree contains more than \
1289 :raises GenerationError: unable to generate a kernel schedule from \
1290 the provided fpaser2 parse tree.
1294 lname = name.lower()
1296 containers = [ctr
for ctr
in psyir.walk(Container)
if
1297 not isinstance(ctr, FileContainer)]
1300 f
"The parse tree supplied to get_routine_schedules() must "
1301 f
"contain a single module but found none when searching for "
1302 f
"kernel '{name}'.")
1303 if len(containers) > 1:
1305 f
"The parse tree supplied to get_routine_schedules() must "
1306 f
"contain a single module but found more than one "
1307 f
"({[ctr.name for ctr in containers]}) when searching for "
1308 f
"kernel '{name}'.")
1309 container = containers[0]
1313 interfaces = walk(module_ast, Fortran2003.Interface_Block)
1315 for interface
in interfaces:
1316 if interface.children[0].children[0].string.lower() == lname:
1319 procs = walk(interface, Fortran2003.Procedure_Stmt)
1321 for child
in proc.children[0].children:
1322 actual_names.append(child.string.lower())
1324 if not actual_names:
1327 actual_names = [lname]
1329 routines = container.walk(Routine)
1330 selected_routines = [routine
for routine
in routines
1331 if routine.name.lower()
in actual_names]
1333 if not selected_routines:
1335 f
"Could not find subroutine or interface '{name}' in the "
1336 f
"module '{container.name}'.")
1338 return selected_routines
1341 def _parse_dimensions(dimensions, symbol_table):
1343 Parse the fparser dimension attribute into a shape list. Each entry of
1344 this list is either None (if the extent is unknown) or a 2-tuple
1345 containing the lower and upper bound of that dimension. If any of the
1346 symbols encountered are instances of the generic Symbol class, they are
1347 specialised (in place) and become instances of DataSymbol with
1350 :param dimensions: fparser dimension attribute.
1352 :py:class:`fparser.two.Fortran2003.Dimension_Attr_Spec`
1353 :param symbol_table: symbol table of the declaration context.
1354 :type symbol_table: :py:class:`psyclone.psyir.symbols.SymbolTable`
1356 :returns: shape of the attribute in column-major order (leftmost \
1357 index is contiguous in memory). Each entry represents an array \
1358 dimension. If it is 'None' the extent of that dimension is \
1359 unknown, otherwise it holds a 2-tuple with the upper and lower \
1360 bounds of the dimension. If it is an empty list then the symbol \
1361 represents a scalar.
1362 :rtype: list of NoneType or 2-tuples of \
1363 :py:class:`psyclone.psyir.nodes.DataNode`
1365 :raises NotImplementedError: if anything other than scalar, integer \
1366 literals or symbols are encounted in the dimensions list.
1369 def _process_bound(bound_expr):
1370 '''Process the supplied fparser2 parse tree for the upper/lower
1371 bound of a dimension in an array declaration.
1373 :param bound_expr: fparser2 parse tree for lower/upper bound.
1374 :type bound_expr: :py:class:`fparser.two.utils.Base`
1376 :returns: PSyIR for the bound.
1377 :rtype: :py:class:`psyclone.psyir.nodes.DataNode`
1379 :raises NotImplementedError: if an unsupported form of array \
1381 :raises GenerationError: invalid Fortran declaration of an \
1382 upper bound without an associated lower bound.
1385 if isinstance(bound_expr, Fortran2003.Int_Literal_Constant):
1386 return Literal(bound_expr.items[0], INTEGER_TYPE)
1388 if isinstance(bound_expr, Fortran2003.Name):
1396 dim_name = bound_expr.string.lower()
1398 sym = symbol_table.lookup(dim_name)
1400 if type(sym)
is Symbol:
1404 sym.specialise(DataSymbol, datatype=UnresolvedType())
1405 elif isinstance(sym.datatype, (UnsupportedType,
1409 elif not (isinstance(sym.datatype, ScalarType)
and
1410 sym.datatype.intrinsic ==
1411 ScalarType.Intrinsic.INTEGER):
1414 raise NotImplementedError(
1415 "Unsupported shape dimension")
1420 sym = DataSymbol(dim_name, default_integer_type(),
1421 interface=UnresolvedInterface())
1422 symbol_table.add(sym)
1423 return Reference(sym)
1425 raise NotImplementedError(
"Unsupported shape dimension")
1427 one = Literal(
"1", INTEGER_TYPE)
1430 for dim
in walk(dimensions, (Fortran2003.Assumed_Shape_Spec,
1431 Fortran2003.Explicit_Shape_Spec,
1432 Fortran2003.Assumed_Size_Spec)):
1434 if isinstance(dim, Fortran2003.Assumed_Shape_Spec):
1441 lower = (_process_bound(dim.children[0])
if dim.children[0]
1444 upper = _process_bound(dim.children[1])
1446 upper = ArrayType.Extent.ATTRIBUTE
if lower
else None
1448 if upper
and not lower:
1450 f
"Found an assumed-shape array declaration with only "
1451 f
"an upper bound ({dimensions}). This is not valid "
1454 shape.append((lower, upper))
1458 elif isinstance(dim, Fortran2003.Explicit_Shape_Spec):
1460 upper = _process_bound(dim.items[1])
1462 lower = _process_bound(dim.items[0])
1463 shape.append((lower, upper))
1466 shape.append((one.copy(), upper))
1467 except NotImplementedError
as err:
1468 raise NotImplementedError(
1469 f
"Could not process {dimensions}. Only scalar integer "
1470 f
"literals or symbols are supported for explicit-shape"
1471 f
" array declarations.")
from err
1473 elif isinstance(dim, Fortran2003.Assumed_Size_Spec):
1474 raise NotImplementedError(
1475 f
"Could not process {dimensions}. Assumed-size arrays"
1476 f
" are not supported.")
1480 f
"Reached end of loop body and array-shape specification "
1481 f
"{type(dim)} has not been handled.")
1488 Search the supplied list of fparser2 nodes (which must represent a
1489 complete Specification Part) for any accessibility
1490 statements (e.g. "PUBLIC :: my_var") to determine the default
1491 visibility of symbols as well as identifying those that are
1492 explicitly declared as public or private.
1494 :param nodes: nodes in the fparser2 parse tree describing a \
1495 Specification Part that will be searched.
1496 :type nodes: list of :py:class:`fparser.two.utils.Base`
1498 :returns: default visibility of symbols within the current scoping \
1499 unit and dict of symbol names with explicit visibilities.
1500 :rtype: 2-tuple of (:py:class:`psyclone.symbols.Symbol.Visibility`, \
1503 :raises InternalError: if an accessibility attribute which is not \
1504 'public' or 'private' is encountered.
1505 :raises GenerationError: if the parse tree is found to contain more \
1506 than one bare accessibility statement (i.e. 'PUBLIC' or 'PRIVATE')
1507 :raises GenerationError: if a symbol is explicitly declared as being \
1508 both public and private.
1511 default_visibility =
None
1514 explicit_public = set()
1515 explicit_private = set()
1518 access_stmts = walk(nodes, Fortran2003.Access_Stmt)
1520 for stmt
in access_stmts:
1522 if stmt.children[0].lower() ==
"public":
1524 elif stmt.children[0].lower() ==
"private":
1528 f
"Failed to process '{stmt}'. Found an accessibility "
1529 f
"attribute of '{stmt.children[0]}' but expected either "
1530 f
"'public' or 'private'.")
1531 if not stmt.children[1]:
1532 if default_visibility:
1536 current_node = stmt.parent
1538 if isinstance(current_node, Fortran2003.Module):
1540 current_node.children[0].children[1])
1542 f
"Module '{mod_name}' contains more than one "
1543 f
"access statement with an omitted "
1544 f
"access-id-list. This is invalid Fortran.")
1545 current_node = current_node.parent
1550 "Found multiple access statements with omitted access-"
1551 "id-lists and no enclosing Module. Both of these "
1552 "things are invalid Fortran.")
1554 default_visibility = Symbol.Visibility.PUBLIC
1556 default_visibility = Symbol.Visibility.PRIVATE
1558 symbol_names = [child.string.lower()
for child
in
1559 stmt.children[1].children]
1561 explicit_public.update(symbol_names)
1563 explicit_private.update(symbol_names)
1566 invalid_symbols = explicit_public.intersection(explicit_private)
1569 f
"Symbols {list(invalid_symbols)} appear in access statements "
1570 f
"with both PUBLIC and PRIVATE access-ids. This is invalid "
1574 if default_visibility
is None:
1575 default_visibility = Symbol.Visibility.PUBLIC
1578 for name
in explicit_public:
1579 visibility_map[name] = Symbol.Visibility.PUBLIC
1580 for name
in explicit_private:
1581 visibility_map[name] = Symbol.Visibility.PRIVATE
1583 return (default_visibility, visibility_map)
1586 def _process_save_statements(nodes, parent):
1588 Search the supplied list of fparser2 nodes (which must represent a
1589 complete Specification Part) for any SAVE statements (e.g.
1590 "SAVE :: my_var") to determine which Symbols are static.
1592 Any common blocks referred to in a SAVE will result in Symbols of
1593 UnsupportedFortranType being added to the symbol table associated with
1596 :param nodes: nodes in the fparser2 parse tree describing a
1597 Specification Part that will be searched.
1598 :type nodes: List[:py:class:`fparser.two.utils.Base`]
1599 :param : the parent node in the PSyIR under construction.
1600 :type : :py:class:`psyclone.psyir.nodes.Node`
1602 :returns: names of symbols that are static or just "*" if they all are.
1605 :raises GenerationError: if the parse tree is found to contain a SAVE
1606 without a saved-entity list *and* one or more SAVE attributes or
1607 SAVE statements (C580).
1610 symbol_table = parent.scope.symbol_table
1611 default_save =
False
1614 explicit_save = set()
1616 save_stmts = walk(nodes, Fortran2003.Save_Stmt)
1618 for stmt
in save_stmts:
1620 if not stmt.children[1]:
1624 symbol_names = [child.string.lower()
for child
in
1625 stmt.children[1].children]
1626 explicit_save.update(symbol_names)
1632 names = sorted(list(explicit_save))
1634 f
"Supplied nodes contain a SAVE without a saved-entity "
1635 f
"list plus one or more SAVES *with* saved-entity lists "
1636 f
"(naming {names}). This is not valid Fortran.")
1637 explicit_save.add(
"*")
1643 for name
in explicit_save.copy():
1644 if name.startswith(
"/"):
1645 uftype = UnsupportedFortranType(f
"SAVE :: {name}")
1646 symbol_table.new_symbol(root_name=
"_PSYCLONE_INTERNAL_SAVE",
1647 symbol_type=DataSymbol,
1649 explicit_save.remove(name)
1650 return list(explicit_save)
1653 def _process_use_stmts(parent, nodes, visibility_map=None):
1655 Process all of the USE statements in the fparser2 parse tree
1656 supplied as a list of nodes. Imported symbols are added to
1657 the symbol table associated with the supplied parent node with
1660 :param parent: PSyIR node in which to insert the symbols found.
1661 :type parent: :py:class:`psyclone.psyir.nodes.KernelSchedule`
1662 :param nodes: fparser2 AST nodes to search for use statements.
1663 :type nodes: list of :py:class:`fparser.two.utils.Base`
1664 :param visibility_map: mapping of symbol name to visibility (for \
1665 those symbols listed in an accessibility statement).
1666 :type visibility_map: dict with str keys and \
1667 :py:class:`psyclone.psyir.symbols.Symbol.Visibility` values
1669 :raises GenerationError: if the parse tree for a use statement has an \
1670 unrecognised structure.
1671 :raises SymbolError: if a symbol imported via a use statement is \
1672 already present in the symbol table.
1673 :raises NotImplementedError: if the form of use statement is not \
1677 if visibility_map
is None:
1680 for decl
in walk(nodes, Fortran2003.Use_Stmt):
1683 if len(decl.items) != 5:
1687 for item
in decl.items:
1691 f
"Expected the parse tree for a USE statement to contain "
1692 f
"5 items but found {len(decl.items)} for '{text}'")
1694 mod_name = str(decl.items[2])
1695 mod_visibility = visibility_map.get(
1696 mod_name, parent.symbol_table.default_visibility)
1701 if mod_name
not in parent.symbol_table:
1702 new_container =
True
1703 container = ContainerSymbol(mod_name,
1704 visibility=mod_visibility)
1705 parent.symbol_table.add(container)
1707 new_container =
False
1708 container = parent.symbol_table.lookup(mod_name)
1709 if not isinstance(container, ContainerSymbol):
1711 f
"Found a USE of module '{mod_name}' but the symbol "
1712 f
"table already has a non-container entry with that "
1713 f
"name ({container}). This is invalid Fortran.")
1716 if isinstance(decl.items[4], Fortran2003.Only_List):
1717 if not new_container
and not container.wildcard_import
and \
1718 not parent.symbol_table.symbols_imported_from(container):
1722 for name
in decl.items[4].items:
1723 if isinstance(name, Fortran2003.Rename):
1728 sym_name = str(name.children[1]).lower()
1729 orig_name = str(name.children[2]).lower()
1732 sym_name = str(name).lower()
1734 sym_visibility = visibility_map.get(
1735 sym_name, parent.symbol_table.default_visibility)
1736 if sym_name
not in parent.symbol_table:
1742 parent.symbol_table.add(
1743 Symbol(sym_name, visibility=sym_visibility,
1744 interface=ImportInterface(
1745 container, orig_name=orig_name)))
1748 existing_symbol = parent.symbol_table.lookup(
1750 if isinstance(existing_symbol, RoutineSymbol):
1754 existing_symbol.interface = ImportInterface(
1755 container, orig_name=orig_name)
1756 elif not existing_symbol.is_import:
1758 f
"Symbol '{sym_name}' is imported from module "
1759 f
"'{mod_name}' but is already present in the "
1760 f
"symbol table as either an argument or a "
1761 f
"local ({existing_symbol}).")
1764 elif not decl.items[3]:
1766 if not new_container
and not container.wildcard_import
and \
1767 not parent.symbol_table.symbols_imported_from(container):
1772 container.wildcard_import =
True
1773 elif decl.items[3].lower().replace(
" ",
"") ==
",only:":
1779 if not new_container
and \
1780 (container.wildcard_import
or
1781 parent.symbol_table.symbols_imported_from(container)):
1787 raise NotImplementedError(f
"Found unsupported USE statement: "
1790 def _process_type_spec(self, parent, type_spec):
1792 Processes the fparser2 parse tree of a type specification in order to
1793 extract the type and precision that are specified.
1795 :param parent: the parent of the current PSyIR node under construction.
1796 :type parent: :py:class:`psyclone.psyir.nodes.Node`
1797 :param type_spec: the fparser2 parse tree of the type specification.
1799 :py:class:`fparser.two.Fortran2003.Intrinsic_Type_Spec` or \
1800 :py:class:`fparser.two.Fortran2003.Declaration_Type_Spec`
1802 :returns: the type and precision specified by the type-spec.
1803 :rtype: 2-tuple of :py:class:`psyclone.psyir.symbols.ScalarType` or \
1804 :py:class:`psyclone.psyir.symbols.DataTypeSymbol` and \
1805 :py:class:`psyclone.psyir.symbols.DataSymbol.Precision` or \
1806 :py:class:`psyclone.psyir.symbols.DataSymbol` or int or NoneType
1808 :raises NotImplementedError: if an unsupported intrinsic type is found.
1809 :raises SymbolError: if a symbol already exists for the name of a \
1810 derived type but is not a DataTypeSymbol.
1811 :raises NotImplementedError: if the supplied type specification is \
1812 not for an intrinsic type or a derived type.
1818 if isinstance(type_spec, Fortran2003.Intrinsic_Type_Spec):
1819 fort_type = str(type_spec.items[0]).lower()
1821 data_name = TYPE_MAP_FROM_FORTRAN[fort_type]
1822 except KeyError
as err:
1823 raise NotImplementedError(
1824 f
"Could not process {type_spec}. Only 'real', 'double "
1825 f
"precision', 'integer', 'logical' and 'character' "
1826 f
"intrinsic types are supported.")
from err
1827 if fort_type ==
"double precision":
1830 precision = ScalarType.Precision.DOUBLE
1835 precision = default_precision(data_name)
1837 if fort_type ==
"character" and type_spec.children[1]:
1838 raise NotImplementedError(
1839 f
"Length or kind attributes not supported on a character "
1840 f
"variable: '{type_spec}'")
1841 base_type = ScalarType(data_name, precision)
1843 elif isinstance(type_spec, Fortran2003.Declaration_Type_Spec):
1845 if type_spec.children[0].lower() !=
"type":
1848 raise NotImplementedError(
1849 f
"Could not process {type_spec} - declarations "
1850 f
"other than 'type' are not yet supported.")
1851 type_name = str(walk(type_spec, Fortran2003.Type_Name)[0])
1853 type_symbol = _find_or_create_unresolved_symbol(parent, type_name)
1855 if type(type_symbol)
is Symbol:
1858 new_symbol = DataTypeSymbol(type_name, UnresolvedType(),
1859 interface=type_symbol.interface,
1860 visibility=type_symbol.visibility)
1861 table = type_symbol.find_symbol_table(parent)
1862 table.swap(type_symbol, new_symbol)
1863 type_symbol = new_symbol
1864 elif not isinstance(type_symbol, DataTypeSymbol):
1866 f
"Search for a DataTypeSymbol named '{type_name}' "
1867 f
"(required by specification '{type_spec}') found a "
1868 f
"'{type(type_symbol).__name__}' instead.")
1869 base_type = type_symbol
1874 raise NotImplementedError(
"Unsupported type specification")
1876 return base_type, precision
1878 def _process_decln(self, scope, symbol_table, decl, visibility_map=None,
1881 Process the supplied fparser2 parse tree for a declaration. For each
1882 entity that is declared, a symbol is added to the supplied symbol
1885 :param scope: PSyIR node in which to insert the symbols found.
1886 :type scope: :py:class:`psyclone.psyir.nodes.ScopingNode`
1887 :param symbol_table: the symbol table to which to add new symbols.
1888 :type symbol_table: py:class:`psyclone.psyir.symbols.SymbolTable`
1889 :param decl: fparser2 parse tree of declaration to process.
1890 :type decl: :py:class:`fparser.two.Fortran2003.Type_Declaration_Stmt`
1891 :param visibility_map: mapping of symbol name to visibility (for
1892 those symbols listed in an accessibility statement).
1893 :type visibility_map: dict with str keys and
1894 :py:class:`psyclone.psyir.symbols.Symbol.Visibility` values
1895 :param statics_list: the names of symbols which are static (due to
1896 appearing in a SAVE statement). If all symbols are static then
1897 this contains the single entry "*".
1898 :type statics_list: Iterable[str]
1900 :raises NotImplementedError: if an unsupported attribute is found.
1901 :raises NotImplementedError: if an unsupported intent attribute is
1903 :raises NotImplementedError: if an unsupported access-spec attribute
1905 :raises NotImplementedError: if the allocatable attribute is found on
1906 a non-array declaration.
1907 :raises InternalError: if an array with defined extent has the
1908 allocatable attribute.
1909 :raises NotImplementedError: if an unsupported initialisation
1910 expression is found for a parameter declaration.
1911 :raises NotImplementedError: if a character-length specification is
1913 :raises SymbolError: if a declaration is found for a symbol that is
1914 already present in the symbol table with a defined interface.
1915 :raises GenerationError: if a set of incompatible Fortran
1916 attributes are found in a symbol declaration.
1920 (type_spec, attr_specs, entities) = decl.items
1927 attribute_shape = []
1930 multiple_interfaces =
False
1932 has_constant_value =
False
1937 decln_access_spec =
None
1939 has_save_attr =
False
1941 for attr
in attr_specs.items:
1942 if isinstance(attr, Fortran2003.Attr_Spec):
1943 normalized_string = str(attr).lower().replace(
' ',
'')
1944 if normalized_string ==
"save":
1945 if interface
is not None:
1946 multiple_interfaces =
True
1947 has_save_attr =
True
1948 elif normalized_string ==
"parameter":
1950 has_constant_value =
True
1951 elif normalized_string ==
"allocatable":
1954 raise NotImplementedError(
1955 f
"Could not process {decl.items}. Unrecognised "
1956 f
"attribute '{attr}'.")
1957 elif isinstance(attr, Fortran2003.Intent_Attr_Spec):
1958 (_, intent) = attr.items
1959 normalized_string = \
1960 intent.string.lower().replace(
' ',
'')
1962 if interface
is not None:
1963 multiple_interfaces =
True
1964 interface = ArgumentInterface(
1965 INTENT_MAPPING[normalized_string])
1966 except KeyError
as info:
1968 f
"Could not process {decl.items}. Unexpected "
1969 f
"intent attribute '{attr}'.")
1971 elif isinstance(attr,
1972 (Fortran2003.Dimension_Attr_Spec,
1973 Fortran2003.Dimension_Component_Attr_Spec)):
1976 elif isinstance(attr, Fortran2003.Access_Spec):
1978 decln_access_spec = _process_access_spec(attr)
1979 except InternalError
as err:
1981 f
"Could not process '{decl.items}': "
1982 f
"{err.value}")
from err
1984 raise NotImplementedError(
1985 f
"Could not process declaration '{decl}'. Unrecognised"
1986 f
" attribute type '{type(attr).__name__}'.")
1992 if has_save_attr
and has_constant_value:
1994 f
"SAVE and PARAMETER attributes are not compatible but "
2000 if has_constant_value
and interface
is None:
2002 interface = StaticInterface()
2004 if allocatable
and has_constant_value:
2006 f
"ALLOCATABLE and PARAMETER attributes are not compatible "
2007 f
"but found:\n {decl}")
2008 if isinstance(interface, ArgumentInterface)
and has_constant_value:
2010 f
"INTENT and PARAMETER attributes are not compatible but"
2011 f
" found:\n {decl}")
2012 if multiple_interfaces:
2014 f
"Multiple or duplicated incompatible attributes "
2015 f
"found in declaration:\n {decl}")
2019 for entity
in entities.items:
2020 (name, array_spec, char_len, initialisation) = entity.items
2025 if array_spec
is not None:
2029 entity_shape = attribute_shape
2031 if allocatable
and not entity_shape:
2034 raise NotImplementedError(
2035 f
"Could not process {decl}. The 'allocatable' attribute is"
2036 f
" only supported on array declarations.")
2038 for idx, extent
in enumerate(entity_shape):
2041 entity_shape[idx] = ArrayType.Extent.DEFERRED
2043 entity_shape[idx] = ArrayType.Extent.ATTRIBUTE
2044 elif not isinstance(extent, ArrayType.Extent)
and \
2049 f
"Invalid Fortran: '{decl}'. An array with defined "
2050 f
"extent cannot have the ALLOCATABLE attribute.")
2055 dummynode = Assignment(parent=scope)
2056 expr = initialisation.items[1]
2057 self.
process_nodesprocess_nodes(parent=dummynode, nodes=[expr])
2058 init_expr = dummynode.children[0].detach()
2060 if char_len
is not None:
2061 raise NotImplementedError(
2062 f
"Could not process {decl.items}. Character length "
2063 f
"specifications are not supported.")
2065 sym_name = str(name).lower()
2067 if decln_access_spec:
2068 visibility = decln_access_spec
2071 if visibility_map
is not None:
2072 visibility = visibility_map.get(
2073 sym_name, symbol_table.default_visibility)
2075 visibility = symbol_table.default_visibility
2077 listed_in_save =
"*" in statics_list
or sym_name
in statics_list
2078 if has_save_attr
or listed_in_save:
2079 if has_save_attr
and listed_in_save:
2081 f
"Invalid Fortran: '{decl}'. Symbol 'sym_name' is "
2082 f
"the subject of a SAVE statement but also has a SAVE "
2083 f
"attribute on its declaration.")
2084 this_interface = StaticInterface()
2089 this_interface = (DefaultModuleInterface()
if
2090 isinstance(scope, Container)
else
2091 AutomaticInterface())
2096 this_interface = interface.copy()
2100 datatype = ArrayType(base_type, entity_shape)
2103 datatype = base_type
2108 sym = symbol_table.lookup(sym_name, scope_limit=scope)
2110 if type(sym)
is Symbol:
2112 sym.specialise(DataSymbol, datatype=datatype,
2113 visibility=visibility,
2114 interface=this_interface,
2115 is_constant=has_constant_value,
2116 initial_value=init_expr)
2118 if sym
is symbol_table.lookup_with_tag(
2119 "own_routine_symbol"):
2125 symbol_table.remove(sym)
2128 tag =
"own_routine_symbol"
2130 if not sym.is_unresolved:
2132 f
"Symbol '{sym_name}' already present in "
2133 f
"SymbolTable with a defined interface "
2134 f
"({sym.interface}).")
2137 sym = DataSymbol(sym_name, datatype,
2138 visibility=visibility,
2139 is_constant=has_constant_value,
2140 initial_value=init_expr)
2148 f
"The fparser2 frontend does not support "
2149 f
"declarations where the routine name is of "
2150 f
"UnsupportedType, but found this case in "
2152 raise NotImplementedError()
2154 symbol_table.add(sym, tag=tag)
2161 sym.interface = StaticInterface()
2163 sym.interface = this_interface
2165 def _process_derived_type_decln(self, parent, decl, visibility_map):
2167 Process the supplied fparser2 parse tree for a derived-type
2168 declaration. A DataTypeSymbol representing the derived-type is added
2169 to the symbol table associated with the parent node.
2171 :param parent: PSyIR node in which to insert the symbols found.
2172 :type parent: :py:class:`psyclone.psyGen.KernelSchedule`
2173 :param decl: fparser2 parse tree of declaration to process.
2174 :type decl: :py:class:`fparser.two.Fortran2003.Type_Declaration_Stmt`
2175 :param visibility_map: mapping of symbol name to visibility (for \
2176 those symbols listed in an accessibility statement).
2177 :type visibility_map: dict with str keys and \
2178 :py:class:`psyclone.psyir.symbols.Symbol.Visibility` values
2180 :raises SymbolError: if a Symbol already exists with the same name \
2181 as the derived type being defined and it is not a DataTypeSymbol \
2182 or is not of UnresolvedType.
2185 name = str(walk(decl.children[0], Fortran2003.Type_Name)[0]).lower()
2187 dtype = StructureType()
2192 private_stmts = walk(decl, Fortran2003.Private_Components_Stmt)
2194 default_compt_visibility = Symbol.Visibility.PRIVATE
2196 default_compt_visibility = Symbol.Visibility.PUBLIC
2199 if name
in visibility_map:
2200 dtype_symbol_vis = visibility_map[name]
2202 specs = walk(decl.children[0], Fortran2003.Access_Spec)
2204 dtype_symbol_vis = _process_access_spec(specs[0])
2206 dtype_symbol_vis = parent.symbol_table.default_visibility
2210 if name
in parent.symbol_table:
2213 tsymbol = parent.symbol_table.lookup(name)
2214 if not isinstance(tsymbol, DataTypeSymbol):
2216 f
"Error processing definition of derived type '{name}'. "
2217 f
"The symbol table already contains an entry with this "
2218 f
"name but it is a '{type(tsymbol).__name__}' when it "
2219 f
"should be a 'DataTypeSymbol' (for the derived-type "
2220 f
"definition '{decl}')")
2223 if not isinstance(tsymbol.datatype, UnresolvedType):
2225 f
"Error processing definition of derived type '{name}'. "
2226 f
"The symbol table already contains a DataTypeSymbol with "
2227 f
"this name but it is of type "
2228 f
"'{type(tsymbol.datatype).__name__}' when it should be "
2229 f
"of 'UnresolvedType'")
2232 tsymbol = DataTypeSymbol(name, dtype, visibility=dtype_symbol_vis)
2233 parent.symbol_table.add(tsymbol)
2242 derived_type_stmt = decl.children[0]
2243 if walk(derived_type_stmt, Fortran2003.Type_Attr_Spec):
2244 raise NotImplementedError(
2245 "Derived-type definition contains unsupported attributes.")
2249 contains = walk(decl, Fortran2003.Contains_Stmt)
2251 raise NotImplementedError(
2252 "Derived-type definition has a CONTAINS statement.")
2255 local_table = SymbolTable(
2256 default_visibility=default_compt_visibility)
2257 for child
in walk(decl, Fortran2003.Data_Component_Def_Stmt):
2260 for symbol
in local_table.symbols:
2261 dtype.add(symbol.name, symbol.datatype, symbol.visibility,
2262 symbol.initial_value)
2265 tsymbol.datatype = dtype
2267 except NotImplementedError:
2270 tsymbol.datatype = UnsupportedFortranType(str(decl))
2271 tsymbol.interface = UnknownInterface()
2273 def _get_partial_datatype(self, node, scope, visibility_map):
2274 '''Try to obtain partial datatype information from node by removing
2275 any unsupported properties in the declaration.
2277 :param node: fparser2 node containing the declaration statement.
2278 :type node: :py:class:`fparser.two.Fortran2008.Type_Declaration_Stmt`
2279 or :py:class:`fparser.two.Fortran2003.Type_Declaration_Stmt`
2280 :param scope: PSyIR node in which to insert the symbols found.
2281 :type scope: :py:class:`psyclone.psyir.nodes.ScopingNode`
2282 :param visibility_map: mapping of symbol names to explicit
2284 :type visibility_map: dict with str keys and values of type
2285 :py:class:`psyclone.psyir.symbols.Symbol.Visibility`
2287 :returns: a 2-tuple containing a PSyIR datatype, or datatype symbol,
2288 containing partial datatype information for the declaration
2289 statement and the PSyIR for any initialisation expression.
2290 When it is not possible to extract partial datatype information
2291 then (None, None) is returned.
2293 Optional[:py:class:`psyclone.psyir.symbols.DataType` |
2294 :py:class:`psyclone.psyir.symbols.DataTypeSymbol`],
2295 Optional[:py:class:`psyclone.psyir.nodes.Node`]]
2299 entity_decl_list = node.children[2]
2300 orig_entity_decl_list = list(entity_decl_list.children[:])
2301 entity_decl_list.items = tuple(entity_decl_list.children[0:1])
2302 entity_decl = entity_decl_list.children[0]
2303 orig_entity_decl_children = list(entity_decl.children[:])
2306 unsupported_attribute_names = [
"pointer",
"target"]
2307 attr_spec_list = node.children[1]
2308 orig_node_children = list(node.children[:])
2309 orig_attr_spec_list_children = (list(node.children[1].children[:])
2310 if attr_spec_list
else None)
2313 for attr_spec
in attr_spec_list.children:
2314 if str(attr_spec).lower()
not in unsupported_attribute_names:
2315 entry_list.append(attr_spec)
2317 node.items = (node.items[0],
None, node.items[2])
2319 node.items[1].items = tuple(entry_list)
2322 symbol_table = SymbolTable()
2326 symbol_name = node.children[2].children[0].children[0].string
2327 symbol_name = symbol_name.lower()
2328 new_sym = symbol_table.lookup(symbol_name)
2329 datatype = new_sym.datatype
2330 init_expr = new_sym.initial_value
2331 except NotImplementedError:
2336 node.items = tuple(orig_node_children)
2337 if node.children[1]:
2338 node.children[1].items = tuple(orig_attr_spec_list_children)
2339 node.children[2].items = tuple(orig_entity_decl_list)
2340 node.children[2].children[0].items = tuple(orig_entity_decl_children)
2343 init_expr = init_expr.detach()
if init_expr
is not None else None
2344 return datatype, init_expr
2346 def _process_parameter_stmts(self, nodes, parent):
2348 Examine the supplied list of fparser2 nodes and handle any
2349 PARAMETER statements. This is done separately so that it can be
2350 performed after all the declarations have been processed (since
2351 a PARAMETER statement can come *before* a symbol's declaration.)
2353 :param nodes: fparser2 AST nodes containing declaration statements.
2354 :type nodes: list of :py:class:`fparser.two.utils.Base`
2355 :param parent: PSyIR node in which to insert the symbols found.
2356 :type parent: :py:class:`psyclone.psyir.nodes.KernelSchedule`
2358 :raises NotImplementedError: if there are any issues parsing a
2359 parameter statement.
2363 if not isinstance(node, Fortran2003.Implicit_Part):
2365 for stmt
in node.children:
2366 if not isinstance(stmt, Fortran2003.Parameter_Stmt):
2368 for parameter_def
in stmt.children[1].items:
2369 name, expr = parameter_def.items
2371 symbol = parent.symbol_table.lookup(str(name))
2372 except Exception
as err:
2376 raise NotImplementedError(
2377 f
"Could not process '{stmt}' because: "
2380 if not isinstance(symbol, DataSymbol):
2381 raise NotImplementedError(
2382 f
"Could not process '{stmt}' because "
2383 f
"'{symbol.name}' is not a DataSymbol.")
2384 if isinstance(symbol.datatype, UnsupportedType):
2385 raise NotImplementedError(
2386 f
"Could not process '{stmt}' because "
2387 f
"'{symbol.name}' has an UnsupportedType.")
2392 dummynode = Assignment(parent=parent)
2393 self.
process_nodesprocess_nodes(parent=dummynode, nodes=[expr])
2397 ct_expr = dummynode.children[0].detach()
2398 symbol.initial_value = ct_expr
2399 symbol.is_constant =
True
2401 symbol.interface = StaticInterface()
2403 def _process_interface_block(self, node, symbol_table, visibility_map):
2405 Processes a Fortran2003.Interface_Block. If the interface is named
2406 and consists only of [module] procedure :: <procedure-list> then a
2407 GenericInterfaceSymbol is created. Otherwise, a RoutineSymbol of
2408 UnsupportedFortranType is created.
2410 :param node: the parse tree for the interface block.
2411 :type node: :py:class:`fparser.two.Fortran2003.Interface_Block`
2412 :param symbol_table: the table to which to add new symbols.
2413 :type symbol_table: :py:class:`psyclone.psyir.symbols.SymbolTable`
2414 :param visibility_map: information on any explicit symbol visibilities
2415 in the current scope.
2416 :type visibility_map: dict[
2417 str, :py:class:`psyclone.psyir.symbols.Symbol.Visibility`]
2425 if not isinstance(node.children[0].children[0],
2432 symbol_table.new_symbol(
2433 root_name=
"_psyclone_internal_interface",
2434 symbol_type=RoutineSymbol,
2435 datatype=UnsupportedFortranType(str(node).lower()))
2439 name = node.children[0].children[0].string.lower()
2440 vis = visibility_map.get(
2441 name, symbol_table.default_visibility)
2449 supported_interface =
True
2451 for child
in node.children:
2452 if isinstance(child, (Fortran2003.Interface_Stmt,
2453 Fortran2003.End_Interface_Stmt)):
2455 if isinstance(child, Fortran2003.Procedure_Stmt):
2457 is_module = child.children[1] ==
'MODULE'
2458 for routine_name
in child.children[0].children:
2462 rsym = symbol_table.find_or_create(
2463 routine_name.string)
2464 if type(rsym)
is Symbol:
2465 rsym.specialise(RoutineSymbol)
2466 elif not isinstance(rsym, RoutineSymbol):
2468 f
"Expected '{rsym.name}' referenced by generic "
2469 f
"interface '{name}' to be a Symbol or a "
2470 f
"RoutineSymbol but found '{type(rsym).__name__}'")
2471 rsymbols.append((rsym, is_module))
2475 supported_interface =
False
2478 if supported_interface:
2483 symbol_table.add(GenericInterfaceSymbol(
2484 name, rsymbols, visibility=vis))
2489 symbol_table.add(RoutineSymbol(
2490 name, datatype=UnsupportedFortranType(str(node).lower()),
2499 symbol_table.new_symbol(
2500 root_name=f
"_psyclone_internal_{name}",
2501 symbol_type=RoutineSymbol,
2502 datatype=UnsupportedFortranType(str(node).lower()),
2506 visibility_map=None):
2508 Transform the variable declarations in the fparser2 parse tree into
2509 symbols in the symbol table of the PSyIR parent node. The default
2510 visibility of any new symbol is taken from the symbol table associated
2511 with the `parent` node if necessary. The `visibility_map` provides
2512 information on any explicit symbol visibilities that are specified
2513 for the declarations.
2515 :param parent: PSyIR node in which to insert the symbols found.
2516 :type parent: :py:class:`psyclone.psyir.nodes.KernelSchedule`
2517 :param nodes: fparser2 AST nodes containing declaration statements.
2518 :type nodes: List[:py:class:`fparser.two.utils.Base`]
2519 :param arg_list: fparser2 AST node containing the argument list.
2520 :type arg_list: :py:class:`fparser.Fortran2003.Dummy_Arg_List`
2521 :param visibility_map: mapping of symbol names to explicit
2523 :type visibility_map: dict[
2524 str, :py:class:`psyclone.psyir.symbols.Symbol.Visibility`]
2526 :raises GenerationError: if an INCLUDE statement is encountered.
2527 :raises NotImplementedError: the provided declarations contain
2528 attributes which are not supported yet.
2529 :raises GenerationError: if the parse tree for a USE statement does
2530 not have the expected structure.
2531 :raises SymbolError: if a declaration is found for a Symbol that is
2532 already in the symbol table with a defined interface.
2533 :raises InternalError: if the provided declaration is an unexpected
2534 or invalid fparser or Fortran expression.
2537 if visibility_map
is None:
2549 for decl
in walk(nodes, Fortran2003.Derived_Type_Def):
2555 incl_nodes = walk(nodes, (Fortran2003.Include_Stmt,
2556 C99Preprocessor.Cpp_Include_Stmt))
2566 if isinstance(node, Fortran2003.Interface_Block):
2571 elif isinstance(node, Fortran2003.Type_Declaration_Stmt):
2573 self.
_process_decln_process_decln(parent, parent.symbol_table, node,
2574 visibility_map, statics_list)
2575 except NotImplementedError:
2581 specs = walk(node, Fortran2003.Access_Spec)
2583 decln_vis = _process_access_spec(specs[0])
2585 decln_vis = parent.symbol_table.default_visibility
2587 orig_children = list(node.children[2].children[:])
2588 for child
in orig_children:
2592 node.children[2].items = (child,)
2593 symbol_name = str(child.children[0]).lower()
2594 vis = visibility_map.get(symbol_name, decln_vis)
2603 routine_sym = parent.symbol_table.lookup_with_tag(
2604 "own_routine_symbol")
2605 if routine_sym.name.lower() == symbol_name:
2606 parent.symbol_table.remove(routine_sym)
2607 tag =
"own_routine_symbol"
2613 node, parent, visibility_map)
2619 parent.symbol_table.add(
2621 symbol_name, UnsupportedFortranType(
2623 partial_datatype=datatype),
2624 interface=UnknownInterface(),
2626 initial_value=init),
2629 except KeyError
as err:
2630 if len(orig_children) == 1:
2632 f
"Error while processing unsupported "
2633 f
"declaration ('{node}'). An entry for "
2634 f
"symbol '{symbol_name}' is already in "
2635 f
"the symbol table.")
from err
2637 node.children[2].items = tuple(orig_children)
2639 elif isinstance(node, (Fortran2003.Access_Stmt,
2640 Fortran2003.Save_Stmt,
2641 Fortran2003.Derived_Type_Def,
2642 Fortran2003.Stmt_Function_Stmt,
2643 Fortran2003.Common_Stmt,
2644 Fortran2003.Use_Stmt)):
2648 elif isinstance(node, Fortran2003.Implicit_Part):
2654 child_nodes = walk(node, Fortran2003.Format_Stmt)
2656 raise NotImplementedError(
2657 f
"Error processing implicit-part: Format statements "
2658 f
"are not supported but found '{child_nodes[0]}'")
2659 child_nodes = walk(node, Fortran2003.Implicit_Stmt)
2660 if any(imp.children != (
'NONE',)
for imp
in child_nodes):
2661 raise NotImplementedError(
2662 f
"Error processing implicit-part: implicit variable "
2663 f
"declarations not supported but found '{node}'")
2665 elif isinstance(node, Fortran2003.Namelist_Stmt):
2672 parent.symbol_table.new_symbol(
2673 root_name=
"_PSYCLONE_INTERNAL_NAMELIST",
2674 symbol_type=DataSymbol,
2675 datatype=UnsupportedFortranType(str(node)))
2677 raise NotImplementedError(
2678 f
"Error processing declarations: fparser2 node of type "
2679 f
"'{type(node).__name__}' not supported")
2691 if visibility_map
is not None:
2695 for name, vis
in visibility_map.items():
2696 if name
not in parent.symbol_table:
2699 _find_or_create_unresolved_symbol(parent, name,
2704 for arg_name
in [x.string.lower()
for x
in arg_list]:
2705 symbol = parent.symbol_table.lookup(arg_name)
2706 if not symbol.is_argument:
2715 symbol.interface = ArgumentInterface(
2716 ArgumentInterface.Access.UNKNOWN)
2717 arg_symbols.append(symbol)
2720 parent.symbol_table.specify_argument_list(arg_symbols)
2721 except KeyError
as info:
2722 decls_str_list = [str(node)
for node
in nodes]
2723 arg_str_list = [arg.string.lower()
for arg
in arg_list]
2725 f
"The argument list {arg_str_list} for routine '{parent.name}'"
2726 f
" does not match the variable declarations:\n"
2727 f
"{os.linesep.join(decls_str_list)}\n"
2728 f
"(Note that PSyclone does not support implicit declarations.)"
2729 f
" Specific PSyIR error is {info}.")
from info
2735 for stmtfn
in walk(nodes, Fortran2003.Stmt_Function_Stmt):
2736 (fn_name, arg_list, scalar_expr) = stmtfn.items
2738 symbol = parent.symbol_table.lookup(fn_name.string.lower())
2742 array_subscript = arg_list.items
2744 assignment_rhs = scalar_expr
2747 assignment = Assignment(parent=parent)
2748 parent.addchild(assignment)
2751 lhs = ArrayReference(symbol, parent=assignment)
2752 self.
process_nodesprocess_nodes(parent=lhs, nodes=array_subscript)
2753 assignment.addchild(lhs)
2757 nodes=[assignment_rhs])
2760 f
"Could not process '{stmtfn}'. Symbol "
2761 f
"'{symbol.name}' is in the SymbolTable but it is not "
2762 f
"an array as expected, so it can not be recovered as "
2763 f
"an array assignment.")
2764 except KeyError
as err:
2765 raise NotImplementedError(
2766 f
"Could not process '{stmtfn}'. Statement Function "
2767 f
"declarations are not supported.")
from err
2770 def _process_common_blocks(nodes, psyir_parent):
2771 ''' Process the fparser2 common block declaration statements. This is
2772 done after the other declarations and it will keep the statement
2773 as a UnsupportedFortranType and update the referenced symbols to a
2774 CommonBlockInterface.
2776 :param nodes: fparser2 AST nodes containing declaration statements.
2777 :type nodes: List[:py:class:`fparser.two.utils.Base`]
2778 :param psyir_parent: the PSyIR Node with a symbol table in which to
2779 add the Common Blocks and update the symbols interfaces.
2780 :type psyir_parent: :py:class:`psyclone.psyir.nodes.ScopingNode`
2782 :raises NotImplementedError: if one of the Symbols in a common block
2783 has initialisation (including when it is a parameter). This is not
2785 :raises NotImplementedError: if it is unable to find one of the
2786 CommonBlock expressions in the symbol table (because it has not
2787 been declared yet or when it is not just the symbol name).
2791 if isinstance(node, Fortran2003.Common_Stmt):
2797 psyir_parent.symbol_table.new_symbol(
2798 root_name=
"_PSYCLONE_INTERNAL_COMMONBLOCK",
2799 symbol_type=DataSymbol,
2800 datatype=UnsupportedFortranType(str(node)))
2807 for cb_object
in node.children[0]:
2808 for symbol_name
in cb_object[1].items:
2809 sym = psyir_parent.symbol_table.lookup(
2811 if sym.initial_value:
2813 raise NotImplementedError(
2814 f
"Symbol '{sym.name}' has an initial value"
2815 f
" ({sym.initial_value.debug_string()}) "
2816 f
"but appears in a common block. This is "
2817 f
"not valid Fortran.")
2818 sym.interface = CommonBlockInterface()
2819 except KeyError
as error:
2820 raise NotImplementedError(
2821 f
"The symbol interface of a common block variable "
2822 f
"could not be updated because of {error}.")
from error
2825 def _process_precision(type_spec, psyir_parent):
2826 '''Processes the fparser2 parse tree of the type specification of a
2827 variable declaration in order to extract precision
2828 information. Two formats for specifying precision are
2829 supported a) "*N" e.g. real*8 and b) "kind=" e.g. kind=i_def, or
2832 :param type_spec: the fparser2 parse tree of the type specification.
2834 :py:class:`fparser.two.Fortran2003.Intrinsic_Type_Spec`
2835 :param psyir_parent: the parent PSyIR node where the new node \
2837 :type psyir_parent: :py:class:`psyclone.psyir.nodes.Node`
2839 :returns: the precision associated with the type specification.
2840 :rtype: :py:class:`psyclone.psyir.symbols.DataSymbol.Precision` or \
2841 :py:class:`psyclone.psyir.symbols.DataSymbol` or int or NoneType
2843 :raises NotImplementedError: if a KIND intrinsic is found with an \
2844 argument other than a real or integer literal.
2845 :raises NotImplementedError: if we have `kind=xxx` but cannot find \
2846 a valid variable name.
2849 symbol_table = psyir_parent.scope.symbol_table
2851 if not isinstance(type_spec.items[1], Fortran2003.Kind_Selector):
2855 kind_selector = type_spec.items[1]
2857 if (isinstance(kind_selector.children[0], str)
and
2858 kind_selector.children[0] ==
"*"):
2860 precision = int(str(kind_selector.children[1]))
2864 intrinsics = walk(kind_selector.items,
2865 Fortran2003.Intrinsic_Function_Reference)
2866 if intrinsics
and isinstance(intrinsics[0].items[0],
2867 Fortran2003.Intrinsic_Name)
and \
2868 str(intrinsics[0].items[0]).lower() ==
"kind":
2872 kind_arg = intrinsics[0].items[1].items[0]
2876 if isinstance(kind_arg, (Fortran2003.Int_Literal_Constant,
2877 Fortran2003.Real_Literal_Constant)):
2878 return get_literal_precision(kind_arg, psyir_parent)
2880 raise NotImplementedError(
2881 f
"Only real and integer literals are supported as arguments "
2882 f
"to the KIND intrinsic but found "
2883 f
"'{type(kind_arg).__name__}' in: {kind_selector}")
2886 kind_names = walk(kind_selector.items, Fortran2003.Name)
2888 raise NotImplementedError(
2889 f
"Failed to find valid Name in Fortran Kind Selector: "
2890 f
"{kind_selector}'")
2892 return _kind_find_or_create(str(kind_names[0]), symbol_table)
2896 Create the PSyIR of the supplied list of nodes in the
2899 :param parent: Parent node in the PSyIR we are constructing.
2900 :type parent: :py:class:`psyclone.psyir.nodes.Node`
2901 :param nodes: List of sibling nodes in fparser2 AST.
2902 :type nodes: list[:py:class:`fparser.two.utils.Base`]
2905 code_block_nodes = []
2906 message =
"PSyclone CodeBlock (unsupported code) reason:"
2910 except NotImplementedError
as err:
2913 message +=
"\n - " + str(err)
2914 code_block_nodes.append(child)
2915 if not isinstance(parent, Schedule):
2921 message =
"PSyclone CodeBlock (unsupported code) reason:"
2925 message =
"PSyclone CodeBlock (unsupported code) reason:"
2926 parent.addchild(psy_child)
2933 def _create_child(self, child, parent=None):
2935 Create a PSyIR node representing the supplied fparser 2 node.
2937 :param child: node in fparser2 AST.
2938 :type child: :py:class:`fparser.two.utils.Base`
2939 :param parent: Parent node of the PSyIR node we are constructing.
2940 :type parent: :py:class:`psyclone.psyir.nodes.Node`
2942 :returns: Returns the PSyIR representation of child, which can be a \
2943 single node, a tree of nodes or None if the child can be \
2945 :rtype: :py:class:`psyclone.psyir.nodes.Node` or NoneType
2947 :raises NotImplementedError: if the child node has a label or there \
2948 isn't a handler for the provided child type.
2952 if isinstance(child, BlockBase):
2956 if (child.content
and child.content[0]
and
2957 child.content[0].item
and child.content[0].item.label):
2958 raise NotImplementedError(
"Unsupported labelled statement")
2959 elif isinstance(child, StmtBase):
2960 if child.item
and child.item.label:
2961 raise NotImplementedError(
"Unsupported labelled statement")
2963 handler = self.
handlershandlers.get(type(child))
2971 generic_type = type(child).__bases__[0]
2972 handler = self.
handlershandlers.get(generic_type)
2974 raise NotImplementedError(
2975 f
"Unsupported statement: {type(child).__name__}")
2976 return handler(child, parent)
2978 def _ignore_handler(self, *_):
2980 This handler returns None indicating that the associated
2981 fparser2 node can be ignored.
2983 Note that this method contains ignored arguments to comform with
2984 the handler(node, parent) method interface.
2991 def _include_handler(self, node, parent):
2993 Handler for Fortran and CPP INCLUDE statements. Since these are not
2994 supported by the PSyIR it simply raises an error.
2996 :param node: node in fparser2 tree.
2997 :type node: :py:class:`fparser.two.Fortran2003.Include_Stmt`
2998 :param parent: parent node of the PSyIR node we are constructing.
2999 :type parent: :py:class:`psyclone.psyir.nodes.Schedule`
3001 :raises GenerationError: as INCLUDE statements must be handled by \
3002 the parser or pre-processor.
3004 config = Config.get()
3007 unit = parent.ancestor((Routine, Container), include_self=
True)
3009 if isinstance(unit, Routine):
3011 out_txt = f
"program '{unit.name}'. "
3013 out_txt = f
"routine '{unit.name}'. "
3014 elif type(unit)
is Container:
3015 out_txt = f
"module '{unit.name}'. "
3017 out_txt = f
"code:\n{str(node.get_root())}\n"
3019 filename = node.children[0].string
3020 if isinstance(node, Fortran2003.Include_Stmt):
3022 f
"Found an unresolved Fortran INCLUDE file '{filename}' while "
3023 f
"processing {out_txt}This file must be made available by "
3024 f
"specifying its location with a -I flag. "
3025 f
"(The list of directories to search is currently set to: "
3026 f
"{config.include_paths}.)")
3029 err_msg = (f
"CPP #include statements are not supported but found a"
3030 f
" #include of file '{node.children[0].string}' while "
3031 f
"processing {out_txt}Such statements must be handled "
3032 f
"using a standard pre-processor before the code can "
3033 f
"be processed by PSyclone.")
3036 def _allocate_handler(self, node, parent):
3038 Transforms an fparser2 Allocate_Stmt into its PSyIR form.
3040 :param node: node in fparser2 tree.
3041 :type node: :py:class:`fparser.two.Fortran2003.Allocate_Stmt`
3042 :param parent: parent node of the PSyIR node we are constructing.
3043 :type parent: :py:class:`psyclone.psyir.nodes.Schedule`
3045 :returns: PSyIR representation of an allocate.
3046 :rtype: :py:class:`psyclone.psyir.nodes.IntrinsicCall`
3048 :raises NotImplementedError: if the allocate has a type specification \
3049 (e.g. allocate(character(len=10) :: my_var)).
3052 call = IntrinsicCall(IntrinsicCall.Intrinsic.ALLOCATE, parent=parent)
3054 type_spec = node.children[0]
3056 raise NotImplementedError(
3057 "Allocate statements with type specifications cannot be "
3058 "handled in the PSyIR")
3060 alloc_list = node.children[1].children
3062 for alloc
in alloc_list:
3066 if isinstance(alloc, (Fortran2003.Name, Fortran2003.Data_Ref)):
3074 nodes=[alloc.children[0]])
3075 cursor = call.children[-1]
3076 while hasattr(cursor,
"member"):
3077 cursor = cursor.member
3078 if isinstance(cursor, Member):
3080 aref = ArrayMember(cursor.name)
3083 aref = ArrayReference(cursor.symbol)
3084 cursor.replace_with(aref)
3087 for shape_spec
in walk(alloc,
3088 Fortran2003.Allocate_Shape_Spec):
3089 self.
process_nodesprocess_nodes(parent=aref, nodes=[shape_spec])
3092 opt_list = walk(node, Fortran2003.Alloc_Opt)
3093 for opt
in opt_list:
3094 self.
process_nodesprocess_nodes(parent=call, nodes=opt.children[1:])
3095 call.append_named_arg(opt.children[0], call.children[-1].detach())
3102 def _allocate_shape_spec_handler(self, node, parent):
3104 Creates a Range node describing the supplied Allocate_Shape_Spec.
3105 This is similar to the subscript_triplet handler except that the
3106 default lower bound is unity and the step is also unity.
3108 :param node: node in fparser2 AST.
3109 :type node: :py:class:`fparser.two.Fortran2003.Allocate_Shape_Spec`
3110 :param parent: parent node of the PSyIR node we are constructing.
3111 :type parent: :py:class:`psyclone.psyir.nodes.Reference`
3113 :returns: PSyIR of fparser2 node.
3114 :rtype: :py:class:`psyclone.psyir.nodes.Range`
3117 my_range = Range(parent=parent)
3118 my_range.children = []
3119 integer_type = default_integer_type()
3121 if node.children[0]:
3122 self.
process_nodesprocess_nodes(parent=my_range, nodes=[node.children[0]])
3125 my_range.addchild(Literal(
"1", integer_type))
3127 self.
process_nodesprocess_nodes(parent=my_range, nodes=[node.children[1]])
3130 my_range.addchild(Literal(
"1", integer_type))
3134 def _create_loop(self, parent, variable):
3136 Create a Loop instance. This is done outside _do_construct_handler
3137 because some APIs may want to instantiate a specialised Loop.
3139 :param parent: the parent of the node.
3140 :type parent: :py:class:`psyclone.psyir.nodes.Node`
3141 :param variable: the loop variable.
3142 :type variable: :py:class:`psyclone.psyir.symbols.DataSymbol`
3144 :return: a new Loop instance.
3145 :rtype: :py:class:`psyclone.psyir.nodes.Loop`
3148 return Loop(parent=parent, variable=variable)
3150 def _create_bounded_loop(self, parent, variable, limits_list):
3152 Create a Loop instance with start, stop, step expressions.
3154 :param parent: the parent of the node.
3155 :type parent: :py:class:`psyclone.psyir.nodes.Node`
3156 :param variable: the loop variable.
3157 :type variable: :py:class:`psyclone.psyir.symbols.DataSymbol`
3158 :param limits_list: a list of fparser expressions reprsenting the
3160 :type limits_list: List[:py:class:`fparser.two.utils.Base`]
3162 :return: a new Loop instance.
3163 :rtype: :py:class:`psyclone.psyir.nodes.Loop`
3167 variable_name = str(variable)
3168 data_symbol = _find_or_create_unresolved_symbol(
3169 parent, variable_name, symbol_type=DataSymbol,
3170 datatype=default_integer_type())
3174 loop = self.
_create_loop_create_loop(parent, data_symbol)
3178 self.
process_nodesprocess_nodes(parent=loop, nodes=[limits_list[0]])
3179 self.
process_nodesprocess_nodes(parent=loop, nodes=[limits_list[1]])
3180 if len(limits_list) == 3
and limits_list[2]
is not None:
3181 self.
process_nodesprocess_nodes(parent=loop, nodes=[limits_list[2]])
3186 default_step = Literal(
"1", default_integer_type())
3187 loop.addchild(default_step)
3190 loop_body = Schedule(parent=loop)
3191 loop.addchild(loop_body)
3194 def _deallocate_handler(self, node, parent):
3196 Transforms a deallocate() statement into its PSyIR form.
3198 :param node: node in fparser2 tree.
3199 :type node: :py:class:`fparser.two.Fortran2003.Deallocate_Stmt`
3200 :param parent: parent node of the PSyIR node we are constructing.
3201 :type parent: :py:class:`psyclone.psyir.nodes.Schedule`
3203 :returns: PSyIR for a deallocate.
3204 :rtype: :py:class:`psyclone.psyir.nodes.IntrinsicCall`
3207 call = IntrinsicCall(
3208 IntrinsicCall.Intrinsic.DEALLOCATE, parent=parent)
3209 dealloc_list = node.children[0].children
3210 for dealloc
in dealloc_list:
3211 self.
process_nodesprocess_nodes(parent=call, nodes=[dealloc])
3214 opt_list = walk(node, Fortran2003.Dealloc_Opt)
3215 for opt
in opt_list:
3216 self.
process_nodesprocess_nodes(parent=call, nodes=opt.children[1:])
3217 call.append_named_arg(opt.children[0], call.children[-1].detach())
3224 def _do_construct_handler(self, node, parent):
3226 Transforms a fparser2 Do Construct into its PSyIR form.
3228 :param node: node in fparser2 tree.
3230 :py:class:`fparser.two.Fortran2003.Block_Nonlabel_Do_Construct`
3231 :param parent: parent node of the PSyIR node we are constructing.
3232 :type parent: :py:class:`psyclone.psyir.nodes.Node`
3234 :returns: PSyIR representation of node
3235 :rtype: Union[:py:class:`psyclone.psyir.nodes.Loop`, \
3236 :py:class:`psyclone.psyir.nodes.WhileLoop`]
3238 :raises NotImplementedError: if the fparser2 tree has a named DO \
3239 containing a reference to that name.
3241 nonlabel_do = walk(node.content, Fortran2003.Nonlabel_Do_Stmt)[0]
3242 if nonlabel_do.item
is not None:
3245 if nonlabel_do.item.name:
3246 construct_name = nonlabel_do.item.name
3249 names = walk(node.content[:-1], Fortran2003.Name)
3250 if construct_name
in [name.string
for name
in names]:
3251 raise NotImplementedError(
3252 "Unsupported label reference within DO")
3254 ctrl = walk(nonlabel_do, Fortran2003.Loop_Control)
3259 if not ctrl
or ctrl[0].items[0]
is not None:
3261 annotation = [
'was_unconditional']
if not ctrl
else None
3262 loop = WhileLoop(parent=parent, annotations=annotation)
3264 condition = [Fortran2003.Logical_Literal_Constant(
".TRUE.")] \
3265 if not ctrl
else [ctrl[0].items[0]]
3266 self.
process_nodesprocess_nodes(parent=loop, nodes=condition)
3268 loop_body = Schedule(parent=loop)
3269 loop_body.ast = node
3270 loop.addchild(loop_body)
3271 elif ctrl[0].items[1]
is not None:
3273 loop_var, limits_list = ctrl[0].items[1]
3276 loop_body = loop.loop_body
3277 loop_body.ast = node
3278 elif ctrl[0].items[3]
is not None:
3283 triplet = walk(ctrl[0].items[3], Fortran2003.Forall_Triplet_Spec)
3285 for expr
in triplet:
3286 variable, start, stop, step = expr.items
3288 [start, stop, step])
3292 new_loop.loop_body.ast = node
3298 loop_body.addchild(new_loop)
3302 loop_body = new_loop.loop_body
3305 raise NotImplementedError(
"Unsupported Loop")
3308 self.
process_nodesprocess_nodes(parent=loop_body, nodes=node.content[1:-1])
3312 def _if_construct_handler(self, node, parent):
3314 Transforms an fparser2 If_Construct to the PSyIR representation.
3316 :param node: node in fparser2 tree.
3317 :type node: :py:class:`fparser.two.Fortran2003.If_Construct`
3318 :param parent: Parent node of the PSyIR node we are constructing.
3319 :type parent: :py:class:`psyclone.psyir.nodes.Node`
3320 :returns: PSyIR representation of node
3321 :rtype: :py:class:`psyclone.psyir.nodes.IfBlock`
3322 :raises InternalError: If the fparser2 tree has an unexpected \
3327 if not isinstance(node.content[0], Fortran2003.If_Then_Stmt):
3329 f
"Failed to find opening if then statement in: {node}")
3330 if not isinstance(node.content[-1], Fortran2003.End_If_Stmt):
3332 f
"Failed to find closing end if statement in: {node}")
3336 for idx, child
in enumerate(node.content):
3337 if isinstance(child, (Fortran2003.If_Then_Stmt,
3338 Fortran2003.Else_Stmt,
3339 Fortran2003.Else_If_Stmt,
3340 Fortran2003.End_If_Stmt)):
3341 clause_indices.append(idx)
3345 currentparent = parent
3346 num_clauses = len(clause_indices) - 1
3347 for idx
in range(num_clauses):
3348 start_idx = clause_indices[idx]
3349 end_idx = clause_indices[idx+1]
3350 clause = node.content[start_idx]
3352 if isinstance(clause, (Fortran2003.If_Then_Stmt,
3353 Fortran2003.Else_If_Stmt)):
3358 if isinstance(clause, Fortran2003.If_Then_Stmt):
3359 ifblock = IfBlock(parent=currentparent)
3361 newifblock = ifblock
3363 elsebody = Schedule(parent=currentparent)
3364 currentparent.addchild(elsebody)
3365 newifblock = IfBlock(parent=elsebody,
3366 annotations=[
'was_elseif'])
3367 elsebody.addchild(newifblock)
3370 elsebody.ast = node.content[start_idx]
3371 newifblock.ast = node.content[start_idx]
3375 nodes=[clause.items[0]])
3378 ifbody = Schedule(parent=newifblock)
3379 ifbody.ast = node.content[start_idx + 1]
3380 ifbody.ast_end = node.content[end_idx - 1]
3381 newifblock.addchild(ifbody)
3383 nodes=node.content[start_idx + 1:end_idx])
3385 currentparent = newifblock
3387 elif isinstance(clause, Fortran2003.Else_Stmt):
3388 if not idx == num_clauses - 1:
3390 f
"Else clause should only be found next to last "
3391 f
"clause, but found {node.content}")
3392 elsebody = Schedule(parent=currentparent)
3393 currentparent.addchild(elsebody)
3394 elsebody.ast = node.content[start_idx]
3395 elsebody.ast_end = node.content[end_idx]
3397 nodes=node.content[start_idx + 1:end_idx])
3400 f
"Only fparser2 If_Then_Stmt, Else_If_Stmt and Else_Stmt "
3401 f
"are expected, but found {clause}.")
3405 def _if_stmt_handler(self, node, parent):
3407 Transforms an fparser2 If_Stmt to the PSyIR representation.
3409 :param node: node in fparser2 AST.
3410 :type node: :py:class:`fparser.two.Fortran2003.If_Stmt`
3411 :param parent: Parent node of the PSyIR node we are constructing.
3412 :type parent: :py:class:`psyclone.psyir.nodes.Node`
3414 :returns: PSyIR representation of node
3415 :rtype: :py:class:`psyclone.psyir.nodes.IfBlock`
3418 ifblock = IfBlock(parent=parent, annotations=[
'was_single_stmt'])
3420 self.
process_nodesprocess_nodes(parent=ifblock, nodes=[node.items[0]])
3421 ifbody = Schedule(parent=ifblock)
3422 ifblock.addchild(ifbody)
3423 self.
process_nodesprocess_nodes(parent=ifbody, nodes=[node.items[1]])
3427 def _add_target_attribute(var_name, table):
3428 '''Ensure that the datatype of the symbol with the supplied name has a
3429 pointer or target attribute and if not, add the target attribute.
3431 The datatype is stored as text within an UnsupportedFortranType. We
3432 therefore re-create the datatype as an fparser2 ast, add the attribute
3433 if required and update the UnsupportedFortranType with the new text.
3435 :param str var_name: the name of the symbol for which we attempt to
3436 modify the datatype.
3437 :param table: a SymbolTable in which to search for the symbol.
3438 :type table: :py:class:`psyclone.psyir.symbols.SymbolTable`
3440 :raises NotImplementedError: if the variable cannot be found, is
3441 unresolved or is not a DataSymbol.
3442 :raises NotImplementedError: if the variable needs to be given
3443 the target attribute but represents a symbol defined externally
3444 (e.g. a routine argument or an imported symbol).
3448 symbol = table.lookup(var_name)
3449 except KeyError
as err:
3450 raise NotImplementedError(
3451 f
"Cannot add TARGET attribute to variable '{var_name}' "
3452 f
"because it is unresolved")
from err
3453 if symbol.is_unresolved
or not isinstance(symbol, DataSymbol):
3454 raise NotImplementedError(
3455 f
"Cannot add TARGET attribute to symbol '{symbol}': it must "
3456 f
"be resolved and a DataSymbol")
3458 datatype = symbol.datatype
3464 f
"subroutine dummy()\n"
3465 f
" {datatype.declaration}\n"
3466 f
"end subroutine\n")
3467 parser = ParserFactory().create(std=
"f2008")
3468 reader = FortranStringReader(dummy_code)
3469 fp2_ast = parser(reader)
3470 type_decl_stmt = fp2_ast.children[0].children[1].children[0]
3475 attr_spec_list = type_decl_stmt.children[1]
3476 attr_spec_str_list = []
3479 for attr_spec
in attr_spec_list.children:
3480 attr_spec_str = attr_spec.string
3481 attr_spec_str_list.append(attr_spec_str)
3482 if attr_spec_str.upper()
in [
"TARGET",
"POINTER"]:
3489 if not (symbol.is_automatic
or symbol.is_modulevar):
3490 raise NotImplementedError(
3491 f
"Type-selector variable '{symbol.name}' is defined externally"
3492 f
" (has interface '{symbol.interface}') and thus cannot be "
3493 f
"given the TARGET attribute")
3495 if attr_spec_str_list:
3498 attr_spec_str_list.append(
"TARGET")
3499 attr_spec_list = Fortran2003.Attr_Spec_List(
3500 ", ".join(attr_spec_str_list))
3503 attr_spec_list = Fortran2003.Attr_Spec_List(
"TARGET")
3504 type_decl_stmt.items = (
3505 type_decl_stmt.items[0], attr_spec_list,
3506 type_decl_stmt.items[2])
3507 attr_spec_list.parent = type_decl_stmt
3509 datatype._declaration = str(type_decl_stmt)
3511 def _create_ifblock_for_select_type_content(
3512 self, parent, select_type, type_string_symbol, pointer_symbols):
3513 '''Use the contents of the supplied SelectTypeInfo instance
3514 to create an if nest to capture the content of the associated
3515 select type construct.
3517 This allows the PSyIR to 'see' the content of the select type
3518 despite not supporting the select type clause directly in
3519 PSyIR. A Codeblock preceding this condition will capture the
3520 conditional logic of the select type and the chosen type will
3521 be communicated to the if nest at runtime via the
3522 'type_string_symbol'. The if nest created here captures the
3523 content of each branch of the original select type.
3525 :param parent: the PSyIR parent to which we are going to add
3526 the PSyIR ifblock and any required symbols.
3527 :type parent: :py:class:`psyclone.psyir.nodes.Node`
3528 :param select_type: information on the select type construct.
3529 :type select_type: :py:class:\
3530 `psyclone.psyir.frontend.fparser2.Fparser2Reader.SelectTypeInfo`
3531 :param type_string_symbol: a run-time symbol capturing (as a string)
3532 the value chosen by the select type construct.
3533 :type type_string_symbol: :py:class:`psyclone.psyir.type.DataSymbol`
3534 :param pointer_symbols: a list of symbols that point to the
3535 different select-type types within the select type codeblock.
3536 :type pointer_symbols:
3537 list[Optional[:py:class:`psyclone.psyir.symbols.Symbol`]]
3539 :returns: the newly created PSyIR IfBlock.
3540 :rtype: :py:class:`psyclone.psyir.nodes.IfBlock`
3542 :raises NotImplementedError: if there is a CodeBlock that contains a
3543 reference to the type-selector variable.
3546 outer_ifblock =
None
3548 currentparent = parent
3549 for idx
in range(select_type.num_clauses):
3550 if idx == select_type.default_idx:
3556 "was_class_is" if select_type.clause_type[idx].upper() ==
3557 "CLASS IS" else "was_type_is")
3560 elsebody = Schedule(parent=currentparent)
3561 ifblock = IfBlock(annotations=[annotation], parent=elsebody)
3562 elsebody.addchild(ifblock)
3563 currentparent.addchild(elsebody)
3566 ifblock = IfBlock(parent=currentparent,
3567 annotations=[annotation])
3568 outer_ifblock = ifblock
3574 clause = BinaryOperation.create(
3575 BinaryOperation.Operator.EQ, Reference(type_string_symbol),
3576 Literal(select_type.guard_type_name[idx], CHARACTER_TYPE))
3578 ifblock.addchild(clause)
3580 ifbody = Schedule(parent=ifblock)
3581 self.
process_nodesprocess_nodes(parent=ifbody, nodes=select_type.stmts[idx])
3584 for cblock
in ifbody.walk(CodeBlock):
3585 names = cblock.get_symbol_names()
3586 if select_type.selector
in names:
3587 raise NotImplementedError(
3588 f
"CodeBlock contains reference to type-selector "
3589 f
"variable '{select_type.selector}'")
3592 for reference
in ifbody.walk(Reference):
3593 symbol = reference.symbol
3594 if symbol.name.lower() == select_type.selector:
3595 reference.symbol = pointer_symbols[idx]
3596 ifblock.addchild(ifbody)
3597 currentparent = ifblock
3599 if select_type.default_idx >= 0:
3602 elsebody = Schedule(parent=currentparent)
3603 currentparent.addchild(elsebody)
3605 parent=elsebody, nodes=select_type.stmts[
3606 select_type.default_idx])
3608 return outer_ifblock
3611 def _create_select_type(
3612 parent, select_type, type_string_name=None):
3613 '''Use the contents of the supplied SelectTypeInfo, `select_type`,
3614 to create a CodeBlock containing a select type to capture its control
3615 logic without capturing its content.
3617 The 'output' of this CodeBlock is a character variable containing the
3618 'name' of the type that was provided, thus identifying which branch of
3619 the code would be executed. A pointer is also created and assigned
3620 to the type-selection of the 'type is' or 'class is' clause.
3622 :param parent: the PSyIR parent to which we are going to add
3623 the PSyIR codeblock and any required symbols.
3624 :type parent: :py:class:`psyclone.psyir.nodes.Node`
3625 :param select_type: instance of the SelectTypeInfo dataclass
3626 containing information about the select type construct.
3627 :type select_type: :py:class:`Self.SelectTypeInfo`
3628 :param Optional[str] type_string_name: the base name to use
3629 for the newly created type_string symbol.
3631 :returns: the DataSymbol representing the character variable which
3632 will hold the 'name' of the type and a list of symbols that
3633 point to the different select-type types within the select
3635 :rtype: tuple[:py:class:`psyclone.psyir.symbols.DataSymbol`,
3636 list[Optional[:py:class:`psyclone.psyir.symbols.Symbol`]]]
3639 pointer_symbols = []
3645 type_string_name = parent.scope.symbol_table.next_available_name(
3649 type_string_type = UnsupportedFortranType(
3650 f
"character(256) :: {type_string_name}")
3651 type_string_symbol = DataSymbol(type_string_name, type_string_type)
3652 parent.scope.symbol_table.add(type_string_symbol)
3659 code =
"program dummy\n"
3660 code += f
"select type({select_type.selector})\n"
3661 for idx
in range(select_type.num_clauses):
3662 if idx == select_type.default_idx:
3665 pointer_symbols.append(
None)
3669 pointer_name = parent.scope.symbol_table.next_available_name(
3670 f
"ptr_{select_type.guard_type_name[idx]}")
3671 if (select_type.intrinsic_type_name[idx]
and
3672 select_type.intrinsic_type_name[idx].upper() ==
3680 tmp_type =
"CHARACTER(LEN=256)"
3683 type_spec =
"CHARACTER(LEN = *)"
3687 tmp_type = f
"{select_type.guard_type[idx]}"
3688 if not select_type.intrinsic_type_name[idx]:
3690 tmp_type = f
"type({tmp_type})"
3693 type_spec = select_type.guard_type[idx]
3699 pointer_type = UnsupportedFortranType(
3700 f
"{tmp_type}, pointer :: {pointer_name} => null()")
3701 pointer_symbol = DataSymbol(pointer_name, pointer_type)
3702 parent.scope.symbol_table.add(pointer_symbol)
3703 pointer_symbols.append(pointer_symbol)
3708 code += f
" {select_type.clause_type[idx]} ({type_spec})\n"
3709 code += (f
" {type_string_name} = "
3710 f
"\"{select_type.guard_type_name[idx].lower()}\"\n")
3711 code += (f
" {pointer_name} => {select_type.selector}\n")
3712 code +=
"end select\n"
3713 code +=
"end program\n"
3717 parser = ParserFactory().create(std=
"f2008")
3718 reader = FortranStringReader(code)
3719 fp2_program = parser(reader)
3721 exec_part = walk(fp2_program, Fortran2003.Execution_Part)
3722 code_block = CodeBlock(exec_part, CodeBlock.Structure.STATEMENT,
3731 parent.addchild(Assignment.create(
3732 Reference(type_string_symbol), Literal(
"", CHARACTER_TYPE)))
3733 parent.addchild(code_block)
3735 return (type_string_symbol, pointer_symbols)
3738 def _create_select_type_info(node):
3739 '''Create and return a SelectTypeInfo instance that stores the required
3740 information for a select-type construct to be used by
3743 :param node: fparser2 node from which to extract the select-type
3745 :type node: :py:class:`fparser2.Fortran2003.Select_Type_Construct`
3747 :returns: instance of the SelectTypeInfo dataclass containing
3748 information about the select-type construct.
3749 :rtype: :py:class:`Self.SelectTypeInfo`
3755 for child
in node.children:
3756 if isinstance(child, Fortran2003.Select_Type_Stmt):
3759 if child.children[0]:
3762 raise NotImplementedError(
3763 f
"The selector variable '{child.children[1]}' is "
3764 f
"renamed to '{child.children[0]}' in the select "
3765 f
"clause '{str(node)}'. This is not yet supported in "
3769 select_type.selector = child.children[1].string.lower()
3770 elif isinstance(child, Fortran2003.Type_Guard_Stmt):
3774 select_type.stmts.append([])
3777 type_spec = child.children[1]
3779 intrinsic_base_name =
None
3780 if type_spec
is None:
3784 elif isinstance(type_spec, Fortran2003.Intrinsic_Type_Spec):
3788 intrinsic_base_name = str(type_spec.children[0]).lower()
3792 type_name = intrinsic_base_name
3794 type_spec.children[1], Fortran2003.Kind_Selector):
3797 kind_spec_value = type_spec.children[1].children[1]
3798 type_name = f
"{type_name}_{kind_spec_value}".lower()
3799 elif walk(type_spec, Fortran2003.Length_Selector):
3804 type_name = f
"{type_name}_star".lower()
3808 type_name = str(type_spec).lower()
3809 select_type.guard_type_name.append(type_name)
3811 select_type.guard_type.append(str(type_spec).lower())
3813 select_type.guard_type.append(
None)
3814 select_type.intrinsic_type_name.append(intrinsic_base_name)
3817 select_type.clause_type.append(child.children[0])
3818 if child.children[0].lower() ==
"class default":
3819 select_type.default_idx = select_idx
3820 elif isinstance(child, Fortran2003.End_Select_Type_Stmt):
3827 select_type.stmts[select_idx].append(child)
3828 select_type.num_clauses = select_idx + 1
3832 def _select_type_construct_handler(self, node, parent):
3834 Transforms an fparser2 Select_Type_Construct to the PSyIR
3835 representation (consisting of an Assignment, a CodeBlock
3838 :param node: node in fparser2 tree.
3839 :type node: :py:class:`fparser.two.Fortran2003.Select_Type_Construct`
3840 :param parent: parent node of the PSyIR node we are constructing.
3841 :type parent: :py:class:`psyclone.psyir.nodes.Node`
3843 :returns: PSyIR representation of the node.
3844 :rtype: :py:class:`psyclone.psyir.nodes.IfBlock`
3846 :raises NotImplementedError: if the symbol representing the type-
3847 selector variable is not resolved or is not a DataSymbol.
3854 insert_index = len(parent.children) - 1
3861 tmp_parent = Schedule(parent=parent)
3871 tmp_parent, select_type, type_string_name=
"type_string")
3878 parent, select_type, type_string_symbol, pointer_symbols)
3884 outer_ifblock.scope.symbol_table)
3888 for child
in reversed(tmp_parent.pop_all_children()):
3889 parent.addchild(child, index=insert_index)
3891 parent.scope.symbol_table.merge(tmp_parent.symbol_table)
3893 return outer_ifblock
3895 def _case_construct_handler(self, node, parent):
3897 Transforms an fparser2 Case_Construct to the PSyIR representation.
3899 :param node: node in fparser2 tree.
3900 :type node: :py:class:`fparser.two.Fortran2003.Case_Construct`
3901 :param parent: parent node of the PSyIR node we are constructing.
3902 :type parent: :py:class:`psyclone.psyir.nodes.Node`
3904 :returns: PSyIR representation of node
3905 :rtype: :py:class:`psyclone.psyir.nodes.IfBlock`
3907 :raises InternalError: If the fparser2 tree has an unexpected
3909 :raises NotImplementedError: If the fparser2 tree contains an
3910 unsupported structure and should be placed in a CodeBlock.
3914 if not isinstance(node.content[0], Fortran2003.Select_Case_Stmt):
3916 f
"Failed to find opening case statement in: {node}")
3917 if not isinstance(node.content[-1], Fortran2003.End_Select_Stmt):
3919 f
"Failed to find closing case statement in: {node}")
3929 default_clause_idx =
None
3930 for idx, child
in enumerate(node.content):
3931 if isinstance(child, Fortran2003.Select_Case_Stmt):
3932 selector = child.items[0]
3933 if isinstance(child, Fortran2003.Case_Stmt):
3934 if not isinstance(child.items[0], Fortran2003.Case_Selector):
3936 f
"Unexpected parse tree structure. Expected child of "
3937 f
"Case_Stmt to be a Case_Selector but got: "
3938 f
"'{type(child.items[0]).__name__}'")
3939 case_expression = child.items[0].items[0]
3940 if case_expression
is None:
3944 default_clause_idx = idx
3945 clause_indices.append(idx)
3946 if isinstance(child, Fortran2003.End_Select_Stmt):
3947 clause_indices.append(idx)
3951 currentparent = parent
3952 num_clauses = len(clause_indices) - 1
3953 for idx
in range(num_clauses):
3955 if clause_indices[idx] == default_clause_idx:
3957 start_idx = clause_indices[idx]
3958 end_idx = clause_indices[idx+1]
3959 clause = node.content[start_idx]
3960 case = clause.items[0]
3965 elsebody = Schedule(parent=currentparent)
3966 currentparent.addchild(elsebody)
3967 ifblock = IfBlock(annotations=[
'was_case'])
3968 elsebody.addchild(ifblock)
3969 elsebody.ast = node.content[start_idx + 1]
3970 elsebody.ast_end = node.content[end_idx - 1]
3972 ifblock = IfBlock(parent=currentparent,
3973 annotations=[
'was_case'])
3984 ifblock.ast = node.content[start_idx + 1]
3985 ifblock.ast_end = node.content[end_idx - 1]
3992 ifbody = Schedule(parent=ifblock)
3994 nodes=node.content[start_idx + 1:
3996 ifblock.addchild(ifbody)
3997 ifbody.ast = node.content[start_idx + 1]
3998 ifbody.ast_end = node.content[end_idx - 1]
4000 currentparent = ifblock
4002 if default_clause_idx:
4008 elsebody = Schedule(parent=currentparent)
4009 currentparent.addchild(elsebody)
4010 currentparent = elsebody
4011 start_idx = default_clause_idx + 1
4015 for idx
in clause_indices:
4016 if idx > default_clause_idx:
4021 nodes=node.content[start_idx:end_idx])
4023 elsebody.ast = node.content[start_idx]
4024 elsebody.ast_end = node.content[end_idx - 1]
4027 def _process_case_value_list(self, selector, nodes, parent):
4029 Processes the supplied list of fparser2 nodes representing case-value
4030 expressions and constructs the equivalent PSyIR representation.
4033 SELECT CASE(my_flag)
4034 CASE(var1, var2:var3, :var5)
4038 the equivalent logical expression is:
4040 my_flag == var1 OR (myflag>=var2 AND myflag <= var3) OR my_flag <= var5
4042 and the corresponding structure of the PSyIR that we create is:
4052 :param selector: the fparser2 parse tree representing the \
4053 selector_expression in SELECT CASE(selector_expression).
4054 :type selector: sub-class of :py:class:`fparser.two.utils.Base`
4055 :param nodes: the nodes representing the label-list of the current \
4057 :type nodes: list of :py:class:`fparser.two.Fortran2003.Name` or \
4058 :py:class:`fparser.two.Fortran2003.Case_Value_Range`
4059 :param parent: parent node in the PSyIR.
4060 :type parent: :py:class:`psyclone.psyir.nodes.Node`
4070 orop = BinaryOperation(BinaryOperation.Operator.OR,
4074 parent.addchild(orop)
4076 def _process_case_value(self, selector, node, parent):
4078 Handles an individual condition inside a CASE statement. This can
4079 be a single scalar expression (e.g. CASE(1)) or a range specification
4080 (e.g. CASE(lim1:lim2)).
4082 :param selector: the node in the fparser2 parse tree representing the
4083 'some_expr' of the SELECT CASE(some_expr).
4084 :type selector: sub-class of :py:class:`fparser.two.utils.Base`
4085 :param node: the node representing the case-value expression in the \
4086 fparser2 parse tree.
4087 :type node: sub-class of :py:class:`fparser.two.utils.Base`
4088 :param parent: parent node in the PSyIR.
4089 :type parent: :py:class:`psyclone.psyir.nodes.Node`
4091 :raises NotImplementedError: If the operator for an equality cannot be
4092 determined (i.e. the statement cannot be
4093 determined to be a logical comparison
4096 if isinstance(node, Fortran2003.Case_Value_Range):
4098 if node.items[0]
and node.items[1]:
4100 aop = BinaryOperation(BinaryOperation.Operator.AND,
4102 parent.addchild(aop)
4110 geop = BinaryOperation(BinaryOperation.Operator.GE,
4112 self.
process_nodesprocess_nodes(parent=geop, nodes=[selector])
4113 self.
process_nodesprocess_nodes(parent=geop, nodes=[node.items[0]])
4114 new_parent.addchild(geop)
4117 leop = BinaryOperation(BinaryOperation.Operator.LE,
4119 self.
process_nodesprocess_nodes(parent=leop, nodes=[selector])
4120 self.
process_nodesprocess_nodes(parent=leop, nodes=[node.items[1]])
4121 new_parent.addchild(leop)
4124 fake_parent = Assignment(parent=parent)
4125 self.
process_nodesprocess_nodes(parent=fake_parent, nodes=[selector])
4126 self.
process_nodesprocess_nodes(parent=fake_parent, nodes=[node])
4128 for operand
in fake_parent.lhs, fake_parent.rhs:
4132 if (hasattr(operand,
"datatype")
and
4133 isinstance(operand.datatype, ScalarType)):
4134 if (operand.datatype.intrinsic ==
4135 ScalarType.Intrinsic.BOOLEAN):
4136 bop = BinaryOperation(BinaryOperation.Operator.EQV,
4139 bop = BinaryOperation(BinaryOperation.Operator.EQ,
4141 parent.addchild(bop)
4142 bop.children.extend(fake_parent.pop_all_children())
4147 cmp_symbol = _find_or_create_psyclone_internal_cmp(parent)
4148 call = Call(parent=parent)
4149 call.addchild(Reference(cmp_symbol))
4150 parent.addchild(call)
4151 call.children.extend(fake_parent.pop_all_children())
4154 def _array_notation_rank(node):
4155 '''Check that the supplied candidate array reference uses supported
4156 array notation syntax and return the rank of the sub-section
4157 of the array that uses array notation. e.g. for a reference
4158 "a(:, 2, :)" the rank of the sub-section is 2.
4160 :param node: the reference to check.
4161 :type node: :py:class:`psyclone.psyir.nodes.ArrayReference` or \
4162 :py:class:`psyclone.psyir.nodes.ArrayMember` or \
4163 :py:class:`psyclone.psyir.nodes.StructureReference`
4165 :returns: rank of the sub-section of the array.
4168 :raises InternalError: if no ArrayMixin node with at least one \
4169 Range in its indices is found.
4170 :raises InternalError: if two or more part references in a \
4171 structure reference contain ranges.
4172 :raises NotImplementedError: if the supplied node is not of a \
4174 :raises NotImplementedError: if any ranges are encountered that are \
4175 not for the full extent of the dimension.
4177 if isinstance(node, (ArrayReference, ArrayMember)):
4179 elif isinstance(node, StructureReference):
4181 arrays = node.walk((ArrayMember, ArrayOfStructuresMixin))
4182 for part_ref
in arrays:
4183 if any(isinstance(idx, Range)
for idx
in part_ref.indices):
4188 f
"Found a structure reference containing two or "
4189 f
"more part references that have ranges: "
4190 f
"'{node.debug_string()}'. This is not valid "
4191 f
"within a WHERE in Fortran.")
4195 f
"No array access found in node '{node.name}'")
4198 raise NotImplementedError(
4199 f
"Expected either an ArrayReference, ArrayMember or a "
4200 f
"StructureReference but got '{type(node).__name__}'")
4211 for idx_node
in array.indices:
4212 if isinstance(idx_node, Range):
4215 if not _is_range_full_extent(idx_node):
4216 raise NotImplementedError(
4217 "Only array notation of the form my_array(:, :, ...) "
4222 def _array_syntax_to_indexed(self, parent, loop_vars):
4224 Utility function that modifies each ArrayReference object in the
4225 supplied PSyIR fragment so that they are indexed using the supplied
4226 loop variables rather than having colon array notation. This indexing
4227 is always done relative to the declared lower bound of the array being
4230 :param parent: root of PSyIR sub-tree to search for Array
4231 references to modify.
4232 :type parent: :py:class:`psyclone.psyir.nodes.Node`
4233 :param loop_vars: the variable names for the array indices.
4234 :type loop_vars: list of str
4236 :raises NotImplementedError: if array sections of differing ranks are
4239 assigns = parent.walk(Assignment)
4244 for assign
in assigns:
4253 table = parent.scope.symbol_table
4254 one = Literal(
"1", INTEGER_TYPE)
4255 arrays = parent.walk(ArrayMixin)
4257 for array
in arrays:
4260 rank = len([child
for child
in array.indices
if
4261 isinstance(child, Range)])
4268 if rank != first_rank:
4269 raise NotImplementedError(
4270 f
"Found array sections of differing ranks within a "
4271 f
"WHERE construct: array section of {array.name} has "
4276 base_ref = _copy_full_base_reference(array)
4277 array_ref = array.ancestor(Reference, include_self=
True)
4278 shape = array_ref.datatype.shape
4279 add_op = BinaryOperation.Operator.ADD
4280 sub_op = BinaryOperation.Operator.SUB
4283 for idx, child
in enumerate(array.indices):
4284 if not isinstance(child, Range):
4290 if isinstance(shape[range_idx], ArrayType.Extent):
4293 lbound = IntrinsicCall.create(
4294 IntrinsicCall.Intrinsic.LBOUND,
4296 (
"dim", Literal(str(idx+1), INTEGER_TYPE))])
4298 if array.is_full_range(idx):
4305 lbound = IntrinsicCall.create(
4306 IntrinsicCall.Intrinsic.LBOUND,
4308 (
"dim", Literal(str(idx+1), INTEGER_TYPE))])
4311 lbound = child.start.copy()
4314 symbol = table.lookup(loop_vars[range_idx])
4315 if isinstance(lbound, Literal)
and lbound.value ==
"1":
4318 expr2 = Reference(symbol)
4323 expr = BinaryOperation.create(
4324 add_op, lbound, Reference(symbol))
4325 expr2 = BinaryOperation.create(sub_op, expr, one.copy())
4326 array.children[idx] = expr2
4329 def _where_construct_handler(self, node, parent):
4331 Construct the canonical PSyIR representation of a WHERE construct or
4332 statement. A construct has the form:
4336 [ELSE WHERE(logical-mask)
4342 while a statement is just:
4344 WHERE(logical-mask) statement
4346 :param node: node in the fparser2 parse tree representing the WHERE.
4347 :type node: :py:class:`fparser.two.Fortran2003.Where_Construct` |
4348 :py:class:`fparser.two.Fortran2003.Where_Stmt`
4349 :param parent: parent node in the PSyIR.
4350 :type parent: :py:class:`psyclone.psyir.nodes.Node`
4352 :returns: the top-level Loop object in the created loop nest.
4353 :rtype: :py:class:`psyclone.psyir.nodes.Loop`
4355 :raises InternalError: if the parse tree does not have the expected
4357 :raises NotImplementedError: if the parse tree contains a Fortran
4358 intrinsic that performs a reduction but still returns an array.
4359 :raises NotImplementedError: if the logical mask of the WHERE does
4360 not use array notation.
4363 def _contains_intrinsic_reduction(pnodes):
4365 Utility to check for Fortran intrinsics that perform a reduction
4366 but result in an array.
4368 :param pnodes: node(s) in the parse tree to check.
4369 :type pnodes: list[:py:class:`fparser.two.utils.Base`] |
4370 :py:class:`fparser.two.utils.Base`
4372 :returns: whether or not the supplied node(s) in the parse tree
4373 contain a call to an intrinsic that performs a reduction
4378 intr_nodes = walk(pnodes, Fortran2003.Intrinsic_Function_Reference)
4379 for intr
in intr_nodes:
4380 if (intr.children[0].string
in
4381 Fortran2003.Intrinsic_Name.array_reduction_names):
4384 arg_specs = walk(intr.children[1],
4385 Fortran2003.Actual_Arg_Spec)
4386 if any(spec.children[0].string ==
'dim'
4387 for spec
in arg_specs):
4391 if isinstance(node, Fortran2003.Where_Stmt):
4394 if not len(node.items) == 2:
4396 f
"Expected a Fortran2003.Where_Stmt to have exactly two "
4397 f
"entries in 'items' but found {len(node.items)}: "
4399 if not isinstance(node.items[1], Fortran2003.Assignment_Stmt):
4401 f
"Expected the second entry of a Fortran2003.Where_Stmt "
4402 f
"items tuple to be an Assignment_Stmt but found: "
4403 f
"{type(node.items[1]).__name__}")
4404 if _contains_intrinsic_reduction(node.items[1]):
4405 raise NotImplementedError(
4406 f
"TODO #1960 - WHERE statements which contain array-"
4407 f
"reduction intrinsics are not supported but found "
4409 was_single_stmt =
True
4410 annotations = [
"was_where",
"was_single_stmt"]
4411 logical_expr = [node.items[0]]
4415 if not isinstance(node.content[0],
4416 Fortran2003.Where_Construct_Stmt):
4417 raise InternalError(f
"Failed to find opening where construct "
4418 f
"statement in: {node}")
4419 if not isinstance(node.content[-1], Fortran2003.End_Where_Stmt):
4421 f
"statement in: {node}")
4422 if _contains_intrinsic_reduction(node.content[1:-1]):
4423 raise NotImplementedError(
4424 f
"TODO #1960 - WHERE constructs which contain an array-"
4425 f
"reduction intrinsic are not supported but found "
4427 was_single_stmt =
False
4428 annotations = [
"was_where"]
4429 logical_expr = node.content[0].items
4439 if _contains_intrinsic_reduction(logical_expr):
4440 raise NotImplementedError(
4441 f
"TODO #1960 - WHERE constructs which contain an array-"
4442 f
"reduction intrinsic in their logical expression are not "
4443 f
"supported but found '{logical_expr}'")
4449 fake_parent = Assignment(parent=parent)
4451 arrays = fake_parent.walk(ArrayMixin)
4459 raise NotImplementedError(
4460 f
"Only WHERE constructs using explicit array notation (e.g. "
4461 f
"my_array(:,:)) are supported but found '{logical_expr}'.")
4463 for array
in arrays:
4464 if any(isinstance(idx, Range)
for idx
in array.indices):
4468 raise NotImplementedError(
4469 f
"Only WHERE constructs using explicit array notation "
4470 f
"including ranges (e.g. 'my_array(1,:)') are supported but "
4471 f
"found '{logical_expr}'")
4473 array_ref = first_array.ancestor(Reference, include_self=
True)
4474 mask_shape = array_ref.datatype.shape
4477 rank = len(mask_shape)
4480 loop_vars = rank*[
""]
4482 symbol_table = parent.scope.symbol_table
4483 integer_type = default_integer_type()
4487 for idx
in range(rank, 0, -1):
4489 data_symbol = symbol_table.new_symbol(
4490 f
"widx{idx}", symbol_type=DataSymbol, datatype=integer_type)
4491 loop_vars[idx-1] = data_symbol.name
4493 loop = Loop(parent=new_parent, variable=data_symbol,
4494 annotations=annotations)
4501 loop.addchild(Literal(
"1", integer_type))
4503 if isinstance(mask_shape[idx-1], ArrayType.Extent):
4507 IntrinsicCall.create(IntrinsicCall.Intrinsic.SIZE,
4509 (
"dim", Literal(str(idx),
4512 loop.addchild(mask_shape[idx-1].upper.copy())
4515 loop.addchild(Literal(
"1", integer_type))
4517 sched = Schedule(parent=loop)
4518 loop.addchild(sched)
4522 if new_parent
is not parent:
4523 new_parent.addchild(loop)
4532 ifblock = IfBlock(parent=new_parent, annotations=annotations)
4534 new_parent.addchild(ifblock)
4550 sched = Schedule(parent=ifblock)
4551 ifblock.addchild(sched)
4559 for idx, child
in enumerate(node.content):
4560 if isinstance(child, (Fortran2003.Elsewhere_Stmt,
4561 Fortran2003.Masked_Elsewhere_Stmt,
4562 Fortran2003.End_Where_Stmt)):
4563 clause_indices.append(idx)
4564 num_clauses = len(clause_indices) - 1
4566 if len(clause_indices) > 1:
4569 self.
process_nodesprocess_nodes(sched, node.content[1:clause_indices[0]])
4570 current_parent = ifblock
4572 for idx
in range(num_clauses):
4573 start_idx = clause_indices[idx]
4574 end_idx = clause_indices[idx+1]
4575 clause = node.content[start_idx]
4577 if isinstance(clause, Fortran2003.Masked_Elsewhere_Stmt):
4578 elsebody = Schedule(parent=current_parent)
4579 current_parent.addchild(elsebody)
4580 newifblock = IfBlock(parent=elsebody,
4581 annotations=annotations)
4582 elsebody.addchild(newifblock)
4585 elsebody.ast = node.content[start_idx]
4586 newifblock.ast = node.content[start_idx]
4590 nodes=[clause.items[0]])
4593 ifbody = Schedule(parent=newifblock)
4594 ifbody.ast = node.content[start_idx + 1]
4595 ifbody.ast_end = node.content[end_idx - 1]
4596 newifblock.addchild(ifbody)
4599 nodes=node.content[start_idx+1:end_idx])
4600 current_parent = newifblock
4602 elif isinstance(clause, Fortran2003.Elsewhere_Stmt):
4603 if idx != num_clauses - 1:
4605 f
"Elsewhere_Stmt should only be found next to "
4606 f
"last clause, but found {node.content}")
4607 elsebody = Schedule(parent=current_parent)
4608 current_parent.addchild(elsebody)
4609 elsebody.ast = node.content[start_idx]
4610 elsebody.ast_end = node.content[end_idx]
4613 nodes=node.content[start_idx + 1:end_idx])
4617 f
"Expected either Fortran2003.Masked_Elsewhere"
4618 f
"_Stmt or Fortran2003.Elsewhere_Stmt but found "
4619 f
"'{type(clause).__name__}'")
4630 def _return_handler(self, node, parent):
4632 Transforms an fparser2 Return_Stmt to the PSyIR representation.
4634 :param node: node in fparser2 parse tree.
4635 :type node: :py:class:`fparser.two.Fortran2003.Return_Stmt`
4636 :param parent: Parent node of the PSyIR node we are constructing.
4637 :type parent: :py:class:`psyclone.psyir.nodes.Node`
4639 :return: PSyIR representation of node.
4640 :rtype: :py:class:`psyclone.psyir.nodes.Return`
4643 rtn = Return(parent=parent)
4647 def _assignment_handler(self, node, parent):
4649 Transforms an fparser2 Assignment_Stmt to the PSyIR representation.
4651 :param node: node in fparser2 AST.
4652 :type node: :py:class:`fparser.two.Fortran2003.Assignment_Stmt`
4653 :param parent: Parent node of the PSyIR node we are constructing.
4654 :type parent: :py:class:`psyclone.psyir.nodes.Node`
4656 :returns: PSyIR representation of node.
4657 :rtype: :py:class:`psyclone.psyir.nodes.Assignment`
4659 assignment = Assignment(node, parent=parent)
4660 self.
process_nodesprocess_nodes(parent=assignment, nodes=[node.items[0]])
4661 self.
process_nodesprocess_nodes(parent=assignment, nodes=[node.items[2]])
4665 def _structure_accessor_handler(self, node, parent):
4667 Create the PSyIR for structure accessors found in fparser2 Data_Ref and
4668 Procedure_Designator (representing an access to a derived type data and
4669 methods respectively).
4671 :param node: node in fparser2 parse tree.
4672 :type node: :py:class:`fparser.two.Fortran2003.Data_Ref` |
4673 :py:class:`fparser.two.Fortran2003.Process_Designator`
4674 :param parent: Parent node of the PSyIR node we are constructing.
4675 :type parent: :py:class:`psyclone.psyir.nodes.Node`
4677 :return: PSyIR representation of node
4678 :rtype: :py:class:`psyclone.psyir.nodes.StructureReference` |
4679 :py:class:`psyclone.psyir.nodes.ArrayOfStructuresReference`
4681 :raises NotImplementedError: if the parse tree contains unsupported
4691 if isinstance(node, Fortran2003.Procedure_Designator):
4696 node, _, binding_name = node.children
4700 if isinstance(node, Fortran2003.Data_Ref):
4703 top_ref = node.children[0]
4704 member_nodes = node.children[1:]
4709 if isinstance(top_ref, Fortran2003.Name):
4712 base_sym = _find_or_create_unresolved_symbol(
4713 parent, top_ref.string.lower(),
4714 symbol_type=DataSymbol, datatype=UnresolvedType())
4716 base_ref = StructureReference
4717 elif isinstance(top_ref, Fortran2003.Part_Ref):
4720 base_sym = _find_or_create_unresolved_symbol(
4721 parent, top_ref.children[0].string.lower(),
4722 symbol_type=DataSymbol, datatype=UnresolvedType())
4725 sched = parent.ancestor(Schedule, include_self=
True)
4726 aref = ArrayReference(parent=sched, symbol=base_sym)
4730 nodes=top_ref.children[1].children)
4731 base_indices = aref.pop_all_children()
4732 base_ref = ArrayOfStructuresReference
4736 raise NotImplementedError(str(node))
4742 for child
in member_nodes:
4743 if isinstance(child, Fortran2003.Name):
4745 members.append(child.string)
4746 elif isinstance(child, Fortran2003.Part_Ref):
4750 sched = parent.ancestor(Schedule, include_self=
True)
4757 array_name = child.children[0].string
4758 new_ref = _create_struct_reference(
4759 sched, base_ref, base_sym,
4760 members + [(array_name, [Literal(
"1", INTEGER_TYPE)])],
4764 current_ref = new_ref
4765 while hasattr(current_ref,
"member"):
4766 current_ref = current_ref.member
4768 current_ref.pop_all_children()
4771 nodes=child.children[1].children)
4774 children = current_ref.pop_all_children()
4775 members.append((array_name, children))
4779 raise NotImplementedError(str(node))
4782 members.append(str(binding_name))
4786 return _create_struct_reference(parent, base_ref, base_sym,
4787 members, base_indices)
4789 def _unary_op_handler(self, node, parent):
4791 Transforms an fparser2 UnaryOpBase to its PSyIR representation.
4793 :param node: node in fparser2 AST.
4794 :type node: :py:class:`fparser.two.utils.UnaryOpBase`
4795 :param parent: Parent node of the PSyIR node we are constructing.
4796 :type parent: :py:class:`psyclone.psyir.nodes.Node`
4798 :return: PSyIR representation of node
4799 :rtype: :py:class:`psyclone.psyir.nodes.UnaryOperation`
4801 :raises NotImplementedError: if the supplied operator is not
4802 supported by this handler.
4805 operator_str = str(node.items[0]).lower()
4807 operator = Fparser2Reader.unary_operators[operator_str]
4808 except KeyError
as err:
4810 raise NotImplementedError(operator_str)
from err
4812 unary_op = UnaryOperation(operator, parent=parent)
4813 self.
process_nodesprocess_nodes(parent=unary_op, nodes=[node.items[1]])
4816 def _binary_op_handler(self, node, parent):
4818 Transforms an fparser2 BinaryOp to its PSyIR representation.
4820 :param node: node in fparser2 AST.
4821 :type node: :py:class:`fparser.two.utils.BinaryOpBase`
4822 :param parent: Parent node of the PSyIR node we are constructing.
4823 :type parent: :py:class:`psyclone.psyir.nodes.Node`
4825 :returns: PSyIR representation of node
4826 :rtype: :py:class:`psyclone.psyir.nodes.BinaryOperation`
4828 :raises NotImplementedError: if the supplied operator is not supported
4832 operator_str = node.items[1].lower()
4833 arg_nodes = [node.items[0], node.items[2]]
4836 operator = Fparser2Reader.binary_operators[operator_str]
4837 except KeyError
as err:
4839 raise NotImplementedError(operator_str)
from err
4841 binary_op = BinaryOperation(operator, parent=parent)
4842 self.
process_nodesprocess_nodes(parent=binary_op, nodes=[arg_nodes[0]])
4843 self.
process_nodesprocess_nodes(parent=binary_op, nodes=[arg_nodes[1]])
4846 def _intrinsic_handler(self, node, parent):
4847 '''Transforms an fparser2 Intrinsic_Function_Reference to the PSyIR
4850 :param node: node in fparser2 Parse Tree.
4852 :py:class:`fparser.two.Fortran2003.Intrinsic_Function_Reference`
4853 :param parent: Parent node of the PSyIR node we are constructing.
4854 :type parent: :py:class:`psyclone.psyir.nodes.Node`
4856 :returns: PSyIR representation of node
4857 :rtype: :py:class:`psyclone.psyir.nodes.IntrinsicCall`
4859 :raises NotImplementedError: if an unsupported intrinsic is found.
4863 intrinsic = IntrinsicCall.Intrinsic[node.items[0].string.upper()]
4865 if not intrinsic.optional_args:
4867 call = IntrinsicCall(intrinsic, parent=parent)
4869 if intrinsic.name.lower()
in [
"minval",
"maxval",
"sum"]:
4872 call = IntrinsicCall(intrinsic, parent=parent)
4874 node, call, canonicalise=_canonicalise_minmaxsum)
4878 call = IntrinsicCall(intrinsic, parent=parent)
4880 except KeyError
as err:
4881 raise NotImplementedError(
4882 f
"Intrinsic '{node.items[0].string}' is not supported"
4885 def _name_handler(self, node, parent):
4887 Transforms an fparser2 Name to the PSyIR representation. If the parent
4888 is connected to a SymbolTable, it checks the reference has been
4889 previously declared.
4891 :param node: node in fparser2 AST.
4892 :type node: :py:class:`fparser.two.Fortran2003.Name`
4893 :param parent: Parent node of the PSyIR node we are constructing.
4894 :type parent: :py:class:`psyclone.psyir.nodes.Node`
4896 :returns: PSyIR representation of node
4897 :rtype: :py:class:`psyclone.psyir.nodes.Reference`
4900 symbol = _find_or_create_unresolved_symbol(parent, node.string)
4901 return Reference(symbol, parent=parent)
4903 def _parenthesis_handler(self, node, parent):
4905 Transforms an fparser2 Parenthesis to the PSyIR representation.
4906 This means ignoring the parentheis and process the fparser2 children
4909 :param node: node in fparser2 AST.
4910 :type node: :py:class:`fparser.two.Fortran2003.Parenthesis`
4911 :param parent: Parent node of the PSyIR node we are constructing.
4912 :type parent: :py:class:`psyclone.psyir.nodes.Node`
4913 :returns: PSyIR representation of node
4914 :rtype: :py:class:`psyclone.psyir.nodes.Node`
4919 return self.
_create_child_create_child(node.items[1], parent)
4921 def _part_ref_handler(self, node, parent):
4923 Transforms an fparser2 Part_Ref to the PSyIR representation. If the
4924 node is connected to a SymbolTable, it checks the reference has been
4925 previously declared.
4927 :param node: node in fparser2 AST.
4928 :type node: :py:class:`fparser.two.Fortran2003.Part_Ref`
4929 :param parent: Parent node of the PSyIR node we are constructing.
4930 :type parent: :py:class:`psyclone.psyir.nodes.Node`
4932 :raises NotImplementedError: if the fparser node represents \
4933 unsupported PSyIR features and should be placed in a CodeBlock.
4935 :returns: the PSyIR node.
4936 :rtype: :py:class:`psyclone.psyir.nodes.ArrayReference` or \
4937 :py:class:`psyclone.psyir.nodes.Call`
4940 reference_name = node.items[0].string.lower()
4944 symbol = _find_or_create_unresolved_symbol(parent, reference_name)
4946 if isinstance(symbol, RoutineSymbol):
4947 call_or_array = Call(parent=parent)
4948 call_or_array.addchild(Reference(symbol))
4950 call_or_array = ArrayReference(symbol, parent=parent)
4951 self.
process_nodesprocess_nodes(parent=call_or_array, nodes=node.items[1].items)
4952 return call_or_array
4954 def _subscript_triplet_handler(self, node, parent):
4956 Transforms an fparser2 Subscript_Triplet to the PSyIR
4959 :param node: node in fparser2 AST.
4960 :type node: :py:class:`fparser.two.Fortran2003.Subscript_Triplet`
4961 :param parent: parent node of the PSyIR node we are constructing.
4962 :type parent: :py:class:`psyclone.psyir.nodes.Node`
4964 :returns: PSyIR of fparser2 node.
4965 :rtype: :py:class:`psyclone.psyir.nodes.Range`
4967 :raises InternalError: if the supplied parent node is not a sub-class \
4968 of either Reference or Member.
4980 if isinstance(parent, (Reference, Member)):
4981 dimension = str(len([kid
for kid
in parent.children
if
4982 not isinstance(kid, Member)]) + 1)
4985 f
"Expected parent PSyIR node to be either a Reference or a "
4986 f
"Member but got '{type(parent).__name__}' when processing "
4989 integer_type = default_integer_type()
4990 my_range = Range(parent=parent)
4991 my_range.children = []
4993 if node.children[0]:
4994 self.
process_nodesprocess_nodes(parent=my_range, nodes=[node.children[0]])
5000 lbound = IntrinsicCall.create(
5001 IntrinsicCall.Intrinsic.LBOUND,
5002 [_copy_full_base_reference(parent),
5003 (
"dim", Literal(dimension, integer_type))])
5004 my_range.children.append(lbound)
5006 if node.children[1]:
5007 self.
process_nodesprocess_nodes(parent=my_range, nodes=[node.children[1]])
5013 ubound = IntrinsicCall.create(
5014 IntrinsicCall.Intrinsic.UBOUND,
5015 [_copy_full_base_reference(parent),
5016 (
"dim", Literal(dimension, integer_type))])
5017 my_range.children.append(ubound)
5019 if node.children[2]:
5020 self.
process_nodesprocess_nodes(parent=my_range, nodes=[node.children[2]])
5026 literal = Literal(
"1", integer_type)
5027 my_range.children.append(literal)
5030 def _number_handler(self, node, parent):
5032 Transforms an fparser2 NumberBase to the PSyIR representation.
5034 :param node: node in fparser2 parse tree.
5035 :type node: :py:class:`fparser.two.utils.NumberBase`
5036 :param parent: Parent node of the PSyIR node we are constructing.
5037 :type parent: :py:class:`psyclone.psyir.nodes.Node`
5039 :returns: PSyIR representation of node.
5040 :rtype: :py:class:`psyclone.psyir.nodes.Literal`
5042 :raises NotImplementedError: if the fparser2 node is not recognised.
5045 if isinstance(node, Fortran2003.Int_Literal_Constant):
5046 integer_type = ScalarType(ScalarType.Intrinsic.INTEGER,
5047 get_literal_precision(node, parent))
5048 return Literal(str(node.items[0]), integer_type)
5049 if isinstance(node, Fortran2003.Real_Literal_Constant):
5050 real_type = ScalarType(ScalarType.Intrinsic.REAL,
5051 get_literal_precision(node, parent))
5053 value = str(node.items[0]).lower()
5056 value = value.replace(
"d",
"e")
5060 if value[0] ==
"." or value[0:1]
in [
"+.",
"-."]:
5061 value = value.replace(
".",
"0.")
5062 return Literal(value, real_type)
5064 raise NotImplementedError(
"Unsupported datatype of literal number")
5066 def _char_literal_handler(self, node, parent):
5068 Transforms an fparser2 character literal into a PSyIR literal.
5069 Currently does not support the use of a double '' or double "" to
5070 represent a single instance of one of those characters within a string
5071 delimited by the same character.
5073 :param node: node in fparser2 parse tree.
5074 :type node: :py:class:`fparser.two.Fortran2003.Char_Literal_Constant`
5075 :param parent: parent node of the PSyIR node we are constructing.
5076 :type parent: :py:class:`psyclone.psyir.nodes.Node`
5078 :returns: PSyIR representation of node.
5079 :rtype: :py:class:`psyclone.psyir.nodes.Literal`
5082 character_type = ScalarType(ScalarType.Intrinsic.CHARACTER,
5083 get_literal_precision(node, parent))
5087 char_value = str(node.items[0])
5088 if not ((char_value.startswith(
"'")
and char_value.endswith(
"'"))
or
5089 (char_value.startswith(
'"')
and char_value.endswith(
'"'))):
5091 f
"Char literal handler expects a quoted value but got: "
5092 f
">>{char_value}<<")
5097 if len(char_value) > 2
and (
"''" in char_value
or '""' in char_value):
5098 raise NotImplementedError(
"Unsupported Literal")
5100 return Literal(char_value[1:-1], character_type)
5102 def _bool_literal_handler(self, node, parent):
5104 Transforms an fparser2 logical literal into a PSyIR literal.
5106 :param node: node in fparser2 parse tree.
5108 :py:class:`fparser.two.Fortran2003.Logical_Literal_Constant`
5109 :param parent: parent node of the PSyIR node we are constructing.
5110 :type parent: :py:class:`psyclone.psyir.nodes.Node`
5112 :returns: PSyIR representation of node.
5113 :rtype: :py:class:`psyclone.psyir.nodes.Literal`
5116 boolean_type = ScalarType(ScalarType.Intrinsic.BOOLEAN,
5117 get_literal_precision(node, parent))
5118 value = str(node.items[0]).lower()
5119 if value ==
".true.":
5120 return Literal(
"true", boolean_type)
5121 if value ==
".false.":
5122 return Literal(
"false", boolean_type)
5124 f
"Expected to find '.true.' or '.false.' as fparser2 logical "
5125 f
"literal, but found '{value}' instead.")
5127 def _call_handler(self, node, parent):
5128 '''Transforms an fparser2 CALL statement into a PSyIR Call node.
5130 :param node: node in fparser2 parse tree.
5131 :type node: :py:class:`fparser.two.Fortran2003.Call_Stmt`
5132 :param parent: parent node of the PSyIR node we are constructing.
5133 :type parent: :py:class:`psyclone.psyir.nodes.Node`
5135 :returns: PSyIR representation of node.
5136 :rtype: :py:class:`psyclone.psyir.nodes.Call`
5138 :raises GenerationError: if the symbol associated with the
5139 name of the call is an unsupported type.
5142 call = Call(parent=parent)
5143 self.
process_nodesprocess_nodes(parent=call, nodes=[node.items[0]])
5144 routine = call.children[0]
5147 if type(routine)
is Reference:
5148 routine_symbol = routine.symbol
5149 if type(routine_symbol)
is Symbol:
5152 routine_symbol.specialise(RoutineSymbol)
5153 elif isinstance(routine_symbol, RoutineSymbol):
5158 f
"Expecting the symbol '{routine_symbol.name}', to be of "
5159 f
"type 'Symbol' or 'RoutineSymbol', but found "
5160 f
"'{type(routine_symbol).__name__}'.")
5164 def _process_args(self, node, call, canonicalise=None):
5165 '''Processes fparser2 call or intrinsic arguments contained in the
5166 node argument and adds them to the PSyIR Call or IntrinsicCall
5167 contained in the call argument, respectively.
5169 The optional canonicalise function allows the order of the
5170 call's arguments and its named arguments to be re-ordered and
5171 modified to a canonical form so that the PSyIR does not need
5172 to support the different forms that are allowed in
5175 For example, both sum(a, dim, mask) and sum(dim=dim,
5176 mask=mask, array=a) are equivalant in Fortran. The canonical
5177 form has all required arguments as positional arguments and
5178 all optional arguments as named arguments, which would result
5179 in sum(a, dim=dim, mask=mask) in this case.
5181 :param node: an fparser call node representing a call or \
5183 :type node: :py:class:`fparser.two.Fortran2003.Call_Stmt` or \
5184 :py:class:`fparser.two.Fortran2003.Intrinsic_Function_Reference`
5185 :param call: a PSyIR call argument representing a call or an \
5187 :type call: :py:class:`psyclone.psyir.nodes.Call` or \
5188 :py:class:`psyclone.psyir.nodes.IntrinsicCall`
5189 :param function canonicalise: a function that canonicalises \
5192 :returns: the PSyIR call argument with the PSyIR \
5193 representation of the fparser2 node arguments.
5194 :rtype: :py:class:`psyclone.psyir.nodes.Call` or \
5195 :py:class:`psyclone.psyir.nodes.IntrinsicCall`
5197 :raises GenerationError: if all named arguments do not follow \
5198 all positional arguments.
5205 arg_nodes, arg_names = _get_arg_names(node.items[1].items)
5217 while index < len(arg_names)
and not arg_names[index]:
5219 for arg_name
in arg_names[index:]:
5222 f
"In Fortran, all named arguments should follow all "
5223 f
"positional arguments, but found '{node}'.")
5231 canonicalise(arg_nodes, arg_names, node)
5233 self.
process_nodesprocess_nodes(parent=call, nodes=arg_nodes)
5237 arg_list = call.arguments[:]
5238 for child
in arg_list:
5240 for idx, child
in enumerate(arg_list):
5241 call.append_named_arg(arg_names[idx], child)
5248 def _subroutine_handler(self, node, parent):
5249 '''Transforms an fparser2 Subroutine_Subprogram or Function_Subprogram
5250 statement into a PSyIR Routine node.
5252 :param node: node in fparser2 parse tree.
5253 :type node: :py:class:`fparser.two.Fortran2003.Subroutine_Subprogram`
5254 or :py:class:`fparser.two.Fortran2003.Function_Subprogram`
5255 :param parent: parent node of the PSyIR node being constructed.
5256 :type parent: subclass of :py:class:`psyclone.psyir.nodes.Node`
5258 :returns: PSyIR representation of node.
5259 :rtype: :py:class:`psyclone.psyir.nodes.Routine`
5262 :raises NotImplementedError: if the node contains a Contains clause.
5263 :raises NotImplementedError: if the node contains an ENTRY statement.
5264 :raises NotImplementedError: if an unsupported prefix is found.
5265 :raises SymbolError: if no explicit type information is available for
5266 the return value of a Function.
5270 _first_type_match(node.children,
5271 Fortran2003.Internal_Subprogram_Part)
5272 raise NotImplementedError(
"PSyclone doesn't yet support 'Contains'"
5273 " inside a Subroutine or Function")
5277 entry_stmts = walk(node, Fortran2003.Entry_Stmt)
5279 raise NotImplementedError(
5280 f
"PSyclone does not support routines that contain one or more "
5281 f
"ENTRY statements but found '{entry_stmts[0]}'")
5283 name = node.children[0].children[1].string
5284 routine = Routine(name, parent=parent)
5289 sub_spec = _first_type_match(node.content,
5290 Fortran2003.Specification_Part)
5291 decl_list = sub_spec.content
5300 if (isinstance(node, (Fortran2003.Subroutine_Subprogram,
5301 Fortran2003.Function_Subprogram))
and
5302 isinstance(node.children[0].children[2],
5303 Fortran2003.Dummy_Arg_List)):
5304 arg_list = node.children[0].children[2].children
5315 prefix = node.children[0].children[0]
5317 for child
in prefix.children:
5318 if isinstance(child, Fortran2003.Prefix_Spec):
5319 if child.string
not in SUPPORTED_ROUTINE_PREFIXES:
5320 raise NotImplementedError(
5321 f
"Routine has unsupported prefix: {child.string}")
5325 if isinstance(node, Fortran2003.Function_Subprogram):
5328 suffix = node.children[0].children[3]
5334 return_name = suffix.children[0].string
5343 if return_name
in routine.symbol_table:
5344 symbol = routine.symbol_table.lookup(return_name)
5350 if isinstance(symbol, RoutineSymbol):
5353 routine.symbol_table.remove(symbol)
5354 keep_tag =
"own_routine_symbol"
5356 if return_name
not in routine.symbol_table:
5371 f
"No explicit return-type information found for "
5372 f
"function '{name}'. PSyclone requires that all "
5373 f
"symbols be explicitly typed.")
5384 routine_symbol = routine.symbol_table.lookup(routine.name)
5385 routine_symbol.datatype = base_type
5389 routine.symbol_table.new_symbol(return_name,
5391 symbol_type=DataSymbol,
5396 routine.return_symbol = routine.symbol_table.lookup(return_name)
5399 sub_exec = _first_type_match(node.content,
5400 Fortran2003.Execution_Part)
5410 def _main_program_handler(self, node, parent):
5411 '''Transforms an fparser2 Main_Program statement into a PSyIR
5414 :param node: node in fparser2 parse tree.
5415 :type node: :py:class:`fparser.two.Fortran2003.Main_Program`
5416 :param parent: parent node of the PSyIR node being constructed.
5417 :type parent: subclass of :py:class:`psyclone.psyir.nodes.Node`
5419 :returns: PSyIR representation of node.
5420 :rtype: :py:class:`psyclone.psyir.nodes.Routine`
5422 :raises NotImplementedError: if the node contains a Contains clause.
5425 _first_type_match(node.children,
5426 Fortran2003.Internal_Subprogram_Part)
5427 raise NotImplementedError(
"PSyclone doesn't yet support 'Contains'"
5428 " inside a Program")
5433 name = node.children[0].children[1].string
5434 routine = Routine(name, parent=parent, is_program=
True)
5438 prog_spec = _first_type_match(node.content,
5439 Fortran2003.Specification_Part)
5440 decl_list = prog_spec.content
5448 prog_exec = _first_type_match(node.content,
5449 Fortran2003.Execution_Part)
5459 def _module_handler(self, node, parent):
5460 '''Transforms an fparser2 Module statement into a PSyIR Container node.
5462 :param node: fparser2 representation of a module.
5463 :type node: :py:class:`fparser.two.Fortran2003.Module`
5464 :param parent: parent node of the PSyIR node being constructed.
5465 :type parent: subclass of :py:class:`psyclone.psyir.nodes.Node`
5467 :returns: PSyIR representation of module.
5468 :rtype: :py:class:`psyclone.psyir.nodes.Container`
5472 mod_name = str(node.children[0].children[1])
5473 container = Container(mod_name, parent=parent)
5480 container.symbol_table.default_visibility = default_visibility
5483 _process_routine_symbols(node, container.symbol_table, visibility_map)
5487 spec_part = _first_type_match(
5488 node.children, Fortran2003.Specification_Part)
5492 if spec_part
is not None:
5499 subprog_part = _first_type_match(
5500 node.children, Fortran2003.Module_Subprogram_Part)
5501 module_subprograms = \
5502 [subprogram
for subprogram
in subprog_part.children
5503 if not isinstance(subprogram, Fortran2003.Contains_Stmt)]
5504 if module_subprograms:
5505 self.
process_nodesprocess_nodes(parent=container, nodes=module_subprograms)
5506 except SymbolError
as err:
5507 raise NotImplementedError(str(err.value))
5513 def _program_handler(self, node, parent):
5514 '''Processes an fparser2 Program statement. Program is the top level
5515 node of a complete fparser2 tree and may contain one or more
5516 program-units. This is captured with a FileContainer node.
5518 :param node: top level node in fparser2 parse tree.
5519 :type node: :py:class:`fparser.two.Fortran2003.Program`
5520 :param parent: parent node of the PSyIR node we are constructing.
5521 :type parent: :py:class:`psyclone.psyir.nodes.Node`
5523 :returns: PSyIR representation of the program.
5524 :rtype: subclass of :py:class:`psyclone.psyir.nodes.Node`
5530 file_container = FileContainer(
"None", parent=parent)
5531 if len(node.children) == 1
and node.children[0]
is None:
5533 return file_container
5534 self.
process_nodesprocess_nodes(file_container, node.children)
5535 return file_container
5539 __all__ = [
"Fparser2Reader"]
def _array_notation_rank(node)
def generate_container(self, module_ast)
def _process_common_blocks(nodes, psyir_parent)
def _create_select_type_info(node)
def process_nodes(self, parent, nodes)
def _process_args(self, node, call, canonicalise=None)
def _add_target_attribute(var_name, table)
def _create_ifblock_for_select_type_content(self, parent, select_type, type_string_symbol, pointer_symbols)
def _create_child(self, child, parent=None)
def process_access_statements(nodes)
def get_routine_schedules(self, name, module_ast)
def _create_loop(self, parent, variable)
def _process_save_statements(nodes, parent)
def _process_case_value_list(self, selector, nodes, parent)
def _parse_dimensions(dimensions, symbol_table)
def _process_interface_block(self, node, symbol_table, visibility_map)
def _process_derived_type_decln(self, parent, decl, visibility_map)
def _process_case_value(self, selector, node, parent)
def _process_decln(self, scope, symbol_table, decl, visibility_map=None, statics_list=())
def _create_select_type(parent, select_type, type_string_name=None)
def _process_parameter_stmts(self, nodes, parent)
def generate_psyir(self, parse_tree)
def _array_syntax_to_indexed(self, parent, loop_vars)
def _include_handler(self, node, parent)
def _create_bounded_loop(self, parent, variable, limits_list)
def _process_precision(type_spec, psyir_parent)
def _process_use_stmts(parent, nodes, visibility_map=None)
def process_declarations(self, parent, nodes, arg_list, visibility_map=None)
def _get_partial_datatype(self, node, scope, visibility_map)
def nodes_to_code_block(parent, fp2_nodes, message=None)
def _process_type_spec(self, parent, type_spec)