Spaces:
Running
Running
#!/usr/bin/env python3 | |
""" | |
crackfortran --- read fortran (77,90) code and extract declaration information. | |
Copyright 1999-2004 Pearu Peterson all rights reserved, | |
Pearu Peterson <[email protected]> | |
Permission to use, modify, and distribute this software is given under the | |
terms of the NumPy License. | |
NO WARRANTY IS EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. | |
$Date: 2005/09/27 07:13:49 $ | |
Pearu Peterson | |
Usage of crackfortran: | |
====================== | |
Command line keys: -quiet,-verbose,-fix,-f77,-f90,-show,-h <pyffilename> | |
-m <module name for f77 routines>,--ignore-contains | |
Functions: crackfortran, crack2fortran | |
The following Fortran statements/constructions are supported | |
(or will be if needed): | |
block data,byte,call,character,common,complex,contains,data, | |
dimension,double complex,double precision,end,external,function, | |
implicit,integer,intent,interface,intrinsic, | |
logical,module,optional,parameter,private,public, | |
program,real,(sequence?),subroutine,type,use,virtual, | |
include,pythonmodule | |
Note: 'virtual' is mapped to 'dimension'. | |
Note: 'implicit integer (z) static (z)' is 'implicit static (z)' (this is minor bug). | |
Note: code after 'contains' will be ignored until its scope ends. | |
Note: 'common' statement is extended: dimensions are moved to variable definitions | |
Note: f2py directive: <commentchar>f2py<line> is read as <line> | |
Note: pythonmodule is introduced to represent Python module | |
Usage: | |
`postlist=crackfortran(files)` | |
`postlist` contains declaration information read from the list of files `files`. | |
`crack2fortran(postlist)` returns a fortran code to be saved to pyf-file | |
`postlist` has the following structure: | |
*** it is a list of dictionaries containing `blocks': | |
B = {'block','body','vars','parent_block'[,'name','prefix','args','result', | |
'implicit','externals','interfaced','common','sortvars', | |
'commonvars','note']} | |
B['block'] = 'interface' | 'function' | 'subroutine' | 'module' | | |
'program' | 'block data' | 'type' | 'pythonmodule' | | |
'abstract interface' | |
B['body'] --- list containing `subblocks' with the same structure as `blocks' | |
B['parent_block'] --- dictionary of a parent block: | |
C['body'][<index>]['parent_block'] is C | |
B['vars'] --- dictionary of variable definitions | |
B['sortvars'] --- dictionary of variable definitions sorted by dependence (independent first) | |
B['name'] --- name of the block (not if B['block']=='interface') | |
B['prefix'] --- prefix string (only if B['block']=='function') | |
B['args'] --- list of argument names if B['block']== 'function' | 'subroutine' | |
B['result'] --- name of the return value (only if B['block']=='function') | |
B['implicit'] --- dictionary {'a':<variable definition>,'b':...} | None | |
B['externals'] --- list of variables being external | |
B['interfaced'] --- list of variables being external and defined | |
B['common'] --- dictionary of common blocks (list of objects) | |
B['commonvars'] --- list of variables used in common blocks (dimensions are moved to variable definitions) | |
B['from'] --- string showing the 'parents' of the current block | |
B['use'] --- dictionary of modules used in current block: | |
{<modulename>:{['only':<0|1>],['map':{<local_name1>:<use_name1>,...}]}} | |
B['note'] --- list of LaTeX comments on the block | |
B['f2pyenhancements'] --- optional dictionary | |
{'threadsafe':'','fortranname':<name>, | |
'callstatement':<C-expr>|<multi-line block>, | |
'callprotoargument':<C-expr-list>, | |
'usercode':<multi-line block>|<list of multi-line blocks>, | |
'pymethoddef:<multi-line block>' | |
} | |
B['entry'] --- dictionary {entryname:argslist,..} | |
B['varnames'] --- list of variable names given in the order of reading the | |
Fortran code, useful for derived types. | |
B['saved_interface'] --- a string of scanned routine signature, defines explicit interface | |
*** Variable definition is a dictionary | |
D = B['vars'][<variable name>] = | |
{'typespec'[,'attrspec','kindselector','charselector','=','typename']} | |
D['typespec'] = 'byte' | 'character' | 'complex' | 'double complex' | | |
'double precision' | 'integer' | 'logical' | 'real' | 'type' | |
D['attrspec'] --- list of attributes (e.g. 'dimension(<arrayspec>)', | |
'external','intent(in|out|inout|hide|c|callback|cache|aligned4|aligned8|aligned16)', | |
'optional','required', etc) | |
K = D['kindselector'] = {['*','kind']} (only if D['typespec'] = | |
'complex' | 'integer' | 'logical' | 'real' ) | |
C = D['charselector'] = {['*','len','kind']} | |
(only if D['typespec']=='character') | |
D['='] --- initialization expression string | |
D['typename'] --- name of the type if D['typespec']=='type' | |
D['dimension'] --- list of dimension bounds | |
D['intent'] --- list of intent specifications | |
D['depend'] --- list of variable names on which current variable depends on | |
D['check'] --- list of C-expressions; if C-expr returns zero, exception is raised | |
D['note'] --- list of LaTeX comments on the variable | |
*** Meaning of kind/char selectors (few examples): | |
D['typespec>']*K['*'] | |
D['typespec'](kind=K['kind']) | |
character*C['*'] | |
character(len=C['len'],kind=C['kind']) | |
(see also fortran type declaration statement formats below) | |
Fortran 90 type declaration statement format (F77 is subset of F90) | |
==================================================================== | |
(Main source: IBM XL Fortran 5.1 Language Reference Manual) | |
type declaration = <typespec> [[<attrspec>]::] <entitydecl> | |
<typespec> = byte | | |
character[<charselector>] | | |
complex[<kindselector>] | | |
double complex | | |
double precision | | |
integer[<kindselector>] | | |
logical[<kindselector>] | | |
real[<kindselector>] | | |
type(<typename>) | |
<charselector> = * <charlen> | | |
([len=]<len>[,[kind=]<kind>]) | | |
(kind=<kind>[,len=<len>]) | |
<kindselector> = * <intlen> | | |
([kind=]<kind>) | |
<attrspec> = comma separated list of attributes. | |
Only the following attributes are used in | |
building up the interface: | |
external | |
(parameter --- affects '=' key) | |
optional | |
intent | |
Other attributes are ignored. | |
<intentspec> = in | out | inout | |
<arrayspec> = comma separated list of dimension bounds. | |
<entitydecl> = <name> [[*<charlen>][(<arrayspec>)] | [(<arrayspec>)]*<charlen>] | |
[/<init_expr>/ | =<init_expr>] [,<entitydecl>] | |
In addition, the following attributes are used: check,depend,note | |
TODO: | |
* Apply 'parameter' attribute (e.g. 'integer parameter :: i=2' 'real x(i)' | |
-> 'real x(2)') | |
The above may be solved by creating appropriate preprocessor program, for example. | |
""" | |
import io | |
import sys | |
import string | |
import fileinput | |
import re | |
import os | |
import copy | |
import platform | |
from . import __version__ | |
# The environment provided by auxfuncs.py is needed for some calls to eval. | |
# As the needed functions cannot be determined by static inspection of the | |
# code, it is safest to use import * pending a major refactoring of f2py. | |
from .auxfuncs import * | |
f2py_version = __version__.version | |
# Global flags: | |
strictf77 = 1 # Ignore `!' comments unless line[0]=='!' | |
sourcecodeform = 'fix' # 'fix','free' | |
quiet = 0 # Be verbose if 0 (Obsolete: not used any more) | |
verbose = 1 # Be quiet if 0, extra verbose if > 1. | |
tabchar = 4 * ' ' | |
pyffilename = '' | |
f77modulename = '' | |
skipemptyends = 0 # for old F77 programs without 'program' statement | |
ignorecontains = 1 | |
dolowercase = 1 | |
debug = [] | |
# Global variables | |
beginpattern = '' | |
currentfilename = '' | |
expectbegin = 1 | |
f90modulevars = {} | |
filepositiontext = '' | |
gotnextfile = 1 | |
groupcache = None | |
groupcounter = 0 | |
grouplist = {groupcounter: []} | |
groupname = '' | |
include_paths = [] | |
neededmodule = -1 | |
onlyfuncs = [] | |
previous_context = None | |
skipblocksuntil = -1 | |
skipfuncs = [] | |
skipfunctions = [] | |
usermodules = [] | |
def reset_global_f2py_vars(): | |
global groupcounter, grouplist, neededmodule, expectbegin | |
global skipblocksuntil, usermodules, f90modulevars, gotnextfile | |
global filepositiontext, currentfilename, skipfunctions, skipfuncs | |
global onlyfuncs, include_paths, previous_context | |
global strictf77, sourcecodeform, quiet, verbose, tabchar, pyffilename | |
global f77modulename, skipemptyends, ignorecontains, dolowercase, debug | |
# flags | |
strictf77 = 1 | |
sourcecodeform = 'fix' | |
quiet = 0 | |
verbose = 1 | |
tabchar = 4 * ' ' | |
pyffilename = '' | |
f77modulename = '' | |
skipemptyends = 0 | |
ignorecontains = 1 | |
dolowercase = 1 | |
debug = [] | |
# variables | |
groupcounter = 0 | |
grouplist = {groupcounter: []} | |
neededmodule = -1 | |
expectbegin = 1 | |
skipblocksuntil = -1 | |
usermodules = [] | |
f90modulevars = {} | |
gotnextfile = 1 | |
filepositiontext = '' | |
currentfilename = '' | |
skipfunctions = [] | |
skipfuncs = [] | |
onlyfuncs = [] | |
include_paths = [] | |
previous_context = None | |
def outmess(line, flag=1): | |
global filepositiontext | |
if not verbose: | |
return | |
if not quiet: | |
if flag: | |
sys.stdout.write(filepositiontext) | |
sys.stdout.write(line) | |
re._MAXCACHE = 50 | |
defaultimplicitrules = {} | |
for c in "abcdefghopqrstuvwxyz$_": | |
defaultimplicitrules[c] = {'typespec': 'real'} | |
for c in "ijklmn": | |
defaultimplicitrules[c] = {'typespec': 'integer'} | |
del c | |
badnames = {} | |
invbadnames = {} | |
for n in ['int', 'double', 'float', 'char', 'short', 'long', 'void', 'case', 'while', | |
'return', 'signed', 'unsigned', 'if', 'for', 'typedef', 'sizeof', 'union', | |
'struct', 'static', 'register', 'new', 'break', 'do', 'goto', 'switch', | |
'continue', 'else', 'inline', 'extern', 'delete', 'const', 'auto', | |
'len', 'rank', 'shape', 'index', 'slen', 'size', '_i', | |
'max', 'min', | |
'flen', 'fshape', | |
'string', 'complex_double', 'float_double', 'stdin', 'stderr', 'stdout', | |
'type', 'default']: | |
badnames[n] = n + '_bn' | |
invbadnames[n + '_bn'] = n | |
def rmbadname1(name): | |
if name in badnames: | |
errmess('rmbadname1: Replacing "%s" with "%s".\n' % | |
(name, badnames[name])) | |
return badnames[name] | |
return name | |
def rmbadname(names): | |
return [rmbadname1(_m) for _m in names] | |
def undo_rmbadname1(name): | |
if name in invbadnames: | |
errmess('undo_rmbadname1: Replacing "%s" with "%s".\n' | |
% (name, invbadnames[name])) | |
return invbadnames[name] | |
return name | |
def undo_rmbadname(names): | |
return [undo_rmbadname1(_m) for _m in names] | |
def getextension(name): | |
i = name.rfind('.') | |
if i == -1: | |
return '' | |
if '\\' in name[i:]: | |
return '' | |
if '/' in name[i:]: | |
return '' | |
return name[i + 1:] | |
is_f_file = re.compile(r'.*\.(for|ftn|f77|f)\Z', re.I).match | |
_has_f_header = re.compile(r'-\*-\s*fortran\s*-\*-', re.I).search | |
_has_f90_header = re.compile(r'-\*-\s*f90\s*-\*-', re.I).search | |
_has_fix_header = re.compile(r'-\*-\s*fix\s*-\*-', re.I).search | |
_free_f90_start = re.compile(r'[^c*]\s*[^\s\d\t]', re.I).match | |
def is_free_format(file): | |
"""Check if file is in free format Fortran.""" | |
# f90 allows both fixed and free format, assuming fixed unless | |
# signs of free format are detected. | |
result = 0 | |
with open(file, 'r') as f: | |
line = f.readline() | |
n = 15 # the number of non-comment lines to scan for hints | |
if _has_f_header(line): | |
n = 0 | |
elif _has_f90_header(line): | |
n = 0 | |
result = 1 | |
while n > 0 and line: | |
if line[0] != '!' and line.strip(): | |
n -= 1 | |
if (line[0] != '\t' and _free_f90_start(line[:5])) or line[-2:-1] == '&': | |
result = 1 | |
break | |
line = f.readline() | |
return result | |
# Read fortran (77,90) code | |
def readfortrancode(ffile, dowithline=show, istop=1): | |
""" | |
Read fortran codes from files and | |
1) Get rid of comments, line continuations, and empty lines; lower cases. | |
2) Call dowithline(line) on every line. | |
3) Recursively call itself when statement \"include '<filename>'\" is met. | |
""" | |
global gotnextfile, filepositiontext, currentfilename, sourcecodeform, strictf77 | |
global beginpattern, quiet, verbose, dolowercase, include_paths | |
if not istop: | |
saveglobals = gotnextfile, filepositiontext, currentfilename, sourcecodeform, strictf77,\ | |
beginpattern, quiet, verbose, dolowercase | |
if ffile == []: | |
return | |
localdolowercase = dolowercase | |
# cont: set to True when the content of the last line read | |
# indicates statement continuation | |
cont = False | |
finalline = '' | |
ll = '' | |
includeline = re.compile( | |
r'\s*include\s*(\'|")(?P<name>[^\'"]*)(\'|")', re.I) | |
cont1 = re.compile(r'(?P<line>.*)&\s*\Z') | |
cont2 = re.compile(r'(\s*&|)(?P<line>.*)') | |
mline_mark = re.compile(r".*?'''") | |
if istop: | |
dowithline('', -1) | |
ll, l1 = '', '' | |
spacedigits = [' '] + [str(_m) for _m in range(10)] | |
filepositiontext = '' | |
fin = fileinput.FileInput(ffile) | |
while True: | |
l = fin.readline() | |
if not l: | |
break | |
if fin.isfirstline(): | |
filepositiontext = '' | |
currentfilename = fin.filename() | |
gotnextfile = 1 | |
l1 = l | |
strictf77 = 0 | |
sourcecodeform = 'fix' | |
ext = os.path.splitext(currentfilename)[1] | |
if is_f_file(currentfilename) and \ | |
not (_has_f90_header(l) or _has_fix_header(l)): | |
strictf77 = 1 | |
elif is_free_format(currentfilename) and not _has_fix_header(l): | |
sourcecodeform = 'free' | |
if strictf77: | |
beginpattern = beginpattern77 | |
else: | |
beginpattern = beginpattern90 | |
outmess('\tReading file %s (format:%s%s)\n' | |
% (repr(currentfilename), sourcecodeform, | |
strictf77 and ',strict' or '')) | |
l = l.expandtabs().replace('\xa0', ' ') | |
# Get rid of newline characters | |
while not l == '': | |
if l[-1] not in "\n\r\f": | |
break | |
l = l[:-1] | |
if not strictf77: | |
(l, rl) = split_by_unquoted(l, '!') | |
l += ' ' | |
if rl[:5].lower() == '!f2py': # f2py directive | |
l, _ = split_by_unquoted(l + 4 * ' ' + rl[5:], '!') | |
if l.strip() == '': # Skip empty line | |
if sourcecodeform == 'free': | |
# In free form, a statement continues in the next line | |
# that is not a comment line [3.3.2.4^1], lines with | |
# blanks are comment lines [3.3.2.3^1]. Hence, the | |
# line continuation flag must retain its state. | |
pass | |
else: | |
# In fixed form, statement continuation is determined | |
# by a non-blank character at the 6-th position. Empty | |
# line indicates a start of a new statement | |
# [3.3.3.3^1]. Hence, the line continuation flag must | |
# be reset. | |
cont = False | |
continue | |
if sourcecodeform == 'fix': | |
if l[0] in ['*', 'c', '!', 'C', '#']: | |
if l[1:5].lower() == 'f2py': # f2py directive | |
l = ' ' + l[5:] | |
else: # Skip comment line | |
cont = False | |
continue | |
elif strictf77: | |
if len(l) > 72: | |
l = l[:72] | |
if not (l[0] in spacedigits): | |
raise Exception('readfortrancode: Found non-(space,digit) char ' | |
'in the first column.\n\tAre you sure that ' | |
'this code is in fix form?\n\tline=%s' % repr(l)) | |
if (not cont or strictf77) and (len(l) > 5 and not l[5] == ' '): | |
# Continuation of a previous line | |
ll = ll + l[6:] | |
finalline = '' | |
origfinalline = '' | |
else: | |
if not strictf77: | |
# F90 continuation | |
r = cont1.match(l) | |
if r: | |
l = r.group('line') # Continuation follows .. | |
if cont: | |
ll = ll + cont2.match(l).group('line') | |
finalline = '' | |
origfinalline = '' | |
else: | |
# clean up line beginning from possible digits. | |
l = ' ' + l[5:] | |
if localdolowercase: | |
finalline = ll.lower() | |
else: | |
finalline = ll | |
origfinalline = ll | |
ll = l | |
cont = (r is not None) | |
else: | |
# clean up line beginning from possible digits. | |
l = ' ' + l[5:] | |
if localdolowercase: | |
finalline = ll.lower() | |
else: | |
finalline = ll | |
origfinalline = ll | |
ll = l | |
elif sourcecodeform == 'free': | |
if not cont and ext == '.pyf' and mline_mark.match(l): | |
l = l + '\n' | |
while True: | |
lc = fin.readline() | |
if not lc: | |
errmess( | |
'Unexpected end of file when reading multiline\n') | |
break | |
l = l + lc | |
if mline_mark.match(lc): | |
break | |
l = l.rstrip() | |
r = cont1.match(l) | |
if r: | |
l = r.group('line') # Continuation follows .. | |
if cont: | |
ll = ll + cont2.match(l).group('line') | |
finalline = '' | |
origfinalline = '' | |
else: | |
if localdolowercase: | |
finalline = ll.lower() | |
else: | |
finalline = ll | |
origfinalline = ll | |
ll = l | |
cont = (r is not None) | |
else: | |
raise ValueError( | |
"Flag sourcecodeform must be either 'fix' or 'free': %s" % repr(sourcecodeform)) | |
filepositiontext = 'Line #%d in %s:"%s"\n\t' % ( | |
fin.filelineno() - 1, currentfilename, l1) | |
m = includeline.match(origfinalline) | |
if m: | |
fn = m.group('name') | |
if os.path.isfile(fn): | |
readfortrancode(fn, dowithline=dowithline, istop=0) | |
else: | |
include_dirs = [ | |
os.path.dirname(currentfilename)] + include_paths | |
foundfile = 0 | |
for inc_dir in include_dirs: | |
fn1 = os.path.join(inc_dir, fn) | |
if os.path.isfile(fn1): | |
foundfile = 1 | |
readfortrancode(fn1, dowithline=dowithline, istop=0) | |
break | |
if not foundfile: | |
outmess('readfortrancode: could not find include file %s in %s. Ignoring.\n' % ( | |
repr(fn), os.pathsep.join(include_dirs))) | |
else: | |
dowithline(finalline) | |
l1 = ll | |
if localdolowercase: | |
finalline = ll.lower() | |
else: | |
finalline = ll | |
origfinalline = ll | |
filepositiontext = 'Line #%d in %s:"%s"\n\t' % ( | |
fin.filelineno() - 1, currentfilename, l1) | |
m = includeline.match(origfinalline) | |
if m: | |
fn = m.group('name') | |
if os.path.isfile(fn): | |
readfortrancode(fn, dowithline=dowithline, istop=0) | |
else: | |
include_dirs = [os.path.dirname(currentfilename)] + include_paths | |
foundfile = 0 | |
for inc_dir in include_dirs: | |
fn1 = os.path.join(inc_dir, fn) | |
if os.path.isfile(fn1): | |
foundfile = 1 | |
readfortrancode(fn1, dowithline=dowithline, istop=0) | |
break | |
if not foundfile: | |
outmess('readfortrancode: could not find include file %s in %s. Ignoring.\n' % ( | |
repr(fn), os.pathsep.join(include_dirs))) | |
else: | |
dowithline(finalline) | |
filepositiontext = '' | |
fin.close() | |
if istop: | |
dowithline('', 1) | |
else: | |
gotnextfile, filepositiontext, currentfilename, sourcecodeform, strictf77,\ | |
beginpattern, quiet, verbose, dolowercase = saveglobals | |
# Crack line | |
beforethisafter = r'\s*(?P<before>%s(?=\s*(\b(%s)\b)))' + \ | |
r'\s*(?P<this>(\b(%s)\b))' + \ | |
r'\s*(?P<after>%s)\s*\Z' | |
## | |
fortrantypes = r'character|logical|integer|real|complex|double\s*(precision\s*(complex|)|complex)|type(?=\s*\([\w\s,=(*)]*\))|byte' | |
typespattern = re.compile( | |
beforethisafter % ('', fortrantypes, fortrantypes, '.*'), re.I), 'type' | |
typespattern4implicit = re.compile(beforethisafter % ( | |
'', fortrantypes + '|static|automatic|undefined', fortrantypes + '|static|automatic|undefined', '.*'), re.I) | |
# | |
functionpattern = re.compile(beforethisafter % ( | |
r'([a-z]+[\w\s(=*+-/)]*?|)', 'function', 'function', '.*'), re.I), 'begin' | |
subroutinepattern = re.compile(beforethisafter % ( | |
r'[a-z\s]*?', 'subroutine', 'subroutine', '.*'), re.I), 'begin' | |
# modulepattern=re.compile(beforethisafter%('[a-z\s]*?','module','module','.*'),re.I),'begin' | |
# | |
groupbegins77 = r'program|block\s*data' | |
beginpattern77 = re.compile( | |
beforethisafter % ('', groupbegins77, groupbegins77, '.*'), re.I), 'begin' | |
groupbegins90 = groupbegins77 + \ | |
r'|module(?!\s*procedure)|python\s*module|(abstract|)\s*interface|' + \ | |
r'type(?!\s*\()' | |
beginpattern90 = re.compile( | |
beforethisafter % ('', groupbegins90, groupbegins90, '.*'), re.I), 'begin' | |
groupends = (r'end|endprogram|endblockdata|endmodule|endpythonmodule|' | |
r'endinterface|endsubroutine|endfunction') | |
endpattern = re.compile( | |
beforethisafter % ('', groupends, groupends, r'[\w\s]*'), re.I), 'end' | |
endifs = r'(end\s*(if|do|where|select|while|forall|associate|block|critical|enum|team))|(module\s*procedure)' | |
endifpattern = re.compile( | |
beforethisafter % (r'[\w]*?', endifs, endifs, r'[\w\s]*'), re.I), 'endif' | |
# | |
implicitpattern = re.compile( | |
beforethisafter % ('', 'implicit', 'implicit', '.*'), re.I), 'implicit' | |
dimensionpattern = re.compile(beforethisafter % ( | |
'', 'dimension|virtual', 'dimension|virtual', '.*'), re.I), 'dimension' | |
externalpattern = re.compile( | |
beforethisafter % ('', 'external', 'external', '.*'), re.I), 'external' | |
optionalpattern = re.compile( | |
beforethisafter % ('', 'optional', 'optional', '.*'), re.I), 'optional' | |
requiredpattern = re.compile( | |
beforethisafter % ('', 'required', 'required', '.*'), re.I), 'required' | |
publicpattern = re.compile( | |
beforethisafter % ('', 'public', 'public', '.*'), re.I), 'public' | |
privatepattern = re.compile( | |
beforethisafter % ('', 'private', 'private', '.*'), re.I), 'private' | |
intrinsicpattern = re.compile( | |
beforethisafter % ('', 'intrinsic', 'intrinsic', '.*'), re.I), 'intrinsic' | |
intentpattern = re.compile(beforethisafter % ( | |
'', 'intent|depend|note|check', 'intent|depend|note|check', r'\s*\(.*?\).*'), re.I), 'intent' | |
parameterpattern = re.compile( | |
beforethisafter % ('', 'parameter', 'parameter', r'\s*\(.*'), re.I), 'parameter' | |
datapattern = re.compile( | |
beforethisafter % ('', 'data', 'data', '.*'), re.I), 'data' | |
callpattern = re.compile( | |
beforethisafter % ('', 'call', 'call', '.*'), re.I), 'call' | |
entrypattern = re.compile( | |
beforethisafter % ('', 'entry', 'entry', '.*'), re.I), 'entry' | |
callfunpattern = re.compile( | |
beforethisafter % ('', 'callfun', 'callfun', '.*'), re.I), 'callfun' | |
commonpattern = re.compile( | |
beforethisafter % ('', 'common', 'common', '.*'), re.I), 'common' | |
usepattern = re.compile( | |
beforethisafter % ('', 'use', 'use', '.*'), re.I), 'use' | |
containspattern = re.compile( | |
beforethisafter % ('', 'contains', 'contains', ''), re.I), 'contains' | |
formatpattern = re.compile( | |
beforethisafter % ('', 'format', 'format', '.*'), re.I), 'format' | |
# Non-fortran and f2py-specific statements | |
f2pyenhancementspattern = re.compile(beforethisafter % ('', 'threadsafe|fortranname|callstatement|callprotoargument|usercode|pymethoddef', | |
'threadsafe|fortranname|callstatement|callprotoargument|usercode|pymethoddef', '.*'), re.I | re.S), 'f2pyenhancements' | |
multilinepattern = re.compile( | |
r"\s*(?P<before>''')(?P<this>.*?)(?P<after>''')\s*\Z", re.S), 'multiline' | |
## | |
def split_by_unquoted(line, characters): | |
""" | |
Splits the line into (line[:i], line[i:]), | |
where i is the index of first occurrence of one of the characters | |
not within quotes, or len(line) if no such index exists | |
""" | |
assert not (set('"\'') & set(characters)), "cannot split by unquoted quotes" | |
r = re.compile( | |
r"\A(?P<before>({single_quoted}|{double_quoted}|{not_quoted})*)" | |
r"(?P<after>{char}.*)\Z".format( | |
not_quoted="[^\"'{}]".format(re.escape(characters)), | |
char="[{}]".format(re.escape(characters)), | |
single_quoted=r"('([^'\\]|(\\.))*')", | |
double_quoted=r'("([^"\\]|(\\.))*")')) | |
m = r.match(line) | |
if m: | |
d = m.groupdict() | |
return (d["before"], d["after"]) | |
return (line, "") | |
def _simplifyargs(argsline): | |
a = [] | |
for n in markoutercomma(argsline).split('@,@'): | |
for r in '(),': | |
n = n.replace(r, '_') | |
a.append(n) | |
return ','.join(a) | |
crackline_re_1 = re.compile(r'\s*(?P<result>\b[a-z]+\w*\b)\s*=.*', re.I) | |
def crackline(line, reset=0): | |
""" | |
reset=-1 --- initialize | |
reset=0 --- crack the line | |
reset=1 --- final check if mismatch of blocks occurred | |
Cracked data is saved in grouplist[0]. | |
""" | |
global beginpattern, groupcounter, groupname, groupcache, grouplist | |
global filepositiontext, currentfilename, neededmodule, expectbegin | |
global skipblocksuntil, skipemptyends, previous_context, gotnextfile | |
_, has_semicolon = split_by_unquoted(line, ";") | |
if has_semicolon and not (f2pyenhancementspattern[0].match(line) or | |
multilinepattern[0].match(line)): | |
# XXX: non-zero reset values need testing | |
assert reset == 0, repr(reset) | |
# split line on unquoted semicolons | |
line, semicolon_line = split_by_unquoted(line, ";") | |
while semicolon_line: | |
crackline(line, reset) | |
line, semicolon_line = split_by_unquoted(semicolon_line[1:], ";") | |
crackline(line, reset) | |
return | |
if reset < 0: | |
groupcounter = 0 | |
groupname = {groupcounter: ''} | |
groupcache = {groupcounter: {}} | |
grouplist = {groupcounter: []} | |
groupcache[groupcounter]['body'] = [] | |
groupcache[groupcounter]['vars'] = {} | |
groupcache[groupcounter]['block'] = '' | |
groupcache[groupcounter]['name'] = '' | |
neededmodule = -1 | |
skipblocksuntil = -1 | |
return | |
if reset > 0: | |
fl = 0 | |
if f77modulename and neededmodule == groupcounter: | |
fl = 2 | |
while groupcounter > fl: | |
outmess('crackline: groupcounter=%s groupname=%s\n' % | |
(repr(groupcounter), repr(groupname))) | |
outmess( | |
'crackline: Mismatch of blocks encountered. Trying to fix it by assuming "end" statement.\n') | |
grouplist[groupcounter - 1].append(groupcache[groupcounter]) | |
grouplist[groupcounter - 1][-1]['body'] = grouplist[groupcounter] | |
del grouplist[groupcounter] | |
groupcounter = groupcounter - 1 | |
if f77modulename and neededmodule == groupcounter: | |
grouplist[groupcounter - 1].append(groupcache[groupcounter]) | |
grouplist[groupcounter - 1][-1]['body'] = grouplist[groupcounter] | |
del grouplist[groupcounter] | |
groupcounter = groupcounter - 1 # end interface | |
grouplist[groupcounter - 1].append(groupcache[groupcounter]) | |
grouplist[groupcounter - 1][-1]['body'] = grouplist[groupcounter] | |
del grouplist[groupcounter] | |
groupcounter = groupcounter - 1 # end module | |
neededmodule = -1 | |
return | |
if line == '': | |
return | |
flag = 0 | |
for pat in [dimensionpattern, externalpattern, intentpattern, optionalpattern, | |
requiredpattern, | |
parameterpattern, datapattern, publicpattern, privatepattern, | |
intrinsicpattern, | |
endifpattern, endpattern, | |
formatpattern, | |
beginpattern, functionpattern, subroutinepattern, | |
implicitpattern, typespattern, commonpattern, | |
callpattern, usepattern, containspattern, | |
entrypattern, | |
f2pyenhancementspattern, | |
multilinepattern | |
]: | |
m = pat[0].match(line) | |
if m: | |
break | |
flag = flag + 1 | |
if not m: | |
re_1 = crackline_re_1 | |
if 0 <= skipblocksuntil <= groupcounter: | |
return | |
if 'externals' in groupcache[groupcounter]: | |
for name in groupcache[groupcounter]['externals']: | |
if name in invbadnames: | |
name = invbadnames[name] | |
if 'interfaced' in groupcache[groupcounter] and name in groupcache[groupcounter]['interfaced']: | |
continue | |
m1 = re.match( | |
r'(?P<before>[^"]*)\b%s\b\s*@\(@(?P<args>[^@]*)@\)@.*\Z' % name, markouterparen(line), re.I) | |
if m1: | |
m2 = re_1.match(m1.group('before')) | |
a = _simplifyargs(m1.group('args')) | |
if m2: | |
line = 'callfun %s(%s) result (%s)' % ( | |
name, a, m2.group('result')) | |
else: | |
line = 'callfun %s(%s)' % (name, a) | |
m = callfunpattern[0].match(line) | |
if not m: | |
outmess( | |
'crackline: could not resolve function call for line=%s.\n' % repr(line)) | |
return | |
analyzeline(m, 'callfun', line) | |
return | |
if verbose > 1 or (verbose == 1 and currentfilename.lower().endswith('.pyf')): | |
previous_context = None | |
outmess('crackline:%d: No pattern for line\n' % (groupcounter)) | |
return | |
elif pat[1] == 'end': | |
if 0 <= skipblocksuntil < groupcounter: | |
groupcounter = groupcounter - 1 | |
if skipblocksuntil <= groupcounter: | |
return | |
if groupcounter <= 0: | |
raise Exception('crackline: groupcounter(=%s) is nonpositive. ' | |
'Check the blocks.' | |
% (groupcounter)) | |
m1 = beginpattern[0].match((line)) | |
if (m1) and (not m1.group('this') == groupname[groupcounter]): | |
raise Exception('crackline: End group %s does not match with ' | |
'previous Begin group %s\n\t%s' % | |
(repr(m1.group('this')), repr(groupname[groupcounter]), | |
filepositiontext) | |
) | |
if skipblocksuntil == groupcounter: | |
skipblocksuntil = -1 | |
grouplist[groupcounter - 1].append(groupcache[groupcounter]) | |
grouplist[groupcounter - 1][-1]['body'] = grouplist[groupcounter] | |
del grouplist[groupcounter] | |
groupcounter = groupcounter - 1 | |
if not skipemptyends: | |
expectbegin = 1 | |
elif pat[1] == 'begin': | |
if 0 <= skipblocksuntil <= groupcounter: | |
groupcounter = groupcounter + 1 | |
return | |
gotnextfile = 0 | |
analyzeline(m, pat[1], line) | |
expectbegin = 0 | |
elif pat[1] == 'endif': | |
pass | |
elif pat[1] == 'contains': | |
if ignorecontains: | |
return | |
if 0 <= skipblocksuntil <= groupcounter: | |
return | |
skipblocksuntil = groupcounter | |
else: | |
if 0 <= skipblocksuntil <= groupcounter: | |
return | |
analyzeline(m, pat[1], line) | |
def markouterparen(line): | |
l = '' | |
f = 0 | |
for c in line: | |
if c == '(': | |
f = f + 1 | |
if f == 1: | |
l = l + '@(@' | |
continue | |
elif c == ')': | |
f = f - 1 | |
if f == 0: | |
l = l + '@)@' | |
continue | |
l = l + c | |
return l | |
def markoutercomma(line, comma=','): | |
l = '' | |
f = 0 | |
before, after = split_by_unquoted(line, comma + '()') | |
l += before | |
while after: | |
if (after[0] == comma) and (f == 0): | |
l += '@' + comma + '@' | |
else: | |
l += after[0] | |
if after[0] == '(': | |
f += 1 | |
elif after[0] == ')': | |
f -= 1 | |
before, after = split_by_unquoted(after[1:], comma + '()') | |
l += before | |
assert not f, repr((f, line, l)) | |
return l | |
def unmarkouterparen(line): | |
r = line.replace('@(@', '(').replace('@)@', ')') | |
return r | |
def appenddecl(decl, decl2, force=1): | |
if not decl: | |
decl = {} | |
if not decl2: | |
return decl | |
if decl is decl2: | |
return decl | |
for k in list(decl2.keys()): | |
if k == 'typespec': | |
if force or k not in decl: | |
decl[k] = decl2[k] | |
elif k == 'attrspec': | |
for l in decl2[k]: | |
decl = setattrspec(decl, l, force) | |
elif k == 'kindselector': | |
decl = setkindselector(decl, decl2[k], force) | |
elif k == 'charselector': | |
decl = setcharselector(decl, decl2[k], force) | |
elif k in ['=', 'typename']: | |
if force or k not in decl: | |
decl[k] = decl2[k] | |
elif k == 'note': | |
pass | |
elif k in ['intent', 'check', 'dimension', 'optional', 'required']: | |
errmess('appenddecl: "%s" not implemented.\n' % k) | |
else: | |
raise Exception('appenddecl: Unknown variable definition key:' + | |
str(k)) | |
return decl | |
selectpattern = re.compile( | |
r'\s*(?P<this>(@\(@.*?@\)@|\*[\d*]+|\*\s*@\(@.*?@\)@|))(?P<after>.*)\Z', re.I) | |
nameargspattern = re.compile( | |
r'\s*(?P<name>\b[\w$]+\b)\s*(@\(@\s*(?P<args>[\w\s,]*)\s*@\)@|)\s*((result(\s*@\(@\s*(?P<result>\b[\w$]+\b)\s*@\)@|))|(bind\s*@\(@\s*(?P<bind>.*)\s*@\)@))*\s*\Z', re.I) | |
callnameargspattern = re.compile( | |
r'\s*(?P<name>\b[\w$]+\b)\s*@\(@\s*(?P<args>.*)\s*@\)@\s*\Z', re.I) | |
real16pattern = re.compile( | |
r'([-+]?(?:\d+(?:\.\d*)?|\d*\.\d+))[dD]((?:[-+]?\d+)?)') | |
real8pattern = re.compile( | |
r'([-+]?((?:\d+(?:\.\d*)?|\d*\.\d+))[eE]((?:[-+]?\d+)?)|(\d+\.\d*))') | |
_intentcallbackpattern = re.compile(r'intent\s*\(.*?\bcallback\b', re.I) | |
def _is_intent_callback(vdecl): | |
for a in vdecl.get('attrspec', []): | |
if _intentcallbackpattern.match(a): | |
return 1 | |
return 0 | |
def _resolvenameargspattern(line): | |
line = markouterparen(line) | |
m1 = nameargspattern.match(line) | |
if m1: | |
return m1.group('name'), m1.group('args'), m1.group('result'), m1.group('bind') | |
m1 = callnameargspattern.match(line) | |
if m1: | |
return m1.group('name'), m1.group('args'), None, None | |
return None, [], None, None | |
def analyzeline(m, case, line): | |
global groupcounter, groupname, groupcache, grouplist, filepositiontext | |
global currentfilename, f77modulename, neededinterface, neededmodule | |
global expectbegin, gotnextfile, previous_context | |
block = m.group('this') | |
if case != 'multiline': | |
previous_context = None | |
if expectbegin and case not in ['begin', 'call', 'callfun', 'type'] \ | |
and not skipemptyends and groupcounter < 1: | |
newname = os.path.basename(currentfilename).split('.')[0] | |
outmess( | |
'analyzeline: no group yet. Creating program group with name "%s".\n' % newname) | |
gotnextfile = 0 | |
groupcounter = groupcounter + 1 | |
groupname[groupcounter] = 'program' | |
groupcache[groupcounter] = {} | |
grouplist[groupcounter] = [] | |
groupcache[groupcounter]['body'] = [] | |
groupcache[groupcounter]['vars'] = {} | |
groupcache[groupcounter]['block'] = 'program' | |
groupcache[groupcounter]['name'] = newname | |
groupcache[groupcounter]['from'] = 'fromsky' | |
expectbegin = 0 | |
if case in ['begin', 'call', 'callfun']: | |
# Crack line => block,name,args,result | |
block = block.lower() | |
if re.match(r'block\s*data', block, re.I): | |
block = 'block data' | |
elif re.match(r'python\s*module', block, re.I): | |
block = 'python module' | |
elif re.match(r'abstract\s*interface', block, re.I): | |
block = 'abstract interface' | |
name, args, result, bind = _resolvenameargspattern(m.group('after')) | |
if name is None: | |
if block == 'block data': | |
name = '_BLOCK_DATA_' | |
else: | |
name = '' | |
if block not in ['interface', 'block data', 'abstract interface']: | |
outmess('analyzeline: No name/args pattern found for line.\n') | |
previous_context = (block, name, groupcounter) | |
if args: | |
args = rmbadname([x.strip() | |
for x in markoutercomma(args).split('@,@')]) | |
else: | |
args = [] | |
if '' in args: | |
while '' in args: | |
args.remove('') | |
outmess( | |
'analyzeline: argument list is malformed (missing argument).\n') | |
# end of crack line => block,name,args,result | |
needmodule = 0 | |
needinterface = 0 | |
if case in ['call', 'callfun']: | |
needinterface = 1 | |
if 'args' not in groupcache[groupcounter]: | |
return | |
if name not in groupcache[groupcounter]['args']: | |
return | |
for it in grouplist[groupcounter]: | |
if it['name'] == name: | |
return | |
if name in groupcache[groupcounter]['interfaced']: | |
return | |
block = {'call': 'subroutine', 'callfun': 'function'}[case] | |
if f77modulename and neededmodule == -1 and groupcounter <= 1: | |
neededmodule = groupcounter + 2 | |
needmodule = 1 | |
if block not in ['interface', 'abstract interface']: | |
needinterface = 1 | |
# Create new block(s) | |
groupcounter = groupcounter + 1 | |
groupcache[groupcounter] = {} | |
grouplist[groupcounter] = [] | |
if needmodule: | |
if verbose > 1: | |
outmess('analyzeline: Creating module block %s\n' % | |
repr(f77modulename), 0) | |
groupname[groupcounter] = 'module' | |
groupcache[groupcounter]['block'] = 'python module' | |
groupcache[groupcounter]['name'] = f77modulename | |
groupcache[groupcounter]['from'] = '' | |
groupcache[groupcounter]['body'] = [] | |
groupcache[groupcounter]['externals'] = [] | |
groupcache[groupcounter]['interfaced'] = [] | |
groupcache[groupcounter]['vars'] = {} | |
groupcounter = groupcounter + 1 | |
groupcache[groupcounter] = {} | |
grouplist[groupcounter] = [] | |
if needinterface: | |
if verbose > 1: | |
outmess('analyzeline: Creating additional interface block (groupcounter=%s).\n' % ( | |
groupcounter), 0) | |
groupname[groupcounter] = 'interface' | |
groupcache[groupcounter]['block'] = 'interface' | |
groupcache[groupcounter]['name'] = 'unknown_interface' | |
groupcache[groupcounter]['from'] = '%s:%s' % ( | |
groupcache[groupcounter - 1]['from'], groupcache[groupcounter - 1]['name']) | |
groupcache[groupcounter]['body'] = [] | |
groupcache[groupcounter]['externals'] = [] | |
groupcache[groupcounter]['interfaced'] = [] | |
groupcache[groupcounter]['vars'] = {} | |
groupcounter = groupcounter + 1 | |
groupcache[groupcounter] = {} | |
grouplist[groupcounter] = [] | |
groupname[groupcounter] = block | |
groupcache[groupcounter]['block'] = block | |
if not name: | |
name = 'unknown_' + block.replace(' ', '_') | |
groupcache[groupcounter]['prefix'] = m.group('before') | |
groupcache[groupcounter]['name'] = rmbadname1(name) | |
groupcache[groupcounter]['result'] = result | |
if groupcounter == 1: | |
groupcache[groupcounter]['from'] = currentfilename | |
else: | |
if f77modulename and groupcounter == 3: | |
groupcache[groupcounter]['from'] = '%s:%s' % ( | |
groupcache[groupcounter - 1]['from'], currentfilename) | |
else: | |
groupcache[groupcounter]['from'] = '%s:%s' % ( | |
groupcache[groupcounter - 1]['from'], groupcache[groupcounter - 1]['name']) | |
for k in list(groupcache[groupcounter].keys()): | |
if not groupcache[groupcounter][k]: | |
del groupcache[groupcounter][k] | |
groupcache[groupcounter]['args'] = args | |
groupcache[groupcounter]['body'] = [] | |
groupcache[groupcounter]['externals'] = [] | |
groupcache[groupcounter]['interfaced'] = [] | |
groupcache[groupcounter]['vars'] = {} | |
groupcache[groupcounter]['entry'] = {} | |
# end of creation | |
if block == 'type': | |
groupcache[groupcounter]['varnames'] = [] | |
if case in ['call', 'callfun']: # set parents variables | |
if name not in groupcache[groupcounter - 2]['externals']: | |
groupcache[groupcounter - 2]['externals'].append(name) | |
groupcache[groupcounter]['vars'] = copy.deepcopy( | |
groupcache[groupcounter - 2]['vars']) | |
try: | |
del groupcache[groupcounter]['vars'][name][ | |
groupcache[groupcounter]['vars'][name]['attrspec'].index('external')] | |
except Exception: | |
pass | |
if block in ['function', 'subroutine']: # set global attributes | |
try: | |
groupcache[groupcounter]['vars'][name] = appenddecl( | |
groupcache[groupcounter]['vars'][name], groupcache[groupcounter - 2]['vars']['']) | |
except Exception: | |
pass | |
if case == 'callfun': # return type | |
if result and result in groupcache[groupcounter]['vars']: | |
if not name == result: | |
groupcache[groupcounter]['vars'][name] = appenddecl( | |
groupcache[groupcounter]['vars'][name], groupcache[groupcounter]['vars'][result]) | |
# if groupcounter>1: # name is interfaced | |
try: | |
groupcache[groupcounter - 2]['interfaced'].append(name) | |
except Exception: | |
pass | |
if block == 'function': | |
t = typespattern[0].match(m.group('before') + ' ' + name) | |
if t: | |
typespec, selector, attr, edecl = cracktypespec0( | |
t.group('this'), t.group('after')) | |
updatevars(typespec, selector, attr, edecl) | |
if case in ['call', 'callfun']: | |
grouplist[groupcounter - 1].append(groupcache[groupcounter]) | |
grouplist[groupcounter - 1][-1]['body'] = grouplist[groupcounter] | |
del grouplist[groupcounter] | |
groupcounter = groupcounter - 1 # end routine | |
grouplist[groupcounter - 1].append(groupcache[groupcounter]) | |
grouplist[groupcounter - 1][-1]['body'] = grouplist[groupcounter] | |
del grouplist[groupcounter] | |
groupcounter = groupcounter - 1 # end interface | |
elif case == 'entry': | |
name, args, result, bind = _resolvenameargspattern(m.group('after')) | |
if name is not None: | |
if args: | |
args = rmbadname([x.strip() | |
for x in markoutercomma(args).split('@,@')]) | |
else: | |
args = [] | |
assert result is None, repr(result) | |
groupcache[groupcounter]['entry'][name] = args | |
previous_context = ('entry', name, groupcounter) | |
elif case == 'type': | |
typespec, selector, attr, edecl = cracktypespec0( | |
block, m.group('after')) | |
last_name = updatevars(typespec, selector, attr, edecl) | |
if last_name is not None: | |
previous_context = ('variable', last_name, groupcounter) | |
elif case in ['dimension', 'intent', 'optional', 'required', 'external', 'public', 'private', 'intrinsic']: | |
edecl = groupcache[groupcounter]['vars'] | |
ll = m.group('after').strip() | |
i = ll.find('::') | |
if i < 0 and case == 'intent': | |
i = markouterparen(ll).find('@)@') - 2 | |
ll = ll[:i + 1] + '::' + ll[i + 1:] | |
i = ll.find('::') | |
if ll[i:] == '::' and 'args' in groupcache[groupcounter]: | |
outmess('All arguments will have attribute %s%s\n' % | |
(m.group('this'), ll[:i])) | |
ll = ll + ','.join(groupcache[groupcounter]['args']) | |
if i < 0: | |
i = 0 | |
pl = '' | |
else: | |
pl = ll[:i].strip() | |
ll = ll[i + 2:] | |
ch = markoutercomma(pl).split('@,@') | |
if len(ch) > 1: | |
pl = ch[0] | |
outmess('analyzeline: cannot handle multiple attributes without type specification. Ignoring %r.\n' % ( | |
','.join(ch[1:]))) | |
last_name = None | |
for e in [x.strip() for x in markoutercomma(ll).split('@,@')]: | |
m1 = namepattern.match(e) | |
if not m1: | |
if case in ['public', 'private']: | |
k = '' | |
else: | |
print(m.groupdict()) | |
outmess('analyzeline: no name pattern found in %s statement for %s. Skipping.\n' % ( | |
case, repr(e))) | |
continue | |
else: | |
k = rmbadname1(m1.group('name')) | |
if k not in edecl: | |
edecl[k] = {} | |
if case == 'dimension': | |
ap = case + m1.group('after') | |
if case == 'intent': | |
ap = m.group('this') + pl | |
if _intentcallbackpattern.match(ap): | |
if k not in groupcache[groupcounter]['args']: | |
if groupcounter > 1: | |
if '__user__' not in groupcache[groupcounter - 2]['name']: | |
outmess( | |
'analyzeline: missing __user__ module (could be nothing)\n') | |
# fixes ticket 1693 | |
if k != groupcache[groupcounter]['name']: | |
outmess('analyzeline: appending intent(callback) %s' | |
' to %s arguments\n' % (k, groupcache[groupcounter]['name'])) | |
groupcache[groupcounter]['args'].append(k) | |
else: | |
errmess( | |
'analyzeline: intent(callback) %s is ignored' % (k)) | |
else: | |
errmess('analyzeline: intent(callback) %s is already' | |
' in argument list' % (k)) | |
if case in ['optional', 'required', 'public', 'external', 'private', 'intrinsic']: | |
ap = case | |
if 'attrspec' in edecl[k]: | |
edecl[k]['attrspec'].append(ap) | |
else: | |
edecl[k]['attrspec'] = [ap] | |
if case == 'external': | |
if groupcache[groupcounter]['block'] == 'program': | |
outmess('analyzeline: ignoring program arguments\n') | |
continue | |
if k not in groupcache[groupcounter]['args']: | |
continue | |
if 'externals' not in groupcache[groupcounter]: | |
groupcache[groupcounter]['externals'] = [] | |
groupcache[groupcounter]['externals'].append(k) | |
last_name = k | |
groupcache[groupcounter]['vars'] = edecl | |
if last_name is not None: | |
previous_context = ('variable', last_name, groupcounter) | |
elif case == 'parameter': | |
edecl = groupcache[groupcounter]['vars'] | |
ll = m.group('after').strip()[1:-1] | |
last_name = None | |
for e in markoutercomma(ll).split('@,@'): | |
try: | |
k, initexpr = [x.strip() for x in e.split('=')] | |
except Exception: | |
outmess( | |
'analyzeline: could not extract name,expr in parameter statement "%s" of "%s"\n' % (e, ll)) | |
continue | |
params = get_parameters(edecl) | |
k = rmbadname1(k) | |
if k not in edecl: | |
edecl[k] = {} | |
if '=' in edecl[k] and (not edecl[k]['='] == initexpr): | |
outmess('analyzeline: Overwriting the value of parameter "%s" ("%s") with "%s".\n' % ( | |
k, edecl[k]['='], initexpr)) | |
t = determineexprtype(initexpr, params) | |
if t: | |
if t.get('typespec') == 'real': | |
tt = list(initexpr) | |
for m in real16pattern.finditer(initexpr): | |
tt[m.start():m.end()] = list( | |
initexpr[m.start():m.end()].lower().replace('d', 'e')) | |
initexpr = ''.join(tt) | |
elif t.get('typespec') == 'complex': | |
initexpr = initexpr[1:].lower().replace('d', 'e').\ | |
replace(',', '+1j*(') | |
try: | |
v = eval(initexpr, {}, params) | |
except (SyntaxError, NameError, TypeError) as msg: | |
errmess('analyzeline: Failed to evaluate %r. Ignoring: %s\n' | |
% (initexpr, msg)) | |
continue | |
edecl[k]['='] = repr(v) | |
if 'attrspec' in edecl[k]: | |
edecl[k]['attrspec'].append('parameter') | |
else: | |
edecl[k]['attrspec'] = ['parameter'] | |
last_name = k | |
groupcache[groupcounter]['vars'] = edecl | |
if last_name is not None: | |
previous_context = ('variable', last_name, groupcounter) | |
elif case == 'implicit': | |
if m.group('after').strip().lower() == 'none': | |
groupcache[groupcounter]['implicit'] = None | |
elif m.group('after'): | |
if 'implicit' in groupcache[groupcounter]: | |
impl = groupcache[groupcounter]['implicit'] | |
else: | |
impl = {} | |
if impl is None: | |
outmess( | |
'analyzeline: Overwriting earlier "implicit none" statement.\n') | |
impl = {} | |
for e in markoutercomma(m.group('after')).split('@,@'): | |
decl = {} | |
m1 = re.match( | |
r'\s*(?P<this>.*?)\s*(\(\s*(?P<after>[a-z-, ]+)\s*\)\s*|)\Z', e, re.I) | |
if not m1: | |
outmess( | |
'analyzeline: could not extract info of implicit statement part "%s"\n' % (e)) | |
continue | |
m2 = typespattern4implicit.match(m1.group('this')) | |
if not m2: | |
outmess( | |
'analyzeline: could not extract types pattern of implicit statement part "%s"\n' % (e)) | |
continue | |
typespec, selector, attr, edecl = cracktypespec0( | |
m2.group('this'), m2.group('after')) | |
kindselect, charselect, typename = cracktypespec( | |
typespec, selector) | |
decl['typespec'] = typespec | |
decl['kindselector'] = kindselect | |
decl['charselector'] = charselect | |
decl['typename'] = typename | |
for k in list(decl.keys()): | |
if not decl[k]: | |
del decl[k] | |
for r in markoutercomma(m1.group('after')).split('@,@'): | |
if '-' in r: | |
try: | |
begc, endc = [x.strip() for x in r.split('-')] | |
except Exception: | |
outmess( | |
'analyzeline: expected "<char>-<char>" instead of "%s" in range list of implicit statement\n' % r) | |
continue | |
else: | |
begc = endc = r.strip() | |
if not len(begc) == len(endc) == 1: | |
outmess( | |
'analyzeline: expected "<char>-<char>" instead of "%s" in range list of implicit statement (2)\n' % r) | |
continue | |
for o in range(ord(begc), ord(endc) + 1): | |
impl[chr(o)] = decl | |
groupcache[groupcounter]['implicit'] = impl | |
elif case == 'data': | |
ll = [] | |
dl = '' | |
il = '' | |
f = 0 | |
fc = 1 | |
inp = 0 | |
for c in m.group('after'): | |
if not inp: | |
if c == "'": | |
fc = not fc | |
if c == '/' and fc: | |
f = f + 1 | |
continue | |
if c == '(': | |
inp = inp + 1 | |
elif c == ')': | |
inp = inp - 1 | |
if f == 0: | |
dl = dl + c | |
elif f == 1: | |
il = il + c | |
elif f == 2: | |
dl = dl.strip() | |
if dl.startswith(','): | |
dl = dl[1:].strip() | |
ll.append([dl, il]) | |
dl = c | |
il = '' | |
f = 0 | |
if f == 2: | |
dl = dl.strip() | |
if dl.startswith(','): | |
dl = dl[1:].strip() | |
ll.append([dl, il]) | |
vars = {} | |
if 'vars' in groupcache[groupcounter]: | |
vars = groupcache[groupcounter]['vars'] | |
last_name = None | |
for l in ll: | |
l = [x.strip() for x in l] | |
if l[0][0] == ',': | |
l[0] = l[0][1:] | |
if l[0][0] == '(': | |
outmess( | |
'analyzeline: implied-DO list "%s" is not supported. Skipping.\n' % l[0]) | |
continue | |
i = 0 | |
j = 0 | |
llen = len(l[1]) | |
for v in rmbadname([x.strip() for x in markoutercomma(l[0]).split('@,@')]): | |
if v[0] == '(': | |
outmess( | |
'analyzeline: implied-DO list "%s" is not supported. Skipping.\n' % v) | |
# XXX: subsequent init expressions may get wrong values. | |
# Ignoring since data statements are irrelevant for | |
# wrapping. | |
continue | |
fc = 0 | |
while (i < llen) and (fc or not l[1][i] == ','): | |
if l[1][i] == "'": | |
fc = not fc | |
i = i + 1 | |
i = i + 1 | |
if v not in vars: | |
vars[v] = {} | |
if '=' in vars[v] and not vars[v]['='] == l[1][j:i - 1]: | |
outmess('analyzeline: changing init expression of "%s" ("%s") to "%s"\n' % ( | |
v, vars[v]['='], l[1][j:i - 1])) | |
vars[v]['='] = l[1][j:i - 1] | |
j = i | |
last_name = v | |
groupcache[groupcounter]['vars'] = vars | |
if last_name is not None: | |
previous_context = ('variable', last_name, groupcounter) | |
elif case == 'common': | |
line = m.group('after').strip() | |
if not line[0] == '/': | |
line = '//' + line | |
cl = [] | |
f = 0 | |
bn = '' | |
ol = '' | |
for c in line: | |
if c == '/': | |
f = f + 1 | |
continue | |
if f >= 3: | |
bn = bn.strip() | |
if not bn: | |
bn = '_BLNK_' | |
cl.append([bn, ol]) | |
f = f - 2 | |
bn = '' | |
ol = '' | |
if f % 2: | |
bn = bn + c | |
else: | |
ol = ol + c | |
bn = bn.strip() | |
if not bn: | |
bn = '_BLNK_' | |
cl.append([bn, ol]) | |
commonkey = {} | |
if 'common' in groupcache[groupcounter]: | |
commonkey = groupcache[groupcounter]['common'] | |
for c in cl: | |
if c[0] not in commonkey: | |
commonkey[c[0]] = [] | |
for i in [x.strip() for x in markoutercomma(c[1]).split('@,@')]: | |
if i: | |
commonkey[c[0]].append(i) | |
groupcache[groupcounter]['common'] = commonkey | |
previous_context = ('common', bn, groupcounter) | |
elif case == 'use': | |
m1 = re.match( | |
r'\A\s*(?P<name>\b\w+\b)\s*((,(\s*\bonly\b\s*:|(?P<notonly>))\s*(?P<list>.*))|)\s*\Z', m.group('after'), re.I) | |
if m1: | |
mm = m1.groupdict() | |
if 'use' not in groupcache[groupcounter]: | |
groupcache[groupcounter]['use'] = {} | |
name = m1.group('name') | |
groupcache[groupcounter]['use'][name] = {} | |
isonly = 0 | |
if 'list' in mm and mm['list'] is not None: | |
if 'notonly' in mm and mm['notonly'] is None: | |
isonly = 1 | |
groupcache[groupcounter]['use'][name]['only'] = isonly | |
ll = [x.strip() for x in mm['list'].split(',')] | |
rl = {} | |
for l in ll: | |
if '=' in l: | |
m2 = re.match( | |
r'\A\s*(?P<local>\b\w+\b)\s*=\s*>\s*(?P<use>\b\w+\b)\s*\Z', l, re.I) | |
if m2: | |
rl[m2.group('local').strip()] = m2.group( | |
'use').strip() | |
else: | |
outmess( | |
'analyzeline: Not local=>use pattern found in %s\n' % repr(l)) | |
else: | |
rl[l] = l | |
groupcache[groupcounter]['use'][name]['map'] = rl | |
else: | |
pass | |
else: | |
print(m.groupdict()) | |
outmess('analyzeline: Could not crack the use statement.\n') | |
elif case in ['f2pyenhancements']: | |
if 'f2pyenhancements' not in groupcache[groupcounter]: | |
groupcache[groupcounter]['f2pyenhancements'] = {} | |
d = groupcache[groupcounter]['f2pyenhancements'] | |
if m.group('this') == 'usercode' and 'usercode' in d: | |
if isinstance(d['usercode'], str): | |
d['usercode'] = [d['usercode']] | |
d['usercode'].append(m.group('after')) | |
else: | |
d[m.group('this')] = m.group('after') | |
elif case == 'multiline': | |
if previous_context is None: | |
if verbose: | |
outmess('analyzeline: No context for multiline block.\n') | |
return | |
gc = groupcounter | |
appendmultiline(groupcache[gc], | |
previous_context[:2], | |
m.group('this')) | |
else: | |
if verbose > 1: | |
print(m.groupdict()) | |
outmess('analyzeline: No code implemented for line.\n') | |
def appendmultiline(group, context_name, ml): | |
if 'f2pymultilines' not in group: | |
group['f2pymultilines'] = {} | |
d = group['f2pymultilines'] | |
if context_name not in d: | |
d[context_name] = [] | |
d[context_name].append(ml) | |
return | |
def cracktypespec0(typespec, ll): | |
selector = None | |
attr = None | |
if re.match(r'double\s*complex', typespec, re.I): | |
typespec = 'double complex' | |
elif re.match(r'double\s*precision', typespec, re.I): | |
typespec = 'double precision' | |
else: | |
typespec = typespec.strip().lower() | |
m1 = selectpattern.match(markouterparen(ll)) | |
if not m1: | |
outmess( | |
'cracktypespec0: no kind/char_selector pattern found for line.\n') | |
return | |
d = m1.groupdict() | |
for k in list(d.keys()): | |
d[k] = unmarkouterparen(d[k]) | |
if typespec in ['complex', 'integer', 'logical', 'real', 'character', 'type']: | |
selector = d['this'] | |
ll = d['after'] | |
i = ll.find('::') | |
if i >= 0: | |
attr = ll[:i].strip() | |
ll = ll[i + 2:] | |
return typespec, selector, attr, ll | |
##### | |
namepattern = re.compile(r'\s*(?P<name>\b\w+\b)\s*(?P<after>.*)\s*\Z', re.I) | |
kindselector = re.compile( | |
r'\s*(\(\s*(kind\s*=)?\s*(?P<kind>.*)\s*\)|\*\s*(?P<kind2>.*?))\s*\Z', re.I) | |
charselector = re.compile( | |
r'\s*(\((?P<lenkind>.*)\)|\*\s*(?P<charlen>.*))\s*\Z', re.I) | |
lenkindpattern = re.compile( | |
r'\s*(kind\s*=\s*(?P<kind>.*?)\s*(@,@\s*len\s*=\s*(?P<len>.*)|)|(len\s*=\s*|)(?P<len2>.*?)\s*(@,@\s*(kind\s*=\s*|)(?P<kind2>.*)|))\s*\Z', re.I) | |
lenarraypattern = re.compile( | |
r'\s*(@\(@\s*(?!/)\s*(?P<array>.*?)\s*@\)@\s*\*\s*(?P<len>.*?)|(\*\s*(?P<len2>.*?)|)\s*(@\(@\s*(?!/)\s*(?P<array2>.*?)\s*@\)@|))\s*(=\s*(?P<init>.*?)|(@\(@|)/\s*(?P<init2>.*?)\s*/(@\)@|)|)\s*\Z', re.I) | |
def removespaces(expr): | |
expr = expr.strip() | |
if len(expr) <= 1: | |
return expr | |
expr2 = expr[0] | |
for i in range(1, len(expr) - 1): | |
if (expr[i] == ' ' and | |
((expr[i + 1] in "()[]{}=+-/* ") or | |
(expr[i - 1] in "()[]{}=+-/* "))): | |
continue | |
expr2 = expr2 + expr[i] | |
expr2 = expr2 + expr[-1] | |
return expr2 | |
def markinnerspaces(line): | |
l = '' | |
f = 0 | |
cc = '\'' | |
cb = '' | |
for c in line: | |
if cb == '\\' and c in ['\\', '\'', '"']: | |
l = l + c | |
cb = c | |
continue | |
if f == 0 and c in ['\'', '"']: | |
cc = c | |
if c == cc: | |
f = f + 1 | |
elif c == cc: | |
f = f - 1 | |
elif c == ' ' and f == 1: | |
l = l + '@_@' | |
continue | |
l = l + c | |
cb = c | |
return l | |
def updatevars(typespec, selector, attrspec, entitydecl): | |
global groupcache, groupcounter | |
last_name = None | |
kindselect, charselect, typename = cracktypespec(typespec, selector) | |
if attrspec: | |
attrspec = [x.strip() for x in markoutercomma(attrspec).split('@,@')] | |
l = [] | |
c = re.compile(r'(?P<start>[a-zA-Z]+)') | |
for a in attrspec: | |
if not a: | |
continue | |
m = c.match(a) | |
if m: | |
s = m.group('start').lower() | |
a = s + a[len(s):] | |
l.append(a) | |
attrspec = l | |
el = [x.strip() for x in markoutercomma(entitydecl).split('@,@')] | |
el1 = [] | |
for e in el: | |
for e1 in [x.strip() for x in markoutercomma(removespaces(markinnerspaces(e)), comma=' ').split('@ @')]: | |
if e1: | |
el1.append(e1.replace('@_@', ' ')) | |
for e in el1: | |
m = namepattern.match(e) | |
if not m: | |
outmess( | |
'updatevars: no name pattern found for entity=%s. Skipping.\n' % (repr(e))) | |
continue | |
ename = rmbadname1(m.group('name')) | |
edecl = {} | |
if ename in groupcache[groupcounter]['vars']: | |
edecl = groupcache[groupcounter]['vars'][ename].copy() | |
not_has_typespec = 'typespec' not in edecl | |
if not_has_typespec: | |
edecl['typespec'] = typespec | |
elif typespec and (not typespec == edecl['typespec']): | |
outmess('updatevars: attempt to change the type of "%s" ("%s") to "%s". Ignoring.\n' % ( | |
ename, edecl['typespec'], typespec)) | |
if 'kindselector' not in edecl: | |
edecl['kindselector'] = copy.copy(kindselect) | |
elif kindselect: | |
for k in list(kindselect.keys()): | |
if k in edecl['kindselector'] and (not kindselect[k] == edecl['kindselector'][k]): | |
outmess('updatevars: attempt to change the kindselector "%s" of "%s" ("%s") to "%s". Ignoring.\n' % ( | |
k, ename, edecl['kindselector'][k], kindselect[k])) | |
else: | |
edecl['kindselector'][k] = copy.copy(kindselect[k]) | |
if 'charselector' not in edecl and charselect: | |
if not_has_typespec: | |
edecl['charselector'] = charselect | |
else: | |
errmess('updatevars:%s: attempt to change empty charselector to %r. Ignoring.\n' | |
% (ename, charselect)) | |
elif charselect: | |
for k in list(charselect.keys()): | |
if k in edecl['charselector'] and (not charselect[k] == edecl['charselector'][k]): | |
outmess('updatevars: attempt to change the charselector "%s" of "%s" ("%s") to "%s". Ignoring.\n' % ( | |
k, ename, edecl['charselector'][k], charselect[k])) | |
else: | |
edecl['charselector'][k] = copy.copy(charselect[k]) | |
if 'typename' not in edecl: | |
edecl['typename'] = typename | |
elif typename and (not edecl['typename'] == typename): | |
outmess('updatevars: attempt to change the typename of "%s" ("%s") to "%s". Ignoring.\n' % ( | |
ename, edecl['typename'], typename)) | |
if 'attrspec' not in edecl: | |
edecl['attrspec'] = copy.copy(attrspec) | |
elif attrspec: | |
for a in attrspec: | |
if a not in edecl['attrspec']: | |
edecl['attrspec'].append(a) | |
else: | |
edecl['typespec'] = copy.copy(typespec) | |
edecl['kindselector'] = copy.copy(kindselect) | |
edecl['charselector'] = copy.copy(charselect) | |
edecl['typename'] = typename | |
edecl['attrspec'] = copy.copy(attrspec) | |
if 'external' in (edecl.get('attrspec') or []) and e in groupcache[groupcounter]['args']: | |
if 'externals' not in groupcache[groupcounter]: | |
groupcache[groupcounter]['externals'] = [] | |
groupcache[groupcounter]['externals'].append(e) | |
if m.group('after'): | |
m1 = lenarraypattern.match(markouterparen(m.group('after'))) | |
if m1: | |
d1 = m1.groupdict() | |
for lk in ['len', 'array', 'init']: | |
if d1[lk + '2'] is not None: | |
d1[lk] = d1[lk + '2'] | |
del d1[lk + '2'] | |
for k in list(d1.keys()): | |
if d1[k] is not None: | |
d1[k] = unmarkouterparen(d1[k]) | |
else: | |
del d1[k] | |
if 'len' in d1 and 'array' in d1: | |
if d1['len'] == '': | |
d1['len'] = d1['array'] | |
del d1['array'] | |
else: | |
d1['array'] = d1['array'] + ',' + d1['len'] | |
del d1['len'] | |
errmess('updatevars: "%s %s" is mapped to "%s %s(%s)"\n' % ( | |
typespec, e, typespec, ename, d1['array'])) | |
if 'array' in d1: | |
dm = 'dimension(%s)' % d1['array'] | |
if 'attrspec' not in edecl or (not edecl['attrspec']): | |
edecl['attrspec'] = [dm] | |
else: | |
edecl['attrspec'].append(dm) | |
for dm1 in edecl['attrspec']: | |
if dm1[:9] == 'dimension' and dm1 != dm: | |
del edecl['attrspec'][-1] | |
errmess('updatevars:%s: attempt to change %r to %r. Ignoring.\n' | |
% (ename, dm1, dm)) | |
break | |
if 'len' in d1: | |
if typespec in ['complex', 'integer', 'logical', 'real']: | |
if ('kindselector' not in edecl) or (not edecl['kindselector']): | |
edecl['kindselector'] = {} | |
edecl['kindselector']['*'] = d1['len'] | |
elif typespec == 'character': | |
if ('charselector' not in edecl) or (not edecl['charselector']): | |
edecl['charselector'] = {} | |
if 'len' in edecl['charselector']: | |
del edecl['charselector']['len'] | |
edecl['charselector']['*'] = d1['len'] | |
if 'init' in d1: | |
if '=' in edecl and (not edecl['='] == d1['init']): | |
outmess('updatevars: attempt to change the init expression of "%s" ("%s") to "%s". Ignoring.\n' % ( | |
ename, edecl['='], d1['init'])) | |
else: | |
edecl['='] = d1['init'] | |
else: | |
outmess('updatevars: could not crack entity declaration "%s". Ignoring.\n' % ( | |
ename + m.group('after'))) | |
for k in list(edecl.keys()): | |
if not edecl[k]: | |
del edecl[k] | |
groupcache[groupcounter]['vars'][ename] = edecl | |
if 'varnames' in groupcache[groupcounter]: | |
groupcache[groupcounter]['varnames'].append(ename) | |
last_name = ename | |
return last_name | |
def cracktypespec(typespec, selector): | |
kindselect = None | |
charselect = None | |
typename = None | |
if selector: | |
if typespec in ['complex', 'integer', 'logical', 'real']: | |
kindselect = kindselector.match(selector) | |
if not kindselect: | |
outmess( | |
'cracktypespec: no kindselector pattern found for %s\n' % (repr(selector))) | |
return | |
kindselect = kindselect.groupdict() | |
kindselect['*'] = kindselect['kind2'] | |
del kindselect['kind2'] | |
for k in list(kindselect.keys()): | |
if not kindselect[k]: | |
del kindselect[k] | |
for k, i in list(kindselect.items()): | |
kindselect[k] = rmbadname1(i) | |
elif typespec == 'character': | |
charselect = charselector.match(selector) | |
if not charselect: | |
outmess( | |
'cracktypespec: no charselector pattern found for %s\n' % (repr(selector))) | |
return | |
charselect = charselect.groupdict() | |
charselect['*'] = charselect['charlen'] | |
del charselect['charlen'] | |
if charselect['lenkind']: | |
lenkind = lenkindpattern.match( | |
markoutercomma(charselect['lenkind'])) | |
lenkind = lenkind.groupdict() | |
for lk in ['len', 'kind']: | |
if lenkind[lk + '2']: | |
lenkind[lk] = lenkind[lk + '2'] | |
charselect[lk] = lenkind[lk] | |
del lenkind[lk + '2'] | |
del charselect['lenkind'] | |
for k in list(charselect.keys()): | |
if not charselect[k]: | |
del charselect[k] | |
for k, i in list(charselect.items()): | |
charselect[k] = rmbadname1(i) | |
elif typespec == 'type': | |
typename = re.match(r'\s*\(\s*(?P<name>\w+)\s*\)', selector, re.I) | |
if typename: | |
typename = typename.group('name') | |
else: | |
outmess('cracktypespec: no typename found in %s\n' % | |
(repr(typespec + selector))) | |
else: | |
outmess('cracktypespec: no selector used for %s\n' % | |
(repr(selector))) | |
return kindselect, charselect, typename | |
###### | |
def setattrspec(decl, attr, force=0): | |
if not decl: | |
decl = {} | |
if not attr: | |
return decl | |
if 'attrspec' not in decl: | |
decl['attrspec'] = [attr] | |
return decl | |
if force: | |
decl['attrspec'].append(attr) | |
if attr in decl['attrspec']: | |
return decl | |
if attr == 'static' and 'automatic' not in decl['attrspec']: | |
decl['attrspec'].append(attr) | |
elif attr == 'automatic' and 'static' not in decl['attrspec']: | |
decl['attrspec'].append(attr) | |
elif attr == 'public': | |
if 'private' not in decl['attrspec']: | |
decl['attrspec'].append(attr) | |
elif attr == 'private': | |
if 'public' not in decl['attrspec']: | |
decl['attrspec'].append(attr) | |
else: | |
decl['attrspec'].append(attr) | |
return decl | |
def setkindselector(decl, sel, force=0): | |
if not decl: | |
decl = {} | |
if not sel: | |
return decl | |
if 'kindselector' not in decl: | |
decl['kindselector'] = sel | |
return decl | |
for k in list(sel.keys()): | |
if force or k not in decl['kindselector']: | |
decl['kindselector'][k] = sel[k] | |
return decl | |
def setcharselector(decl, sel, force=0): | |
if not decl: | |
decl = {} | |
if not sel: | |
return decl | |
if 'charselector' not in decl: | |
decl['charselector'] = sel | |
return decl | |
for k in list(sel.keys()): | |
if force or k not in decl['charselector']: | |
decl['charselector'][k] = sel[k] | |
return decl | |
def getblockname(block, unknown='unknown'): | |
if 'name' in block: | |
return block['name'] | |
return unknown | |
# post processing | |
def setmesstext(block): | |
global filepositiontext | |
try: | |
filepositiontext = 'In: %s:%s\n' % (block['from'], block['name']) | |
except Exception: | |
pass | |
def get_usedict(block): | |
usedict = {} | |
if 'parent_block' in block: | |
usedict = get_usedict(block['parent_block']) | |
if 'use' in block: | |
usedict.update(block['use']) | |
return usedict | |
def get_useparameters(block, param_map=None): | |
global f90modulevars | |
if param_map is None: | |
param_map = {} | |
usedict = get_usedict(block) | |
if not usedict: | |
return param_map | |
for usename, mapping in list(usedict.items()): | |
usename = usename.lower() | |
if usename not in f90modulevars: | |
outmess('get_useparameters: no module %s info used by %s\n' % | |
(usename, block.get('name'))) | |
continue | |
mvars = f90modulevars[usename] | |
params = get_parameters(mvars) | |
if not params: | |
continue | |
# XXX: apply mapping | |
if mapping: | |
errmess('get_useparameters: mapping for %s not impl.' % (mapping)) | |
for k, v in list(params.items()): | |
if k in param_map: | |
outmess('get_useparameters: overriding parameter %s with' | |
' value from module %s' % (repr(k), repr(usename))) | |
param_map[k] = v | |
return param_map | |
def postcrack2(block, tab='', param_map=None): | |
global f90modulevars | |
if not f90modulevars: | |
return block | |
if isinstance(block, list): | |
ret = [postcrack2(g, tab=tab + '\t', param_map=param_map) | |
for g in block] | |
return ret | |
setmesstext(block) | |
outmess('%sBlock: %s\n' % (tab, block['name']), 0) | |
if param_map is None: | |
param_map = get_useparameters(block) | |
if param_map is not None and 'vars' in block: | |
vars = block['vars'] | |
for n in list(vars.keys()): | |
var = vars[n] | |
if 'kindselector' in var: | |
kind = var['kindselector'] | |
if 'kind' in kind: | |
val = kind['kind'] | |
if val in param_map: | |
kind['kind'] = param_map[val] | |
new_body = [postcrack2(b, tab=tab + '\t', param_map=param_map) | |
for b in block['body']] | |
block['body'] = new_body | |
return block | |
def postcrack(block, args=None, tab=''): | |
""" | |
TODO: | |
function return values | |
determine expression types if in argument list | |
""" | |
global usermodules, onlyfunctions | |
if isinstance(block, list): | |
gret = [] | |
uret = [] | |
for g in block: | |
setmesstext(g) | |
g = postcrack(g, tab=tab + '\t') | |
# sort user routines to appear first | |
if 'name' in g and '__user__' in g['name']: | |
uret.append(g) | |
else: | |
gret.append(g) | |
return uret + gret | |
setmesstext(block) | |
if not isinstance(block, dict) and 'block' not in block: | |
raise Exception('postcrack: Expected block dictionary instead of ' + | |
str(block)) | |
if 'name' in block and not block['name'] == 'unknown_interface': | |
outmess('%sBlock: %s\n' % (tab, block['name']), 0) | |
block = analyzeargs(block) | |
block = analyzecommon(block) | |
block['vars'] = analyzevars(block) | |
block['sortvars'] = sortvarnames(block['vars']) | |
if 'args' in block and block['args']: | |
args = block['args'] | |
block['body'] = analyzebody(block, args, tab=tab) | |
userisdefined = [] | |
if 'use' in block: | |
useblock = block['use'] | |
for k in list(useblock.keys()): | |
if '__user__' in k: | |
userisdefined.append(k) | |
else: | |
useblock = {} | |
name = '' | |
if 'name' in block: | |
name = block['name'] | |
# and not userisdefined: # Build a __user__ module | |
if 'externals' in block and block['externals']: | |
interfaced = [] | |
if 'interfaced' in block: | |
interfaced = block['interfaced'] | |
mvars = copy.copy(block['vars']) | |
if name: | |
mname = name + '__user__routines' | |
else: | |
mname = 'unknown__user__routines' | |
if mname in userisdefined: | |
i = 1 | |
while '%s_%i' % (mname, i) in userisdefined: | |
i = i + 1 | |
mname = '%s_%i' % (mname, i) | |
interface = {'block': 'interface', 'body': [], | |
'vars': {}, 'name': name + '_user_interface'} | |
for e in block['externals']: | |
if e in interfaced: | |
edef = [] | |
j = -1 | |
for b in block['body']: | |
j = j + 1 | |
if b['block'] == 'interface': | |
i = -1 | |
for bb in b['body']: | |
i = i + 1 | |
if 'name' in bb and bb['name'] == e: | |
edef = copy.copy(bb) | |
del b['body'][i] | |
break | |
if edef: | |
if not b['body']: | |
del block['body'][j] | |
del interfaced[interfaced.index(e)] | |
break | |
interface['body'].append(edef) | |
else: | |
if e in mvars and not isexternal(mvars[e]): | |
interface['vars'][e] = mvars[e] | |
if interface['vars'] or interface['body']: | |
block['interfaced'] = interfaced | |
mblock = {'block': 'python module', 'body': [ | |
interface], 'vars': {}, 'name': mname, 'interfaced': block['externals']} | |
useblock[mname] = {} | |
usermodules.append(mblock) | |
if useblock: | |
block['use'] = useblock | |
return block | |
def sortvarnames(vars): | |
indep = [] | |
dep = [] | |
for v in list(vars.keys()): | |
if 'depend' in vars[v] and vars[v]['depend']: | |
dep.append(v) | |
else: | |
indep.append(v) | |
n = len(dep) | |
i = 0 | |
while dep: # XXX: How to catch dependence cycles correctly? | |
v = dep[0] | |
fl = 0 | |
for w in dep[1:]: | |
if w in vars[v]['depend']: | |
fl = 1 | |
break | |
if fl: | |
dep = dep[1:] + [v] | |
i = i + 1 | |
if i > n: | |
errmess('sortvarnames: failed to compute dependencies because' | |
' of cyclic dependencies between ' | |
+ ', '.join(dep) + '\n') | |
indep = indep + dep | |
break | |
else: | |
indep.append(v) | |
dep = dep[1:] | |
n = len(dep) | |
i = 0 | |
return indep | |
def analyzecommon(block): | |
if not hascommon(block): | |
return block | |
commonvars = [] | |
for k in list(block['common'].keys()): | |
comvars = [] | |
for e in block['common'][k]: | |
m = re.match( | |
r'\A\s*\b(?P<name>.*?)\b\s*(\((?P<dims>.*?)\)|)\s*\Z', e, re.I) | |
if m: | |
dims = [] | |
if m.group('dims'): | |
dims = [x.strip() | |
for x in markoutercomma(m.group('dims')).split('@,@')] | |
n = rmbadname1(m.group('name').strip()) | |
if n in block['vars']: | |
if 'attrspec' in block['vars'][n]: | |
block['vars'][n]['attrspec'].append( | |
'dimension(%s)' % (','.join(dims))) | |
else: | |
block['vars'][n]['attrspec'] = [ | |
'dimension(%s)' % (','.join(dims))] | |
else: | |
if dims: | |
block['vars'][n] = { | |
'attrspec': ['dimension(%s)' % (','.join(dims))]} | |
else: | |
block['vars'][n] = {} | |
if n not in commonvars: | |
commonvars.append(n) | |
else: | |
n = e | |
errmess( | |
'analyzecommon: failed to extract "<name>[(<dims>)]" from "%s" in common /%s/.\n' % (e, k)) | |
comvars.append(n) | |
block['common'][k] = comvars | |
if 'commonvars' not in block: | |
block['commonvars'] = commonvars | |
else: | |
block['commonvars'] = block['commonvars'] + commonvars | |
return block | |
def analyzebody(block, args, tab=''): | |
global usermodules, skipfuncs, onlyfuncs, f90modulevars | |
setmesstext(block) | |
body = [] | |
for b in block['body']: | |
b['parent_block'] = block | |
if b['block'] in ['function', 'subroutine']: | |
if args is not None and b['name'] not in args: | |
continue | |
else: | |
as_ = b['args'] | |
if b['name'] in skipfuncs: | |
continue | |
if onlyfuncs and b['name'] not in onlyfuncs: | |
continue | |
b['saved_interface'] = crack2fortrangen( | |
b, '\n' + ' ' * 6, as_interface=True) | |
else: | |
as_ = args | |
b = postcrack(b, as_, tab=tab + '\t') | |
if b['block'] in ['interface', 'abstract interface'] and not b['body']: | |
if 'f2pyenhancements' not in b: | |
continue | |
if b['block'].replace(' ', '') == 'pythonmodule': | |
usermodules.append(b) | |
else: | |
if b['block'] == 'module': | |
f90modulevars[b['name']] = b['vars'] | |
body.append(b) | |
return body | |
def buildimplicitrules(block): | |
setmesstext(block) | |
implicitrules = defaultimplicitrules | |
attrrules = {} | |
if 'implicit' in block: | |
if block['implicit'] is None: | |
implicitrules = None | |
if verbose > 1: | |
outmess( | |
'buildimplicitrules: no implicit rules for routine %s.\n' % repr(block['name'])) | |
else: | |
for k in list(block['implicit'].keys()): | |
if block['implicit'][k].get('typespec') not in ['static', 'automatic']: | |
implicitrules[k] = block['implicit'][k] | |
else: | |
attrrules[k] = block['implicit'][k]['typespec'] | |
return implicitrules, attrrules | |
def myeval(e, g=None, l=None): | |
""" Like `eval` but returns only integers and floats """ | |
r = eval(e, g, l) | |
if type(r) in [int, float]: | |
return r | |
raise ValueError('r=%r' % (r)) | |
getlincoef_re_1 = re.compile(r'\A\b\w+\b\Z', re.I) | |
def getlincoef(e, xset): # e = a*x+b ; x in xset | |
""" | |
Obtain ``a`` and ``b`` when ``e == "a*x+b"``, where ``x`` is a symbol in | |
xset. | |
>>> getlincoef('2*x + 1', {'x'}) | |
(2, 1, 'x') | |
>>> getlincoef('3*x + x*2 + 2 + 1', {'x'}) | |
(5, 3, 'x') | |
>>> getlincoef('0', {'x'}) | |
(0, 0, None) | |
>>> getlincoef('0*x', {'x'}) | |
(0, 0, 'x') | |
>>> getlincoef('x*x', {'x'}) | |
(None, None, None) | |
This can be tricked by sufficiently complex expressions | |
>>> getlincoef('(x - 0.5)*(x - 1.5)*(x - 1)*x + 2*x + 3', {'x'}) | |
(2.0, 3.0, 'x') | |
""" | |
try: | |
c = int(myeval(e, {}, {})) | |
return 0, c, None | |
except Exception: | |
pass | |
if getlincoef_re_1.match(e): | |
return 1, 0, e | |
len_e = len(e) | |
for x in xset: | |
if len(x) > len_e: | |
continue | |
if re.search(r'\w\s*\([^)]*\b' + x + r'\b', e): | |
# skip function calls having x as an argument, e.g max(1, x) | |
continue | |
re_1 = re.compile(r'(?P<before>.*?)\b' + x + r'\b(?P<after>.*)', re.I) | |
m = re_1.match(e) | |
if m: | |
try: | |
m1 = re_1.match(e) | |
while m1: | |
ee = '%s(%s)%s' % ( | |
m1.group('before'), 0, m1.group('after')) | |
m1 = re_1.match(ee) | |
b = myeval(ee, {}, {}) | |
m1 = re_1.match(e) | |
while m1: | |
ee = '%s(%s)%s' % ( | |
m1.group('before'), 1, m1.group('after')) | |
m1 = re_1.match(ee) | |
a = myeval(ee, {}, {}) - b | |
m1 = re_1.match(e) | |
while m1: | |
ee = '%s(%s)%s' % ( | |
m1.group('before'), 0.5, m1.group('after')) | |
m1 = re_1.match(ee) | |
c = myeval(ee, {}, {}) | |
# computing another point to be sure that expression is linear | |
m1 = re_1.match(e) | |
while m1: | |
ee = '%s(%s)%s' % ( | |
m1.group('before'), 1.5, m1.group('after')) | |
m1 = re_1.match(ee) | |
c2 = myeval(ee, {}, {}) | |
if (a * 0.5 + b == c and a * 1.5 + b == c2): | |
return a, b, x | |
except Exception: | |
pass | |
break | |
return None, None, None | |
_varname_match = re.compile(r'\A[a-z]\w*\Z').match | |
def getarrlen(dl, args, star='*'): | |
""" | |
Parameters | |
---------- | |
dl : sequence of two str objects | |
dimensions of the array | |
args : Iterable[str] | |
symbols used in the expression | |
star : Any | |
unused | |
Returns | |
------- | |
expr : str | |
Some numeric expression as a string | |
arg : Optional[str] | |
If understood, the argument from `args` present in `expr` | |
expr2 : Optional[str] | |
If understood, an expression fragment that should be used as | |
``"(%s%s".format(something, expr2)``. | |
Examples | |
-------- | |
>>> getarrlen(['10*x + 20', '40*x'], {'x'}) | |
('30 * x - 19', 'x', '+19)/(30)') | |
>>> getarrlen(['1', '10*x + 20'], {'x'}) | |
('10 * x + 20', 'x', '-20)/(10)') | |
>>> getarrlen(['10*x + 20', '1'], {'x'}) | |
('-10 * x - 18', 'x', '+18)/(-10)') | |
>>> getarrlen(['20', '1'], {'x'}) | |
('-18', None, None) | |
""" | |
edl = [] | |
try: | |
edl.append(myeval(dl[0], {}, {})) | |
except Exception: | |
edl.append(dl[0]) | |
try: | |
edl.append(myeval(dl[1], {}, {})) | |
except Exception: | |
edl.append(dl[1]) | |
if isinstance(edl[0], int): | |
p1 = 1 - edl[0] | |
if p1 == 0: | |
d = str(dl[1]) | |
elif p1 < 0: | |
d = '%s-%s' % (dl[1], -p1) | |
else: | |
d = '%s+%s' % (dl[1], p1) | |
elif isinstance(edl[1], int): | |
p1 = 1 + edl[1] | |
if p1 == 0: | |
d = '-(%s)' % (dl[0]) | |
else: | |
d = '%s-(%s)' % (p1, dl[0]) | |
else: | |
d = '%s-(%s)+1' % (dl[1], dl[0]) | |
try: | |
return repr(myeval(d, {}, {})), None, None | |
except Exception: | |
pass | |
d1, d2 = getlincoef(dl[0], args), getlincoef(dl[1], args) | |
if None not in [d1[0], d2[0]]: | |
if (d1[0], d2[0]) == (0, 0): | |
return repr(d2[1] - d1[1] + 1), None, None | |
b = d2[1] - d1[1] + 1 | |
d1 = (d1[0], 0, d1[2]) | |
d2 = (d2[0], b, d2[2]) | |
if d1[0] == 0 and d2[2] in args: | |
if b < 0: | |
return '%s * %s - %s' % (d2[0], d2[2], -b), d2[2], '+%s)/(%s)' % (-b, d2[0]) | |
elif b: | |
return '%s * %s + %s' % (d2[0], d2[2], b), d2[2], '-%s)/(%s)' % (b, d2[0]) | |
else: | |
return '%s * %s' % (d2[0], d2[2]), d2[2], ')/(%s)' % (d2[0]) | |
if d2[0] == 0 and d1[2] in args: | |
if b < 0: | |
return '%s * %s - %s' % (-d1[0], d1[2], -b), d1[2], '+%s)/(%s)' % (-b, -d1[0]) | |
elif b: | |
return '%s * %s + %s' % (-d1[0], d1[2], b), d1[2], '-%s)/(%s)' % (b, -d1[0]) | |
else: | |
return '%s * %s' % (-d1[0], d1[2]), d1[2], ')/(%s)' % (-d1[0]) | |
if d1[2] == d2[2] and d1[2] in args: | |
a = d2[0] - d1[0] | |
if not a: | |
return repr(b), None, None | |
if b < 0: | |
return '%s * %s - %s' % (a, d1[2], -b), d2[2], '+%s)/(%s)' % (-b, a) | |
elif b: | |
return '%s * %s + %s' % (a, d1[2], b), d2[2], '-%s)/(%s)' % (b, a) | |
else: | |
return '%s * %s' % (a, d1[2]), d2[2], ')/(%s)' % (a) | |
if d1[0] == d2[0] == 1: | |
c = str(d1[2]) | |
if c not in args: | |
if _varname_match(c): | |
outmess('\tgetarrlen:variable "%s" undefined\n' % (c)) | |
c = '(%s)' % c | |
if b == 0: | |
d = '%s-%s' % (d2[2], c) | |
elif b < 0: | |
d = '%s-%s-%s' % (d2[2], c, -b) | |
else: | |
d = '%s-%s+%s' % (d2[2], c, b) | |
elif d1[0] == 0: | |
c2 = str(d2[2]) | |
if c2 not in args: | |
if _varname_match(c2): | |
outmess('\tgetarrlen:variable "%s" undefined\n' % (c2)) | |
c2 = '(%s)' % c2 | |
if d2[0] == 1: | |
pass | |
elif d2[0] == -1: | |
c2 = '-%s' % c2 | |
else: | |
c2 = '%s*%s' % (d2[0], c2) | |
if b == 0: | |
d = c2 | |
elif b < 0: | |
d = '%s-%s' % (c2, -b) | |
else: | |
d = '%s+%s' % (c2, b) | |
elif d2[0] == 0: | |
c1 = str(d1[2]) | |
if c1 not in args: | |
if _varname_match(c1): | |
outmess('\tgetarrlen:variable "%s" undefined\n' % (c1)) | |
c1 = '(%s)' % c1 | |
if d1[0] == 1: | |
c1 = '-%s' % c1 | |
elif d1[0] == -1: | |
c1 = '+%s' % c1 | |
elif d1[0] < 0: | |
c1 = '+%s*%s' % (-d1[0], c1) | |
else: | |
c1 = '-%s*%s' % (d1[0], c1) | |
if b == 0: | |
d = c1 | |
elif b < 0: | |
d = '%s-%s' % (c1, -b) | |
else: | |
d = '%s+%s' % (c1, b) | |
else: | |
c1 = str(d1[2]) | |
if c1 not in args: | |
if _varname_match(c1): | |
outmess('\tgetarrlen:variable "%s" undefined\n' % (c1)) | |
c1 = '(%s)' % c1 | |
if d1[0] == 1: | |
c1 = '-%s' % c1 | |
elif d1[0] == -1: | |
c1 = '+%s' % c1 | |
elif d1[0] < 0: | |
c1 = '+%s*%s' % (-d1[0], c1) | |
else: | |
c1 = '-%s*%s' % (d1[0], c1) | |
c2 = str(d2[2]) | |
if c2 not in args: | |
if _varname_match(c2): | |
outmess('\tgetarrlen:variable "%s" undefined\n' % (c2)) | |
c2 = '(%s)' % c2 | |
if d2[0] == 1: | |
pass | |
elif d2[0] == -1: | |
c2 = '-%s' % c2 | |
else: | |
c2 = '%s*%s' % (d2[0], c2) | |
if b == 0: | |
d = '%s%s' % (c2, c1) | |
elif b < 0: | |
d = '%s%s-%s' % (c2, c1, -b) | |
else: | |
d = '%s%s+%s' % (c2, c1, b) | |
return d, None, None | |
word_pattern = re.compile(r'\b[a-z][\w$]*\b', re.I) | |
def _get_depend_dict(name, vars, deps): | |
if name in vars: | |
words = vars[name].get('depend', []) | |
if '=' in vars[name] and not isstring(vars[name]): | |
for word in word_pattern.findall(vars[name]['=']): | |
if word not in words and word in vars: | |
words.append(word) | |
for word in words[:]: | |
for w in deps.get(word, []) \ | |
or _get_depend_dict(word, vars, deps): | |
if w not in words: | |
words.append(w) | |
else: | |
outmess('_get_depend_dict: no dependence info for %s\n' % (repr(name))) | |
words = [] | |
deps[name] = words | |
return words | |
def _calc_depend_dict(vars): | |
names = list(vars.keys()) | |
depend_dict = {} | |
for n in names: | |
_get_depend_dict(n, vars, depend_dict) | |
return depend_dict | |
def get_sorted_names(vars): | |
""" | |
""" | |
depend_dict = _calc_depend_dict(vars) | |
names = [] | |
for name in list(depend_dict.keys()): | |
if not depend_dict[name]: | |
names.append(name) | |
del depend_dict[name] | |
while depend_dict: | |
for name, lst in list(depend_dict.items()): | |
new_lst = [n for n in lst if n in depend_dict] | |
if not new_lst: | |
names.append(name) | |
del depend_dict[name] | |
else: | |
depend_dict[name] = new_lst | |
return [name for name in names if name in vars] | |
def _kind_func(string): | |
# XXX: return something sensible. | |
if string[0] in "'\"": | |
string = string[1:-1] | |
if real16pattern.match(string): | |
return 8 | |
elif real8pattern.match(string): | |
return 4 | |
return 'kind(' + string + ')' | |
def _selected_int_kind_func(r): | |
# XXX: This should be processor dependent | |
m = 10 ** r | |
if m <= 2 ** 8: | |
return 1 | |
if m <= 2 ** 16: | |
return 2 | |
if m <= 2 ** 32: | |
return 4 | |
if m <= 2 ** 63: | |
return 8 | |
if m <= 2 ** 128: | |
return 16 | |
return -1 | |
def _selected_real_kind_func(p, r=0, radix=0): | |
# XXX: This should be processor dependent | |
# This is only good for 0 <= p <= 20 | |
if p < 7: | |
return 4 | |
if p < 16: | |
return 8 | |
machine = platform.machine().lower() | |
if machine.startswith(('aarch64', 'power', 'ppc', 'riscv', 's390x', 'sparc')): | |
if p <= 20: | |
return 16 | |
else: | |
if p < 19: | |
return 10 | |
elif p <= 20: | |
return 16 | |
return -1 | |
def get_parameters(vars, global_params={}): | |
params = copy.copy(global_params) | |
g_params = copy.copy(global_params) | |
for name, func in [('kind', _kind_func), | |
('selected_int_kind', _selected_int_kind_func), | |
('selected_real_kind', _selected_real_kind_func), ]: | |
if name not in g_params: | |
g_params[name] = func | |
param_names = [] | |
for n in get_sorted_names(vars): | |
if 'attrspec' in vars[n] and 'parameter' in vars[n]['attrspec']: | |
param_names.append(n) | |
kind_re = re.compile(r'\bkind\s*\(\s*(?P<value>.*)\s*\)', re.I) | |
selected_int_kind_re = re.compile( | |
r'\bselected_int_kind\s*\(\s*(?P<value>.*)\s*\)', re.I) | |
selected_kind_re = re.compile( | |
r'\bselected_(int|real)_kind\s*\(\s*(?P<value>.*)\s*\)', re.I) | |
for n in param_names: | |
if '=' in vars[n]: | |
v = vars[n]['='] | |
if islogical(vars[n]): | |
v = v.lower() | |
for repl in [ | |
('.false.', 'False'), | |
('.true.', 'True'), | |
# TODO: test .eq., .neq., etc replacements. | |
]: | |
v = v.replace(*repl) | |
v = kind_re.sub(r'kind("\1")', v) | |
v = selected_int_kind_re.sub(r'selected_int_kind(\1)', v) | |
# We need to act according to the data. | |
# The easy case is if the data has a kind-specifier, | |
# then we may easily remove those specifiers. | |
# However, it may be that the user uses other specifiers...(!) | |
is_replaced = False | |
if 'kindselector' in vars[n]: | |
if 'kind' in vars[n]['kindselector']: | |
orig_v_len = len(v) | |
v = v.replace('_' + vars[n]['kindselector']['kind'], '') | |
# Again, this will be true if even a single specifier | |
# has been replaced, see comment above. | |
is_replaced = len(v) < orig_v_len | |
if not is_replaced: | |
if not selected_kind_re.match(v): | |
v_ = v.split('_') | |
# In case there are additive parameters | |
if len(v_) > 1: | |
v = ''.join(v_[:-1]).lower().replace(v_[-1].lower(), '') | |
# Currently this will not work for complex numbers. | |
# There is missing code for extracting a complex number, | |
# which may be defined in either of these: | |
# a) (Re, Im) | |
# b) cmplx(Re, Im) | |
# c) dcmplx(Re, Im) | |
# d) cmplx(Re, Im, <prec>) | |
if isdouble(vars[n]): | |
tt = list(v) | |
for m in real16pattern.finditer(v): | |
tt[m.start():m.end()] = list( | |
v[m.start():m.end()].lower().replace('d', 'e')) | |
v = ''.join(tt) | |
elif iscomplex(vars[n]): | |
# FIXME complex numbers may also have exponents | |
if v[0] == '(' and v[-1] == ')': | |
# FIXME, unused l looks like potential bug | |
l = markoutercomma(v[1:-1]).split('@,@') | |
try: | |
params[n] = eval(v, g_params, params) | |
except Exception as msg: | |
params[n] = v | |
outmess('get_parameters: got "%s" on %s\n' % (msg, repr(v))) | |
if isstring(vars[n]) and isinstance(params[n], int): | |
params[n] = chr(params[n]) | |
nl = n.lower() | |
if nl != n: | |
params[nl] = params[n] | |
else: | |
print(vars[n]) | |
outmess( | |
'get_parameters:parameter %s does not have value?!\n' % (repr(n))) | |
return params | |
def _eval_length(length, params): | |
if length in ['(:)', '(*)', '*']: | |
return '(*)' | |
return _eval_scalar(length, params) | |
_is_kind_number = re.compile(r'\d+_').match | |
def _eval_scalar(value, params): | |
if _is_kind_number(value): | |
value = value.split('_')[0] | |
try: | |
value = str(eval(value, {}, params)) | |
except (NameError, SyntaxError, TypeError): | |
return value | |
except Exception as msg: | |
errmess('"%s" in evaluating %r ' | |
'(available names: %s)\n' | |
% (msg, value, list(params.keys()))) | |
return value | |
def analyzevars(block): | |
global f90modulevars | |
setmesstext(block) | |
implicitrules, attrrules = buildimplicitrules(block) | |
vars = copy.copy(block['vars']) | |
if block['block'] == 'function' and block['name'] not in vars: | |
vars[block['name']] = {} | |
if '' in block['vars']: | |
del vars[''] | |
if 'attrspec' in block['vars']['']: | |
gen = block['vars']['']['attrspec'] | |
for n in list(vars.keys()): | |
for k in ['public', 'private']: | |
if k in gen: | |
vars[n] = setattrspec(vars[n], k) | |
svars = [] | |
args = block['args'] | |
for a in args: | |
try: | |
vars[a] | |
svars.append(a) | |
except KeyError: | |
pass | |
for n in list(vars.keys()): | |
if n not in args: | |
svars.append(n) | |
params = get_parameters(vars, get_useparameters(block)) | |
dep_matches = {} | |
name_match = re.compile(r'[A-Za-z][\w$]*').match | |
for v in list(vars.keys()): | |
m = name_match(v) | |
if m: | |
n = v[m.start():m.end()] | |
try: | |
dep_matches[n] | |
except KeyError: | |
dep_matches[n] = re.compile(r'.*\b%s\b' % (v), re.I).match | |
for n in svars: | |
if n[0] in list(attrrules.keys()): | |
vars[n] = setattrspec(vars[n], attrrules[n[0]]) | |
if 'typespec' not in vars[n]: | |
if not('attrspec' in vars[n] and 'external' in vars[n]['attrspec']): | |
if implicitrules: | |
ln0 = n[0].lower() | |
for k in list(implicitrules[ln0].keys()): | |
if k == 'typespec' and implicitrules[ln0][k] == 'undefined': | |
continue | |
if k not in vars[n]: | |
vars[n][k] = implicitrules[ln0][k] | |
elif k == 'attrspec': | |
for l in implicitrules[ln0][k]: | |
vars[n] = setattrspec(vars[n], l) | |
elif n in block['args']: | |
outmess('analyzevars: typespec of variable %s is not defined in routine %s.\n' % ( | |
repr(n), block['name'])) | |
if 'charselector' in vars[n]: | |
if 'len' in vars[n]['charselector']: | |
l = vars[n]['charselector']['len'] | |
try: | |
l = str(eval(l, {}, params)) | |
except Exception: | |
pass | |
vars[n]['charselector']['len'] = l | |
if 'kindselector' in vars[n]: | |
if 'kind' in vars[n]['kindselector']: | |
l = vars[n]['kindselector']['kind'] | |
try: | |
l = str(eval(l, {}, params)) | |
except Exception: | |
pass | |
vars[n]['kindselector']['kind'] = l | |
savelindims = {} | |
if 'attrspec' in vars[n]: | |
attr = vars[n]['attrspec'] | |
attr.reverse() | |
vars[n]['attrspec'] = [] | |
dim, intent, depend, check, note = None, None, None, None, None | |
for a in attr: | |
if a[:9] == 'dimension': | |
dim = (a[9:].strip())[1:-1] | |
elif a[:6] == 'intent': | |
intent = (a[6:].strip())[1:-1] | |
elif a[:6] == 'depend': | |
depend = (a[6:].strip())[1:-1] | |
elif a[:5] == 'check': | |
check = (a[5:].strip())[1:-1] | |
elif a[:4] == 'note': | |
note = (a[4:].strip())[1:-1] | |
else: | |
vars[n] = setattrspec(vars[n], a) | |
if intent: | |
if 'intent' not in vars[n]: | |
vars[n]['intent'] = [] | |
for c in [x.strip() for x in markoutercomma(intent).split('@,@')]: | |
# Remove spaces so that 'in out' becomes 'inout' | |
tmp = c.replace(' ', '') | |
if tmp not in vars[n]['intent']: | |
vars[n]['intent'].append(tmp) | |
intent = None | |
if note: | |
note = note.replace('\\n\\n', '\n\n') | |
note = note.replace('\\n ', '\n') | |
if 'note' not in vars[n]: | |
vars[n]['note'] = [note] | |
else: | |
vars[n]['note'].append(note) | |
note = None | |
if depend is not None: | |
if 'depend' not in vars[n]: | |
vars[n]['depend'] = [] | |
for c in rmbadname([x.strip() for x in markoutercomma(depend).split('@,@')]): | |
if c not in vars[n]['depend']: | |
vars[n]['depend'].append(c) | |
depend = None | |
if check is not None: | |
if 'check' not in vars[n]: | |
vars[n]['check'] = [] | |
for c in [x.strip() for x in markoutercomma(check).split('@,@')]: | |
if c not in vars[n]['check']: | |
vars[n]['check'].append(c) | |
check = None | |
if dim and 'dimension' not in vars[n]: | |
vars[n]['dimension'] = [] | |
for d in rmbadname([x.strip() for x in markoutercomma(dim).split('@,@')]): | |
star = '*' | |
if d == ':': | |
star = ':' | |
if d in params: | |
d = str(params[d]) | |
for p in list(params.keys()): | |
re_1 = re.compile(r'(?P<before>.*?)\b' + p + r'\b(?P<after>.*)', re.I) | |
m = re_1.match(d) | |
while m: | |
d = m.group('before') + \ | |
str(params[p]) + m.group('after') | |
m = re_1.match(d) | |
if d == star: | |
dl = [star] | |
else: | |
dl = markoutercomma(d, ':').split('@:@') | |
if len(dl) == 2 and '*' in dl: # e.g. dimension(5:*) | |
dl = ['*'] | |
d = '*' | |
if len(dl) == 1 and not dl[0] == star: | |
dl = ['1', dl[0]] | |
if len(dl) == 2: | |
d, v, di = getarrlen(dl, list(block['vars'].keys())) | |
if d[:4] == '1 * ': | |
d = d[4:] | |
if di and di[-4:] == '/(1)': | |
di = di[:-4] | |
if v: | |
savelindims[d] = v, di | |
vars[n]['dimension'].append(d) | |
if 'dimension' in vars[n]: | |
if isintent_c(vars[n]): | |
shape_macro = 'shape' | |
else: | |
shape_macro = 'shape' # 'fshape' | |
if isstringarray(vars[n]): | |
if 'charselector' in vars[n]: | |
d = vars[n]['charselector'] | |
if '*' in d: | |
d = d['*'] | |
errmess('analyzevars: character array "character*%s %s(%s)" is considered as "character %s(%s)"; "intent(c)" is forced.\n' | |
% (d, n, | |
','.join(vars[n]['dimension']), | |
n, ','.join(vars[n]['dimension'] + [d]))) | |
vars[n]['dimension'].append(d) | |
del vars[n]['charselector'] | |
if 'intent' not in vars[n]: | |
vars[n]['intent'] = [] | |
if 'c' not in vars[n]['intent']: | |
vars[n]['intent'].append('c') | |
else: | |
errmess( | |
"analyzevars: charselector=%r unhandled." % (d)) | |
if 'check' not in vars[n] and 'args' in block and n in block['args']: | |
flag = 'depend' not in vars[n] | |
if flag: | |
vars[n]['depend'] = [] | |
vars[n]['check'] = [] | |
if 'dimension' in vars[n]: | |
#/----< no check | |
i = -1 | |
ni = len(vars[n]['dimension']) | |
for d in vars[n]['dimension']: | |
ddeps = [] # dependencies of 'd' | |
ad = '' | |
pd = '' | |
if d not in vars: | |
if d in savelindims: | |
pd, ad = '(', savelindims[d][1] | |
d = savelindims[d][0] | |
else: | |
for r in block['args']: | |
if r not in vars: | |
continue | |
if re.match(r'.*?\b' + r + r'\b', d, re.I): | |
ddeps.append(r) | |
if d in vars: | |
if 'attrspec' in vars[d]: | |
for aa in vars[d]['attrspec']: | |
if aa[:6] == 'depend': | |
ddeps += aa[6:].strip()[1:-1].split(',') | |
if 'depend' in vars[d]: | |
ddeps = ddeps + vars[d]['depend'] | |
i = i + 1 | |
if d in vars and ('depend' not in vars[d]) \ | |
and ('=' not in vars[d]) and (d not in vars[n]['depend']) \ | |
and l_or(isintent_in, isintent_inout, isintent_inplace)(vars[n]): | |
vars[d]['depend'] = [n] | |
if ni > 1: | |
vars[d]['='] = '%s%s(%s,%s)%s' % ( | |
pd, shape_macro, n, i, ad) | |
else: | |
vars[d]['='] = '%slen(%s)%s' % (pd, n, ad) | |
# /---< no check | |
if 1 and 'check' not in vars[d]: | |
if ni > 1: | |
vars[d]['check'] = ['%s%s(%s,%i)%s==%s' | |
% (pd, shape_macro, n, i, ad, d)] | |
else: | |
vars[d]['check'] = [ | |
'%slen(%s)%s>=%s' % (pd, n, ad, d)] | |
if 'attrspec' not in vars[d]: | |
vars[d]['attrspec'] = ['optional'] | |
if ('optional' not in vars[d]['attrspec']) and\ | |
('required' not in vars[d]['attrspec']): | |
vars[d]['attrspec'].append('optional') | |
elif d not in ['*', ':']: | |
#/----< no check | |
if flag: | |
if d in vars: | |
if n not in ddeps: | |
vars[n]['depend'].append(d) | |
else: | |
vars[n]['depend'] = vars[n]['depend'] + ddeps | |
elif isstring(vars[n]): | |
length = '1' | |
if 'charselector' in vars[n]: | |
if '*' in vars[n]['charselector']: | |
length = _eval_length(vars[n]['charselector']['*'], | |
params) | |
vars[n]['charselector']['*'] = length | |
elif 'len' in vars[n]['charselector']: | |
length = _eval_length(vars[n]['charselector']['len'], | |
params) | |
del vars[n]['charselector']['len'] | |
vars[n]['charselector']['*'] = length | |
if not vars[n]['check']: | |
del vars[n]['check'] | |
if flag and not vars[n]['depend']: | |
del vars[n]['depend'] | |
if '=' in vars[n]: | |
if 'attrspec' not in vars[n]: | |
vars[n]['attrspec'] = [] | |
if ('optional' not in vars[n]['attrspec']) and \ | |
('required' not in vars[n]['attrspec']): | |
vars[n]['attrspec'].append('optional') | |
if 'depend' not in vars[n]: | |
vars[n]['depend'] = [] | |
for v, m in list(dep_matches.items()): | |
if m(vars[n]['=']): | |
vars[n]['depend'].append(v) | |
if not vars[n]['depend']: | |
del vars[n]['depend'] | |
if isscalar(vars[n]): | |
vars[n]['='] = _eval_scalar(vars[n]['='], params) | |
for n in list(vars.keys()): | |
if n == block['name']: # n is block name | |
if 'note' in vars[n]: | |
block['note'] = vars[n]['note'] | |
if block['block'] == 'function': | |
if 'result' in block and block['result'] in vars: | |
vars[n] = appenddecl(vars[n], vars[block['result']]) | |
if 'prefix' in block: | |
pr = block['prefix'] | |
ispure = 0 | |
isrec = 1 | |
pr1 = pr.replace('pure', '') | |
ispure = (not pr == pr1) | |
pr = pr1.replace('recursive', '') | |
isrec = (not pr == pr1) | |
m = typespattern[0].match(pr) | |
if m: | |
typespec, selector, attr, edecl = cracktypespec0( | |
m.group('this'), m.group('after')) | |
kindselect, charselect, typename = cracktypespec( | |
typespec, selector) | |
vars[n]['typespec'] = typespec | |
if kindselect: | |
if 'kind' in kindselect: | |
try: | |
kindselect['kind'] = eval( | |
kindselect['kind'], {}, params) | |
except Exception: | |
pass | |
vars[n]['kindselector'] = kindselect | |
if charselect: | |
vars[n]['charselector'] = charselect | |
if typename: | |
vars[n]['typename'] = typename | |
if ispure: | |
vars[n] = setattrspec(vars[n], 'pure') | |
if isrec: | |
vars[n] = setattrspec(vars[n], 'recursive') | |
else: | |
outmess( | |
'analyzevars: prefix (%s) were not used\n' % repr(block['prefix'])) | |
if not block['block'] in ['module', 'pythonmodule', 'python module', 'block data']: | |
if 'commonvars' in block: | |
neededvars = copy.copy(block['args'] + block['commonvars']) | |
else: | |
neededvars = copy.copy(block['args']) | |
for n in list(vars.keys()): | |
if l_or(isintent_callback, isintent_aux)(vars[n]): | |
neededvars.append(n) | |
if 'entry' in block: | |
neededvars.extend(list(block['entry'].keys())) | |
for k in list(block['entry'].keys()): | |
for n in block['entry'][k]: | |
if n not in neededvars: | |
neededvars.append(n) | |
if block['block'] == 'function': | |
if 'result' in block: | |
neededvars.append(block['result']) | |
else: | |
neededvars.append(block['name']) | |
if block['block'] in ['subroutine', 'function']: | |
name = block['name'] | |
if name in vars and 'intent' in vars[name]: | |
block['intent'] = vars[name]['intent'] | |
if block['block'] == 'type': | |
neededvars.extend(list(vars.keys())) | |
for n in list(vars.keys()): | |
if n not in neededvars: | |
del vars[n] | |
return vars | |
analyzeargs_re_1 = re.compile(r'\A[a-z]+[\w$]*\Z', re.I) | |
def expr2name(a, block, args=[]): | |
orig_a = a | |
a_is_expr = not analyzeargs_re_1.match(a) | |
if a_is_expr: # `a` is an expression | |
implicitrules, attrrules = buildimplicitrules(block) | |
at = determineexprtype(a, block['vars'], implicitrules) | |
na = 'e_' | |
for c in a: | |
c = c.lower() | |
if c not in string.ascii_lowercase + string.digits: | |
c = '_' | |
na = na + c | |
if na[-1] == '_': | |
na = na + 'e' | |
else: | |
na = na + '_e' | |
a = na | |
while a in block['vars'] or a in block['args']: | |
a = a + 'r' | |
if a in args: | |
k = 1 | |
while a + str(k) in args: | |
k = k + 1 | |
a = a + str(k) | |
if a_is_expr: | |
block['vars'][a] = at | |
else: | |
if a not in block['vars']: | |
if orig_a in block['vars']: | |
block['vars'][a] = block['vars'][orig_a] | |
else: | |
block['vars'][a] = {} | |
if 'externals' in block and orig_a in block['externals'] + block['interfaced']: | |
block['vars'][a] = setattrspec(block['vars'][a], 'external') | |
return a | |
def analyzeargs(block): | |
setmesstext(block) | |
implicitrules, attrrules = buildimplicitrules(block) | |
if 'args' not in block: | |
block['args'] = [] | |
args = [] | |
for a in block['args']: | |
a = expr2name(a, block, args) | |
args.append(a) | |
block['args'] = args | |
if 'entry' in block: | |
for k, args1 in list(block['entry'].items()): | |
for a in args1: | |
if a not in block['vars']: | |
block['vars'][a] = {} | |
for b in block['body']: | |
if b['name'] in args: | |
if 'externals' not in block: | |
block['externals'] = [] | |
if b['name'] not in block['externals']: | |
block['externals'].append(b['name']) | |
if 'result' in block and block['result'] not in block['vars']: | |
block['vars'][block['result']] = {} | |
return block | |
determineexprtype_re_1 = re.compile(r'\A\(.+?,.+?\)\Z', re.I) | |
determineexprtype_re_2 = re.compile(r'\A[+-]?\d+(_(?P<name>\w+)|)\Z', re.I) | |
determineexprtype_re_3 = re.compile( | |
r'\A[+-]?[\d.]+[-\d+de.]*(_(?P<name>\w+)|)\Z', re.I) | |
determineexprtype_re_4 = re.compile(r'\A\(.*\)\Z', re.I) | |
determineexprtype_re_5 = re.compile(r'\A(?P<name>\w+)\s*\(.*?\)\s*\Z', re.I) | |
def _ensure_exprdict(r): | |
if isinstance(r, int): | |
return {'typespec': 'integer'} | |
if isinstance(r, float): | |
return {'typespec': 'real'} | |
if isinstance(r, complex): | |
return {'typespec': 'complex'} | |
if isinstance(r, dict): | |
return r | |
raise AssertionError(repr(r)) | |
def determineexprtype(expr, vars, rules={}): | |
if expr in vars: | |
return _ensure_exprdict(vars[expr]) | |
expr = expr.strip() | |
if determineexprtype_re_1.match(expr): | |
return {'typespec': 'complex'} | |
m = determineexprtype_re_2.match(expr) | |
if m: | |
if 'name' in m.groupdict() and m.group('name'): | |
outmess( | |
'determineexprtype: selected kind types not supported (%s)\n' % repr(expr)) | |
return {'typespec': 'integer'} | |
m = determineexprtype_re_3.match(expr) | |
if m: | |
if 'name' in m.groupdict() and m.group('name'): | |
outmess( | |
'determineexprtype: selected kind types not supported (%s)\n' % repr(expr)) | |
return {'typespec': 'real'} | |
for op in ['+', '-', '*', '/']: | |
for e in [x.strip() for x in markoutercomma(expr, comma=op).split('@' + op + '@')]: | |
if e in vars: | |
return _ensure_exprdict(vars[e]) | |
t = {} | |
if determineexprtype_re_4.match(expr): # in parenthesis | |
t = determineexprtype(expr[1:-1], vars, rules) | |
else: | |
m = determineexprtype_re_5.match(expr) | |
if m: | |
rn = m.group('name') | |
t = determineexprtype(m.group('name'), vars, rules) | |
if t and 'attrspec' in t: | |
del t['attrspec'] | |
if not t: | |
if rn[0] in rules: | |
return _ensure_exprdict(rules[rn[0]]) | |
if expr[0] in '\'"': | |
return {'typespec': 'character', 'charselector': {'*': '*'}} | |
if not t: | |
outmess( | |
'determineexprtype: could not determine expressions (%s) type.\n' % (repr(expr))) | |
return t | |
###### | |
def crack2fortrangen(block, tab='\n', as_interface=False): | |
global skipfuncs, onlyfuncs | |
setmesstext(block) | |
ret = '' | |
if isinstance(block, list): | |
for g in block: | |
if g and g['block'] in ['function', 'subroutine']: | |
if g['name'] in skipfuncs: | |
continue | |
if onlyfuncs and g['name'] not in onlyfuncs: | |
continue | |
ret = ret + crack2fortrangen(g, tab, as_interface=as_interface) | |
return ret | |
prefix = '' | |
name = '' | |
args = '' | |
blocktype = block['block'] | |
if blocktype == 'program': | |
return '' | |
argsl = [] | |
if 'name' in block: | |
name = block['name'] | |
if 'args' in block: | |
vars = block['vars'] | |
for a in block['args']: | |
a = expr2name(a, block, argsl) | |
if not isintent_callback(vars[a]): | |
argsl.append(a) | |
if block['block'] == 'function' or argsl: | |
args = '(%s)' % ','.join(argsl) | |
f2pyenhancements = '' | |
if 'f2pyenhancements' in block: | |
for k in list(block['f2pyenhancements'].keys()): | |
f2pyenhancements = '%s%s%s %s' % ( | |
f2pyenhancements, tab + tabchar, k, block['f2pyenhancements'][k]) | |
intent_lst = block.get('intent', [])[:] | |
if blocktype == 'function' and 'callback' in intent_lst: | |
intent_lst.remove('callback') | |
if intent_lst: | |
f2pyenhancements = '%s%sintent(%s) %s' %\ | |
(f2pyenhancements, tab + tabchar, | |
','.join(intent_lst), name) | |
use = '' | |
if 'use' in block: | |
use = use2fortran(block['use'], tab + tabchar) | |
common = '' | |
if 'common' in block: | |
common = common2fortran(block['common'], tab + tabchar) | |
if name == 'unknown_interface': | |
name = '' | |
result = '' | |
if 'result' in block: | |
result = ' result (%s)' % block['result'] | |
if block['result'] not in argsl: | |
argsl.append(block['result']) | |
body = crack2fortrangen(block['body'], tab + tabchar, as_interface=as_interface) | |
vars = vars2fortran( | |
block, block['vars'], argsl, tab + tabchar, as_interface=as_interface) | |
mess = '' | |
if 'from' in block and not as_interface: | |
mess = '! in %s' % block['from'] | |
if 'entry' in block: | |
entry_stmts = '' | |
for k, i in list(block['entry'].items()): | |
entry_stmts = '%s%sentry %s(%s)' \ | |
% (entry_stmts, tab + tabchar, k, ','.join(i)) | |
body = body + entry_stmts | |
if blocktype == 'block data' and name == '_BLOCK_DATA_': | |
name = '' | |
ret = '%s%s%s %s%s%s %s%s%s%s%s%s%send %s %s' % ( | |
tab, prefix, blocktype, name, args, result, mess, f2pyenhancements, use, vars, common, body, tab, blocktype, name) | |
return ret | |
def common2fortran(common, tab=''): | |
ret = '' | |
for k in list(common.keys()): | |
if k == '_BLNK_': | |
ret = '%s%scommon %s' % (ret, tab, ','.join(common[k])) | |
else: | |
ret = '%s%scommon /%s/ %s' % (ret, tab, k, ','.join(common[k])) | |
return ret | |
def use2fortran(use, tab=''): | |
ret = '' | |
for m in list(use.keys()): | |
ret = '%s%suse %s,' % (ret, tab, m) | |
if use[m] == {}: | |
if ret and ret[-1] == ',': | |
ret = ret[:-1] | |
continue | |
if 'only' in use[m] and use[m]['only']: | |
ret = '%s only:' % (ret) | |
if 'map' in use[m] and use[m]['map']: | |
c = ' ' | |
for k in list(use[m]['map'].keys()): | |
if k == use[m]['map'][k]: | |
ret = '%s%s%s' % (ret, c, k) | |
c = ',' | |
else: | |
ret = '%s%s%s=>%s' % (ret, c, k, use[m]['map'][k]) | |
c = ',' | |
if ret and ret[-1] == ',': | |
ret = ret[:-1] | |
return ret | |
def true_intent_list(var): | |
lst = var['intent'] | |
ret = [] | |
for intent in lst: | |
try: | |
f = globals()['isintent_%s' % intent] | |
except KeyError: | |
pass | |
else: | |
if f(var): | |
ret.append(intent) | |
return ret | |
def vars2fortran(block, vars, args, tab='', as_interface=False): | |
""" | |
TODO: | |
public sub | |
... | |
""" | |
setmesstext(block) | |
ret = '' | |
nout = [] | |
for a in args: | |
if a in block['vars']: | |
nout.append(a) | |
if 'commonvars' in block: | |
for a in block['commonvars']: | |
if a in vars: | |
if a not in nout: | |
nout.append(a) | |
else: | |
errmess( | |
'vars2fortran: Confused?!: "%s" is not defined in vars.\n' % a) | |
if 'varnames' in block: | |
nout.extend(block['varnames']) | |
if not as_interface: | |
for a in list(vars.keys()): | |
if a not in nout: | |
nout.append(a) | |
for a in nout: | |
if 'depend' in vars[a]: | |
for d in vars[a]['depend']: | |
if d in vars and 'depend' in vars[d] and a in vars[d]['depend']: | |
errmess( | |
'vars2fortran: Warning: cross-dependence between variables "%s" and "%s"\n' % (a, d)) | |
if 'externals' in block and a in block['externals']: | |
if isintent_callback(vars[a]): | |
ret = '%s%sintent(callback) %s' % (ret, tab, a) | |
ret = '%s%sexternal %s' % (ret, tab, a) | |
if isoptional(vars[a]): | |
ret = '%s%soptional %s' % (ret, tab, a) | |
if a in vars and 'typespec' not in vars[a]: | |
continue | |
cont = 1 | |
for b in block['body']: | |
if a == b['name'] and b['block'] == 'function': | |
cont = 0 | |
break | |
if cont: | |
continue | |
if a not in vars: | |
show(vars) | |
outmess('vars2fortran: No definition for argument "%s".\n' % a) | |
continue | |
if a == block['name']: | |
if block['block'] != 'function' or block.get('result'): | |
# 1) skip declaring a variable that name matches with | |
# subroutine name | |
# 2) skip declaring function when its type is | |
# declared via `result` construction | |
continue | |
if 'typespec' not in vars[a]: | |
if 'attrspec' in vars[a] and 'external' in vars[a]['attrspec']: | |
if a in args: | |
ret = '%s%sexternal %s' % (ret, tab, a) | |
continue | |
show(vars[a]) | |
outmess('vars2fortran: No typespec for argument "%s".\n' % a) | |
continue | |
vardef = vars[a]['typespec'] | |
if vardef == 'type' and 'typename' in vars[a]: | |
vardef = '%s(%s)' % (vardef, vars[a]['typename']) | |
selector = {} | |
if 'kindselector' in vars[a]: | |
selector = vars[a]['kindselector'] | |
elif 'charselector' in vars[a]: | |
selector = vars[a]['charselector'] | |
if '*' in selector: | |
if selector['*'] in ['*', ':']: | |
vardef = '%s*(%s)' % (vardef, selector['*']) | |
else: | |
vardef = '%s*%s' % (vardef, selector['*']) | |
else: | |
if 'len' in selector: | |
vardef = '%s(len=%s' % (vardef, selector['len']) | |
if 'kind' in selector: | |
vardef = '%s,kind=%s)' % (vardef, selector['kind']) | |
else: | |
vardef = '%s)' % (vardef) | |
elif 'kind' in selector: | |
vardef = '%s(kind=%s)' % (vardef, selector['kind']) | |
c = ' ' | |
if 'attrspec' in vars[a]: | |
attr = [l for l in vars[a]['attrspec'] | |
if l not in ['external']] | |
if attr: | |
vardef = '%s, %s' % (vardef, ','.join(attr)) | |
c = ',' | |
if 'dimension' in vars[a]: | |
vardef = '%s%sdimension(%s)' % ( | |
vardef, c, ','.join(vars[a]['dimension'])) | |
c = ',' | |
if 'intent' in vars[a]: | |
lst = true_intent_list(vars[a]) | |
if lst: | |
vardef = '%s%sintent(%s)' % (vardef, c, ','.join(lst)) | |
c = ',' | |
if 'check' in vars[a]: | |
vardef = '%s%scheck(%s)' % (vardef, c, ','.join(vars[a]['check'])) | |
c = ',' | |
if 'depend' in vars[a]: | |
vardef = '%s%sdepend(%s)' % ( | |
vardef, c, ','.join(vars[a]['depend'])) | |
c = ',' | |
if '=' in vars[a]: | |
v = vars[a]['='] | |
if vars[a]['typespec'] in ['complex', 'double complex']: | |
try: | |
v = eval(v) | |
v = '(%s,%s)' % (v.real, v.imag) | |
except Exception: | |
pass | |
vardef = '%s :: %s=%s' % (vardef, a, v) | |
else: | |
vardef = '%s :: %s' % (vardef, a) | |
ret = '%s%s%s' % (ret, tab, vardef) | |
return ret | |
###### | |
def crackfortran(files): | |
global usermodules | |
outmess('Reading fortran codes...\n', 0) | |
readfortrancode(files, crackline) | |
outmess('Post-processing...\n', 0) | |
usermodules = [] | |
postlist = postcrack(grouplist[0]) | |
outmess('Post-processing (stage 2)...\n', 0) | |
postlist = postcrack2(postlist) | |
return usermodules + postlist | |
def crack2fortran(block): | |
global f2py_version | |
pyf = crack2fortrangen(block) + '\n' | |
header = """! -*- f90 -*- | |
! Note: the context of this file is case sensitive. | |
""" | |
footer = """ | |
! This file was auto-generated with f2py (version:%s). | |
! See http://cens.ioc.ee/projects/f2py2e/ | |
""" % (f2py_version) | |
return header + pyf + footer | |
if __name__ == "__main__": | |
files = [] | |
funcs = [] | |
f = 1 | |
f2 = 0 | |
f3 = 0 | |
showblocklist = 0 | |
for l in sys.argv[1:]: | |
if l == '': | |
pass | |
elif l[0] == ':': | |
f = 0 | |
elif l == '-quiet': | |
quiet = 1 | |
verbose = 0 | |
elif l == '-verbose': | |
verbose = 2 | |
quiet = 0 | |
elif l == '-fix': | |
if strictf77: | |
outmess( | |
'Use option -f90 before -fix if Fortran 90 code is in fix form.\n', 0) | |
skipemptyends = 1 | |
sourcecodeform = 'fix' | |
elif l == '-skipemptyends': | |
skipemptyends = 1 | |
elif l == '--ignore-contains': | |
ignorecontains = 1 | |
elif l == '-f77': | |
strictf77 = 1 | |
sourcecodeform = 'fix' | |
elif l == '-f90': | |
strictf77 = 0 | |
sourcecodeform = 'free' | |
skipemptyends = 1 | |
elif l == '-h': | |
f2 = 1 | |
elif l == '-show': | |
showblocklist = 1 | |
elif l == '-m': | |
f3 = 1 | |
elif l[0] == '-': | |
errmess('Unknown option %s\n' % repr(l)) | |
elif f2: | |
f2 = 0 | |
pyffilename = l | |
elif f3: | |
f3 = 0 | |
f77modulename = l | |
elif f: | |
try: | |
open(l).close() | |
files.append(l) | |
except IOError as detail: | |
errmess('IOError: %s\n' % str(detail)) | |
else: | |
funcs.append(l) | |
if not strictf77 and f77modulename and not skipemptyends: | |
outmess("""\ | |
Warning: You have specified module name for non Fortran 77 code | |
that should not need one (expect if you are scanning F90 code | |
for non module blocks but then you should use flag -skipemptyends | |
and also be sure that the files do not contain programs without program statement). | |
""", 0) | |
postlist = crackfortran(files) | |
if pyffilename: | |
outmess('Writing fortran code to file %s\n' % repr(pyffilename), 0) | |
pyf = crack2fortran(postlist) | |
with open(pyffilename, 'w') as f: | |
f.write(pyf) | |
if showblocklist: | |
show(postlist) | |