Skip to content

Commit

Permalink
Merge pull request #24840 from charris/backport-24778
Browse files Browse the repository at this point in the history
BUG: Fix DATA statements for f2py
  • Loading branch information
charris committed Oct 1, 2023
2 parents b55e1ef + b7bf593 commit f75cf1d
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 35 deletions.
78 changes: 43 additions & 35 deletions numpy/f2py/crackfortran.py
Original file line number Diff line number Diff line change
Expand Up @@ -1425,53 +1425,61 @@ def analyzeline(m, case, line):
if dl.startswith(','):
dl = dl[1:].strip()
ll.append([dl, il])
vars = {}
if 'vars' in groupcache[groupcounter]:
vars = groupcache[groupcounter]['vars']
vars = groupcache[groupcounter].get('vars', {})
last_name = None
for l in ll:
l = [x.strip() for x in l]
if l[0][0] == ',':
l[0], l[1] = l[0].strip(), l[1].strip()
if l[0].startswith(','):
l[0] = l[0][1:]
if l[0][0] == '(':
outmess(
'analyzeline: implied-DO list "%s" is not supported. Skipping.\n' % l[0])
if l[0].startswith('('):
outmess('analyzeline: implied-DO list "%s" is not supported. Skipping.\n' % l[0])
continue
llen = len(l[1])
for idx, v in enumerate(rmbadname(
[x.strip() for x in markoutercomma(l[0]).split('@,@')])
):
if v[0] == '(':
outmess(
'analyzeline: implied-DO list "%s" is not supported. Skipping.\n' % v)
for idx, v in enumerate(rmbadname([x.strip() for x in markoutercomma(l[0]).split('@,@')])):
if v.startswith('('):
outmess('analyzeline: implied-DO list "%s" is not supported. Skipping.\n' % v)
# XXX: subsequent init expressions may get wrong values.
# Ignoring since data statements are irrelevant for
# wrapping.
continue
fc = 0
if '!' in l[1]:
# Fixes gh-24746 pyf generation
# XXX: This essentially ignores the value for generating the pyf which is fine:
# integer dimension(3) :: mytab
# common /mycom/ mytab
# Since in any case it is initialized in the Fortran code
outmess('Comment line in declaration "%s" is not supported. Skipping.\n' % l[1])
continue
vars.setdefault(v, {})
vtype = vars[v].get('typespec')
vdim = getdimension(vars[v])

if (vtype == 'complex'):
cmplxpat = r"\(.*?\)"
matches = re.findall(cmplxpat, l[1])
else:
matches = l[1].split(',')

if v not in vars:
vars[v] = {}
if '=' in vars[v] and not vars[v]['='] == matches[idx]:
outmess('analyzeline: changing init expression of "%s" ("%s") to "%s"\n' % (
v, vars[v]['='], matches[idx]))

