diff --git a/numpy/f2py/crackfortran.py b/numpy/f2py/crackfortran.py index cc041ec56e6e..d39acb039073 100755 --- a/numpy/f2py/crackfortran.py +++ b/numpy/f2py/crackfortran.py @@ -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() diff --git a/numpy/f2py/tests/src/crackfortran/data_common.f b/numpy/f2py/tests/src/crackfortran/data_common.f new file mode 100644 index 000000000000..5ffd865c8379 --- /dev/null +++ b/numpy/f2py/tests/src/crackfortran/data_common.f @@ -0,0 +1,8 @@ + BLOCK DATA PARAM_INI + COMMON /MYCOM/ MYDATA + DATA MYDATA /0/ + END + SUBROUTINE SUB1 + COMMON /MYCOM/ MYDATA + MYDATA = MYDATA + 1 + END diff --git a/numpy/f2py/tests/src/crackfortran/data_multiplier.f b/numpy/f2py/tests/src/crackfortran/data_multiplier.f new file mode 100644 index 000000000000..19ff8a83e97b --- /dev/null +++ b/numpy/f2py/tests/src/crackfortran/data_multiplier.f @@ -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 diff --git a/numpy/f2py/tests/src/crackfortran/data_stmts.f90 b/numpy/f2py/tests/src/crackfortran/data_stmts.f90 index 0eb97a563899..576c5e485baf 100644 --- a/numpy/f2py/tests/src/crackfortran/data_stmts.f90 +++ b/numpy/f2py/tests/src/crackfortran/data_stmts.f90 @@ -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 @@ -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 diff --git a/numpy/f2py/tests/src/crackfortran/data_with_comments.f b/numpy/f2py/tests/src/crackfortran/data_with_comments.f new file mode 100644 index 000000000000..4128f004e840 --- /dev/null +++ b/numpy/f2py/tests/src/crackfortran/data_with_comments.f @@ -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 diff --git a/numpy/f2py/tests/test_data.py b/numpy/f2py/tests/test_data.py index 3b8ca544ec74..4e5604c006b1 100644 --- a/numpy/f2py/tests/test_data.py +++ b/numpy/f2py/tests/test_data.py @@ -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])) @@ -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