Skip to content

Commit

Permalink
Used valid FORTRAN test program for a couple frontend tests + Made `f…
Browse files Browse the repository at this point in the history
…loatlit2string()` convert the FORTRAN real literal strings into python floats. (#1733)

Replace some of the invalid fortran test programs with valid ones (that
passes under `gfortran -Wall`). This breaks some of the tests, so add
fix for them too. `test_fortran_frontend_loop1()` still has an invalid
test program, but the nearest valid one breaks for a different reason,
and will be fixed later.
  • Loading branch information
pratyai authored Nov 6, 2024
1 parent 72ee732 commit 5669030
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 59 deletions.
3 changes: 0 additions & 3 deletions dace/frontend/fortran/ast_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -987,9 +987,6 @@ def block_nonlabel_do_construct(self, node: FASTNode):
body=ast_internal_classes.Execution_Part_Node(execution=body),
line_number=do.line_number)

def real_literal_constant(self, node: FASTNode):
return node

def subscript_triplet(self, node: FASTNode):
if node.string == ":":
return ast_internal_classes.ParDecl_Node(type="ALL")
Expand Down
27 changes: 25 additions & 2 deletions dace/frontend/fortran/ast_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,31 @@ def intlit2string(self, node: ast_internal_classes.Int_Literal_Node):
return "".join(map(str, node.value))

def floatlit2string(self, node: ast_internal_classes.Real_Literal_Node):

return "".join(map(str, node.value))
# Typecheck and crash early if unexpected.
assert hasattr(node, 'value')
lit = node.value
assert isinstance(lit, str)

# Fortran "real literals" may have an additional suffix at the end.
# Examples:
# valid: 1.0 => 1
# valid: 1. => 1
# valid: 1.e5 => 1e5
# valid: 1.d5 => 1e5
# valid: 1._kinder => 1 (precondition: somewhere earlier, `integer, parameter :: kinder=8`)
# valid: 1.e5_kinder => 1e5
# not valid: 1.d5_kinder => 1e5
# TODO: Is there a complete spec of the structure of real literals?
if '_' in lit:
# First, deal with kind specification and remove it altogether, since we know the type anyway.
parts = lit.split('_')
assert 1 <= len(parts) <= 2, f"{lit} is not a valid fortran literal."
lit = parts[0]
assert 'd' not in lit, f"{lit} is not a valid fortran literal."
if 'd' in lit:
# Again, since we know the type anyway, here we just make the s/d/e/ replacement.
lit = lit.replace('d', 'e')
return f"{float(lit)}"

def boollit2string(self, node: ast_internal_classes.Bool_Literal_Node):

Expand Down
28 changes: 28 additions & 0 deletions tests/fortran/ast_utils_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import pytest

from dace.frontend.fortran.ast_internal_classes import Real_Literal_Node

from dace.frontend.fortran.ast_utils import TaskletWriter


def test_floatlit2string():
def parse(fl: str) -> float:
t = TaskletWriter([], []) # The parameters won't matter.
return t.floatlit2string(Real_Literal_Node(value=fl))

assert parse('1.0') == '1.0'
assert parse('1.') == '1.0'
assert parse('1.e5') == '100000.0'
assert parse('1.d5') == '100000.0'
assert parse('1._kinder') == '1.0'
assert parse('1.e5_kinder') == '100000.0'
with pytest.raises(AssertionError):
parse('1.d5_kinder')
with pytest.raises(AssertionError):
parse('1._kinder_kinder')
with pytest.raises(ValueError, match="could not convert string to float"):
parse('1.2.0')
with pytest.raises(ValueError, match="could not convert string to float"):
parse('1.d0d0')
with pytest.raises(ValueError, match="could not convert string to float"):
parse('foo')
96 changes: 42 additions & 54 deletions tests/fortran/fortran_language_test.py
Original file line number Diff line number Diff line change
@@ -1,46 +1,34 @@
# Copyright 2023 ETH Zurich and the DaCe authors. All rights reserved.

from fparser.common.readfortran import FortranStringReader
from fparser.common.readfortran import FortranFileReader
from fparser.two.parser import ParserFactory
import sys, os
import numpy as np
import pytest


