Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
crackfortran.py3726 linesDownload Raw Back to f2py
1"""
2crackfortran --- read fortran (77,90) code and extract declaration information.
3
4Copyright 1999 -- 2011 Pearu Peterson all rights reserved.
5Copyright 2011 -- present NumPy Developers.
6Permission to use, modify, and distribute this software is given under the
7terms of the NumPy License.
8
9NO WARRANTY IS EXPRESSED OR IMPLIED.  USE AT YOUR OWN RISK.
10
11
12Usage of crackfortran:
13======================
14Command line keys: -quiet,-verbose,-fix,-f77,-f90,-show,-h <pyffilename>
15                   -m <module name for f77 routines>,--ignore-contains
16Functions: crackfortran, crack2fortran
17The following Fortran statements/constructions are supported
18(or will be if needed):
19   block data,byte,call,character,common,complex,contains,data,
20   dimension,double complex,double precision,end,external,function,
21   implicit,integer,intent,interface,intrinsic,
22   logical,module,optional,parameter,private,public,
23   program,real,(sequence?),subroutine,type,use,virtual,
24   include,pythonmodule
25Note: 'virtual' is mapped to 'dimension'.
26Note: 'implicit integer (z) static (z)' is 'implicit static (z)' (this is minor bug).
27Note: code after 'contains' will be ignored until its scope ends.
28Note: 'common' statement is extended: dimensions are moved to variable definitions
29Note: f2py directive: <commentchar>f2py<line> is read as <line>
30Note: pythonmodule is introduced to represent Python module
31
32Usage:
33  `postlist=crackfortran(files)`
34  `postlist` contains declaration information read from the list of files `files`.
35  `crack2fortran(postlist)` returns a fortran code to be saved to pyf-file
36
37  `postlist` has the following structure:
38 *** it is a list of dictionaries containing `blocks':
39     B = {'block','body','vars','parent_block'[,'name','prefix','args','result',
40          'implicit','externals','interfaced','common','sortvars',
41          'commonvars','note']}
42     B['block'] = 'interface' | 'function' | 'subroutine' | 'module' |
43                  'program' | 'block data' | 'type' | 'pythonmodule' |
44                  'abstract interface'
45     B['body'] --- list containing `subblocks' with the same structure as `blocks'
46     B['parent_block'] --- dictionary of a parent block:
47                             C['body'][<index>]['parent_block'] is C
48     B['vars'] --- dictionary of variable definitions
49     B['sortvars'] --- dictionary of variable definitions sorted by dependence (independent first)
50     B['name'] --- name of the block (not if B['block']=='interface')
51     B['prefix'] --- prefix string (only if B['block']=='function')
52     B['args'] --- list of argument names if B['block']== 'function' | 'subroutine'
53     B['result'] --- name of the return value (only if B['block']=='function')
54     B['implicit'] --- dictionary {'a':<variable definition>,'b':...} | None
55     B['externals'] --- list of variables being external
56     B['interfaced'] --- list of variables being external and defined
57     B['common'] --- dictionary of common blocks (list of objects)
58     B['commonvars'] --- list of variables used in common blocks (dimensions are moved to variable definitions)
59     B['from'] --- string showing the 'parents' of the current block
60     B['use'] --- dictionary of modules used in current block:
61         {<modulename>:{['only':<0|1>],['map':{<local_name1>:<use_name1>,...}]}}
62     B['note'] --- list of LaTeX comments on the block
63     B['f2pyenhancements'] --- optional dictionary
64          {'threadsafe':'','fortranname':<name>,
65           'callstatement':<C-expr>|<multi-line block>,
66           'callprotoargument':<C-expr-list>,
67           'usercode':<multi-line block>|<list of multi-line blocks>,
68           'pymethoddef:<multi-line block>'
69           }
70     B['entry'] --- dictionary {entryname:argslist,..}
71     B['varnames'] --- list of variable names given in the order of reading the
72                       Fortran code, useful for derived types.
73     B['saved_interface'] --- a string of scanned routine signature, defines explicit interface
74 *** Variable definition is a dictionary
75     D = B['vars'][<variable name>] =
76     {'typespec'[,'attrspec','kindselector','charselector','=','typename']}
77     D['typespec'] = 'byte' | 'character' | 'complex' | 'double complex' |
78                     'double precision' | 'integer' | 'logical' | 'real' | 'type'
79     D['attrspec'] --- list of attributes (e.g. 'dimension(<arrayspec>)',
80                       'external','intent(in|out|inout|hide|c|callback|cache|aligned4|aligned8|aligned16)',
81                       'optional','required', etc)
82     K = D['kindselector'] = {['*','kind']} (only if D['typespec'] =
83                         'complex' | 'integer' | 'logical' | 'real' )
84     C = D['charselector'] = {['*','len','kind','f2py_len']}
85                             (only if D['typespec']=='character')
86     D['='] --- initialization expression string
87     D['typename'] --- name of the type if D['typespec']=='type'
88     D['dimension'] --- list of dimension bounds
89     D['intent'] --- list of intent specifications
90     D['depend'] --- list of variable names on which current variable depends on
91     D['check'] --- list of C-expressions; if C-expr returns zero, exception is raised
92     D['note'] --- list of LaTeX comments on the variable
93 *** Meaning of kind/char selectors (few examples):
94     D['typespec>']*K['*']
95     D['typespec'](kind=K['kind'])
96     character*C['*']
97     character(len=C['len'],kind=C['kind'], f2py_len=C['f2py_len'])
98     (see also fortran type declaration statement formats below)
99
100Fortran 90 type declaration statement format (F77 is subset of F90)
101====================================================================
102(Main source: IBM XL Fortran 5.1 Language Reference Manual)
103type declaration = <typespec> [[<attrspec>]::] <entitydecl>
104<typespec> = byte                          |
105             character[<charselector>]     |
106             complex[<kindselector>]       |
107             double complex                |
108             double precision              |
109             integer[<kindselector>]       |
110             logical[<kindselector>]       |
111             real[<kindselector>]          |
112             type(<typename>)
113<charselector> = * <charlen>               |
114             ([len=]<len>[,[kind=]<kind>]) |
115             (kind=<kind>[,len=<len>])
116<kindselector> = * <intlen>                |
117             ([kind=]<kind>)
118<attrspec> = comma separated list of attributes.
119             Only the following attributes are used in
120             building up the interface:
121                external
122                (parameter --- affects '=' key)
123                optional
124                intent
125             Other attributes are ignored.
126<intentspec> = in | out | inout
127<arrayspec> = comma separated list of dimension bounds.
128<entitydecl> = <name> [[*<charlen>][(<arrayspec>)] | [(<arrayspec>)]*<charlen>]
129                      [/<init_expr>/ | =<init_expr>] [,<entitydecl>]
130
131In addition, the following attributes are used: check,depend,note
132
133TODO:
134    * Apply 'parameter' attribute (e.g. 'integer parameter :: i=2' 'real x(i)'
135                                   -> 'real x(2)')
136    The above may be solved by creating appropriate preprocessor program, for example.
137
138"""
139import codecs
140import copy
141import fileinput
142import os
143import platform
144import re
145import string
146import sys
147from pathlib import Path
148
149try:
150    import charset_normalizer
151except ImportError:
152    charset_normalizer = None
153
154from . import __version__, symbolic
155
156# The environment provided by auxfuncs.py is needed for some calls to eval.
157# As the needed functions cannot be determined by static inspection of the
158# code, it is safest to use import * pending a major refactoring of f2py.
159from .auxfuncs import *
160
161f2py_version = __version__.version
162
163# Global flags:
164strictf77 = 1          # Ignore `!' comments unless line[0]=='!'
165sourcecodeform = 'fix'  # 'fix','free'
166quiet = 0              # Be verbose if 0 (Obsolete: not used any more)
167verbose = 1            # Be quiet if 0, extra verbose if > 1.
168tabchar = 4 * ' '
169pyffilename = ''
170f77modulename = ''
171skipemptyends = 0      # for old F77 programs without 'program' statement
172ignorecontains = 1
173dolowercase = 1
174debug = []
175
176# Global variables
177beginpattern = ''
178currentfilename = ''
179expectbegin = 1
180f90modulevars = {}
181filepositiontext = ''
182gotnextfile = 1
183groupcache = None
184groupcounter = 0
185grouplist = {groupcounter: []}
186groupname = ''
187include_paths = []
188neededmodule = -1
189onlyfuncs = []
190previous_context = None
191skipblocksuntil = -1
192skipfuncs = []
193skipfunctions = []
194usermodules = []
195
196
197def reset_global_f2py_vars():
198    global groupcounter, grouplist, neededmodule, expectbegin
199    global skipblocksuntil, usermodules, f90modulevars, gotnextfile
200    global filepositiontext, currentfilename, skipfunctions, skipfuncs
201    global onlyfuncs, include_paths, previous_context
202    global strictf77, sourcecodeform, quiet, verbose, tabchar, pyffilename
203    global f77modulename, skipemptyends, ignorecontains, dolowercase, debug
204
205    # flags
206    strictf77 = 1
207    sourcecodeform = 'fix'
208    quiet = 0
209    verbose = 1
210    tabchar = 4 * ' '
211    pyffilename = ''
212    f77modulename = ''
213    skipemptyends = 0
214    ignorecontains = 1
215    dolowercase = 1
216    debug = []
217    # variables
218    groupcounter = 0
219    grouplist = {groupcounter: []}
220    neededmodule = -1
221    expectbegin = 1
222    skipblocksuntil = -1
223    usermodules = []
224    f90modulevars = {}
225    gotnextfile = 1
226    filepositiontext = ''
227    currentfilename = ''
228    skipfunctions = []
229    skipfuncs = []
230    onlyfuncs = []
231    include_paths = []
232    previous_context = None
233
234
235def outmess(line, flag=1):
236    global filepositiontext
237
238    if not verbose:
239        return
240    if not quiet:
241        if flag:
242            sys.stdout.write(filepositiontext)
243        sys.stdout.write(line)
244
245
246re._MAXCACHE = 50
247defaultimplicitrules = {}
248for c in "abcdefghopqrstuvwxyz$_":
249    defaultimplicitrules[c] = {'typespec': 'real'}
250for c in "ijklmn":
251    defaultimplicitrules[c] = {'typespec': 'integer'}
252badnames = {}
253invbadnames = {}
254for n in ['int', 'double', 'float', 'char', 'short', 'long', 'void', 'case', 'while',
255          'return', 'signed', 'unsigned', 'if', 'for', 'typedef', 'sizeof', 'union',
256          'struct', 'static', 'register', 'new', 'break', 'do', 'goto', 'switch',
257          'continue', 'else', 'inline', 'extern', 'delete', 'const', 'auto',
258          'len', 'rank', 'shape', 'index', 'slen', 'size', '_i',
259          'max', 'min',
260          'flen', 'fshape',
261          'string', 'complex_double', 'float_double', 'stdin', 'stderr', 'stdout',
262          'type', 'default']:
263    badnames[n] = n + '_bn'
264    invbadnames[n + '_bn'] = n
265
266
267def rmbadname1(name):
268    if name in badnames:
269        errmess(f'rmbadname1: Replacing "{name}" with "{badnames[name]}".\n')
270        return badnames[name]
271    return name
272
273
274def rmbadname(names):
275    return [rmbadname1(_m) for _m in names]
276
277
278def undo_rmbadname1(name):
279    if name in invbadnames:
280        errmess(f'undo_rmbadname1: Replacing "{name}" with "{invbadnames[name]}".\n')
281        return invbadnames[name]
282    return name
283
284
285def undo_rmbadname(names):
286    return [undo_rmbadname1(_m) for _m in names]
287
288
289_has_f_header = re.compile(r'-\*-\s*fortran\s*-\*-', re.I).search
290_has_f90_header = re.compile(r'-\*-\s*f90\s*-\*-', re.I).search
291_has_fix_header = re.compile(r'-\*-\s*fix\s*-\*-', re.I).search
292_free_f90_start = re.compile(r'[^c*]\s*[^\s\d\t]', re.I).match
293
294# Extensions
295COMMON_FREE_EXTENSIONS = ['.f90', '.f95', '.f03', '.f08']
296COMMON_FIXED_EXTENSIONS = ['.for', '.ftn', '.f77', '.f']
297
298
299def openhook(filename, mode):
300    """Ensures that filename is opened with correct encoding parameter.
301
302    This function uses charset_normalizer package, when available, for
303    determining the encoding of the file to be opened. When charset_normalizer
304    is not available, the function detects only UTF encodings, otherwise, ASCII
305    encoding is used as fallback.
306    """
307    # Reads in the entire file. Robust detection of encoding.
308    # Correctly handles comments or late stage unicode characters
309    # gh-22871
310    if charset_normalizer is not None:
311        encoding = charset_normalizer.from_path(filename).best().encoding
312    else:
313        # hint: install charset_normalizer for correct encoding handling
314        # No need to read the whole file for trying with startswith
315        nbytes = min(32, os.path.getsize(filename))
316        with open(filename, 'rb') as fhandle:
317            raw = fhandle.read(nbytes)
318            if raw.startswith(codecs.BOM_UTF8):
319                encoding = 'UTF-8-SIG'
320            elif raw.startswith((codecs.BOM_UTF32_LE, codecs.BOM_UTF32_BE)):
321                encoding = 'UTF-32'
322            elif raw.startswith((codecs.BOM_LE, codecs.BOM_BE)):
323                encoding = 'UTF-16'
324            else:
325                # Fallback, without charset_normalizer
326                encoding = 'ascii'
327    return open(filename, mode, encoding=encoding)
328
329
330def is_free_format(fname):
331    """Check if file is in free format Fortran."""
332    # f90 allows both fixed and free format, assuming fixed unless
333    # signs of free format are detected.
334    result = False
335    if Path(fname).suffix.lower() in COMMON_FREE_EXTENSIONS:
336        result = True
337    with openhook(fname, 'r') as fhandle:
338        line = fhandle.readline()
339        n = 15  # the number of non-comment lines to scan for hints
340        if _has_f_header(line):
341            n = 0
342        elif _has_f90_header(line):
343            n = 0
344            result = True
345        while n > 0 and line:
346            if line[0] != '!' and line.strip():
347                n -= 1
348                if (line[0] != '\t' and _free_f90_start(line[:5])) or line[-2:-1] == '&':
349                    result = True
350                    break
351            line = fhandle.readline()
352    return result
353
354
355# Read fortran (77,90) code
356def readfortrancode(ffile, dowithline=show, istop=1):
357    """
358    Read fortran codes from files and
359     1) Get rid of comments, line continuations, and empty lines; lower cases.
360     2) Call dowithline(line) on every line.
361     3) Recursively call itself when statement \"include '<filename>'\" is met.
362    """
363    global gotnextfile, filepositiontext, currentfilename, sourcecodeform, strictf77
364    global beginpattern, quiet, verbose, dolowercase, include_paths
365
366    if not istop:
367        saveglobals = gotnextfile, filepositiontext, currentfilename, sourcecodeform, strictf77,\
368            beginpattern, quiet, verbose, dolowercase
369    if ffile == []:
370        return
371    localdolowercase = dolowercase
372    # cont: set to True when the content of the last line read
373    # indicates statement continuation
374    cont = False
375    finalline = ''
376    ll = ''
377    includeline = re.compile(
378        r'\s*include\s*(\'|")(?P<name>[^\'"]*)(\'|")', re.I)
379    cont1 = re.compile(r'(?P<line>.*)&\s*\Z')
380    cont2 = re.compile(r'(\s*&|)(?P<line>.*)')
381    mline_mark = re.compile(r".*?'''")
382    if istop:
383        dowithline('', -1)
384    ll, l1 = '', ''
385    spacedigits = [' '] + [str(_m) for _m in range(10)]
386    filepositiontext = ''
387    fin = fileinput.FileInput(ffile, openhook=openhook)
388    while True:
389        try:
390            l = fin.readline()
391        except UnicodeDecodeError as msg:
392            raise Exception(
393                f'readfortrancode: reading {fin.filename()}#{fin.lineno()}'
394                f' failed with\n{msg}.\nIt is likely that installing charset_normalizer'
395                ' package will help f2py determine the input file encoding'
396                ' correctly.')
397        if not l:
398            break
399        if fin.isfirstline():
400            filepositiontext = ''
401            currentfilename = fin.filename()
402            gotnextfile = 1
403            l1 = l
404            strictf77 = 0
405            sourcecodeform = 'fix'
406            ext = os.path.splitext(currentfilename)[1]
407            if Path(currentfilename).suffix.lower() in COMMON_FIXED_EXTENSIONS and \
408                    not (_has_f90_header(l) or _has_fix_header(l)):
409                strictf77 = 1
410            elif is_free_format(currentfilename) and not _has_fix_header(l):
411                sourcecodeform = 'free'
412            if strictf77:
413                beginpattern = beginpattern77
414            else:
415                beginpattern = beginpattern90
416            outmess('\tReading file %s (format:%s%s)\n'
417                    % (repr(currentfilename), sourcecodeform,
418                       (strictf77 and ',strict') or ''))
419
420        l = l.expandtabs().replace('\xa0', ' ')
421        # Get rid of newline characters
422        while not l == '':
423            if l[-1] not in "\n\r\f":
424                break
425            l = l[:-1]
426        # Do not lower for directives, gh-2547, gh-27697, gh-26681
427        is_f2py_directive = False
428        # Unconditionally remove comments
429        (l, rl) = split_by_unquoted(l, '!')
430        l += ' '
431        if rl[:5].lower() == '!f2py':  # f2py directive
432            l, _ = split_by_unquoted(l + 4 * ' ' + rl[5:], '!')
433            is_f2py_directive = True
434        if l.strip() == '':  # Skip empty line
435            if sourcecodeform == 'free':
436                # In free form, a statement continues in the next line
437                # that is not a comment line [3.3.2.4^1], lines with
438                # blanks are comment lines [3.3.2.3^1]. Hence, the
439                # line continuation flag must retain its state.
440                pass
441            else:
442                # In fixed form, statement continuation is determined
443                # by a non-blank character at the 6-th position. Empty
444                # line indicates a start of a new statement
445                # [3.3.3.3^1]. Hence, the line continuation flag must
446                # be reset.
447                cont = False
448            continue
449        if sourcecodeform == 'fix':
450            if l[0] in ['*', 'c', '!', 'C', '#']:
451                if l[1:5].lower() == 'f2py':  # f2py directive
452                    l = '     ' + l[5:]
453                    is_f2py_directive = True
454                else:  # Skip comment line
455                    cont = False
456                    is_f2py_directive = False
457                    continue
458            elif strictf77:
459                if len(l) > 72:
460                    l = l[:72]
461            if l[0] not in spacedigits:
462                raise Exception('readfortrancode: Found non-(space,digit) char '
463                                'in the first column.\n\tAre you sure that '
464                                'this code is in fix form?\n\tline=%s' % repr(l))
465
466            if (not cont or strictf77) and (len(l) > 5 and not l[5] == ' '):
467                # Continuation of a previous line
468                ll = ll + l[6:]
469                finalline = ''
470                origfinalline = ''
471            else:
472                r = cont1.match(l)
473                if r:
474                    l = r.group('line')  # Continuation follows ..
475                if cont:
476                    ll = ll + cont2.match(l).group('line')
477                    finalline = ''
478                    origfinalline = ''
479                else:
480                    # clean up line beginning from possible digits.
481                    l = '     ' + l[5:]
482                    # f2py directives are already stripped by this point
483                    if localdolowercase:
484                        finalline = ll.lower()
485                    else:
486                        finalline = ll
487                    origfinalline = ll
488                    ll = l
489
490        elif sourcecodeform == 'free':
491            if not cont and ext == '.pyf' and mline_mark.match(l):
492                l = l + '\n'
493                while True:
494                    lc = fin.readline()
495                    if not lc:
496                        errmess(
497                            'Unexpected end of file when reading multiline\n')
498                        break
499                    l = l + lc
500                    if mline_mark.match(lc):
501                        break
502                l = l.rstrip()
503            r = cont1.match(l)
504            if r:
505                l = r.group('line')  # Continuation follows ..
506            if cont:
507                ll = ll + cont2.match(l).group('line')
508                finalline = ''
509                origfinalline = ''
510            else:
511                if localdolowercase:
512                    # only skip lowering for C style constructs
513                    # gh-2547, gh-27697, gh-26681, gh-28014
514                    finalline = ll.lower() if not (is_f2py_directive and iscstyledirective(ll)) else ll
515                else:
516                    finalline = ll
517                origfinalline = ll
518                ll = l
519            cont = (r is not None)
520        else:
521            raise ValueError(
522                f"Flag sourcecodeform must be either 'fix' or 'free': {repr(sourcecodeform)}")
523        filepositiontext = 'Line #%d in %s:"%s"\n\t' % (
524            fin.filelineno() - 1, currentfilename, l1)
525        m = includeline.match(origfinalline)
526        if m:
527            fn = m.group('name')
528            if os.path.isfile(fn):
529                readfortrancode(fn, dowithline=dowithline, istop=0)
530            else:
531                include_dirs = [
532                    os.path.dirname(currentfilename)] + include_paths
533                foundfile = 0
534                for inc_dir in include_dirs:
535                    fn1 = os.path.join(inc_dir, fn)
536                    if os.path.isfile(fn1):
537                        foundfile = 1
538                        readfortrancode(fn1, dowithline=dowithline, istop=0)
539                        break
540                if not foundfile:
541                    outmess('readfortrancode: could not find include file %s in %s. Ignoring.\n' % (
542                        repr(fn), os.pathsep.join(include_dirs)))
543        else:
544            dowithline(finalline)
545        l1 = ll
546    # Last line should never have an f2py directive anyway
547    if localdolowercase:
548        finalline = ll.lower()
549    else:
550        finalline = ll
551    origfinalline = ll
552    filepositiontext = 'Line #%d in %s:"%s"\n\t' % (
553        fin.filelineno() - 1, currentfilename, l1)
554    m = includeline.match(origfinalline)
555    if m:
556        fn = m.group('name')
557        if os.path.isfile(fn):
558            readfortrancode(fn, dowithline=dowithline, istop=0)
559        else:
560            include_dirs = [os.path.dirname(currentfilename)] + include_paths
561            foundfile = 0
562            for inc_dir in include_dirs:
563                fn1 = os.path.join(inc_dir, fn)
564                if os.path.isfile(fn1):
565                    foundfile = 1
566                    readfortrancode(fn1, dowithline=dowithline, istop=0)
567                    break
568            if not foundfile:
569                outmess('readfortrancode: could not find include file %s in %s. Ignoring.\n' % (
570                    repr(fn), os.pathsep.join(include_dirs)))
571    else:
572        dowithline(finalline)
573    filepositiontext = ''
574    fin.close()
575    if istop:
576        dowithline('', 1)
577    else:
578        gotnextfile, filepositiontext, currentfilename, sourcecodeform, strictf77,\
579            beginpattern, quiet, verbose, dolowercase = saveglobals
580
581
582# Crack line
583beforethisafter = r'\s*(?P<before>%s(?=\s*(\b(%s)\b)))'\
584    r'\s*(?P<this>(\b(%s)\b))'\
585    r'\s*(?P<after>%s)\s*\Z'
586##
587fortrantypes = r'character|logical|integer|real|complex|double\s*(precision\s*(complex|)|complex)|type(?=\s*\([\w\s,=(*)]*\))|byte'
588typespattern = re.compile(
589    beforethisafter % ('', fortrantypes, fortrantypes, '.*'), re.I), 'type'
590typespattern4implicit = re.compile(beforethisafter % (
591    '', fortrantypes + '|static|automatic|undefined', fortrantypes + '|static|automatic|undefined', '.*'), re.I)
592#
593functionpattern = re.compile(beforethisafter % (
594    r'([a-z]+[\w\s(=*+-/)]*?|)', 'function', 'function', '.*'), re.I), 'begin'
595subroutinepattern = re.compile(beforethisafter % (
596    r'[a-z\s]*?', 'subroutine', 'subroutine', '.*'), re.I), 'begin'
597# modulepattern=re.compile(beforethisafter%('[a-z\s]*?','module','module','.*'),re.I),'begin'
598#
599groupbegins77 = r'program|block\s*data'
600beginpattern77 = re.compile(
601    beforethisafter % ('', groupbegins77, groupbegins77, '.*'), re.I), 'begin'
602groupbegins90 = groupbegins77 + \
603    r'|module(?!\s*procedure)|python\s*module|(abstract|)\s*interface|'\
604    r'type(?!\s*\()'
605beginpattern90 = re.compile(
606    beforethisafter % ('', groupbegins90, groupbegins90, '.*'), re.I), 'begin'
607groupends = (r'end|endprogram|endblockdata|endmodule|endpythonmodule|'
608             r'endinterface|endsubroutine|endfunction')
609endpattern = re.compile(
610    beforethisafter % ('', groupends, groupends, '.*'), re.I), 'end'
611# block, the Fortran 2008 construct needs special handling in the rest of the file
612endifs = r'end\s*(if|do|where|select|while|forall|associate|'\
613         r'critical|enum|team)'
614endifpattern = re.compile(
615    beforethisafter % (r'[\w]*?', endifs, endifs, '.*'), re.I), 'endif'
616#
617moduleprocedures = r'module\s*procedure'
618moduleprocedurepattern = re.compile(
619    beforethisafter % ('', moduleprocedures, moduleprocedures, '.*'), re.I), \
620    'moduleprocedure'
621implicitpattern = re.compile(
622    beforethisafter % ('', 'implicit', 'implicit', '.*'), re.I), 'implicit'
623dimensionpattern = re.compile(beforethisafter % (
624    '', 'dimension|virtual', 'dimension|virtual', '.*'), re.I), 'dimension'
625externalpattern = re.compile(
626    beforethisafter % ('', 'external', 'external', '.*'), re.I), 'external'
627optionalpattern = re.compile(
628    beforethisafter % ('', 'optional', 'optional', '.*'), re.I), 'optional'
629requiredpattern = re.compile(
630    beforethisafter % ('', 'required', 'required', '.*'), re.I), 'required'
631publicpattern = re.compile(
632    beforethisafter % ('', 'public', 'public', '.*'), re.I), 'public'
633privatepattern = re.compile(
634    beforethisafter % ('', 'private', 'private', '.*'), re.I), 'private'
635intrinsicpattern = re.compile(
636    beforethisafter % ('', 'intrinsic', 'intrinsic', '.*'), re.I), 'intrinsic'
637intentpattern = re.compile(beforethisafter % (
638    '', 'intent|depend|note|check', 'intent|depend|note|check', r'\s*\(.*?\).*'), re.I), 'intent'
639parameterpattern = re.compile(
640    beforethisafter % ('', 'parameter', 'parameter', r'\s*\(.*'), re.I), 'parameter'
641datapattern = re.compile(
642    beforethisafter % ('', 'data', 'data', '.*'), re.I), 'data'
643callpattern = re.compile(
644    beforethisafter % ('', 'call', 'call', '.*'), re.I), 'call'
645entrypattern = re.compile(
646    beforethisafter % ('', 'entry', 'entry', '.*'), re.I), 'entry'
647callfunpattern = re.compile(
648    beforethisafter % ('', 'callfun', 'callfun', '.*'), re.I), 'callfun'
649commonpattern = re.compile(
650    beforethisafter % ('', 'common', 'common', '.*'), re.I), 'common'
651usepattern = re.compile(
652    beforethisafter % ('', 'use', 'use', '.*'), re.I), 'use'
653containspattern = re.compile(
654    beforethisafter % ('', 'contains', 'contains', ''), re.I), 'contains'
655formatpattern = re.compile(
656    beforethisafter % ('', 'format', 'format', '.*'), re.I), 'format'
657# Non-fortran and f2py-specific statements
658f2pyenhancementspattern = re.compile(beforethisafter % ('', 'threadsafe|fortranname|callstatement|callprotoargument|usercode|pymethoddef',
659                                                        'threadsafe|fortranname|callstatement|callprotoargument|usercode|pymethoddef', '.*'), re.I | re.S), 'f2pyenhancements'
660multilinepattern = re.compile(
661    r"\s*(?P<before>''')(?P<this>.*?)(?P<after>''')\s*\Z", re.S), 'multiline'
662##
663
664def split_by_unquoted(line, characters):
665    """
666    Splits the line into (line[:i], line[i:]),
667    where i is the index of first occurrence of one of the characters
668    not within quotes, or len(line) if no such index exists
669    """
670    assert not (set('"\'') & set(characters)), "cannot split by unquoted quotes"
671    r = re.compile(
672        r"\A(?P<before>({single_quoted}|{double_quoted}|{not_quoted})*)"
673        r"(?P<after>{char}.*)\Z".format(
674            not_quoted=f"[^\"'{re.escape(characters)}]",
675            char=f"[{re.escape(characters)}]",
676            single_quoted=r"('([^'\\]|(\\.))*')",
677            double_quoted=r'("([^"\\]|(\\.))*")'))
678    m = r.match(line)
679    if m:
680        d = m.groupdict()
681        return (d["before"], d["after"])
682    return (line, "")
683
684def _simplifyargs(argsline):
685    a = []
686    for n in markoutercomma(argsline).split('@,@'):
687        for r in '(),':
688            n = n.replace(r, '_')
689        a.append(n)
690    return ','.join(a)
691
692
693crackline_re_1 = re.compile(r'\s*(?P<result>\b[a-z]+\w*\b)\s*=.*', re.I)
694crackline_bind_1 = re.compile(r'\s*(?P<bind>\b[a-z]+\w*\b)\s*=.*', re.I)
695crackline_bindlang = re.compile(r'\s*bind\(\s*(?P<lang>[^,]+)\s*,\s*name\s*=\s*"(?P<lang_name>[^"]+)"\s*\)', re.I)
696
697def crackline(line, reset=0):
698    """
699    reset=-1  --- initialize
700    reset=0   --- crack the line
701    reset=1   --- final check if mismatch of blocks occurred
702
703    Cracked data is saved in grouplist[0].
704    """
705    global beginpattern, groupcounter, groupname, groupcache, grouplist
706    global filepositiontext, currentfilename, neededmodule, expectbegin
707    global skipblocksuntil, skipemptyends, previous_context, gotnextfile
708
709    _, has_semicolon = split_by_unquoted(line, ";")
710    if has_semicolon and not (f2pyenhancementspattern[0].match(line) or
711                               multilinepattern[0].match(line)):
712        # XXX: non-zero reset values need testing
713        assert reset == 0, repr(reset)
714        # split line on unquoted semicolons
715        line, semicolon_line = split_by_unquoted(line, ";")
716        while semicolon_line:
717            crackline(line, reset)
718            line, semicolon_line = split_by_unquoted(semicolon_line[1:], ";")
719        crackline(line, reset)
720        return
721    if reset < 0:
722        groupcounter = 0
723        groupname = {groupcounter: ''}
724        groupcache = {groupcounter: {}}
725        grouplist = {groupcounter: []}
726        groupcache[groupcounter]['body'] = []
727        groupcache[groupcounter]['vars'] = {}
728        groupcache[groupcounter]['block'] = ''
729        groupcache[groupcounter]['name'] = ''
730        neededmodule = -1
731        skipblocksuntil = -1
732        return
733    if reset > 0:
734        fl = 0
735        if f77modulename and neededmodule == groupcounter:
736            fl = 2
737        while groupcounter > fl:
738            outmess('crackline: groupcounter=%s groupname=%s\n' %
739                    (repr(groupcounter), repr(groupname)))
740            outmess(
741                'crackline: Mismatch of blocks encountered. Trying to fix it by assuming "end" statement.\n')
742            grouplist[groupcounter - 1].append(groupcache[groupcounter])
743            grouplist[groupcounter - 1][-1]['body'] = grouplist[groupcounter]
744            del grouplist[groupcounter]
745            groupcounter = groupcounter - 1
746        if f77modulename and neededmodule == groupcounter:
747            grouplist[groupcounter - 1].append(groupcache[groupcounter])
748            grouplist[groupcounter - 1][-1]['body'] = grouplist[groupcounter]
749            del grouplist[groupcounter]
750            groupcounter = groupcounter - 1  # end interface
751            grouplist[groupcounter - 1].append(groupcache[groupcounter])
752            grouplist[groupcounter - 1][-1]['body'] = grouplist[groupcounter]
753            del grouplist[groupcounter]
754            groupcounter = groupcounter - 1  # end module
755            neededmodule = -1
756        return
757    if line == '':
758        return
759    flag = 0
760    for pat in [dimensionpattern, externalpattern, intentpattern, optionalpattern,
761                requiredpattern,
762                parameterpattern, datapattern, publicpattern, privatepattern,
763                intrinsicpattern,
764                endifpattern, endpattern,
765                formatpattern,
766                beginpattern, functionpattern, subroutinepattern,
767                implicitpattern, typespattern, commonpattern,
768                callpattern, usepattern, containspattern,
769                entrypattern,
770                f2pyenhancementspattern,
771                multilinepattern,
772                moduleprocedurepattern
773                ]:
774        m = pat[0].match(line)
775        if m:
776            break
777        flag = flag + 1
778    if not m:
779        re_1 = crackline_re_1
780        if 0 <= skipblocksuntil <= groupcounter:
781            return
782        if 'externals' in groupcache[groupcounter]:
783            for name in groupcache[groupcounter]['externals']:
784                if name in invbadnames:
785                    name = invbadnames[name]
786                if 'interfaced' in groupcache[groupcounter] and name in groupcache[groupcounter]['interfaced']:
787                    continue
788                m1 = re.match(
789                    r'(?P<before>[^"]*)\b%s\b\s*@\(@(?P<args>[^@]*)@\)@.*\Z' % name, markouterparen(line), re.I)
790                if m1:
791                    m2 = re_1.match(m1.group('before'))
792                    a = _simplifyargs(m1.group('args'))
793                    if m2:
794                        line = f"callfun {name}({a}) result ({m2.group('result')})"
795                    else:
796                        line = f'callfun {name}({a})'
797                    m = callfunpattern[0].match(line)
798                    if not m:
799                        outmess(
800                            f'crackline: could not resolve function call for line={repr(line)}.\n')
801                        return
802                    analyzeline(m, 'callfun', line)
803                    return
804        if verbose > 1 or (verbose == 1 and currentfilename.lower().endswith('.pyf')):
805            previous_context = None
806            outmess('crackline:%d: No pattern for line\n' % (groupcounter))
807        return
808    elif pat[1] == 'end':
809        if 0 <= skipblocksuntil < groupcounter:
810            groupcounter = groupcounter - 1
811            if skipblocksuntil <= groupcounter:
812                return
813        if groupcounter <= 0:
814            raise Exception('crackline: groupcounter(=%s) is nonpositive. '
815                            'Check the blocks.'
816                            % (groupcounter))
817        m1 = beginpattern[0].match(line)
818        if (m1) and (not m1.group('this') == groupname[groupcounter]):
819            raise Exception('crackline: End group %s does not match with '
820                            'previous Begin group %s\n\t%s' %
821                            (repr(m1.group('this')), repr(groupname[groupcounter]),
822                             filepositiontext)
823                            )
824        if skipblocksuntil == groupcounter:
825            skipblocksuntil = -1
826        grouplist[groupcounter - 1].append(groupcache[groupcounter])
827        grouplist[groupcounter - 1][-1]['body'] = grouplist[groupcounter]
828        del grouplist[groupcounter]
829        groupcounter = groupcounter - 1
830        if not skipemptyends:
831            expectbegin = 1
832    elif pat[1] == 'begin':
833        if 0 <= skipblocksuntil <= groupcounter:
834            groupcounter = groupcounter + 1
835            return
836        gotnextfile = 0
837        analyzeline(m, pat[1], line)
838        expectbegin = 0
839    elif pat[1] == 'endif':
840        pass
841    elif pat[1] == 'moduleprocedure':
842        analyzeline(m, pat[1], line)
843    elif pat[1] == 'contains':
844        if ignorecontains:
845            return
846        if 0 <= skipblocksuntil <= groupcounter:
847            return
848        skipblocksuntil = groupcounter
849    else:
850        if 0 <= skipblocksuntil <= groupcounter:
851            return
852        analyzeline(m, pat[1], line)
853
854
855def markouterparen(line):
856    l = ''
857    f = 0
858    for c in line:
859        if c == '(':
860            f = f + 1
861            if f == 1:
862                l = l + '@(@'
863                continue
864        elif c == ')':
865            f = f - 1
866            if f == 0:
867                l = l + '@)@'
868                continue
869        l = l + c
870    return l
871
872
873def markoutercomma(line, comma=','):
874    l = ''
875    f = 0
876    before, after = split_by_unquoted(line, comma + '()')
877    l += before
878    while after:
879        if (after[0] == comma) and (f == 0):
880            l += '@' + comma + '@'
881        else:
882            l += after[0]
883            if after[0] == '(':
884                f += 1
885            elif after[0] == ')':
886                f -= 1
887        before, after = split_by_unquoted(after[1:], comma + '()')
888        l += before
889    assert not f, repr((f, line, l))
890    return l
891
892def unmarkouterparen(line):
893    r = line.replace('@(@', '(').replace('@)@', ')')
894    return r
895
896
897def appenddecl(decl, decl2, force=1):
898    if not decl:
899        decl = {}
900    if not decl2:
901        return decl
902    if decl is decl2:
903        return decl
904    for k in list(decl2.keys()):
905        if k == 'typespec':
906            if force or k not in decl:
907                decl[k] = decl2[k]
908        elif k == 'attrspec':
909            for l in decl2[k]:
910                decl = setattrspec(decl, l, force)
911        elif k == 'kindselector':
912            decl = setkindselector(decl, decl2[k], force)
913        elif k == 'charselector':
914            decl = setcharselector(decl, decl2[k], force)
915        elif k in ['=', 'typename']:
916            if force or k not in decl:
917                decl[k] = decl2[k]
918        elif k == 'note':
919            pass
920        elif k in ['intent', 'check', 'dimension', 'optional',
921                   'required', 'depend']:
922            errmess(f'appenddecl: "{k}" not implemented.\n')
923        else:
924            raise Exception('appenddecl: Unknown variable definition key: ' +
925                            str(k))
926    return decl
927
928
929selectpattern = re.compile(
930    r'\s*(?P<this>(@\(@.*?@\)@|\*[\d*]+|\*\s*@\(@.*?@\)@|))(?P<after>.*)\Z', re.I)
931typedefpattern = re.compile(
932    r'(?:,(?P<attributes>[\w(),]+))?(::)?(?P<name>\b[a-z$_][\w$]*\b)'
933    r'(?:\((?P<params>[\w,]*)\))?\Z', re.I)
934nameargspattern = re.compile(
935    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)
936operatorpattern = re.compile(
937    r'\s*(?P<scheme>(operator|assignment))'
938    r'@\(@\s*(?P<name>[^)]+)\s*@\)@\s*\Z', re.I)
939callnameargspattern = re.compile(
940    r'\s*(?P<name>\b[\w$]+\b)\s*@\(@\s*(?P<args>.*)\s*@\)@\s*\Z', re.I)
941real16pattern = re.compile(
942    r'([-+]?(?:\d+(?:\.\d*)?|\d*\.\d+))[dD]((?:[-+]?\d+)?)')
943real8pattern = re.compile(
944    r'([-+]?((?:\d+(?:\.\d*)?|\d*\.\d+))[eE]((?:[-+]?\d+)?)|(\d+\.\d*))')
945
946_intentcallbackpattern = re.compile(r'intent\s*\(.*?\bcallback\b', re.I)
947
948
949def _is_intent_callback(vdecl):
950    for a in vdecl.get('attrspec', []):
951        if _intentcallbackpattern.match(a):
952            return 1
953    return 0
954
955
956def _resolvetypedefpattern(line):
957    line = ''.join(line.split())  # removes whitespace
958    m1 = typedefpattern.match(line)
959    print(line, m1)
960    if m1:
961        attrs = m1.group('attributes')
962        attrs = [a.lower() for a in attrs.split(',')] if attrs else []
963        return m1.group('name'), attrs, m1.group('params')
964    return None, [], None
965
966def parse_name_for_bind(line):
967    pattern = re.compile(r'bind\(\s*(?P<lang>[^,]+)(?:\s*,\s*name\s*=\s*["\'](?P<name>[^"\']+)["\']\s*)?\)', re.I)
968    match = pattern.search(line)
969    bind_statement = None
970    if match:
971        bind_statement = match.group(0)
972        # Remove the 'bind' construct from the line.
973        line = line[:match.start()] + line[match.end():]
974    return line, bind_statement
975
976def _resolvenameargspattern(line):
977    line, bind_cname = parse_name_for_bind(line)
978    line = markouterparen(line)
979    m1 = nameargspattern.match(line)
980    if m1:
981        return m1.group('name'), m1.group('args'), m1.group('result'), bind_cname
982    m1 = operatorpattern.match(line)
983    if m1:
984        name = m1.group('scheme') + '(' + m1.group('name') + ')'
985        return name, [], None, None
986    m1 = callnameargspattern.match(line)
987    if m1:
988        return m1.group('name'), m1.group('args'), None, None
989    return None, [], None, None
990
991
992def analyzeline(m, case, line):
993    """
994    Reads each line in the input file in sequence and updates global vars.
995
996    Effectively reads and collects information from the input file to the
997    global variable groupcache, a dictionary containing info about each part
998    of the fortran module.
999
1000    At the end of analyzeline, information is filtered into the correct dict
1001    keys, but parameter values and dimensions are not yet interpreted.
1002    """
1003    global groupcounter, groupname, groupcache, grouplist, filepositiontext
1004    global currentfilename, f77modulename, neededinterface, neededmodule
1005    global expectbegin, gotnextfile, previous_context
1006
1007    block = m.group('this')
1008    if case != 'multiline':
1009        previous_context = None
1010    if expectbegin and case not in ['begin', 'call', 'callfun', 'type'] \
1011       and not skipemptyends and groupcounter < 1:
1012        newname = os.path.basename(currentfilename).split('.')[0]
1013        outmess(
1014            f'analyzeline: no group yet. Creating program group with name "{newname}".\n')
1015        gotnextfile = 0
1016        groupcounter = groupcounter + 1
1017        groupname[groupcounter] = 'program'
1018        groupcache[groupcounter] = {}
1019        grouplist[groupcounter] = []
1020        groupcache[groupcounter]['body'] = []
1021        groupcache[groupcounter]['vars'] = {}
1022        groupcache[groupcounter]['block'] = 'program'
1023        groupcache[groupcounter]['name'] = newname
1024        groupcache[groupcounter]['from'] = 'fromsky'
1025        expectbegin = 0
1026    if case in ['begin', 'call', 'callfun']:
1027        # Crack line => block,name,args,result
1028        block = block.lower()
1029        if re.match(r'block\s*data', block, re.I):
1030            block = 'block data'
1031        elif re.match(r'python\s*module', block, re.I):
1032            block = 'python module'
1033        elif re.match(r'abstract\s*interface', block, re.I):
1034            block = 'abstract interface'
1035        if block == 'type':
1036            name, attrs, _ = _resolvetypedefpattern(m.group('after'))
1037            groupcache[groupcounter]['vars'][name] = {'attrspec': attrs}
1038            args = []
1039            result = None
1040        else:
1041            name, args, result, bindcline = _resolvenameargspattern(m.group('after'))
1042        if name is None:
1043            if block == 'block data':
1044                name = '_BLOCK_DATA_'
1045            else:
1046                name = ''
1047            if block not in ['interface', 'block data', 'abstract interface']:
1048                outmess('analyzeline: No name/args pattern found for line.\n')
1049
1050        previous_context = (block, name, groupcounter)
1051        if args:
1052            args = rmbadname([x.strip()
1053                              for x in markoutercomma(args).split('@,@')])
1054        else:
1055            args = []
1056        if '' in args:
1057            while '' in args:
1058                args.remove('')
1059            outmess(
1060                'analyzeline: argument list is malformed (missing argument).\n')
1061
1062        # end of crack line => block,name,args,result
1063        needmodule = 0
1064        needinterface = 0
1065
1066        if case in ['call', 'callfun']:
1067            needinterface = 1
1068            if 'args' not in groupcache[groupcounter]:
1069                return
1070            if name not in groupcache[groupcounter]['args']:
1071                return
1072            for it in grouplist[groupcounter]:
1073                if it['name'] == name:
1074                    return
1075            if name in groupcache[groupcounter]['interfaced']:
1076                return
1077            block = {'call': 'subroutine', 'callfun': 'function'}[case]
1078        if f77modulename and neededmodule == -1 and groupcounter <= 1:
1079            neededmodule = groupcounter + 2
1080            needmodule = 1
1081            if block not in ['interface', 'abstract interface']:
1082                needinterface = 1
1083        # Create new block(s)
1084        groupcounter = groupcounter + 1
1085        groupcache[groupcounter] = {}
1086        grouplist[groupcounter] = []
1087        if needmodule:
1088            if verbose > 1:
1089                outmess('analyzeline: Creating module block %s\n' %
1090                        repr(f77modulename), 0)
1091            groupname[groupcounter] = 'module'
1092            groupcache[groupcounter]['block'] = 'python module'
1093            groupcache[groupcounter]['name'] = f77modulename
1094            groupcache[groupcounter]['from'] = ''
1095            groupcache[groupcounter]['body'] = []
1096            groupcache[groupcounter]['externals'] = []
1097            groupcache[groupcounter]['interfaced'] = []
1098            groupcache[groupcounter]['vars'] = {}
1099            groupcounter = groupcounter + 1
1100            groupcache[groupcounter] = {}
1101            grouplist[groupcounter] = []
1102        if needinterface:
1103            if verbose > 1:
1104                outmess('analyzeline: Creating additional interface block (groupcounter=%s).\n' % (
1105                    groupcounter), 0)
1106            groupname[groupcounter] = 'interface'
1107            groupcache[groupcounter]['block'] = 'interface'
1108            groupcache[groupcounter]['name'] = 'unknown_interface'
1109            groupcache[groupcounter]['from'] = '%s:%s' % (
1110                groupcache[groupcounter - 1]['from'], groupcache[groupcounter - 1]['name'])
1111            groupcache[groupcounter]['body'] = []
1112            groupcache[groupcounter]['externals'] = []
1113            groupcache[groupcounter]['interfaced'] = []
1114            groupcache[groupcounter]['vars'] = {}
1115            groupcounter = groupcounter + 1
1116            groupcache[groupcounter] = {}
1117            grouplist[groupcounter] = []
1118        groupname[groupcounter] = block
1119        groupcache[groupcounter]['block'] = block
1120        if not name:
1121            name = 'unknown_' + block.replace(' ', '_')
1122        groupcache[groupcounter]['prefix'] = m.group('before')
1123        groupcache[groupcounter]['name'] = rmbadname1(name)
1124        groupcache[groupcounter]['result'] = result
1125        if groupcounter == 1:
1126            groupcache[groupcounter]['from'] = currentfilename
1127        elif f77modulename and groupcounter == 3:
1128            groupcache[groupcounter]['from'] = '%s:%s' % (
1129                groupcache[groupcounter - 1]['from'], currentfilename)
1130        else:
1131            groupcache[groupcounter]['from'] = '%s:%s' % (
1132                groupcache[groupcounter - 1]['from'], groupcache[groupcounter - 1]['name'])
1133        for k in list(groupcache[groupcounter].keys()):
1134            if not groupcache[groupcounter][k]:
1135                del groupcache[groupcounter][k]
1136
1137        groupcache[groupcounter]['args'] = args
1138        groupcache[groupcounter]['body'] = []
1139        groupcache[groupcounter]['externals'] = []
1140        groupcache[groupcounter]['interfaced'] = []
1141        groupcache[groupcounter]['vars'] = {}
1142        groupcache[groupcounter]['entry'] = {}
1143        # end of creation
1144        if block == 'type':
1145            groupcache[groupcounter]['varnames'] = []
1146
1147        if case in ['call', 'callfun']:  # set parents variables
1148            if name not in groupcache[groupcounter - 2]['externals']:
1149                groupcache[groupcounter - 2]['externals'].append(name)
1150            groupcache[groupcounter]['vars'] = copy.deepcopy(
1151                groupcache[groupcounter - 2]['vars'])
1152            try:
1153                del groupcache[groupcounter]['vars'][name][
1154                    groupcache[groupcounter]['vars'][name]['attrspec'].index('external')]
1155            except Exception:
1156                pass
1157        if block in ['function', 'subroutine']:  # set global attributes
1158            # name is fortran name
1159            if bindcline:
1160                bindcdat = re.search(crackline_bindlang, bindcline)
1161                if bindcdat:
1162                    groupcache[groupcounter]['bindlang'] = {name: {}}
1163                    groupcache[groupcounter]['bindlang'][name]["lang"] = bindcdat.group('lang')
1164                    if bindcdat.group('lang_name'):
1165                        groupcache[groupcounter]['bindlang'][name]["name"] = bindcdat.group('lang_name')
1166            try:
1167                groupcache[groupcounter]['vars'][name] = appenddecl(
1168                    groupcache[groupcounter]['vars'][name], groupcache[groupcounter - 2]['vars'][''])
1169            except Exception:
1170                pass
1171            if case == 'callfun':  # return type
1172                if result and result in groupcache[groupcounter]['vars']:
1173                    if not name == result:
1174                        groupcache[groupcounter]['vars'][name] = appenddecl(
1175                            groupcache[groupcounter]['vars'][name], groupcache[groupcounter]['vars'][result])
1176            # if groupcounter>1: # name is interfaced
1177            try:
1178                groupcache[groupcounter - 2]['interfaced'].append(name)
1179            except Exception:
1180                pass
1181        if block == 'function':
1182            t = typespattern[0].match(m.group('before') + ' ' + name)
1183            if t:
1184                typespec, selector, attr, edecl = cracktypespec0(
1185                    t.group('this'), t.group('after'))
1186                updatevars(typespec, selector, attr, edecl)
1187
1188        if case in ['call', 'callfun']:
1189            grouplist[groupcounter - 1].append(groupcache[groupcounter])
1190            grouplist[groupcounter - 1][-1]['body'] = grouplist[groupcounter]
1191            del grouplist[groupcounter]
1192            groupcounter = groupcounter - 1  # end routine
1193            grouplist[groupcounter - 1].append(groupcache[groupcounter])
1194            grouplist[groupcounter - 1][-1]['body'] = grouplist[groupcounter]
1195            del grouplist[groupcounter]
1196            groupcounter = groupcounter - 1  # end interface
1197
1198    elif case == 'entry':
1199        name, args, result, _ = _resolvenameargspattern(m.group('after'))
1200        if name is not None:

Showing the first 1,200 of 3726 lines. Download the file for the rest.

codekingpro/portable-devtools · Team Ai