mirror of https://gitlab.com/scemama/irpf90.git
1027 lines
33 KiB
Python
1027 lines
33 KiB
Python
#!/usr/bin/env python
|
|
# IRPF90 is a Fortran90 preprocessor written in Python for programming using
|
|
# the Implicit Reference to Parameters (IRP) method.
|
|
# Copyright (C) 2009 Anthony SCEMAMA
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License along
|
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
#
|
|
# Anthony Scemama
|
|
# LCPQ - IRSAMC - CNRS
|
|
# Universite Paul Sabatier
|
|
# 118, route de Narbonne
|
|
# 31062 Toulouse Cedex 4
|
|
# scemama@irsamc.ups-tlse.fr
|
|
|
|
from irpf90_t import *
|
|
from regexps import *
|
|
from command_line import command_line
|
|
from util import *
|
|
import sys
|
|
|
|
# Local regular expressions
|
|
re_endif = re.compile("end +if")
|
|
re_elseif = re.compile("else +if")
|
|
re_enddo = re.compile("end +do")
|
|
re_endwhere = re.compile("end +where")
|
|
|
|
re_endtype = re.compile("end +type.*")
|
|
re_endmodule = re.compile("end +module", re.I)
|
|
re_endselect = re.compile("end +select")
|
|
re_endinterface = re.compile("end +interface")
|
|
|
|
# Local variables
|
|
Free_form = 0
|
|
Fixed_form = 1
|
|
|
|
######################################################################
|
|
# Dictionary of simple statements
|
|
simple_dict = {
|
|
"program": Program,
|
|
"subroutine": Subroutine,
|
|
"begin_shell": Begin_shell,
|
|
"end_shell": End_shell,
|
|
"begin_template": Begin_template,
|
|
"end_template": End_template,
|
|
"subst": Subst,
|
|
"end_doc": End_doc,
|
|
"begin_provider": Begin_provider,
|
|
"begin_provider_immutable": Begin_provider,
|
|
"&begin_provider": Cont_provider,
|
|
"&begin_provider_immutable": Cont_provider,
|
|
"end_provider": End_provider,
|
|
"end_provider_immutable": End_provider,
|
|
"assert": Assert,
|
|
"touch": Touch,
|
|
"soft_touch": SoftTouch,
|
|
"provide": Provide,
|
|
"no_dep": NoDep,
|
|
"free": Free,
|
|
"irp_if": Irp_If,
|
|
"irp_else": Irp_Else,
|
|
"irp_endif": Irp_Endif,
|
|
"irp_read": Irp_read,
|
|
"irp_write": Irp_write,
|
|
"use": Use,
|
|
"do": Do,
|
|
"if": If,
|
|
"case": Case,
|
|
"elseif": Elseif,
|
|
"else": Else,
|
|
"enddo": Enddo,
|
|
"endif": Endif,
|
|
"endselect": End_select,
|
|
"end": End,
|
|
"include": Include,
|
|
"call": Call,
|
|
"continue": Continue,
|
|
"return": Return,
|
|
"implicit": Implicit,
|
|
"save": Declaration,
|
|
"function": Function,
|
|
"recursive": Function,
|
|
"select": Select,
|
|
"selectcase": Select,
|
|
"module": Module,
|
|
"endmodule": End_module,
|
|
"interface": Interface,
|
|
"endinterface": End_interface,
|
|
"where": Where,
|
|
"elsewhere": Elsewhere,
|
|
"endwhere": Endwhere,
|
|
}
|
|
|
|
|
|
def get_canonized_text(text_lower):
|
|
|
|
text_canonized = text_lower
|
|
text_canonized = re_elseif.sub("elseif", text_canonized)
|
|
text_canonized = re_enddo.sub("enddo", text_canonized)
|
|
text_canonized = re_endtype.sub("endtype", text_canonized)
|
|
text_canonized = re_endmodule.sub("endmodule", text_canonized)
|
|
text_canonized = re_endif.sub("endif", text_canonized)
|
|
text_canonized = re_endselect.sub("endselect", text_canonized)
|
|
text_canonized = re_endinterface.sub("endinterface", text_canonized)
|
|
text_canonized = re_endwhere.sub('endwhere', text_canonized)
|
|
|
|
for c in """()'"[]""":
|
|
text_canonized = text_canonized.replace(c, " %s " % c)
|
|
return text_canonized
|
|
|
|
|
|
def get_type(i, filename, line, line_lower, line_lower_canonized, is_doc):
|
|
# ( int,str,str,str,str,bool) -> Irpf90_t
|
|
'''Find the type of a text line'''
|
|
|
|
line = line.rstrip()
|
|
l_word = line_lower_canonized.split()
|
|
|
|
if not l_word:
|
|
return [Empty_line(i, line, filename)], is_doc
|
|
|
|
# Handle archaic do loop of f77
|
|
firstword = l_word[0]
|
|
if firstword.isdigit():
|
|
l_word = l_word[1:]
|
|
firstword = l_word[0]
|
|
|
|
if firstword == "contains":
|
|
return [Contains(i, line, filename)], False
|
|
if firstword == "end_doc":
|
|
return [End_doc(i, line, filename)], False
|
|
|
|
if firstword == "begin_doc":
|
|
return [Begin_doc(i, line, filename)], True
|
|
|
|
if is_doc:
|
|
return [Doc(i, line, filename)], is_doc
|
|
|
|
if firstword in simple_dict:
|
|
type_ = simple_dict[firstword]
|
|
return [type_(i, line, filename)], is_doc
|
|
|
|
#label do-loop (outer: do i=1,sze)
|
|
reg_do_lab = ur":\s+do\s+"
|
|
if re.search(reg_do_lab, line_lower):
|
|
return [Do(i, line, filename)], is_doc
|
|
|
|
lower_line = line_lower.strip()[1:]
|
|
|
|
if len(lower_line) <= 3:
|
|
return [Simple_line(i, line, filename)], is_doc
|
|
|
|
#WARNING: The regex shloud match endtype. Don't know why it's not the case
|
|
if re_decl.match(line_lower_canonized) or line_lower_canonized == 'endtype':
|
|
if "function" in l_word[1:3]:
|
|
return [Function(i, line, filename)], is_doc
|
|
else:
|
|
return [Declaration(i, line, filename)], is_doc
|
|
|
|
if firstword.startswith('#'):
|
|
result = [Simple_line(i, line, filename)]
|
|
|
|
logger.info("%s:"
|
|
"irpf90 may not work with preprocessor directives. You can use"
|
|
"Irp_if ... Irp_else ... Irp_endif"
|
|
"instead of"
|
|
"#ifdef ... #else ... #endif" % line)
|
|
return result, is_doc
|
|
|
|
if firstword.startswith("case("):
|
|
return [Case(i, line, filename)], is_doc
|
|
|
|
if lower_line.startswith("$omp"):
|
|
return [Openmp(i, line, filename)], is_doc
|
|
if lower_line.startswith(("dec$", "dir$")) and command_line.directives:
|
|
return [Directive(i, line, filename)], is_doc
|
|
if lower_line.startswith("$ "):
|
|
return [Openmp(i, line, filename)], is_doc
|
|
|
|
# Detect errors
|
|
if firstword == "dowhile":
|
|
logger.error("%s 'do while' should be in 2 words." % Do(i, line, filename))
|
|
sys.exit(1)
|
|
|
|
return [Simple_line(i, line, filename)], is_doc
|
|
|
|
######################################################################
|
|
import os.path
|
|
|
|
|
|
def save_and_execute(irpdir, scriptname, code, interpreter):
|
|
# (str, str, List, str) -> List[Line]
|
|
''' Save the script in irpdir/scriptname and Execute it
|
|
|
|
Note:
|
|
The script are executed in the orginal directory of the .irp.f (aka '..')
|
|
and this directory is added to PYTHONPATH.
|
|
'''
|
|
|
|
irpdir_scriptname = os.path.abspath(os.path.join(irpdir, scriptname))
|
|
with open(irpdir_scriptname, 'w') as f:
|
|
f.writelines(code)
|
|
|
|
# Execute shell
|
|
import util
|
|
try:
|
|
text = util.check_output(
|
|
'PYTHONPATH=$PYTHONPATH:. %s %s' % (interpreter, irpdir_scriptname),
|
|
shell=True,
|
|
bufsize=-1,
|
|
cwd=os.path.join(irpdir, '..'))
|
|
except:
|
|
util.logger.error("Something wrong append with embeded '%s' script: %s" %
|
|
(interpreter, irpdir_scriptname))
|
|
import sys
|
|
sys.exit(1)
|
|
|
|
# Create the Line
|
|
p = Preprocess_text(scriptname)
|
|
p.text = text
|
|
return p.lines_overloaded
|
|
|
|
|
|
#######################################################################
|
|
def execute_shell(text):
|
|
# (List[Line]) -> List[Line]
|
|
'''Execute the embedded shell scripts'''
|
|
|
|
l_begin = [i for i, line in enumerate(text) if isinstance(line, Begin_shell)]
|
|
l_end = [i for i, line in enumerate(text) if isinstance(line, End_shell)]
|
|
l_output = []
|
|
|
|
# ~=~=~=~
|
|
# E x e c u t e S h e l l
|
|
# ~=~=~=~
|
|
from util import logger
|
|
import sys
|
|
|
|
def fail(l, a, b):
|
|
logger.error("%s In Begin_Shell, %s '%s'" % (l, a, b))
|
|
sys.exit(1)
|
|
|
|
for begin, end in zip(l_begin, l_end):
|
|
|
|
header = text[begin]
|
|
header_text = header.text
|
|
|
|
for bracket in ['[', ']']:
|
|
n = header_text.count(bracket)
|
|
assert n <= 1, fail(header_text, "Too many", bracket)
|
|
assert n >= 1, fail(header_text, "Missing", bracket)
|
|
else:
|
|
interpreter = header_text[header_text.find('[') + 1:header_text.find(']')].strip()
|
|
script = ['%s\n' % l.text for l in text[begin + 1:end]]
|
|
scriptname = "%s_shell_%d" % (header.filename, header.i)
|
|
|
|
l_output.append(save_and_execute(irpdir, scriptname, script, interpreter))
|
|
|
|
# ~=~=~=~
|
|
# R e p l a c e
|
|
# ~=~=~=~
|
|
|
|
#Deep copy for pure function
|
|
text_new = text[:]
|
|
|
|
# Because we use slicing and we want to include the end line
|
|
l_end_include = [i + 1 for i in l_end]
|
|
padding = 0
|
|
for begin, end, out in zip(l_begin, l_end_include, l_output):
|
|
text_new[begin + padding:end + padding] = out
|
|
padding += len(out) - (end - begin)
|
|
|
|
return text_new
|
|
|
|
|
|
######################################################################
|
|
def execute_templates(text):
|
|
'''Execute the templates'''
|
|
|
|
def fail(l, a, b):
|
|
error.fail(l, "In %s, %s" % (a, b))
|
|
|
|
def get_variables(line):
|
|
buffer = line.text.split('[', 1)
|
|
if len(buffer) < 2:
|
|
fail(line, "Subst", "Syntax error")
|
|
buffer = buffer[1].replace(']', '')
|
|
buffer = buffer.split(',')
|
|
return map(lambda x: '$%s' % (x.strip()), buffer)
|
|
|
|
TEMPLATE = 1
|
|
SUBST = 2
|
|
inside = 0
|
|
result = []
|
|
for line in text:
|
|
if inside == 0:
|
|
if type(line) == Begin_template:
|
|
script = []
|
|
inside = TEMPLATE
|
|
script = "template = \"\"\"\n"
|
|
else:
|
|
result.append(line)
|
|
elif inside == TEMPLATE:
|
|
if type(line) == Begin_template:
|
|
fail(line, "template", "Nested Begin_Template")
|
|
elif type(line) == End_template:
|
|
fail(line, "template", "Missing Subst")
|
|
elif type(line) == Subst:
|
|
inside = SUBST
|
|
script += "\"\"\"\n"
|
|
variables = get_variables(line)
|
|
script += "v = []\n"
|
|
subst = ""
|
|
else:
|
|
script += line.text + "\n"
|
|
else: # inside == SUBST
|
|
if type(line) == Begin_template:
|
|
fail(line, "subst", "Nested Begin_template")
|
|
elif type(line) == Subst:
|
|
fail(line, "subst", "Subst already defined")
|
|
elif type(line) == End_template:
|
|
inside = 0
|
|
subst = subst.rstrip()
|
|
if subst[-2:] == ';;':
|
|
subst = subst[:-2]
|
|
for s in subst.split(';;'):
|
|
buffer = map(lambda x: x.strip(), s.split(';'))
|
|
if len(buffer) != len(variables):
|
|
fail(line, "subst", "%d variables defined, and %d substitutions" %
|
|
(len(variables), len(buffer)))
|
|
script += "v.append( { \\\n"
|
|
for t, v in zip(variables, buffer):
|
|
script += ' "%s": """%s""" ,\n' % (t, v)
|
|
script += "} )\n"
|
|
script += "for d in v:\n t0 = str(template)\n"
|
|
for v in variables:
|
|
script += " t0 = t0.replace('%s',d['%s'])\n" % (v, v)
|
|
script += " print t0\n"
|
|
result += save_and_execute(
|
|
irpdir,
|
|
scriptname="%s_template_%d" % (line.filename, line.i),
|
|
code=script,
|
|
interpreter="python")
|
|
else:
|
|
subst += line.text + '\n'
|
|
|
|
return result
|
|
|
|
|
|
######################################################################
|
|
def form(text):
|
|
'''Find if the text is in fixed form or in free form'''
|
|
assert type(text) == list
|
|
if len(text) == 0:
|
|
return Free_form
|
|
assert isinstance(text[0], Line)
|
|
|
|
re2 = re.compile(r"^\s*[!#]")
|
|
re3 = re.compile(r"^\s*[^ 0-9]+")
|
|
for line in text:
|
|
if type(line) in [Empty_line, Doc, Openmp, Directive]:
|
|
pass
|
|
else:
|
|
if len(line.text) > 5:
|
|
test = line.text[0:5]
|
|
if test[0] in "Cc#!*":
|
|
pass
|
|
else:
|
|
if re2.match(test) is None and re3.match(test) is not None:
|
|
return Free_form
|
|
if line.text.rstrip()[-1] == '&':
|
|
return Free_form
|
|
return Fixed_form
|
|
|
|
|
|
######################################################################
|
|
def add_operators(text):
|
|
re_incr = re.compile(r"(\s*)(.*)(\+=)(.*$)", re.S)
|
|
re_decr = re.compile(r"(\s*)(.*)(-=)(.*$)", re.S)
|
|
re_mult = re.compile(r"(\s*)(.*)(\*=)(.*$)", re.S)
|
|
re_incr_if = re.compile(r"(.*)(\))(\s*)(.*)(\+=)(.*$)", re.S)
|
|
re_decr_if = re.compile(r"(.*)(\))(\s*)(.*)(-=)(.*$)", re.S)
|
|
re_mult_if = re.compile(r"(.*)(\))(\s*)(.*)(\*=)(.*$)", re.S)
|
|
'''Change additional operators'''
|
|
result = []
|
|
for line in text:
|
|
buffer = line.text
|
|
ls = buffer.strip()
|
|
if ls.startswith('print ') or \
|
|
ls.startswith('print*') or \
|
|
ls.startswith('write('):
|
|
pass
|
|
elif buffer.lstrip().startswith("if "):
|
|
if "+=" in buffer:
|
|
line.text = re.sub(re_incr_if, r'\1\2\4=\4+(\6)', buffer)
|
|
elif "-=" in buffer:
|
|
line.text = re.sub(re_incr_if, r'\1\2\4=\4-(\6)', buffer)
|
|
elif "*=" in buffer:
|
|
line.text = re.sub(re_incr_if, r'\1\2\4=\4*(\6)', buffer)
|
|
else:
|
|
if "+=" in buffer:
|
|
line.text = re.sub(re_incr, r'\1\2=\2+(\4)', buffer)
|
|
elif "-=" in buffer:
|
|
line.text = re.sub(re_decr, r'\1\2=\2-(\4)', buffer)
|
|
elif "*=" in buffer:
|
|
line.text = re.sub(re_mult, r'\1\2=\2*(\4)', buffer)
|
|
result.append(line)
|
|
return result
|
|
|
|
|
|
######################################################################
|
|
def remove_comments(text, form):
|
|
# (List[Line], int) -> List[Line]
|
|
'''Remove all comments
|
|
|
|
Note:
|
|
This function is unpur
|
|
'''
|
|
result = []
|
|
|
|
def remove_after_bang(str_):
|
|
# str -> str
|
|
i_bang = str_.find('!')
|
|
|
|
if i_bang == -1:
|
|
return str_
|
|
else:
|
|
sentinel, inside = None, False
|
|
for i, c in enumerate(str_):
|
|
if c == '"' or c == "'":
|
|
if not inside:
|
|
inside = True
|
|
sentinel = c
|
|
elif sentinel == c:
|
|
inside = False
|
|
|
|
elif c == '!' and not inside:
|
|
return str_[:i].strip()
|
|
|
|
return str_
|
|
|
|
if form == Free_form:
|
|
for line in text:
|
|
if type(line) in [Openmp, Doc, Directive]:
|
|
result.append(line)
|
|
elif type(line) == Empty_line:
|
|
pass
|
|
else:
|
|
newline = line.text.lstrip()
|
|
if (newline != "" and newline[0] != "!#"):
|
|
text = remove_after_bang(line.text)
|
|
if text:
|
|
line.text = text
|
|
result.append(line)
|
|
|
|
return result
|
|
else:
|
|
for line in text:
|
|
if type(line) in [Openmp, Doc, Directive]:
|
|
result.append(line)
|
|
elif type(line) == Empty_line:
|
|
pass
|
|
else:
|
|
newline = line.text.lstrip()
|
|
if newline == "" or newline[0] == "!":
|
|
pass
|
|
else:
|
|
line.text = remove_after_bang(line.text)
|
|
if line.text[0] in "#123456789 ":
|
|
result.append(line)
|
|
return result
|
|
|
|
|
|
######################################################################
|
|
def remove_continuation(text, form):
|
|
'''Removes continuation lines'''
|
|
result = []
|
|
buffer = ""
|
|
number = 0
|
|
t = None
|
|
if form == Free_form:
|
|
for line in text:
|
|
if line.text[-1] == '&':
|
|
buffer = "%s%s\n" % (buffer, line.text)
|
|
if number == 0:
|
|
t = type(line)
|
|
number = line.i
|
|
else:
|
|
if number != 0:
|
|
newline = t(number, \
|
|
"%s%s"%(buffer,line.text), \
|
|
line.filename)
|
|
line = newline
|
|
number = 0
|
|
buffer = ""
|
|
result.append(line)
|
|
else:
|
|
rev_text = list(text)
|
|
rev_text.reverse()
|
|
for line in rev_text:
|
|
is_continuation = False
|
|
if type(line) == Simple_line:
|
|
if len(line.text) >= 6:
|
|
if line.text[5] != ' ':
|
|
is_continuation = True
|
|
if is_continuation:
|
|
buffer = "&\n%s %s %s" % (line.text[:5], line.text[6:], buffer)
|
|
else:
|
|
line.text = line.text + buffer
|
|
result.insert(0, line)
|
|
buffer = ""
|
|
return result
|
|
|
|
|
|
######################################################################
|
|
def irp_simple_statements(text):
|
|
'''Processes simple statements'''
|
|
|
|
def process_irp_rw(line, rw, t):
|
|
'''Read Write'''
|
|
assert type(line) == t
|
|
buffer = line.text.split()
|
|
if len(buffer) == 2:
|
|
dummy, variable = buffer
|
|
num = "0"
|
|
elif len(buffer) == 3:
|
|
dummy, variable, num = buffer
|
|
else:
|
|
error.fail(line, "Error in IRP_%s statement" % (rw, ))
|
|
variable = variable.lower()
|
|
i = line.i
|
|
f = line.filename
|
|
txt = line.text.lstrip()
|
|
result = [
|
|
Empty_line(i, "!", f),
|
|
t(i, "! >>> %s" % txt, variable),
|
|
Provide_all(i, " call %ser_%s('%s')" % (rw, variable, num), f),
|
|
Empty_line(i, "! >>> END %s " % (txt, ), f),
|
|
Empty_line(line.i, "!", f),
|
|
]
|
|
return result
|
|
|
|
def process_irp_read(line):
|
|
assert type(line) == Irp_read
|
|
return process_irp_rw(line, 'read', Irp_read)
|
|
|
|
def process_irp_write(line):
|
|
assert type(line) == Irp_write
|
|
return process_irp_rw(line, 'writ', Irp_write)
|
|
|
|
def process_return(line):
|
|
assert type(line) == Return
|
|
if command_line.do_debug:
|
|
newline = Simple_line(line.i, " call irp_leave(irp_here)", line.filename)
|
|
result = [newline, line]
|
|
else:
|
|
result = [line]
|
|
return result
|
|
|
|
def debug_conditions(line):
|
|
'''Find condition in assert statement for debug'''
|
|
assert type(line) == Assert
|
|
match = re_test.search(line.text)
|
|
result = []
|
|
if match is not None:
|
|
matches = [match.group(1).strip(), match.group(3).strip()]
|
|
for m in matches:
|
|
ok = m != "" # not empty
|
|
ok = ok and not m.isdigit() # not a digit
|
|
ok = ok and "'" not in m # not a string
|
|
ok = ok and m.count('(') == m.count(')') # balanced parenthesis
|
|
if ok:
|
|
result.append(
|
|
Simple_line(line.i, " print *, '%s = ', %s" % (m, m), line.filename))
|
|
result.append(Simple_line(line.i, " print *, ''", line.filename))
|
|
return result
|
|
|
|
def process_assert(line):
|
|
assert type(line) == Assert
|
|
if command_line.do_assert:
|
|
if '(' not in line.text or ')' not in line.text:
|
|
error.fail(line, "Syntax error in ASSERT statement (parentheses)")
|
|
condition = "(%s" % (line.text.split('(', 1)[1])
|
|
if condition == "":
|
|
error.fail(line, "Error in Assert statement")
|
|
condition_str = condition.replace("'", "''")
|
|
i = line.i
|
|
f = line.filename
|
|
txt = line.text.strip()
|
|
result = [
|
|
Empty_line(i, "!", f),
|
|
Empty_line(i, "! >>> %s" % (txt, ), f),
|
|
If(i, " if (.not.%s) then" % (condition, ), f),
|
|
Simple_line(i, " call irp_trace", f),
|
|
Simple_line(i, " print *, irp_here//': Assert failed:'", f),
|
|
Simple_line(i, " print *, ' file: %s, line: %d'" % (f, i), f),
|
|
Simple_line(i, " print *, '%s'" % (condition_str, ), f),
|
|
] + debug_conditions(line) + [
|
|
Simple_line(i, " stop 1", f), Endif(i, " endif", f), Empty_line(
|
|
i, "! <<< END %s" % (txt, ), f), Empty_line(i, "!", f)
|
|
]
|
|
else:
|
|
result = []
|
|
return result
|
|
|
|
def process_end(line):
|
|
'''Add irp_leave if necessary'''
|
|
|
|
if command_line.do_debug:
|
|
i = line.i
|
|
f = line.filename
|
|
result = [Simple_line(i, " call irp_leave(irp_here)", f), line]
|
|
else:
|
|
result = [line]
|
|
return result
|
|
|
|
def process_begin_provider(line):
|
|
assert type(line) == Begin_provider
|
|
import string
|
|
trans = string.maketrans("[]", " ")
|
|
buffer = line.lower.translate(trans).split(',')
|
|
|
|
if len(buffer) < 2:
|
|
import sys
|
|
print line
|
|
print "Error in Begin_provider statement"
|
|
sys.exit(1)
|
|
|
|
varname = buffer[1].strip()
|
|
length = len(varname)
|
|
i = line.i
|
|
f = line.filename
|
|
result = [
|
|
Begin_provider(i, line.text, (f, varname)),
|
|
Declaration(i, " character*(%d) :: irp_here = '%s'" % (length, varname), f)
|
|
]
|
|
if command_line.do_debug:
|
|
result += [Simple_line(i, " call irp_enter(irp_here)", f), ]
|
|
return result
|
|
|
|
def process_cont_provider(line):
|
|
assert type(line) == Cont_provider
|
|
buf = line.lower.replace('[', " ").replace(']', "").split(',')
|
|
if len(buf) < 2:
|
|
error.fail(line, "Error in Cont_provider statement")
|
|
varname = buf[1].strip()
|
|
i = line.i
|
|
f = line.filename
|
|
return [Cont_provider(i, line.text, (f, varname))]
|
|
|
|
def process_subroutine(line):
|
|
assert type(line) == Subroutine
|
|
subname = line.subname
|
|
length = len(subname)
|
|
i = line.i
|
|
f = line.filename
|
|
result = [
|
|
line, Declaration(i, " character*(%d) :: irp_here = '%s'" % (length, subname), f)
|
|
]
|
|
|
|
if command_line.do_debug:
|
|
result += [Simple_line(i, " call irp_enter_routine(irp_here)", f), ]
|
|
return result
|
|
|
|
def process_function(line):
|
|
assert type(line) == Function
|
|
subname = line.subname
|
|
length = len(subname)
|
|
i = line.i
|
|
f = line.filename
|
|
result = [
|
|
line, Declaration(i, " character*(%d) :: irp_here = '%s'" % (length, subname), f)
|
|
]
|
|
if command_line.do_debug:
|
|
result += [Simple_line(i, " call irp_enter_routine(irp_here)", f), ]
|
|
return result
|
|
|
|
def process_program(line):
|
|
assert type(line) == Program
|
|
program_name = line.lower.split()[1]
|
|
temp = [Program(0, "program irp_program", program_name)]
|
|
|
|
if command_line.do_Task:
|
|
for i in [" call omp_set_nested(.TRUE.)", "!$omp parallel", "!$omp single"]:
|
|
temp += [Simple_line(0, i, line.filename)]
|
|
|
|
if command_line.do_profile:
|
|
temp += [Simple_line(0, "call irp_init_timer()", line.filename)]
|
|
# Need to choose between lazy lock or are big full initialization
|
|
# if command_line.do_openmp:
|
|
# temp += [Simple_line(0, " call irp_init_locks_%s()" % (irp_id), line.filename)]
|
|
if command_line.do_debug or command_line.do_assert:
|
|
temp += [Simple_line(0, " CALL irp_stack_init", line.filename)]
|
|
|
|
temp += [Call(0, " call %s" % (program_name), line.filename)]
|
|
if command_line.do_profile:
|
|
temp += [Simple_line(0, "call irp_print_timer()", line.filename)]
|
|
|
|
temp += [Simple_line(0, " call irp_finalize_%s()" % (irp_id), line.filename)]
|
|
|
|
if command_line.do_Task:
|
|
for i in ["!$omp taskwait", "!$omp end single", "!$omp end parallel"]:
|
|
temp += [Simple_line(0, i, line.filename)]
|
|
|
|
temp += [End(0, "end program", line.filename)]
|
|
|
|
result = temp + process_subroutine(
|
|
Subroutine(line.i, "subroutine %s" % (program_name, ), line.filename))
|
|
|
|
return result
|
|
|
|
d = {
|
|
Irp_read: process_irp_read,
|
|
Irp_write: process_irp_write,
|
|
Return: process_return,
|
|
Assert: process_assert,
|
|
End: process_end,
|
|
Begin_provider: process_begin_provider,
|
|
Cont_provider: process_cont_provider,
|
|
End_provider: process_end,
|
|
Subroutine: process_subroutine,
|
|
Function: process_function,
|
|
Program: process_program,
|
|
}
|
|
|
|
result = []
|
|
for line in text:
|
|
buffer = [line]
|
|
for t in d:
|
|
if type(line) == t:
|
|
buffer = d[t](line)
|
|
break
|
|
result += buffer
|
|
|
|
return result
|
|
|
|
|
|
######################################################################
|
|
def change_includes(text):
|
|
'''Deals with include files'''
|
|
result = []
|
|
for line in text:
|
|
if type(line) == Include:
|
|
txt = line.text.replace('"', "'").split("'")
|
|
if len(txt) != 3:
|
|
error.fail(line, "Error in include statement")
|
|
directory = (("./" + line.filename).rsplit('/', 1)[0] + '/')[2:]
|
|
if directory == "":
|
|
filename = txt[1].strip()
|
|
else:
|
|
filename = directory + txt[1].strip()
|
|
try:
|
|
result.append(Include(line.i, "! include '%s'" % filename, filename))
|
|
result += Preprocess_text(filename).preprocessed_text
|
|
except IOError:
|
|
result.append(Declaration(line.i, line.text, line.filename))
|
|
else:
|
|
result.append(line)
|
|
return result
|
|
|
|
|
|
######################################################################
|
|
def process_old_style_do(text):
|
|
# (List[Line]) -> List[Line]
|
|
'''Changes archaic do loops to new style
|
|
DO 1 i=1,10'''
|
|
|
|
def change_matching_enddo(begin, number):
|
|
for i, line in enumerate(text[begin + 1:]):
|
|
if isinstance(line, (Continue, Enddo)) and line.text.split()[0] == number:
|
|
text[begin + 1 + i] = Enddo(line.i, " enddo", line.filename)
|
|
return
|
|
|
|
from util import logger
|
|
logger.error(text[begin], "(%s) Old-style do loops should end with 'continue' or 'end do'" %
|
|
text[begin])
|
|
from util import sys
|
|
sys.exit(1)
|
|
|
|
result = []
|
|
for i in range(len(text)):
|
|
line = text[i]
|
|
if type(line) == Do:
|
|
buffer = line.text.split()
|
|
try:
|
|
if buffer[1].isdigit():
|
|
number = buffer.pop(1)
|
|
change_matching_enddo(i, number)
|
|
line.text = " ".join(buffer)
|
|
except IndexError:
|
|
pass
|
|
result.append(line)
|
|
return result
|
|
|
|
|
|
######################################################################
|
|
def change_single_line_ifs(text):
|
|
# List[Line] -> List[Line]
|
|
'''Changes: `if (test) result`
|
|
into
|
|
`if (test) then
|
|
result
|
|
endif`'''
|
|
|
|
regex = r"\(.*?\)"
|
|
|
|
result = []
|
|
for line in text:
|
|
if type(line) == If:
|
|
if line.lower.endswith("then"):
|
|
result.append(line)
|
|
else:
|
|
buffer = line.text
|
|
begin = buffer.find('(')
|
|
if begin == -1:
|
|
logger.error("No '(' in if statemnt: %s" % line)
|
|
sys.exit(1)
|
|
|
|
level = 0
|
|
instring = False
|
|
for i, c in enumerate(buffer[begin:]):
|
|
if c == "'":
|
|
instring = not instring
|
|
if instring:
|
|
pass
|
|
elif c == '(':
|
|
level += 1
|
|
elif c == ')':
|
|
level -= 1
|
|
if level == 0:
|
|
end = begin + i + 1
|
|
break
|
|
if level != 0:
|
|
logger.error("If statement not valid: %s (%s)" % (line, line.filename))
|
|
sys.exit(1)
|
|
|
|
test = buffer[:end]
|
|
code = buffer[end:]
|
|
i = line.i
|
|
f = line.filename
|
|
result.append(If(i, "%s then" % (test, ), f))
|
|
result += get_type(i, f, code, code.lower(), code.lower(), False)[0]
|
|
result.append(Endif(i, " endif", f))
|
|
else:
|
|
result.append(line)
|
|
return result
|
|
|
|
|
|
######################################################################
|
|
def check_begin_end(raw_text):
|
|
# List[Line] -> None
|
|
'''Checks x...endx consistence
|
|
|
|
|
|
Note:
|
|
fortran 'ifdef' statement may cause this function to bug.
|
|
Indeed we count the numboer of 'x' statement and compare it to the number of 'endx'.
|
|
Maybe more of one 'x' statement in defined cause in 'ifdef/else/endif' statement.
|
|
'''
|
|
|
|
d_block = {
|
|
Enddo: [Do],
|
|
Endif: [If],
|
|
End_provider: [Begin_provider],
|
|
End_doc: [Begin_doc],
|
|
End: [Program, Subroutine, Function],
|
|
End_module: [Module],
|
|
End_interface: [Interface]
|
|
}
|
|
|
|
from collections import defaultdict
|
|
d_type = defaultdict(list)
|
|
|
|
for line in raw_text:
|
|
d_type[type(line)].append(line)
|
|
|
|
for t_end, l_begin in d_block.iteritems():
|
|
n_end = len(d_type[t_end])
|
|
n_begin = sum(len(d_type[t_begin]) for t_begin in l_begin)
|
|
|
|
if n_end != n_begin:
|
|
|
|
if n_end > n_begin:
|
|
logger.error("You have more close statement than open statement (%s) (%s)",
|
|
line.filename, t_end)
|
|
else:
|
|
logger.error('You have more end statement than open statenemt for (%s) (%s)' %
|
|
(line.filename, t_end))
|
|
|
|
for i in zip([l for i in l_begin for l in d_type[i]], d_type[t_end]):
|
|
logger.debug(i)
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
######################################################################
|
|
def remove_ifdefs(text):
|
|
assert type(text) == list
|
|
result = []
|
|
|
|
do_print = True
|
|
for line in text:
|
|
if type(line) == Irp_If:
|
|
var = line.text.split()[1]
|
|
do_print = var in command_line.defined
|
|
elif type(line) == Irp_Else:
|
|
do_print = not do_print
|
|
elif type(line) == Irp_Endif:
|
|
do_print = True
|
|
elif do_print:
|
|
result.append(line)
|
|
|
|
return result
|
|
|
|
|
|
######################################################################
|
|
def check_OpenMP(text):
|
|
'Not working!'
|
|
assert type(text) == list
|
|
inside_openmp = False
|
|
for line in text:
|
|
if type(line) == Openmp:
|
|
# Detect OpenMP blocks
|
|
buffer = line.lower.split()
|
|
|
|
if buffer[1] == "parallel":
|
|
inside_openmp = True
|
|
elif buffer[1] == "end" and buffer[2] == "parallel":
|
|
inside_openmp = False
|
|
|
|
# if inside_openmp and isinstance(line, (Provide_all, Provide, Touch, SoftTouch)):
|
|
# logger.error("%s is not allowed in an OpenMP block: %s" % (type(line),line))
|
|
|
|
return text
|
|
|
|
|
|
######################################################################
|
|
class Preprocess_text(object):
|
|
def __init__(self, filename):
|
|
self.filename = filename
|
|
|
|
@irpy.lazy_property_mutable
|
|
def text(self):
|
|
with open(self.filename, 'r') as f:
|
|
str_ = f.read()
|
|
|
|
#Dirty thing. We will replace 'end program' by 'end subroutine'
|
|
#because afterward the program will be replaced by a subroutine...
|
|
|
|
import re
|
|
transform = re.compile(re.escape('end program'), re.IGNORECASE)
|
|
|
|
return transform.sub('end subroutine', str_)
|
|
|
|
@irpy.lazy_property_mutable
|
|
def text_align(self):
|
|
from command_line import command_line
|
|
return self.text.replace("$IRP_ALIGN", command_line.align)
|
|
|
|
@irpy.lazy_property
|
|
def text_lower(self):
|
|
return self.text_align.lower()
|
|
|
|
@irpy.lazy_property
|
|
def text_lower_canonized(self):
|
|
return get_canonized_text(self.text_lower)
|
|
|
|
@irpy.lazy_property
|
|
def lines(self):
|
|
return self.text_align.split('\n')
|
|
|
|
@irpy.lazy_property
|
|
def lines_lower(self):
|
|
return self.text_lower.split('\n')
|
|
|
|
@irpy.lazy_property
|
|
def lines_lower_canonized(self):
|
|
return self.text_lower_canonized.replace("!", " ! ").split('\n')
|
|
|
|
@irpy.lazy_property
|
|
def lines_overloaded(self):
|
|
'''Labeled lines'''
|
|
result = []
|
|
is_doc = False
|
|
|
|
for i, (l, ll,
|
|
llc) in enumerate(zip(self.lines, self.lines_lower, self.lines_lower_canonized)):
|
|
line, is_doc = get_type(i + 1, self.filename, l, ll, llc, is_doc)
|
|
result += line
|
|
return result
|
|
|
|
@irpy.lazy_property
|
|
def preprocessed_text(self):
|
|
result = self.lines_overloaded
|
|
result = execute_templates(result)
|
|
result = execute_shell(result)
|
|
fortran_form = form(result)
|
|
result = remove_ifdefs(result)
|
|
result = remove_comments(result, fortran_form)
|
|
|
|
result = remove_continuation(result, fortran_form)
|
|
result = add_operators(result)
|
|
result = change_includes(result)
|
|
result = change_single_line_ifs(result)
|
|
|
|
result = process_old_style_do(result)
|
|
result = irp_simple_statements(result)
|
|
result = check_OpenMP(result)
|
|
|
|
check_begin_end(result)
|
|
|
|
return result
|
|
|
|
if __name__ == '__main__':
|
|
debug()
|