From c685c909e63f765aac695a5393d4e5de06d84d44 Mon Sep 17 00:00:00 2001 From: Christopher Albert Date: Fri, 26 Dec 2025 17:00:22 +0100 Subject: [PATCH 1/3] Fix parsing of nested kind specifiers --- examples/Makefile | 1 + examples/issue353_selected_kind/Makefile | 14 +++++++++ examples/issue353_selected_kind/run.py | 18 +++++++++++ .../test_selected_kind.f90 | 6 ++++ f90wrap/parser.py | 31 +++++++++++++++---- 5 files changed, 64 insertions(+), 6 deletions(-) create mode 100644 examples/issue353_selected_kind/Makefile create mode 100644 examples/issue353_selected_kind/run.py create mode 100644 examples/issue353_selected_kind/test_selected_kind.f90 diff --git a/examples/Makefile b/examples/Makefile index 3963561a..71c5e874 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -40,6 +40,7 @@ EXAMPLES = \ issue306_allocatable_realloc \ issue307_logical_array \ issue32 \ + issue353_selected_kind \ issue41_abstract_classes \ keep_single_interface \ keyword_renaming_issue160 \ diff --git a/examples/issue353_selected_kind/Makefile b/examples/issue353_selected_kind/Makefile new file mode 100644 index 00000000..c9ddde51 --- /dev/null +++ b/examples/issue353_selected_kind/Makefile @@ -0,0 +1,14 @@ +include ../make.inc + +.PHONY: all test clean + +all: + f90wrap test_selected_kind.f90 -v + +test: all + $(PYTHON) run.py + +clean: + rm -f f90wrap*.f90 .f2py_f2cmap + rm -rf __pycache__/ build/ _build_dir/ + diff --git a/examples/issue353_selected_kind/run.py b/examples/issue353_selected_kind/run.py new file mode 100644 index 00000000..ff711da8 --- /dev/null +++ b/examples/issue353_selected_kind/run.py @@ -0,0 +1,18 @@ +from pathlib import Path + +wrapper = Path("f90wrap_toplevel.f90").read_text(encoding="utf-8") + +expected = [ + "integer(selected_int_kind(9)), intent(out) :: i", + "real(selected_real_kind(13,300)), intent(out) :: a", +] + +missing = [line for line in expected if line not in wrapper] +if missing: + raise SystemExit( + "Missing expected wrapper lines:\n" + + "\n".join(missing) + + "\n\nGenerated wrapper:\n" + + wrapper + ) + diff --git a/examples/issue353_selected_kind/test_selected_kind.f90 b/examples/issue353_selected_kind/test_selected_kind.f90 new file mode 100644 index 00000000..ed4d6dd5 --- /dev/null +++ b/examples/issue353_selected_kind/test_selected_kind.f90 @@ -0,0 +1,6 @@ +subroutine test_selected_kind(i, a) + implicit none + integer(kind=selected_int_kind(9)), intent(out) :: i + real(kind=selected_real_kind(13,300)), intent(out) :: a +end subroutine test_selected_kind + diff --git a/f90wrap/parser.py b/f90wrap/parser.py index e64302e2..97c0c51c 100644 --- a/f90wrap/parser.py +++ b/f90wrap/parser.py @@ -1481,7 +1481,17 @@ def check_decl(cl, file): filename = file.filename lineno = file.lineno - tp = re.match(types_re, cl).group() + tp_match = re.match(types_re, cl) + tp = tp_match.group() + tp_end = tp_match.end() + while tp.count('(') > tp.count(')') and tp_end < len(cl): + while tp_end < len(cl) and cl[tp_end].isspace(): + tp_end += 1 + if tp_end < len(cl) and cl[tp_end] == ')': + tp += ')' + tp_end += 1 + else: + break atr = re.search(attr_re, cl) if atr != None: atrl = s_attrib_re.findall(atr.group()) @@ -1493,7 +1503,7 @@ def check_decl(cl, file): if m is not None: names = cl[m.end():] else: - names = types_re.sub('', cl) + names = cl[tp_end:] # old line - doesn't handle array constants # nl=re.split(r'\s*,\s*',names) @@ -1575,15 +1585,24 @@ def check_arg(cl, file): filename = file.filename lineno = file.lineno - tp = re.match(types_re, cl).group() + tp_match = re.match(types_re, cl) + tp = tp_match.group() + tp_end = tp_match.end() + while tp.count('(') > tp.count(')') and tp_end < len(cl): + while tp_end < len(cl) and cl[tp_end].isspace(): + tp_end += 1 + if tp_end < len(cl) and cl[tp_end] == ')': + tp += ')' + tp_end += 1 + else: + break m = re.search(d_colon, cl) if m is not None: - atr_temp = cl[re.match(types_re, cl).end():m.start()] + atr_temp = cl[tp_end:m.start()] names = cl[m.end():] else: atr_temp = '' - # Need to remove ONLY THE FIRST type string (the name may have the type in it) - names = types_re.sub('', cl, 1) + names = cl[tp_end:] atrl = split_attribs(atr_temp) From 60c0c54d4414c537c5df082635321eaa4bc45774 Mon Sep 17 00:00:00 2001 From: Christopher Albert Date: Fri, 26 Dec 2025 17:01:40 +0100 Subject: [PATCH 2/3] Example: clean generated mod.py --- examples/issue353_selected_kind/Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/issue353_selected_kind/Makefile b/examples/issue353_selected_kind/Makefile index c9ddde51..a6aa39a2 100644 --- a/examples/issue353_selected_kind/Makefile +++ b/examples/issue353_selected_kind/Makefile @@ -9,6 +9,5 @@ test: all $(PYTHON) run.py clean: - rm -f f90wrap*.f90 .f2py_f2cmap + rm -f f90wrap*.f90 .f2py_f2cmap mod.py rm -rf __pycache__/ build/ _build_dir/ - From 293ecd767a806937039be2df4a55982deaa46360 Mon Sep 17 00:00:00 2001 From: Christopher Albert Date: Mon, 5 Jan 2026 11:47:41 +0100 Subject: [PATCH 3/3] Example: add Makefile.meson for issue353 and fix run.py for Direct-C The issue353_selected_kind example was missing Makefile.meson needed for Direct-C mode testing. Also updated run.py to search for any f90wrap_*.f90 file rather than hardcoding f90wrap_toplevel.f90, since Direct-C mode generates differently named wrapper files. --- examples/issue353_selected_kind/Makefile.meson | 7 +++++++ examples/issue353_selected_kind/run.py | 9 ++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 examples/issue353_selected_kind/Makefile.meson diff --git a/examples/issue353_selected_kind/Makefile.meson b/examples/issue353_selected_kind/Makefile.meson new file mode 100644 index 00000000..888dad8b --- /dev/null +++ b/examples/issue353_selected_kind/Makefile.meson @@ -0,0 +1,7 @@ +include ../make.meson.inc + +NAME := test_selected_kind +LIBSRC_WRAP_FPP_FILES := test_selected_kind.f90 + +test: extension + $(PYTHON) run.py diff --git a/examples/issue353_selected_kind/run.py b/examples/issue353_selected_kind/run.py index ff711da8..9133d615 100644 --- a/examples/issue353_selected_kind/run.py +++ b/examples/issue353_selected_kind/run.py @@ -1,6 +1,13 @@ from pathlib import Path +import glob -wrapper = Path("f90wrap_toplevel.f90").read_text(encoding="utf-8") +wrapper_files = glob.glob("f90wrap_*.f90") +if not wrapper_files: + raise SystemExit("No f90wrap_*.f90 files found") + +wrapper = "" +for f in wrapper_files: + wrapper += Path(f).read_text(encoding="utf-8") expected = [ "integer(selected_int_kind(9)), intent(out) :: i",