PageRenderTime 64ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/numpy/f2py/crackfortran.py

http://github.com/numpy/numpy
Python | 3347 lines | 3320 code | 0 blank | 27 comment | 7 complexity | 39c963dad02652ef6ab54954ddeb0ef2 MD5 | raw file
Possible License(s): BSD-3-Clause, JSON, Unlicense

Large files files are truncated, but you can click here to view the full file

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

Large files files are truncated, but you can click here to view the full file