| """
|
| crackfortran --- read fortran (77,90) code and extract declaration information.
|
|
|
| Copyright 1999 -- 2011 Pearu Peterson all rights reserved.
|
| Copyright 2011 -- present NumPy Developers.
|
| 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.
|
|
|
|
|
| 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','f2py_len']}
|
| (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'], f2py_len=C['f2py_len'])
|
| (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 sys
|
| import string
|
| import fileinput
|
| import re
|
| import os
|
| import copy
|
| import platform
|
| import codecs
|
| from pathlib import Path
|
| try:
|
| import charset_normalizer
|
| except ImportError:
|
| charset_normalizer = None
|
|
|
| from . import __version__
|
|
|
|
|
|
|
|
|
| from .auxfuncs import *
|
| from . import symbolic
|
|
|
| f2py_version = __version__.version
|
|
|
|
|
| strictf77 = 1
|
| sourcecodeform = 'fix'
|
| quiet = 0
|
| verbose = 1
|
| tabchar = 4 * ' '
|
| pyffilename = ''
|
| f77modulename = ''
|
| skipemptyends = 0
|
| ignorecontains = 1
|
| dolowercase = 1
|
| debug = []
|
|
|
|
|
| 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
|
|
|
|
|
| strictf77 = 1
|
| sourcecodeform = 'fix'
|
| quiet = 0
|
| verbose = 1
|
| tabchar = 4 * ' '
|
| pyffilename = ''
|
| f77modulename = ''
|
| skipemptyends = 0
|
| ignorecontains = 1
|
| dolowercase = 1
|
| debug = []
|
|
|
| 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'}
|
| 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]
|
|
|
|
|
| _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
|
|
|
|
|
| COMMON_FREE_EXTENSIONS = ['.f90', '.f95', '.f03', '.f08']
|
| COMMON_FIXED_EXTENSIONS = ['.for', '.ftn', '.f77', '.f']
|
|
|
|
|
| def openhook(filename, mode):
|
| """Ensures that filename is opened with correct encoding parameter.
|
|
|
| This function uses charset_normalizer package, when available, for
|
| determining the encoding of the file to be opened. When charset_normalizer
|
| is not available, the function detects only UTF encodings, otherwise, ASCII
|
| encoding is used as fallback.
|
| """
|
|
|
|
|
|
|
| if charset_normalizer is not None:
|
| encoding = charset_normalizer.from_path(filename).best().encoding
|
| else:
|
|
|
|
|
| nbytes = min(32, os.path.getsize(filename))
|
| with open(filename, 'rb') as fhandle:
|
| raw = fhandle.read(nbytes)
|
| if raw.startswith(codecs.BOM_UTF8):
|
| encoding = 'UTF-8-SIG'
|
| elif raw.startswith((codecs.BOM_UTF32_LE, codecs.BOM_UTF32_BE)):
|
| encoding = 'UTF-32'
|
| elif raw.startswith((codecs.BOM_LE, codecs.BOM_BE)):
|
| encoding = 'UTF-16'
|
| else:
|
|
|
| encoding = 'ascii'
|
| return open(filename, mode, encoding=encoding)
|
|
|
|
|
| def is_free_format(fname):
|
| """Check if file is in free format Fortran."""
|
|
|
|
|
| result = False
|
| if Path(fname).suffix.lower() in COMMON_FREE_EXTENSIONS:
|
| result = True
|
| with openhook(fname, 'r') as fhandle:
|
| line = fhandle.readline()
|
| n = 15
|
| if _has_f_header(line):
|
| n = 0
|
| elif _has_f90_header(line):
|
| n = 0
|
| result = True
|
| 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 = True
|
| break
|
| line = fhandle.readline()
|
| return result
|
|
|
|
|
|
|
| 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 = 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, openhook=openhook)
|
| while True:
|
| try:
|
| l = fin.readline()
|
| except UnicodeDecodeError as msg:
|
| raise Exception(
|
| f'readfortrancode: reading {fin.filename()}#{fin.lineno()}'
|
| f' failed with\n{msg}.\nIt is likely that installing charset_normalizer'
|
| ' package will help f2py determine the input file encoding'
|
| ' correctly.')
|
| 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 Path(currentfilename).suffix.lower() in COMMON_FIXED_EXTENSIONS 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', ' ')
|
|
|
| while not l == '':
|
| if l[-1] not in "\n\r\f":
|
| break
|
| l = l[:-1]
|
|
|
| is_f2py_directive = False
|
|
|
| (l, rl) = split_by_unquoted(l, '!')
|
| l += ' '
|
| if rl[:5].lower() == '!f2py':
|
| l, _ = split_by_unquoted(l + 4 * ' ' + rl[5:], '!')
|
| is_f2py_directive = True
|
| if l.strip() == '':
|
| if sourcecodeform == 'free':
|
|
|
|
|
|
|
|
|
| pass
|
| else:
|
|
|
|
|
|
|
|
|
|
|
| cont = False
|
| continue
|
| if sourcecodeform == 'fix':
|
| if l[0] in ['*', 'c', '!', 'C', '#']:
|
| if l[1:5].lower() == 'f2py':
|
| l = ' ' + l[5:]
|
| is_f2py_directive = True
|
| else:
|
| cont = False
|
| is_f2py_directive = False
|
| continue
|
| elif strictf77:
|
| if len(l) > 72:
|
| l = l[:72]
|
| if l[0] not 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] == ' '):
|
|
|
| ll = ll + l[6:]
|
| finalline = ''
|
| origfinalline = ''
|
| else:
|
| r = cont1.match(l)
|
| if r:
|
| l = r.group('line')
|
| if cont:
|
| ll = ll + cont2.match(l).group('line')
|
| finalline = ''
|
| origfinalline = ''
|
| else:
|
|
|
| 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')
|
| if cont:
|
| ll = ll + cont2.match(l).group('line')
|
| finalline = ''
|
| origfinalline = ''
|
| else:
|
| if localdolowercase:
|
|
|
|
|
| finalline = ll.lower() if not (is_f2py_directive and iscstyledirective(ll)) else ll
|
| 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
|
|
|
|
|
| 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'
|
|
|
|
|
| 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, '.*'), re.I), 'end'
|
|
|
| endifs = r'end\s*(if|do|where|select|while|forall|associate|' + \
|
| r'critical|enum|team)'
|
| endifpattern = re.compile(
|
| beforethisafter % (r'[\w]*?', endifs, endifs, '.*'), re.I), 'endif'
|
|
|
| moduleprocedures = r'module\s*procedure'
|
| moduleprocedurepattern = re.compile(
|
| beforethisafter % ('', moduleprocedures, moduleprocedures, '.*'), re.I), \
|
| 'moduleprocedure'
|
| 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'
|
|
|
| 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)
|
| crackline_bind_1 = re.compile(r'\s*(?P<bind>\b[a-z]+\w*\b)\s*=.*', re.I)
|
| crackline_bindlang = re.compile(r'\s*bind\(\s*(?P<lang>[^,]+)\s*,\s*name\s*=\s*"(?P<lang_name>[^"]+)"\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)):
|
|
|
| assert reset == 0, repr(reset)
|
|
|
| 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
|
| grouplist[groupcounter - 1].append(groupcache[groupcounter])
|
| grouplist[groupcounter - 1][-1]['body'] = grouplist[groupcounter]
|
| del grouplist[groupcounter]
|
| groupcounter = groupcounter - 1
|
| 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,
|
| moduleprocedurepattern
|
| ]:
|
| 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] == 'moduleprocedure':
|
| analyzeline(m, pat[1], line)
|
| 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', 'depend']:
|
| 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)
|
| typedefpattern = re.compile(
|
| r'(?:,(?P<attributes>[\w(),]+))?(::)?(?P<name>\b[a-z$_][\w$]*\b)'
|
| r'(?:\((?P<params>[\w,]*)\))?\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)
|
| operatorpattern = re.compile(
|
| r'\s*(?P<scheme>(operator|assignment))'
|
| r'@\(@\s*(?P<name>[^)]+)\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 _resolvetypedefpattern(line):
|
| line = ''.join(line.split())
|
| m1 = typedefpattern.match(line)
|
| print(line, m1)
|
| if m1:
|
| attrs = m1.group('attributes')
|
| attrs = [a.lower() for a in attrs.split(',')] if attrs else []
|
| return m1.group('name'), attrs, m1.group('params')
|
| return None, [], None
|
|
|
| def parse_name_for_bind(line):
|
| pattern = re.compile(r'bind\(\s*(?P<lang>[^,]+)(?:\s*,\s*name\s*=\s*["\'](?P<name>[^"\']+)["\']\s*)?\)', re.I)
|
| match = pattern.search(line)
|
| bind_statement = None
|
| if match:
|
| bind_statement = match.group(0)
|
|
|
| line = line[:match.start()] + line[match.end():]
|
| return line, bind_statement
|
|
|
| def _resolvenameargspattern(line):
|
| line, bind_cname = parse_name_for_bind(line)
|
| line = markouterparen(line)
|
| m1 = nameargspattern.match(line)
|
| if m1:
|
| return m1.group('name'), m1.group('args'), m1.group('result'), bind_cname
|
| m1 = operatorpattern.match(line)
|
| if m1:
|
| name = m1.group('scheme') + '(' + m1.group('name') + ')'
|
| return name, [], None, None
|
| m1 = callnameargspattern.match(line)
|
| if m1:
|
| return m1.group('name'), m1.group('args'), None, None
|
| return None, [], None, None
|
|
|
|
|
| def analyzeline(m, case, line):
|
| """
|
| Reads each line in the input file in sequence and updates global vars.
|
|
|
| Effectively reads and collects information from the input file to the
|
| global variable groupcache, a dictionary containing info about each part
|
| of the fortran module.
|
|
|
| At the end of analyzeline, information is filtered into the correct dict
|
| keys, but parameter values and dimensions are not yet interpreted.
|
| """
|
| 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']:
|
|
|
| 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'
|
| if block == 'type':
|
| name, attrs, _ = _resolvetypedefpattern(m.group('after'))
|
| groupcache[groupcounter]['vars'][name] = dict(attrspec = attrs)
|
| args = []
|
| result = None
|
| else:
|
| name, args, result, bindcline = _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')
|
|
|
|
|
| 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
|
|
|
| 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'] = {}
|
|
|
| if block == 'type':
|
| groupcache[groupcounter]['varnames'] = []
|
|
|
| if case in ['call', 'callfun']:
|
| 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']:
|
|
|
| if bindcline:
|
| bindcdat = re.search(crackline_bindlang, bindcline)
|
| if bindcdat:
|
| groupcache[groupcounter]['bindlang'] = {name : {}}
|
| groupcache[groupcounter]['bindlang'][name]["lang"] = bindcdat.group('lang')
|
| if bindcdat.group('lang_name'):
|
| groupcache[groupcounter]['bindlang'][name]["name"] = bindcdat.group('lang_name')
|
| try:
|
| groupcache[groupcounter]['vars'][name] = appenddecl(
|
| groupcache[groupcounter]['vars'][name], groupcache[groupcounter - 2]['vars'][''])
|
| except Exception:
|
| pass
|
| if case == 'callfun':
|
| 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])
|
|
|
| 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
|
| grouplist[groupcounter - 1].append(groupcache[groupcounter])
|
| grouplist[groupcounter - 1][-1]['body'] = grouplist[groupcounter]
|
| del grouplist[groupcounter]
|
| groupcounter = groupcounter - 1
|
|
|
| elif case == 'entry':
|
| name, args, result, _= _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 case in ['public', 'private'] and \
|
| (k == 'operator' or k == 'assignment'):
|
| k += m1.group('after')
|
| 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')
|
|
|
| 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\n' % (k))
|
| else:
|
| errmess('analyzeline: intent(callback) %s is already'
|
| ' in argument list\n' % (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 == 'moduleprocedure':
|
| groupcache[groupcounter]['implementedby'] = \
|
| [x.strip() for x in m.group('after').split(',')]
|
| 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 = groupcache[groupcounter].get('vars', {})
|
| last_name = None
|
| for l in ll:
|
| l[0], l[1] = l[0].strip(), l[1].strip()
|
| if l[0].startswith(','):
|
| l[0] = l[0][1:]
|
| if l[0].startswith('('):
|
| outmess('analyzeline: implied-DO list "%s" is not supported. Skipping.\n' % l[0])
|
| continue
|
| for idx, v in enumerate(rmbadname([x.strip() for x in markoutercomma(l[0]).split('@,@')])):
|
| if v.startswith('('):
|
| outmess('analyzeline: implied-DO list "%s" is not supported. Skipping.\n' % v)
|
|
|
|
|
|
|
| continue
|
| if '!' in l[1]:
|
|
|
|
|
|
|
|
|
|
|
| outmess('Comment line in declaration "%s" is not supported. Skipping.\n' % l[1])
|
| continue
|
| vars.setdefault(v, {})
|
| vtype = vars[v].get('typespec')
|
| vdim = getdimension(vars[v])
|
| matches = re.findall(r"\(.*?\)", l[1]) if vtype == 'complex' else l[1].split(',')
|
| try:
|
| new_val = "(/{}/)".format(", ".join(matches)) if vdim else matches[idx]
|
| except IndexError:
|
|
|
|
|
|
|
|
|
| if any("*" in m for m in matches):
|
| expanded_list = []
|
| for match in matches:
|
| if "*" in match:
|
| try:
|
| multiplier, value = match.split("*")
|
| expanded_list.extend([value.strip()] * int(multiplier))
|
| except ValueError:
|
| expanded_list.append(match.strip())
|
| else:
|
| expanded_list.append(match.strip())
|
| matches = expanded_list
|
| new_val = "(/{}/)".format(", ".join(matches)) if vdim else matches[idx]
|
| current_val = vars[v].get('=')
|
| if current_val and (current_val != new_val):
|
| outmess('analyzeline: changing init expression of "%s" ("%s") to "%s"\n' % (v, current_val, new_val))
|
| vars[v]['='] = new_val
|
| last_name = v
|
| groupcache[groupcounter]['vars'] = vars
|
| if last_name:
|
| 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>.*)|)'
|
| r'|(len\s*=\s*|)(?P<len2>.*?)\s*(@,@\s*(kind\s*=\s*|)(?P<kind2>.*)'
|
| r'|(f2py_len\s*=\s*(?P<f2py_len>.*))|))\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):
|
| """
|
| The function replace all spaces in the input variable line which are
|
| surrounded with quotation marks, with the triplet "@_@".
|
|
|
| For instance, for the input "a 'b c'" the function returns "a 'b@_@c'"
|
|
|
| Parameters
|
| ----------
|
| line : str
|
|
|
| Returns
|
| -------
|
| str
|
|
|
| """
|
| fragment = ''
|
| inside = False
|
| current_quote = None
|
| escaped = ''
|
| for c in line:
|
| if escaped == '\\' and c in ['\\', '\'', '"']:
|
| fragment += c
|
| escaped = c
|
| continue
|
| if not inside and c in ['\'', '"']:
|
| current_quote = c
|
| if c == current_quote:
|
| inside = not inside
|
| elif c == ' ' and inside:
|
| fragment += '@_@'
|
| continue
|
| fragment += c
|
| escaped = c
|
| return fragment
|
|
|
|
|
| def updatevars(typespec, selector, attrspec, entitydecl):
|
| """
|
| Returns last_name, the variable name without special chars, parenthesis
|
| or dimension specifiers.
|
|
|
| Alters groupcache to add the name, typespec, attrspec (and possibly value)
|
| of current variable.
|
| """
|
| 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']
|
| 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']
|
| del d1['len']
|
| 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 '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']
|
| del 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']
|
| del 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']
|
|
|
| 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
|
|
|
| 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']
|
| if lenkind['f2py_len'] is not None:
|
|
|
| charselect['f2py_len'] = lenkind['f2py_len']
|
| 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
|
|
|
|
|
|
|
|
|
| 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
|
|
|
| if mapping:
|
| errmess('get_useparameters: mapping for %s not impl.\n' % (mapping))
|
| for k, v in list(params.items()):
|
| if k in param_map:
|
| outmess('get_useparameters: overriding parameter %s with'
|
| ' value from module %s\n' % (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')
|
|
|
| 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 block.get('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']
|
|
|
| if block.get('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:
|
| 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)
|
|
|
| maybe_private = {
|
| key: value
|
| for key, value in block['vars'].items()
|
| if 'attrspec' not in value or 'public' not in value['attrspec']
|
| }
|
|
|
| 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 maybe_private.keys():
|
| skipfuncs.append(b['name'])
|
| 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'] and not b.get('implementedby'):
|
| 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):
|
| """
|
| 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):
|
|
|
| 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, {}, {})
|
|
|
| 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
|
|
|
|
|
| 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 and word != name:
|
| 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):
|
|
|
| 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):
|
|
|
| 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):
|
|
|
|
|
| if p < 7:
|
| return 4
|
| if p < 16:
|
| return 8
|
| machine = platform.machine().lower()
|
| if machine.startswith(('aarch64', 'alpha', 'arm64', 'loongarch', 'mips', 'power', 'ppc', 'riscv', 's390x', 'sparc')):
|
| if p <= 33:
|
| return 16
|
| else:
|
| if p < 19:
|
| return 10
|
| elif p <= 33:
|
| 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'),
|
|
|
| ]:
|
| v = v.replace(*repl)
|
|
|
| v = kind_re.sub(r'kind("\1")', v)
|
| v = selected_int_kind_re.sub(r'selected_int_kind(\1)', v)
|
|
|
|
|
|
|
|
|
|
|
| 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'], '')
|
|
|
|
|
| is_replaced = len(v) < orig_v_len
|
|
|
| if not is_replaced:
|
| if not selected_kind_re.match(v):
|
| v_ = v.split('_')
|
|
|
| if len(v_) > 1:
|
| v = ''.join(v_[:-1]).lower().replace(v_[-1].lower(), '')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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]):
|
| outmess(f'get_parameters[TODO]: '
|
| f'implement evaluation of complex expression {v}\n')
|
|
|
| dimspec = ([s.removeprefix('dimension').strip()
|
| for s in vars[n]['attrspec']
|
| if s.startswith('dimension')] or [None])[0]
|
|
|
|
|
|
|
| if real16pattern.search(v):
|
| v = 8
|
| elif real8pattern.search(v):
|
| v = 4
|
| try:
|
| params[n] = param_eval(v, g_params, params, dimspec=dimspec)
|
| except Exception as msg:
|
| params[n] = v
|
| outmess(f'get_parameters: got "{msg}" on {n!r}\n')
|
|
|
| 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(f'get_parameters:parameter {n!r} does not have value?!\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 = eval(value, {}, params)
|
| value = (repr if isinstance(value, str) else str)(value)
|
| 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):
|
| """
|
| Sets correct dimension information for each variable/parameter
|
| """
|
|
|
| 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 set(vars) | set(b['name'] for b in block['body']):
|
| for k in ['public', 'private']:
|
| if k in gen:
|
| vars[n] = setattrspec(vars.get(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
|
|
|
| dimension_exprs = {}
|
| 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('@,@')]:
|
|
|
| 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('@,@')]
|
| ):
|
|
|
|
|
| try:
|
|
|
|
|
| d = param_parse(d, params)
|
| except (ValueError, IndexError, KeyError):
|
| outmess(
|
| 'analyzevars: could not parse dimension for '
|
| f'variable {d!r}\n'
|
| )
|
|
|
| dim_char = ':' if d == ':' else '*'
|
| if d == dim_char:
|
| dl = [dim_char]
|
| else:
|
| dl = markoutercomma(d, ':').split('@:@')
|
| if len(dl) == 2 and '*' in dl:
|
| dl = ['*']
|
| d = '*'
|
| if len(dl) == 1 and dl[0] != dim_char:
|
| dl = ['1', dl[0]]
|
| if len(dl) == 2:
|
| d1, d2 = map(symbolic.Expr.parse, dl)
|
| dsize = d2 - d1 + 1
|
| d = dsize.tostring(language=symbolic.Language.C)
|
|
|
|
|
|
|
| solver_and_deps = {}
|
| for v in block['vars']:
|
| s = symbolic.as_symbol(v)
|
| if dsize.contains(s):
|
| try:
|
| a, b = dsize.linear_solve(s)
|
|
|
| def solve_v(s, a=a, b=b):
|
| return (s - b) / a
|
|
|
| all_symbols = set(a.symbols())
|
| all_symbols.update(b.symbols())
|
| except RuntimeError as msg:
|
|
|
|
|
|
|
|
|
|
|
| solve_v = None
|
| all_symbols = set(dsize.symbols())
|
| v_deps = set(
|
| s.data for s in all_symbols
|
| if s.data in vars)
|
| solver_and_deps[v] = solve_v, list(v_deps)
|
|
|
|
|
|
|
|
|
|
|
|
|
| dimension_exprs[d] = solver_and_deps
|
| vars[n]['dimension'].append(d)
|
|
|
| if 'check' not in vars[n] and 'args' in block and n in block['args']:
|
|
|
|
|
|
|
|
|
| n_deps = vars[n].get('depend', [])
|
| n_checks = []
|
| n_is_input = l_or(isintent_in, isintent_inout,
|
| isintent_inplace)(vars[n])
|
| if isarray(vars[n]):
|
| for i, d in enumerate(vars[n]['dimension']):
|
| coeffs_and_deps = dimension_exprs.get(d)
|
| if coeffs_and_deps is None:
|
|
|
| pass
|
| elif n_is_input:
|
|
|
|
|
|
|
| for v, (solver, deps) in coeffs_and_deps.items():
|
| def compute_deps(v, deps):
|
| for v1 in coeffs_and_deps.get(v, [None, []])[1]:
|
| if v1 not in deps:
|
| deps.add(v1)
|
| compute_deps(v1, deps)
|
| all_deps = set()
|
| compute_deps(v, all_deps)
|
| if (v in n_deps
|
| or '=' in vars[v]
|
| or 'depend' in vars[v]):
|
|
|
|
|
|
|
|
|
| continue
|
| if solver is not None and v not in all_deps:
|
|
|
|
|
|
|
| is_required = False
|
| init = solver(symbolic.as_symbol(
|
| f'shape({n}, {i})'))
|
| init = init.tostring(
|
| language=symbolic.Language.C)
|
| vars[v]['='] = init
|
|
|
|
|
|
|
| vars[v]['depend'] = [n] + deps
|
| if 'check' not in vars[v]:
|
|
|
|
|
| vars[v]['check'] = [
|
| f'shape({n}, {i}) == {d}']
|
| else:
|
|
|
|
|
|
|
| is_required = True
|
| if 'intent' not in vars[v]:
|
| vars[v]['intent'] = []
|
| if 'in' not in vars[v]['intent']:
|
| vars[v]['intent'].append('in')
|
|
|
| n_deps.append(v)
|
| n_checks.append(
|
| f'shape({n}, {i}) == {d}')
|
| v_attr = vars[v].get('attrspec', [])
|
| if not ('optional' in v_attr
|
| or 'required' in v_attr):
|
| v_attr.append(
|
| 'required' if is_required else 'optional')
|
| if v_attr:
|
| vars[v]['attrspec'] = v_attr
|
| if coeffs_and_deps is not None:
|
|
|
| for v, (solver, deps) in coeffs_and_deps.items():
|
| v_deps = vars[v].get('depend', [])
|
| for aa in vars[v].get('attrspec', []):
|
| if aa.startswith('depend'):
|
| aa = ''.join(aa.split())
|
| v_deps.extend(aa[7:-1].split(','))
|
| if v_deps:
|
| vars[v]['depend'] = list(set(v_deps))
|
| if n not in v_deps:
|
| n_deps.append(v)
|
| elif isstring(vars[n]):
|
| 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 n_checks:
|
| vars[n]['check'] = n_checks
|
| if n_deps:
|
| vars[n]['depend'] = list(set(n_deps))
|
|
|
| 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']:
|
| 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']
|
| 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
|
| try:
|
| if block['result']:
|
| vars[block['result']]['typespec'] = typespec
|
| except Exception:
|
| pass
|
| 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 block['block'] not 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 param_eval(v, g_params, params, dimspec=None):
|
| """
|
| Creates a dictionary of indices and values for each parameter in a
|
| parameter array to be evaluated later.
|
|
|
| WARNING: It is not possible to initialize multidimensional array
|
| parameters e.g. dimension(-3:1, 4, 3:5) at this point. This is because in
|
| Fortran initialization through array constructor requires the RESHAPE
|
| intrinsic function. Since the right-hand side of the parameter declaration
|
| is not executed in f2py, but rather at the compiled c/fortran extension,
|
| later, it is not possible to execute a reshape of a parameter array.
|
| One issue remains: if the user wants to access the array parameter from
|
| python, we should either
|
| 1) allow them to access the parameter array using python standard indexing
|
| (which is often incompatible with the original fortran indexing)
|
| 2) allow the parameter array to be accessed in python as a dictionary with
|
| fortran indices as keys
|
| We are choosing 2 for now.
|
| """
|
| if dimspec is None:
|
| try:
|
| p = eval(v, g_params, params)
|
| except Exception as msg:
|
| p = v
|
| outmess(f'param_eval: got "{msg}" on {v!r}\n')
|
| return p
|
|
|
|
|
|
|
| if len(dimspec) < 2 or dimspec[::len(dimspec)-1] != "()":
|
| raise ValueError(f'param_eval: dimension {dimspec} can\'t be parsed')
|
| dimrange = dimspec[1:-1].split(',')
|
| if len(dimrange) == 1:
|
|
|
| dimrange = dimrange[0].split(':')
|
|
|
| if len(dimrange) == 1:
|
| bound = param_parse(dimrange[0], params)
|
| dimrange = range(1, int(bound)+1)
|
| else:
|
| lbound = param_parse(dimrange[0], params)
|
| ubound = param_parse(dimrange[1], params)
|
| dimrange = range(int(lbound), int(ubound)+1)
|
| else:
|
| raise ValueError('param_eval: multidimensional array parameters '
|
| f'{dimspec} not supported')
|
|
|
|
|
| v = (v[2:-2] if v.startswith('(/') else v).split(',')
|
| v_eval = []
|
| for item in v:
|
| try:
|
| item = eval(item, g_params, params)
|
| except Exception as msg:
|
| outmess(f'param_eval: got "{msg}" on {item!r}\n')
|
| v_eval.append(item)
|
|
|
| p = dict(zip(dimrange, v_eval))
|
|
|
| return p
|
|
|
|
|
| def param_parse(d, params):
|
| """Recursively parse array dimensions.
|
|
|
| Parses the declaration of an array variable or parameter
|
| `dimension` keyword, and is called recursively if the
|
| dimension for this array is a previously defined parameter
|
| (found in `params`).
|
|
|
| Parameters
|
| ----------
|
| d : str
|
| Fortran expression describing the dimension of an array.
|
| params : dict
|
| Previously parsed parameters declared in the Fortran source file.
|
|
|
| Returns
|
| -------
|
| out : str
|
| Parsed dimension expression.
|
|
|
| Examples
|
| --------
|
|
|
| * If the line being analyzed is
|
|
|
| `integer, parameter, dimension(2) :: pa = (/ 3, 5 /)`
|
|
|
| then `d = 2` and we return immediately, with
|
|
|
| >>> d = '2'
|
| >>> param_parse(d, params)
|
| 2
|
|
|
| * If the line being analyzed is
|
|
|
| `integer, parameter, dimension(pa) :: pb = (/1, 2, 3/)`
|
|
|
| then `d = 'pa'`; since `pa` is a previously parsed parameter,
|
| and `pa = 3`, we call `param_parse` recursively, to obtain
|
|
|
| >>> d = 'pa'
|
| >>> params = {'pa': 3}
|
| >>> param_parse(d, params)
|
| 3
|
|
|
| * If the line being analyzed is
|
|
|
| `integer, parameter, dimension(pa(1)) :: pb = (/1, 2, 3/)`
|
|
|
| then `d = 'pa(1)'`; since `pa` is a previously parsed parameter,
|
| and `pa(1) = 3`, we call `param_parse` recursively, to obtain
|
|
|
| >>> d = 'pa(1)'
|
| >>> params = dict(pa={1: 3, 2: 5})
|
| >>> param_parse(d, params)
|
| 3
|
| """
|
| if "(" in d:
|
|
|
| dname = d[:d.find("(")]
|
| ddims = d[d.find("(")+1:d.rfind(")")]
|
|
|
|
|
| index = int(param_parse(ddims, params))
|
| return str(params[dname][index])
|
| elif d in params:
|
| return str(params[d])
|
| else:
|
| for p in params:
|
| 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)
|
| return d
|
|
|
|
|
| def expr2name(a, block, args=[]):
|
| orig_a = a
|
| a_is_expr = not analyzeargs_re_1.match(a)
|
| if a_is_expr:
|
| 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, _ = 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):
|
| 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):
|
| 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'):
|
|
|
|
|
|
|
|
|
| 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 as_interface and 'intent(in)' in attr and 'intent(out)' in attr:
|
|
|
|
|
|
|
|
|
|
|
| attr.remove('intent(out)')
|
| 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
|
|
|
|
|
|
|
|
|
|
|
| post_processing_hooks = []
|
|
|
|
|
| def crackfortran(files):
|
| global usermodules, post_processing_hooks
|
|
|
| outmess('Reading fortran codes...\n', 0)
|
| readfortrancode(files, crackline)
|
| outmess('Post-processing...\n', 0)
|
| usermodules = []
|
| postlist = postcrack(grouplist[0])
|
| outmess('Applying post-processing hooks...\n', 0)
|
| for hook in post_processing_hooks:
|
| outmess(f' {hook.__name__}\n', 0)
|
| postlist = traverse(postlist, hook)
|
| 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:
|
| ! https://web.archive.org/web/20140822061353/http://cens.ioc.ee/projects/f2py2e
|
| """ % (f2py_version)
|
| return header + pyf + footer
|
|
|
|
|
| def _is_visit_pair(obj):
|
| return (isinstance(obj, tuple)
|
| and len(obj) == 2
|
| and isinstance(obj[0], (int, str)))
|
|
|
|
|
| def traverse(obj, visit, parents=[], result=None, *args, **kwargs):
|
| '''Traverse f2py data structure with the following visit function:
|
|
|
| def visit(item, parents, result, *args, **kwargs):
|
| """
|
|
|
| parents is a list of key-"f2py data structure" pairs from which
|
| items are taken from.
|
|
|
| result is a f2py data structure that is filled with the
|
| return value of the visit function.
|
|
|
| item is 2-tuple (index, value) if parents[-1][1] is a list
|
| item is 2-tuple (key, value) if parents[-1][1] is a dict
|
|
|
| The return value of visit must be None, or of the same kind as
|
| item, that is, if parents[-1] is a list, the return value must
|
| be 2-tuple (new_index, new_value), or if parents[-1] is a
|
| dict, the return value must be 2-tuple (new_key, new_value).
|
|
|
| If new_index or new_value is None, the return value of visit
|
| is ignored, that is, it will not be added to the result.
|
|
|
| If the return value is None, the content of obj will be
|
| traversed, otherwise not.
|
| """
|
| '''
|
|
|
| if _is_visit_pair(obj):
|
| if obj[0] == 'parent_block':
|
|
|
| return obj
|
| new_result = visit(obj, parents, result, *args, **kwargs)
|
| if new_result is not None:
|
| assert _is_visit_pair(new_result)
|
| return new_result
|
| parent = obj
|
| result_key, obj = obj
|
| else:
|
| parent = (None, obj)
|
| result_key = None
|
|
|
| if isinstance(obj, list):
|
| new_result = []
|
| for index, value in enumerate(obj):
|
| new_index, new_item = traverse((index, value), visit,
|
| parents=parents + [parent],
|
| result=result, *args, **kwargs)
|
| if new_index is not None:
|
| new_result.append(new_item)
|
| elif isinstance(obj, dict):
|
| new_result = dict()
|
| for key, value in obj.items():
|
| new_key, new_value = traverse((key, value), visit,
|
| parents=parents + [parent],
|
| result=result, *args, **kwargs)
|
| if new_key is not None:
|
| new_result[new_key] = new_value
|
| else:
|
| new_result = obj
|
|
|
| if result_key is None:
|
| return new_result
|
| return result_key, new_result
|
|
|
|
|
| def character_backward_compatibility_hook(item, parents, result,
|
| *args, **kwargs):
|
| """Previously, Fortran character was incorrectly treated as
|
| character*1. This hook fixes the usage of the corresponding
|
| variables in `check`, `dimension`, `=`, and `callstatement`
|
| expressions.
|
|
|
| The usage of `char*` in `callprotoargument` expression can be left
|
| unchanged because C `character` is C typedef of `char`, although,
|
| new implementations should use `character*` in the corresponding
|
| expressions.
|
|
|
| See https://github.com/numpy/numpy/pull/19388 for more information.
|
|
|
| """
|
| parent_key, parent_value = parents[-1]
|
| key, value = item
|
|
|
| def fix_usage(varname, value):
|
| value = re.sub(r'[*]\s*\b' + varname + r'\b', varname, value)
|
| value = re.sub(r'\b' + varname + r'\b\s*[\[]\s*0\s*[\]]',
|
| varname, value)
|
| return value
|
|
|
| if parent_key in ['dimension', 'check']:
|
| assert parents[-3][0] == 'vars'
|
| vars_dict = parents[-3][1]
|
| elif key == '=':
|
| assert parents[-2][0] == 'vars'
|
| vars_dict = parents[-2][1]
|
| else:
|
| vars_dict = None
|
|
|
| new_value = None
|
| if vars_dict is not None:
|
| new_value = value
|
| for varname, vd in vars_dict.items():
|
| if ischaracter(vd):
|
| new_value = fix_usage(varname, new_value)
|
| elif key == 'callstatement':
|
| vars_dict = parents[-2][1]['vars']
|
| new_value = value
|
| for varname, vd in vars_dict.items():
|
| if ischaracter(vd):
|
|
|
|
|
| new_value = re.sub(
|
| r'(?<![&])\b' + varname + r'\b', '&' + varname, new_value)
|
|
|
| if new_value is not None:
|
| if new_value != value:
|
|
|
|
|
|
|
|
|
| outmess(f'character_bc_hook[{parent_key}.{key}]:'
|
| f' replaced `{value}` -> `{new_value}`\n', 1)
|
| return (key, new_value)
|
|
|
|
|
| post_processing_hooks.append(character_backward_compatibility_hook)
|
|
|
|
|
| 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 OSError as detail:
|
| errmess(f'OSError: {detail!s}\n')
|
| 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)
|
|
|