From bae98ef8e5c7e8e7131238396932218742075bf3 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Fri, 19 Jun 2026 11:09:49 +0100 Subject: [PATCH 01/12] Implementation of the Fortran2008 intrinsics --- doc/source/developers_guide.rst | 8 ++ src/fparser/two/Fortran2003.py | 13 ++- src/fparser/two/Fortran2008/__init__.py | 1 + src/fparser/two/Fortran2008/f08_intrinsics.py | 96 +++++++++++++++++++ .../tests/fortran2008/test_f08_intrinsics.py | 74 ++++++++++++++ 5 files changed, 185 insertions(+), 7 deletions(-) create mode 100644 src/fparser/two/Fortran2008/f08_intrinsics.py create mode 100644 src/fparser/two/tests/fortran2008/test_f08_intrinsics.py diff --git a/doc/source/developers_guide.rst b/doc/source/developers_guide.rst index 63d7f558..d71b02a2 100644 --- a/doc/source/developers_guide.rst +++ b/doc/source/developers_guide.rst @@ -170,6 +170,14 @@ returned. An example of a simple choice rule is `R202`. See the :ref:`program-unit-class` section for a description of its implementation. +Another example is the support for Fortran 2008 intrinsics. +These are defined in the `Fortran2008_Intrinsic_Names` class, which is then +defined in the `subclass_names` list of the base `Intrinsic_Names` class in +the Fortran2003 spec. When fparser runs with the 2008 standard, it will attempt +to match both the Fortran2003 `Intrinsic_Names` and the +`Fortran2008_Intrinsic_Names` classes, but with only the 2003 standard it will +only use the base Fortran2003 intrinsic lists. + The `use_names` list should contain any classes that are referenced by the implementation of the current class. These lists of names are aggregated (along with `subclass_names`) and used to ensure that all necessary `Scalar_`, diff --git a/src/fparser/two/Fortran2003.py b/src/fparser/two/Fortran2003.py index 177b40e4..0141b1ae 100644 --- a/src/fparser/two/Fortran2003.py +++ b/src/fparser/two/Fortran2003.py @@ -12457,7 +12457,7 @@ class Intrinsic_Name(STRINGBase): # No explicit rule specific_function_names.keys() ) - subclass_names = [] + subclass_names = ["Fortran2008_Intrinsic_Names"] @staticmethod def match(string): @@ -12509,9 +12509,9 @@ def match(string): result = CallBase.match(Intrinsic_Name, Actual_Arg_Spec_List, string) if not result: return None - # There is a match so check the number of args provided # matches the number of args expected by the intrinsic. + intrinsic_type = type(result[0]) function_name = str(result[0]) function_args = result[1] @@ -12528,16 +12528,15 @@ def match(string): pass nargs = 0 if function_args is None else len(function_args.items) - - if function_name in Intrinsic_Name.specific_function_names.keys(): + if function_name in intrinsic_type.specific_function_names.keys(): # If this is a specific function then use its generic # name to test min and max number of arguments. - test_name = Intrinsic_Name.specific_function_names[function_name] + test_name = intrinsic_type.specific_function_names[function_name] else: test_name = function_name - min_nargs = Intrinsic_Name.generic_function_names[test_name]["min"] - max_nargs = Intrinsic_Name.generic_function_names[test_name]["max"] + min_nargs = intrinsic_type.generic_function_names[test_name]["min"] + max_nargs = intrinsic_type.generic_function_names[test_name]["max"] # None indicates an unlimited number of arguments if max_nargs is None: diff --git a/src/fparser/two/Fortran2008/__init__.py b/src/fparser/two/Fortran2008/__init__.py index 832a46d2..724f67cf 100644 --- a/src/fparser/two/Fortran2008/__init__.py +++ b/src/fparser/two/Fortran2008/__init__.py @@ -101,6 +101,7 @@ ) from fparser.two.Fortran2008.label_do_stmt_r816 import Label_Do_Stmt from fparser.two.Fortran2008.nonlabel_do_stmt_r817 import Nonlabel_Do_Stmt +from fparser.two.Fortran2008.f08_intrinsics import Fortran2008_Intrinsic_Names # pylint: disable=eval-used # pylint: disable=exec-used diff --git a/src/fparser/two/Fortran2008/f08_intrinsics.py b/src/fparser/two/Fortran2008/f08_intrinsics.py new file mode 100644 index 00000000..5dae70c0 --- /dev/null +++ b/src/fparser/two/Fortran2008/f08_intrinsics.py @@ -0,0 +1,96 @@ +# ----------------------------------------------------------------------------- +# BSD 3-Clause License +# +# Copyright (c) 2026, Science and Technology Facilities Council. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ----------------------------------------------------------------------------- + +""" +Module containing Fortran2008 Intrinsics. +""" + +from fparser.two.Fortran2003 import Intrinsic_Name + +class Fortran2008_Intrinsic_Names(Intrinsic_Name): + """ + Represents the name of a Fortran 2008 intrinsic function. + + All generic intrinsic names are specified as keys in the + `generic_function_names` dictionary, with their values indicating + the minimum and maximum number of arguments allowed for this + intrinsic function. A `-1` indicates an unlimited number of + arguments. The names are split into the categories specified in + the Fortran2003 specification document. + + All specific intrinsic names (which have a different name to their + generic counterpart) are specified as keys in the + `specific_function_names` dictionary, with their values indicating + which generic function they are associated with + """ + + f08_math_intrinsics = { + "ERF": {"min": 1, "max": 1}, + "GAMMA": {"min": 1, "max": 1}, + } + + f08_bitshift_intrinsics = { + "SHIFTL": {"min": 2, "max": 2}, + "SHIFTR": {"min": 2, "max": 2}, + "SHIFTA": {"min": 2, "max": 2}, + } + + generic_function_names = {} + generic_function_names.update(f08_math_intrinsics) + generic_function_names.update(f08_bitshift_intrinsics) + + specific_function_names = {} + + # A list of all function names + function_names = list(generic_function_names.keys()) + list( + specific_function_names.keys() + ) + + @staticmethod + def match(string): + """Attempt to match the input `string` with the intrinsic function + names defined in `generic_function_names` or + `specific_function_names`. If there is a match the resultant + string will be converted to upper case. + + :param str string: The pattern to be matched. + + :returns: A tuple containing the matched string (converted to \ + upper case) if there is a match or None if there is not. + :rtype: (str,) or NoneType + + """ + from fparser.two.utils import STRINGBase + return STRINGBase.match(Fortran2008_Intrinsic_Names.function_names, + string) diff --git a/src/fparser/two/tests/fortran2008/test_f08_intrinsics.py b/src/fparser/two/tests/fortran2008/test_f08_intrinsics.py new file mode 100644 index 00000000..e439a17a --- /dev/null +++ b/src/fparser/two/tests/fortran2008/test_f08_intrinsics.py @@ -0,0 +1,74 @@ +# ----------------------------------------------------------------------------- +# BSD 3-Clause License +# +# Copyright (c) 2023-2024, Science and Technology Facilities Council. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ----------------------------------------------------------------------------- + +'''Test the Fortran 2008 intrinsic support.''' + +from fparser.common.readfortran import FortranStringReader +from fparser.two.Fortran2003 import Part_Ref +from fparser.two.Fortran2008 import Fortran2008_Intrinsic_Names +from fparser.two.utils import walk + + +def test_f2008_intrinsic(f2008_parser): + """Test Fortran2008 intrinsic is created with the f2008 parser.""" + + reader = FortranStringReader( + """subroutine test + integer :: i + + i = erf(i) + end subroutine test + """ + ) + tree = f2008_parser(reader) + intrinsic = walk(tree, Fortran2008_Intrinsic_Names) + assert len(intrinsic) == 1 + assert str(intrinsic[0]) == "ERF" + +def test_f2008_intrinsic_f2003_parse(f2003_parser): + """Test Fortran2008 intrinsic is not created with the f2003 parser.""" + reader = FortranStringReader( + """subroutine test + integer :: i + + i = erf(i) + end subroutine test + """ + ) + tree = f2003_parser(reader) + intrinsic = walk(tree, Fortran2008_Intrinsic_Names) + assert len(intrinsic) == 0 + partref = walk(tree, Part_Ref) + assert len(partref) == 1 + assert str(partref[0]) == "erf(i)" From fd6da0db0481f2813728cdf7541b62ed84211d89 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Fri, 19 Jun 2026 11:32:37 +0100 Subject: [PATCH 02/12] Formatting --- src/fparser/two/Fortran2008/f08_intrinsics.py | 13 +++++++------ .../tests/fortran2008/test_f08_intrinsics.py | 17 +++++++---------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/fparser/two/Fortran2008/f08_intrinsics.py b/src/fparser/two/Fortran2008/f08_intrinsics.py index 5dae70c0..3b6b4d99 100644 --- a/src/fparser/two/Fortran2008/f08_intrinsics.py +++ b/src/fparser/two/Fortran2008/f08_intrinsics.py @@ -38,6 +38,7 @@ from fparser.two.Fortran2003 import Intrinsic_Name + class Fortran2008_Intrinsic_Names(Intrinsic_Name): """ Represents the name of a Fortran 2008 intrinsic function. @@ -59,11 +60,11 @@ class Fortran2008_Intrinsic_Names(Intrinsic_Name): "ERF": {"min": 1, "max": 1}, "GAMMA": {"min": 1, "max": 1}, } - + f08_bitshift_intrinsics = { - "SHIFTL": {"min": 2, "max": 2}, - "SHIFTR": {"min": 2, "max": 2}, - "SHIFTA": {"min": 2, "max": 2}, + "SHIFTL": {"min": 2, "max": 2}, + "SHIFTR": {"min": 2, "max": 2}, + "SHIFTA": {"min": 2, "max": 2}, } generic_function_names = {} @@ -92,5 +93,5 @@ def match(string): """ from fparser.two.utils import STRINGBase - return STRINGBase.match(Fortran2008_Intrinsic_Names.function_names, - string) + + return STRINGBase.match(Fortran2008_Intrinsic_Names.function_names, string) diff --git a/src/fparser/two/tests/fortran2008/test_f08_intrinsics.py b/src/fparser/two/tests/fortran2008/test_f08_intrinsics.py index e439a17a..56a8843c 100644 --- a/src/fparser/two/tests/fortran2008/test_f08_intrinsics.py +++ b/src/fparser/two/tests/fortran2008/test_f08_intrinsics.py @@ -32,40 +32,37 @@ # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- -'''Test the Fortran 2008 intrinsic support.''' +"""Test the Fortran 2008 intrinsic support.""" from fparser.common.readfortran import FortranStringReader from fparser.two.Fortran2003 import Part_Ref from fparser.two.Fortran2008 import Fortran2008_Intrinsic_Names -from fparser.two.utils import walk +from fparser.two.utils import walk def test_f2008_intrinsic(f2008_parser): """Test Fortran2008 intrinsic is created with the f2008 parser.""" - reader = FortranStringReader( - """subroutine test + reader = FortranStringReader("""subroutine test integer :: i i = erf(i) end subroutine test - """ - ) + """) tree = f2008_parser(reader) intrinsic = walk(tree, Fortran2008_Intrinsic_Names) assert len(intrinsic) == 1 assert str(intrinsic[0]) == "ERF" + def test_f2008_intrinsic_f2003_parse(f2003_parser): """Test Fortran2008 intrinsic is not created with the f2003 parser.""" - reader = FortranStringReader( - """subroutine test + reader = FortranStringReader("""subroutine test integer :: i i = erf(i) end subroutine test - """ - ) + """) tree = f2003_parser(reader) intrinsic = walk(tree, Fortran2008_Intrinsic_Names) assert len(intrinsic) == 0 From 47f679616fee022040d7236b31e3b048f59ece4e Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Wed, 24 Jun 2026 15:46:31 +0100 Subject: [PATCH 03/12] Updates for review and F2008 structure --- src/fparser/two/Fortran2003.py | 18 ++++++---- src/fparser/two/Fortran2008/__init__.py | 4 ++- .../{f08_intrinsics.py => intrinsics_f08.py} | 34 ++++++++++--------- .../tests/fortran2008/test_f08_intrinsics.py | 7 ++-- 4 files changed, 36 insertions(+), 27 deletions(-) rename src/fparser/two/Fortran2008/{f08_intrinsics.py => intrinsics_f08.py} (79%) diff --git a/src/fparser/two/Fortran2003.py b/src/fparser/two/Fortran2003.py index 0141b1ae..f7496df3 100644 --- a/src/fparser/two/Fortran2003.py +++ b/src/fparser/two/Fortran2003.py @@ -12457,10 +12457,10 @@ class Intrinsic_Name(STRINGBase): # No explicit rule specific_function_names.keys() ) - subclass_names = ["Fortran2008_Intrinsic_Names"] + subclass_names = [] - @staticmethod - def match(string): + @classmethod + def match(cls, string): """Attempt to match the input `string` with the intrinsic function names defined in `generic_function_names` or `specific_function_names`. If there is a match the resultant @@ -12473,7 +12473,7 @@ def match(string): :rtype: (str,) or NoneType """ - return STRINGBase.match(Intrinsic_Name.function_names, string) + return STRINGBase.match(cls.function_names, string) class Intrinsic_Function_Reference(CallBase): # No explicit rule @@ -12486,9 +12486,12 @@ class Intrinsic_Function_Reference(CallBase): # No explicit rule subclass_names = [] use_names = ["Intrinsic_Name", "Actual_Arg_Spec_List"] + # Set the type of Intrinsic_Name to be used (so it can be overridden + # in subclasses). + _intrinsic_type = Intrinsic_Name - @staticmethod - def match(string): + @classmethod + def match(cls, string): """Match the string as an intrinsic function. Also check that the number of arguments provided matches the number expected by the intrinsic. @@ -12506,7 +12509,8 @@ def match(string): (that overrides it) into scope. """ - result = CallBase.match(Intrinsic_Name, Actual_Arg_Spec_List, string) + result = CallBase.match(cls._intrinsic_type, + Actual_Arg_Spec_List, string) if not result: return None # There is a match so check the number of args provided diff --git a/src/fparser/two/Fortran2008/__init__.py b/src/fparser/two/Fortran2008/__init__.py index 724f67cf..64ea322d 100644 --- a/src/fparser/two/Fortran2008/__init__.py +++ b/src/fparser/two/Fortran2008/__init__.py @@ -101,7 +101,9 @@ ) from fparser.two.Fortran2008.label_do_stmt_r816 import Label_Do_Stmt from fparser.two.Fortran2008.nonlabel_do_stmt_r817 import Nonlabel_Do_Stmt -from fparser.two.Fortran2008.f08_intrinsics import Fortran2008_Intrinsic_Names +from fparser.two.Fortran2008.intrinsics_f08 import ( + Intrinsic_Name, Intrinsic_Function_Reference +) # pylint: disable=eval-used # pylint: disable=exec-used diff --git a/src/fparser/two/Fortran2008/f08_intrinsics.py b/src/fparser/two/Fortran2008/intrinsics_f08.py similarity index 79% rename from src/fparser/two/Fortran2008/f08_intrinsics.py rename to src/fparser/two/Fortran2008/intrinsics_f08.py index 3b6b4d99..1ff1ccc2 100644 --- a/src/fparser/two/Fortran2008/f08_intrinsics.py +++ b/src/fparser/two/Fortran2008/intrinsics_f08.py @@ -36,10 +36,16 @@ Module containing Fortran2008 Intrinsics. """ -from fparser.two.Fortran2003 import Intrinsic_Name +from typing import Union +from fparser.two.Fortran2003 import Intrinsic_Name as F2003_Intrinsic_Name +from fparser.two.Fortran2003 import ( + Intrinsic_Function_Reference as F2003_Intrinsic_Function_Reference, +) +from fparser.two.utils import STRINGBase -class Fortran2008_Intrinsic_Names(Intrinsic_Name): + +class Intrinsic_Name(F2003_Intrinsic_Name): """ Represents the name of a Fortran 2008 intrinsic function. @@ -67,31 +73,27 @@ class Fortran2008_Intrinsic_Names(Intrinsic_Name): "SHIFTA": {"min": 2, "max": 2}, } + # Create the dicts (not inherited from F2003_Intrinsic_Name) generic_function_names = {} + generic_function_names.update(F2003_Intrinsic_Name.generic_function_names) generic_function_names.update(f08_math_intrinsics) generic_function_names.update(f08_bitshift_intrinsics) - specific_function_names = {} + specific_function_names = F2003_Intrinsic_Name.specific_function_names # A list of all function names function_names = list(generic_function_names.keys()) + list( specific_function_names.keys() ) - @staticmethod - def match(string): - """Attempt to match the input `string` with the intrinsic function - names defined in `generic_function_names` or - `specific_function_names`. If there is a match the resultant - string will be converted to upper case. - :param str string: The pattern to be matched. +class Intrinsic_Function_Reference(F2003_Intrinsic_Function_Reference): + """ + Represents Fortran intrinsics:: - :returns: A tuple containing the matched string (converted to \ - upper case) if there is a match or None if there is not. - :rtype: (str,) or NoneType + function-reference is intrinsic-name ( [ actual-arg-spec-list ] ) - """ - from fparser.two.utils import STRINGBase + """ - return STRINGBase.match(Fortran2008_Intrinsic_Names.function_names, string) + # Set the type of Intrinsic_Name to be used + _intrinsic_type = Intrinsic_Name diff --git a/src/fparser/two/tests/fortran2008/test_f08_intrinsics.py b/src/fparser/two/tests/fortran2008/test_f08_intrinsics.py index 56a8843c..7aa894dd 100644 --- a/src/fparser/two/tests/fortran2008/test_f08_intrinsics.py +++ b/src/fparser/two/tests/fortran2008/test_f08_intrinsics.py @@ -36,7 +36,7 @@ from fparser.common.readfortran import FortranStringReader from fparser.two.Fortran2003 import Part_Ref -from fparser.two.Fortran2008 import Fortran2008_Intrinsic_Names +from fparser.two.Fortran2008 import Intrinsic_Name from fparser.two.utils import walk @@ -50,7 +50,8 @@ def test_f2008_intrinsic(f2008_parser): end subroutine test """) tree = f2008_parser(reader) - intrinsic = walk(tree, Fortran2008_Intrinsic_Names) + intrinsic = walk(tree, Intrinsic_Name) + print(tree.__repr__) assert len(intrinsic) == 1 assert str(intrinsic[0]) == "ERF" @@ -64,7 +65,7 @@ def test_f2008_intrinsic_f2003_parse(f2003_parser): end subroutine test """) tree = f2003_parser(reader) - intrinsic = walk(tree, Fortran2008_Intrinsic_Names) + intrinsic = walk(tree, Intrinsic_Name) assert len(intrinsic) == 0 partref = walk(tree, Part_Ref) assert len(partref) == 1 From 85dacbd632f6bc6d1fbc29f27ce267173dfb0c56 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Wed, 24 Jun 2026 15:47:46 +0100 Subject: [PATCH 04/12] fix formatting --- src/fparser/two/Fortran2003.py | 24 ++++++++++++++++-------- src/fparser/two/Fortran2008/__init__.py | 21 ++++++++++++++------- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/src/fparser/two/Fortran2003.py b/src/fparser/two/Fortran2003.py index f7496df3..23025f55 100644 --- a/src/fparser/two/Fortran2003.py +++ b/src/fparser/two/Fortran2003.py @@ -12509,8 +12509,7 @@ def match(cls, string): (that overrides it) into scope. """ - result = CallBase.match(cls._intrinsic_type, - Actual_Arg_Spec_List, string) + result = CallBase.match(cls._intrinsic_type, Actual_Arg_Spec_List, string) if not result: return None # There is a match so check the number of args provided @@ -13289,27 +13288,36 @@ def tostr(self): _names.append(n) n = n[:-5] # Generate 'list' class - exec("""\ + exec( + """\ class %s_List(SequenceBase): subclass_names = [\'%s\'] use_names = [] def match(string): return SequenceBase.match(r\',\', %s, string) -""" % (n, n, n)) +""" + % (n, n, n) + ) elif n.endswith("_Name"): _names.append(n) n = n[:-5] - exec("""\ + exec( + """\ class %s_Name(Base): subclass_names = [\'Name\'] -""" % (n)) +""" + % (n) + ) elif n.startswith("Scalar_"): _names.append(n) n = n[7:] - exec("""\ + exec( + """\ class Scalar_%s(Base): subclass_names = [\'%s\'] -""" % (n, n)) +""" + % (n, n) + ) DynamicImport().import_now() diff --git a/src/fparser/two/Fortran2008/__init__.py b/src/fparser/two/Fortran2008/__init__.py index 64ea322d..8570a99c 100644 --- a/src/fparser/two/Fortran2008/__init__.py +++ b/src/fparser/two/Fortran2008/__init__.py @@ -102,7 +102,8 @@ from fparser.two.Fortran2008.label_do_stmt_r816 import Label_Do_Stmt from fparser.two.Fortran2008.nonlabel_do_stmt_r817 import Nonlabel_Do_Stmt from fparser.two.Fortran2008.intrinsics_f08 import ( - Intrinsic_Name, Intrinsic_Function_Reference + Intrinsic_Name, + Intrinsic_Function_Reference, ) # pylint: disable=eval-used @@ -130,27 +131,33 @@ _names.append(n) n = n[:-5] # Generate 'list' class - exec(f"""\ + exec( + f"""\ class {n}_List(SequenceBase): subclass_names = [\'{n}\'] use_names = [] @staticmethod def match(string): return SequenceBase.match(r\',\', {n}, string) -""") +""" + ) elif n.endswith("_Name"): _names.append(n) n = n[:-5] - exec(f"""\ + exec( + f"""\ class {n}_Name(Base): subclass_names = [\'Name\'] -""") +""" + ) elif n.startswith("Scalar_"): _names.append(n) n = n[7:] - exec(f"""\ + exec( + f"""\ class Scalar_{n}(Base): subclass_names = [\'{n}\'] -""") +""" + ) # Make sure NEW_CLS does not reference a class so is not accidentally # picked up in __all__. NEW_CLS = None From 601e30c894abceaebd7335a8dab58da05f269a52 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Wed, 24 Jun 2026 15:51:20 +0100 Subject: [PATCH 05/12] manual pyblack update --- src/fparser/two/Fortran2003.py | 12 +++--------- src/fparser/two/Fortran2008/__init__.py | 9 +++------ 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/fparser/two/Fortran2003.py b/src/fparser/two/Fortran2003.py index 23025f55..dc94b723 100644 --- a/src/fparser/two/Fortran2003.py +++ b/src/fparser/two/Fortran2003.py @@ -13295,9 +13295,7 @@ class %s_List(SequenceBase): use_names = [] def match(string): return SequenceBase.match(r\',\', %s, string) -""" - % (n, n, n) - ) +""" % (n, n, n)) elif n.endswith("_Name"): _names.append(n) n = n[:-5] @@ -13305,9 +13303,7 @@ def match(string): return SequenceBase.match(r\',\', %s, string) """\ class %s_Name(Base): subclass_names = [\'Name\'] -""" - % (n) - ) +""" % (n)) elif n.startswith("Scalar_"): _names.append(n) n = n[7:] @@ -13315,9 +13311,7 @@ class %s_Name(Base): """\ class Scalar_%s(Base): subclass_names = [\'%s\'] -""" - % (n, n) - ) +""" % (n, n)) DynamicImport().import_now() diff --git a/src/fparser/two/Fortran2008/__init__.py b/src/fparser/two/Fortran2008/__init__.py index 8570a99c..3056bcb9 100644 --- a/src/fparser/two/Fortran2008/__init__.py +++ b/src/fparser/two/Fortran2008/__init__.py @@ -138,8 +138,7 @@ class {n}_List(SequenceBase): use_names = [] @staticmethod def match(string): return SequenceBase.match(r\',\', {n}, string) -""" - ) +""") elif n.endswith("_Name"): _names.append(n) n = n[:-5] @@ -147,8 +146,7 @@ def match(string): return SequenceBase.match(r\',\', {n}, string) f"""\ class {n}_Name(Base): subclass_names = [\'Name\'] -""" - ) +""") elif n.startswith("Scalar_"): _names.append(n) n = n[7:] @@ -156,8 +154,7 @@ class {n}_Name(Base): f"""\ class Scalar_{n}(Base): subclass_names = [\'{n}\'] -""" - ) +""") # Make sure NEW_CLS does not reference a class so is not accidentally # picked up in __all__. NEW_CLS = None From 53b2dd2e955acab0ec34dc3c40eba9f93462f448 Mon Sep 17 00:00:00 2001 From: LonelyCat124 <3043914+LonelyCat124@users.noreply.github.com.> Date: Wed, 24 Jun 2026 15:52:52 +0100 Subject: [PATCH 06/12] More formatting changes --- src/fparser/two/Fortran2003.py | 9 +++------ src/fparser/two/Fortran2008/__init__.py | 9 +++------ 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/fparser/two/Fortran2003.py b/src/fparser/two/Fortran2003.py index dc94b723..b9ad9972 100644 --- a/src/fparser/two/Fortran2003.py +++ b/src/fparser/two/Fortran2003.py @@ -13288,8 +13288,7 @@ def tostr(self): _names.append(n) n = n[:-5] # Generate 'list' class - exec( - """\ + exec("""\ class %s_List(SequenceBase): subclass_names = [\'%s\'] use_names = [] @@ -13299,16 +13298,14 @@ def match(string): return SequenceBase.match(r\',\', %s, string) elif n.endswith("_Name"): _names.append(n) n = n[:-5] - exec( - """\ + exec("""\ class %s_Name(Base): subclass_names = [\'Name\'] """ % (n)) elif n.startswith("Scalar_"): _names.append(n) n = n[7:] - exec( - """\ + exec("""\ class Scalar_%s(Base): subclass_names = [\'%s\'] """ % (n, n)) diff --git a/src/fparser/two/Fortran2008/__init__.py b/src/fparser/two/Fortran2008/__init__.py index 3056bcb9..e381a04f 100644 --- a/src/fparser/two/Fortran2008/__init__.py +++ b/src/fparser/two/Fortran2008/__init__.py @@ -131,8 +131,7 @@ _names.append(n) n = n[:-5] # Generate 'list' class - exec( - f"""\ + exec(f"""\ class {n}_List(SequenceBase): subclass_names = [\'{n}\'] use_names = [] @@ -142,16 +141,14 @@ def match(string): return SequenceBase.match(r\',\', {n}, string) elif n.endswith("_Name"): _names.append(n) n = n[:-5] - exec( - f"""\ + exec(f"""\ class {n}_Name(Base): subclass_names = [\'Name\'] """) elif n.startswith("Scalar_"): _names.append(n) n = n[7:] - exec( - f"""\ + exec(f"""\ class Scalar_{n}(Base): subclass_names = [\'{n}\'] """) From 3ab5c2fb9e968c2c70df02c4595d16757cf5ab41 Mon Sep 17 00:00:00 2001 From: Andrew Porter Date: Thu, 25 Jun 2026 11:10:51 +0100 Subject: [PATCH 07/12] #514 fix pylint issues --- src/fparser/two/Fortran2008/intrinsics_f08.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/fparser/two/Fortran2008/intrinsics_f08.py b/src/fparser/two/Fortran2008/intrinsics_f08.py index 1ff1ccc2..d80096c4 100644 --- a/src/fparser/two/Fortran2008/intrinsics_f08.py +++ b/src/fparser/two/Fortran2008/intrinsics_f08.py @@ -36,13 +36,10 @@ Module containing Fortran2008 Intrinsics. """ -from typing import Union - from fparser.two.Fortran2003 import Intrinsic_Name as F2003_Intrinsic_Name from fparser.two.Fortran2003 import ( Intrinsic_Function_Reference as F2003_Intrinsic_Function_Reference, ) -from fparser.two.utils import STRINGBase class Intrinsic_Name(F2003_Intrinsic_Name): From 3c2efbfd0df735076b89fccf9e384dce82ae9a3c Mon Sep 17 00:00:00 2001 From: Andrew Porter Date: Thu, 25 Jun 2026 11:14:29 +0100 Subject: [PATCH 08/12] #514 rm print --- src/fparser/two/tests/fortran2008/test_f08_intrinsics.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/fparser/two/tests/fortran2008/test_f08_intrinsics.py b/src/fparser/two/tests/fortran2008/test_f08_intrinsics.py index 7aa894dd..86e537f1 100644 --- a/src/fparser/two/tests/fortran2008/test_f08_intrinsics.py +++ b/src/fparser/two/tests/fortran2008/test_f08_intrinsics.py @@ -51,7 +51,6 @@ def test_f2008_intrinsic(f2008_parser): """) tree = f2008_parser(reader) intrinsic = walk(tree, Intrinsic_Name) - print(tree.__repr__) assert len(intrinsic) == 1 assert str(intrinsic[0]) == "ERF" From 9db78c5b519f37eafa937250b8a32f72725b0b1c Mon Sep 17 00:00:00 2001 From: Andrew Porter Date: Thu, 25 Jun 2026 11:19:10 +0100 Subject: [PATCH 09/12] #514 move new tests into existing test file --- .../tests/fortran2008/test_intrinsics_2008.py | 49 +++++++++++++++++-- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/src/fparser/two/tests/fortran2008/test_intrinsics_2008.py b/src/fparser/two/tests/fortran2008/test_intrinsics_2008.py index 10d9e195..ea11a241 100644 --- a/src/fparser/two/tests/fortran2008/test_intrinsics_2008.py +++ b/src/fparser/two/tests/fortran2008/test_intrinsics_2008.py @@ -32,14 +32,16 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -"""Test intrinsic handling within Fortran2008. At the moment, the only -special consideration (beyond 2003) is that we can't disambiguate a -shadowed intrinsic within a submodule. +"""Test intrinsic handling within Fortran2008. In particular: the special +consideration (beyond 2003) is that we can't disambiguate a shadowed +intrinsic within a submodule. There are also a few new intrinsic +functions. """ import pytest from fparser.api import get_reader +from fparser.common.readfortran import FortranStringReader from fparser.two import Fortran2003, Fortran2008 from fparser.two.utils import walk @@ -51,7 +53,8 @@ def test_intrinsic_in_submodule(): assume that is bringing the shadowed symbol into scope). """ - reader = get_reader("""\ + reader = get_reader( + """\ submodule (foobar) bar contains @@ -60,6 +63,42 @@ def test_intrinsic_in_submodule(): a = dot_product(1,2,3) end subroutine my_sub end - """) + """ + ) ast = Fortran2008.Submodule(reader) assert not walk(ast, Fortran2003.Intrinsic_Function_Reference) + + +def test_f2008_intrinsic(f2008_parser): + """Test Fortran2008 intrinsic is created with the f2008 parser.""" + + reader = FortranStringReader( + """subroutine test + integer :: i + + i = erf(i) + end subroutine test + """ + ) + tree = f2008_parser(reader) + intrinsic = walk(tree, Fortran2008.Intrinsic_Name) + assert len(intrinsic) == 1 + assert str(intrinsic[0]) == "ERF" + + +def test_f2008_intrinsic_f2003_parse(f2003_parser): + """Test Fortran2008 intrinsic is not created with the f2003 parser.""" + reader = FortranStringReader( + """subroutine test + integer :: i + + i = erf(i) + end subroutine test + """ + ) + tree = f2003_parser(reader) + intrinsic = walk(tree, Fortran2008.Intrinsic_Name) + assert len(intrinsic) == 0 + partref = walk(tree, Fortran2003.Part_Ref) + assert len(partref) == 1 + assert str(partref[0]) == "erf(i)" From 1cf044c80dec1594c1ff3c55e3469c8592e8f88c Mon Sep 17 00:00:00 2001 From: Andrew Porter Date: Thu, 25 Jun 2026 11:19:27 +0100 Subject: [PATCH 10/12] #514 rm new test file --- .../tests/fortran2008/test_f08_intrinsics.py | 71 ------------------- 1 file changed, 71 deletions(-) delete mode 100644 src/fparser/two/tests/fortran2008/test_f08_intrinsics.py diff --git a/src/fparser/two/tests/fortran2008/test_f08_intrinsics.py b/src/fparser/two/tests/fortran2008/test_f08_intrinsics.py deleted file mode 100644 index 86e537f1..00000000 --- a/src/fparser/two/tests/fortran2008/test_f08_intrinsics.py +++ /dev/null @@ -1,71 +0,0 @@ -# ----------------------------------------------------------------------------- -# BSD 3-Clause License -# -# Copyright (c) 2023-2024, Science and Technology Facilities Council. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# * Neither the name of the copyright holder nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# ----------------------------------------------------------------------------- - -"""Test the Fortran 2008 intrinsic support.""" - -from fparser.common.readfortran import FortranStringReader -from fparser.two.Fortran2003 import Part_Ref -from fparser.two.Fortran2008 import Intrinsic_Name -from fparser.two.utils import walk - - -def test_f2008_intrinsic(f2008_parser): - """Test Fortran2008 intrinsic is created with the f2008 parser.""" - - reader = FortranStringReader("""subroutine test - integer :: i - - i = erf(i) - end subroutine test - """) - tree = f2008_parser(reader) - intrinsic = walk(tree, Intrinsic_Name) - assert len(intrinsic) == 1 - assert str(intrinsic[0]) == "ERF" - - -def test_f2008_intrinsic_f2003_parse(f2003_parser): - """Test Fortran2008 intrinsic is not created with the f2003 parser.""" - reader = FortranStringReader("""subroutine test - integer :: i - - i = erf(i) - end subroutine test - """) - tree = f2003_parser(reader) - intrinsic = walk(tree, Intrinsic_Name) - assert len(intrinsic) == 0 - partref = walk(tree, Part_Ref) - assert len(partref) == 1 - assert str(partref[0]) == "erf(i)" From 8cf6a4313491c73e2dc1a4f85acf6acc295a8c0e Mon Sep 17 00:00:00 2001 From: Andrew Porter Date: Thu, 25 Jun 2026 11:53:11 +0100 Subject: [PATCH 11/12] #514 fix black formatting for 3.14 --- .../tests/fortran2008/test_intrinsics_2008.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/fparser/two/tests/fortran2008/test_intrinsics_2008.py b/src/fparser/two/tests/fortran2008/test_intrinsics_2008.py index ea11a241..ff54ee60 100644 --- a/src/fparser/two/tests/fortran2008/test_intrinsics_2008.py +++ b/src/fparser/two/tests/fortran2008/test_intrinsics_2008.py @@ -53,8 +53,7 @@ def test_intrinsic_in_submodule(): assume that is bringing the shadowed symbol into scope). """ - reader = get_reader( - """\ + reader = get_reader("""\ submodule (foobar) bar contains @@ -63,8 +62,7 @@ def test_intrinsic_in_submodule(): a = dot_product(1,2,3) end subroutine my_sub end - """ - ) + """) ast = Fortran2008.Submodule(reader) assert not walk(ast, Fortran2003.Intrinsic_Function_Reference) @@ -72,14 +70,12 @@ def test_intrinsic_in_submodule(): def test_f2008_intrinsic(f2008_parser): """Test Fortran2008 intrinsic is created with the f2008 parser.""" - reader = FortranStringReader( - """subroutine test + reader = FortranStringReader("""subroutine test integer :: i i = erf(i) end subroutine test - """ - ) + """) tree = f2008_parser(reader) intrinsic = walk(tree, Fortran2008.Intrinsic_Name) assert len(intrinsic) == 1 @@ -88,14 +84,12 @@ def test_f2008_intrinsic(f2008_parser): def test_f2008_intrinsic_f2003_parse(f2003_parser): """Test Fortran2008 intrinsic is not created with the f2003 parser.""" - reader = FortranStringReader( - """subroutine test + reader = FortranStringReader("""subroutine test integer :: i i = erf(i) end subroutine test - """ - ) + """) tree = f2003_parser(reader) intrinsic = walk(tree, Fortran2008.Intrinsic_Name) assert len(intrinsic) == 0 From 5a27f242f7ddbfc87c4acf6f1155abc55f84df44 Mon Sep 17 00:00:00 2001 From: Sergi Siso Date: Thu, 25 Jun 2026 13:59:06 +0100 Subject: [PATCH 12/12] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2534268f..aee0f6fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ Modifications by (in alphabetical order): * P. Vitt, University of Siegen, Germany * A. Voysey, UK Met Office +25/06/2026 PR #514 towards #428. Add some Fortran2008-only intrinsics. + 19/06/2026 PR #513 for #512. Fix circular import in Fortran2008. ## Release 0.2.3 (11/06/2026) ##