diff --git a/CHANGELOG.md b/CHANGELOG.md index 96b152b2..202a531d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,9 @@ Modifications by (in alphabetical order): * P. Vitt, University of Siegen, Germany * A. Voysey, UK Met Office +26/04/2023 PR #406 for #405. Add support for F2008 optional "::" in PROCEDURE + statement. + 03/04/2023 PR #392 for #326. Add support for F2008 block and critical constructs. 30/03/2023 PR #396 for #395. Fix trailing whitespace bug in CallBase. diff --git a/doc/source/fparser2.rst b/doc/source/fparser2.rst index dfbb20b7..66d55f7f 100644 --- a/doc/source/fparser2.rst +++ b/doc/source/fparser2.rst @@ -42,8 +42,10 @@ Fortran 2003. This is implemented in the Fortran2003.py `file`__ and contains an entirely separate parser to fparser1 that includes rules for Fortran 2003 syntax. Support for Fortran 2008 is being added in the Fortran2008.py `file`__ which extends the Fortran2003 rules -appropriately. At this time fparser2 supports submodules, co-arrays, -the 'mold' argument to allocate and the 'contiguous' keyword in Fortran2008. +appropriately. At this time fparser2 supports the following +Fortran2008 features: submodules, co-arrays, the 'mold' argument to +allocate, the 'contiguous' keyword, the 'BLOCK' construct, the +'CRITICAL' construct and the optional '::' for a procedure statement. __ https://github.com/stfc/fparser/blob/master/src/fparser/two/Fortran2003.py __ https://github.com/stfc/fparser/blob/master/src/fparser/two/Fortran2008.py diff --git a/src/fparser/two/Fortran2008.py b/src/fparser/two/Fortran2008.py index ddea5401..9a46e264 100644 --- a/src/fparser/two/Fortran2008.py +++ b/src/fparser/two/Fortran2008.py @@ -137,6 +137,7 @@ Executable_Construct_C201 as Executable_Construct_C201_2003, If_Stmt as If_Stmt_2003, Open_Stmt as Open_Stmt_2003, + Procedure_Stmt as Procedure_Stmt_2003, Program_Unit as Program_Unit_2003, Type_Declaration_Stmt as Type_Declaration_Stmt_2003, ) @@ -165,7 +166,7 @@ class Program_Unit(Program_Unit_2003): # R202 class Executable_Construct(Executable_Construct_2003): # R213 # pylint: disable=invalid-name """ - Fortran 2003 rule R213. + Fortran 2008 rule R213. .. code-block:: fortran @@ -1578,6 +1579,54 @@ def match(string): ) +class Procedure_Stmt(Procedure_Stmt_2003): # R1206 + """ + Fortran 2008 Rule 1206. + + procedure-stmt is [ MODULE ] PROCEDURE [ :: ] procedure-name-list + + """ + + @staticmethod + def match(string): + """:param str string: Fortran code to check for a match + + :returns: 3-tuple containing a boolean indicating whether the \ + optional MODULE keyword is included, a boolean indicating \ + whether the optional '::' is included and a Procedure_Name_List \ + instance, or None if there is no match. + :rtype: Optional[Tuple[ \ + bool, bool, \ + :py:class:`fparser.two.Fortran2003.Procedure_Name_List`]]] + + """ + line = string.lstrip() + optional_module = None + if line[:6].upper() == "MODULE": + line = line[6:].lstrip() + optional_module = "MODULE" + if line[:9].upper() != "PROCEDURE": + return None + line = line[9:].lstrip() + optional_colons = None + if line[:2] == "::": + line = line[2:].lstrip() + optional_colons = "::" + return (Procedure_Name_List(line), optional_module, optional_colons) + + def tostr(self): + """ + :returns: the string representation of this node. + :rtype: str + """ + result = "PROCEDURE" + if self.items[1]: + result = f"MODULE {result}" + if self.items[2]: + result = f"{result} ::" + return f"{result} {self.items[0]}" + + # # GENERATE Scalar_, _List, _Name CLASSES # diff --git a/src/fparser/two/tests/fortran2008/test_procedure_stmt_r1206.py b/src/fparser/two/tests/fortran2008/test_procedure_stmt_r1206.py new file mode 100644 index 00000000..ef8bb07b --- /dev/null +++ b/src/fparser/two/tests/fortran2008/test_procedure_stmt_r1206.py @@ -0,0 +1,89 @@ +# Copyright (c) 2023 Science and Technology Facilities Council + +# All rights reserved. + +# Modifications made as part of the fparser project are distributed +# under the following license: + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: + +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. + +# 2. 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. + +# 3. 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 Fortran 2008 rule R1206 + + procedure-stmt is [ MODULE ] PROCEDURE [ :: ] procedure-name-list + +""" + +import pytest +from fparser.two.Fortran2008 import Procedure_Stmt +from fparser.two.utils import NoMatchError + + +def test_start_space(): + """Test that there is a match if the string contains white space at + the start and that the tostr() output is as expected. + + """ + result = Procedure_Stmt(" procedure dummy") + assert isinstance(result, Procedure_Stmt) + assert str(result) == "PROCEDURE dummy" + + +def test_module(): + """Test that there is a match if the string contains the optional + MODULE keyword and that the tostr() output is as expected. + + """ + result = Procedure_Stmt(" module procedure dummy") + assert isinstance(result, Procedure_Stmt) + assert str(result) == "MODULE PROCEDURE dummy" + + +def test_colons(): + """Test that there is a match if the string contains optional :: after + the procedure keyword and that the tostr() output is as expected. + + """ + result = Procedure_Stmt(" module procedure :: dummy") + assert isinstance(result, Procedure_Stmt) + assert str(result) == "MODULE PROCEDURE :: dummy" + + +@pytest.mark.parametrize( + "string", + [ + "procedur dummy", + "modul procedure dummy", + "procedure : dummy", + "procedure ", + "procedure :: ", + ], +) +def test_invalid(string): + """Test that there is no match for various invalid input strings.""" + with pytest.raises(NoMatchError): + _ = Procedure_Stmt(string) diff --git a/src/fparser/two/tests/test_fortran2003.py b/src/fparser/two/tests/test_fortran2003.py index ca0d89f6..f49a2ad1 100644 --- a/src/fparser/two/tests/test_fortran2003.py +++ b/src/fparser/two/tests/test_fortran2003.py @@ -2964,6 +2964,10 @@ def test_procedure_stmt(): # R1206 assert isinstance(obj, tcls), repr(obj) assert str(obj) == "MODULE PROCEDURE a, b" + # '::' is only valid from F2008 onwards + with pytest.raises(NoMatchError): + _ = tcls("procedure :: a") + def test_generic_spec(): # R1207 tcls = Generic_Spec