From ae373c55a5ddc238e71d02b4e6d21380bdc709d4 Mon Sep 17 00:00:00 2001 From: Rupert Ford Date: Sun, 4 Nov 2018 15:49:23 -0800 Subject: [PATCH 1/7] issue #118. Fixed the bug. --- src/fparser/two/Fortran2003.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fparser/two/Fortran2003.py b/src/fparser/two/Fortran2003.py index 1bff8a0f..e07f163f 100644 --- a/src/fparser/two/Fortran2003.py +++ b/src/fparser/two/Fortran2003.py @@ -7314,9 +7314,9 @@ def match(string): if line[0] != ':': return line = line[1:].lstrip() - # Missing Only_List/Rename_List after 'USE Module_Name, ONLY:' if not line: - return + # Missing Only_List after 'USE Module_Name, ONLY:' + return nature, dcolon, name, ', ONLY:', None return nature, dcolon, name, ', ONLY:', Only_List(line) return nature, dcolon, name, ',', Rename_List(line) From 2e33b94e549191b343491073d33af1c98dbe540d Mon Sep 17 00:00:00 2001 From: Rupert Ford Date: Sun, 4 Nov 2018 17:19:22 -0800 Subject: [PATCH 2/7] issue #118. Added initial set of tests and fixed a bug. --- src/fparser/two/Fortran2003.py | 4 + .../tests/fortran2003/test_usestmt_r1109.py | 144 ++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 src/fparser/two/tests/fortran2003/test_usestmt_r1109.py diff --git a/src/fparser/two/Fortran2003.py b/src/fparser/two/Fortran2003.py index e07f163f..ff810d71 100644 --- a/src/fparser/two/Fortran2003.py +++ b/src/fparser/two/Fortran2003.py @@ -7310,6 +7310,10 @@ def match(string): return if line[:4].upper() == 'ONLY': line = line[4:].lstrip() + if not line: + # Expected ':' but there is nothing after the 'ONLY' + # specification + return # Missing ':' after ', ONLY' specification if line[0] != ':': return diff --git a/src/fparser/two/tests/fortran2003/test_usestmt_r1109.py b/src/fparser/two/tests/fortran2003/test_usestmt_r1109.py new file mode 100644 index 00000000..79ca8fb5 --- /dev/null +++ b/src/fparser/two/tests/fortran2003/test_usestmt_r1109.py @@ -0,0 +1,144 @@ +# Copyright (c) 2018 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 2003 rule R1109 : This file tests the support for the +Use statement. + +''' + +import pytest +from fparser.api import get_reader +from fparser.two.Fortran2003 import Use_Stmt + +# match() use ... + + +# match() 'use x' +def test_use(f2003_create): + '''Just get something working to start off with''' + reader = get_reader("use my_model") + ast = Use_Stmt(reader) + assert "USE my_model" in str(ast) + + +# match() 'use :: x' +def test_use_colons(f2003_create): + '''Just get something working to start off with''' + reader = get_reader("use :: my_model") + ast = Use_Stmt(reader) + assert "USE :: my_model" in str(ast) + + +# match() 'use, nature :: x' +def test_use_nature(f2003_create): + '''Just get something working to start off with''' + reader = get_reader("use, intrinsic :: my_model") + ast = Use_Stmt(reader) + assert "USE, INTRINSIC :: my_model" in str(ast) + + +# match() 'use x, rename' +def test_use_rename(f2003_create): + '''Just get something working to start off with''' + reader = get_reader("use my_model, name=>new_name") + ast = Use_Stmt(reader) + assert "USE my_model, name => new_name" in str(ast) + + +# match() 'use x, only: y' +def test_use_only(f2003_create): + '''Just get something working to start off with''' + reader = get_reader("use my_model, only: name") + ast = Use_Stmt(reader) + assert "USE my_model, ONLY: name" in str(ast) + + +# match() 'use x, only:' +def test_use_only_empty(f2003_create): + '''Just get something working to start off with''' + reader = get_reader("use my_model, only:") + ast = Use_Stmt(reader) + assert "USE my_model, ONLY:" in str(ast) + + +# match() ' use , nature :: x , name=>new_name' +def test_use_spaces_1(f2003_create): + '''Just get something working to start off with''' + reader = get_reader(" use , intrinsic :: my_model , name=>new_name ") + ast = Use_Stmt(reader) + assert "USE, INTRINSIC :: my_model, name => new_name" in str(ast) + + +# match() ' use , nature :: x , only : name' +def test_use_spaces_2(f2003_create): + '''Just get something working to start off with''' + reader = get_reader(" use , intrinsic :: my_model , only : name ") + ast = Use_Stmt(reader) + assert "USE, INTRINSIC :: my_model, ONLY: name" in str(ast) + + +# match() mixed case +def test_use_mixed_case(f2003_create): + '''Just get something working to start off with''' + reader = get_reader("UsE my_model, OnLy: name") + ast = Use_Stmt(reader) + assert "USE my_model, ONLY: name" in str(ast) + +# match() Syntax errors + + +def test_syntaxerror(f2003_create): + '''Test that None is returned if: the line is shorter than 3 + characters, it is miss-spelt, the line is empty after use, there + is no space after use, nature is missing, there is a missing name + after nature, there is a missing :: after nature, there is a + missing name before only_list (*2), and there is a missing : after + only (*2) + + ''' + for name in ["us", "ust", "use", "usemy_model", "use, ::", + "use, intrinsic::", "use, intrinsic my_module", "use,", + "use, instrinsic ::,", "use my_model,", "use my_model, only", + "use my_model, only ;"]: + reader = get_reader(name) + ast = Use_Stmt(reader) + assert not ast + + + +# match() Internal errors + +# tostr() Errors + +# misc From ccefbf330e72f08b314f677999b25d8368635753 Mon Sep 17 00:00:00 2001 From: Rupert Ford Date: Wed, 7 Nov 2018 07:58:05 -0800 Subject: [PATCH 3/7] issue #118. Completed tests and updated Python code with internalerrors. --- src/fparser/two/Fortran2003.py | 32 +++- .../tests/fortran2003/test_usestmt_r1109.py | 170 +++++++++++++----- src/fparser/two/tests/test_fortran2003.py | 128 ------------- 3 files changed, 146 insertions(+), 184 deletions(-) diff --git a/src/fparser/two/Fortran2003.py b/src/fparser/two/Fortran2003.py index ff810d71..187647d9 100644 --- a/src/fparser/two/Fortran2003.py +++ b/src/fparser/two/Fortran2003.py @@ -7256,10 +7256,11 @@ def match(string): "ONLY" specification and optional "Rename" or "Only" list) :rtype: 5-tuple of objects (module name and 4 optional) ''' - # Incorrect 'USE' statement - if string[:3].upper() != 'USE': + line = string.strip() + # Incorrect 'USE' statement or line too short + if line[:3].upper() != 'USE': return - line = string[3:] + line = line[3:] # Empty string after 'USE' if not line: return @@ -7328,21 +7329,34 @@ def tostr(self): ''' :return: parsed representation of "USE" statement :rtype: string + :raises InternalError: if items array is not the expected size + :raises InternalError: if items array[2] is not a string or is an empty string + :raises InternalError: if items array[3] is 'None' as it should be a string ''' + if len(self.items) != 5: + raise InternalError( + "Use_Stmt.tostr(). 'Items' should be of size 5 but found " + "'{0}'.".format(len(self.items))) + if not self.items[2]: + raise InternalError("Use_Stmt.tostr(). 'Items' entry 2 should " + "be a module name but it is empty") + if self.items[3] == None: + raise InternalError("Use_Stmt.tostr(). 'Items' entry 3 should " + "be a string but found 'None'") usestmt = 'USE' # Add optional Module_Nature ("INTRINSIC" or "NON_INTRINSIC") # followed by a double colon to "USE" statement - if self.items[0] is not None and self.items[1] is not None: - usestmt += ', %s %s' % (self.items[0], self.items[1]) + if self.items[0] and self.items[1]: + usestmt += ", {0} {1}".format(self.items[0], self.items[1]) # Add optional double colon after "USE" statement without # Module_Nature (valid Fortran) - elif self.items[0] is None and self.items[1] is not None: - usestmt += ' %s' % (self.items[1]) + elif not self.items[0] and self.items[1]: + usestmt += " {0}".format(self.items[1]) # Add Module_Name and optional "ONLY" specifier if present - usestmt += ' %s%s' % (self.items[2], self.items[3]) + usestmt += " {0}{1}".format(self.items[2], self.items[3]) # Add optional Only_List or Rename_List if present if self.items[4] is not None: - usestmt += ' %s' % (self.items[4]) + usestmt += " {0}".format(self.items[4]) return usestmt diff --git a/src/fparser/two/tests/fortran2003/test_usestmt_r1109.py b/src/fparser/two/tests/fortran2003/test_usestmt_r1109.py index 79ca8fb5..e2373b8b 100644 --- a/src/fparser/two/tests/fortran2003/test_usestmt_r1109.py +++ b/src/fparser/two/tests/fortran2003/test_usestmt_r1109.py @@ -40,105 +40,181 @@ import pytest from fparser.api import get_reader from fparser.two.Fortran2003 import Use_Stmt +from fparser.two.utils import NoMatchError, InternalError # match() use ... -# match() 'use x' +# match() 'use x'. Use both string and reader input here, but from +# here on we will just use string input as that is what is passed to +# the match() method def test_use(f2003_create): - '''Just get something working to start off with''' - reader = get_reader("use my_model") - ast = Use_Stmt(reader) - assert "USE my_model" in str(ast) + '''Check that a basic use is parsed correctly. Input separately as a + string and as a reader object + + ''' + def check_use(reader): + ''' ''' + ast = Use_Stmt(reader) + assert "USE my_model" in str(ast) + assert repr(ast) == "Use_Stmt(None, None, Name('my_model'), '', None)" + + line = "use my_model" + check_use(line) + reader = get_reader(line) + check_use(reader) # match() 'use :: x' def test_use_colons(f2003_create): - '''Just get something working to start off with''' - reader = get_reader("use :: my_model") - ast = Use_Stmt(reader) + '''Check that a basic use with '::' is parsed correctly.''' + line = "use :: my_model" + ast = Use_Stmt(line) assert "USE :: my_model" in str(ast) + assert repr(ast) == "Use_Stmt(None, '::', Name('my_model'), '', None)" # match() 'use, nature :: x' def test_use_nature(f2003_create): - '''Just get something working to start off with''' - reader = get_reader("use, intrinsic :: my_model") - ast = Use_Stmt(reader) + '''Check that a use with a 'nature' specification is parsed correctly.''' + line = "use, intrinsic :: my_model" + ast = Use_Stmt(line) assert "USE, INTRINSIC :: my_model" in str(ast) + assert repr(ast) == ( + "Use_Stmt(Module_Nature('INTRINSIC'), '::', Name('my_model'), " + "'', None)") # match() 'use x, rename' def test_use_rename(f2003_create): - '''Just get something working to start off with''' - reader = get_reader("use my_model, name=>new_name") - ast = Use_Stmt(reader) - assert "USE my_model, name => new_name" in str(ast) + '''Check that a use with a nename clause is parsed correctly.''' + line = "use my_module, name=>new_name" + ast = Use_Stmt(line) + assert "USE my_module, name => new_name" in str(ast) + assert repr(ast) == ( + "Use_Stmt(None, None, Name('my_module'), ',', " + "Rename(None, Name('name'), Name('new_name')))") # match() 'use x, only: y' def test_use_only(f2003_create): - '''Just get something working to start off with''' - reader = get_reader("use my_model, only: name") - ast = Use_Stmt(reader) + '''Check that a use statement is parsed correctly when there is an + only clause. + + ''' + line = "use my_model, only: name" + ast = Use_Stmt(line) assert "USE my_model, ONLY: name" in str(ast) + assert repr(ast) == ( + "Use_Stmt(None, None, Name('my_model'), ', ONLY:', Name('name'))") # match() 'use x, only:' def test_use_only_empty(f2003_create): - '''Just get something working to start off with''' - reader = get_reader("use my_model, only:") - ast = Use_Stmt(reader) + '''Check that a use statement is parsed correctly when there is an + only clause without any content. + + ''' + line = "use my_model, only:" + ast = Use_Stmt(line) assert "USE my_model, ONLY:" in str(ast) + assert repr(ast) == ( + "Use_Stmt(None, None, Name('my_model'), ', ONLY:', None)") # match() ' use , nature :: x , name=>new_name' def test_use_spaces_1(f2003_create): - '''Just get something working to start off with''' - reader = get_reader(" use , intrinsic :: my_model , name=>new_name ") - ast = Use_Stmt(reader) - assert "USE, INTRINSIC :: my_model, name => new_name" in str(ast) + '''Check that a use statement with spaces works correctly with + renaming. + ''' + line = " Use , intrinsic :: my_model , name=>new_name " + ast = Use_Stmt(line) + assert "USE, INTRINSIC :: my_model, name => new_name" in str(ast) + assert repr(ast) == ( + "Use_Stmt(Module_Nature('INTRINSIC'), '::', Name('my_model'), ',', " + "Rename(None, Name('name'), Name('new_name')))") # match() ' use , nature :: x , only : name' def test_use_spaces_2(f2003_create): - '''Just get something working to start off with''' - reader = get_reader(" use , intrinsic :: my_model , only : name ") - ast = Use_Stmt(reader) + '''Check that a use statement with spaces works correctly with an only + clause. + + ''' + line = " use , intrinsic :: my_model , only : name " + ast = Use_Stmt(line) assert "USE, INTRINSIC :: my_model, ONLY: name" in str(ast) + assert repr(ast) == ("Use_Stmt(Module_Nature('INTRINSIC'), '::', " + "Name('my_model'), ', ONLY:', Name('name'))") # match() mixed case def test_use_mixed_case(f2003_create): - '''Just get something working to start off with''' - reader = get_reader("UsE my_model, OnLy: name") - ast = Use_Stmt(reader) + '''Check that a use statement with mixed case keywords ('use' and + 'only') works as expected. + + ''' + line = "UsE my_model, OnLy: name" + ast = Use_Stmt(line) assert "USE my_model, ONLY: name" in str(ast) + assert repr(ast) == ("Use_Stmt(None, None, Name('my_model'), ', ONLY:', " + "Name('name'))") # match() Syntax errors def test_syntaxerror(f2003_create): - '''Test that None is returned if: the line is shorter than 3 - characters, it is miss-spelt, the line is empty after use, there - is no space after use, nature is missing, there is a missing name - after nature, there is a missing :: after nature, there is a - missing name before only_list (*2), and there is a missing : after - only (*2) + '''Test that NoMatchError is raised for various syntax errors.''' + for line in ["us", "ust", "use", "usemy_model", "use, ", "use, ::", + "use, intrinsic", "use, intrinsic::", + "use, intrinsic my_module", "use,", "use my_model,", + "use my_model, only", "use my_model, only ;", + "use my_model, only name"]: + with pytest.raises(NoMatchError) as excinfo: + ast = Use_Stmt(line) + assert "Use_Stmt: '{0}'".format(line) in str(excinfo) + +# match() Internal errors + + +def test_use_internal_error1(f2003_create): + '''Check that an internal error is raised if the length of the Items + list is not 5 as the str() method assumes that it is. ''' - for name in ["us", "ust", "use", "usemy_model", "use, ::", - "use, intrinsic::", "use, intrinsic my_module", "use,", - "use, instrinsic ::,", "use my_model,", "use my_model, only", - "use my_model, only ;"]: - reader = get_reader(name) - ast = Use_Stmt(reader) - assert not ast + line = "use my_model" + ast = Use_Stmt(line) + ast.items = (None, None, None, None) + with pytest.raises(InternalError) as excinfo: + str(ast) + assert "should be of size 5 but found '4'" in str(excinfo) +def test_use_internal_error2(f2003_create): + '''Check that an internal error is raised if the module name (entry 2 + of Items) is empty or None as the str() method assumes that it is + a string with content. + + ''' + line = "use my_model" + ast = Use_Stmt(line) + for content in [None, ""]: + ast.items = (None, None, content, None, None) + with pytest.raises(InternalError) as excinfo: + str(ast) + assert ("entry 2 should be a module name but it is " + "empty") in str(excinfo) -# match() Internal errors -# tostr() Errors +def test_use_internal_error3(f2003_create): + '''Check that an internal error is raised if entry 3 of Items is + 'None' as the str() method assumes it is a (potentially empty) + string. -# misc + ''' + line = "use my_model" + ast = Use_Stmt(line) + ast.items = (None, None, "my_module", None, None) + with pytest.raises(InternalError) as excinfo: + str(ast) + assert "entry 3 should be a string but found 'None'" in str(excinfo) diff --git a/src/fparser/two/tests/test_fortran2003.py b/src/fparser/two/tests/test_fortran2003.py index a751b03d..d5ed38cf 100644 --- a/src/fparser/two/tests/test_fortran2003.py +++ b/src/fparser/two/tests/test_fortran2003.py @@ -3621,134 +3621,6 @@ def test_module_subprogram_part(): # R1107 '\n a = 1.0\nEND SUBROUTINE foo') -def test_use_stmt(): - ''' Tests that USE statement is parsed correctly (R1109). ''' - tcls = Use_Stmt - obj = tcls('use a') - assert isinstance(obj, tcls), repr(obj) - assert str(obj) == 'USE a' - assert repr(obj) == "Use_Stmt(None, None, Name('a'), '', None)" - - obj = tcls('use :: a') - assert isinstance(obj, tcls), repr(obj) - assert str(obj) == 'USE :: a' - assert repr(obj) == "Use_Stmt(None, '::', Name('a'), '', None)" - - obj = tcls('use a, only: b') - assert isinstance(obj, tcls), repr(obj) - assert str(obj) == 'USE a, ONLY: b' - assert repr(obj) == ( - "Use_Stmt(None, None, Name('a'), ', ONLY:', Name('b'))") - - obj = tcls('use :: a, only: b') - assert isinstance(obj, tcls), repr(obj) - assert str(obj) == 'USE :: a, ONLY: b' - assert repr(obj) == ( - "Use_Stmt(None, '::', Name('a'), ', ONLY:', Name('b'))") - - obj = tcls('use a, ONLY : b') - assert isinstance(obj, tcls), repr(obj) - assert str(obj) == 'USE a, ONLY: b' - assert repr(obj) == ( - "Use_Stmt(None, None, Name('a'), ', ONLY:', Name('b'))") - - obj = tcls('use, intrinsic :: a, ONLY: b') - assert isinstance(obj, tcls), repr(obj) - assert str(obj) == 'USE, INTRINSIC :: a, ONLY: b' - assert repr(obj) == ( - "Use_Stmt(Module_Nature('INTRINSIC'), '::', Name('a'), " - "', ONLY:', Name('b'))") - - obj = tcls('use, non_intrinsic :: a, ONLY: b, c, d') - assert isinstance(obj, tcls), repr(obj) - assert str(obj) == 'USE, NON_INTRINSIC :: a, ONLY: b, c, d' - assert repr(obj) == ( - "Use_Stmt(Module_Nature('NON_INTRINSIC'), '::', Name('a'), " - "', ONLY:', Only_List(',', (Name('b'), Name('c'), Name('d'))))") - - obj = tcls('use a, c=>d') - assert isinstance(obj, tcls), repr(obj) - assert str(obj) == 'USE a, c => d' - assert repr(obj) == ( - "Use_Stmt(None, None, Name('a'), " - "',', Rename(None, Name('c'), Name('d')))") - - obj = tcls('use :: a, operator(.hey.)=>operator(.hoo.)') - assert isinstance(obj, tcls), repr(obj) - assert str(obj) == 'USE :: a, OPERATOR(.HEY.) => OPERATOR(.HOO.)' - assert repr(obj) == ( - "Use_Stmt(None, '::', Name('a'), ',', " - "Rename('OPERATOR', Defined_Op('.HEY.'), Defined_Op('.HOO.')))") - - obj = tcls('use, intrinsic :: a, operator(.hey.)=>operator(.hoo.), c=>g') - assert isinstance(obj, tcls), repr(obj) - assert str(obj) == ( - 'USE, INTRINSIC :: a, OPERATOR(.HEY.) => OPERATOR(.HOO.), c => g') - assert repr(obj) == ( - "Use_Stmt(Module_Nature('INTRINSIC'), '::', Name('a'), " - "',', Rename_List(',', (" - "Rename('OPERATOR', Defined_Op('.HEY.'), Defined_Op('.HOO.')), " - "Rename(None, Name('c'), Name('g')))))") - - obj = tcls('use, non_intrinsic :: a, ONLY: b => c') - assert isinstance(obj, tcls), repr(obj) - assert str(obj) == 'USE, NON_INTRINSIC :: a, ONLY: b => c' - assert repr(obj) == ( - "Use_Stmt(Module_Nature('NON_INTRINSIC'), '::', Name('a'), " - "', ONLY:', Rename(None, Name('b'), Name('c')))") - - # Checks that no match is found for incorrect 'USE' statement contructs - # Incorrect 'USE' statement - with pytest.raises(NoMatchError) as excinfo: - _ = tcls('8se') - assert "Use_Stmt: '8se'" in str(excinfo) - - # Empty string after 'USE' - with pytest.raises(NoMatchError) as excinfo: - _ = tcls('use') - assert "Use_Stmt: 'use'" in str(excinfo) - - # No separation between 'USE' statement and its specifiers - with pytest.raises(NoMatchError) as excinfo: - _ = tcls('usemodulename') - assert "Use_Stmt: 'usemodulename'" in str(excinfo) - - # Missing Module_Nature between ',' and '::' - with pytest.raises(NoMatchError) as excinfo: - _ = tcls('use, ::') - assert "Use_Stmt: 'use, ::'" in str(excinfo) - - # No Module_Name after 'USE, Module_Nature ::' - with pytest.raises(NoMatchError) as excinfo: - _ = tcls('use, intrinsic ::') - assert "Use_Stmt: 'use, intrinsic ::'" in str(excinfo) - - # Missing '::' after Module_Nature - with pytest.raises(NoMatchError) as excinfo: - _ = tcls('use, intrinsic a') - assert "Use_Stmt: 'use, intrinsic a'" in str(excinfo) - - # Missing Module_Name before Only_List - with pytest.raises(NoMatchError) as excinfo: - _ = tcls('use , only: b') - assert "Use_Stmt: 'use , only: b'" in str(excinfo) - - # Missing 'ONLY' specification after 'USE Module_Name,' - with pytest.raises(NoMatchError) as excinfo: - _ = tcls('use a,') - assert "Use_Stmt: 'use a,'" in str(excinfo) - - # Missing ':' after ', ONLY' specification - with pytest.raises(NoMatchError) as excinfo: - _ = tcls('use a, only b') - assert "Use_Stmt: 'use a, only b" in str(excinfo) - - # Missing Only_List/Rename_List after 'USE Module_Name, ONLY:' - with pytest.raises(NoMatchError) as excinfo: - _ = tcls('use a, only:') - assert "Use_Stmt: 'use a, only:" in str(excinfo) - - def test_module_nature(): ''' Tests that a module nature statement is parsed correctly (INTRINSIC or NON_INTRINSIC allowed, R1110). ''' From 7cee0e464d2e8da188de634c852a0aaed2a6ae4e Mon Sep 17 00:00:00 2001 From: Rupert Ford Date: Wed, 7 Nov 2018 15:39:45 -0800 Subject: [PATCH 4/7] issue #118. Tidying code. --- src/fparser/two/Fortran2003.py | 30 ++++++++++--------- .../tests/fortran2003/test_usestmt_r1109.py | 5 ++-- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/fparser/two/Fortran2003.py b/src/fparser/two/Fortran2003.py index 187647d9..dbc668ba 100644 --- a/src/fparser/two/Fortran2003.py +++ b/src/fparser/two/Fortran2003.py @@ -7235,14 +7235,14 @@ class Module_Subprogram(Base): # R1108 class Use_Stmt(StmtBase): # pylint: disable=invalid-name - """ - R1109 + ''' + Fortran 2003 rule R1109 - = USE [ [ , ] :: ] - [ , ] - | USE [ [ , ] :: ] , - ONLY: [ ] - """ + use-stmt is USE [ [ , module-nature ] :: ] module-name [ , rename-list ] + or USE [ [ , module-nature ] :: ] module-name , + ONLY : [ only-list ] + + ''' subclass_names = [] use_names = ['Module_Nature', 'Module_Name', 'Rename_List', 'Only_List'] @@ -7297,15 +7297,15 @@ def match(string): if nature is not None: return - i = line.find(',') - if i == -1: + position = line.find(',') + if position == -1: return nature, dcolon, Module_Name(line), '', None - name = line[:i].rstrip() + name = line[:position].rstrip() # Missing Module_Name before Only_List if not name: return name = Module_Name(name) - line = line[i+1:].lstrip() + line = line[position+1:].lstrip() # Missing 'ONLY' specification after 'USE Module_Name,' if not line: return @@ -7330,8 +7330,10 @@ def tostr(self): :return: parsed representation of "USE" statement :rtype: string :raises InternalError: if items array is not the expected size - :raises InternalError: if items array[2] is not a string or is an empty string - :raises InternalError: if items array[3] is 'None' as it should be a string + :raises InternalError: if items array[2] is not a string or is an \ + empty string + :raises InternalError: if items array[3] is 'None' as it should be \ + a string ''' if len(self.items) != 5: raise InternalError( @@ -7340,7 +7342,7 @@ def tostr(self): if not self.items[2]: raise InternalError("Use_Stmt.tostr(). 'Items' entry 2 should " "be a module name but it is empty") - if self.items[3] == None: + if self.items[3] is None: raise InternalError("Use_Stmt.tostr(). 'Items' entry 3 should " "be a string but found 'None'") usestmt = 'USE' diff --git a/src/fparser/two/tests/fortran2003/test_usestmt_r1109.py b/src/fparser/two/tests/fortran2003/test_usestmt_r1109.py index e2373b8b..337c22ba 100644 --- a/src/fparser/two/tests/fortran2003/test_usestmt_r1109.py +++ b/src/fparser/two/tests/fortran2003/test_usestmt_r1109.py @@ -54,7 +54,7 @@ def test_use(f2003_create): ''' def check_use(reader): - ''' ''' + '''Internal helper function to avoid code replication.''' ast = Use_Stmt(reader) assert "USE my_model" in str(ast) assert repr(ast) == "Use_Stmt(None, None, Name('my_model'), '', None)" @@ -135,6 +135,7 @@ def test_use_spaces_1(f2003_create): "Use_Stmt(Module_Nature('INTRINSIC'), '::', Name('my_model'), ',', " "Rename(None, Name('name'), Name('new_name')))") + # match() ' use , nature :: x , only : name' def test_use_spaces_2(f2003_create): '''Check that a use statement with spaces works correctly with an only @@ -171,7 +172,7 @@ def test_syntaxerror(f2003_create): "use my_model, only", "use my_model, only ;", "use my_model, only name"]: with pytest.raises(NoMatchError) as excinfo: - ast = Use_Stmt(line) + _ = Use_Stmt(line) assert "Use_Stmt: '{0}'".format(line) in str(excinfo) # match() Internal errors From 00141af3e947b122f7b9e9a65b36e94518250063 Mon Sep 17 00:00:00 2001 From: Rupert Ford Date: Fri, 23 Nov 2018 04:00:51 -0800 Subject: [PATCH 5/7] pr #122. Addressing reviewers comments. --- src/fparser/two/Fortran2003.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/fparser/two/Fortran2003.py b/src/fparser/two/Fortran2003.py index d1c02e2f..111729d8 100644 --- a/src/fparser/two/Fortran2003.py +++ b/src/fparser/two/Fortran2003.py @@ -7315,8 +7315,9 @@ def match(string): # Expected ':' but there is nothing after the 'ONLY' # specification return - # Missing ':' after ', ONLY' specification if line[0] != ':': + # Expected ':' but there is a different character + # after the 'ONLY' specification return line = line[1:].lstrip() if not line: @@ -7340,11 +7341,11 @@ def tostr(self): "Use_Stmt.tostr(). 'Items' should be of size 5 but found " "'{0}'.".format(len(self.items))) if not self.items[2]: - raise InternalError("Use_Stmt.tostr(). 'Items' entry 2 should " - "be a module name but it is empty") + raise InternalError("Use_Stmt.tostr(). 'Items' entry 2 should " + "be a module name but it is empty") if self.items[3] is None: - raise InternalError("Use_Stmt.tostr(). 'Items' entry 3 should " - "be a string but found 'None'") + raise InternalError("Use_Stmt.tostr(). 'Items' entry 3 should " + "be a string but found 'None'") usestmt = 'USE' # Add optional Module_Nature ("INTRINSIC" or "NON_INTRINSIC") # followed by a double colon to "USE" statement From e1b8fe187693a21d11460ebefd4454fc9dc30527 Mon Sep 17 00:00:00 2001 From: TeranIvy Date: Fri, 23 Nov 2018 15:46:15 +0000 Subject: [PATCH 6/7] PR #122, I #118: Updated changelog, ready for merge to master. --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30ff6615..78f4a258 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ Modifications by (in alphabetical order): * A. R. Porter, Science & Technology Facilities Council, UK * P. Vitt, University of Siegen, Germany +23/11/2018 PR #122 for issue #118. Bug fix for reporting invalid + Fortran when parsing "use module_name, only:" in fparser2. + 14/11/2018 PR #121 for #120. Fixes bug in BlockBase such that name matches are no longer case sensitive. Improves error handling. From 65e5280f832b541a46da5f8cff88f12800d512f0 Mon Sep 17 00:00:00 2001 From: TeranIvy Date: Fri, 23 Nov 2018 16:08:42 +0000 Subject: [PATCH 7/7] PR #122, I #118: Updated changelog, ready for merge to master. --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e079f6c9..2d064a53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ Modifications by (in alphabetical order): * A. R. Porter, Science & Technology Facilities Council, UK * P. Vitt, University of Siegen, Germany +23/11/2018 PR #122 for issue #118. Bug fix for reporting invalid + Fortran when parsing `use module_name, only:` in fparser2. + 21/11/2018 PR #127 for #126. Adds get_child function to help AST traversal. 19/11/2018 PR #124 for #112. Bug fix - spaces within names are now