from dace import SDFG, SDFGState, nodes, dtypes, data, subsets, symbolic
from dace.frontend.fortran import fortran_parser
from fparser.two.symbol_table import SymbolTable
from dace.sdfg import utils as sdutil

import dace.frontend.fortran.ast_components as ast_components
import dace.frontend.fortran.ast_transforms as ast_transforms
import dace.frontend.fortran.ast_utils as ast_utils
import dace.frontend.fortran.ast_internal_classes as ast_internal_classes


def test_fortran_frontend_real_kind_selector():
"""
Tests that the size intrinsics are correctly parsed and translated to DaCe.
"""
test_string = """
PROGRAM real_kind_selector_test
implicit none
INTEGER, PARAMETER :: JPRB = SELECTED_REAL_KIND(13,300)
INTEGER, PARAMETER :: JPIM = SELECTED_INT_KIND(9)
REAL(KIND=JPRB) d(4)
CALL real_kind_selector_test_function(d)
end
SUBROUTINE real_kind_selector_test_function(d)
REAL(KIND=JPRB) d(4)
INTEGER(KIND=JPIM) i
i=7
d(2)=5.5+i
END SUBROUTINE real_kind_selector_test_function
"""
program real_kind_selector_test
implicit none
integer, parameter :: JPRB = selected_real_kind(13, 300)
real(KIND=JPRB) d(4)
call real_kind_selector_test_function(d)
end
subroutine real_kind_selector_test_function(d)
implicit none
integer, parameter :: JPRB = selected_real_kind(13, 300)
integer, parameter :: JPIM = selected_int_kind(9)
real(KIND=JPRB) d(4)
integer(KIND=JPIM) i
i = 7
d(2) = 5.5 + i
end subroutine real_kind_selector_test_function
"""
sdfg = fortran_parser.create_sdfg_from_string(test_string, "real_kind_selector_test")
sdfg.simplify(verbose=True)
a = np.full([4], 42, order="F", dtype=np.float64)
Expand Down Expand Up @@ -129,30 +117,30 @@ def test_fortran_frontend_function_statement1():
"""
Tests that the function statement are correctly removed recursively.
"""

test_string = """
PROGRAM function_statement1_test
implicit none
double precision d(3,4,5)
CALL function_statement1_test_function(d)
end

SUBROUTINE function_statement1_test_function(d)
double precision d(3,4,5)
double precision :: PTARE,RTT(2),FOEDELTA,FOELDCP
double precision :: RALVDCP(2),RALSDCP(2),RES
FOEDELTA (PTARE) = MAX (0.0,SIGN(1.0,PTARE-RTT(1)))
FOELDCP ( PTARE ) = FOEDELTA(PTARE)*RALVDCP(1) + (1.0-FOEDELTA(PTARE))*RALSDCP(1)
RTT(1)=4.5
RALVDCP(1)=4.9
RALSDCP(1)=5.1
d(1,1,1)=FOELDCP(3.0)
RES=FOELDCP(3.0)
d(1,1,2)=RES
END SUBROUTINE function_statement1_test_function
"""
test_string = """
program function_statement1_test
implicit none
double precision d(3, 4, 5)
call function_statement1_test_function(d)
end
subroutine function_statement1_test_function(d)
double precision d(3, 4, 5)
double precision :: PTARE, RTT(2), FOEDELTA, FOELDCP
double precision :: RALVDCP(2), RALSDCP(2), RES
FOEDELTA(PTARE) = max(0.0, sign(1.d0, PTARE - RTT(1)))
FOELDCP(PTARE) = FOEDELTA(PTARE)*RALVDCP(1) + (1.0 - FOEDELTA(PTARE))*RALSDCP(1)
RTT(1) = 4.5
RALVDCP(1) = 4.9
RALSDCP(1) = 5.1
d(1, 1, 1) = FOELDCP(3.d0)
RES = FOELDCP(3.d0)
d(1, 1, 2) = RES
end subroutine function_statement1_test_function
"""
sdfg = fortran_parser.create_sdfg_from_string(test_string, "function_statement1_test")
sdfg.simplify(verbose=True)
d = np.full([3, 4, 5], 42, order="F", dtype=np.float64)
Expand Down

0 comments on commit 5669030

Please sign in to comment.