Skip to content

Commit df11a85

Browse files
committed
use 'parse_expr' in 'process_dimension_pragmas'
1 parent 914c492 commit df11a85

File tree

5 files changed

+72
-15
lines changed

5 files changed

+72
-15
lines changed

loki/frontend/fparser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1864,7 +1864,7 @@ def visit_Subroutine_Subprogram(self, o, **kwargs):
18641864

18651865
# Update array shapes with Loki dimension pragmas
18661866
with pragmas_attached(routine, ir.VariableDeclaration):
1867-
routine.spec = process_dimension_pragmas(routine.spec)
1867+
routine.spec = process_dimension_pragmas(routine.spec, scope=routine)
18681868

18691869
if isinstance(o, Fortran2003.Subroutine_Body):
18701870
# Return the subroutine object along with any clutter before it for interface declarations

loki/frontend/ofp.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1230,7 +1230,7 @@ def visit_subroutine(self, o, **kwargs):
12301230

12311231
# Update array shapes with Loki dimension pragmas
12321232
with pragmas_attached(routine, ir.VariableDeclaration):
1233-
routine.spec = process_dimension_pragmas(routine.spec)
1233+
routine.spec = process_dimension_pragmas(routine.spec, scope=routine)
12341234

12351235
return routine
12361236

loki/frontend/omni.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ def visit_FfunctionDefinition(self, o, **kwargs):
433433

434434
# Update array shapes with Loki dimension pragmas
435435
with pragmas_attached(routine, ir.VariableDeclaration):
436-
routine.spec = process_dimension_pragmas(routine.spec)
436+
routine.spec = process_dimension_pragmas(routine.spec, scope=routine)
437437

438438
return routine
439439

loki/frontend/tests/test_frontends.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1970,3 +1970,64 @@ def test_source_sanitize_fp_module(preprocess):
19701970

19711971

19721972
# TODO: Add tests for source sanitizer with other frontends
1973+
1974+
1975+
@pytest.mark.parametrize('frontend', available_frontends(xfail=[(OMNI, 'OMNI does not like Loki pragmas, yet!')]))
1976+
def test_frontend_routine_variables_dimension_pragmas(frontend):
1977+
"""
1978+
Test that `!$loki dimension` pragmas can be used to verride the
1979+
conceptual `.shape` of local and argument variables.
1980+
"""
1981+
fcode = """
1982+
subroutine routine_variables_dimensions(x, y, v0, v1, v2, v3, v4)
1983+
integer, parameter :: jprb = selected_real_kind(13,300)
1984+
integer, intent(in) :: x, y
1985+
1986+
!$loki dimension(10)
1987+
real(kind=jprb), intent(inout) :: v0(:)
1988+
!$loki dimension(x)
1989+
real(kind=jprb), intent(inout) :: v1(:)
1990+
!$loki dimension(x,y,:)
1991+
real(kind=jprb), dimension(:,:,:), intent(inout) :: v2, v3
1992+
!$loki dimension(x,y)
1993+
real(kind=jprb), pointer, intent(inout) :: v4(:,:)
1994+
!$loki dimension(x+y,2*x)
1995+
real(kind=jprb), allocatable :: v5(:,:)
1996+
!$loki dimension(x/2, x**2, (x+y)/x)
1997+
real(kind=jprb), dimension(:, :, :), pointer :: v6
1998+
1999+
end subroutine routine_variables_dimensions
2000+
"""
2001+
def to_str(expr):
2002+
return str(expr).lower().replace(' ', '')
2003+
2004+
routine = Subroutine.from_source(fcode, frontend=frontend)
2005+
assert routine.variable_map['v0'].shape[0] == 10
2006+
assert isinstance(routine.variable_map['v0'].shape[0], sym.IntLiteral)
2007+
assert isinstance(routine.variable_map['v1'].shape[0], sym.Scalar)
2008+
assert routine.variable_map['v2'].shape[0] == 'x'
2009+
assert routine.variable_map['v2'].shape[1] == 'y'
2010+
assert routine.variable_map['v2'].shape[2] == ':'
2011+
assert isinstance(routine.variable_map['v2'].shape[0], sym.Scalar)
2012+
assert isinstance(routine.variable_map['v2'].shape[1], sym.Scalar)
2013+
assert isinstance(routine.variable_map['v2'].shape[2], sym.RangeIndex)
2014+
assert routine.variable_map['v3'].shape[0] == 'x'
2015+
assert routine.variable_map['v3'].shape[1] == 'y'
2016+
assert routine.variable_map['v3'].shape[2] == ':'
2017+
assert isinstance(routine.variable_map['v3'].shape[0], sym.Scalar)
2018+
assert isinstance(routine.variable_map['v3'].shape[1], sym.Scalar)
2019+
assert isinstance(routine.variable_map['v3'].shape[2], sym.RangeIndex)
2020+
assert routine.variable_map['v4'].shape[0] == 'x'
2021+
assert routine.variable_map['v4'].shape[1] == 'y'
2022+
assert isinstance(routine.variable_map['v4'].shape[0], sym.Scalar)
2023+
assert isinstance(routine.variable_map['v4'].shape[1], sym.Scalar)
2024+
assert to_str(routine.variable_map['v5'].shape[0]) == 'x+y'
2025+
assert to_str(routine.variable_map['v5'].shape[1]) == '2*x'
2026+
assert isinstance(routine.variable_map['v5'].shape[0], sym.Sum)
2027+
assert isinstance(routine.variable_map['v5'].shape[1], sym.Product)
2028+
assert to_str(routine.variable_map['v6'].shape[0]) == 'x/2'
2029+
assert to_str(routine.variable_map['v6'].shape[1]) == 'x**2'
2030+
assert to_str(routine.variable_map['v6'].shape[2]) == '(x+y)/x'
2031+
assert isinstance(routine.variable_map['v6'].shape[0], sym.Quotient)
2032+
assert isinstance(routine.variable_map['v6'].shape[1], sym.Power)
2033+
assert isinstance(routine.variable_map['v6'].shape[2], sym.Quotient)

