Reference Guide  2.5.0
psyclone.psyir.backend.fortran.FortranWriter Class Reference
Inheritance diagram for psyclone.psyir.backend.fortran.FortranWriter:
Collaboration diagram for psyclone.psyir.backend.fortran.FortranWriter:

Public Member Functions

def __init__ (self, skip_nodes=False, indent_string=" ", initial_indent_depth=0, check_global_constraints=True)
 
def get_operator (self, operator)
 
def gen_indices (self, indices, var_name=None)
 
def gen_use (self, symbol, symbol_table)
 
def gen_vardecl (self, symbol, include_visibility=False)
 
def gen_interfacedecl (self, symbol)
 
def gen_typedecl (self, symbol, include_visibility=True)
 
def gen_default_access_stmt (self, symbol_table)
 
def gen_access_stmts (self, symbol_table)
 
def gen_decls (self, symbol_table, is_module_scope=False)
 
def filecontainer_node (self, node)
 
def container_node (self, node)
 
def routine_node (self, node)
 
def assignment_node (self, node)
 
def binaryoperation_node (self, node)
 
def range_node (self, node)
 
def literal_node (self, node)
 
def ifblock_node (self, node)
 
def whileloop_node (self, node)
 
def loop_node (self, node)
 
def unaryoperation_node (self, node)
 
def return_node (self, _)
 
def codeblock_node (self, node)
 
def operandclause_node (self, node)
 
def regiondirective_node (self, node)
 
def standalonedirective_node (self, node)
 
def call_node (self, node)
 
def kernelfunctor_node (self, node)
 
- Public Member Functions inherited from psyclone.psyir.backend.language_writer.LanguageWriter
def __init__ (self, array_parenthesis, structure_character, skip_nodes=False, indent_string=" ", initial_indent_depth=0, check_global_constraints=True)
 
def array_parenthesis (self)
 
def structure_character (self)
 
def arrayreference_node (self, node)
 
def structurereference_node (self, node)
 
def member_node (self, node)
 
def arrayofstructuresreference_node (self, node)
 
def clause_node (self, node)
 
- Public Member Functions inherited from psyclone.psyir.backend.visitor.PSyIRVisitor
def reference_node (self, node)
 
def __call__ (self, node)
 

Detailed Description

Implements a PSyIR-to-Fortran back end for PSyIR kernel code (not
currently PSyIR algorithm code which has its own gen method for
generating Fortran).

:param bool skip_nodes: If skip_nodes is False then an exception \
    is raised if a visitor method for a PSyIR node has not been \
    implemented, otherwise the visitor silently continues. This is an \
    optional argument which defaults to False.
:param str indent_string: Specifies what to use for indentation. This \
    is an optional argument that defaults to two spaces.
:param int initial_indent_depth: Specifies how much indentation to \
    start with. This is an optional argument that defaults to 0.
:param bool check_global_constraints: whether or not to validate all \
    global constraints when walking the tree. Defaults to True.

Definition at line 302 of file fortran.py.

Member Function Documentation

◆ assignment_node()

def psyclone.psyir.backend.fortran.FortranWriter.assignment_node (   self,
  node 
)
This method is called when an Assignment instance is found in the
PSyIR tree.

