Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#2853 Allow setting of the Fortran standard in the config file. #2855

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions config/psyclone.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ OCL_DEVICES_PER_NODE = 1
# and will not produce a warning message if they cannot be found.
IGNORE_MODULES = netcdf, mpi

# The Fortran standard to use when parsing files. This value is passed
# to fparser. Valid values are f2003 and f2008
FORTRAN_STANDARD = f2008

# Settings specific to the LFRic API
# ==================================
[lfric]
Expand Down
4 changes: 4 additions & 0 deletions doc/user_guide/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ section e.g.:
REPROD_PAD_SIZE = 8
PSYIR_ROOT_NAME = psyir_tmp
VALID_PSY_DATA_PREFIXES = profile, extract
FORTRAN_STANDARD = f2008

and an optional API specific section, for example for the
``lfric`` section:
Expand Down Expand Up @@ -176,6 +177,9 @@ BACKEND_CHECKS_ENABLED Optional (defaults to True). Whether or not the PSyIR
backend should validate the tree that it is passed.
Can be overridden by the ``--backend`` command-line
flag (see :ref:`backend-options`).
FORTRAN_STANDARD Optional (defaults to f2008). The Fortran standard str
that should be used by fparser. Valid values are
f2003 and f2008.
======================= ======================================================= ===========

Common Sections
Expand Down
19 changes: 19 additions & 0 deletions src/psyclone/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,9 @@ def __init__(self):
# checks which can be useful in the case of unimplemented features.
self._backend_checks_enabled = True

# The Fortran standard that fparser should use
self._fortran_standard = None

# -------------------------------------------------------------------------
def load(self, config_file=None):
'''Loads a configuration file.
Expand Down Expand Up @@ -369,6 +372,16 @@ def load(self, config_file=None):
for module_name in ignore_modules:
mod_manager.add_ignore_module(module_name)

# Get the Fortran standard to use:
self._fortran_standard = \
self._config['DEFAULT'].get("FORTRAN_STANDARD", "f2008").lower()
valid_standard = ["f2003", "f2008"]
if self._fortran_standard not in valid_standard:
raise ConfigurationError(f"Invalid Fortran standard "
f"'{self._fortran_standard}' specified "
f"in config file. Must be one of"
f"{valid_standard}")

# Set the flag that the config file has been loaded now.
Config._HAS_CONFIG_BEEN_INITIALISED = True

Expand Down Expand Up @@ -698,6 +711,12 @@ def ocl_devices_per_node(self):
:rtype: int'''
return self._ocl_devices_per_node

@property
def fortran_standard(self) -> str:
''':returns: The Fortran standard to be used by fparser.
'''
return self._fortran_standard

def get_default_keys(self):
'''Returns all keys from the default section.
:returns list: List of all keys of the default section as strings.
Expand Down
4 changes: 3 additions & 1 deletion src/psyclone/domain/gocean/kernel/psyir.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
# -----------------------------------------------------------------------------
# Author: R. W. Ford, STFC Daresbury Lab
# Modified: A. R. Porter and S. Siso, STFC Daresbury Lab
# J. Henrichs, Bureau of Meteorology

'''This module contains PSyclone Kernel-layer-specific PSyIR classes
for the GOcean API.
Expand Down Expand Up @@ -252,7 +253,8 @@ def create_from_fortran_string(fortran_string):
kernel_metadata = GOceanKernelMetadata()

# Ensure the Fortran2003 parser is initialised.
_ = ParserFactory().create(std="f2003")
std = Config.get().fortran_standard
_ = ParserFactory().create(std=std)
reader = FortranStringReader(fortran_string)
try:
spec_part = Fortran2003.Derived_Type_Def(reader)
Expand Down
5 changes: 4 additions & 1 deletion src/psyclone/domain/lfric/kernel/common_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
from fparser.two.parser import ParserFactory
from fparser.two.utils import NoMatchError, FortranSyntaxError

from psyclone.configuration import Config


# TODO issue #1886. This class and its subclasses may have
# commonalities with the GOcean metadata processing.
Expand Down Expand Up @@ -120,7 +122,8 @@ def create_fparser2(fortran_string, encoding):
expected form.