loki/ir/pragma_utils.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@
1414
from loki.ir.find import FindNodes
1515
from loki.ir.transformer import Transformer
1616
from loki.ir.visitor import Visitor
17-
17+
# from loki.expression.parser import parse_expr
1818
from loki.tools.util import as_tuple, replace_windowed
19-
from loki.types import BasicType
2019
from loki.logging import debug, warning
2120

2221

@@ -51,7 +50,7 @@ def is_loki_pragma(pragma, starts_with=None):
5150
return True
5251

5352

54-
_get_pragma_parameters_re = re.compile(r'(?P<command>[\w-]+)\s*(?:\((?P<arg>.+?)\))?')
53+
_get_pragma_parameters_re = re.compile(r'(?P<command>[\w-]+)\s*(?:\((?P<arg>.*)\))?')
5554

5655
def get_pragma_parameters(pragma, starts_with=None, only_loki_pragmas=True):
5756
"""
@@ -94,7 +93,7 @@ def get_pragma_parameters(pragma, starts_with=None, only_loki_pragmas=True):
9493
return parameters
9594

9695

97-
def process_dimension_pragmas(ir):
96+
def process_dimension_pragmas(ir, scope=None):
9897
"""
9998
Process any ``!$loki dimension`` pragmas to override deferred dimensions
10099
@@ -106,21 +105,18 @@ def process_dimension_pragmas(ir):
106105
ir : :any:`Node`
107106
Root node of the (section of the) internal representation to process
108107
"""
109-
from loki.expression.symbols import Literal, Variable # pylint: disable=import-outside-toplevel
108+
from loki.expression.parser import parse_expr # pylint: disable=import-outside-toplevel
110109

111110
for decl in FindNodes(VariableDeclaration).visit(ir):
112111
if is_loki_pragma(decl.pragma, starts_with='dimension'):
113112
for v in decl.symbols:
114113
# Found dimension override for variable
115114
dims = get_pragma_parameters(decl.pragma)['dimension']
116115
dims = [d.strip() for d in dims.split(',')]
117-
shape = []
118-
for d in dims:
119-
if d.isnumeric():
120-
shape += [Literal(value=int(d), type=BasicType.INTEGER)]
121-
else:
122-
shape += [Variable(name=d, scope=v.scope)]
123-
v.scope.symbol_attrs[v.name] = v.type.clone(shape=as_tuple(shape))
116+
# parse each dimension
117+
shape = tuple(parse_expr(d, scope=scope) for d in dims)
118+
# update symbol table
119+
v.scope.symbol_attrs[v.name] = v.type.clone(shape=shape)
124120
return ir
125121

126122

0 commit comments

Comments
 (0)