:param node: an Assignment PSyIR node.
:type node: :py:class:`psyclone.psyir.nodes.Assignment``

:returns: the Fortran code as a string.
:rtype: str

Definition at line 1229 of file fortran.py.

1229  def assignment_node(self, node):
1230  '''This method is called when an Assignment instance is found in the
1231  PSyIR tree.
1232 
1233  :param node: an Assignment PSyIR node.
1234  :type node: :py:class:`psyclone.psyir.nodes.Assignment``
1235 
1236  :returns: the Fortran code as a string.
1237  :rtype: str
1238 
1239  '''
1240  lhs = self._visit(node.lhs)
1241  rhs = self._visit(node.rhs)
1242  result = f"{self._nindent}{lhs} = {rhs}\n"
1243  return result
1244 

References psyclone.psyir.backend.visitor.PSyIRVisitor._visit().

Here is the call graph for this function:

◆ binaryoperation_node()

def psyclone.psyir.backend.fortran.FortranWriter.binaryoperation_node (   self,
  node 
)
This method is called when a BinaryOperation instance is found in
the PSyIR tree.

:param node: a BinaryOperation PSyIR node.
:type node: :py:class:`psyclone.psyir.nodes.BinaryOperation`

:returns: the Fortran code as a string.
:rtype: str

Definition at line 1245 of file fortran.py.

1245  def binaryoperation_node(self, node):
1246  '''This method is called when a BinaryOperation instance is found in
1247  the PSyIR tree.
1248 
1249  :param node: a BinaryOperation PSyIR node.
1250  :type node: :py:class:`psyclone.psyir.nodes.BinaryOperation`
1251 
1252  :returns: the Fortran code as a string.
1253  :rtype: str
1254 
1255  '''
1256  lhs = self._visit(node.children[0])
1257  rhs = self._visit(node.children[1])
1258  try:
1259  fort_oper = self.get_operator(node.operator)
1260  parent = node.parent
1261  if isinstance(parent, Operation):
1262  # We may need to enforce precedence
1263  parent_fort_oper = self.get_operator(parent.operator)
1264  if precedence(fort_oper) < precedence(parent_fort_oper):
1265  # We need brackets to enforce precedence
1266  return f"({lhs} {fort_oper} {rhs})"
1267  if precedence(fort_oper) == precedence(parent_fort_oper):
1268  # We still may need to enforce precedence
1269  if (isinstance(parent, UnaryOperation) or
1270  (isinstance(parent, BinaryOperation) and
1271  parent.children[1] == node)):
1272  # We need brackets to enforce precedence
1273  # as a) a unary operator is performed
1274  # before a binary operator and b) floating
1275  # point operations are not actually
1276  # associative due to rounding errors.
1277  return f"({lhs} {fort_oper} {rhs})"
1278  return f"{lhs} {fort_oper} {rhs}"
1279  except KeyError as error:
1280  raise VisitorError(
1281  f"Unexpected binary op '{node.operator}'.") from error
1282 

References psyclone.psyir.backend.visitor.PSyIRVisitor._visit(), and psyclone.psyir.backend.fortran.FortranWriter.get_operator().

Here is the call graph for this function:

◆ call_node()

def psyclone.psyir.backend.fortran.FortranWriter.call_node (   self,
  node 
)
Translate the PSyIR call node to Fortran.

:param node: a Call PSyIR node.
:type node: :py:class:`psyclone.psyir.nodes.Call`

:returns: the equivalent Fortran code.
:rtype: str

Definition at line 1704 of file fortran.py.

1704  def call_node(self, node):
1705  '''Translate the PSyIR call node to Fortran.
1706 
1707  :param node: a Call PSyIR node.
1708  :type node: :py:class:`psyclone.psyir.nodes.Call`
1709 
1710  :returns: the equivalent Fortran code.
1711  :rtype: str
1712 
1713  '''
1714  args = self._gen_arguments(node)
1715  if isinstance(node, IntrinsicCall) and node.routine.name in [
1716  "ALLOCATE", "DEALLOCATE"]:
1717  # An allocate/deallocate doesn't have 'call'.
1718  return f"{self._nindent}{node.routine.name}({args})\n"
1719  if not node.parent or isinstance(node.parent, Schedule):
1720  return f"{self._nindent}call {self._visit(node.routine)}({args})\n"
1721 
1722  # Otherwise it is inside-expression function call
1723  return f"{self._visit(node.routine)}({args})"
1724 

References psyclone.psyir.backend.fortran.FortranWriter._gen_arguments().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ codeblock_node()

def psyclone.psyir.backend.fortran.FortranWriter.codeblock_node (   self,
  node 
)
This method is called when a CodeBlock instance is found in the
PSyIR tree. It returns the content of the CodeBlock as a
Fortran string, indenting as appropriate.

:param node: a CodeBlock PSyIR node.
:type node: :py:class:`psyclone.psyir.nodes.CodeBlock`

:returns: the Fortran code as a string.
:rtype: str

Definition at line 1546 of file fortran.py.

1546  def codeblock_node(self, node):
1547  '''This method is called when a CodeBlock instance is found in the
1548  PSyIR tree. It returns the content of the CodeBlock as a
1549  Fortran string, indenting as appropriate.
1550 
1551  :param node: a CodeBlock PSyIR node.
1552  :type node: :py:class:`psyclone.psyir.nodes.CodeBlock`
1553 
1554  :returns: the Fortran code as a string.
1555  :rtype: str
1556 
1557  '''
1558  result = ""
1559  if node.structure == CodeBlock.Structure.STATEMENT:
1560  # indent and newlines required
1561  for ast_node in node.get_ast_nodes:
1562  # Using tofortran() ensures we get any label associated
1563  # with this statement.
1564  for line in ast_node.tofortran().split("\n"):
1565  result += f"{self._nindent}{line}\n"
1566  elif node.structure == CodeBlock.Structure.EXPRESSION:
1567  for ast_node in node.get_ast_nodes:
1568  result += str(ast_node)
1569  else:
1570  raise VisitorError(
1571  f"Unsupported CodeBlock Structure '{node.structure}' found.")
1572  return result
1573 

◆ container_node()

def psyclone.psyir.backend.fortran.FortranWriter.container_node (   self,
  node 
)
This method is called when a Container instance is found in
the PSyIR tree.

A container node is mapped to a module in the Fortran back end.

:param node: a Container PSyIR node.
:type node: :py:class:`psyclone.psyir.nodes.Container`

:returns: the Fortran code as a string.
:rtype: str

:raises VisitorError: if the name attribute of the supplied \
    node is empty or None.
:raises VisitorError: if any of the children of the supplied \
    Container node are not Routines or CodeBlocks.

Definition at line 1076 of file fortran.py.

1076  def container_node(self, node):
1077  '''This method is called when a Container instance is found in
1078  the PSyIR tree.
1079 
1080  A container node is mapped to a module in the Fortran back end.
1081 
1082  :param node: a Container PSyIR node.
1083  :type node: :py:class:`psyclone.psyir.nodes.Container`
1084 
1085  :returns: the Fortran code as a string.
1086  :rtype: str
1087 
1088  :raises VisitorError: if the name attribute of the supplied \
1089  node is empty or None.
1090  :raises VisitorError: if any of the children of the supplied \
1091  Container node are not Routines or CodeBlocks.
1092 
1093  '''
1094  if not node.name:
1095  raise VisitorError("Expected Container node name to have a value.")
1096 
1097  # All children must be either Routines or CodeBlocks as modules within
1098  # modules are not supported.
1099  if not all(isinstance(child, (Routine, CodeBlock)) for
1100  child in node.children):
1101  raise VisitorError(
1102  f"The Fortran back-end requires all children of a Container "
1103  f"to be either CodeBlocks or sub-classes of Routine but found:"
1104  f" {[type(child).__name__ for child in node.children]}.")
1105 
1106  result = f"{self._nindent}module {node.name}\n"
1107 
1108  self._depth += 1
1109 
1110  # Generate module imports
1111  imports = ""
1112  for symbol in node.symbol_table.containersymbols:
1113  imports += self.gen_use(symbol, node.symbol_table)
1114 
1115  # Declare the Container's data
1116  declarations = self.gen_decls(node.symbol_table, is_module_scope=True)
1117 
1118  # Generate the access statement (PRIVATE or PUBLIC)
1119  declarations += self.gen_default_access_stmt(node.symbol_table)
1120 
1121  # Accessibility statements for imported and routine symbols
1122  declarations += self.gen_access_stmts(node.symbol_table)
1123 
1124  # Get the subroutine statements.
1125  subroutines = ""
1126  for child in node.children:
1127  subroutines += self._visit(child)
1128 
1129  result += (
1130  f"{imports}"
1131  f"{self._nindent}implicit none\n"
1132  f"{declarations}\n"
1133  f"{self._nindent}contains\n"
1134  f"{subroutines}\n")
1135 
1136  self._depth -= 1
1137  result += f"{self._nindent}end module {node.name}\n"
1138  return result
1139 

References psyclone.psyir.backend.visitor.PSyIRVisitor._depth, psyclone.psyir.backend.visitor.PSyIRVisitor._visit(), psyclone.psyir.backend.fortran.FortranWriter.gen_access_stmts(), psyclone.psyir.backend.fortran.FortranWriter.gen_decls(), psyclone.psyir.backend.fortran.FortranWriter.gen_default_access_stmt(), and psyclone.psyir.backend.fortran.FortranWriter.gen_use().

Here is the call graph for this function:

◆ filecontainer_node()

def psyclone.psyir.backend.fortran.FortranWriter.filecontainer_node (   self,
  node 
)
This method is called when a FileContainer instance is found in
the PSyIR tree.

A file container node requires no explicit text in the Fortran
back end.

:param node: a Container PSyIR node.
:type node: :py:class:`psyclone.psyir.nodes.FileContainer`

:returns: the Fortran code as a string.
:rtype: str

:raises VisitorError: if the attached symbol table contains \
    any data symbols.
:raises VisitorError: if more than one child is a Routine Node \
    with is_program set to True.

Definition at line 1038 of file fortran.py.

1038  def filecontainer_node(self, node):
1039  '''This method is called when a FileContainer instance is found in
1040  the PSyIR tree.
1041 
1042  A file container node requires no explicit text in the Fortran
1043  back end.
1044 
1045  :param node: a Container PSyIR node.
1046  :type node: :py:class:`psyclone.psyir.nodes.FileContainer`
1047 
1048  :returns: the Fortran code as a string.
1049  :rtype: str
1050 
1051  :raises VisitorError: if the attached symbol table contains \
1052  any data symbols.
1053  :raises VisitorError: if more than one child is a Routine Node \
1054  with is_program set to True.
1055 
1056  '''
1057  if node.symbol_table.symbols:
1058  raise VisitorError(
1059  f"In the Fortran backend, a file container should not have "
1060  f"any symbols associated with it, but found "
1061  f"{len(node.symbol_table.symbols)}.")
1062 
1063  program_nodes = len([child for child in node.children if
1064  isinstance(child, Routine) and child.is_program])
1065  if program_nodes > 1:
1066  raise VisitorError(
1067  f"In the Fortran backend, a file container should contain at "
1068  f"most one routine node that is a program, but found "
1069  f"{program_nodes}.")
1070 
1071  result = ""
1072  for child in node.children:
1073  result += self._visit(child)
1074  return result
1075 

References psyclone.psyir.backend.visitor.PSyIRVisitor._visit().

Here is the call graph for this function:

◆ gen_access_stmts()

def psyclone.psyir.backend.fortran.FortranWriter.gen_access_stmts (   self,
  symbol_table 
)
Creates the accessibility statements (R518) for any routine or
imported symbols in the supplied symbol table.

:param symbol_table: the symbol table for which to generate \
                     accessibility statements.
:type symbol_table: :py:class:`psyclone.psyir.symbols.SymbolTable`

:returns: the accessibility statements for any routine or imported \
          symbols.
:rtype: str

Definition at line 768 of file fortran.py.

768  def gen_access_stmts(self, symbol_table):
769  '''
770  Creates the accessibility statements (R518) for any routine or
771  imported symbols in the supplied symbol table.
772 
773  :param symbol_table: the symbol table for which to generate \
774  accessibility statements.
775  :type symbol_table: :py:class:`psyclone.psyir.symbols.SymbolTable`
776 
777  :returns: the accessibility statements for any routine or imported \
778  symbols.
779  :rtype: str
780 
781  '''
782  # Find the symbol that represents itself, this one will not need
783  # an accessibility statement
784  try:
785  itself = symbol_table.lookup_with_tag('own_routine_symbol')
786  except KeyError:
787  itself = None
788 
789  public_symbols = []
790  private_symbols = []
791  for symbol in symbol_table.symbols:
792  if (isinstance(symbol, RoutineSymbol) or
793  symbol.is_unresolved or symbol.is_import):
794 
795  # Skip the symbol representing the routine where these
796  # declarations belong
797  if isinstance(symbol, RoutineSymbol) and symbol is itself:
798  continue
799 
800  # It doesn't matter whether this symbol has a local or import
801  # interface - its accessibility in *this* context is determined
802  # by the local accessibility statements. e.g. if we are
803  # dealing with the declarations in a given module which itself
804  # uses a public symbol from some other module, the
805  # accessibility of that symbol is determined by the
806  # accessibility statements in the current module.
807  if (symbol_table.default_visibility in
808  [None, Symbol.Visibility.PUBLIC]):
809  if symbol.visibility == Symbol.Visibility.PRIVATE:
810  # Default vis. is public but this symbol is private
811  private_symbols.append(symbol.name)
812  else:
813  if symbol.visibility == Symbol.Visibility.PUBLIC:
814  # Default vis. is private but this symbol is public
815  public_symbols.append(symbol.name)
816 
817  result = "\n"
818  if public_symbols:
819  result += f"{self._nindent}public :: {', '.join(public_symbols)}\n"
820  if private_symbols:
821  result += (f"{self._nindent}private :: "
822  f"{', '.join(private_symbols)}\n")
823 
824  if len(result) > 1:
825  return result
826  return ""
827 

References psyclone.psyir.backend.fortran.FortranWriter._call_tree_utils, and psyclone.psyir.backend.fortran.FortranWriter.gen_vardecl().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ gen_decls()

def psyclone.psyir.backend.fortran.FortranWriter.gen_decls (   self,
  symbol_table,
  is_module_scope = False 
)
Create and return the Fortran declarations for the supplied
SymbolTable.

:param symbol_table: the SymbolTable instance.
:type symbol_table: :py:class:`psyclone.psyir.symbols.SymbolTable`
:param bool is_module_scope: whether or not the declarations are in
    a module scoping unit. Default is False.

:returns: the Fortran declarations for the table.
:rtype: str

:raises VisitorError: if one of the symbols is a RoutineSymbol which
    does not have an ImportInterface or UnresolvedInterface (
    representing named and unqualified imports respectively) or
    ModuleDefaultInterface (representing routines declared in the
    same module) or is not a Fortran intrinsic.
:raises VisitorError: if args_allowed is False and one or more
    argument declarations exist in symbol_table.
:raises VisitorError: if there are any symbols (other than
    RoutineSymbols) in the supplied table that do not have an
    explicit declaration (UnresolvedInterface) and there are no
    wildcard imports or unknown interfaces.

Definition at line 903 of file fortran.py.

903  def gen_decls(self, symbol_table, is_module_scope=False):
904  '''Create and return the Fortran declarations for the supplied
905  SymbolTable.
906 
907  :param symbol_table: the SymbolTable instance.
908  :type symbol_table: :py:class:`psyclone.psyir.symbols.SymbolTable`
909  :param bool is_module_scope: whether or not the declarations are in
910  a module scoping unit. Default is False.
911 
912  :returns: the Fortran declarations for the table.
913  :rtype: str
914 
915  :raises VisitorError: if one of the symbols is a RoutineSymbol which
916  does not have an ImportInterface or UnresolvedInterface (
917  representing named and unqualified imports respectively) or
918  ModuleDefaultInterface (representing routines declared in the
919  same module) or is not a Fortran intrinsic.
920  :raises VisitorError: if args_allowed is False and one or more
921  argument declarations exist in symbol_table.
922  :raises VisitorError: if there are any symbols (other than
923  RoutineSymbols) in the supplied table that do not have an
924  explicit declaration (UnresolvedInterface) and there are no
925  wildcard imports or unknown interfaces.
926 
927  '''
928  # pylint: disable=too-many-branches
929  declarations = ""
930 
931  # Get all symbols local to this symbol table
932  all_symbols = symbol_table.symbols
933 
934  # Before processing the declarations we remove:
935  for sym in all_symbols[:]:
936  # Everything that is a container or imported (because it should
937  # already be done by the gen_use() method before)
938  if isinstance(sym, ContainerSymbol):
939  all_symbols.remove(sym)
940  continue
941  if sym.is_import:
942  all_symbols.remove(sym)
943  continue
944  # All the IntrinsicSymbols and RoutineSymbols with an
945  # UresolvedInterface (Fortran can have Calls which are
946  # only resolved at link time)
947  if isinstance(sym, IntrinsicSymbol) or (
948  isinstance(sym, RoutineSymbol) and
949  isinstance(sym.interface, UnresolvedInterface)):
950  all_symbols.remove(sym)
951  # We ignore all symbols with a PreprocessorInterface
952  if isinstance(sym.interface, PreprocessorInterface):
953  all_symbols.remove(sym)
954 
955  # If the symbol table contains any symbols with an
956  # UnresolvedInterface interface (they are not explicitly
957  # declared), we need to check that we have at least one
958  # wildcard import which could be bringing them into this
959  # scope, or an unknown interface which could be declaring
960  # them.
961  unresolved_symbols = []
962  for sym in all_symbols[:]:
963  if isinstance(sym.interface, UnresolvedInterface):
964  unresolved_symbols.append(sym)
965  all_symbols.remove(sym)
966  try:
967  internal_interface_symbol = symbol_table.lookup(
968  "_psyclone_internal_interface")
969  except KeyError:
970  internal_interface_symbol = None
971  if unresolved_symbols and not (
972  symbol_table.wildcard_imports() or internal_interface_symbol):
973  symbols_txt = ", ".join(
974  ["'" + sym.name + "'" for sym in unresolved_symbols])
975  raise VisitorError(
976  f"The following symbols are not explicitly declared or "
977  f"imported from a module and there are no wildcard "
978  f"imports which could be bringing them into scope: "
979  f"{symbols_txt}")
980 
981  # As a convention, we will declare the variables in the following
982  # order:
983 
984  # 1: Routine declarations and interfaces. (Note that accessibility
985  # statements are generated in gen_access_stmts().)
986  for sym in all_symbols[:]:
987  if not isinstance(sym, RoutineSymbol):
988  continue
989  # Interfaces can be GenericInterfaceSymbols or RoutineSymbols
990  # of UnsupportedFortranType.
991  if isinstance(sym, GenericInterfaceSymbol):
992  declarations += self.gen_interfacedecl(sym)
993  elif isinstance(sym.datatype, UnsupportedType):
994  declarations += self.gen_vardecl(
995  sym, include_visibility=is_module_scope)
996  elif not (sym.is_modulevar or sym.is_automatic):
997  raise VisitorError(
998  f"Routine symbol '{sym.name}' has '{sym.interface}'. "
999  f"This is not supported by the Fortran back-end.")
1000  all_symbols.remove(sym)
1001 
1002  # 2: Constants.
1003  declarations += self._gen_parameter_decls(symbol_table,
1004  is_module_scope)
1005  for sym in all_symbols[:]:
1006  if isinstance(sym, DataSymbol) and sym.is_constant:
1007  all_symbols.remove(sym)
1008 
1009  # 3: Argument variable declarations
1010  if symbol_table.argument_datasymbols and is_module_scope:
1011  raise VisitorError(
1012  f"Arguments are not allowed in this context but this symbol "
1013  f"table contains argument(s): "
1014  f"'{[sym.name for sym in symbol_table.argument_datasymbols]}'."
1015  )
1016  # We use symbol_table.argument_datasymbols because it has the
1017  # symbol order that we need
1018  for symbol in symbol_table.argument_datasymbols:
1019  declarations += self.gen_vardecl(
1020  symbol, include_visibility=is_module_scope)
1021  all_symbols.remove(symbol)
1022 
1023  # 4: Derived-type declarations. These must come before any declarations
1024  # of symbols of these types.
1025  for symbol in all_symbols[:]:
1026  if isinstance(symbol, DataTypeSymbol):
1027  declarations += self.gen_typedecl(
1028  symbol, include_visibility=is_module_scope)
1029  all_symbols.remove(symbol)
1030 
1031  # 5: The rest of the symbols
1032  for symbol in all_symbols:
1033  declarations += self.gen_vardecl(
1034  symbol, include_visibility=is_module_scope)
1035 
1036  return declarations
1037 

References psyclone.psyir.backend.fortran.FortranWriter._gen_parameter_decls(), psyclone.psyir.backend.fortran.FortranWriter.gen_interfacedecl(), psyclone.psyir.backend.fortran.FortranWriter.gen_typedecl(), and psyclone.psyir.backend.fortran.FortranWriter.gen_vardecl().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ gen_default_access_stmt()

def psyclone.psyir.backend.fortran.FortranWriter.gen_default_access_stmt (   self,
  symbol_table 
)
Generates the access statement for a module - either "private" or
"public". Although the PSyIR captures the visibility of every Symbol
explicitly, this information is required in order
to ensure the correct visibility of symbols that have been imported
into the current module from another one using a wildcard import
(i.e. a `use` without an `only` clause) and also for those Symbols
that are of UnsupportedFortranType (because their declaration may or
may not include visibility information).

:returns: text containing the access statement line.
:rtype: str

:raises InternalError: if the symbol table has an invalid default \
                       visibility.

Definition at line 738 of file fortran.py.

738  def gen_default_access_stmt(self, symbol_table):
739  '''
740  Generates the access statement for a module - either "private" or
741  "public". Although the PSyIR captures the visibility of every Symbol
742  explicitly, this information is required in order
743  to ensure the correct visibility of symbols that have been imported
744  into the current module from another one using a wildcard import
745  (i.e. a `use` without an `only` clause) and also for those Symbols
746  that are of UnsupportedFortranType (because their declaration may or
747  may not include visibility information).
748 
749  :returns: text containing the access statement line.
750  :rtype: str
751 
752  :raises InternalError: if the symbol table has an invalid default \
753  visibility.
754  '''
755  # If no default visibility has been set then we use the Fortran
756  # default of public.
757  if symbol_table.default_visibility in [None, Symbol.Visibility.PUBLIC]:
758  return self._nindent + "public\n"
759  if symbol_table.default_visibility == Symbol.Visibility.PRIVATE:
760  return self._nindent + "private\n"
761 
762  raise InternalError(
763  f"Unrecognised visibility ('{symbol_table.default_visibility}') "
764  f"found when attempting to generate access statement. Should be "
765  f"either 'Symbol.Visibility.PUBLIC' or "
766  f"'Symbol.Visibility.PRIVATE'\n")
767 

References psyclone.psyir.backend.visitor.PSyIRVisitor._nindent().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ gen_indices()

def psyclone.psyir.backend.fortran.FortranWriter.gen_indices (   self,
  indices,
  var_name = None 
)
Given a list of PSyIR nodes representing the dimensions of an
array, return a list of strings representing those array dimensions.
This is used both for array references and array declarations. Note
that 'indices' can also be a shape in case of Fortran.

:param indices: list of PSyIR nodes.
:type indices: list of :py:class:`psyclone.psyir.symbols.Node`
:param str var_name: name of the variable for which the dimensions \
    are created. Not used in the Fortran implementation.

:returns: the Fortran representation of the dimensions.
:rtype: list of str

:raises NotImplementedError: if the format of the dimension is not \
    supported.

Reimplemented from psyclone.psyir.backend.language_writer.LanguageWriter.

Reimplemented in psyclone.psyir.backend.sympy_writer.SymPyWriter.

Definition at line 389 of file fortran.py.

389  def gen_indices(self, indices, var_name=None):
390  '''Given a list of PSyIR nodes representing the dimensions of an
391  array, return a list of strings representing those array dimensions.
392  This is used both for array references and array declarations. Note
393  that 'indices' can also be a shape in case of Fortran.
394 
395  :param indices: list of PSyIR nodes.
396  :type indices: list of :py:class:`psyclone.psyir.symbols.Node`
397  :param str var_name: name of the variable for which the dimensions \
398  are created. Not used in the Fortran implementation.
399 
400  :returns: the Fortran representation of the dimensions.
401  :rtype: list of str
402 
403  :raises NotImplementedError: if the format of the dimension is not \
404  supported.
405 
406  '''
407  dims = []
408  for index in indices:
409  if isinstance(index, (DataNode, Range)):
410  # literal constant, symbol reference, or computed
411  # dimension
412  expression = self._visit(index)
413  dims.append(expression)
414  elif isinstance(index, ArrayType.ArrayBounds):
415  # Lower and upper bounds of an array declaration specified
416  # by literal constant, symbol reference, or computed dimension
417  lower_expression = self._visit(index.lower)
418  if isinstance(index.upper, ArrayType.Extent):
419  # We have an assumed-shape array (R514) where only the
420  # lower bound is specified.
421  upper_expression = ""
422  else:
423  upper_expression = self._visit(index.upper)
424  if lower_expression == "1":
425  # Lower bound of 1 is the default in Fortran
426  dims.append(upper_expression)
427  else:
428  dims.append(lower_expression+":"+upper_expression)
429  elif isinstance(index, ArrayType.Extent):
430  # unknown extent
431  dims.append(":")
432  else:
433  raise NotImplementedError(
434  f"unsupported gen_indices index '{index}'")
435  return dims
436 

References psyclone.psyir.backend.visitor.PSyIRVisitor._visit().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ gen_interfacedecl()

def psyclone.psyir.backend.fortran.FortranWriter.gen_interfacedecl (   self,
  symbol 
)
Generate the declaration for a generic interface.

Since a GenericInterfaceSymbol is a subclass of RoutineSymbol, any
necessary accessibility statement will be generated in
gen_access_stmts().

:param symbol: the GenericInterfaceSymbol to be declared.
:type symbol: :py:class:`psyclone.psyir.symbols.GenericInterfaceSymbol`

:returns: the corresponding Fortran declaration.
:rtype: str

:raises InternalError: if passed something that is not a
                       GenericInterfaceSymbol.

Definition at line 627 of file fortran.py.

627  def gen_interfacedecl(self, symbol):
628  '''
629  Generate the declaration for a generic interface.
630 
631  Since a GenericInterfaceSymbol is a subclass of RoutineSymbol, any
632  necessary accessibility statement will be generated in
633  gen_access_stmts().
634 
635  :param symbol: the GenericInterfaceSymbol to be declared.
636  :type symbol: :py:class:`psyclone.psyir.symbols.GenericInterfaceSymbol`
637 
638  :returns: the corresponding Fortran declaration.
639  :rtype: str
640 
641  :raises InternalError: if passed something that is not a
642  GenericInterfaceSymbol.
643 
644  '''
645  if not isinstance(symbol, GenericInterfaceSymbol):
646  raise InternalError(
647  f"gen_interfacedecl only supports 'GenericInterfaceSymbol's "
648  f"but got '{type(symbol).__name__}'")
649 
650  decln = f"{self._nindent}interface {symbol.name}\n"
651  self._depth += 1
652  # Any module procedures.
653  routines = ", ".join([rsym.name for rsym in symbol.container_routines])
654  if routines:
655  decln += f"{self._nindent}module procedure :: {routines}\n"
656  # Any other (external) procedures.
657  routines = ", ".join([rsym.name for rsym in symbol.external_routines])
658  if routines:
659  decln += f"{self._nindent}procedure :: {routines}\n"
660  self._depth -= 1
661  decln += f"{self._nindent}end interface {symbol.name}\n"
662 
663  return decln
664 

References psyclone.psyir.backend.visitor.PSyIRVisitor._depth.

Here is the caller graph for this function:

◆ gen_typedecl()

def psyclone.psyir.backend.fortran.FortranWriter.gen_typedecl (   self,
  symbol,
  include_visibility = True 
)
Creates a derived-type declaration for the supplied DataTypeSymbol.

:param symbol: the derived-type to declare.
:type symbol: :py:class:`psyclone.psyir.symbols.DataTypeSymbol`
:param bool include_visibility: whether or not to include visibility
    information in the declaration. (Default is True.)

:returns: the Fortran declaration of the derived type.
:rtype: str

:raises VisitorError: if the supplied symbol is not a DataTypeSymbol.
:raises VisitorError: if the datatype of the symbol is of
    UnsupportedType but is not of UnsupportedFortranType.
:raises InternalError: if include_visibility is True and the
    visibility of the symbol is not of the correct type.
:raises VisitorError: if the supplied symbol is of UnresolvedType.

Definition at line 665 of file fortran.py.

665  def gen_typedecl(self, symbol, include_visibility=True):
666  '''
667  Creates a derived-type declaration for the supplied DataTypeSymbol.
668 
669  :param symbol: the derived-type to declare.
670  :type symbol: :py:class:`psyclone.psyir.symbols.DataTypeSymbol`
671  :param bool include_visibility: whether or not to include visibility
672  information in the declaration. (Default is True.)
673 
674  :returns: the Fortran declaration of the derived type.
675  :rtype: str
676 
677  :raises VisitorError: if the supplied symbol is not a DataTypeSymbol.
678  :raises VisitorError: if the datatype of the symbol is of
679  UnsupportedType but is not of UnsupportedFortranType.
680  :raises InternalError: if include_visibility is True and the
681  visibility of the symbol is not of the correct type.
682  :raises VisitorError: if the supplied symbol is of UnresolvedType.
683 
684  '''
685  if not isinstance(symbol, DataTypeSymbol):
686  raise VisitorError(
687  f"gen_typedecl expects a DataTypeSymbol as argument but "
688  f"got: '{type(symbol).__name__}'")
689 
690  if isinstance(symbol.datatype, UnsupportedType):
691  if isinstance(symbol.datatype, UnsupportedFortranType):
692  # This is a declaration of UnsupportedType. We have to ensure
693  # that its visibility is correctly specified though.
694  if include_visibility:
695  decln = add_accessibility_to_unsupported_declaration(
696  symbol)
697  else:
698  decln = symbol.datatype.declaration
699  return f"{self._nindent}{decln}\n"
700 
701  raise VisitorError(
702  f"Fortran backend cannot generate code for symbol "
703  f"'{symbol.name}' of type '{type(symbol.datatype).__name__}'")
704 
705  result = f"{self._nindent}type"
706 
707  if include_visibility:
708  if symbol.visibility == Symbol.Visibility.PRIVATE:
709  result += ", private"
710  elif symbol.visibility == Symbol.Visibility.PUBLIC:
711  result += ", public"
712  else:
713  raise InternalError(
714  f"A Symbol's visibility must be one of Symbol.Visibility."
715  f"PRIVATE/PUBLIC but '{symbol.name}' has visibility of "
716  f"type '{type(symbol.visibility).__name__}'")
717  result += f" :: {symbol.name}\n"
718 
719  if isinstance(symbol.datatype, UnresolvedType):
720  raise VisitorError(
721  f"Local Symbol '{symbol.name}' is of UnresolvedType and "
722  f"therefore no declaration can be created for it. Should it "
723  f"have an ImportInterface?")
724 
725  self._depth += 1
726 
727  for member in symbol.datatype.components.values():
728  # We can only specify the visibility of components within
729  # a derived type if the declaration is within the specification
730  # part of a module.
731  result += self.gen_vardecl(member,
732  include_visibility=include_visibility)
733  self._depth -= 1
734 
735  result += f"{self._nindent}end type {symbol.name}\n"
736  return result
737 

References psyclone.psyir.backend.visitor.PSyIRVisitor._depth, and psyclone.psyir.backend.fortran.FortranWriter.gen_vardecl().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ gen_use()

def psyclone.psyir.backend.fortran.FortranWriter.gen_use (   self,
  symbol,
  symbol_table 
)
 Performs consistency checks and then creates and returns the
Fortran use statement(s) for this ContainerSymbol as required for
the supplied symbol table.

:param symbol: the container symbol instance.
:type symbol: :py:class:`psyclone.psyir.symbols.ContainerSymbol`
:param symbol_table: the symbol table containing this container symbol.
:type symbol_table: :py:class:`psyclone.psyir.symbols.SymbolTable`

:returns: the Fortran use statement(s) as a string.
:rtype: str

:raises VisitorError: if the symbol argument is not a ContainerSymbol.
:raises VisitorError: if the symbol_table argument is not a \
                    SymbolTable.
:raises VisitorError: if the supplied symbol is not in the supplied \
                    SymbolTable.
:raises VisitorError: if the supplied symbol has the same name as an \
                    entry in the SymbolTable but is a different object.

Definition at line 437 of file fortran.py.

437  def gen_use(self, symbol, symbol_table):
438  ''' Performs consistency checks and then creates and returns the
439  Fortran use statement(s) for this ContainerSymbol as required for
440  the supplied symbol table.
441 
442  :param symbol: the container symbol instance.
443  :type symbol: :py:class:`psyclone.psyir.symbols.ContainerSymbol`
444  :param symbol_table: the symbol table containing this container symbol.
445  :type symbol_table: :py:class:`psyclone.psyir.symbols.SymbolTable`
446 
447  :returns: the Fortran use statement(s) as a string.
448  :rtype: str
449 
450  :raises VisitorError: if the symbol argument is not a ContainerSymbol.
451  :raises VisitorError: if the symbol_table argument is not a \
452  SymbolTable.
453  :raises VisitorError: if the supplied symbol is not in the supplied \
454  SymbolTable.
455  :raises VisitorError: if the supplied symbol has the same name as an \
456  entry in the SymbolTable but is a different object.
457  '''
458  if not isinstance(symbol, ContainerSymbol):
459  raise VisitorError(
460  f"gen_use() expects a ContainerSymbol as its first argument "
461  f"but got '{type(symbol).__name__}'")
462  if not isinstance(symbol_table, SymbolTable):
463  raise VisitorError(
464  f"gen_use() expects a SymbolTable as its second argument but "
465  f"got '{type(symbol_table).__name__}'")
466  if symbol.name not in symbol_table:
467  raise VisitorError(
468  f"gen_use() - the supplied symbol ('{symbol.name}') is not"
469  f" in the supplied SymbolTable.")
470  if symbol_table.lookup(symbol.name) is not symbol:
471  raise VisitorError(
472  f"gen_use() - the supplied symbol ('{symbol.name}') is not "
473  f"the same object as the entry with that name in the supplied "
474  f"SymbolTable.")
475 
476  # Construct the list of symbol names for the ONLY clause
477  only_list = []
478  for dsym in symbol_table.symbols_imported_from(symbol):
479  if dsym.interface.orig_name:
480  # This variable is renamed on import. Use Fortran's
481  # 'new_name=>orig_name' syntax to reflect this.
482  only_list.append(f"{dsym.name}=>{dsym.interface.orig_name}")
483  else:
484  # This variable is not renamed.
485  only_list.append(dsym.name)
486 
487  # Finally construct the use statements for this Container (module)
488  if not only_list and not symbol.wildcard_import:
489  # We have a "use xxx, only:" - i.e. an empty only list
490  return f"{self._nindent}use {symbol.name}, only :\n"
491  if only_list and not symbol.wildcard_import:
492  return f"{self._nindent}use {symbol.name}, only : " + \
493  ", ".join(sorted(only_list)) + "\n"
494 
495  return f"{self._nindent}use {symbol.name}\n"
496 
Here is the caller graph for this function:

◆ gen_vardecl()

def psyclone.psyir.backend.fortran.FortranWriter.gen_vardecl (   self,
  symbol,
  include_visibility = False 
)
Create and return the Fortran variable declaration for this Symbol
or derived-type member.

:param symbol: the symbol or member instance.
:type symbol: :py:class:`psyclone.psyir.symbols.DataSymbol` or
    :py:class:`psyclone.psyir.nodes.MemberReference`
:param bool include_visibility: whether to include the visibility of
    the symbol in the generated declaration (default False).

:returns: the Fortran variable declaration as a string.
:rtype: str

:raises VisitorError: if the symbol is of UnresolvedType.
:raises VisitorError: if the symbol is of UnsupportedType other than
    UnsupportedFortranType.
:raises VisitorError: if the symbol is of known type but does not
    specify a variable declaration (it is not a local declaration or
    an argument declaration).
:raises VisitorError: if the symbol is a runtime constant but does not
    have a StaticInterface.
:raises InternalError: if the symbol is a ContainerSymbol or an import.
:raises InternalError: if the symbol is a RoutineSymbol other than
    UnsupportedFortranType.
:raises InternalError: if visibility is to be included but is not
    either PUBLIC or PRIVATE.

Definition at line 497 of file fortran.py.

497  def gen_vardecl(self, symbol, include_visibility=False):
498  '''Create and return the Fortran variable declaration for this Symbol
499  or derived-type member.
500 
501  :param symbol: the symbol or member instance.
502  :type symbol: :py:class:`psyclone.psyir.symbols.DataSymbol` or
503  :py:class:`psyclone.psyir.nodes.MemberReference`
504  :param bool include_visibility: whether to include the visibility of
505  the symbol in the generated declaration (default False).
506 
507  :returns: the Fortran variable declaration as a string.
508  :rtype: str
509 
510  :raises VisitorError: if the symbol is of UnresolvedType.
511  :raises VisitorError: if the symbol is of UnsupportedType other than
512  UnsupportedFortranType.
513  :raises VisitorError: if the symbol is of known type but does not
514  specify a variable declaration (it is not a local declaration or
515  an argument declaration).
516  :raises VisitorError: if the symbol is a runtime constant but does not
517  have a StaticInterface.
518  :raises InternalError: if the symbol is a ContainerSymbol or an import.
519  :raises InternalError: if the symbol is a RoutineSymbol other than
520  UnsupportedFortranType.
521  :raises InternalError: if visibility is to be included but is not
522  either PUBLIC or PRIVATE.
523 
524  '''
525  # pylint: disable=too-many-branches
526  if isinstance(symbol.datatype, UnresolvedType):
527  raise VisitorError(f"Symbol '{symbol.name}' has a UnresolvedType "
528  f"and we can not generate a declaration for "
529  f"UnresolvedTypes.")
530  if isinstance(symbol, ContainerSymbol) or \
531  isinstance(symbol, Symbol) and symbol.is_import:
532  raise InternalError(f"Symbol '{symbol.name}' is brought into scope"
533  f" from a Fortran USE statement and should be "
534  f"generated by 'gen_use' instead of "
535  f"'gen_vardecl'.")
536  if (isinstance(symbol, RoutineSymbol) and
537  not isinstance(symbol.datatype, UnsupportedFortranType)):
538  raise InternalError(f"Symbol '{symbol.name}' is a RoutineSymbol "
539  f"which is not imported or of "
540  f"UnsupportedFortranType. This is already "
541  f"implicitly declared by the routine itself "
542  f"and should not be provided to 'gen_vardecl'."
543  )
544 
545  # Whether we're dealing with an array declaration and, if so, the
546  # shape of that array.
547  if isinstance(symbol.datatype, ArrayType):
548  array_shape = symbol.datatype.shape
549  else:
550  array_shape = []
551 
552  if isinstance(symbol.datatype, UnsupportedType):
553  if isinstance(symbol.datatype, UnsupportedFortranType):
554 
555  if (include_visibility and
556  not isinstance(symbol, RoutineSymbol) and
557  not symbol.name.startswith("_PSYCLONE_INTERNAL")):
558  # We don't attempt to add accessibility to RoutineSymbols
559  # or to those created by PSyclone to handle named common
560  # blocks appearing in SAVE statements.
561  decln = add_accessibility_to_unsupported_declaration(
562  symbol)
563  return f"{self._nindent}{decln}\n"
564 
565  decln = symbol.datatype.declaration
566  return f"{self._nindent}{decln}\n"
567  # The Fortran backend only handles UnsupportedFortranType
568  # declarations.
569  raise VisitorError(
570  f"{type(symbol).__name__} '{symbol.name}' is of "
571  f"'{type(symbol.datatype).__name__}' type. This is not "
572  f"supported by the Fortran backend.")
573 
574  datatype = gen_datatype(symbol.datatype, symbol.name)
575  result = f"{self._nindent}{datatype}"
576 
577  if ArrayType.Extent.DEFERRED in array_shape:
578  # A 'deferred' array extent means this is an allocatable array
579  result += ", allocatable"
580 
581  # Specify Fortran attributes
582  if array_shape:
583  dims = self.gen_indices(array_shape)
584  result += ", dimension(" + ",".join(dims) + ")"
585 
586  if isinstance(symbol, DataSymbol) and symbol.is_argument:
587  intent = gen_intent(symbol)
588  if intent:
589  result += f", intent({intent})"
590 
591  if isinstance(symbol, DataSymbol) and symbol.is_constant:
592  result += ", parameter"
593  elif isinstance(symbol, DataSymbol) and symbol.is_static:
594  # This condition is an elif because SAVE and PARAMETER are
595  # incompatible, but we let PARAMETER take precedence because
596  # a parameter is already behaving like a static value
597  result += ", save"
598 
599  if include_visibility:
600  if symbol.visibility == Symbol.Visibility.PRIVATE:
601  result += ", private"
602  elif symbol.visibility == Symbol.Visibility.PUBLIC:
603  result += ", public"
604  else:
605  raise InternalError(
606  f"A Symbol must be either public or private but symbol "
607  f"'{symbol.name}' has visibility '{symbol.visibility}'")
608 
609  # Specify name
610  result += f" :: {symbol.name}"
611 
612  # Specify initialisation expression
613  if (isinstance(symbol, StructureType.ComponentType) and
614  symbol.initial_value):
615  result += " = " + self._visit(symbol.initial_value)
616  elif isinstance(symbol, DataSymbol) and symbol.initial_value:
617  if not symbol.is_static:
618  raise VisitorError(
619  f"{type(symbol).__name__} '{symbol.name}' has an initial "
620  f"value ({self._visit(symbol.initial_value)}) and "
621  f"therefore (in Fortran) must have a StaticInterface. "
622  f"However it has an interface of '{symbol.interface}'.")
623  result += " = " + self._visit(symbol.initial_value)
624 
625  return result + "\n"
626 

References psyclone.psyir.backend.visitor.PSyIRVisitor._visit(), psyclone.psyir.backend.c.CWriter.gen_indices(), psyclone.psyir.backend.fortran.FortranWriter.gen_indices(), psyclone.psyir.backend.language_writer.LanguageWriter.gen_indices(), and psyclone.psyir.backend.sympy_writer.SymPyWriter.gen_indices().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_operator()

def psyclone.psyir.backend.fortran.FortranWriter.get_operator (   self,
  operator 
)
Determine the Fortran operator that is equivalent to the provided
PSyIR operator. This is achieved by reversing the Fparser2Reader
maps that are used to convert from Fortran operator names to PSyIR
operator names.

:param operator: a PSyIR operator.
:type operator: :py:class:`psyclone.psyir.nodes.Operation.Operator`

:returns: the Fortran operator.
:rtype: str

:raises KeyError: if the supplied operator is not known.

Definition at line 372 of file fortran.py.

372  def get_operator(self, operator):
373  '''Determine the Fortran operator that is equivalent to the provided
374  PSyIR operator. This is achieved by reversing the Fparser2Reader
375  maps that are used to convert from Fortran operator names to PSyIR
376  operator names.
377 
378  :param operator: a PSyIR operator.
379  :type operator: :py:class:`psyclone.psyir.nodes.Operation.Operator`
380 
381  :returns: the Fortran operator.
382  :rtype: str
383 
384  :raises KeyError: if the supplied operator is not known.
385 
386  '''
387  return self._operator_2_str[operator]
388 

References psyclone.psyir.backend.fortran.FortranWriter._operator_2_str.

Here is the caller graph for this function:

◆ ifblock_node()

def psyclone.psyir.backend.fortran.FortranWriter.ifblock_node (   self,
  node 
)
This method is called when an IfBlock instance is found in the
PSyIR tree.

:param node: an IfBlock PSyIR node.
:type node: :py:class:`psyclone.psyir.nodes.IfBlock`

:returns: the Fortran code as a string.
:rtype: str

Definition at line 1384 of file fortran.py.

1384  def ifblock_node(self, node):
1385  '''This method is called when an IfBlock instance is found in the
1386  PSyIR tree.
1387 
1388  :param node: an IfBlock PSyIR node.
1389  :type node: :py:class:`psyclone.psyir.nodes.IfBlock`
1390 
1391  :returns: the Fortran code as a string.
1392  :rtype: str
1393 
1394  '''
1395  condition = self._visit(node.children[0])
1396 
1397  self._depth += 1
1398  if_body = ""
1399  for child in node.if_body:
1400  if_body += self._visit(child)
1401  else_body = ""
1402  # node.else_body is None if there is no else clause.
1403  if node.else_body:
1404  for child in node.else_body:
1405  else_body += self._visit(child)
1406  self._depth -= 1
1407 
1408  if else_body:
1409  result = (
1410  f"{self._nindent}if ({condition}) then\n"
1411  f"{if_body}"
1412  f"{self._nindent}else\n"
1413  f"{else_body}"
1414  f"{self._nindent}end if\n")
1415  else:
1416  result = (
1417  f"{self._nindent}if ({condition}) then\n"
1418  f"{if_body}"
1419  f"{self._nindent}end if\n")
1420  return result
1421 

References psyclone.psyir.backend.visitor.PSyIRVisitor._depth, and psyclone.psyir.backend.visitor.PSyIRVisitor._visit().

Here is the call graph for this function:

◆ kernelfunctor_node()

def psyclone.psyir.backend.fortran.FortranWriter.kernelfunctor_node (   self,
  node 
)
Translate the Kernel functor into Fortran.

:param node: the PSyIR node to translate.
:type node: :py:class:`psyclone.domain.common.algorithm.KernelFunctor`

:returns: the equivalent Fortran code.
:rtype: str

Definition at line 1725 of file fortran.py.

1725  def kernelfunctor_node(self, node):
1726  '''
1727  Translate the Kernel functor into Fortran.
1728 
1729  :param node: the PSyIR node to translate.
1730  :type node: :py:class:`psyclone.domain.common.algorithm.KernelFunctor`
1731 
1732  :returns: the equivalent Fortran code.
1733  :rtype: str
1734 
1735  '''
1736  result_list = []
1737  for child in node.children:
1738  result_list.append(self._visit(child))
1739  args = ", ".join(result_list)
1740  return f"{node.name}({args})"

References psyclone.psyir.backend.visitor.PSyIRVisitor._visit().

Here is the call graph for this function:

◆ literal_node()

def psyclone.psyir.backend.fortran.FortranWriter.literal_node (   self,
  node 
)
This method is called when a Literal instance is found in the PSyIR
tree.

:param node: a Literal PSyIR node.
:type node: :py:class:`psyclone.psyir.nodes.Literal`

:returns: the Fortran code for the literal.
:rtype: str

Reimplemented in psyclone.psyir.backend.sympy_writer.SymPyWriter.

Definition at line 1324 of file fortran.py.

1324  def literal_node(self, node):
1325  '''This method is called when a Literal instance is found in the PSyIR
1326  tree.
1327 
1328  :param node: a Literal PSyIR node.
1329  :type node: :py:class:`psyclone.psyir.nodes.Literal`
1330 
1331  :returns: the Fortran code for the literal.
1332  :rtype: str
1333 
1334  '''
1335  # pylint: disable=too-many-branches
1336  precision = node.datatype.precision
1337 
1338  if node.datatype.intrinsic == ScalarType.Intrinsic.BOOLEAN:
1339  # Booleans need to be converted to Fortran format
1340  result = '.' + node.value + '.'
1341  elif node.datatype.intrinsic == ScalarType.Intrinsic.CHARACTER:
1342  # Need to take care with which quotation symbol to use since a
1343  # character string may include quotation marks, e.g. a format
1344  # specifier: "('hello',3A)". The outermost quotation marks are
1345  # not stored so we have to decide whether to use ' or ".
1346  if "'" not in node.value:
1347  # No single quotes in the string so use those
1348  quote_symbol = "'"
1349  else:
1350  # There are single quotes in the string so we use double
1351  # quotes (after verifying that there aren't both single *and*
1352  # double quotes in the string).
1353  if '"' in node.value:
1354  raise NotImplementedError(
1355  f"Character literals containing both single and double"
1356  f" quotes are not supported but found >>{node.value}<<"
1357  )
1358  quote_symbol = '"'
1359  result = f"{quote_symbol}{node.value}{quote_symbol}"
1360  elif (node.datatype.intrinsic == ScalarType.Intrinsic.REAL and
1361  precision == ScalarType.Precision.DOUBLE):
1362  # The PSyIR stores real scalar values using the standard 'e'
1363  # notation. If the scalar is in fact double precision then this
1364  # 'e' must be replaced by 'd' for Fortran.
1365  result = node.value.replace("e", "d", 1)
1366  else:
1367  result = node.value
1368 
1369  if isinstance(precision, DataSymbol):
1370  # A KIND variable has been specified
1371  if node.datatype.intrinsic == ScalarType.Intrinsic.CHARACTER:
1372  result = f"{precision.name}_{result}"
1373  else:
1374  result = f"{result}_{precision.name}"
1375  if isinstance(precision, int):
1376  # A KIND value has been specified
1377  if node.datatype.intrinsic == ScalarType.Intrinsic.CHARACTER:
1378  result = f"{precision}_{result}"
1379  else:
1380  result = f"{result}_{precision}"
1381 
1382  return result
1383 

◆ loop_node()

def psyclone.psyir.backend.fortran.FortranWriter.loop_node (   self,
  node 
)
This method is called when a Loop instance is found in the
PSyIR tree.

:param node: a Loop PSyIR node.
:type node: :py:class:`psyclone.psyir.nodes.Loop`

:returns: the loop node converted into a (language specific) string.
:rtype: str

Definition at line 1447 of file fortran.py.

1447  def loop_node(self, node):
1448  '''This method is called when a Loop instance is found in the
1449  PSyIR tree.
1450 
1451  :param node: a Loop PSyIR node.
1452  :type node: :py:class:`psyclone.psyir.nodes.Loop`
1453 
1454  :returns: the loop node converted into a (language specific) string.
1455  :rtype: str
1456 
1457  '''
1458  start = self._visit(node.start_expr)
1459  stop = self._visit(node.stop_expr)
1460  step = self._visit(node.step_expr)
1461 
1462  self._depth += 1
1463  body = ""
1464  for child in node.loop_body:
1465  body += self._visit(child)
1466  self._depth -= 1
1467 
1468  # A generation error is raised if variable is not defined. This
1469  # happens in LFRic kernel that iterate over a domain.
1470  try:
1471  variable_name = node.variable.name
1472  except GenerationError:
1473  # If a kernel iterates over a domain - there is
1474  # no loop. But the loop node is maintained since it handles halo
1475  # exchanges. So just return the body in this case
1476  return body
1477 
1478  return (
1479  f"{self._nindent}do {variable_name} = {start}, {stop}, {step}\n"
1480  f"{body}"
1481  f"{self._nindent}enddo\n")
1482 

References psyclone.psyir.backend.visitor.PSyIRVisitor._depth, and psyclone.psyir.backend.visitor.PSyIRVisitor._visit().

Here is the call graph for this function:

◆ operandclause_node()

def psyclone.psyir.backend.fortran.FortranWriter.operandclause_node (   self,
  node 
)
This method is called when a OperandClause is
found in the PSyIR tree. It returns the clause and its children
as a string.

:param node: an OperandClause PSyIR node.
:type node: :py:class:`psyclone.psyir.nodes.OperandClause`

:returns: the Fortran code for this node.
:rtype: str

Definition at line 1574 of file fortran.py.

1574  def operandclause_node(self, node):
1575  '''This method is called when a OperandClause is
1576  found in the PSyIR tree. It returns the clause and its children
1577  as a string.
1578 
1579  :param node: an OperandClause PSyIR node.
1580  :type node: :py:class:`psyclone.psyir.nodes.OperandClause`
1581 
1582  :returns: the Fortran code for this node.
1583  :rtype: str
1584 
1585  '''
1586  if len(node.children) == 0:
1587  return ""
1588 
1589  result = node.clause_string
1590 
1591  result = result + "(" + node.operand + ": "
1592 
1593  child_list = []
1594  for child in node.children:
1595  child_list.append(self._visit(child))
1596 
1597  result = result + ",".join(child_list) + ")"
1598 
1599  return result
1600 

References psyclone.psyir.backend.visitor.PSyIRVisitor._visit().

Here is the call graph for this function:

◆ range_node()

def psyclone.psyir.backend.fortran.FortranWriter.range_node (   self,
  node 
)
This method is called when a Range instance is found in the PSyIR
tree.

:param node: a Range PSyIR node.
:type node: :py:class:`psyclone.psyir.nodes.Range`

:returns: the Fortran code as a string.
:rtype: str

Reimplemented in psyclone.psyir.backend.sympy_writer.SymPyWriter.

Definition at line 1283 of file fortran.py.

1283  def range_node(self, node):
1284  '''This method is called when a Range instance is found in the PSyIR
1285  tree.
1286 
1287  :param node: a Range PSyIR node.
1288  :type node: :py:class:`psyclone.psyir.nodes.Range`
1289 
1290  :returns: the Fortran code as a string.
1291  :rtype: str
1292 
1293  '''
1294  if node.parent and node.parent.is_lower_bound(
1295  node.parent.indices.index(node)):
1296  # The range starts for the first element in this
1297  # dimension. This is the default in Fortran so no need to
1298  # output anything.
1299  start = ""
1300  else:
1301  start = self._visit(node.start)
1302 
1303  if node.parent and node.parent.is_upper_bound(
1304  node.parent.indices.index(node)):
1305  # The range ends with the last element in this
1306  # dimension. This is the default in Fortran so no need to
1307  # output anything.
1308  stop = ""
1309  else:
1310  stop = self._visit(node.stop)
1311  result = f"{start}:{stop}"
1312 
1313  if isinstance(node.step, Literal) and \
1314  node.step.datatype.intrinsic == ScalarType.Intrinsic.INTEGER and \
1315  node.step.value == "1":
1316  # Step is 1. This is the default in Fortran so no need to
1317  # output any text.
1318  pass
1319  else:
1320  step = self._visit(node.step)
1321  result += f":{step}"
1322  return result
1323 

References psyclone.psyir.backend.visitor.PSyIRVisitor._visit().

Here is the call graph for this function:

◆ regiondirective_node()

def psyclone.psyir.backend.fortran.FortranWriter.regiondirective_node (   self,
  node 
)
This method is called when a RegionDirective instance is found in
the PSyIR tree. It returns the opening and closing directives, and
the statements in between as a string.

:param node: a RegionDirective PSyIR node.
:type node: :py:class:`psyclone.psyir.nodes.RegionDirective`

:returns: the Fortran code for this node.
:rtype: str

Definition at line 1601 of file fortran.py.

1601  def regiondirective_node(self, node):
1602  '''This method is called when a RegionDirective instance is found in
1603  the PSyIR tree. It returns the opening and closing directives, and
1604  the statements in between as a string.
1605 
1606  :param node: a RegionDirective PSyIR node.
1607  :type node: :py:class:`psyclone.psyir.nodes.RegionDirective`
1608 
1609  :returns: the Fortran code for this node.
1610  :rtype: str
1611 
1612  '''
1613  result = f"{self._nindent}!${node.begin_string()}"
1614 
1615  clause_list = []
1616  for clause in node.clauses:
1617  val = self._visit(clause)
1618  # Some clauses return empty strings if they should not
1619  # generate any output (e.g. private clause with no children).
1620  if val != "":
1621  clause_list.append(val)
1622  # Add a space only if there are clauses
1623  if len(clause_list) > 0:
1624  result = result + " "
1625  result = result + ", ".join(clause_list)
1626  result = result + "\n"
1627 
1628  for child in node.dir_body:
1629  result = result + self._visit(child)
1630 
1631  end_string = node.end_string()
1632  if end_string:
1633  result = result + f"{self._nindent}!${end_string}\n"
1634  return result
1635 

References psyclone.psyir.backend.visitor.PSyIRVisitor._visit().

Here is the call graph for this function:

◆ return_node()

def psyclone.psyir.backend.fortran.FortranWriter.return_node (   self,
  _ 
)
This method is called when a Return instance is found in
the PSyIR tree.

:param node: a Return PSyIR node.
:type node: :py:class:`psyclone.psyir.nodes.Return`

:returns: the Fortran code as a string.
:rtype: str

Definition at line 1533 of file fortran.py.

1533  def return_node(self, _):
1534  '''This method is called when a Return instance is found in
1535  the PSyIR tree.
1536 
1537  :param node: a Return PSyIR node.
1538  :type node: :py:class:`psyclone.psyir.nodes.Return`
1539 
1540  :returns: the Fortran code as a string.
1541  :rtype: str
1542 
1543  '''
1544  return f"{self._nindent}return\n"
1545 

◆ routine_node()

def psyclone.psyir.backend.fortran.FortranWriter.routine_node (   self,
  node 
)
This method is called when a Routine node is found in
the PSyIR tree.

:param node: a Routine PSyIR node.
:type node: :py:class:`psyclone.psyir.nodes.Routine`

:returns: the Fortran code for this node.
:rtype: str

:raises VisitorError: if the name attribute of the supplied \
                      node is empty or None.

Definition at line 1140 of file fortran.py.

1140  def routine_node(self, node):
1141  '''This method is called when a Routine node is found in
1142  the PSyIR tree.
1143 
1144  :param node: a Routine PSyIR node.
1145  :type node: :py:class:`psyclone.psyir.nodes.Routine`
1146 
1147  :returns: the Fortran code for this node.
1148  :rtype: str
1149 
1150  :raises VisitorError: if the name attribute of the supplied \
1151  node is empty or None.
1152 
1153  '''
1154  if not node.name:
1155  raise VisitorError("Expected node name to have a value.")
1156 
1157  if node.is_program:
1158  result = f"{self._nindent}program {node.name}\n"
1159  routine_type = "program"
1160  else:
1161  args = [symbol.name for symbol in node.symbol_table.argument_list]
1162  suffix = ""
1163  if node.return_symbol:
1164  # This Routine has a return value and is therefore a Function
1165  routine_type = "function"
1166  if node.return_symbol.name.lower() != node.name.lower():
1167  suffix = f" result({node.return_symbol.name})"
1168  else:
1169  routine_type = "subroutine"
1170  result = f"{self._nindent}{routine_type} {node.name}("
1171  result += ", ".join(args) + f"){suffix}\n"
1172 
1173  self._depth += 1
1174 
1175  if self._DISABLE_LOWERING:
1176  # If we are not lowering we don't have a deep_copied tree so it
1177  # should NOT make any modifications to the provided node or
1178  # symbol table.
1179  whole_routine_scope = node.symbol_table
1180  else:
1181  # The PSyIR has nested scopes but Fortran only supports declaring
1182  # variables at the routine level scope. For this reason, at this
1183  # point we have to unify all declarations and resolve possible name
1184  # clashes that appear when merging the scopes. Make sure we use
1185  # the same SymbolTable class used in the base class to get an
1186  # API-specific table here:
1187  whole_routine_scope = type(node.symbol_table)()
1188 
1189  for schedule in node.walk(Schedule):
1190  sched_table = schedule.symbol_table
1191  # We can't declare a routine inside itself so make sure we
1192  # skip any RoutineSymbol representing this routine.
1193  try:
1194  rsym = sched_table.lookup_with_tag("own_routine_symbol")
1195  skip = [rsym] if isinstance(rsym, RoutineSymbol) else []
1196  except KeyError:
1197  skip = []
1198  whole_routine_scope.merge(sched_table, skip)
1199  if schedule is node:
1200  # Replace the Routine's symbol table as soon as we've
1201  # merged it into the new one. This ensures that the new
1202  # table has full information on outer scopes which is
1203  # important when merging.
1204  node.symbol_table.detach()
1205  whole_routine_scope.attach(node)
1206 
1207  # Generate module imports
1208  imports = ""
1209  for symbol in whole_routine_scope.containersymbols:
1210  imports += self.gen_use(symbol, whole_routine_scope)
1211 
1212  # Generate declaration statements
1213  declarations = self.gen_decls(whole_routine_scope)
1214 
1215  # Get the executable statements.
1216  exec_statements = ""
1217  for child in node.children:
1218  exec_statements += self._visit(child)
1219  result += (
1220  f"{imports}"
1221  f"{declarations}\n"
1222  f"{exec_statements}\n")
1223 
1224  self._depth -= 1
1225  result += f"{self._nindent}end {routine_type} {node.name}\n"
1226 
1227  return result
1228 

References psyclone.psyir.backend.visitor.PSyIRVisitor._depth, psyclone.psyir.backend.debug_writer.DebugWriter._DISABLE_LOWERING, psyclone.psyir.backend.sympy_writer.SymPyWriter._DISABLE_LOWERING, psyclone.psyir.backend.visitor.PSyIRVisitor._DISABLE_LOWERING, psyclone.psyir.backend.visitor.PSyIRVisitor._visit(), psyclone.psyir.backend.fortran.FortranWriter.gen_decls(), and psyclone.psyir.backend.fortran.FortranWriter.gen_use().

Here is the call graph for this function:

◆ standalonedirective_node()

def psyclone.psyir.backend.fortran.FortranWriter.standalonedirective_node (   self,
  node 
)
This method is called when a StandaloneDirective instance is found
in the PSyIR tree. It returns the directive as a string.

:param node: a StandaloneDirective PSyIR node.
:type node: :py:class:`psyclone.psyir.nodes.StandloneDirective`

:returns: the Fortran code for this node.
:rtype: str

Definition at line 1636 of file fortran.py.

1636  def standalonedirective_node(self, node):
1637  '''This method is called when a StandaloneDirective instance is found
1638  in the PSyIR tree. It returns the directive as a string.
1639 
1640  :param node: a StandaloneDirective PSyIR node.
1641  :type node: :py:class:`psyclone.psyir.nodes.StandloneDirective`
1642 
1643  :returns: the Fortran code for this node.
1644  :rtype: str
1645 
1646  '''
1647  result = f"{self._nindent}!${node.begin_string()}"
1648 
1649  clause_list = []
1650  # Currently no standalone directives have clauses associated
1651  # so this code is left commented out. If a standalone directive
1652  # is added with clauses, this should be added in.
1653  # for clause in node.clauses:
1654  # clause_list.append(self._visit(clause))
1655  # Add a space only if there are clauses
1656  # if len(clause_list) > 0:
1657  # result = result + " "
1658  result = result + ", ".join(clause_list)
1659  result = result + "\n"
1660 
1661  return result
1662 

References psyclone.psyir.backend.visitor.PSyIRVisitor._visit().

Here is the call graph for this function:

◆ unaryoperation_node()

def psyclone.psyir.backend.fortran.FortranWriter.unaryoperation_node (   self,
  node 
)
This method is called when a UnaryOperation instance is found in
the PSyIR tree.

:param node: a UnaryOperation PSyIR node.
:type node: :py:class:`psyclone.psyir.nodes.UnaryOperation`

:returns: the Fortran code as a string.
:rtype: str

:raises VisitorError: if an unexpected Unary op is encountered.

Definition at line 1483 of file fortran.py.

1483  def unaryoperation_node(self, node):
1484  '''This method is called when a UnaryOperation instance is found in
1485  the PSyIR tree.
1486 
1487  :param node: a UnaryOperation PSyIR node.
1488  :type node: :py:class:`psyclone.psyir.nodes.UnaryOperation`
1489 
1490  :returns: the Fortran code as a string.
1491  :rtype: str
1492 
1493  :raises VisitorError: if an unexpected Unary op is encountered.
1494 
1495  '''
1496  content = self._visit(node.children[0])
1497  try:
1498  fort_oper = self.get_operator(node.operator)
1499  # If the parent node is a UnaryOperation or a BinaryOperation
1500  # such as '-' or '**' then we need parentheses. This ensures we
1501  # don't generate invalid Fortran such as 'a ** -b', 'a - -b' or
1502  # '-a ** b' which is '-(a ** b)' instead of intended '(-a) ** b'.
1503  # Also consider the grandparent node to avoid generating invalid
1504  # Fortran such as 'a + -b * c' instead of intended 'a + (-b) * c'.
1505  parent = node.parent
1506  if isinstance(parent, UnaryOperation):
1507  return f"({fort_oper}{content})"
1508  if isinstance(parent, BinaryOperation):
1509  parent_fort_oper = self.get_operator(parent.operator)
1510  if (node is parent.children[1] or
1511  (parent_fort_oper == "**" and fort_oper == "-")):
1512  return f"({fort_oper}{content})"
1513  grandparent = parent.parent
1514  # Case: 'a op1 (-b) op2 c'
1515  # and precedence(op2) > precedence(op1)
1516  # implying that '(-b) op2 c' is not parenthesized.
1517  if isinstance(grandparent, BinaryOperation):
1518  grandparent_fort_oper = self.get_operator(
1519  grandparent.operator
1520  )
1521  if (parent is grandparent.children[1]
1522  and node is parent.children[0]
1523  and (precedence(parent_fort_oper)
1524  > precedence(grandparent_fort_oper))
1525  and fort_oper == "-"):
1526  return f"({fort_oper}{content})"
1527  return f"{fort_oper}{content}"
1528 
1529  except KeyError as error:
1530  raise VisitorError(
1531  f"Unexpected unary op '{node.operator}'.") from error
1532 

References psyclone.psyir.backend.visitor.PSyIRVisitor._visit(), and psyclone.psyir.backend.fortran.FortranWriter.get_operator().

Here is the call graph for this function:

◆ whileloop_node()

def psyclone.psyir.backend.fortran.FortranWriter.whileloop_node (   self,
  node 
)
This method is called when a WhileLoop instance is found in the
PSyIR tree.

:param node: a WhileLoop PSyIR node.
:type node: :py:class:`psyclone.psyir.nodes.WhileLoop`

:returns: the Fortran code.
:rtype: str

Definition at line 1422 of file fortran.py.

1422  def whileloop_node(self, node):
1423  '''This method is called when a WhileLoop instance is found in the
1424  PSyIR tree.
1425 
1426  :param node: a WhileLoop PSyIR node.
1427  :type node: :py:class:`psyclone.psyir.nodes.WhileLoop`
1428 
1429  :returns: the Fortran code.
1430  :rtype: str
1431 
1432  '''
1433  condition = self._visit(node.condition)
1434 
1435  self._depth += 1
1436  body = ""
1437  for child in node.loop_body:
1438  body += self._visit(child)
1439  self._depth -= 1
1440 
1441  result = (
1442  f"{self._nindent}do while ({condition})\n"
1443  f"{body}"
1444  f"{self._nindent}end do\n")
1445  return result
1446 

References psyclone.psyir.backend.visitor.PSyIRVisitor._depth, and psyclone.psyir.backend.visitor.PSyIRVisitor._visit().

Here is the call graph for this function:

The documentation for this class was generated from the following file: