Skip to content

Commit

Permalink
#2716 add InterfaceDeclGen to f2pygen
Browse files Browse the repository at this point in the history
  • Loading branch information
arporter committed Oct 8, 2024
1 parent 0b6ce32 commit a00f367
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 3 deletions.
52 changes: 52 additions & 0 deletions src/psyclone/f2pygen.py
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down
35 changes: 32 additions & 3 deletions src/psyclone/tests/f2pygen_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand All @@ -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 '''
Expand All @@ -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"
Expand Down

0 comments on commit a00f367

Please sign in to comment.