Skip to content

Commit

Permalink
Merge pull request #311 from stfc/310_allow_no_scope
Browse files Browse the repository at this point in the history
(Closes #310) allow for current_scope to be None and extend tests
  • Loading branch information
rupertford authored Mar 15, 2022
2 parents e0b14f8 + 03ac629 commit c44c53c
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 85 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Modifications by (in alphabetical order):
* P. Vitt, University of Siegen, Germany
* A. Voysey, UK Met Office

15/03/2022 PR #311 for #310. Allows symbol table scope to be None.

08/12/2021 PR #293 towards #201. Initial symbol-table support added.

02/12/2021 PR #305 for #304. Fix failing build of documentation on RTD.
Expand Down
26 changes: 15 additions & 11 deletions src/fparser/two/Fortran2003.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env python

# Modified work Copyright (c) 2017-2021 Science and Technology
# Modified work Copyright (c) 2017-2022 Science and Technology
# Facilities Council.
# Original work Copyright (c) 1999-2008 Pearu Peterson

Expand Down Expand Up @@ -2668,7 +2668,7 @@ def match(string):
'''
Attempts to match the supplied string as a type declaration. If the
match is successful the declared symbols are added to the symbol table
of the current scope.
of the current scope (if there is one).
:param str string: the string to match.
Expand All @@ -2684,7 +2684,7 @@ def match(string):
# symbol table of the current scoping region.
table = SYMBOL_TABLES.current_scope

if isinstance(result[0], Intrinsic_Type_Spec):
if table and isinstance(result[0], Intrinsic_Type_Spec):
# We have a definition of symbol(s) of intrinsic type
decl_list = walk(result, Entity_Decl)
for decl in decl_list:
Expand Down Expand Up @@ -9293,7 +9293,8 @@ class Use_Stmt(StmtBase): # pylint: disable=invalid-name
def match(string):
'''
Wrapper for the match method that captures any successfully-matched
use statements in the symbol table associated with the current scope.
use statements in the symbol table associated with the current scope
(if there is one).
:param str string: Fortran code to check for a match.
Expand All @@ -9308,12 +9309,13 @@ def match(string):
result = Use_Stmt._match(string)
if result:
table = SYMBOL_TABLES.current_scope
only_list = None
# TODO #201 we currently ignore any symbol renaming here
if isinstance(result[4], Only_List):
names = walk(result[4], Name)
only_list = [name.string for name in names]
table.add_use_symbols(str(result[2]), only_list)
if table:
only_list = None
# TODO #201 we currently ignore any symbol renaming here
if isinstance(result[4], Only_List):
names = walk(result[4], Name)
only_list = [name.string for name in names]
table.add_use_symbols(str(result[2]), only_list)

return result

Expand Down Expand Up @@ -10281,7 +10283,9 @@ def match(string):
table.lookup(function_name)
# We found a matching name so refuse to match this intrinsic.
return None
except KeyError:
except (KeyError, AttributeError):
# There is either no matching name in the table or we have
# no current scoping region.
pass

# This if/else will not be needed once issue #170 has been
Expand Down
26 changes: 13 additions & 13 deletions src/fparser/two/tests/fortran2003/test_intrinsics.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -----------------------------------------------------------------------------
# BSD 3-Clause License
#
# Copyright (c) 2019-2021, Science and Technology Facilities Council.
# Copyright (c) 2019-2022, Science and Technology Facilities Council.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -105,18 +105,26 @@ def test_intrinsic_name_case_insensitive(f2003_create):
# class intrinsic_function_reference


@pytest.mark.usefixtures("fake_symbol_table")
def test_intrinsic_function_reference_generic(f2003_create):
@pytest.mark.usefixtures("f2003_create")
def test_intrinsic_function_reference_generic():
'''Test that class Intrinsic_Function_Reference correctly matches a
generic intrinsic with a valid number of arguments.
generic intrinsic with a valid number of arguments. We test both
with and without the existance of a symbol table.
'''
result = Intrinsic_Function_Reference("SIN(A)")
assert isinstance(result, Intrinsic_Function_Reference)
assert str(result) == "SIN(A)"
# Repeat when there is a scoping region.
SYMBOL_TABLES.enter_scope("test_scope")
result = Intrinsic_Function_Reference("SIN(A)")
assert isinstance(result, Intrinsic_Function_Reference)
assert str(result) == "SIN(A)"
table = SYMBOL_TABLES.current_scope
assert "sin" not in table._data_symbols
SYMBOL_TABLES.exit_scope()


@pytest.mark.usefixtures("fake_symbol_table")
def test_intrinsic_function_reference(f2003_create):
'''Test that class Intrinsic_Function_Reference correctly matches a
specific intrinsic with a valid number of arguments.
Expand All @@ -127,7 +135,6 @@ def test_intrinsic_function_reference(f2003_create):
assert str(result) == "DSIN(A)"


@pytest.mark.usefixtures("fake_symbol_table")
def test_intrinsic_function_nomatch(f2003_create):
'''Test that class Intrinsic_Function_Reference raises the expected
exception if there is no match.
Expand All @@ -137,7 +144,6 @@ def test_intrinsic_function_nomatch(f2003_create):
_ = Intrinsic_Function_Reference("NO_MATCH(A)")


@pytest.mark.usefixtures("fake_symbol_table")
def test_intrinsic_function_reference_multi_args(f2003_create):
'''Test that class Intrinsic_Function_Reference correctly matches a
generic intrinsic which accepts more than one argument (two in
Expand All @@ -149,7 +155,6 @@ def test_intrinsic_function_reference_multi_args(f2003_create):
assert str(result) == "MATMUL(A, B)"


@pytest.mark.usefixtures("fake_symbol_table")
def test_intrinsic_function_reference_zero_args(f2003_create):
'''Test that class Intrinsic_Function_Reference correctly matches a
generic intrinsic which accepts zero arguments.
Expand All @@ -160,7 +165,6 @@ def test_intrinsic_function_reference_zero_args(f2003_create):
assert str(result) == "COMMAND_ARGUMENT_COUNT()"


@pytest.mark.usefixtures("fake_symbol_table")
def test_intrinsic_function_reference_range_args(f2003_create):
'''Test that class Intrinsic_Function_Reference correctly matches a
generic intrinsic which accepts a range of number of arguments.
Expand All @@ -172,7 +176,6 @@ def test_intrinsic_function_reference_range_args(f2003_create):
assert str(result) == "SYSTEM_CLOCK({0})".format(args)


@pytest.mark.usefixtures("fake_symbol_table")
def test_intrinsic_function_reference_unlimited_args(f2003_create):
'''Test that class Intrinsic_Function_Reference correctly matches a
generic intrinsic which accepts an unlimitednumber of arguments.
Expand All @@ -184,7 +187,6 @@ def test_intrinsic_function_reference_unlimited_args(f2003_create):
assert str(result) == "MAX({0})".format(args)


@pytest.mark.usefixtures("fake_symbol_table")
def test_intrinsic_function_reference_error1(f2003_create):
'''Test that class Intrinsic_Function_Reference raises the expected
exception when the valid min and max args are equal (2 in this case)
Expand All @@ -202,7 +204,6 @@ def test_intrinsic_function_reference_error1(f2003_create):
"" in str(excinfo.value))


@pytest.mark.usefixtures("fake_symbol_table")
def test_intrinsic_function_reference_error2(f2003_create):
'''Test that class Intrinsic_Function_Reference raises the expected
exception when the valid min args is less than the valid max args
Expand All @@ -220,7 +221,6 @@ def test_intrinsic_function_reference_error2(f2003_create):
"" in str(excinfo.value))