'''
_ = ParserFactory().create(std="f2003")
std = Config.get().fortran_standard
_ = ParserFactory().create(std=std)
reader = FortranStringReader(fortran_string)
match = True
try:
Expand Down
5 changes: 3 additions & 2 deletions src/psyclone/parse/file_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,10 +412,11 @@ def get_fparser_tree(
return self._fparser_tree

try:
config = Config.get()
reader = FortranStringReader(
source_code, include_dirs=Config.get().include_paths
source_code, include_dirs=config.include_paths
)
parser = ParserFactory().create(std="f2008")
parser = ParserFactory().create(std=config.fortran_standard)
self._fparser_tree = parser(reader)

except Exception as err:
Expand Down
10 changes: 5 additions & 5 deletions src/psyclone/parse/kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@

import psyclone.expression as expr
from psyclone.errors import InternalError
from psyclone.configuration import LFRIC_API_NAMES, GOCEAN_API_NAMES
from psyclone.configuration import Config, LFRIC_API_NAMES, GOCEAN_API_NAMES
from psyclone.parse.utils import check_api, check_line_length, ParseError


Expand Down Expand Up @@ -912,8 +912,8 @@ def get_integer_variable(self, name):
:raises ParseError: if the RHS of the assignment is not a Name.

'''
# Ensure the Fortran2008 parser is initialised
_ = ParserFactory().create(std="f2008")
# Ensure the Fortran parser is initialised
_ = ParserFactory().create(std=Config.get().fortran_standard)
# Fortran is not case sensitive so nor is our matching
lower_name = name.lower()

Expand Down Expand Up @@ -955,8 +955,8 @@ def get_integer_array(self, name):
does not match the extent of the array.

'''
# Ensure the classes are setup for the Fortran2008 parser
_ = ParserFactory().create(std="f2008")
# Ensure the classes are setup for the Fortran parser
_ = ParserFactory().create(std=Config.get().fortran_standard)
# Fortran is not case sensitive so nor is our matching
lower_name = name.lower()

Expand Down
2 changes: 1 addition & 1 deletion src/psyclone/parse/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@ def parse_fp2(filename):
:raises ParseError: if the file could not be parsed.

'''
parser = ParserFactory().create(std="f2008")
# We get the directories to search for any Fortran include files from
# our configuration object.
config = Config.get()
Expand All @@ -124,6 +123,7 @@ def parse_fp2(filename):
raise ParseError(
f"algorithm.py:parse_fp2: Failed to parse file '{filename}'. "
f"Error returned was ' {error} '.") from error
parser = ParserFactory().create(std=config.fortran_standard)
try:
parse_tree = parser(reader)
except FortranSyntaxError as msg:
Expand Down
3 changes: 2 additions & 1 deletion src/psyclone/psyGen.py
Original file line number Diff line number Diff line change
Expand Up @@ -1577,7 +1577,8 @@ def ast(self):
# Use the fparser1 AST to generate Fortran source
fortran = self._module_code.tofortran()
# Create an fparser2 Fortran2008 parser
my_parser = parser.ParserFactory().create(std="f2008")
std = Config.get().fortran_standard
my_parser = parser.ParserFactory().create(std=std)
# Parse that Fortran using our parser
reader = FortranStringReader(fortran)
self._fp2_ast = my_parser(reader)
Expand Down
4 changes: 3 additions & 1 deletion src/psyclone/psyir/frontend/fortran.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
# -----------------------------------------------------------------------------
# Author: S. Siso, STFC Daresbury Lab
# Modifications: A. R. Porter, N. Nobre and R. W. Ford, STFC Daresbury Lab
# J. Henrichs, Bureau of Meteorology
# -----------------------------------------------------------------------------

