diff --git a/src/psyclone/f2pygen.py b/src/psyclone/f2pygen.py index 4db3407e44..f983e8f715 100644 --- a/src/psyclone/f2pygen.py +++ b/src/psyclone/f2pygen.py @@ -1275,6 +1275,58 @@ def _check_initial_values(self, _type, _values): "for derived-type declarations are not supported.") +class InterfaceDeclGen(BaseDeclGen): + ''' + Generates the declaration for a Fortran interface block of the form: + + interface my_wrapper + module procedure :: name1, name1, ... + end interface my_wrapper + + The declaration is added to the supplied parent node. + + :param parent: f2pygen node to which to add this declaration as a child. + :type parent: :py:class:`psyclone.f2pygen.ModuleGen` + :param str name: the name of the interface block. + :param entity_decls: names of procedures to declare within the block. + :type entity_decls: list[str] + + :raises TypeError: if the supplied parent is not a ModuleGen (since + interfaces must be declared within modules). + :raises ValueError: if a list of procedure names is not supplied. + + ''' + def __init__(self, parent, name, entity_decls): + if not isinstance(parent, ModuleGen): + raise TypeError(f"An InterfaceDeclGen must have a ModuleGen as " + f"parent but got '{type(parent).__name__}'") + if not isinstance(entity_decls, list) or not entity_decls: + raise ValueError( + f"The routine names to use within Interface '{name}' must be " + f"supplied as a list of str to 'entity_decls' but got " + f"{entity_decls}") + name_list = ", ".join(entity_decls) + reader = FortranStringReader(f"interface {name}\n" + f" module procedure :: {name_list}\n" + f"end interface {name}\n") + reader.set_format(FortranFormat(True, True)) # free form, strict + line1 = reader.next() + interf = fparser1.block_statements.Interface(parent.root, line1) + line2 = reader.next() + procedures = fparser1.block_statements.ModuleProcedure(parent.root, + line2) + interf.content.append(procedures) + line3 = reader.next() + end_interf = fparser1.block_statements.EndInterface(interf, line3) + self._decl = interf + interf.content.append(end_interf) + + def _check_initial_values(self): + ''' + Empty routine required to override that from abstract base class. + ''' + + class TypeCase(Case): ''' Generate a Fortran SELECT CASE statement ''' # TODO can this whole class be deleted? diff --git a/src/psyclone/tests/f2pygen_test.py b/src/psyclone/tests/f2pygen_test.py index fccefd5b24..78e48bf1b6 100644 --- a/src/psyclone/tests/f2pygen_test.py +++ b/src/psyclone/tests/f2pygen_test.py @@ -40,7 +40,8 @@ from psyclone.f2pygen import ( adduse, AssignGen, AllocateGen, BaseGen, CallGen, CharDeclGen, CommentGen, DeallocateGen, DeclGen, DirectiveGen, DoGen, IfThenGen, ImplicitNoneGen, - ModuleGen, PSyIRGen, SelectionGen, SubroutineGen, TypeDeclGen, UseGen) + InterfaceDeclGen, ModuleGen, PSyIRGen, SelectionGen, SubroutineGen, + TypeDeclGen, UseGen) from psyclone.errors import GenerationError, InternalError from psyclone.psyir.nodes import Node, Return from psyclone.tests.utilities import Compile, count_lines, line_number @@ -1342,7 +1343,6 @@ def test_declgen_multiple_use2(): sub.add(DeclGen(sub, datatype="integer", entity_decls=datanames)) gen = str(sub.root) - print(gen) expected = ( " INTEGER data1, data2\n" " REAL data1") @@ -1351,6 +1351,36 @@ def test_declgen_multiple_use2(): assert datanames == ["data1", "data2"] +def test_interfacedeclgen(): + '''Tests for InterfaceDeclGen.''' + module = ModuleGen(name="testmodule") + sub1 = SubroutineGen(module, name="sub32") + module.add(sub1) + sub2 = SubroutineGen(module, name="sub64") + module.add(sub2) + with pytest.raises(TypeError) as err: + InterfaceDeclGen(sub1, name="my_interface", entity_decls=[]) + assert ("An InterfaceDeclGen must have a ModuleGen as parent" + in str(err.value)) + with pytest.raises(ValueError) as err: + InterfaceDeclGen(module, name="my_interface", entity_decls=[]) + assert ("The routine names to use within Interface 'my_interface' must be" + in str(err.value)) + module.add(InterfaceDeclGen(module, name="my_interface", + entity_decls=["sub32", "sub64"])) + gen = str(module.root).lower() + expected = """\ + module testmodule + implicit none + interface my_interface + module procedure sub32, sub64 + end interface my_interface + contains""" + assert expected in gen + # Can't compile this without properly creating subroutines with arguments + # of different precisions. + + @pytest.mark.xfail(reason="No way to add body of DEFAULT clause") def test_selectiongen(): ''' Check that SelectionGen works as expected ''' @@ -1364,7 +1394,6 @@ def test_selectiongen(): # TODO how do we specify what happens in the default case? sgen.adddefault() gen = str(sub.root) - print(gen) expected = ("SELECT CASE ( my_var )\n" "CASE ( 1 )\n" " happy = .TRUE.\n"