Skip to content

Commit

Permalink
Merge pull request #417 from stfc/411_move_fortran2008_file
Browse files Browse the repository at this point in the history
use __init__ to import Fortran2008 classes (towards #411)
  • Loading branch information
arporter authored Jun 12, 2023
2 parents ca11947 + 9370504 commit 38364a9
Show file tree
Hide file tree
Showing 5 changed files with 216 additions and 71 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ Modifications by (in alphabetical order):
* P. Vitt, University of Siegen, Germany
* A. Voysey, UK Met Office

12/06/2023 PR #417 towards #411. Moves Fortran2008.py into a 'Fortran2008'
directory and moves the associated class generation into an '__init__.py'
in that directory.

16/05/2023 PR #414 for #412. Bug fix for disappearing line when parsing
include files.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,6 @@
# module is first imported.
# pylint: disable=exec-used
# pylint: disable=unused-import
import inspect
import sys

from fparser.common.splitline import string_replace_map, splitparen
from fparser.two import pattern_tools as pattern
from fparser.two.utils import (
Expand Down Expand Up @@ -394,6 +391,10 @@ def match(string):
:py:class:`fparser.two.Fortran2003.Component_Decl_List`)
"""
# Avoid circular dependencies by importing here.
# pylint: disable=import-outside-toplevel
from fparser.two.Fortran2008 import Component_Attr_Spec_List

return Type_Declaration_StmtBase.match(
Declaration_Type_Spec, Component_Attr_Spec_List, Component_Decl_List, string
)
Expand Down Expand Up @@ -461,6 +462,10 @@ def get_attr_spec_list_cls():
This overwrites the Fortran 2003 type with the Fortran 2008 variant.
"""
# Avoid circular dependencies by importing here.
# pylint: disable=import-outside-toplevel
from fparser.two.Fortran2008 import Attr_Spec_List

return Attr_Spec_List


Expand Down Expand Up @@ -652,6 +657,10 @@ def match(string):
:py:class:`fparser.two:Fortran2008.Lower_Cobound` or `None`)
"""
# Avoid circular dependencies by importing here.
# pylint: disable=import-outside-toplevel
from fparser.two.Fortran2008 import Coshape_Spec_List

if not string.endswith("*"):
return None
line = string[:-1].rstrip()
Expand Down Expand Up @@ -821,6 +830,10 @@ def alloc_opt_list(cls):
:returns: the Fortran2008 flavour of Alloc_Opt_List.
:rtype: type
"""
# Avoid circular dependencies by importing here.
# pylint: disable=import-outside-toplevel
from fparser.two.Fortran2008 import Alloc_Opt_List

return Alloc_Opt_List


Expand Down Expand Up @@ -1147,6 +1160,10 @@ def match(fstring):
:py:class:`fparser.two.Fortran2008.Submodule_Name`]]
"""
# Avoid circular dependencies by importing here.
# pylint: disable=import-outside-toplevel
from fparser.two.Fortran2008 import Submodule_Name

# First look for "SUBMODULE"
name = "SUBMODULE"
if fstring[: len(name)].upper() != name:
Expand Down Expand Up @@ -1218,6 +1235,10 @@ def match(fstring):
is a match or `None` if there is no match
"""
# Avoid circular dependencies by importing here.
# pylint: disable=import-outside-toplevel
from fparser.two.Fortran2008 import Submodule_Name

return EndStmtBase.match("SUBMODULE", Submodule_Name, fstring)

def get_name(self): # C1114
Expand Down Expand Up @@ -1259,6 +1280,10 @@ def match(fstring):
is a match or `None` if there is no match
"""
# Avoid circular dependencies by importing here.
# pylint: disable=import-outside-toplevel
from fparser.two.Fortran2008 import Ancestor_Module_Name, Parent_SubModule_Name

split_string = fstring.split(":")
len_split_string = len(split_string)
lhs_name = split_string[0].lstrip().rstrip()
Expand Down Expand Up @@ -1300,8 +1325,10 @@ def match(string):
:rtype: Optional[:py:class:`fparser.two.Fortran2008.Open_Stmt]
"""
# The Connect_Spec_List class is generated automatically
# by code at the end of this module
# Avoid circular dependencies by importing here.
# pylint: disable=import-outside-toplevel
from fparser.two.Fortran2008 import Connect_Spec_List

