Skip to content

Commit

Permalink
Merge pull request #418 from stfc/313_permit_intrinsic_shadowing
Browse files Browse the repository at this point in the history
(Closes #313) permit intrinsic shadowing
  • Loading branch information
sergisiso authored Jan 25, 2024
2 parents 5c3808d + f7ebb67 commit 0410062
Show file tree
Hide file tree
Showing 9 changed files with 519 additions and 106 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Modifications by (in alphabetical order):
* P. Vitt, University of Siegen, Germany
* A. Voysey, UK Met Office

25/01/2024 PR #418 for #313. Allow intrinsic shadowing and improve fparser symbol table.

11/01/2024 PR #439 for #432. Fix RTD build and clean up setuptools config.

03/10/2023 PR #431 for #430. Fixes bug in WHERE handling in fparser1.
Expand Down
1 change: 1 addition & 0 deletions doc/source/developers_guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ corresponding dictionary entries are instances of the `SymbolTable`
class:

.. autoclass:: fparser.two.symbol_table.SymbolTable
:members:

The entries in these tables are instances of the named tuple,
`SymbolTable.Symbol` which currently has the properties:
Expand Down
136 changes: 72 additions & 64 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-2023 Science and Technology
# Modified work Copyright (c) 2017-2024 Science and Technology
# Facilities Council.
# Original work Copyright (c) 1999-2008 Pearu Peterson

Expand Down Expand Up @@ -12333,77 +12333,85 @@ def match(string):
:param str string: the string to match with the pattern rule.
:return: a tuple of size 2 containing the name of the \
intrinsic and its arguments if there is a match, or None if \
there is not.
:rtype: (:py:class:`fparser.two.Fortran2003.Intrinsic_Name`, \
:py:class:`fparser.two.Fortran2003.Actual_Arg_Spec_List`) or \
NoneType
:return: a tuple of size 2 containing the name of the intrinsic
and its arguments if there is a match, or None if there is not.
:rtype: Tuple[:py:class:`fparser.two.Fortran2003.Intrinsic_Name`,
:py:class:`fparser.two.Fortran2003.Actual_Arg_Spec_List`] | NoneType
:raises InternalSyntaxError: If the number of arguments \
provided does not match the number of arguments expected by \
the intrinsic.
:raises InternalSyntaxError: If the number of arguments provided does
not match the number of arguments expected by the intrinsic and
there are no wildcard imports that could be bringing a routine
(that overrides it) into scope.
"""
result = CallBase.match(Intrinsic_Name, Actual_Arg_Spec_List, string)
if result:
# There is a match so check the number of args provided
# matches the number of args expected by the intrinsic.
function_name = str(result[0])
function_args = result[1]
if not result:
return None

# Check that that this name is not being shadowed (i.e. overridden)
# by a symbol in scope at this point.
table = SYMBOL_TABLES.current_scope
try:
table.lookup(function_name)
# We found a matching name so refuse to match this intrinsic.
return None
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
# addressed.
if isinstance(function_args, Actual_Arg_Spec_List):
nargs = len(function_args.items)
elif function_args is None:
nargs = 0
else:
nargs = 1
# There is a match so check the number of args provided
# matches the number of args expected by the intrinsic.
function_name = str(result[0])
function_args = result[1]

# Check that that this name is not being shadowed (i.e. overridden)
# by a symbol in scope at this point.
table = SYMBOL_TABLES.current_scope
try:
table.lookup(function_name)
# We found a matching name so refuse to match this intrinsic.
return None
except (KeyError, AttributeError):
# There is either no matching name in the table or we have
# no current scoping region.
pass

nargs = 0 if function_args is None else len(function_args.items)

if function_name in Intrinsic_Name.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]
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"]

# None indicates an unlimited number of arguments
if max_nargs is None:
if nargs < min_nargs:
if table and not table.all_symbols_resolved:
# Wrong number of arguments to be an intrinsic so it must
# be a call to a routine being brought into scope from
# elsewhere.
return None

if function_name in Intrinsic_Name.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]
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"]

if max_nargs is None:
if nargs < min_nargs:
# None indicates an unlimited number of arguments
raise InternalSyntaxError(
"Intrinsic '{0}' expects at least {1} args but found "
"{2}.".format(function_name, min_nargs, nargs)
)
# The number of arguments is valid. Return here as
# further tests will fail due to max_args being
# None.
return result
if min_nargs == max_nargs and nargs != min_nargs:
raise InternalSyntaxError(
"Intrinsic '{0}' expects {1} arg(s) but found {2}."
"".format(function_name, min_nargs, nargs)
)
if min_nargs < max_nargs and (nargs < min_nargs or nargs > max_nargs):
raise InternalSyntaxError(
"Intrinsic '{0}' expects between {1} and {2} args but "
"found {3}.".format(function_name, min_nargs, max_nargs, nargs)
"Intrinsic '{0}' expects at least {1} args but found "
"{2}.".format(function_name, min_nargs, nargs)
)
# The number of arguments is valid. Return here as
# further tests will fail due to max_args being
# None.
return result
if min_nargs == max_nargs and nargs != min_nargs:
if table and not table.all_symbols_resolved:
return None
raise InternalSyntaxError(
"Intrinsic '{0}' expects {1} arg(s) but found {2}."
"".format(function_name, min_nargs, nargs)
)
if min_nargs < max_nargs and (nargs < min_nargs or nargs > max_nargs):
if table and not table.all_symbols_resolved:
# Wrong number of arguments to be an intrinsic so it must
# be a call to a routine being brought into scope from
# elsewhere.
return None

raise InternalSyntaxError(
"Intrinsic '{0}' expects between {1} and {2} args but "
"found {3}.".format(function_name, min_nargs, max_nargs, nargs)
)
return result


Expand Down
Loading

0 comments on commit 0410062

Please sign in to comment.