summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
authorDamien Caliste <dcaliste@free.fr>2019-11-29 09:29:21 +0100
committerDamien Caliste <dcaliste@free.fr>2022-01-04 09:16:59 +0100
commit753a146a3e1b91ebe383dac7452bc6f3697fac44 (patch)
treeff6fc96a64487824e8b990211efc2e16bb1a4bdd /numpy
parent20f972ce3693d7700afe51898089613ebe4b3ee5 (diff)
downloadnumpy-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-xnumpy/f2py/crackfortran.py30
-rw-r--r--numpy/f2py/tests/src/crackfortran/operators.f9049
-rw-r--r--numpy/f2py/tests/test_crackfortran.py20
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")]