obj = CALLBase.match("OPEN", Connect_Spec_List, string, require_rhs=True)
if not obj:
return None
Expand Down Expand Up @@ -1396,6 +1423,14 @@ def match(string):
supplied string is not a match
:rtype: Optional[Tuple[str, Any]]
"""
# Avoid circular dependencies by importing here.
# pylint: disable=import-outside-toplevel
from fparser.two.Fortran2008 import (
Scalar_Default_Char_Expr,
Scalar_Int_Variable,
Scalar_Int_Expr,
)

if "=" not in string:
# The only argument which need not be named is the unit number
return "UNIT", File_Unit_Number(string)
Expand Down Expand Up @@ -1559,6 +1594,10 @@ def match(string):
Optional[:py:class:`fparser.two.Fortran2003.Block_Construct_Name`]]]
"""
# Avoid circular dependencies by importing here.
# pylint: disable=import-outside-toplevel
from fparser.two.Fortran2008 import Block_Construct_Name

return EndStmtBase.match(
"BLOCK", Block_Construct_Name, string, require_stmt_type=True
)
Expand Down Expand Up @@ -1665,6 +1704,10 @@ def match(string):
Optional[:py:class:`fparser.two.Fortran2003.Critical_Construct_Name`]]]
"""
# Avoid circular dependencies by importing here.
# pylint: disable=import-outside-toplevel
from fparser.two.Fortran2008 import Critical_Construct_Name

return EndStmtBase.match(
"CRITICAL", Critical_Construct_Name, string, require_stmt_type=True
)
Expand All @@ -1691,6 +1734,10 @@ def match(string):
:py:class:`fparser.two.Fortran2003.Procedure_Name_List`]]]
"""
# Avoid circular dependencies by importing here.
# pylint: disable=import-outside-toplevel
from fparser.two.Fortran2008 import Procedure_Name_List

line = string.lstrip()
optional_module = None
if line[:6].upper() == "MODULE":
Expand All @@ -1716,67 +1763,3 @@ def tostr(self):
if self.items[2]:
result = f"{result} ::"
return f"{result} {self.items[0]}"


#
# GENERATE Scalar_, _List, _Name CLASSES
#


ClassType = type(Base)
_names = dir()
for clsname in _names:
new_cls = eval(clsname)
if not (
isinstance(new_cls, ClassType)
and issubclass(new_cls, Base)
and not new_cls.__name__.endswith("Base")
):
continue

names = getattr(new_cls, "subclass_names", []) + getattr(new_cls, "use_names", [])
for n in names:
if n in _names:
continue
if n.endswith("_List"):
_names.append(n)
n = n[:-5]
# Generate 'list' class
exec(
f"""\
class {n}_List(SequenceBase):
subclass_names = [\'{n}\']
use_names = []
@staticmethod
def match(string): return SequenceBase.match(r\',\', {n}, string)
"""
)
elif n.endswith("_Name"):
_names.append(n)
n = n[:-5]
exec(
f"""\
class {n}_Name(Base):
subclass_names = [\'Name\']
"""
)
elif n.startswith("Scalar_"):
_names.append(n)
n = n[7:]
exec(
f"""\
class Scalar_{n}(Base):
subclass_names = [\'{n}\']
"""
)


# Inspect the contents of this module and list all of the classes in __all__
# for automatic documentation generation with AutoDoc.

classes = inspect.getmembers(
sys.modules[__name__],
lambda member: inspect.isclass(member) and member.__module__ == __name__,
)

