diff options
author | Damien Caliste <dcaliste@free.fr> | 2019-11-29 09:29:21 +0100 |
---|---|---|
committer | Damien Caliste <dcaliste@free.fr> | 2022-01-04 09:16:59 +0100 |
commit | 753a146a3e1b91ebe383dac7452bc6f3697fac44 (patch) | |
tree | ff6fc96a64487824e8b990211efc2e16bb1a4bdd /numpy | |
parent | 20f972ce3693d7700afe51898089613ebe4b3ee5 (diff) | |
download | numpy-753a146a3e1b91ebe383dac7452bc6f3697fac44.tar.gz |
ENH: add support for operator() in crackfortran.
Some interface name may contains parenthesis when used
with operator, like:
interface operator(==)
module procedure my_type_equals
end interface operator(==)
Make the end part properly detected, and store also
the operator ('==' in that case) in the name.
Also implement support to list the implemented by in
any interface declaration.
Diffstat (limited to 'numpy')
-rwxr-xr-x | numpy/f2py/crackfortran.py | 30 | ||||
-rw-r--r-- | numpy/f2py/tests/src/crackfortran/operators.f90 | 49 | ||||
-rw-r--r-- | numpy/f2py/tests/test_crackfortran.py | 20 |
3 files changed, 95 insertions, 4 deletions
diff --git a/numpy/f2py/crackfortran.py b/numpy/f2py/crackfortran.py index 824d87e4c..10b210d14 100755 --- a/numpy/f2py/crackfortran.py +++ b/numpy/f2py/crackfortran.py @@ -574,11 +574,16 @@ beginpattern90 = re.compile( groupends = (r'end|endprogram|endblockdata|endmodule|endpythonmodule|' r'endinterface|endsubroutine|endfunction') endpattern = re.compile( - beforethisafter % ('', groupends, groupends, r'[\w\s]*'), re.I), 'end' -endifs = r'(end\s*(if|do|where|select|while|forall|associate|block|critical|enum|team))|(module\s*procedure)' + beforethisafter % ('', groupends, groupends, r'.*'), re.I), 'end' +endifs = r'end\s*(if|do|where|select|while|forall|associate|block|' + \ + r'critical|enum|team)' endifpattern = re.compile( beforethisafter % (r'[\w]*?', endifs, endifs, r'[\w\s]*'), re.I), 'endif' # +moduleprocedures = r'module\s*procedure' +moduleprocedurepattern = re.compile( + beforethisafter % ('', moduleprocedures, moduleprocedures, r'.*'), re.I), \ + 'moduleprocedure' implicitpattern = re.compile( beforethisafter % ('', 'implicit', 'implicit', '.*'), re.I), 'implicit' dimensionpattern = re.compile(beforethisafter % ( @@ -727,7 +732,8 @@ def crackline(line, reset=0): callpattern, usepattern, containspattern, entrypattern, f2pyenhancementspattern, - multilinepattern + multilinepattern, + moduleprocedurepattern ]: m = pat[0].match(line) if m: @@ -797,6 +803,8 @@ def crackline(line, reset=0): expectbegin = 0 elif pat[1] == 'endif': pass + elif pat[1] == 'moduleprocedure': + analyzeline(m, pat[1], line) elif pat[1] == 'contains': if ignorecontains: return @@ -886,6 +894,9 @@ selectpattern = re.compile( r'\s*(?P<this>(@\(@.*?@\)@|\*[\d*]+|\*\s*@\(@.*?@\)@|))(?P<after>.*)\Z', re.I) nameargspattern = re.compile( r'\s*(?P<name>\b[\w$]+\b)\s*(@\(@\s*(?P<args>[\w\s,]*)\s*@\)@|)\s*((result(\s*@\(@\s*(?P<result>\b[\w$]+\b)\s*@\)@|))|(bind\s*@\(@\s*(?P<bind>.*)\s*@\)@))*\s*\Z', re.I) +operatorpattern = re.compile( + r'\s*(?P<scheme>(operator|assignment))' + r'@\(@\s*(?P<name>[^)]+)\s*@\)@\s*\Z', re.I) callnameargspattern = re.compile( r'\s*(?P<name>\b[\w$]+\b)\s*@\(@\s*(?P<args>.*)\s*@\)@\s*\Z', re.I) real16pattern = re.compile( @@ -908,6 +919,10 @@ def _resolvenameargspattern(line): m1 = nameargspattern.match(line) if m1: return m1.group('name'), m1.group('args'), m1.group('result'), m1.group('bind') + m1 = operatorpattern.match(line) + if m1: + name = m1.group('scheme') + '(' + m1.group('name') + ')' + return name, [], None, None m1 = callnameargspattern.match(line) if m1: return m1.group('name'), m1.group('args'), None, None @@ -1151,6 +1166,9 @@ def analyzeline(m, case, line): continue else: k = rmbadname1(m1.group('name')) + if case in ['public', 'private'] and \ + (k == 'operator' or k == 'assignment'): + k += m1.group('after') if k not in edecl: edecl[k] = {} if case == 'dimension': @@ -1193,6 +1211,9 @@ def analyzeline(m, case, line): groupcache[groupcounter]['vars'] = edecl if last_name is not None: previous_context = ('variable', last_name, groupcounter) + elif case == 'moduleprocedure': + groupcache[groupcounter]['implementedby'] = \ + [x.strip() for x in m.group('after').split(',')] elif case == 'parameter': edecl = groupcache[groupcounter]['vars'] ll = m.group('after').strip()[1:-1] @@ -2105,7 +2126,8 @@ def analyzebody(block, args, tab=''): else: as_ = args b = postcrack(b, as_, tab=tab + '\t') - if b['block'] in ['interface', 'abstract interface'] and not b['body']: + if b['block'] in ['interface', 'abstract interface'] and \ + not b['body'] and not b['implementedby']: if 'f2pyenhancements' not in b: continue if b['block'].replace(' ', '') == 'pythonmodule': diff --git a/numpy/f2py/tests/src/crackfortran/operators.f90 b/numpy/f2py/tests/src/crackfortran/operators.f90 new file mode 100644 index 000000000..1d060a3d2 --- /dev/null +++ b/numpy/f2py/tests/src/crackfortran/operators.f90 @@ -0,0 +1,49 @@ +module foo + type bar + character(len = 32) :: item + end type bar + interface operator(.item.) + module procedure item_int, item_real + end interface operator(.item.) + interface operator(==) + module procedure items_are_equal + end interface operator(==) + interface assignment(=) + module procedure get_int, get_real + end interface assignment(=) +contains + function item_int(val) result(elem) + integer, intent(in) :: val + type(bar) :: elem + + write(elem%item, "(I32)") val + end function item_int + + function item_real(val) result(elem) + real, intent(in) :: val + type(bar) :: elem + + write(elem%item, "(1PE32.12)") val + end function item_real + + function items_are_equal(val1, val2) result(equal) + type(bar), intent(in) :: val1, val2 + logical :: equal + + equal = (val1%item == val2%item) + end function items_are_equal + + subroutine get_real(rval, item) + real, intent(out) :: rval + type(bar), intent(in) :: item + + read(item%item, *) rval + end subroutine get_real + + subroutine get_int(rval, item) + integer, intent(out) :: rval + type(bar), intent(in) :: item + + read(item%item, *) rval + end subroutine get_int +end module foo diff --git a/numpy/f2py/tests/test_crackfortran.py b/numpy/f2py/tests/test_crackfortran.py index fb47eb31d..e33e12d62 100644 --- a/numpy/f2py/tests/test_crackfortran.py +++ b/numpy/f2py/tests/test_crackfortran.py @@ -45,6 +45,26 @@ class TestPublicPrivate: assert "public" in mod["vars"]["seta"]["attrspec"] +class TestModuleProcedure(): + def test_moduleOperators(self, tmp_path): + fpath = util.getpath("tests", "src", "crackfortran", "operators.f90") + mod = crackfortran.crackfortran([str(fpath)]) + assert len(mod) == 1 + mod = mod[0] + assert "body" in mod and len(mod["body"]) == 9 + assert mod["body"][1]["name"] == "operator(.item.)" + assert "implementedby" in mod["body"][1] + assert mod["body"][1]["implementedby"] == \ + ["item_int", "item_real"] + assert mod["body"][2]["name"] == "operator(==)" + assert "implementedby" in mod["body"][2] + assert mod["body"][2]["implementedby"] == ["items_are_equal"] + assert mod["body"][3]["name"] == "assignment(=)" + assert "implementedby" in mod["body"][3] + assert mod["body"][3]["implementedby"] == \ + ["get_int", "get_real"] + + class TestExternal(util.F2PyTest): # issue gh-17859: add external attribute support sources = [util.getpath("tests", "src", "crackfortran", "gh17859.f")] |