Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Small parsing fixes #190

Merged
merged 3 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions loki/backend/fgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,20 +328,25 @@ def visit_VariableDeclaration(self, o, **kwargs):
# the symbol has a known derived type
ignore = ['shape', 'dimensions', 'variables', 'source', 'initial']

if isinstance(types[0].dtype, ProcedureType):
# Statement functions can share declarations with scalars, so we collect the variable types here
_var_types = [t.dtype.return_type.dtype if isinstance(t.dtype, ProcedureType) else t.dtype for t in types]
_procedure_types = [t for t in types if isinstance(t.dtype, ProcedureType)]

if _procedure_types:
# Statement functions are the only symbol with ProcedureType that should appear
# in a VariableDeclaration as all other forms of procedure declarations (bindings,
# pointers, EXTERNAL statements) are handled by ProcedureDeclaration.
# However, the fact that statement function declarations can appear mixed with actual
# variable declarations forbids this in this case.
assert types[0].is_stmt_func
assert all(t.is_stmt_func for t in _procedure_types)
# TODO: We can't fully compare statement functions, yet but we can make at least sure
# other declared attributes are compatible and that all have the same return type
ignore += ['dtype']
assert all(t.dtype.return_type == types[0].dtype.return_type or
t.dtype.return_type.compare(types[0].dtype.return_type, ignore=ignore) for t in types)
assert all(t.dtype.return_type == _procedure_types[0].dtype.return_type or
t.dtype.return_type.compare(_procedure_types[0].dtype.return_type, ignore=ignore)
for t in _procedure_types)

assert all(t.compare(types[0], ignore=ignore) for t in types)
assert all((t == _var_types[0]) for t in _var_types)

is_function = isinstance(types[0].dtype, ProcedureType) and types[0].dtype.is_function
if is_function:
Expand Down
7 changes: 4 additions & 3 deletions loki/frontend/fparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -1812,11 +1812,12 @@ def visit_Subroutine_Subprogram(self, o, **kwargs):
rescope_symbols=True, source=source, incomplete=False
)

# Once statement functions are in place, we need to update the original declaration symbol
# Once statement functions are in place, we need to update the original declaration so that it
# contains ProcedureSymbols rather than Scalars
for decl in FindNodes(ir.VariableDeclaration).visit(spec):
if any(routine.symbol_attrs[s.name].is_stmt_func for s in decl.symbols):
assert all(routine.symbol_attrs[s.name].is_stmt_func for s in decl.symbols)
decl._update(symbols=tuple(s.clone() for s in decl.symbols))
decl._update(symbols=tuple(s.clone() if routine.symbol_attrs[s.name].is_stmt_func else s
for s in decl.symbols))

# Big, but necessary hack:
# For deferred array dimensions on allocatables, we infer the conceptual
Expand Down
4 changes: 2 additions & 2 deletions loki/frontend/regex.py
Original file line number Diff line number Diff line change
Expand Up @@ -468,8 +468,8 @@ def __init__(self):
r'^(?P<prefix>[ \t\w()=]*)?(?P<keyword>subroutine|function)[ \t]+(?P<name>\w+)\b.*?$'
r'(?P<spec>(?:.*?(?:^(?:abstract[ \t]+)?interface\b.*?^end[ \t]+interface)?)+)'
r'(?P<contains>^contains\n(?:'
r'(?:[ \t\w()]*?subroutine.*?^end[ \t]*subroutine\b(?:[ \t]\w+)?\n)|'
r'(?:[ \t\w()]*?function.*?^end[ \t]*function\b(?:[ \t]\w+)?\n)|'
r'(?:[ \t\w()=]*?subroutine.*?^end[ \t]*subroutine\b(?:[ \t]\w+)?\n)|'
r'(?:[ \t\w()=]*?function.*?^end[ \t]*function\b(?:[ \t]\w+)?\n)|'
r'(?:^#\w+.*?\n)'
r')*)?'
r'^end[ \t]*(?P=keyword)\b(?:[ \t](?P=name))?',
Expand Down
14 changes: 8 additions & 6 deletions tests/test_frontends.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ def test_regex_subroutine_from_source():
! arg2
j
)
use parkind1, only : jpim
implicit none
integer, intent(in) :: i, j
integer b
Expand All @@ -391,16 +392,17 @@ def test_regex_subroutine_from_source():
call routine_a()
contains
!abc ^$^**
integer(kind=jpim) function contained_e(i)
integer, intent(in) :: i
contained_e = i
end function

subroutine contained_c(i)
integer, intent(in) :: i
integer c
c = 5
end subroutine contained_c
! cc£$^£$^
integer function contained_e(i)
integer, intent(in) :: i
contained_e = i
end function

subroutine contained_d(i)
integer, intent(in) :: i
Expand All @@ -415,7 +417,7 @@ def test_regex_subroutine_from_source():
assert not routine.is_function
assert routine.arguments == ()
assert routine.argnames == []
assert [r.name for r in routine.subroutines] == ['contained_c', 'contained_e', 'contained_d']
assert [r.name for r in routine.subroutines] == ['contained_e', 'contained_c', 'contained_d']

contained_c = routine['contained_c']
assert contained_c.name == 'contained_c'
Expand Down Expand Up @@ -1487,7 +1489,7 @@ def test_regex_fypp():


@pytest.mark.parametrize(
'frontend',
'frontend',
available_frontends(include_regex=True, xfail=[(OMNI, 'OMNI may segfault on empty files')])
)
@pytest.mark.parametrize('fcode', ['', '\n', '\n\n\n\n'])
Expand Down
3 changes: 1 addition & 2 deletions tests/test_subroutine.py
Original file line number Diff line number Diff line change
Expand Up @@ -1493,8 +1493,7 @@ def test_subroutine_stmt_func(here, frontend):
integer, intent(in) :: a
integer, intent(out) :: b
integer :: array(a)
integer :: i, j
integer :: plus, minus
integer :: i, j, plus, minus
plus(i, j) = i + j
minus(i, j) = i - j
integer :: mult
Expand Down