__all__ = [name[0] for name in classes]
154 changes: 154 additions & 0 deletions src/fparser/two/Fortran2008/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# -----------------------------------------------------------------------------
# BSD 3-Clause License
#
# Copyright (c) 2023, Science and Technology Facilities Council.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# * 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.
#
# * 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.
# -----------------------------------------------------------------------------

"""
Fortran 2008 module. Contains classes which extend the Fortran
2003 standard to implement the Fortran 2008 standard.
"""
import inspect
import sys

from fparser.two.Fortran2003 import Base, SequenceBase
from fparser.two.Fortran2008.Fortran2008 import (
Program_Unit,
Executable_Construct,
Executable_Construct_C201,
Action_Stmt,
Action_Stmt_C201,
Action_Stmt_C816,
Action_Stmt_C828,
Data_Component_Def_Stmt,
Component_Attr_Spec,
Type_Declaration_Stmt,
Codimension_Attr_Spec,
Coarray_Bracket_Spec,
Attr_Spec,
Coarray_Spec,
Deferred_Coshape_Spec,
Explicit_Coshape_Spec,
Coshape_Spec,
Lower_Cobound,
Upper_Cobound,
Do_Term_Action_Stmt,
Alloc_Opt,
Allocate_Stmt,
Loop_Control,
If_Stmt,
Error_Stop_Stmt,
Specification_Part_C1112,
Implicit_Part_C1112,
Implicit_Part_Stmt_C1112,
Declaration_Construct_C1112,
Submodule,
Submodule_Stmt,
End_Submodule_Stmt,
Parent_Identifier,
Open_Stmt,
Connect_Spec,
Block_Construct,
Block_Stmt,
End_Block_Stmt,
Critical_Construct,
Critical_Stmt,
End_Critical_Stmt,
Procedure_Stmt,
)


# pylint: disable=eval-used
# pylint: disable=exec-used

#
# GENERATE Scalar_, _List, _Name CLASSES
#
ClassType = type(Base)
_names = dir()
for clsname in _names:
NEW_CLS = eval(clsname)
if not (
isinstance(NEW_CLS, ClassType)
and issubclass(NEW_CLS, Base)
and not NEW_CLS.__name__.endswith("Base")
):
continue

names = getattr(NEW_CLS, "subclass_names", []) + getattr(NEW_CLS, "use_names", [])
for n in names:
if n in _names:
continue
if n.endswith("_List"):
_names.append(n)
n = n[:-5]
# Generate 'list' class
exec(
f"""\
class {n}_List(SequenceBase):
subclass_names = [\'{n}\']
use_names = []
@staticmethod
def match(string): return SequenceBase.match(r\',\', {n}, string)
"""
)
elif n.endswith("_Name"):
_names.append(n)
n = n[:-5]
exec(
f"""\
class {n}_Name(Base):
subclass_names = [\'Name\']
"""
)
elif n.startswith("Scalar_"):
_names.append(n)
n = n[7:]
exec(
f"""\
class Scalar_{n}(Base):
subclass_names = [\'{n}\']
"""
)
# Make sure NEW_CLS does not reference a class so is not accidentally
# picked up in __all__.
NEW_CLS = None


# Determine the generated classes in this module and list these in
# __all__ to support automatic documentation generation with AutoDoc.

classes = inspect.getmembers(
sys.modules[__name__],
lambda member: inspect.isclass(member) and member.__module__ == __name__,
)
__all__ = [name[0] for name in classes if name[0]]
4 changes: 3 additions & 1 deletion src/fparser/two/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,9 @@ def create(self, std=None):
# First find all Fortran2008 classes.
from fparser.two import Fortran2008

f2008_cls_members = get_module_classes(Fortran2008)
f2008_cls_members = inspect.getmembers(
sys.modules[Fortran2008.__name__], inspect.isclass
)

# next add in Fortran2003 classes if they do not already
# exist as a Fortran2008 class.
Expand Down
Loading

0 comments on commit 38364a9

Please sign in to comment.