''' This module provides the PSyIR Fortran front-end.'''
Expand Down Expand Up @@ -84,7 +85,8 @@ def __init__(self, free_form: bool = True, ignore_comments: bool = True,
last_comments_as_codeblocks: bool = False,
resolve_modules: Union[bool, List[str]] = False):
if not self._parser:
self._parser = ParserFactory().create(std="f2008")
std = Config.get().fortran_standard
self._parser = ParserFactory().create(std=std)
self._free_form = free_form
if ignore_comments and not ignore_directives:
raise ValueError(
Expand Down
5 changes: 3 additions & 2 deletions src/psyclone/psyir/frontend/fparser2.py
Original file line number Diff line number Diff line change
Expand Up @@ -3445,7 +3445,7 @@ def _add_target_attribute(var_name, table):
f"subroutine dummy()\n"
f" {datatype.declaration}\n"
f"end subroutine\n")
parser = ParserFactory().create(std="f2008")
parser = ParserFactory().create(std=Config.get().fortran_standard)
reader = FortranStringReader(dummy_code)
fp2_ast = parser(reader)
type_decl_stmt = fp2_ast.children[0].children[1].children[0]
Expand Down Expand Up @@ -3695,7 +3695,8 @@ def _create_select_type(

# Parse the the created Fortran text to an fparser2 tree and
# store the resulting tree in a PSyIR CodeBlock.
parser = ParserFactory().create(std="f2008")
std = Config.get().fortran_standard
parser = ParserFactory().create(std=std)
reader = FortranStringReader(code)
fp2_program = parser(reader)
# Ignore the program part of the fparser2 tree
Expand Down
2 changes: 1 addition & 1 deletion src/psyclone/psyir/nodes/psy_data_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -774,7 +774,7 @@ def gen_type_bound_call(typename, methodname, argument_list=None,
argument_str += ",".join([str(arg) for arg in argument_list])
argument_str += ")"

ParserFactory().create(std="f2008")
ParserFactory().create(std=Config.get().fortran_standard)
reader = FortranStringReader(
f"CALL {typename}%{methodname}{argument_str}")
# Tell the reader that the source is free format
Expand Down
7 changes: 4 additions & 3 deletions src/psyclone/psyir/symbols/datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
from enum import Enum
from typing import Any, Union

from psyclone.configuration import Config
from psyclone.errors import InternalError
from psyclone.psyir.commentable_mixin import CommentableMixin
from psyclone.psyir.symbols.data_type_symbol import DataTypeSymbol
Expand Down Expand Up @@ -219,7 +220,7 @@ def type_text(self):
string_reader = FortranStringReader(self._declaration)
# Set reader to free format.
string_reader.set_format(FortranFormat(True, False))
ParserFactory().create(std="f2008")
ParserFactory().create(std=Config.get().fortran_standard)
try:
ptree = Fortran2003.Specification_Part(
string_reader)
Expand Down Expand Up @@ -641,8 +642,8 @@ def _validate_shape(self, extents):
:type extents: List[
:py:class:`psyclone.psyir.symbols.ArrayType.Extent` | int
| :py:class:`psyclone.psyir.nodes.DataNode` |
Tuple[int | :py:class:`psyclone.psyir.nodes.DataNode |
:py:class:`psyclone.psyir.symbols.ArrayType.Extent]]
Tuple[int | :py:class:`psyclone.psyir.nodes.DataNode` |
:py:class:`psyclone.psyir.symbols.ArrayType.Extent`]]

:raises TypeError: if extents is not a list.
:raises TypeError: if one or more of the supplied extents is a
Expand Down
35 changes: 34 additions & 1 deletion src/psyclone/tests/config_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
# -----------------------------------------------------------------------------
# Author: A. R. Porter, STFC Daresbury Lab
# Modified: I. Kavcic and O. Brunt, Met Office,
# R. W. Ford, STFC Daresbury Lab
# R. W. Ford, STFC Daresbury Lab
# J. Henrichs, Bureau of Meteorology
# N. Nobre, STFC Daresbury Lab
# S. Siso, STFC Daresbury Lab
Expand Down Expand Up @@ -74,6 +74,7 @@
OCL_DEVICES_PER_NODE = 1
IGNORE_MODULES = netcdf, mpi
BACKEND_CHECKS_ENABLED = false
FORTRAN_STANDARD = f2003
[lfric]
access_mapping = gh_read: read, gh_write: write, gh_readwrite: readwrite,
gh_inc: inc, gh_sum: sum
Expand Down Expand Up @@ -766,3 +767,35 @@ def test_aliased_api_names(tmpdir):
assert "lfric" in config._api_conf
assert "dynamo0.3" not in config._api_conf
assert config._api_conf['lfric'].num_any_space == 13


def test_fortran_standard(tmpdir):
'''Test the handling of the Fortran standard. The dummy
config content here specifies f2003 in the config file,
so we can check that we get the expected default of f2008
if it is removed.
'''
# Check that we read the expected value in the config file:
config_file = tmpdir.join("config")
content = _CONFIG_CONTENT
config = get_config(config_file, content)
assert config.fortran_standard == "f2003"
# Remove the Fortran_standard from the config file, in
# which case we must get the default of f2008:
content = re.sub(r"^FORTRAN_STANDARD = f2003$", "",
content, flags=re.MULTILINE)
config = get_config(config_file, content)
assert config.fortran_standard == "f2008"

# Check that an invalid Fortran standard raises an exception.
content = _CONFIG_CONTENT
# Set an invalid Fortran_standard
content = re.sub(r"^FORTRAN_STANDARD = f2003",
"FORTRAN_STANDARD = invalid",
content, flags=re.MULTILINE)
with pytest.raises(ConfigurationError) as err:
get_config(config_file, content)

assert ("PSyclone configuration error: Invalid Fortran standard 'invalid' "
"specified in config file. Must be one of['f2003', 'f2008']"
in str(err.value))
Loading