mirror of
https://github.com/cp2k/cp2k.git
synced 2026-07-21 14:35:15 -04:00
1318 lines
47 KiB
Python
1318 lines
47 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import sys
|
|
import re
|
|
import logging
|
|
from collections import deque
|
|
from io import StringIO
|
|
|
|
# Declare various RE snippets for building larger REs
|
|
|
|
# Valid fortran argument/variable name
|
|
VALID_NAME = r"[a-zA-Z][a-zA-Z_0-9]*"
|
|
|
|
# Set up valid type declaration
|
|
DECL_KIND_KW = rf"\( (?:kind = )? (?:{VALID_NAME}|[0-9]+) \)".replace(" ", r"\s*")
|
|
DECL_KIND_STAR = r"\s*\*\s*[0-9]+"
|
|
CHAR_KIND_SPEC = rf"(?:(?:len|kind) = )? (?:{VALID_NAME}|[0-9]+|\*|:)"
|
|
CHAR_KIND_SPEC = rf"\( {CHAR_KIND_SPEC}(?: , {CHAR_KIND_SPEC}) \)".replace(" ", r"\s*")
|
|
DECL_KIND_SPEC = rf"\s*(?:{DECL_KIND_STAR}|{DECL_KIND_KW})"
|
|
DECL_TYPE_DEF = rf"""(?:
|
|
(?:integer|real|complex)(?:{DECL_KIND_SPEC})?|
|
|
character(?:{DECL_KIND_STAR}|{CHAR_KIND_SPEC})?|
|
|
logical(?:{DECL_KIND_KW})?|type|class)"""
|
|
|
|
# String styles
|
|
DQUOTED = r'"[^"]*"'
|
|
SQUOTED = r"'[^']*'"
|
|
QUOTED = rf"(?:{DQUOTED}|{SQUOTED})"
|
|
|
|
# Stop at punctuation
|
|
NOT_PUNC = r"[^()\"'\[\]]"
|
|
NOT_PUNC_COMMA = r"[^()\"',\[\]]"
|
|
NOT_BRAC = r"[^()]"
|
|
|
|
# Enable new-style fortran arrays (square brackets)
|
|
BRAC_OPEN = r"[(\[]"
|
|
BRAC_CLOSE = r"[)\]]"
|
|
NOT_PAREN = r"[^()]"
|
|
|
|
# Nested parens for up to 3-level nesting (1st level's parens are outside this variable)
|
|
# Should more than 3 levels be necessary, need to reduplicate this deeper.
|
|
NESTED_PAREN_1TO3_CONTENTS = (
|
|
rf"(?:{NOT_PAREN}+|\((?:{NOT_PAREN}+|\({NOT_PAREN}*\))*\))*"
|
|
)
|
|
|
|
# Variable statement
|
|
VAR_RE = re.compile(
|
|
rf"""
|
|
\s*(?P<var>{VALID_NAME})
|
|
\s*(?P<rest>(:?\((?P<param>{NESTED_PAREN_1TO3_CONTENTS})\))?
|
|
\s*(?:(?P<assignment>=>?)\s*(?P<value>(?:
|
|
{NOT_PUNC_COMMA}+|
|
|
{BRAC_OPEN}(?:{NOT_PUNC}|{BRAC_OPEN}{NOT_PUNC}*{BRAC_CLOSE}|{QUOTED})*{BRAC_CLOSE}|
|
|
{QUOTED})+))?)?
|
|
\s*(?:(?P<continue>,)|\n?)
|
|
\s*""",
|
|
re.IGNORECASE | re.VERBOSE,
|
|
)
|
|
|
|
# Use statement
|
|
USE_PARSE_RE = re.compile(
|
|
rf"""\s*
|
|
use(\s+|(?P<intrinsic>\s*,\s*Intrinsic\s+::\s*))
|
|
(?P<module>{VALID_NAME})
|
|
(?P<only>\s*,\s*only\s*:)?\s*
|
|
(?P<imports>.*)$""",
|
|
flags=re.IGNORECASE | re.VERBOSE,
|
|
)
|
|
|
|
COMMON_USES_RE = re.compile(r'^#include\s*"([^"]*(cp_common_uses.f90|base_uses.f90))"')
|
|
LOCAL_NAME_RE = re.compile(
|
|
rf"\s*(?P<localName>{VALID_NAME})(?:\s*=>\s*{VALID_NAME})?\s*$", re.VERBOSE
|
|
)
|
|
VAR_DECL_RE = re.compile(
|
|
rf"""
|
|
\s*(?P<type>{DECL_TYPE_DEF})
|
|
\s*(?P<parameters>\({NESTED_PAREN_1TO3_CONTENTS}\))?
|
|
\s*(?P<attributes>(?:\s*,\s*[a-zA-Z_0-9]+(?:\s*\({NESTED_PAREN_1TO3_CONTENTS}\))?)+)?\s*
|
|
(?P<dpnt>::)?
|
|
(?P<vars>[^\n]+)\n?
|
|
""",
|
|
re.IGNORECASE | re.VERBOSE,
|
|
)
|
|
INDENT_SIZE = 2
|
|
DECL_LINELENGTH = 100
|
|
DECL_OFFSET = 50
|
|
|
|
OMP_DIR_RE = re.compile(r"^\s*(!\$omp)", re.IGNORECASE)
|
|
OMP_RE = re.compile(r"^\s*(!\$)", re.IGNORECASE)
|
|
|
|
FCT_RE = re.compile(
|
|
r"^([^\"'!]* )?FUNCTION\s+\w+\s*(\(.*\))?(\s*RESULT\s*\(\w+\))?\s*;?\s*$",
|
|
re.IGNORECASE,
|
|
)
|
|
|
|
SUBR_RE = re.compile(
|
|
r"^([^\"'!]* )?SUBROUTINE\s+\w+\s*(\(.*\))?\s*;?\s*$", re.IGNORECASE
|
|
)
|
|
|
|
END_RE = re.compile(r" *end\s*(?:subroutine|function)", re.IGNORECASE)
|
|
START_ROUTINE_RE = re.compile(
|
|
r"^([^\"'!]* )?(?P<kind>subroutine|function) +(?P<name>[a-zA-Z_][a-zA-Z_0-9]*) *(?:\((?P<arguments>[^()]*)\))? *(?:result *\( *(?P<result>[a-zA-Z_][a-zA-Z_0-9]*) *\))? *(?:bind *\([^()]+\))? *\n?",
|
|
re.IGNORECASE,
|
|
) # $
|
|
typeBeginRe = re.compile(
|
|
r" *(?P<type>integer(?: *\* *[0-9]+)?|logical|character(?: *\* *[0-9]+)?|real(?: *\* *[0-9]+)?|complex(?: *\* *[0-9]+)?|type)[,( ]",
|
|
re.IGNORECASE,
|
|
)
|
|
attributeRe = re.compile(
|
|
r" *, *(?P<attribute>[a-zA-Z_0-9]+) *(?:\( *(?P<param>(?:[^()]+|\((?:[^()]+|\([^()]*\))*\))*)\))? *",
|
|
re.IGNORECASE,
|
|
)
|
|
IGNORE_RE = re.compile(r" *(?:|implicit +none *)$", re.IGNORECASE)
|
|
INTERFACE_START_RE = re.compile(r" *interface *$", re.IGNORECASE)
|
|
INTERFACE_END_RE = re.compile(r" *end +interface *$", re.IGNORECASE)
|
|
|
|
INCLUDE_RE = re.compile(r"#? *include +[\"'](?P<file>.+)[\"'] *$", re.IGNORECASE)
|
|
|
|
CONTAINS_RE = re.compile(r" *contains *$", re.IGNORECASE)
|
|
|
|
NON_WORD_RE = re.compile(r"(\(/|/\)|[^-+a-zA-Z0-9_.])")
|
|
|
|
STR_RE = re.compile(r"('[^'\n]*'|\"[^\"\n]*\")")
|
|
|
|
MODULE_N_RE = re.compile(
|
|
r".*:: *moduleN *= *(['\"])[a-zA-Z_0-9]+\1", flags=re.IGNORECASE
|
|
)
|
|
|
|
MODULE_RE = re.compile(
|
|
r" *(?:module|program) +(?P<moduleName>[a-zA-Z_][a-zA-Z_0-9]*) *(?:!.*)?$",
|
|
flags=re.IGNORECASE,
|
|
)
|
|
|
|
|
|
class InputStreamError(Exception):
|
|
pass
|
|
|
|
|
|
class CharFilter(object):
|
|
"""
|
|
An iterator to wrap the iterator returned by `enumerate`
|
|
and ignore comments and characters inside strings
|
|
"""
|
|
|
|
def __init__(self, it):
|
|
self._it = it
|
|
self._instring = ""
|
|
|
|
def __iter__(self):
|
|
return self
|
|
|
|
def __next__(self):
|
|
"""python 3 version"""
|
|
pos, char = next(self._it)
|
|
if not self._instring and char == "!":
|
|
raise StopIteration
|
|
|
|
# detect start/end of a string
|
|
if char == '"' or char == "'":
|
|
if self._instring == char:
|
|
self._instring = ""
|
|
elif not self._instring:
|
|
self._instring = char
|
|
|
|
if self._instring:
|
|
return self.__next__()
|
|
|
|
return (pos, char)
|
|
|
|
def next(self):
|
|
"""python 2 version"""
|
|
pos, char = self._it.next()
|
|
if not self._instring and char == "!":
|
|
raise StopIteration
|
|
|
|
# detect start/end of a string
|
|
if char == '"' or char == "'":
|
|
if self._instring == char:
|
|
self._instring = ""
|
|
elif not self._instring:
|
|
self._instring = char
|
|
|
|
if self._instring:
|
|
return self.next()
|
|
|
|
return (pos, char)
|
|
|
|
|
|
class InputStream(object):
|
|
"""
|
|
Class to read logical Fortran lines from a Fortran file.
|
|
"""
|
|
|
|
def __init__(self, infile):
|
|
self.line_buffer = deque([])
|
|
self.infile = infile
|
|
self.line_nr = 0
|
|
|
|
def nextFortranLine(self):
|
|
"""Reads a group of connected lines (connected with &, separated by newline or semicolon)
|
|
returns a touple with the joined line, and a list with the original lines.
|
|
Doesn't support multiline character constants!
|
|
"""
|
|
lineRe = re.compile(
|
|
# $
|
|
r"(?:(?P<preprocessor>#.*\n?)| *(&)?(?P<core>(?:!\$|[^&!\"']+|\"[^\"]*\"|'[^']*')*)(?P<continue>&)? *(?P<comment>!.*)?\n?)",
|
|
re.IGNORECASE,
|
|
)
|
|
joinedLine = ""
|
|
comments = []
|
|
lines = []
|
|
continuation = 0
|
|
|
|
while True:
|
|
if not self.line_buffer:
|
|
line = self.infile.readline().replace("\t", 8 * " ")
|
|
self.line_nr += 1
|
|
# convert OMP-conditional fortran statements into normal fortran statements
|
|
# but remember to convert them back
|
|
is_omp_conditional = False
|
|
omp_indent = 0
|
|
if OMP_RE.match(line):
|
|
omp_indent = len(line) - len(line.lstrip(" "))
|
|
line = OMP_RE.sub("", line, count=1)
|
|
is_omp_conditional = True
|
|
line_start = 0
|
|
for pos, char in CharFilter(enumerate(line)):
|
|
if char == ";" or pos + 1 == len(line):
|
|
self.line_buffer.append(
|
|
omp_indent * " "
|
|
+ "!$" * is_omp_conditional
|
|
+ line[line_start : pos + 1]
|
|
)
|
|
omp_indent = 0
|
|
is_omp_conditional = False
|
|
line_start = pos + 1
|
|
if line_start < len(line):
|
|
# line + comment
|
|
self.line_buffer.append(
|
|
"!$" * is_omp_conditional + line[line_start:]
|
|
)
|
|
|
|
if self.line_buffer:
|
|
line = self.line_buffer.popleft()
|
|
|
|
if not line:
|
|
break
|
|
|
|
lines.append(line)
|
|
m = lineRe.match(line)
|
|
if not m or m.span()[1] != len(line):
|
|
# FIXME: does not handle line continuation of
|
|
# omp conditional fortran statements
|
|
# starting with an ampersand.
|
|
raise InputStreamError("unexpected line format:" + repr(line))
|
|
if m.group("preprocessor"):
|
|
if len(lines) > 1:
|
|
raise InputStreamError(
|
|
"continuation to a preprocessor line not supported "
|
|
+ repr(line)
|
|
)
|
|
comments.append(line)
|
|
break
|
|
coreAtt = m.group("core")
|
|
if OMP_RE.match(coreAtt) and joinedLine.strip():
|
|
# remove omp '!$' for line continuation
|
|
coreAtt = OMP_RE.sub("", coreAtt, count=1).lstrip()
|
|
joinedLine = joinedLine.rstrip("\n") + coreAtt
|
|
if coreAtt and not coreAtt.isspace():
|
|
continuation = 0
|
|
if m.group("continue"):
|
|
continuation = 1
|
|
if line.lstrip().startswith("!") and not OMP_RE.search(line):
|
|
comments.append(line.rstrip("\n"))
|
|
elif m.group("comment"):
|
|
comments.append(m.group("comment"))
|
|
else:
|
|
comments.append("")
|
|
if not continuation:
|
|
break
|
|
return (joinedLine, comments, lines)
|
|
|
|
|
|
def parseRoutine(inFile, logger):
|
|
"""Parses a routine"""
|
|
|
|
routine = {
|
|
"preRoutine": [],
|
|
"core": [],
|
|
"strippedCore": [],
|
|
"begin": [],
|
|
"end": [],
|
|
"preDeclComments": [],
|
|
"declarations": [],
|
|
"declComments": [],
|
|
"postDeclComments": [],
|
|
"parsedDeclarations": [],
|
|
"postRoutine": [],
|
|
"kind": None,
|
|
"name": None,
|
|
"arguments": None,
|
|
"result": None,
|
|
"interfaceCount": 0,
|
|
"use": [],
|
|
}
|
|
stream = InputStream(inFile)
|
|
while True:
|
|
jline, _, lines = stream.nextFortranLine()
|
|
if len(lines) == 0:
|
|
break
|
|
if FCT_RE.match(jline) or SUBR_RE.match(jline):
|
|
break
|
|
routine["preRoutine"].extend(lines)
|
|
m = INCLUDE_RE.match(lines[0])
|
|
if m:
|
|
try:
|
|
subF = open(m.group("file"), "r", encoding="utf8")
|
|
subStream = InputStream(subF)
|
|
while True:
|
|
subjline, _, sublines = subStream.nextFortranLine()
|
|
if not sublines:
|
|
break
|
|
routine["strippedCore"].append(subjline)
|
|
subF.close()
|
|
except Exception:
|
|
import traceback
|
|
|
|
logger.debug(
|
|
"error trying to follow include '{}', this might lead to the removal of used variables".format(
|
|
m.group("file")
|
|
)
|
|
)
|
|
|
|
if logger.isEnabledFor(logging.DEBUG):
|
|
traceback.print_exc()
|
|
|
|
if jline:
|
|
routine["begin"] = lines
|
|
m = START_ROUTINE_RE.match(jline)
|
|
if not m or m.span()[1] != len(jline):
|
|
raise SyntaxError("unexpected subroutine start format:" + repr(lines))
|
|
routine["name"] = m.group("name")
|
|
routine["kind"] = m.group("kind")
|
|
if m.group("arguments") and m.group("arguments").strip():
|
|
routine["arguments"] = list(
|
|
x.strip() for x in m.group("arguments").split(",")
|
|
)
|
|
if m.group("result"):
|
|
routine["result"] = m.group("result")
|
|
if (not routine["result"]) and (routine["kind"].lower() == "function"):
|
|
routine["result"] = routine["name"]
|
|
|
|
while True:
|
|
jline, comment_list, lines = stream.nextFortranLine()
|
|
comments = "\n".join(_ for _ in comment_list)
|
|
if len(lines) == 0:
|
|
break
|
|
if lines[0].lower().startswith("#include"):
|
|
break
|
|
if not IGNORE_RE.match(jline):
|
|
if typeBeginRe.match(jline):
|
|
if routine["postDeclComments"]:
|
|
routine["declComments"].extend(routine["postDeclComments"])
|
|
routine["postDeclComments"] = []
|
|
|
|
if typeBeginRe.match(jline):
|
|
m = VAR_DECL_RE.match(jline)
|
|
if m.group("type").lower() == "type" and not m.group("parameters"):
|
|
break
|
|
if not m or m.span()[1] != len(jline):
|
|
raise SyntaxError("unexpected type format:" + repr(jline))
|
|
decl = {
|
|
"type": m.group("type"),
|
|
"parameters": None,
|
|
"attributes": [],
|
|
"vars": [],
|
|
}
|
|
if m.group("parameters"):
|
|
decl["parameters"] = (
|
|
m.group("parameters").replace(" ", "").replace(",", ", ")
|
|
)
|
|
str = m.group("attributes")
|
|
while str:
|
|
m2 = attributeRe.match(str)
|
|
if not m2:
|
|
raise SyntaxError(
|
|
"unexpected attribute format "
|
|
+ repr(str)
|
|
+ " in "
|
|
+ repr(lines)
|
|
)
|
|
decl["attributes"].append(
|
|
m2.group().replace(" ", "").replace(",", ", ")[2:]
|
|
)
|
|
str = str[m2.span()[1] :]
|
|
str = m.group("vars")
|
|
while True:
|
|
m2 = VAR_RE.match(str)
|
|
if not m2:
|
|
raise SyntaxError(
|
|
"unexpected var format " + repr(str) + " in " + repr(lines)
|
|
)
|
|
var = m2.group("var")
|
|
if m2.group("param"):
|
|
var += "(" + m2.group("param") + ")"
|
|
if m2.group("value"):
|
|
var += " {} ".format(m2["assignment"])
|
|
var += m2.group("value")
|
|
decl["vars"].append(var)
|
|
str = str[m2.span()[1] :]
|
|
if not m2.group("continue"):
|
|
if str:
|
|
raise SyntaxError(
|
|
"error parsing vars (leftover="
|
|
+ repr(str)
|
|
+ ") in "
|
|
+ repr(lines)
|
|
)
|
|
break
|
|
routine["parsedDeclarations"].append(decl)
|
|
elif INTERFACE_START_RE.match(jline):
|
|
istart = lines
|
|
interfaceDeclFile = StringIO()
|
|
while True:
|
|
jline, _, lines = stream.nextFortranLine()
|
|
if INTERFACE_END_RE.match(jline):
|
|
iend = lines
|
|
break
|
|
interfaceDeclFile.writelines(lines)
|
|
interfaceDeclFile = StringIO(interfaceDeclFile.getvalue())
|
|
iroutines = []
|
|
while True:
|
|
iroutine = parseRoutine(interfaceDeclFile, logger)
|
|
if not iroutine["kind"]:
|
|
if len(iroutines) == 0:
|
|
interfaceDeclFile.seek(0)
|
|
raise SyntaxError(
|
|
"error parsing interface:"
|
|
+ repr(interfaceDeclFile.read())
|
|
)
|
|
iroutines[-1]["postRoutine"].extend(iroutine["preRoutine"])
|
|
break
|
|
iroutines.append(iroutine)
|
|
for iroutine in iroutines:
|
|
routine["interfaceCount"] += 1
|
|
decl = {
|
|
"type": "z_interface%02d" % (routine["interfaceCount"]),
|
|
"parameters": None,
|
|
"attributes": [],
|
|
"vars": [iroutine["name"]],
|
|
"iroutine": iroutine,
|
|
"istart": istart,
|
|
"iend": iend,
|
|
}
|
|
routine["parsedDeclarations"].append(decl)
|
|
elif USE_PARSE_RE.match(jline):
|
|
routine["use"].append("".join(lines))
|
|
else:
|
|
break
|
|
routine["declarations"].append("".join(lines))
|
|
if (
|
|
len(routine["parsedDeclarations"]) == 0
|
|
and len(routine["use"]) == 0
|
|
and not re.match(" *implicit +none *$", jline, re.IGNORECASE)
|
|
):
|
|
routine["preDeclComments"].append("".join(lines))
|
|
else:
|
|
routine["postDeclComments"].append(comments)
|
|
|
|
while len(lines) > 0:
|
|
if END_RE.match(jline):
|
|
routine["end"] = lines
|
|
break
|
|
routine["strippedCore"].append(jline)
|
|
routine["core"].append("".join(lines))
|
|
if CONTAINS_RE.match(lines[0]):
|
|
break
|
|
m = INCLUDE_RE.match(lines[0])
|
|
if m:
|
|
try:
|
|
subF = open(m.group("file"), "r", encoding="utf8")
|
|
subStream = InputStream(subF)
|
|
while True:
|
|
subjline, _, sublines = subStream.nextFortranLine()
|
|
if not sublines:
|
|
break
|
|
routine["strippedCore"].append(subjline)
|
|
subF.close()
|
|
except Exception:
|
|
import traceback
|
|
|
|
logger.debug(
|
|
"error trying to follow include '{}', this might lead to the removal of used variables".format(
|
|
m.group("file")
|
|
)
|
|
)
|
|
|
|
if logger.isEnabledFor(logging.DEBUG):
|
|
traceback.print_exc()
|
|
|
|
jline, _, lines = stream.nextFortranLine()
|
|
return routine
|
|
|
|
|
|
def findWord(word, text, options=re.IGNORECASE):
|
|
"""Returns the position of word in text or -1 if not found.
|
|
A match is valid only if it is a whole word (i.e. findWord('try','retry')
|
|
returns false)"""
|
|
wordRe = re.compile(
|
|
"(?<![a-zA-Z_0-9%])"
|
|
+ word
|
|
+ "(?![a-zA-Z_0-9])|(?<=[0-9.]_)"
|
|
+ word
|
|
+ "(?![a-zA-Z_0-9])",
|
|
options,
|
|
)
|
|
m = wordRe.search(text)
|
|
if m:
|
|
pos = m.span()[0]
|
|
else:
|
|
pos = -1
|
|
return pos
|
|
|
|
|
|
def enforceDeclDependecies(declarations):
|
|
"""enforces the dependencies between the vars
|
|
and compacts the declarations, returns the variables needed by other variables"""
|
|
idecl = 0
|
|
ii = 0
|
|
while idecl < len(declarations):
|
|
typeParam = "".join(declarations[idecl]["attributes"])
|
|
if declarations[idecl]["parameters"]:
|
|
typeParam += " " + declarations[idecl]["parameters"]
|
|
typeParam = typeParam.lower()
|
|
|
|
ivar = 0
|
|
while ivar < len(declarations[idecl]["vars"]):
|
|
moved = 0
|
|
m = VAR_RE.match(declarations[idecl]["vars"][ivar])
|
|
if not m:
|
|
raise SyntaxError(
|
|
"could not match var " + repr(declarations[idecl]["vars"][ivar])
|
|
)
|
|
rest = m.group("rest")
|
|
rest = rest.lower()
|
|
if rest:
|
|
for ivar2 in range(ivar + 1, len(declarations[idecl]["vars"])):
|
|
m = VAR_RE.match(declarations[idecl]["vars"][ivar2])
|
|
if findWord(m.group("var").lower(), rest) != -1:
|
|
moved = ivar2 + 1
|
|
if moved:
|
|
declarations[idecl]["vars"][moved:moved] = [
|
|
declarations[idecl]["vars"][ivar]
|
|
]
|
|
del declarations[idecl]["vars"][ivar]
|
|
else:
|
|
for idecl2 in range(idecl + 1, len(declarations)):
|
|
for ivar2 in range(len(declarations[idecl2]["vars"])):
|
|
ii += 1
|
|
if ii > 100000:
|
|
raise Error("could not enforce all constraints")
|
|
m = VAR_RE.match(declarations[idecl2]["vars"][ivar2])
|
|
if (
|
|
ivar == 0
|
|
and findWord(m.group("var").lower(), typeParam) != -1
|
|
):
|
|
declarations.insert(idecl2 + 1, declarations[idecl])
|
|
del declarations[idecl]
|
|
ivar = 0
|
|
moved = 1
|
|
break
|
|
if rest and findWord(m.group("var").lower(), rest) != -1:
|
|
if len(declarations[idecl]["vars"]) > 1:
|
|
newDecl = {}
|
|
newDecl.update(declarations[idecl])
|
|
newDecl["vars"] = [declarations[idecl]["vars"][ivar]]
|
|
declarations.insert(idecl2 + 1, newDecl)
|
|
del declarations[idecl]["vars"][ivar]
|
|
else:
|
|
declarations.insert(idecl2 + 1, declarations[idecl])
|
|
del declarations[idecl]
|
|
ivar = 0
|
|
moved = 1
|
|
break
|
|
if moved:
|
|
break
|
|
if not moved:
|
|
ivar += 1
|
|
idecl += 1
|
|
|
|
for i in range(len(declarations) - 1, 0, -1):
|
|
if (
|
|
declarations[i]["normalizedType"].lower()
|
|
== declarations[i - 1]["normalizedType"].lower()
|
|
):
|
|
declarations[i - 1]["vars"].extend(declarations[i]["vars"])
|
|
del declarations[i]
|
|
|
|
|
|
def sortDeclarations(declarations):
|
|
"""sorts, compacts declarations and respects dependencies
|
|
normalizedType has to be defined for the declarations"""
|
|
|
|
declarations.sort(key=lambda x: x["normalizedType"].lower())
|
|
|
|
for i in range(len(declarations) - 1, 0, -1):
|
|
if (
|
|
declarations[i]["normalizedType"].lower()
|
|
== declarations[i - 1]["normalizedType"].lower()
|
|
):
|
|
declarations[i - 1]["vars"].extend(declarations[i]["vars"])
|
|
del declarations[i]
|
|
|
|
for decl in declarations:
|
|
decl["vars"].sort(key=lambda x: x.lower())
|
|
enforceDeclDependecies(declarations)
|
|
|
|
|
|
def writeRoutine(routine, outFile):
|
|
"""writes the given routine to outFile"""
|
|
outFile.writelines(routine["preRoutine"])
|
|
outFile.writelines(routine["begin"])
|
|
outFile.writelines(routine["declarations"])
|
|
outFile.writelines(routine["core"])
|
|
outFile.writelines(routine["end"])
|
|
outFile.writelines(routine["postRoutine"])
|
|
|
|
|
|
def writeInCols(dLine, indentCol, maxCol, indentAtt, file):
|
|
"""writes out the strings (trying not to cut them) in dLine up to maxCol
|
|
indenting each newline with indentCol.
|
|
The '&' of the continuation line is at maxCol.
|
|
indentAtt is the actual intent, and the new indent is returned"""
|
|
|
|
maxSize = maxCol - indentCol - 1
|
|
tol = min(maxSize / 6, 6) + indentCol
|
|
for fragment in dLine:
|
|
if indentAtt + len(fragment) < maxCol:
|
|
file.write(fragment)
|
|
indentAtt += len(fragment)
|
|
elif len(fragment.lstrip()) <= maxSize:
|
|
file.write("&\n" + (" " * indentCol))
|
|
file.write(fragment.lstrip())
|
|
indentAtt = indentCol + len(fragment.lstrip())
|
|
else:
|
|
sPieces = STR_RE.split(fragment)
|
|
for sPiece in sPieces:
|
|
if sPiece and (not (sPiece[0] == '"' or sPiece[0] == "'")):
|
|
subPieces = NON_WORD_RE.split(sPiece)
|
|
else:
|
|
subPieces = [sPiece]
|
|
for subPiece in subPieces:
|
|
if indentAtt == indentCol:
|
|
file.write(subPiece.lstrip())
|
|
indentAtt += len(subPiece.lstrip())
|
|
elif indentAtt < tol or indentAtt + len(subPiece) < maxCol:
|
|
file.write(subPiece)
|
|
indentAtt += len(subPiece)
|
|
else:
|
|
file.write("&\n" + (" " * indentCol))
|
|
file.write(subPiece.lstrip())
|
|
indentAtt = indentCol + len(subPiece.lstrip())
|
|
return indentAtt
|
|
|
|
|
|
def writeCompactDeclaration(declaration, file):
|
|
"""Writes a declaration in a compact way"""
|
|
d = declaration
|
|
if "iroutine" in d.keys():
|
|
file.writelines(d["istart"])
|
|
writeRoutine(d["iroutine"], file)
|
|
file.writelines(d["iend"])
|
|
else:
|
|
if len(d["vars"]) > 0:
|
|
decl = " " * INDENT_SIZE * 2 + d["type"]
|
|
if d["parameters"]: # do not drop empty parameter lists?
|
|
decl += d["parameters"]
|
|
if d["attributes"]:
|
|
for a in d["attributes"]:
|
|
decl += ", " + a
|
|
decl += " :: "
|
|
|
|
dLine = [decl]
|
|
for var in d["vars"]:
|
|
cur_len = sum([len(l) for l in dLine])
|
|
if len(dLine) > 1 and cur_len + len(var) > 600:
|
|
writeInCols(dLine, 3 * INDENT_SIZE, DECL_LINELENGTH, 0, file)
|
|
file.write("\n")
|
|
dLine = [decl]
|
|
if len(dLine) > 1:
|
|
dLine[-1] += ", "
|
|
dLine.append(var)
|
|
writeInCols(dLine, 3 * INDENT_SIZE, DECL_LINELENGTH, 0, file)
|
|
file.write("\n")
|
|
|
|
|
|
def writeExtendedDeclaration(declaration, file):
|
|
"""Writes a declaration in a nicer way (using more space)"""
|
|
d = declaration
|
|
if len(d["vars"]) == 0:
|
|
return
|
|
if "iroutine" in d.keys():
|
|
file.writelines(d["istart"])
|
|
writeRoutine(d["iroutine"], file)
|
|
file.writelines(d["iend"])
|
|
else:
|
|
dLine = []
|
|
dLine.append(" " * INDENT_SIZE * 2 + d["type"])
|
|
if d["parameters"]: # do not drop empty parameter lists?
|
|
dLine.append(d["parameters"])
|
|
if d["attributes"]:
|
|
for a in d["attributes"]:
|
|
dLine[-1:] = [dLine[-1] + ", "]
|
|
dLine.append(a)
|
|
|
|
indentAtt = writeInCols(
|
|
dLine, 3 * INDENT_SIZE, DECL_OFFSET + 1 + 2 * INDENT_SIZE, 0, file
|
|
)
|
|
file.write(" " * (DECL_OFFSET + 2 * INDENT_SIZE - indentAtt))
|
|
file.write(" :: ")
|
|
indentAtt = DECL_OFFSET + 8
|
|
|
|
dLine = []
|
|
for var in d["vars"][:-1]:
|
|
dLine.append(var + ", ")
|
|
dLine.append(d["vars"][-1])
|
|
|
|
writeInCols(
|
|
dLine, DECL_OFFSET + 4 + 2 * INDENT_SIZE, DECL_LINELENGTH, indentAtt, file
|
|
)
|
|
file.write("\n")
|
|
|
|
|
|
def writeDeclarations(parsedDeclarations, file):
|
|
"""Writes the declarations to the given file"""
|
|
for d in parsedDeclarations:
|
|
maxLenVar = 0
|
|
totalLen = 0
|
|
for v in d["vars"]:
|
|
maxLenVar = max(maxLenVar, len(v))
|
|
totalLen += len(v)
|
|
if maxLenVar > 30 or totalLen > DECL_LINELENGTH - 4:
|
|
writeCompactDeclaration(d, file)
|
|
else:
|
|
writeExtendedDeclaration(d, file)
|
|
|
|
|
|
def cleanDeclarations(routine, logger):
|
|
"""cleans up the declaration part of the given parsed routine
|
|
removes unused variables"""
|
|
|
|
if routine["core"]:
|
|
if CONTAINS_RE.match(routine["core"][-1]):
|
|
logger.debug(
|
|
"routine %s contains other routines, declarations not cleaned"
|
|
% (routine["name"])
|
|
)
|
|
return
|
|
nullifyRe = re.compile(
|
|
r" *nullify *\(([^()]+)\) *\n?", re.IGNORECASE | re.MULTILINE
|
|
)
|
|
|
|
if not routine["kind"]:
|
|
return
|
|
if routine["core"]:
|
|
if re.match(" *type *[a-zA-Z_]+ *$", routine["core"][0], re.IGNORECASE):
|
|
logger.debug(
|
|
"routine %s contains local types, not fully cleaned" % (routine["name"])
|
|
)
|
|
if re.match(" *import+ *$", routine["core"][0], re.IGNORECASE):
|
|
logger.debug(
|
|
"routine %s contains import, not fully cleaned" % (routine["name"])
|
|
)
|
|
if re.search("^#", "".join(routine["declarations"]), re.MULTILINE):
|
|
logger.debug(
|
|
"routine %s declarations contain preprocessor directives, declarations not cleaned"
|
|
% (routine["name"])
|
|
)
|
|
return
|
|
try:
|
|
rest = "".join(routine["strippedCore"]).lower()
|
|
nullifys = ",".join(nullifyRe.findall(rest))
|
|
rest = nullifyRe.sub("", rest)
|
|
paramDecl = []
|
|
decls = []
|
|
for d in routine["parsedDeclarations"]:
|
|
d["normalizedType"] = d["type"]
|
|
if d["parameters"]:
|
|
d["normalizedType"] += d["parameters"]
|
|
if d["attributes"]:
|
|
d["attributes"].sort(key=lambda x: x.lower())
|
|
d["normalizedType"] += ", "
|
|
d["normalizedType"] += ", ".join(d["attributes"])
|
|
if any(a.lower() == "parameter" for a in d["attributes"]):
|
|
paramDecl.append(d)
|
|
else:
|
|
decls.append(d)
|
|
|
|
sortDeclarations(paramDecl)
|
|
sortDeclarations(decls)
|
|
has_routinen = 0
|
|
pos_routinep = -1
|
|
for d in paramDecl:
|
|
for i in range(len(d["vars"])):
|
|
v = d["vars"][i]
|
|
m = VAR_RE.match(v)
|
|
lowerV = m.group("var").lower()
|
|
if lowerV == "routinen":
|
|
has_routinen = 1
|
|
d["vars"][i] = "routineN = '" + routine["name"] + "'"
|
|
elif lowerV == "routinep":
|
|
pos_routinep = i
|
|
d["vars"][i] = "routineP = moduleN//':'//routineN"
|
|
if not has_routinen and pos_routinep >= 0:
|
|
d["vars"].insert(pos_routinep, "routineN = '" + routine["name"] + "'")
|
|
|
|
if routine["arguments"]:
|
|
routine["lowercaseArguments"] = list(
|
|
x.lower() for x in routine["arguments"]
|
|
)
|
|
else:
|
|
routine["lowercaseArguments"] = []
|
|
if routine["result"]:
|
|
routine["lowercaseArguments"].append(routine["result"].lower())
|
|
argDeclDict = {}
|
|
localDecl = []
|
|
for d in decls:
|
|
localD = {}
|
|
localD.update(d)
|
|
localD["vars"] = []
|
|
argD = None
|
|
for v in d["vars"]:
|
|
m = VAR_RE.match(v)
|
|
lowerV = m.group("var").lower()
|
|
if lowerV in routine["lowercaseArguments"]:
|
|
argD = {}
|
|
argD.update(d)
|
|
argD["vars"] = [v]
|
|
if lowerV in argDeclDict.keys():
|
|
raise SyntaxError(
|
|
"multiple declarations not supported. var="
|
|
+ v
|
|
+ " declaration="
|
|
+ str(d)
|
|
+ "routine="
|
|
+ routine["name"]
|
|
)
|
|
argDeclDict[lowerV] = argD
|
|
else:
|
|
pos = findWord(lowerV, rest)
|
|
if pos != -1:
|
|
localD["vars"].append(v)
|
|
else:
|
|
if findWord(lowerV, nullifys) != -1:
|
|
if not rmNullify(lowerV, routine["core"]):
|
|
raise SyntaxError(
|
|
"could not remove nullify of "
|
|
+ lowerV
|
|
+ " as expected, routine="
|
|
+ routine["name"]
|
|
)
|
|
logger.info(
|
|
"removed var %s in routine %s\n" % (lowerV, routine["name"])
|
|
)
|
|
if len(localD["vars"]):
|
|
localDecl.append(localD)
|
|
argDecl = []
|
|
for arg in routine["lowercaseArguments"]:
|
|
if arg in argDeclDict.keys():
|
|
argDecl.append(argDeclDict[arg])
|
|
else:
|
|
logger.debug(
|
|
"warning, implicitly typed argument '{}' in routine '{}'".format(
|
|
arg, routine["name"]
|
|
)
|
|
)
|
|
|
|
if routine["kind"].lower() == "function":
|
|
aDecl = argDecl[:-1]
|
|
else:
|
|
aDecl = argDecl
|
|
|
|
# try to have arg/param/local, but checks for dependencies arg/param
|
|
# and param/local
|
|
argDecl.extend(paramDecl)
|
|
enforceDeclDependecies(argDecl)
|
|
splitPos = 0
|
|
for i in range(len(argDecl) - 1, -1, -1):
|
|
if not any(a.lower() == "parameter" for a in argDecl[i]["attributes"]):
|
|
splitPos = i + 1
|
|
break
|
|
paramDecl = argDecl[splitPos:]
|
|
argDecl = argDecl[:splitPos]
|
|
paramDecl.extend(localDecl)
|
|
enforceDeclDependecies(paramDecl)
|
|
splitPos = 0
|
|
for i in range(len(paramDecl) - 1, -1, -1):
|
|
if any(a.lower() == "parameter" for a in paramDecl[i]["attributes"]):
|
|
splitPos = i + 1
|
|
break
|
|
localDecl = paramDecl[splitPos:]
|
|
paramDecl = paramDecl[:splitPos]
|
|
|
|
newDecl = StringIO()
|
|
for comment in routine["preDeclComments"]:
|
|
newDecl.write(comment)
|
|
newDecl.writelines(routine["use"])
|
|
writeDeclarations(argDecl, newDecl)
|
|
if argDecl and paramDecl:
|
|
newDecl.write("\n")
|
|
writeDeclarations(paramDecl, newDecl)
|
|
if (argDecl or paramDecl) and localDecl:
|
|
newDecl.write("\n")
|
|
writeDeclarations(localDecl, newDecl)
|
|
if argDecl or paramDecl or localDecl:
|
|
newDecl.write("\n")
|
|
wrote = 0
|
|
for comment in routine["declComments"]:
|
|
if comment.strip():
|
|
newDecl.write(comment.strip())
|
|
newDecl.write("\n")
|
|
wrote = 1
|
|
if wrote:
|
|
newDecl.write("\n")
|
|
routine["declarations"] = [newDecl.getvalue()]
|
|
except Exception:
|
|
if "name" in routine.keys():
|
|
logger.critical("exception cleaning routine " + routine["name"])
|
|
logger.critical("parsedDeclartions={}".format(routine["parsedDeclarations"]))
|
|
raise
|
|
|
|
newDecl = StringIO()
|
|
if routine["postDeclComments"]:
|
|
comment_start = 0
|
|
for comment in routine["postDeclComments"]:
|
|
if comment.strip():
|
|
break
|
|
else:
|
|
comment_start += 1
|
|
|
|
for comment in routine["postDeclComments"][comment_start:]:
|
|
newDecl.write(comment)
|
|
newDecl.write("\n")
|
|
routine["declarations"][0] += newDecl.getvalue()
|
|
|
|
|
|
def rmNullify(var, strings):
|
|
removed = 0
|
|
var = var.lower()
|
|
nullifyRe = re.compile(r" *nullify *\(", re.IGNORECASE)
|
|
nullify2Re = re.compile(
|
|
r"(?P<nullif> *nullify *\()(?P<vars>[^()!&]+)\)", re.IGNORECASE
|
|
)
|
|
|
|
for i in range(len(strings) - 1, -1, -1):
|
|
line = strings[i]
|
|
comments = []
|
|
if nullifyRe.match(line) and findWord(var, line) != -1:
|
|
core = ""
|
|
comments = []
|
|
for l in line.splitlines():
|
|
pos = l.find("&")
|
|
pos2 = l.find("!")
|
|
if pos == -1:
|
|
if pos2 == -1:
|
|
core += l
|
|
else:
|
|
core += l[:pos2]
|
|
comments.append(l[pos2:] + "\n")
|
|
else:
|
|
core += l[:pos]
|
|
if pos2 != -1:
|
|
comments.append(l[pos2:] + "\n")
|
|
m = nullify2Re.match(core)
|
|
if not m:
|
|
raise SyntaxError(
|
|
"could not match nullify to " + repr(core) + "in" + repr(line)
|
|
)
|
|
allVars = []
|
|
vars = m.group("vars")
|
|
v = list(s.strip() for s in vars.split(","))
|
|
removedNow = 0
|
|
for j in range(len(v) - 1, -1, -1):
|
|
if findWord(var, v[j].lower()) != -1:
|
|
del v[j]
|
|
removedNow = 1
|
|
if removedNow:
|
|
if len(v) == 0:
|
|
if not comments:
|
|
del strings[i]
|
|
else:
|
|
strings[i] = "".join(comments)
|
|
else:
|
|
for j in range(len(v) - 1):
|
|
v[j] += ", "
|
|
v[-1] += ")"
|
|
newS = StringIO()
|
|
v.insert(0, m.group("nullif"))
|
|
writeInCols(v, len(v[0]) - len(v[0].lstrip()) + 5, 77, 0, newS)
|
|
newS.write("\n")
|
|
if comments:
|
|
for c in comments:
|
|
newS.write(c)
|
|
strings[i] = newS.getvalue()
|
|
removed += 1
|
|
return removed
|
|
|
|
|
|
def parseUse(inFile):
|
|
"""Parses the use statements in inFile
|
|
The parsing stops at the first non use statement.
|
|
Returns something like:
|
|
([{'module':'module1','only':['el1','el2=>el3']},...],
|
|
'! comment1\\n!comment2...\\n',
|
|
'last line (the line that stopped the parsing)')
|
|
"""
|
|
lineNr = 0
|
|
preComments = []
|
|
modules = []
|
|
origLines = []
|
|
commonUses = ""
|
|
stream = InputStream(inFile)
|
|
while True:
|
|
jline, comment_list, lines = stream.nextFortranLine()
|
|
comments = "\n".join(_ for _ in comment_list if _)
|
|
lineNr = lineNr + len(lines)
|
|
if not lines:
|
|
break
|
|
origLines.append("".join(lines))
|
|
# parse use
|
|
m = USE_PARSE_RE.match(jline)
|
|
if m:
|
|
useAtt = {"module": m.group("module"), "comments": []}
|
|
|
|
if m.group("only"):
|
|
useAtt["only"] = list(s.strip() for s in m.group("imports").split(","))
|
|
else:
|
|
useAtt["renames"] = list(
|
|
s.strip() for s in m.group("imports").split(",")
|
|
)
|
|
if useAtt["renames"] == [""]:
|
|
del useAtt["renames"]
|
|
if comments:
|
|
useAtt["comments"].append(comments)
|
|
# add use to modules
|
|
modules.append(useAtt)
|
|
elif jline and not jline.isspace():
|
|
break
|
|
else:
|
|
if comments and COMMON_USES_RE.match(comments):
|
|
commonUses += "".join(lines)
|
|
elif len(modules) == 0:
|
|
preComments.append(("".join(lines)))
|
|
elif comments:
|
|
modules[-1]["comments"].append(comments)
|
|
|
|
return {
|
|
"modules": modules,
|
|
"preComments": preComments,
|
|
"commonUses": commonUses,
|
|
"postLine": "".join(lines),
|
|
"origLines": origLines[:-1],
|
|
}
|
|
|
|
|
|
def normalizeModules(modules):
|
|
"""Sorts the modules and their export and removes duplicates.
|
|
renames aren't sorted correctly"""
|
|
# orders modules
|
|
modules.sort(key=lambda x: x["module"])
|
|
for i in range(len(modules) - 1, 0, -1):
|
|
if modules[i]["module"].lower() == modules[i - 1]["module"].lower():
|
|
if not ("only" in modules[i - 1].keys() and "only" in modules[i].keys()):
|
|
raise SyntaxError(
|
|
"rejoining of module "
|
|
+ str(modules[i]["module"])
|
|
+ " failed as at least one of the use is not a use ...,only:"
|
|
)
|
|
modules[i - 1]["only"].extend(modules[i]["only"])
|
|
del modules[i]
|
|
# orders imports
|
|
for m in modules:
|
|
if "only" in m.keys():
|
|
m["only"].sort()
|
|
for i in range(len(m["only"]) - 1, 0, -1):
|
|
if m["only"][i - 1].lower() == m["only"][i].lower():
|
|
del m["only"][i]
|
|
|
|
|
|
def writeUses(modules, outFile):
|
|
"""Writes the use declaration using a long or short form depending on how
|
|
many only statements there are"""
|
|
for m in modules:
|
|
if "only" in m.keys() and len(m["only"]) > 8:
|
|
writeUseShort(m, outFile)
|
|
else:
|
|
writeUseLong(m, outFile)
|
|
|
|
|
|
def writeUseLong(m, outFile):
|
|
"""Writes a use declaration in a nicer, but longer way"""
|
|
if "only" in m.keys():
|
|
outFile.write(
|
|
INDENT_SIZE * " "
|
|
+ "USE "
|
|
+ m["module"]
|
|
+ ","
|
|
+ "ONLY: ".rjust(38 - len(m["module"]))
|
|
)
|
|
if m["only"]:
|
|
outFile.write(m["only"][0])
|
|
for i in range(1, len(m["only"])):
|
|
outFile.write(",&\n" + "".ljust(43 + INDENT_SIZE) + m["only"][i])
|
|
else:
|
|
outFile.write(INDENT_SIZE * " " + "USE " + m["module"])
|
|
if "renames" in m.keys() and m["renames"]:
|
|
outFile.write("," + "".ljust(38) + m["renames"][0])
|
|
for i in range(1, len(m["renames"])):
|
|
outFile.write(",&\n" + "".ljust(43 + INDENT_SIZE) + m["renames"][i])
|
|
if m["comments"]:
|
|
outFile.write("\n")
|
|
outFile.write("\n".join(m["comments"]))
|
|
outFile.write("\n")
|
|
|
|
|
|
def writeUseShort(m, file):
|
|
"""Writes a use declaration in a compact way"""
|
|
uLine = []
|
|
if "only" in m.keys():
|
|
file.write(
|
|
INDENT_SIZE * " "
|
|
+ "USE "
|
|
+ m["module"]
|
|
+ ","
|
|
+ "ONLY: &\n".rjust(40 - len(m["module"]))
|
|
)
|
|
for k in m["only"][:-1]:
|
|
uLine.append(k + ", ")
|
|
uLine.append(m["only"][-1])
|
|
uLine[0] = " " * (5 + INDENT_SIZE) + uLine[0]
|
|
elif "renames" in m.keys() and m["renames"]:
|
|
uLine.append(INDENT_SIZE * " " + "USE " + m["module"] + ", ")
|
|
for k in m["renames"][:-1]:
|
|
uLine.append(k + ", ")
|
|
uLine.append(m["renames"][-1])
|
|
else:
|
|
uLine.append(INDENT_SIZE * " " + "USE " + m["module"])
|
|
writeInCols(uLine, 5 + INDENT_SIZE, DECL_LINELENGTH, 0, file)
|
|
if m["comments"]:
|
|
file.write("\n")
|
|
file.write("\n".join(m["comments"]))
|
|
file.write("\n")
|
|
|
|
|
|
def prepareImplicitUses(modules):
|
|
"""Transforms a modulesDict into an implictUses (dictionary of module names
|
|
each containing a dictionary with the only, and the special key '_WHOLE_'
|
|
which is true if the whole module is implicitly present"""
|
|
mods = {}
|
|
for m in modules:
|
|
m_name = m["module"].lower()
|
|
if m_name not in mods.keys():
|
|
mods[m["module"]] = {"_WHOLE_": 0}
|
|
m_att = mods[m_name]
|
|
if "only" in m.keys():
|
|
for k in m["only"]:
|
|
m = LOCAL_NAME_RE.match(k)
|
|
if not m:
|
|
raise SyntaxError("could not parse use only:" + repr(k))
|
|
impAtt = m.group("localName").lower()
|
|
m_att[impAtt] = 1
|
|
else:
|
|
m_att["_WHOLE_"] = 1
|
|
return mods
|
|
|
|
|
|
def cleanUse(modulesDict, rest, implicitUses, logger):
|
|
"""Removes the unneeded modules (the ones that are not used in rest)"""
|
|
|
|
exceptions = {}
|
|
modules = modulesDict["modules"]
|
|
rest = rest.lower()
|
|
|
|
for i in range(len(modules) - 1, -1, -1):
|
|
m_att = {}
|
|
m_name = modules[i]["module"].lower()
|
|
if implicitUses and m_name in implicitUses.keys():
|
|
m_att = implicitUses[m_name]
|
|
if "_WHOLE_" in m_att.keys() and m_att["_WHOLE_"]:
|
|
logger.info("removed USE of module " + m_name)
|
|
del modules[i]
|
|
elif "only" in modules[i].keys():
|
|
els = modules[i]["only"]
|
|
for j in range(len(els) - 1, -1, -1):
|
|
m = LOCAL_NAME_RE.match(els[j])
|
|
if not m:
|
|
raise SyntaxError("could not parse use only:" + repr(els[j]))
|
|
impAtt = m.group("localName").lower()
|
|
if impAtt in m_att.keys():
|
|
logger.info("removed USE " + m_name + ", only: " + repr(els[j]))
|
|
del els[j]
|
|
elif impAtt not in exceptions.keys():
|
|
if findWord(impAtt, rest) == -1:
|
|
logger.info("removed USE " + m_name + ", only: " + repr(els[j]))
|
|
del els[j]
|
|
if len(modules[i]["only"]) == 0:
|
|
if modules[i]["comments"]:
|
|
modulesDict["preComments"] += [
|
|
x + "\n" for x in modules[i]["comments"]
|
|
]
|
|
del modules[i]
|
|
|
|
|
|
def resetModuleN(moduleName, lines):
|
|
"resets the moduleN variable to the module name in the lines lines"
|
|
for i in range(len(lines)):
|
|
lines[i] = MODULE_N_RE.sub(
|
|
" " * INDENT_SIZE
|
|
+ "CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = '"
|
|
+ moduleName
|
|
+ "'",
|
|
lines[i],
|
|
)
|
|
|
|
|
|
def rewriteFortranFile(
|
|
inFile, outFile, indent, decl_linelength, decl_offset, orig_filename=None
|
|
):
|
|
"""rewrites the use statements and declarations of inFile to outFile.
|
|
It sorts them and removes the repetitions."""
|
|
import os.path
|
|
|
|
global INDENT_SIZE
|
|
global DECL_OFFSET
|
|
global DECL_LINELENGTH
|
|
INDENT_SIZE = indent
|
|
DECL_OFFSET = decl_offset
|
|
DECL_LINELENGTH = decl_linelength
|
|
|
|
coreLines = []
|
|
while True:
|
|
line = inFile.readline()
|
|
if not line:
|
|
break
|
|
if line[0] == "#":
|
|
coreLines.append(line)
|
|
outFile.write(line)
|
|
m = MODULE_RE.match(line)
|
|
if m:
|
|
if not orig_filename:
|
|
orig_filename = inFile.name
|
|
fn = os.path.basename(orig_filename).rsplit(".", 1)[0]
|
|
break
|
|
|
|
logger = logging.LoggerAdapter(
|
|
logging.getLogger("prettify-logger"), {"ffilename": orig_filename}
|
|
)
|
|
|
|
modulesDict = parseUse(inFile)
|
|
routines = []
|
|
coreLines.append(modulesDict["postLine"])
|
|
routine = parseRoutine(inFile, logger)
|
|
coreLines.extend(routine["preRoutine"])
|
|
|
|
if m:
|
|
resetModuleN(m.group("moduleName"), routine["preRoutine"])
|
|
|
|
routines.append(routine)
|
|
|
|
while routine["kind"]:
|
|
routine = parseRoutine(inFile, logger)
|
|
routines.append(routine)
|
|
|
|
for routine in routines:
|
|
cleanDeclarations(routine, logger) # in-place modification of 'routine'
|
|
coreLines.extend(routine["declarations"])
|
|
coreLines.extend(routine["strippedCore"])
|
|
|
|
rest = "".join(coreLines)
|
|
nonStPrep = 0
|
|
|
|
for line in modulesDict["origLines"]:
|
|
if re.search("^#", line) and not COMMON_USES_RE.match(line):
|
|
logger.debug("noMatch " + repr(line) + "\n") # what does it mean?
|
|
nonStPrep = 1
|
|
|
|
if nonStPrep:
|
|
logger.debug("use statements contains preprocessor directives, not cleaning")
|
|
outFile.writelines(modulesDict["origLines"])
|
|
else:
|
|
implicitUses = None
|
|
if modulesDict["commonUses"]:
|
|
inc_fn = COMMON_USES_RE.match(modulesDict["commonUses"]).group(1)
|
|
inc_absfn = os.path.join(os.path.dirname(orig_filename), inc_fn)
|
|
try:
|
|
with open(inc_absfn, "r", encoding="utf8") as fhandle:
|
|
implicitUsesRaw = parseUse(fhandle)
|
|
implicitUses = prepareImplicitUses(implicitUsesRaw["modules"])
|
|
except Exception:
|
|
logger.critical(
|
|
"failed to parse use statements contained in common uses precompiler file {}".format(
|
|
inc_absfn
|
|
)
|
|
)
|
|
raise
|
|
|
|
cleanUse(modulesDict, rest, implicitUses, logger)
|
|
normalizeModules(modulesDict["modules"])
|
|
outFile.writelines(modulesDict["preComments"])
|
|
writeUses(modulesDict["modules"], outFile)
|
|
outFile.write(modulesDict["commonUses"])
|
|
if modulesDict["modules"]:
|
|
outFile.write("\n")
|
|
|
|
outFile.write(modulesDict["postLine"])
|
|
|
|
for routine in routines:
|
|
writeRoutine(routine, outFile)
|