@pytest.mark.usefixtures("fake_symbol_table")
def test_intrinsic_function_reference_error3(f2003_create):
'''Test that class Intrinsic_Function_Reference raises the expected
exception when the number of arguments is unlimited.
Expand Down
116 changes: 116 additions & 0 deletions src/fparser/two/tests/fortran2003/test_type_decl_stmt_r501.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Copyright (c) 2022 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.

'''
pytest tests for Fortran2003 rule R501 - Type Declaration Statement.
TODO #318 - these tests need extending to fully cover the
Fortran2003.Type_Declaration_Stmtclass. They will also need to be
extended as part of #259 as that will add support for the various
constraints that apply to R501.
'''

import pytest
from fparser.two import Fortran2003
from fparser.two.symbol_table import SYMBOL_TABLES


@pytest.mark.usefixtures("f2003_create")
@pytest.mark.parametrize("table_name", ["", "test_mod"])
def test_type_declaration_stmt(table_name):
''' Various tests for the type declaration statement (R501). We test both
with and without an existing scoping region. '''
if table_name:
SYMBOL_TABLES.enter_scope(table_name)
table = SYMBOL_TABLES.current_scope

tcls = Fortran2003.Type_Declaration_Stmt
obj = tcls('integer a')
assert isinstance(obj, tcls), repr(obj)
assert str(obj) == 'INTEGER :: a'
assert (repr(obj) ==
"Type_Declaration_Stmt(Intrinsic_Type_Spec('INTEGER', "
"None), None, Entity_Decl_List(',', (Entity_Decl(Name('a'), None, "
"None, None),)))")
if table:
assert "a" in table._data_symbols

obj = tcls('integer ,dimension(2):: b*3')
assert isinstance(obj, tcls), repr(obj)
assert str(obj) == 'INTEGER, DIMENSION(2) :: b*3'
if table:
assert "b" in table._data_symbols

obj = tcls('real c')
assert isinstance(obj, tcls), repr(obj)
assert str(obj) == 'REAL :: c'
assert (repr(obj) ==
"Type_Declaration_Stmt(Intrinsic_Type_Spec('REAL', None), None, "
"Entity_Decl_List(',', (Entity_Decl(Name('c'), None, None, "
"None),)))")
if table:
assert "c" in table._data_symbols

obj = tcls('REAL D( LDA, * ), E( LDB, * )')
assert isinstance(obj, tcls), repr(obj)
assert str(obj) == 'REAL :: D(LDA, *), E(LDB, *)'
if table:
assert "d" in table._data_symbols
assert "e" in table._data_symbols

obj = tcls('DOUBLE PRECISION ALPHA, BETA')
assert isinstance(obj, tcls), repr(obj)
assert str(obj) == 'DOUBLE PRECISION :: ALPHA, BETA'
if table:
assert "alpha" in table._data_symbols
assert "beta" in table._data_symbols

obj = tcls('logical,parameter:: T=.true.')
assert isinstance(obj, tcls), repr(obj)
assert str(obj) == 'LOGICAL, PARAMETER :: T = .TRUE.'
if table:
assert "t" in table._data_symbols

obj = tcls('character(n),private:: x(n)')
assert isinstance(obj, tcls), repr(obj)
assert str(obj) == 'CHARACTER(LEN = n), PRIVATE :: x(n)'
if table:
assert "x" in table._data_symbols

obj = tcls('character(lenmax),private:: y(n)')
assert isinstance(obj, tcls), repr(obj)
assert str(obj) == 'CHARACTER(LEN = lenmax), PRIVATE :: y(n)'
if table:
assert "y" in table._data_symbols
Loading

0 comments on commit c44c53c

Please sign in to comment.