# Modified work Copyright (c) 2017-2022 Science and Technology
# Facilities Council
# Original work Copyright (c) 1999-2008 Pearu Peterson
# 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.
# --------------------------------------------------------------------
# The original software (in the f2py project) was 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:
# a. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# b. 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.
# c. Neither the name of the F2PY project 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 REGENTS 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 single line statements.
"""
import re
import sys
from fparser.common.base_classes import Statement
# Auxiliary tools
from fparser.common.utils import (
split_comma,
specs_split_comma,
AnalyzeError,
ParseError,
parse_bind,
parse_result,
is_name,
extract_bracketed_list_items,
)
from fparser.common.utils import classes
__all__ = [
"GeneralAssignment",
"Assignment",
"PointerAssignment",
"Assign",
"Call",
"Goto",
"ComputedGoto",
"AssignedGoto",
"Continue",
"Return",
"Stop",
"Print",
"Read",
"Read0",
"Read1",
"Write",
"Flush",
"Wait",
"Contains",
"Allocate",
"Deallocate",
"ModuleProcedure",
"Access",
"Public",
"Private",
"Close",
"Cycle",
"Backspace",
"Endfile",
"Rewind",
"Open",
"Format",
"Save",
"Data",
"Nullify",
"Use",
"Exit",
"Parameter",
"Equivalence",
"Dimension",
"Target",
"Pointer",
"Protected",
"Volatile",
"Value",
"ArithmeticIf",
"Intrinsic",
"Inquire",
"Sequence",
"External",
"Namelist",
"Common",
"Optional",
"Intent",
"Entry",
"Import",
"ForallStmt",
"SpecificBinding",
"GenericBinding",
"FinalBinding",
"Allocatable",
"Asynchronous",
"Bind",
"Else",
"ElseIf",
"Case",
"TypeIs",
"ClassIs",
"WhereStmt",
"ElseWhere",
"Enumerator",
"FortranName",
"Threadsafe",
"Depend",
"Check",
"CallStatement",
"CallProtoArgument",
"Pause",
"Comment",
]
class StatementWithNamelist(Statement):
"""
<statement> [ :: ] <name-list>
"""
def process_item(self):
if self.item.has_map():
self.isvalid = False
return
if hasattr(self, "stmtname"):
clsname = self.stmtname
else:
clsname = self.__class__.__name__
line = self.item.get_line()[len(clsname) :].lstrip()
if line.startswith("::"):
line = line[2:].lstrip()
self.items = items = []
for item in split_comma(line):
if not is_name(item):
self.isvalid = False
return
items.append(item)
return
def tofortran(self, isfix=None):
if hasattr(self, "stmtname"):
clsname = self.stmtname.upper()
else:
clsname = self.__class__.__name__.upper()
s = ", ".join(self.items)
if s:
s = " " + s
return self.get_indent_tab(isfix=isfix) + clsname + s
# Execution statements
[docs]
class GeneralAssignment(Statement):
"""
<variable> = <expr>
<pointer variable> => <expr>
"""
[docs]
match = re.compile(r"\w[^=]*\s*=\>?").match
[docs]
item_re = re.compile(
r"(?P<variable>\w[^=]*)\s*(?P<sign>=\>?)\s*(?P<expr>.*)\Z", re.I
).match
[docs]
_repr_attr_names = ["variable", "sign", "expr"] + Statement._repr_attr_names
[docs]
def process_item(self):
m = self.item_re(self.item.get_line())
if not m:
self.isvalid = False
return
self.sign = sign = m.group("sign")
if isinstance(self, Assignment) and sign != "=":
self.isvalid = False
return
elif isinstance(self, PointerAssignment) and sign != "=>":
self.isvalid = False
return
else:
if sign == "=>":
self.__class__ = PointerAssignment
else:
self.__class__ = Assignment
apply_map = self.item.apply_map
v1 = v = m.group("variable").replace(" ", "")
while True:
i = v.find(")")
if i == -1:
break
v = v[i + 1 :]
if v.startswith("(") or v.startswith(r"%"):
continue
if v:
self.isvalid = False
return
self.variable = apply_map(v1)
self.expr = apply_map(m.group("expr"))
return
[docs]
def tofortran(self, isfix=None):
return self.get_indent_tab(isfix=isfix) + "%s %s %s" % (
self.variable,
self.sign,
self.expr,
)
[docs]
def analyze(self):
return
[docs]
class Assignment(GeneralAssignment):
pass
[docs]
class PointerAssignment(GeneralAssignment):
pass
[docs]
class Assign(Statement):
"""
ASSIGN <label> TO <int-variable-name>
"""
[docs]
match = re.compile(r"assign\s*\d+\s*to\s*\w+\s*\Z", re.I).match
[docs]
def process_item(self):
line = self.item.get_line()[6:].lstrip()
i = line.lower().find("to")
assert not self.item.has_map()
self.items = [line[:i].rstrip(), line[i + 2 :].lstrip()]
return
[docs]
def tofortran(self, isfix=None):
return self.get_indent_tab(isfix=isfix) + "ASSIGN %s TO %s" % (
self.items[0],
self.items[1],
)
[docs]
def analyze(self):
return
[docs]
class Call(Statement):
"""
Call statement class::
CALL <procedure-designator> [ ( [ <actual-arg-spec-list> ] ) ]
<procedure-designator> = <procedure-name>
| <proc-component-ref>
| <data-ref> % <binding-name>
<actual-arg-spec> = [ <keyword> = ] <actual-arg>
<actual-arg> = <expr>
| <variable>
| <procedure-name>
| <proc-component-ref>
| <alt-return-spec>
<alt-return-spec> = * <label>
<proc-component-ref> = <variable> % <procedure-component-name>
<variable> = <designator>
"""
# As indicated in the specification above, a call to a subroutine
# that has no arguments does *not* require parentheses.
# e.g.:
# call bob
# is valid Fortran.
[docs]
match = re.compile(r"call\b\s*\w([\s\w\(\)\%]*\w)?\s*(\(.*\))?\s*$", re.I).match
[docs]
def process_item(self):
"""Parse the string containing the Call and store the
designator and list of arguments (if any)"""
item = self.item
apply_map = item.apply_map
line = item.get_line()[4:].strip()
# Work backwards from the end of the line in order to allow
# for code like:
# call my_type(1)%my_function(arg(2))
# The following code will also support something like:
# call my_type(1)%my_function("(")
# because fparser will previously have identified the "(" as a
# string expression and replaced it with something like
# "F2PY_EXPR_TUPLE_2"
if line.endswith(")"):
# Work back down the line until we find the matching '('
i = len(line) - 2
nopen = 1
while i > 0:
if line[i] == ")":
nopen += 1
elif line[i] == "(":
nopen -= 1
if nopen == 0:
# Have found the matching '(' at position i
break
i -= 1
if i <= 0:
# Have reached the beginning of the string without
# finding the matching '('
self.isvalid = False
return
self.designator = apply_map(line[:i]).strip()
items = split_comma(line[i + 1 : -1], item)
else:
# Call has no argument list
items = []
self.designator = apply_map(line).strip()
self.items = items
return
[docs]
def tofortran(self, isfix=None):
"""Returns the Fortran representation of this object as a string"""
txt = self.get_indent_tab(isfix=isfix) + "CALL " + str(self.designator)
if self.items:
txt += "(" + ", ".join(map(str, self.items)) + ")"
return txt
[docs]
def analyze(self):
a = self.programblock.a
variables = a.variables
if hasattr(a, "external"):
external = a.external
if self.designator in external:
print("Need to analyze:", self, file=sys.stderr)
return
[docs]
class Goto(Statement):
"""
GO TO <label>
"""
[docs]
match = re.compile(r"go\s*to\s*\d+\s*\Z", re.I).match
[docs]
def process_item(self):
assert not self.item.has_map()
self.label = self.item.get_line()[2:].lstrip()[2:].lstrip()
return
[docs]
def tofortran(self, isfix=None):
return self.get_indent_tab(isfix=isfix) + "GO TO %s" % (self.label)
[docs]
def analyze(self):
return
[docs]
class ComputedGoto(Statement):
"""
GO TO ( <label-list> ) [ , ] <scalar-int-expr>
"""
[docs]
match = re.compile(r"go\s*to\s*\(", re.I).match
[docs]
def process_item(self):
apply_map = self.item.apply_map
line = self.item.get_line()[2:].lstrip()[2:].lstrip()
i = line.index(")")
self.items = split_comma(line[1:i], self.item)
line = line[i + 1 :].lstrip()
if line.startswith(","):
line = line[1:].lstrip()
self.expr = apply_map(line)
return
[docs]
def tofortran(self, isfix=None):
return self.get_indent_tab(isfix=isfix) + "GO TO (%s) %s" % (
", ".join(self.items),
self.expr,
)
[docs]
def analyze(self):
return
[docs]
class AssignedGoto(Statement):
"""
GO TO <int-variable-name> [ ( <label> [ , <label> ]... ) ]
"""
[docs]
match = re.compile(r"go\s*to\s*\w+\s*\(?", re.I).match
[docs]
def process_item(self):
line = self.item.get_line()[2:].lstrip()[2:].lstrip()
i = line.find("(")
if i == -1:
self.varname = line
self.items = []
return
self.varname = line[:i].rstrip()
assert line[-1] == ")", repr(line)
self
self.items = split_comma(line[i + 1 : -1], self.item)
return
[docs]
def tofortran(self, isfix=None):
tab = self.get_indent_tab(isfix=isfix)
if self.items:
return tab + "GO TO %s (%s)" % (self.varname, ", ".join(self.items))
return tab + "GO TO %s" % (self.varname)
[docs]
def analyze(self):
return
[docs]
class Continue(Statement):
"""
CONTINUE
"""
[docs]
match = re.compile(r"continue\Z", re.I).match
[docs]
def process_item(self):
self.label = self.item.label
return
[docs]
def tofortran(self, isfix=None):
return self.get_indent_tab(deindent=True) + "CONTINUE"
[docs]
def analyze(self):
return
[docs]
class Return(Statement):
"""
RETURN [ <scalar-int-expr> ]
"""
[docs]
match = re.compile(r"return\b", re.I).match
[docs]
def process_item(self):
self.expr = self.item.apply_map(self.item.get_line()[6:].lstrip())
return
[docs]
def tofortran(self, isfix=None):
tab = self.get_indent_tab(isfix=isfix)
if self.expr:
return tab + "RETURN %s" % (self.expr)
return tab + "RETURN"
[docs]
def analyze(self):
return
[docs]
class Stop(Statement):
"""
STOP [ <stop-code> ]
<stop-code> = <scalar-char-constant> | <1-5-digit>
"""
[docs]
match = re.compile(r'stop\s*((\'\w*\'|"\w*")+|\d+|)\Z', re.I).match
[docs]
def process_item(self):
self.code = self.item.apply_map(self.item.get_line()[4:].lstrip())
return
[docs]
def tofortran(self, isfix=None):
tab = self.get_indent_tab(isfix=isfix)
if self.code:
return tab + "STOP %s" % (self.code)
return tab + "STOP"
[docs]
def analyze(self):
return
[docs]
class Print(Statement):
"""
::
PRINT <format> [, <output-item-list>]
<format> = <default-char-expr> | <label> | *
<output-item> = <expr> | <io-implied-do>
<io-implied-do> = ( <io-implied-do-object-list> , <implied-do-control> )
<io-implied-do-object> = <input-item> | <output-item>
<implied-do-control> = <do-variable>
= <scalar-int-expr> ,
<scalar-int-expr> [ , <scalar-int-expr> ]
<input-item> = <variable> | <io-implied-do>
"""
[docs]
match = re.compile(r"print\s*(\'\w*\'|\"\w*\"|\d+|[*]|\b\w)", re.I).match
[docs]
def process_item(self):
item = self.item
apply_map = item.apply_map
line = item.get_line()[5:].lstrip()
items = split_comma(line, item)
self.format = items[0]
self.items = items[1:]
return
[docs]
def tofortran(self, isfix=None):
return self.get_indent_tab(isfix=isfix) + "PRINT %s" % (
", ".join([self.format] + self.items)
)
[docs]
def analyze(self):
return
[docs]
class Read(Statement):
"""
Read0: READ ( <io-control-spec-list> ) [ <input-item-list> ]
<io-control-spec-list> = [ UNIT = ] <io-unit>
| [ FORMAT = ] <format>
| [ NML = ] <namelist-group-name>
| ADVANCE = <scalar-default-char-expr>
...
Read1: READ <format> [, <input-item-list>]
<format> == <default-char-expr> | <label> | *
"""
[docs]
match = re.compile(r'read\b\s*[\w(*\'"]', re.I).match
[docs]
def process_item(self):
item = self.item
line = item.get_line()[4:].lstrip()
if line.startswith("("):
self.__class__ = Read0
else:
self.__class__ = Read1
self.process_item()
return
[docs]
def analyze(self):
return
[docs]
class Read0(Read):
[docs]
def process_item(self):
item = self.item
line = item.get_line()[4:].lstrip()
i = line.find(")")
self.specs = specs_split_comma(line[1:i], item)
self.items = split_comma(line[i + 1 :], item)
return
[docs]
def tofortran(self, isfix=None):
s = self.get_indent_tab(isfix=isfix) + "READ (%s)" % (", ".join(self.specs))
if self.items:
return s + " " + ", ".join(self.items)
return s
[docs]
class Read1(Read):
[docs]
def process_item(self):
item = self.item
line = item.get_line()[4:].lstrip()
items = split_comma(line, item)
self.format = items[0]
self.items = items[1:]
return
[docs]
def tofortran(self, isfix=None):
return (
self.get_indent_tab(isfix=isfix)
+ "READ "
+ ", ".join([self.format] + self.items)
)
[docs]
class Write(Statement):
"""
WRITE ( io-control-spec-list ) [<output-item-list>]
"""
[docs]
match = re.compile(r"write\s*\(", re.I).match
[docs]
def process_item(self):
item = self.item
line = item.get_line()[5:].lstrip()
i = line.find(")")
assert i != -1, repr(line)
self.specs = specs_split_comma(line[1:i], item)
self.items = split_comma(line[i + 1 :], item)
return
[docs]
def tofortran(self, isfix=None):
s = self.get_indent_tab(isfix=isfix) + "WRITE (%s)" % ", ".join(self.specs)
if self.items:
s += " " + ", ".join(self.items)
return s
[docs]
def analyze(self):
return
[docs]
class Flush(Statement):
"""
::
FLUSH <file-unit-number>
FLUSH ( <flush-spec-list> )
<flush-spec> = [ UNIT = ] <file-unit-number>
| IOSTAT = <scalar-int-variable>
| IOMSG = <iomsg-variable>
| ERR = <label>
"""
[docs]
match = re.compile(r"flush\b", re.I).match
[docs]
def process_item(self):
line = self.item.get_line()[5:].lstrip()
if not line:
self.isvalid = False
return
if line.startswith("("):
assert line[-1] == ")", repr(line)
self.specs = specs_split_comma(line[1:-1], self.item)
else:
self.specs = specs_split_comma(line, self.item)
return
[docs]
def tofortran(self, isfix=None):
tab = self.get_indent_tab(isfix=isfix)
return tab + "FLUSH (%s)" % (", ".join(self.specs))
[docs]
def analyze(self):
return
[docs]
class Wait(Statement):
"""
::
WAIT ( <wait-spec-list> )
<wait-spec> = [ UNIT = ] <file-unit-number>
| END = <label>
| EOR = <label>
| ERR = <label>
| ID = <scalar-int-expr>
| IOMSG = <iomsg-variable>
| IOSTAT = <scalar-int-variable>
"""
[docs]
match = re.compile(r"wait\s*\(.*\)\Z", re.I).match
[docs]
def process_item(self):
self.specs = specs_split_comma(
self.item.get_line()[4:].lstrip()[1:-1], self.item
)
return
[docs]
def tofortran(self, isfix=None):
tab = self.get_indent_tab(isfix=isfix)
return tab + "WAIT (%s)" % (", ".join(self.specs))
[docs]
def analyze(self):
return
[docs]
class Contains(Statement):
"""
CONTAINS
"""
[docs]
match = re.compile(r"contains\Z", re.I).match
[docs]
def process_item(self):
return
[docs]
def tofortran(self, isfix=None):
return self.get_indent_tab(isfix=isfix) + "CONTAINS"
[docs]
class Allocate(Statement):
"""
::
ALLOCATE ( [ <type-spec> :: ] <allocation-list> [ , <alloc-opt-list> ] )
<alloc-opt> = STAT = <stat-variable>
| ERRMSG = <errmsg-variable>
| SOURCE = <source-expr>
<allocation> = <allocate-object> [ ( <allocate-shape-spec-list> ) ]
"""
[docs]
match = re.compile(r"allocate\s*\(.*\)\Z", re.I).match
[docs]
def process_item(self):
"""
Process the ALLOCATE statement and store the various entities being
allocated in self.items. Any type-specification is stored in
self.spec.
:raises ParseError: if an invalid type-specification is used
"""
line = self.item.get_line()[8:].lstrip()[1:-1].strip()
item2 = self.item.copy(line, True)
line2 = item2.get_line()
i = line2.find("::")
if i != -1:
spec = item2.apply_map(line2[:i].rstrip())
from .block_statements import type_spec
stmt = None
for cls in type_spec:
if cls.match(spec):
stmt = cls(self, item2.copy(spec))
if stmt.isvalid:
break
if stmt is not None and stmt.isvalid:
spec = stmt
elif is_name(spec):
# Type spec is the name of a derived type
pass
else:
raise ParseError(
"Unrecognised type-specification in ALLOCATE statement: "
"{0}".format(self.item.line)
)
line2 = line2[i + 2 :].lstrip()
else:
spec = None
self.spec = spec
self.items = specs_split_comma(line2, item2)
return
[docs]
def tofortran(self, isfix=None):
"""
Create the Fortran code for this ALLOCATE statement
:param bool isfix: whether or not to generate fixed-format code
:return: Fortran code
:rtype: str
"""
type_spec = ""
if self.spec:
if isinstance(self.spec, str):
type_spec = self.spec
else:
type_spec = self.spec.tostr()
type_spec += " :: "
return self.get_indent_tab(isfix=isfix) + "ALLOCATE (%s%s)" % (
type_spec,
", ".join(self.items),
)
[docs]
def analyze(self):
return
[docs]
class Deallocate(Statement):
"""
::
DEALLOCATE ( <allocate-object-list> [ , <dealloc-opt-list> ] )
<allocate-object> = <variable-name>
| <structure-component>
<structure-component> = <data-ref>
<dealloc-opt> = STAT = <stat-variable>
| ERRMSG = <errmsg-variable>
"""
[docs]
match = re.compile(r"deallocate\s*\(.*\)\Z", re.I).match
[docs]
def process_item(self):
line = self.item.get_line()[10:].lstrip()[1:-1].strip()
self.items = specs_split_comma(line, self.item)
return
[docs]
def tofortran(self, isfix=None):
return self.get_indent_tab(isfix=isfix) + "DEALLOCATE (%s)" % (
", ".join(self.items)
)
[docs]
def analyze(self):
return
[docs]
class ModuleProcedure(Statement):
"""
[ MODULE ] PROCEDURE [::] <procedure-name-list>
"""
[docs]
match = re.compile(r"(module\s*|)procedure\b\s*(::)?", re.I).match
[docs]
def process_item(self):
line = self.item.get_line()
m = self.match(line)
assert m, repr(line)
items = split_comma(line[m.end() :].strip(), self.item)
for n in items:
if not is_name(n):
self.isvalid = False
return
self.items = items
return
[docs]
def tofortran(self, isfix=None):
tab = self.get_indent_tab(isfix=isfix)
return tab + "MODULE PROCEDURE %s" % (", ".join(self.items))
[docs]
def analyze(self):
module_procedures = self.parent.a.module_procedures
module_procedures.extend(self.items)
# XXX: add names to parent_provides
return
[docs]
class Access(Statement):
"""
<access-spec> [ [::] <access-id-list>]
<access-spec> = PUBLIC | PRIVATE
<access-id> = <use-name> | <generic-spec>
"""
[docs]
match = re.compile(r"(public|private)\b", re.I).match
[docs]
def process_item(self):
clsname = self.__class__.__name__.lower()
line = self.item.get_line()
if not line.lower().startswith(clsname):
self.isvalid = False
return
line = line[len(clsname) :].lstrip()
if line.startswith("::"):
line = line[2:].lstrip()
self.items = split_comma(line, self.item)
return
[docs]
def tofortran(self, isfix=None):
clsname = self.__class__.__name__.upper()
tab = self.get_indent_tab(isfix=isfix)
if self.items:
return tab + clsname + " " + ", ".join(self.items)
return tab + clsname
[docs]
def analyze(self):
clsname = self.__class__.__name__
bits = getattr(self.parent.a, clsname.lower() + "_id_list")
if self.items:
for name in self.items:
if name not in bits:
bits.append(name)
else:
if "" not in bits:
bits.append("")
if not isinstance(self.parent, classes.Module):
parentclsname = self.parent.__class__.__name__
message = (
"C548 violation: %s statement only allowed in the"
" specification-part of a module, not in a %s."
% (clsname.upper(), parentclsname.lower())
)
self.warning(message)
access_id_list = self.parent.a.private_id_list + self.parent.a.public_id_list
if access_id_list.count("") > 1:
message = (
"C548 violation: only one access-stmt with omitted"
" access-id-list is permitted in"
" the module-specification-part."
)
self.warning(message)
# todo: check for conflicting access statement usages
# (e.g. private foo; public foo)
# todo: check for conflicting generic-spec id-s.
return
[docs]
class Close(Statement):
"""
::
CLOSE ( <close-spec-list> )
<close-spec> = [ UNIT = ] <file-unit-number>
| IOSTAT = <scalar-int-variable>
| IOMSG = <iomsg-variable>
| ERR = <label>
| STATUS = <scalar-default-char-expr>
"""
[docs]
match = re.compile(r"close\s*\(.*\)\Z", re.I).match
[docs]
def process_item(self):
line = self.item.get_line()[5:].lstrip()[1:-1].strip()
self.specs = specs_split_comma(line, self.item)
return
[docs]
def tofortran(self, isfix=None):
tab = self.get_indent_tab(isfix=isfix)
return tab + "CLOSE (%s)" % (", ".join(self.specs))
[docs]
def analyze(self):
return
[docs]
class Cycle(Statement):
"""
CYCLE [ <do-construct-name> ]
"""
[docs]
match = re.compile(r"cycle\b\s*\w*\s*\Z", re.I).match
[docs]
def process_item(self):
self.name = self.item.get_line()[5:].lstrip()
return
[docs]
def tofortran(self, isfix=None):
if self.name:
return self.get_indent_tab(isfix=isfix) + "CYCLE " + self.name
return self.get_indent_tab(isfix=isfix) + "CYCLE"
[docs]
def analyze(self):
return
class FilePositioningStatement(Statement):
"""
REWIND <file-unit-number>
REWIND ( <position-spec-list> )
<position-spec-list> = [ UNIT = ] <file-unit-number>
| IOMSG = <iomsg-variable>
| IOSTAT = <scalar-int-variable>
| ERR = <label>
The same for BACKSPACE, ENDFILE.
"""
match = re.compile(r"(rewind|backspace|endfile)\b", re.I).match
def process_item(self):
clsname = self.__class__.__name__.lower()
line = self.item.get_line()
if not line.lower().startswith(clsname):
self.isvalid = False
return
line = line[len(clsname) :].lstrip()
if line.startswith("("):
assert line[-1] == ")", repr(line)
spec = line[1:-1].strip()
else:
spec = line
self.specs = specs_split_comma(spec, self.item)
return
def tofortran(self, isfix=None):
clsname = self.__class__.__name__.upper()
return (
self.get_indent_tab(isfix=isfix)
+ clsname
+ " (%s)" % (", ".join(self.specs))
)
def analyze(self):
return
[docs]
class Backspace(FilePositioningStatement):
pass
[docs]
class Endfile(FilePositioningStatement):
pass
[docs]
class Rewind(FilePositioningStatement):
pass
[docs]
class Open(Statement):
"""
::
OPEN ( <connect-spec-list> )
<connect-spec> = [ UNIT = ] <file-unit-number>
| ACCESS = <scalar-default-char-expr>
| ..
"""
[docs]
match = re.compile(r"open\s*\(.*\)\Z", re.I).match
[docs]
def process_item(self):
line = self.item.get_line()[4:].lstrip()[1:-1].strip()
self.specs = specs_split_comma(line, self.item)
return
[docs]
def tofortran(self, isfix=None):
return self.get_indent_tab(isfix=isfix) + "OPEN (%s)" % (", ".join(self.specs))
[docs]
def analyze(self):
return
[docs]
class Save(Statement):
"""
::
SAVE [ [ :: ] saved-entity-list ]
saved-entity is object-name
or proc-pointer-name
or / common-block-name /
proc-pointer-name is name
object-name is name
"""
[docs]
match = re.compile(r"save\b", re.I).match
[docs]
def process_item(self):
assert not self.item.has_map()
line = self.item.get_line()[4:].lstrip()
if line.startswith("::"):
line = line[2:].lstrip()
items = []
for s in line.split(","):
s = s.strip()
if not s:
continue
if s.startswith("/"):
assert s.endswith("/"), repr(s)
n = s[1:-1].strip()
assert is_name(n), repr(n)
items.append("/%s/" % (n))
elif is_name(s):
items.append(s)
else:
self.isvalid = False
return
self.items = items
return
[docs]
def tofortran(self, isfix=None):
tab = self.get_indent_tab(isfix=isfix)
if not self.items:
return tab + "SAVE"
return tab + "SAVE %s" % (", ".join(self.items))
[docs]
def analyze(self):
return
[docs]
class Data(Statement):
"""
::
DATA data-stmt-set [ [ , ] data-stmt-set ]...
data-stmt-set is data-stmt-object-list / data-stmt-value-list /
data-stmt-object is variable or data-implied-do
data-implied-do is ( data-i-do-object-list ,
data-i-do-variable = scalar-int-expr ,
scalar-int-expr [ , scalar-int-expr ] )
data-i-do-object is array-element
or scalar-structure-component
or data-implied-do
data-i-do-variable is scalar-int-variable
variable is designator
designator is object-name
or array-element
or array-section
or structure-component
or substring
array-element is data-ref
array-section is data-ref [ ( substring-range ) ]
"""
[docs]
match = re.compile(r"data\b", re.I).match
[docs]
def process_item(self):
line = self.item.get_line()[4:].lstrip()
stmts = []
self.isvalid = False
while line:
i = line.find("/")
if i == -1:
return
j = line.find("/", i + 1)
if j == -1:
return
l1, l2 = line[:i].rstrip(), line[i + 1 : j].strip()
l1 = split_comma(l1, self.item)
l2 = split_comma(l2, self.item)
stmts.append((l1, l2))
line = line[j + 1 :].lstrip()
if line.startswith(","):
line = line[1:].lstrip()
self.stmts = stmts
self.isvalid = True
return
[docs]
def tofortran(self, isfix=None):
tab = self.get_indent_tab(isfix=isfix)
bits = []
for o, v in self.stmts:
bits.append("%s / %s /" % (", ".join(o), ", ".join(v)))
return tab + "DATA " + " ".join(bits)
[docs]
def analyze(self):
return
[docs]
class Nullify(Statement):
"""
NULLIFY ( <pointer-object-list> )
<pointer-object> = <variable-name>
"""
[docs]
match = re.compile(r"nullify\s*\(.*\)\Z", re.I).match
[docs]
def process_item(self):
line = self.item.get_line()[7:].lstrip()[1:-1].strip()
self.items = split_comma(line, self.item)
return
[docs]
def tofortran(self, isfix=None):
return self.get_indent_tab(isfix=isfix) + "NULLIFY (%s)" % (
", ".join(self.items)
)
[docs]
def analyze(self):
return
[docs]
class Use(Statement):
"""Parses USE statement.
:param class Statement: Base fparser class.
:raises AnalyzeError: If entity name is not in module.
Fortran syntax construct::
USE [ [ , module-nature ] :: ] module-name [ , rename-list ]
USE [ [ , module-nature ] :: ] module-name , ONLY : [ only-list ]
module-nature is INTRINSIC or NON_INTRINSIC
rename is local-name => use-name
or OPERATOR ( local-defined-operator ) =>
OPERATOR ( use-defined-operator )
only is generic-spec or only-use-name or rename
only-use-name is use-name
"""
[docs]
match = re.compile(r"use\b", re.I).match
[docs]
def process_item(self):
"""Parse the string containing the Use and store the
module name and list of attributes (if any)"""
line = self.item.get_line()[3:].lstrip()
nature = ""
if line.startswith(","):
i = line.find("::")
nature = line[1:i].strip().upper()
line = line[i + 2 :].lstrip()
if line.startswith("::"):
line = line[2:].lstrip()
if nature and not is_name(nature):
self.isvalid = False
return
self.nature = nature
i = line.find(",")
self.isonly = False
if i == -1:
self.name = line
self.items = []
else:
self.name = line[:i].rstrip()
line = line[i + 1 :].lstrip()
if line.lower().startswith("only") and line[4:].lstrip().startswith(":"):
self.isonly = True
line = line[4:].lstrip()[1:].lstrip()
self.items = split_comma(line, self.item)
return
[docs]
def tofortran(self, isfix=None): # pylint: disable=invalid-name
"""
Returns the Fortran representation of this object as a string
:param bool isfix: Whether or not to generated fixed-format Fortran
:return: Fortran representation of this object
:rtype: str
"""
tab = self.get_indent_tab(isfix=isfix)
s = "USE"
if self.nature:
s += ", " + self.nature + " ::"
s += " " + self.name
if self.isonly:
s += ", ONLY:"
elif self.items:
s += ","
if self.items:
s += " " + ", ".join(self.items)
return tab + s
[docs]
def analyze(self): # pylint: disable=invalid-name
"""Returns warnings if this object is incorrect"""
use = self.parent.a.use
if self.name in use:
return
modules = self.top.a.module
if self.name not in modules:
fn = self.reader.find_module_source_file(self.name)
if fn is not None:
from fparser.common.readfortran import FortranFileReader
from fparser.one.parsefortran import FortranParser
self.info("looking module information from %r" % (fn))
reader = FortranFileReader(
fn,
include_dirs=self.reader.include_dirs,
source_only=self.reader.source_only,
)
parser = FortranParser(reader)
parser.parse()
parser.block.a.module.update(modules)
parser.analyze()
modules.update(parser.block.a.module)
if self.name not in modules:
self.warning(
"no information about the module %r in use statement" % (self.name)
)
return
module = modules[self.name]
use[self.name] = module
use_provides = self.parent.a.use_provides
renames = [split_comma(item, comma="=>") for item in self.items if "=>" in item]
norenames = [item for item in self.items if "=>" not in item]
all_mod_provides = dict(module.a.module_provides)
all_mod_provides.update(module.a.use_provides)
if self.isonly:
# populate use_provides with items/renames only.
for rename, orig in renames:
self.populate_use_provides(all_mod_provides, use_provides, orig, rename)
for name in norenames:
self.populate_use_provides(all_mod_provides, use_provides, name)
else:
# norenames should be empty
if norenames:
self.warning(
"'use' without 'only' clause does not rename the "
"variables '%s'" % ", ".join(norenames)
)
# populate use_provides with renamed vars from module.
for rename, orig in renames:
self.populate_use_provides(all_mod_provides, use_provides, orig, rename)
# get all the rest
unrenamed = set(all_mod_provides) - set([b for (a, b) in renames])
for name in unrenamed:
self.populate_use_provides(all_mod_provides, use_provides, name)
return
[docs]
def populate_use_provides(self, all_mod_provides, use_provides, name, rename=None):
"""Checks for entity name in the module"""
ovar = all_mod_provides.get(name, None)
if ovar is None:
raise AnalyzeError(
"entity name '%s' is not in module '%s'" % (name, self.name)
)
if rename:
# XXX: rename != ovar.name -- should mark this somehow?
name_idx = rename
else:
name_idx = name
if name_idx in use_provides:
if ovar != use_provides[name_idx]:
self.warning(
"entity name '%s' is already declared in module "
"'%s' while adding it to '%s', overriding."
% (name, self.name, self.parent.name)
)
use_provides[name_idx] = ovar
[docs]
class Exit(Statement):
"""
EXIT [ <do-construct-name> ]
"""
[docs]
match = re.compile(r"exit\b\s*\w*\s*\Z", re.I).match
[docs]
def process_item(self):
self.name = self.item.get_line()[4:].lstrip()
return
[docs]
def tofortran(self, isfix=None):
if self.name:
return self.get_indent_tab(isfix=isfix) + "EXIT " + self.name
return self.get_indent_tab(isfix=isfix) + "EXIT"
[docs]
def analyze(self):
return
[docs]
class Parameter(Statement):
"""
PARAMETER ( <named-constant-def-list> )
<named-constant-def> = <named-constant> = <initialization-expr>
"""
[docs]
match = re.compile(r"parameter\s*\(.*\)\Z", re.I).match
[docs]
def process_item(self):
line = self.item.get_line()[9:].lstrip()[1:-1].strip()
self.items = split_comma(line, self.item)
return
[docs]
def tofortran(self, isfix=None):
return self.get_indent_tab(isfix=isfix) + "PARAMETER (%s)" % (
", ".join(self.items)
)
[docs]
def analyze(self):
for item in self.items:
i = item.find("=")
assert i != -1, repr(item)
name = item[:i].rstrip()
value = item[i + 1 :].lstrip()
var = self.get_variable(name)
var.update("parameter")
var.set_init(value)
return
[docs]
class Equivalence(Statement):
"""
EQUIVALENCE <equivalence-set-list>
<equivalence-set> = ( <equivalence-object> , <equivalence-object-list> )
<equivalence-object> = <variable-name> | <array-element> | <substring>
"""
[docs]
match = re.compile(r"equivalence\s*\(.*\)\Z", re.I).match
[docs]
def process_item(self):
items = []
for s in self.item.get_line()[11:].lstrip().split(","):
s = s.strip()
assert s[0] + s[-1] == "()", repr((s, self.item.get_line()))
s = ", ".join(split_comma(s[1:-1], self.item))
items.append("(" + s + ")")
self.items = items
return
[docs]
def tofortran(self, isfix=None):
return self.get_indent_tab(isfix=isfix) + "EQUIVALENCE %s" % (
", ".join(self.items)
)
[docs]
def analyze(self):
return
[docs]
class Dimension(Statement):
"""
DIMENSION [ :: ] <array-name> ( <array-spec> )
[ , <array-name> ( <array-spec> ) ]...
"""
[docs]
match = re.compile(r"dimension\b", re.I).match
[docs]
def process_item(self):
line = self.item.get_line()[9:].lstrip()
if line.startswith("::"):
line = line[2:].lstrip()
self.items = split_comma(line, self.item)
return
[docs]
def tofortran(self, isfix=None):
return self.get_indent_tab(isfix=isfix) + "DIMENSION %s" % (
", ".join(self.items)
)
[docs]
def analyze(self):
for line in self.items:
i = line.find("(")
assert i != -1 and line.endswith(")"), repr(line)
name = line[:i].rstrip()
array_spec = split_comma(line[i + 1 : -1].strip(), self.item)
var = self.get_variable(name)
var.set_bounds(array_spec)
return
[docs]
class Target(Statement):
"""
TARGET [ :: ] <object-name> ( <array-spec> )
[ , <object-name> ( <array-spec> ) ]...
"""
[docs]
match = re.compile(r"target\b", re.I).match
[docs]
def process_item(self):
line = self.item.get_line()[6:].lstrip()
if line.startswith("::"):
line = line[2:].lstrip()
self.items = split_comma(line, self.item)
return
[docs]
def tofortran(self, isfix=None):
return self.get_indent_tab(isfix=isfix) + "TARGET %s" % (", ".join(self.items))
[docs]
def analyze(self):
for line in self.items:
i = line.find("(")
assert i != -1 and line.endswith(")"), repr(line)
name = line[:i].rstrip()
array_spec = split_comma(line[i + 1 : -1].strip(), self.item)
var = self.get_variable(name)
var.set_bounds(array_spec)
var.update("target")
return
[docs]
class Pointer(Statement):
"""
::
POINTER [ :: ] pointer-decl-list
pointer-decl is object-name [ ( deferred-shape-spec-list ) ]
or proc-entity-name
"""
[docs]
match = re.compile(r"pointer\b", re.I).match
[docs]
def process_item(self):
line = self.item.get_line()[7:].lstrip()
if line.startswith("::"):
line = line[2:].lstrip()
self.items = split_comma(line, self.item)
return
[docs]
def tofortran(self, isfix=None):
return self.get_indent_tab(isfix=isfix) + "POINTER %s" % (", ".join(self.items))
[docs]
def analyze(self):
for line in self.items:
i = line.find("(")
if i == -1:
name = line
array_spec = None
else:
assert line.endswith(")"), repr(line)
name = line[:i].rstrip()
array_spec = split_comma(line[i + 1 : -1].strip(), self.item)
var = self.get_variable(name)
var.set_bounds(array_spec)
var.update("pointer")
return
[docs]
class Protected(StatementWithNamelist):
"""
PROTECTED [ :: ] <entity-name-list>
"""
[docs]
match = re.compile(r"protected\b", re.I).match
[docs]
def analyze(self):
for name in self.items:
var = self.get_variable(name)
var.update("protected")
return
[docs]
class Volatile(StatementWithNamelist):
"""
VOLATILE [ :: ] <object-name-list>
"""
[docs]
match = re.compile(r"volatile\b", re.I).match
[docs]
def analyze(self):
for name in self.items:
var = self.get_variable(name)
var.update("volatile")
return
[docs]
class Value(StatementWithNamelist):
"""
VALUE [ :: ] <dummy-arg-name-list>
"""
[docs]
match = re.compile(r"value\b", re.I).match
[docs]
def analyze(self):
for name in self.items:
var = self.get_variable(name)
var.update("value")
return
[docs]
class ArithmeticIf(Statement):
"""
IF ( <scalar-numeric-expr> ) <label> , <label> , <label>
"""
[docs]
match = re.compile(r"if\s*\(.*\)\s*\d+\s*,\s*\d+\s*,\s*\d+\s*\Z", re.I).match
[docs]
def process_item(self):
line = self.item.get_line()[2:].lstrip()
line, l2, l3 = line.rsplit(",", 2)
i = line.rindex(")")
l1 = line[i + 1 :]
self.expr = self.item.apply_map(line[1:i]).strip()
self.labels = [l1.strip(), l2.strip(), l3.strip()]
return
[docs]
def tofortran(self, isfix=None):
return self.get_indent_tab(isfix=isfix) + "IF (%s) %s" % (
self.expr,
", ".join(self.labels),
)
[docs]
def analyze(self):
return
[docs]
class Intrinsic(StatementWithNamelist):
"""
INTRINSIC [ :: ] <intrinsic-procedure-name-list>
"""
[docs]
match = re.compile(r"intrinsic\b", re.I).match
[docs]
def analyze(self):
for name in self.items:
var = self.get_variable(name)
var.update("intrinsic")
return
[docs]
class Inquire(Statement):
"""
INQUIRE ( <inquire-spec-list> )
INQUIRE ( IOLENGTH = <scalar-int-variable> ) <output-item-list>
<inquire-spec> = [ UNIT = ] <file-unit-number>
| FILE = <file-name-expr>
...
<output-item> = <expr>
| <io-implied-do>
"""
[docs]
match = re.compile(r"inquire\s*\(", re.I).match
[docs]
def process_item(self):
line = self.item.get_line()[7:].lstrip()
i = line.index(")")
self.specs = specs_split_comma(line[1:i].strip(), self.item)
self.items = split_comma(line[i + 1 :].lstrip(), self.item)
return
[docs]
def tofortran(self, isfix=None):
if self.items:
return self.get_indent_tab(isfix=isfix) + "INQUIRE (%s) %s" % (
", ".join(self.specs),
", ".join(self.items),
)
return self.get_indent_tab(isfix=isfix) + "INQUIRE (%s)" % (
", ".join(self.specs)
)
[docs]
def analyze(self):
return
[docs]
class Sequence(Statement):
"""
SEQUENCE
"""
[docs]
match = re.compile(r"sequence\Z", re.I).match
[docs]
def process_item(self):
return
[docs]
def tofortran(self, isfix=None):
return self.get_indent_tab(isfix=isfix) + "SEQUENCE"
[docs]
def analyze(self):
self.parent.update_attributes("SEQUENCE")
return
[docs]
class External(StatementWithNamelist):
"""
EXTERNAL [ :: ] <external-name-list>
"""
[docs]
match = re.compile(r"external\b", re.I).match
[docs]
def analyze(self):
for name in self.items:
var = self.get_variable(name)
var.update("external")
return
[docs]
class Namelist(Statement):
"""
NAMELIST / <namelist-group-name> / <namelist-group-object-list> [ [ , ]
/ <namelist-group-name> / <namelist-group-object-list> ]...
<namelist-group-object> = <variable-name>
"""
[docs]
match = re.compile(r"namelist\b", re.I).match
[docs]
def process_item(self):
line = self.item.get_line()[8:].lstrip()
items = []
while line:
assert line.startswith("/"), repr(line)
i = line.find("/", 1)
assert i != -1, repr(line)
name = line[: i + 1]
line = line[i + 1 :].lstrip()
i = line.find("/")
if i == -1:
items.append((name, line))
line = ""
continue
s = line[:i].rstrip()
if s.endswith(","):
s = s[:-1].rstrip()
items.append((name, s))
line = line[i + 1 :].lstrip()
self.items = items
return
[docs]
def tofortran(self, isfix=None):
bits = []
for name, s in self.items:
bits.append("%s %s" % (name, s))
tab = self.get_indent_tab(isfix=isfix)
return tab + "NAMELIST " + ", ".join(bits)
[docs]
class Common(Statement):
"""
::
COMMON [ / [ common-block-name ] / ] common-block-object-list \
[ [ , ] / [ common-block-name ] / common-block-object-list ]...
common-block-object is variable-name [ ( explicit-shape-spec-list ) ]
or proc-pointer-name
"""
[docs]
match = re.compile(r"common\b", re.I).match
[docs]
def process_item(self):
item = self.item
line = item.get_line()[6:].lstrip()
items = []
while line:
if not line.startswith("/"):
name = ""
assert not items, repr(line)
else:
i = line.find("/", 1)
assert i != -1, repr(line)
name = line[1:i].strip()
line = line[i + 1 :].lstrip()
i = line.find("/")
if i == -1:
items.append((name, split_comma(line, item)))
line = ""
continue
s = line[:i].rstrip()
if s.endswith(","):
s = s[:-1].rstrip()
items.append((name, split_comma(s, item)))
line = line[i:].lstrip()
self.items = items
return
[docs]
def tofortran(self, isfix=None):
bits = []
for name, s in self.items:
s = ", ".join(s)
if name:
bits.append("/ %s / %s" % (name, s))
else:
bits.append(s)
tab = self.get_indent_tab(isfix=isfix)
return tab + "COMMON " + " ".join(bits)
[docs]
def analyze(self):
for cname, items in self.items:
for item in items:
i = item.find("(")
if i != -1:
assert item.endswith(")"), repr(item)
name = item[:i].rstrip()
shape = split_comma(item[i + 1 : -1].strip(), self.item)
else:
name = item
shape = None
var = self.get_variable(name)
if shape is not None:
var.set_bounds(shape)
# XXX: add name,var to parent_provides
return
[docs]
class Optional(StatementWithNamelist):
"""
OPTIONAL [ :: ] <dummy-arg-name-list>
<dummy-arg-name> = <name>
"""
[docs]
match = re.compile(r"optional\b", re.I).match
[docs]
def analyze(self):
for name in self.items:
var = self.get_variable(name)
var.update("optional")
return
[docs]
class Intent(Statement):
"""
INTENT ( <intent-spec> ) [ :: ] <dummy-arg-name-list>
<intent-spec> = IN | OUT | INOUT
generalization for pyf-files:
INTENT ( <intent-spec-list> ) [ :: ] <dummy-arg-name-list>
<intent-spec> = IN | OUT | INOUT | CACHE | HIDE | OUT = <name>
"""
[docs]
match = re.compile(r"intent\s*\(", re.I).match
[docs]
def process_item(self):
line = self.item.get_line()[6:].lstrip()
i = line.find(")")
self.specs = specs_split_comma(line[1:i], self.item, upper=True)
line = line[i + 1 :].lstrip()
if line.startswith("::"):
line = line[2:].lstrip()
self.items = [s.strip() for s in line.split(",")]
for n in self.items:
if not is_name(n):
self.isvalid = False
return
return
[docs]
def tofortran(self, isfix=None):
return self.get_indent_tab(isfix=isfix) + "INTENT (%s) %s" % (
", ".join(self.specs),
", ".join(self.items),
)
[docs]
def analyze(self):
for name in self.items:
var = self.get_variable(name)
var.set_intent(self.specs)
return
[docs]
class Entry(Statement):
"""
::
ENTRY entry-name [ ( [ dummy-arg-list ] ) [ suffix ] ]
suffix is proc-language-binding-spec [ RESULT ( result-name ) ]
| RESULT ( result-name ) [ proc-language-binding-spec ]
proc-language-binding-spec is language-binding-spec
language-binding-spec is
BIND ( C [ , NAME = scalar-char-initialization-expr ] )
dummy-arg is dummy-arg-name | *
"""
[docs]
match = re.compile(r"entry\s+[a-zA-Z]", re.I).match
[docs]
def process_item(self):
line = self.item.get_line()[5:].lstrip()
m = re.match(r"\w+", line)
name = line[: m.end()]
line = line[m.end() :].lstrip()
if line.startswith("("):
i = line.find(")")
assert i != -1, repr(line)
items = split_comma(line[1:i], self.item)
line = line[i + 1 :].lstrip()
else:
items = []
self.bind, line = parse_bind(line, self.item)
self.result, line = parse_result(line, self.item)
if line:
assert self.bind is None, repr(self.bind)
self.bind, line = parse_bind(line, self.item)
assert not line, repr(line)
self.name = name
self.items = items
return
[docs]
def tofortran(self, isfix=None):
tab = self.get_indent_tab(isfix=isfix)
s = tab + "ENTRY " + self.name
if self.items:
s += " (%s)" % (", ".join(self.items))
if self.result:
s += " RESULT (%s)" % (self.result)
if self.bind:
s += " BIND (%s)" % (", ".join(self.bind))
return s
[docs]
class Import(StatementWithNamelist):
"""
IMPORT [ [ :: ] <import-name-list> ]
"""
[docs]
match = re.compile(r"import(\b|\Z)", re.I).match
class Forall(Statement):
"""
FORALL <forall-header> <forall-assignment-stmt>
<forall-header> = ( <forall-triplet-spec-list> [ , <scalar-mask-expr> ] )
<forall-triplet-spec> =
<index-name> = <subscript> : <subscript> [ : <stride> ]
<subscript|stride> = <scalar-int-expr>
<forall-assignment-stmt> = <assignment-stmt> | <pointer-assignment-stmt>
"""
match = re.compile(r"forall\s*\(.*\).*=", re.I).match
def process_item(self):
line = self.item.get_line()[6:].lstrip()
i = line.index(")")
line0 = line[1:i]
line = line[i + 1 :].lstrip()
stmt = GeneralAssignment(self, self.item.copy(line, True))
if stmt.isvalid:
self.content = [stmt]
else:
self.isvalid = False
return
specs = []
mask = ""
for l in split_comma(line0, self.item):
j = l.find("=")
if j == -1:
assert not mask, repr((mask, l))
mask = l
continue
assert j != -1, repr(l)
index = l[:j].rstrip()
it = self.item.copy(l[j + 1 :].lstrip())
line = it.get_line()
k = line.split(":")
if len(k) == 3:
s1, s2, s3 = list(
map(it.apply_map, [k[0].strip(), k[1].strip(), k[2].strip()])
)
else:
assert len(k) == 2, repr(k)
s1, s2 = list(map(it.apply_map, [k[0].strip(), k[1].strip()]))
s3 = "1"
specs.append((index, s1, s2, s3))
self.specs = specs
self.mask = mask
return
def tofortran(self, isfix=None):
tab = self.get_indent_tab(isfix=isfix)
lines = []
for index, s1, s2, s3 in self.specs:
s = "%s = %s : %s" % (index, s1, s2)
if s3 != "1":
s += " : %s" % (s3)
lines.append(s)
s = ", ".join(lines)
if self.mask:
s += ", " + self.mask
return tab + "FORALL (%s) %s" % (s, str(self.content[0]).lstrip())
def analyze(self):
return
[docs]
class SpecificBinding(Statement):
"""
PROCEDURE [ ( <interface-name> ) ] [ [ , <binding-attr-list> ] :: ]
<binding-name> [ => <procedure-name> ]
<binding-attr> = PASS [ ( <arg-name> ) ]
| NOPASS
| NON_OVERRIDABLE
| DEFERRED
| <access-spec>
<access-spec> = PUBLIC | PRIVATE
"""
[docs]
match = re.compile(r"procedure\b", re.I).match
[docs]
def process_item(self):
line = self.item.get_line()[9:].lstrip()
if line.startswith("("):
i = line.index(")")
name = line[1:i].strip()
line = line[i + 1 :].lstrip()
else:
name = ""
self.iname = name
if line.startswith(","):
line = line[1:].lstrip()
i = line.find("::")
if i != -1:
attrs = split_comma(line[:i], self.item)
line = line[i + 2 :].lstrip()
else:
attrs = []
attrs1 = []
for attr in attrs:
if is_name(attr):
attr = attr.upper()
else:
i = attr.find("(")
assert i != -1 and attr.endswith(")"), repr(attr)
attr = "%s (%s)" % (attr[:i].rstrip().upper(), attr[i + 1 : -1].strip())
attrs1.append(attr)
self.attrs = attrs1
i = line.find("=")
if i == -1:
self.name = line
self.bname = ""
else:
self.name = line[:i].rstrip()
self.bname = line[i + 1 :].lstrip()[1:].lstrip()
return
[docs]
def tofortran(self, isfix=None):
tab = self.get_indent_tab(isfix=isfix)
s = "PROCEDURE "
if self.iname:
s += "(" + self.iname + ") "
if self.attrs:
s += ", " + ", ".join(self.attrs) + " :: "
if self.bname:
s += "%s => %s" % (self.name, self.bname)
else:
s += self.name
return tab + s
[docs]
class GenericBinding(Statement):
"""
GENERIC [ , <access-spec> ] :: <generic-spec> => <binding-name-list>
"""
[docs]
match = re.compile(r"generic\b.*::.*=\>.*\Z", re.I).match
[docs]
def process_item(self):
line = self.item.get_line()[7:].lstrip()
if line.startswith(","):
line = line[1:].lstrip()
i = line.index("::")
self.aspec = line[:i].rstrip().upper()
line = line[i + 2 :].lstrip()
i = line.index("=>")
self.spec = self.item.apply_map(line[:i].rstrip())
self.items = split_comma(line[i + 2 :].lstrip())
return
[docs]
def tofortran(self, isfix=None):
tab = self.get_indent_tab(isfix=isfix)
s = "GENERIC"
if self.aspec:
s += ", " + self.aspec
s += " :: " + self.spec + " => " + ", ".join(self.items)
return tab + s
[docs]
class FinalBinding(StatementWithNamelist):
"""
FINAL [ :: ] <final-subroutine-name-list>
"""
[docs]
match = re.compile(r"final\b", re.I).match
[docs]
class Allocatable(Statement):
"""
ALLOCATABLE [ :: ] <object-name> [ ( <deferred-shape-spec-list> ) ]
[ , <object-name>
[ ( <deferred-shape-spec-list> ) ]
]...
"""
[docs]
match = re.compile(r"allocatable\b", re.I).match
[docs]
def process_item(self):
line = self.item.get_line()[11:].lstrip()
if line.startswith("::"):
line = line[2:].lstrip()
self.items = split_comma(line, self.item)
return
[docs]
def tofortran(self, isfix=None):
return self.get_indent_tab(isfix=isfix) + "ALLOCATABLE " + ", ".join(self.items)
[docs]
def analyze(self):
for line in self.items:
i = line.find("(")
if i == -1:
name = line
array_spec = None
else:
assert line.endswith(")")
name = line[:i].rstrip()
array_spec = split_comma(line[i + 1 : -1], self.item)
var = self.get_variable(name)
var.update("allocatable")
if array_spec is not None:
var.set_bounds(array_spec)
return
[docs]
class Asynchronous(StatementWithNamelist):
"""
ASYNCHRONOUS [ :: ] <object-name-list>
"""
[docs]
match = re.compile(r"asynchronous\b", re.I).match
[docs]
def analyze(self):
for name in self.items:
var = self.get_variable(name)
var.update("asynchronous")
return
[docs]
class Bind(Statement):
"""
::
language-binding-spec [ :: ] bind-entity-list
language-binding-spec is
BIND ( C [ , NAME = scalar-char-initialization-expr ] )
bind-entity is entity-name> | / common-block-name /
"""
[docs]
match = re.compile(r"bind\s*\(.*\)", re.I).match
[docs]
def process_item(self):
line = self.item.line
self.specs, line = parse_bind(line, self.item)
if line.startswith("::"):
line = line[2:].lstrip()
items = []
for item in split_comma(line, self.item):
if item.startswith("/"):
assert item.endswith("/"), repr(item)
item = "/ " + item[1:-1].strip() + " /"
items.append(item)
self.items = items
return
[docs]
def tofortran(self, isfix=None):
return self.get_indent_tab(isfix=isfix) + "BIND (%s) %s" % (
", ".join(self.specs),
", ".join(self.items),
)
# IF construct statements
[docs]
class Else(Statement):
"""
ELSE [<if-construct-name>]
"""
[docs]
match = re.compile(r"else\b\s*\w*\s*\Z", re.I).match
[docs]
def process_item(self):
item = self.item
self.name = item.get_line()[4:].strip()
parent_name = getattr(self.parent, "name", "")
if self.name and self.name != parent_name:
self.warning(
"expected if-construct-name %r but got %r, skipping."
% (parent_name, self.name)
)
self.isvalid = False
return
[docs]
def tofortran(self, isfix=None):
if self.name:
return self.get_indent_tab(deindent=True) + "ELSE " + self.name
return self.get_indent_tab(deindent=True) + "ELSE"
[docs]
def analyze(self):
return
[docs]
class ElseIf(Statement):
"""
ELSE IF ( <scalar-logical-expr> ) THEN [ <if-construct-name> ]
"""
[docs]
match = re.compile(r"else\s*if\s*\(.*\)\s*then\s*\w*\s*\Z", re.I).match
[docs]
def process_item(self):
item = self.item
line = item.get_line()[4:].lstrip()[2:].lstrip()
i = line.find(")")
assert line[0] == "("
self.expr = item.apply_map(line[1:i])
self.name = line[i + 1 :].lstrip()[4:].strip()
parent_name = getattr(self.parent, "name", "")
if self.name and self.name != parent_name:
self.warning(
"expected if-construct-name %r but got %r, skipping."
% (parent_name, self.name)
)
self.isvalid = False
return
[docs]
def tofortran(self, isfix=None):
s = ""
if self.name:
s = " " + self.name
return self.get_indent_tab(deindent=True) + "ELSE IF (%s) THEN%s" % (
self.expr,
s,
)
[docs]
def analyze(self):
return
# SelectCase construct statements
[docs]
class Case(Statement):
"""
Captures a Case statement::
CASE case-selector [ case-construct-name ]
case-selector is ( case-value-range-list ) | DEFAULT
case-value-range is case-value
or case-value :
or : case-value
or case-value : case-value
case-value is scalar-(int|char|logical)-initialization-expr
"""
[docs]
match = re.compile(r"case\b\s*(\(.*\)|DEFAULT)\s*\w*\Z", re.I).match
[docs]
def process_item(self):
"""Populate the state of this item by parsing the associated line
of code"""
line = self.item.get_line()[4:].lstrip()
try:
self.items = extract_bracketed_list_items(line, self.item)
idx = line.rfind(")")
self.name = line[idx + 1 :].lstrip()
except ParseError:
# No list in parentheses found so we must have a 'case default'
if not line.lower().startswith("default"):
# We should never get to here because such a string should
# not have generated a match
self.warning(
"Internal error when parsing CASE statement: {0}".format(line)
)
self.isvalid = False
return
self.items = []
self.name = line[7:].lstrip()
parent_name = getattr(self.parent, "name", "")
if self.name and self.name != parent_name:
self.warning(
"Expected case-construct-name {0} but got {1}, "
"skipping.".format(parent_name, self.name)
)
self.isvalid = False
return
[docs]
def tofortran(self, isfix=None):
"""Return the Fortran for this object as a string"""
tab = self.get_indent_tab(isfix=isfix)
txt = tab + "CASE"
if self.items:
lst = []
for item in self.items:
lst.append((" : ".join(item)).strip())
txt += " ( %s )" % (", ".join(lst))
else:
txt += " DEFAULT"
if self.name:
txt += " " + self.name
return txt
[docs]
def analyze(self):
return
[docs]
class TypeIs(Statement):
"""
TYPE IS <type-selector> [ <case-construct-name> ]
<type-selector> = ( <type-value-range-list> )
<type-value-range> = <case-value>
<case-value> = <char>
"""
[docs]
match = re.compile(r"type\b\s*is\b\s*\(.*\)\s*\w*\Z", re.I).match
[docs]
def process_item(self):
"""Populate the state of this object by parsing the associated
line of code"""
line = self.item.get_line()
# We have a 'type is (...)' statement. At this point
# any expression used for the type specifier will have
# been replaced with e.g. F2PY_EXPR_TUPLE_3 and so
# will not itself contain any parentheses.
self.items = extract_bracketed_list_items(line, self.item)
# Get and store the case-construct-name (if any)
idx2 = line.rfind(")")
self.name = line[idx2 + 1 :].lstrip()
parent_name = getattr(self.parent, "name", "")
if self.name and self.name != parent_name:
self.warning(
"expected type-is-construct-name %r but got %r, skipping."
% (parent_name, self.name)
)
self.isvalid = False
return
[docs]
def tofortran(self, isfix=None):
"""Create the Fortran representation of this object and return
it as a string"""
tab = self.get_indent_tab(isfix=isfix)
text = tab + "TYPE IS"
if self.items:
lst = []
for item in self.items:
lst.append((" : ".join(item)).strip())
text += " ( %s )" % (", ".join(lst))
else:
raise ParseError("TYPE IS construct must have arguments")
if self.name:
text += " " + self.name
return text
[docs]
def analyze(self):
return
[docs]
class ClassIs(Statement):
"""
CLASS <class-selector>
<class-selector> = ( IS <type-value-range-list> | DEFAULT )
<type-value-range> = <case-value>
<case-value> = <char>
"""
[docs]
match = re.compile(r"class\b\s*(is\b\s*\(.*\)|default)\s*\w*\Z", re.I).match
[docs]
def process_item(self):
"""Populate the state of this object by parsing the string"""
line = self.item.get_line()[5:].lstrip()
try:
self.items = extract_bracketed_list_items(line, self.item)
# We have a 'class is ...' statement. At this point
# any expression used for the class specifier will have
# been replaced with e.g. F2PY_EXPR_TUPLE_3 and so
# will not contain any parentheses.
idx = line.rfind(")")
self.name = line[idx + 1 :].lstrip()
except ParseError:
# We have a 'class default' statement
if not line.lower().startswith("default"):
# We should never get here because such a string should
# not have generated a match
self.warning(
"Internal error when parsing CLASS statement: {0}".format(line)
)
self.isvalid = False
return
self.items = []
self.name = line[7:].lstrip()
parent_name = getattr(self.parent, "name", "")
if self.name and self.name != parent_name:
self.warning(
"expected class-construct-name %r but got %r, skipping."
% (parent_name, self.name)
)
self.isvalid = False
return
[docs]
def tofortran(self, isfix=None):
"""Returns the Fortran for this object as a string"""
tab = self.get_indent_tab(isfix=isfix)
text = tab + "CLASS"
if self.items:
text += " IS"
lchar = []
for item in self.items:
lchar.append((" : ".join(item)).strip())
text += " ( %s )" % (", ".join(lchar))
else:
text += " DEFAULT"
if self.name:
text += " " + self.name
return text
[docs]
def analyze(self):
return
# Where construct statements
class Where(Statement):
"""
WHERE ( <mask-expr> ) <where-assignment-stmt>
"""
match = re.compile(r"where\s*\(.*\)\s*\w.*\Z", re.I).match
def process_item(self):
line = self.item.get_line()[5:].lstrip()
i = line.index(")")
self.expr = self.item.apply_map(line[1:i].strip())
line = line[i + 1 :].lstrip()
newitem = self.item.copy(line)
cls = Assignment
if cls.match(line):
stmt = cls(self, newitem)
if stmt.isvalid:
self.content = [stmt]
return
self.isvalid = False
return
def tofortran(self, isfix=None):
tab = self.get_indent_tab(isfix=isfix)
return tab + "WHERE ( %s ) %s" % (self.expr, str(self.content[0]).lstrip())
def analyze(self):
return
[docs]
class ElseWhere(Statement):
"""
ELSE WHERE ( <mask-expr> ) [ <where-construct-name> ]
ELSE WHERE [ <where-construct-name> ]
"""
[docs]
match = re.compile(r"else\s*where\b", re.I).match
[docs]
def process_item(self):
line = self.item.get_line()[4:].lstrip()[5:].lstrip()
self.expr = None
if line.startswith("("):
i = line.index(")")
assert i != -1, repr(line)
self.expr = self.item.apply_map(line[1:i].strip())
line = line[i + 1 :].lstrip()
self.name = line
parent_name = getattr(self.parent, "name", "")
if self.name and not self.name == parent_name:
self.warning(
"expected where-construct-name %r but got %r, "
"skipping." % (parent_name, self.name)
)
self.isvalid = False
return
[docs]
def tofortran(self, isfix=None):
tab = self.get_indent_tab(isfix=isfix)
s = "ELSE WHERE"
if self.expr is not None:
s += " ( %s )" % (self.expr)
if self.name:
s += " " + self.name
return tab + s
[docs]
def analyze(self):
return
# Enum construct statements
[docs]
class Enumerator(Statement):
"""
ENUMERATOR [ :: ] <enumerator-list>
<enumerator> = <named-constant> [ = <scalar-int-initialization-expr> ]
"""
[docs]
match = re.compile(r"enumerator\b", re.I).match
[docs]
def process_item(self):
line = self.item.get_line()[10:].lstrip()
if line.startswith("::"):
line = line[2:].lstrip()
self.items = split_comma(line, self.item)
return
[docs]
def tofortran(self, isfix=None):
return self.get_indent_tab(isfix=isfix) + "ENUMERATOR " + ", ".join(self.items)
# F2PY specific statements
[docs]
class FortranName(Statement):
"""
FORTRANNAME <name>
"""
[docs]
match = re.compile(r"fortranname\s*\w+\Z", re.I).match
[docs]
def process_item(self):
self.value = self.item.get_line()[11:].lstrip()
return
[docs]
def tofortran(self, isfix=None):
return self.get_indent_tab(isfix=isfix) + "FORTRANNAME " + self.value
[docs]
class Threadsafe(Statement):
"""
THREADSAFE
"""
[docs]
match = re.compile(r"threadsafe\Z", re.I).match
[docs]
def process_item(self):
return
[docs]
def tofortran(self, isfix=None):
return self.get_indent_tab(isfix=isfix) + "THREADSAFE"
[docs]
class Depend(Statement):
"""
DEPEND ( <name-list> ) [ :: ] <dummy-arg-name-list>
"""
[docs]
match = re.compile(r"depend\s*\(", re.I).match
[docs]
def process_item(self):
line = self.item.get_line()[6:].lstrip()
i = line.find(")")
self.depends = split_comma(line[1:i].strip(), self.item)
line = line[i + 1 :].lstrip()
if line.startswith("::"):
line = line[2:].lstrip()
self.items = split_comma(line)
return
[docs]
def tofortran(self, isfix=None):
return self.get_indent_tab(isfix=isfix) + "DEPEND ( %s ) %s" % (
", ".join(self.depends),
", ".join(self.items),
)
[docs]
class Check(Statement):
"""
CHECK ( <c-int-scalar-expr> ) [ :: ] <name>
"""
[docs]
match = re.compile(r"check\s*\(", re.I).match
[docs]
def process_item(self):
line = self.item.get_line()[5:].lstrip()
i = line.find(")")
assert i != -1, repr(line)
self.expr = self.item.apply_map(line[1:i].strip())
line = line[i + 1 :].lstrip()
if line.startswith("::"):
line = line[2:].lstrip()
self.value = line
return
[docs]
def tofortran(self, isfix=None):
return self.get_indent_tab(isfix=isfix) + "CHECK ( %s ) %s" % (
self.expr,
self.value,
)
[docs]
class CallStatement(Statement):
"""
CALLSTATEMENT <c-expr>
"""
[docs]
match = re.compile(r"callstatement\b", re.I).match
[docs]
def process_item(self):
self.expr = self.item.apply_map(self.item.get_line()[13:].lstrip())
return
[docs]
def tofortran(self, isfix=None):
return self.get_indent_tab(isfix=isfix) + "CALLSTATEMENT " + self.expr
[docs]
class CallProtoArgument(Statement):
"""
CALLPROTOARGUMENT <c-type-spec-list>
"""
[docs]
match = re.compile(r"callprotoargument\b", re.I).match
[docs]
def process_item(self):
self.specs = self.item.apply_map(self.item.get_line()[17:].lstrip())
return
[docs]
def tofortran(self, isfix=None):
return self.get_indent_tab(isfix=isfix) + "CALLPROTOARGUMENT " + self.specs
# Non-standard statements
[docs]
class Pause(Statement):
"""
PAUSE [ <char-literal-constant|int-literal-constant> ]
"""
[docs]
match = re.compile(r'pause\s*(\d+|\'\w*\'|"\w*"|)\Z', re.I).match
[docs]
def process_item(self):
self.value = self.item.apply_map(self.item.get_line()[5:].lstrip())
return
[docs]
def tofortran(self, isfix=None):
if self.value:
return self.get_indent_tab(isfix=isfix) + "PAUSE " + self.value
return self.get_indent_tab(isfix=isfix) + "PAUSE"
[docs]
def analyze(self):
return