if vdim is not None:
# Need to assign multiple values to one variable
vars[v]['='] = "(/{}/)".format(", ".join(matches))
else:
vars[v]['='] = matches[idx]
matches = re.findall(r"\(.*?\)", l[1]) if vtype == 'complex' else l[1].split(',')
try:
new_val = "(/{}/)".format(", ".join(matches)) if vdim else matches[idx]
except IndexError:
# gh-24746
# Runs only if above code fails. Fixes the line
# DATA IVAR1, IVAR2, IVAR3, IVAR4, EVAR5 /4*0,0.0D0/
# by expanding to ['0', '0', '0', '0', '0.0d0']
if any("*" in m for m in matches):
expanded_list = []
for match in matches:
if "*" in match:
try:
multiplier, value = match.split("*")
expanded_list.extend([value.strip()] * int(multiplier))
except ValueError: # if int(multiplier) fails
expanded_list.append(match.strip())
else:
expanded_list.append(match.strip())
matches = expanded_list
new_val = "(/{}/)".format(", ".join(matches)) if vdim else matches[idx]
current_val = vars[v].get('=')
if current_val and (current_val != new_val):
outmess('analyzeline: changing init expression of "%s" ("%s") to "%s"\n' % (v, current_val, new_val))
vars[v]['='] = new_val
last_name = v
groupcache[groupcounter]['vars'] = vars
if last_name is not None:
if last_name:
previous_context = ('variable', last_name, groupcounter)
elif case == 'common':
line = m.group('after').strip()
Expand Down
8 changes: 8 additions & 0 deletions numpy/f2py/tests/src/crackfortran/data_common.f
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
BLOCK DATA PARAM_INI
COMMON /MYCOM/ MYDATA
DATA MYDATA /0/
END
SUBROUTINE SUB1
COMMON /MYCOM/ MYDATA
MYDATA = MYDATA + 1
END
5 changes: 5 additions & 0 deletions numpy/f2py/tests/src/crackfortran/data_multiplier.f
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
BLOCK DATA MYBLK
IMPLICIT DOUBLE PRECISION (A-H,O-Z)
COMMON /MYCOM/ IVAR1, IVAR2, IVAR3, IVAR4, EVAR5
DATA IVAR1, IVAR2, IVAR3, IVAR4, EVAR5 /2*3,2*2,0.0D0/
END
2 changes: 2 additions & 0 deletions numpy/f2py/tests/src/crackfortran/data_stmts.f90
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module cmplxdat
integer :: i, j
real :: x, y
real, dimension(2) :: z
real(kind=8) :: pi
complex(kind=8), target :: medium_ref_index
complex(kind=8), target :: ref_index_one, ref_index_two
complex(kind=8), dimension(2) :: my_array
Expand All @@ -15,4 +16,5 @@ module cmplxdat
data medium_ref_index / (1.d0, 0.d0) /
data ref_index_one, ref_index_two / (13.0d0, 21.0d0), (-30.0d0, 43.0d0) /
data my_array / (1.0d0, 2.0d0), (-3.0d0, 4.0d0) /
data pi / 3.1415926535897932384626433832795028841971693993751058209749445923078164062d0 /
end module cmplxdat
8 changes: 8 additions & 0 deletions numpy/f2py/tests/src/crackfortran/data_with_comments.f
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
BLOCK DATA PARAM_INI
COMMON /MYCOM/ MYTAB
INTEGER MYTAB(3)
DATA MYTAB/
* 0, ! 1 and more commenty stuff
* 4, ! 2
* 0 /
END
37 changes: 37 additions & 0 deletions numpy/f2py/tests/test_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ def test_data_stmts(self):
assert self.module.cmplxdat.j == 3
assert self.module.cmplxdat.x == 1.5
assert self.module.cmplxdat.y == 2.0
assert self.module.cmplxdat.pi == 3.1415926535897932384626433832795028841971693993751058209749445923078164062
assert self.module.cmplxdat.medium_ref_index == np.array(1.+0.j)
assert np.all(self.module.cmplxdat.z == np.array([3.5, 7.0]))
assert np.all(self.module.cmplxdat.my_array == np.array([ 1.+2.j, -3.+4.j]))
Expand All @@ -26,8 +27,44 @@ def test_crackedlines(self):
mod = crackfortran(self.sources)
assert mod[0]['vars']['x']['='] == '1.5'
assert mod[0]['vars']['y']['='] == '2.0'
assert mod[0]['vars']['pi']['='] == '3.1415926535897932384626433832795028841971693993751058209749445923078164062d0'
assert mod[0]['vars']['my_real_array']['='] == '(/1.0d0, 2.0d0, 3.0d0/)'
assert mod[0]['vars']['ref_index_one']['='] == '(13.0d0, 21.0d0)'
assert mod[0]['vars']['ref_index_two']['='] == '(-30.0d0, 43.0d0)'
assert mod[0]['vars']['my_array']['='] == '(/(1.0d0, 2.0d0), (-3.0d0, 4.0d0)/)'
assert mod[0]['vars']['z']['='] == '(/3.5, 7.0/)'

class TestDataF77(util.F2PyTest):
sources = [util.getpath("tests", "src", "crackfortran", "data_common.f")]

# For gh-23276
def test_data_stmts(self):
assert self.module.mycom.mydata == 0

def test_crackedlines(self):
mod = crackfortran(str(self.sources[0]))
print(mod[0]['vars'])
assert mod[0]['vars']['mydata']['='] == '0'


class TestDataMultiplierF77(util.F2PyTest):
sources = [util.getpath("tests", "src", "crackfortran", "data_multiplier.f")]

# For gh-23276
def test_data_stmts(self):
assert self.module.mycom.ivar1 == 3
assert self.module.mycom.ivar2 == 3
assert self.module.mycom.ivar3 == 2
assert self.module.mycom.ivar4 == 2
assert self.module.mycom.evar5 == 0


class TestDataWithCommentsF77(util.F2PyTest):
sources = [util.getpath("tests", "src", "crackfortran", "data_with_comments.f")]

# For gh-23276
def test_data_stmts(self):
assert len(self.module.mycom.mytab) == 3
assert self.module.mycom.mytab[0] == 0
assert self.module.mycom.mytab[1] == 4
assert self.module.mycom.mytab[2] == 0

0 comments on commit f75cf1d

Please sign in to comment.