crackfortran.py 136 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545
  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. 'abstract interface'
  42. B['body'] --- list containing `subblocks' with the same structure as `blocks'
  43. B['parent_block'] --- dictionary of a parent block:
  44. C['body'][<index>]['parent_block'] is C
  45. B['vars'] --- dictionary of variable definitions
  46. B['sortvars'] --- dictionary of variable definitions sorted by dependence (independent first)
  47. B['name'] --- name of the block (not if B['block']=='interface')
  48. B['prefix'] --- prefix string (only if B['block']=='function')
  49. B['args'] --- list of argument names if B['block']== 'function' | 'subroutine'
  50. B['result'] --- name of the return value (only if B['block']=='function')
  51. B['implicit'] --- dictionary {'a':<variable definition>,'b':...} | None
  52. B['externals'] --- list of variables being external
  53. B['interfaced'] --- list of variables being external and defined
  54. B['common'] --- dictionary of common blocks (list of objects)
  55. B['commonvars'] --- list of variables used in common blocks (dimensions are moved to variable definitions)
  56. B['from'] --- string showing the 'parents' of the current block
  57. B['use'] --- dictionary of modules used in current block:
  58. {<modulename>:{['only':<0|1>],['map':{<local_name1>:<use_name1>,...}]}}
  59. B['note'] --- list of LaTeX comments on the block
  60. B['f2pyenhancements'] --- optional dictionary
  61. {'threadsafe':'','fortranname':<name>,
  62. 'callstatement':<C-expr>|<multi-line block>,
  63. 'callprotoargument':<C-expr-list>,
  64. 'usercode':<multi-line block>|<list of multi-line blocks>,
  65. 'pymethoddef:<multi-line block>'
  66. }
  67. B['entry'] --- dictionary {entryname:argslist,..}
  68. B['varnames'] --- list of variable names given in the order of reading the
  69. Fortran code, useful for derived types.
  70. B['saved_interface'] --- a string of scanned routine signature, defines explicit interface
  71. *** Variable definition is a dictionary
  72. D = B['vars'][<variable name>] =
  73. {'typespec'[,'attrspec','kindselector','charselector','=','typename']}
  74. D['typespec'] = 'byte' | 'character' | 'complex' | 'double complex' |
  75. 'double precision' | 'integer' | 'logical' | 'real' | 'type'
  76. D['attrspec'] --- list of attributes (e.g. 'dimension(<arrayspec>)',
  77. 'external','intent(in|out|inout|hide|c|callback|cache|aligned4|aligned8|aligned16)',
  78. 'optional','required', etc)
  79. K = D['kindselector'] = {['*','kind']} (only if D['typespec'] =
  80. 'complex' | 'integer' | 'logical' | 'real' )
  81. C = D['charselector'] = {['*','len','kind','f2py_len']}
  82. (only if D['typespec']=='character')
  83. D['='] --- initialization expression string
  84. D['typename'] --- name of the type if D['typespec']=='type'
  85. D['dimension'] --- list of dimension bounds
  86. D['intent'] --- list of intent specifications
  87. D['depend'] --- list of variable names on which current variable depends on
  88. D['check'] --- list of C-expressions; if C-expr returns zero, exception is raised
  89. D['note'] --- list of LaTeX comments on the variable
  90. *** Meaning of kind/char selectors (few examples):
  91. D['typespec>']*K['*']
  92. D['typespec'](kind=K['kind'])
  93. character*C['*']
  94. character(len=C['len'],kind=C['kind'], f2py_len=C['f2py_len'])
  95. (see also fortran type declaration statement formats below)
  96. Fortran 90 type declaration statement format (F77 is subset of F90)
  97. ====================================================================
  98. (Main source: IBM XL Fortran 5.1 Language Reference Manual)
  99. type declaration = <typespec> [[<attrspec>]::] <entitydecl>
  100. <typespec> = byte |
  101. character[<charselector>] |
  102. complex[<kindselector>] |
  103. double complex |
  104. double precision |
  105. integer[<kindselector>] |
  106. logical[<kindselector>] |
  107. real[<kindselector>] |
  108. type(<typename>)
  109. <charselector> = * <charlen> |
  110. ([len=]<len>[,[kind=]<kind>]) |
  111. (kind=<kind>[,len=<len>])
  112. <kindselector> = * <intlen> |
  113. ([kind=]<kind>)
  114. <attrspec> = comma separated list of attributes.
  115. Only the following attributes are used in
  116. building up the interface:
  117. external
  118. (parameter --- affects '=' key)
  119. optional
  120. intent
  121. Other attributes are ignored.
  122. <intentspec> = in | out | inout
  123. <arrayspec> = comma separated list of dimension bounds.
  124. <entitydecl> = <name> [[*<charlen>][(<arrayspec>)] | [(<arrayspec>)]*<charlen>]
  125. [/<init_expr>/ | =<init_expr>] [,<entitydecl>]
  126. In addition, the following attributes are used: check,depend,note
  127. TODO:
  128. * Apply 'parameter' attribute (e.g. 'integer parameter :: i=2' 'real x(i)'
  129. -> 'real x(2)')
  130. The above may be solved by creating appropriate preprocessor program, for example.
  131. """
  132. import sys
  133. import string
  134. import fileinput
  135. import re
  136. import os
  137. import copy
  138. import platform
  139. import codecs
  140. try:
  141. import charset_normalizer
  142. except ImportError:
  143. charset_normalizer = None
  144. from . import __version__
  145. # The environment provided by auxfuncs.py is needed for some calls to eval.
  146. # As the needed functions cannot be determined by static inspection of the
  147. # code, it is safest to use import * pending a major refactoring of f2py.
  148. from .auxfuncs import *
  149. from . import symbolic
  150. f2py_version = __version__.version
  151. # Global flags:
  152. strictf77 = 1 # Ignore `!' comments unless line[0]=='!'
  153. sourcecodeform = 'fix' # 'fix','free'
  154. quiet = 0 # Be verbose if 0 (Obsolete: not used any more)
  155. verbose = 1 # Be quiet if 0, extra verbose if > 1.
  156. tabchar = 4 * ' '
  157. pyffilename = ''
  158. f77modulename = ''
  159. skipemptyends = 0 # for old F77 programs without 'program' statement
  160. ignorecontains = 1
  161. dolowercase = 1
  162. debug = []
  163. # Global variables
  164. beginpattern = ''
  165. currentfilename = ''
  166. expectbegin = 1
  167. f90modulevars = {}
  168. filepositiontext = ''
  169. gotnextfile = 1
  170. groupcache = None
  171. groupcounter = 0
  172. grouplist = {groupcounter: []}
  173. groupname = ''
  174. include_paths = []
  175. neededmodule = -1
  176. onlyfuncs = []
  177. previous_context = None
  178. skipblocksuntil = -1
  179. skipfuncs = []
  180. skipfunctions = []
  181. usermodules = []
  182. def reset_global_f2py_vars():
  183. global groupcounter, grouplist, neededmodule, expectbegin
  184. global skipblocksuntil, usermodules, f90modulevars, gotnextfile
  185. global filepositiontext, currentfilename, skipfunctions, skipfuncs
  186. global onlyfuncs, include_paths, previous_context
  187. global strictf77, sourcecodeform, quiet, verbose, tabchar, pyffilename
  188. global f77modulename, skipemptyends, ignorecontains, dolowercase, debug
  189. # flags
  190. strictf77 = 1
  191. sourcecodeform = 'fix'
  192. quiet = 0
  193. verbose = 1
  194. tabchar = 4 * ' '
  195. pyffilename = ''
  196. f77modulename = ''
  197. skipemptyends = 0
  198. ignorecontains = 1
  199. dolowercase = 1
  200. debug = []
  201. # variables
  202. groupcounter = 0
  203. grouplist = {groupcounter: []}
  204. neededmodule = -1
  205. expectbegin = 1
  206. skipblocksuntil = -1
  207. usermodules = []
  208. f90modulevars = {}
  209. gotnextfile = 1
  210. filepositiontext = ''
  211. currentfilename = ''
  212. skipfunctions = []
  213. skipfuncs = []
  214. onlyfuncs = []
  215. include_paths = []
  216. previous_context = None
  217. def outmess(line, flag=1):
  218. global filepositiontext
  219. if not verbose:
  220. return
  221. if not quiet:
  222. if flag:
  223. sys.stdout.write(filepositiontext)
  224. sys.stdout.write(line)
  225. re._MAXCACHE = 50
  226. defaultimplicitrules = {}
  227. for c in "abcdefghopqrstuvwxyz$_":
  228. defaultimplicitrules[c] = {'typespec': 'real'}
  229. for c in "ijklmn":
  230. defaultimplicitrules[c] = {'typespec': 'integer'}
  231. badnames = {}
  232. invbadnames = {}
  233. for n in ['int', 'double', 'float', 'char', 'short', 'long', 'void', 'case', 'while',
  234. 'return', 'signed', 'unsigned', 'if', 'for', 'typedef', 'sizeof', 'union',
  235. 'struct', 'static', 'register', 'new', 'break', 'do', 'goto', 'switch',
  236. 'continue', 'else', 'inline', 'extern', 'delete', 'const', 'auto',
  237. 'len', 'rank', 'shape', 'index', 'slen', 'size', '_i',
  238. 'max', 'min',
  239. 'flen', 'fshape',
  240. 'string', 'complex_double', 'float_double', 'stdin', 'stderr', 'stdout',
  241. 'type', 'default']:
  242. badnames[n] = n + '_bn'
  243. invbadnames[n + '_bn'] = n
  244. def rmbadname1(name):
  245. if name in badnames:
  246. errmess('rmbadname1: Replacing "%s" with "%s".\n' %
  247. (name, badnames[name]))
  248. return badnames[name]
  249. return name
  250. def rmbadname(names):
  251. return [rmbadname1(_m) for _m in names]
  252. def undo_rmbadname1(name):
  253. if name in invbadnames:
  254. errmess('undo_rmbadname1: Replacing "%s" with "%s".\n'
  255. % (name, invbadnames[name]))
  256. return invbadnames[name]
  257. return name
  258. def undo_rmbadname(names):
  259. return [undo_rmbadname1(_m) for _m in names]
  260. def getextension(name):
  261. i = name.rfind('.')
  262. if i == -1:
  263. return ''
  264. if '\\' in name[i:]:
  265. return ''
  266. if '/' in name[i:]:
  267. return ''
  268. return name[i + 1:]
  269. is_f_file = re.compile(r'.*\.(for|ftn|f77|f)\Z', re.I).match
  270. _has_f_header = re.compile(r'-\*-\s*fortran\s*-\*-', re.I).search
  271. _has_f90_header = re.compile(r'-\*-\s*f90\s*-\*-', re.I).search
  272. _has_fix_header = re.compile(r'-\*-\s*fix\s*-\*-', re.I).search
  273. _free_f90_start = re.compile(r'[^c*]\s*[^\s\d\t]', re.I).match
  274. def openhook(filename, mode):
  275. """Ensures that filename is opened with correct encoding parameter.
  276. This function uses charset_normalizer package, when available, for
  277. determining the encoding of the file to be opened. When charset_normalizer
  278. is not available, the function detects only UTF encodings, otherwise, ASCII
  279. encoding is used as fallback.
  280. """
  281. # Reads in the entire file. Robust detection of encoding.
  282. # Correctly handles comments or late stage unicode characters
  283. # gh-22871
  284. if charset_normalizer is not None:
  285. encoding = charset_normalizer.from_path(filename).best().encoding
  286. else:
  287. # hint: install charset_normalizer for correct encoding handling
  288. # No need to read the whole file for trying with startswith
  289. nbytes = min(32, os.path.getsize(filename))
  290. with open(filename, 'rb') as fhandle:
  291. raw = fhandle.read(nbytes)
  292. if raw.startswith(codecs.BOM_UTF8):
  293. encoding = 'UTF-8-SIG'
  294. elif raw.startswith((codecs.BOM_UTF32_LE, codecs.BOM_UTF32_BE)):
  295. encoding = 'UTF-32'
  296. elif raw.startswith((codecs.BOM_LE, codecs.BOM_BE)):
  297. encoding = 'UTF-16'
  298. else:
  299. # Fallback, without charset_normalizer
  300. encoding = 'ascii'
  301. return open(filename, mode, encoding=encoding)
  302. def is_free_format(file):
  303. """Check if file is in free format Fortran."""
  304. # f90 allows both fixed and free format, assuming fixed unless
  305. # signs of free format are detected.
  306. result = 0
  307. with openhook(file, 'r') as f:
  308. line = f.readline()
  309. n = 15 # the number of non-comment lines to scan for hints
  310. if _has_f_header(line):
  311. n = 0
  312. elif _has_f90_header(line):
  313. n = 0
  314. result = 1
  315. while n > 0 and line:
  316. if line[0] != '!' and line.strip():
  317. n -= 1
  318. if (line[0] != '\t' and _free_f90_start(line[:5])) or line[-2:-1] == '&':
  319. result = 1
  320. break
  321. line = f.readline()
  322. return result
  323. # Read fortran (77,90) code
  324. def readfortrancode(ffile, dowithline=show, istop=1):
  325. """
  326. Read fortran codes from files and
  327. 1) Get rid of comments, line continuations, and empty lines; lower cases.
  328. 2) Call dowithline(line) on every line.
  329. 3) Recursively call itself when statement \"include '<filename>'\" is met.
  330. """
  331. global gotnextfile, filepositiontext, currentfilename, sourcecodeform, strictf77
  332. global beginpattern, quiet, verbose, dolowercase, include_paths
  333. if not istop:
  334. saveglobals = gotnextfile, filepositiontext, currentfilename, sourcecodeform, strictf77,\
  335. beginpattern, quiet, verbose, dolowercase
  336. if ffile == []:
  337. return
  338. localdolowercase = dolowercase
  339. # cont: set to True when the content of the last line read
  340. # indicates statement continuation
  341. cont = False
  342. finalline = ''
  343. ll = ''
  344. includeline = re.compile(
  345. r'\s*include\s*(\'|")(?P<name>[^\'"]*)(\'|")', re.I)
  346. cont1 = re.compile(r'(?P<line>.*)&\s*\Z')
  347. cont2 = re.compile(r'(\s*&|)(?P<line>.*)')
  348. mline_mark = re.compile(r".*?'''")
  349. if istop:
  350. dowithline('', -1)
  351. ll, l1 = '', ''
  352. spacedigits = [' '] + [str(_m) for _m in range(10)]
  353. filepositiontext = ''
  354. fin = fileinput.FileInput(ffile, openhook=openhook)
  355. while True:
  356. try:
  357. l = fin.readline()
  358. except UnicodeDecodeError as msg:
  359. raise Exception(
  360. f'readfortrancode: reading {fin.filename()}#{fin.lineno()}'
  361. f' failed with\n{msg}.\nIt is likely that installing charset_normalizer'
  362. ' package will help f2py determine the input file encoding'
  363. ' correctly.')
  364. if not l:
  365. break
  366. if fin.isfirstline():
  367. filepositiontext = ''
  368. currentfilename = fin.filename()
  369. gotnextfile = 1
  370. l1 = l
  371. strictf77 = 0
  372. sourcecodeform = 'fix'
  373. ext = os.path.splitext(currentfilename)[1]
  374. if is_f_file(currentfilename) and \
  375. not (_has_f90_header(l) or _has_fix_header(l)):
  376. strictf77 = 1
  377. elif is_free_format(currentfilename) and not _has_fix_header(l):
  378. sourcecodeform = 'free'
  379. if strictf77:
  380. beginpattern = beginpattern77
  381. else:
  382. beginpattern = beginpattern90
  383. outmess('\tReading file %s (format:%s%s)\n'
  384. % (repr(currentfilename), sourcecodeform,
  385. strictf77 and ',strict' or ''))
  386. l = l.expandtabs().replace('\xa0', ' ')
  387. # Get rid of newline characters
  388. while not l == '':
  389. if l[-1] not in "\n\r\f":
  390. break
  391. l = l[:-1]
  392. if not strictf77:
  393. (l, rl) = split_by_unquoted(l, '!')
  394. l += ' '
  395. if rl[:5].lower() == '!f2py': # f2py directive
  396. l, _ = split_by_unquoted(l + 4 * ' ' + rl[5:], '!')
  397. if l.strip() == '': # Skip empty line
  398. if sourcecodeform == 'free':
  399. # In free form, a statement continues in the next line
  400. # that is not a comment line [3.3.2.4^1], lines with
  401. # blanks are comment lines [3.3.2.3^1]. Hence, the
  402. # line continuation flag must retain its state.
  403. pass
  404. else:
  405. # In fixed form, statement continuation is determined
  406. # by a non-blank character at the 6-th position. Empty
  407. # line indicates a start of a new statement
  408. # [3.3.3.3^1]. Hence, the line continuation flag must
  409. # be reset.
  410. cont = False
  411. continue
  412. if sourcecodeform == 'fix':
  413. if l[0] in ['*', 'c', '!', 'C', '#']:
  414. if l[1:5].lower() == 'f2py': # f2py directive
  415. l = ' ' + l[5:]
  416. else: # Skip comment line
  417. cont = False
  418. continue
  419. elif strictf77:
  420. if len(l) > 72:
  421. l = l[:72]
  422. if not (l[0] in spacedigits):
  423. raise Exception('readfortrancode: Found non-(space,digit) char '
  424. 'in the first column.\n\tAre you sure that '
  425. 'this code is in fix form?\n\tline=%s' % repr(l))
  426. if (not cont or strictf77) and (len(l) > 5 and not l[5] == ' '):
  427. # Continuation of a previous line
  428. ll = ll + l[6:]
  429. finalline = ''
  430. origfinalline = ''
  431. else:
  432. if not strictf77:
  433. # F90 continuation
  434. r = cont1.match(l)
  435. if r:
  436. l = r.group('line') # Continuation follows ..
  437. if cont:
  438. ll = ll + cont2.match(l).group('line')
  439. finalline = ''
  440. origfinalline = ''
  441. else:
  442. # clean up line beginning from possible digits.
  443. l = ' ' + l[5:]
  444. if localdolowercase:
  445. finalline = ll.lower()
  446. else:
  447. finalline = ll
  448. origfinalline = ll
  449. ll = l
  450. cont = (r is not None)
  451. else:
  452. # clean up line beginning from possible digits.
  453. l = ' ' + l[5:]
  454. if localdolowercase:
  455. finalline = ll.lower()
  456. else:
  457. finalline = ll
  458. origfinalline = ll
  459. ll = l
  460. elif sourcecodeform == 'free':
  461. if not cont and ext == '.pyf' and mline_mark.match(l):
  462. l = l + '\n'
  463. while True:
  464. lc = fin.readline()
  465. if not lc:
  466. errmess(
  467. 'Unexpected end of file when reading multiline\n')
  468. break
  469. l = l + lc
  470. if mline_mark.match(lc):
  471. break
  472. l = l.rstrip()
  473. r = cont1.match(l)
  474. if r:
  475. l = r.group('line') # Continuation follows ..
  476. if cont:
  477. ll = ll + cont2.match(l).group('line')
  478. finalline = ''
  479. origfinalline = ''
  480. else:
  481. if localdolowercase:
  482. finalline = ll.lower()
  483. else:
  484. finalline = ll
  485. origfinalline = ll
  486. ll = l
  487. cont = (r is not None)
  488. else:
  489. raise ValueError(
  490. "Flag sourcecodeform must be either 'fix' or 'free': %s" % repr(sourcecodeform))
  491. filepositiontext = 'Line #%d in %s:"%s"\n\t' % (
  492. fin.filelineno() - 1, currentfilename, l1)
  493. m = includeline.match(origfinalline)
  494. if m:
  495. fn = m.group('name')
  496. if os.path.isfile(fn):
  497. readfortrancode(fn, dowithline=dowithline, istop=0)
  498. else:
  499. include_dirs = [
  500. os.path.dirname(currentfilename)] + include_paths
  501. foundfile = 0
  502. for inc_dir in include_dirs:
  503. fn1 = os.path.join(inc_dir, fn)
  504. if os.path.isfile(fn1):
  505. foundfile = 1
  506. readfortrancode(fn1, dowithline=dowithline, istop=0)
  507. break
  508. if not foundfile:
  509. outmess('readfortrancode: could not find include file %s in %s. Ignoring.\n' % (
  510. repr(fn), os.pathsep.join(include_dirs)))
  511. else:
  512. dowithline(finalline)
  513. l1 = ll
  514. if localdolowercase:
  515. finalline = ll.lower()
  516. else:
  517. finalline = ll
  518. origfinalline = ll
  519. filepositiontext = 'Line #%d in %s:"%s"\n\t' % (
  520. fin.filelineno() - 1, currentfilename, l1)
  521. m = includeline.match(origfinalline)
  522. if m:
  523. fn = m.group('name')
  524. if os.path.isfile(fn):
  525. readfortrancode(fn, dowithline=dowithline, istop=0)
  526. else:
  527. include_dirs = [os.path.dirname(currentfilename)] + include_paths
  528. foundfile = 0
  529. for inc_dir in include_dirs:
  530. fn1 = os.path.join(inc_dir, fn)
  531. if os.path.isfile(fn1):
  532. foundfile = 1
  533. readfortrancode(fn1, dowithline=dowithline, istop=0)
  534. break
  535. if not foundfile:
  536. outmess('readfortrancode: could not find include file %s in %s. Ignoring.\n' % (
  537. repr(fn), os.pathsep.join(include_dirs)))
  538. else:
  539. dowithline(finalline)
  540. filepositiontext = ''
  541. fin.close()
  542. if istop:
  543. dowithline('', 1)
  544. else:
  545. gotnextfile, filepositiontext, currentfilename, sourcecodeform, strictf77,\
  546. beginpattern, quiet, verbose, dolowercase = saveglobals
  547. # Crack line
  548. beforethisafter = r'\s*(?P<before>%s(?=\s*(\b(%s)\b)))' + \
  549. r'\s*(?P<this>(\b(%s)\b))' + \
  550. r'\s*(?P<after>%s)\s*\Z'
  551. ##
  552. fortrantypes = r'character|logical|integer|real|complex|double\s*(precision\s*(complex|)|complex)|type(?=\s*\([\w\s,=(*)]*\))|byte'
  553. typespattern = re.compile(
  554. beforethisafter % ('', fortrantypes, fortrantypes, '.*'), re.I), 'type'
  555. typespattern4implicit = re.compile(beforethisafter % (
  556. '', fortrantypes + '|static|automatic|undefined', fortrantypes + '|static|automatic|undefined', '.*'), re.I)
  557. #
  558. functionpattern = re.compile(beforethisafter % (
  559. r'([a-z]+[\w\s(=*+-/)]*?|)', 'function', 'function', '.*'), re.I), 'begin'
  560. subroutinepattern = re.compile(beforethisafter % (
  561. r'[a-z\s]*?', 'subroutine', 'subroutine', '.*'), re.I), 'begin'
  562. # modulepattern=re.compile(beforethisafter%('[a-z\s]*?','module','module','.*'),re.I),'begin'
  563. #
  564. groupbegins77 = r'program|block\s*data'
  565. beginpattern77 = re.compile(
  566. beforethisafter % ('', groupbegins77, groupbegins77, '.*'), re.I), 'begin'
  567. groupbegins90 = groupbegins77 + \
  568. r'|module(?!\s*procedure)|python\s*module|(abstract|)\s*interface|' + \
  569. r'type(?!\s*\()'
  570. beginpattern90 = re.compile(
  571. beforethisafter % ('', groupbegins90, groupbegins90, '.*'), re.I), 'begin'
  572. groupends = (r'end|endprogram|endblockdata|endmodule|endpythonmodule|'
  573. r'endinterface|endsubroutine|endfunction')
  574. endpattern = re.compile(
  575. beforethisafter % ('', groupends, groupends, r'.*'), re.I), 'end'
  576. endifs = r'end\s*(if|do|where|select|while|forall|associate|block|' + \
  577. r'critical|enum|team)'
  578. endifpattern = re.compile(
  579. beforethisafter % (r'[\w]*?', endifs, endifs, r'[\w\s]*'), re.I), 'endif'
  580. #
  581. moduleprocedures = r'module\s*procedure'
  582. moduleprocedurepattern = re.compile(
  583. beforethisafter % ('', moduleprocedures, moduleprocedures, r'.*'), re.I), \
  584. 'moduleprocedure'
  585. implicitpattern = re.compile(
  586. beforethisafter % ('', 'implicit', 'implicit', '.*'), re.I), 'implicit'
  587. dimensionpattern = re.compile(beforethisafter % (
  588. '', 'dimension|virtual', 'dimension|virtual', '.*'), re.I), 'dimension'
  589. externalpattern = re.compile(
  590. beforethisafter % ('', 'external', 'external', '.*'), re.I), 'external'
  591. optionalpattern = re.compile(
  592. beforethisafter % ('', 'optional', 'optional', '.*'), re.I), 'optional'
  593. requiredpattern = re.compile(
  594. beforethisafter % ('', 'required', 'required', '.*'), re.I), 'required'
  595. publicpattern = re.compile(
  596. beforethisafter % ('', 'public', 'public', '.*'), re.I), 'public'
  597. privatepattern = re.compile(
  598. beforethisafter % ('', 'private', 'private', '.*'), re.I), 'private'
  599. intrinsicpattern = re.compile(
  600. beforethisafter % ('', 'intrinsic', 'intrinsic', '.*'), re.I), 'intrinsic'
  601. intentpattern = re.compile(beforethisafter % (
  602. '', 'intent|depend|note|check', 'intent|depend|note|check', r'\s*\(.*?\).*'), re.I), 'intent'
  603. parameterpattern = re.compile(
  604. beforethisafter % ('', 'parameter', 'parameter', r'\s*\(.*'), re.I), 'parameter'
  605. datapattern = re.compile(
  606. beforethisafter % ('', 'data', 'data', '.*'), re.I), 'data'
  607. callpattern = re.compile(
  608. beforethisafter % ('', 'call', 'call', '.*'), re.I), 'call'
  609. entrypattern = re.compile(
  610. beforethisafter % ('', 'entry', 'entry', '.*'), re.I), 'entry'
  611. callfunpattern = re.compile(
  612. beforethisafter % ('', 'callfun', 'callfun', '.*'), re.I), 'callfun'
  613. commonpattern = re.compile(
  614. beforethisafter % ('', 'common', 'common', '.*'), re.I), 'common'
  615. usepattern = re.compile(
  616. beforethisafter % ('', 'use', 'use', '.*'), re.I), 'use'
  617. containspattern = re.compile(
  618. beforethisafter % ('', 'contains', 'contains', ''), re.I), 'contains'
  619. formatpattern = re.compile(
  620. beforethisafter % ('', 'format', 'format', '.*'), re.I), 'format'
  621. # Non-fortran and f2py-specific statements
  622. f2pyenhancementspattern = re.compile(beforethisafter % ('', 'threadsafe|fortranname|callstatement|callprotoargument|usercode|pymethoddef',
  623. 'threadsafe|fortranname|callstatement|callprotoargument|usercode|pymethoddef', '.*'), re.I | re.S), 'f2pyenhancements'
  624. multilinepattern = re.compile(
  625. r"\s*(?P<before>''')(?P<this>.*?)(?P<after>''')\s*\Z", re.S), 'multiline'
  626. ##
  627. def split_by_unquoted(line, characters):
  628. """
  629. Splits the line into (line[:i], line[i:]),
  630. where i is the index of first occurrence of one of the characters
  631. not within quotes, or len(line) if no such index exists
  632. """
  633. assert not (set('"\'') & set(characters)), "cannot split by unquoted quotes"
  634. r = re.compile(
  635. r"\A(?P<before>({single_quoted}|{double_quoted}|{not_quoted})*)"
  636. r"(?P<after>{char}.*)\Z".format(
  637. not_quoted="[^\"'{}]".format(re.escape(characters)),
  638. char="[{}]".format(re.escape(characters)),
  639. single_quoted=r"('([^'\\]|(\\.))*')",
  640. double_quoted=r'("([^"\\]|(\\.))*")'))
  641. m = r.match(line)
  642. if m:
  643. d = m.groupdict()
  644. return (d["before"], d["after"])
  645. return (line, "")
  646. def _simplifyargs(argsline):
  647. a = []
  648. for n in markoutercomma(argsline).split('@,@'):
  649. for r in '(),':
  650. n = n.replace(r, '_')
  651. a.append(n)
  652. return ','.join(a)
  653. crackline_re_1 = re.compile(r'\s*(?P<result>\b[a-z]+\w*\b)\s*=.*', re.I)
  654. def crackline(line, reset=0):
  655. """
  656. reset=-1 --- initialize
  657. reset=0 --- crack the line
  658. reset=1 --- final check if mismatch of blocks occurred
  659. Cracked data is saved in grouplist[0].
  660. """
  661. global beginpattern, groupcounter, groupname, groupcache, grouplist
  662. global filepositiontext, currentfilename, neededmodule, expectbegin
  663. global skipblocksuntil, skipemptyends, previous_context, gotnextfile
  664. _, has_semicolon = split_by_unquoted(line, ";")
  665. if has_semicolon and not (f2pyenhancementspattern[0].match(line) or
  666. multilinepattern[0].match(line)):
  667. # XXX: non-zero reset values need testing
  668. assert reset == 0, repr(reset)
  669. # split line on unquoted semicolons
  670. line, semicolon_line = split_by_unquoted(line, ";")
  671. while semicolon_line:
  672. crackline(line, reset)
  673. line, semicolon_line = split_by_unquoted(semicolon_line[1:], ";")
  674. crackline(line, reset)
  675. return
  676. if reset < 0:
  677. groupcounter = 0
  678. groupname = {groupcounter: ''}
  679. groupcache = {groupcounter: {}}
  680. grouplist = {groupcounter: []}
  681. groupcache[groupcounter]['body'] = []
  682. groupcache[groupcounter]['vars'] = {}
  683. groupcache[groupcounter]['block'] = ''
  684. groupcache[groupcounter]['name'] = ''
  685. neededmodule = -1
  686. skipblocksuntil = -1
  687. return
  688. if reset > 0:
  689. fl = 0
  690. if f77modulename and neededmodule == groupcounter:
  691. fl = 2
  692. while groupcounter > fl:
  693. outmess('crackline: groupcounter=%s groupname=%s\n' %
  694. (repr(groupcounter), repr(groupname)))
  695. outmess(
  696. 'crackline: Mismatch of blocks encountered. Trying to fix it by assuming "end" statement.\n')
  697. grouplist[groupcounter - 1].append(groupcache[groupcounter])
  698. grouplist[groupcounter - 1][-1]['body'] = grouplist[groupcounter]
  699. del grouplist[groupcounter]
  700. groupcounter = groupcounter - 1
  701. if f77modulename and neededmodule == groupcounter:
  702. grouplist[groupcounter - 1].append(groupcache[groupcounter])
  703. grouplist[groupcounter - 1][-1]['body'] = grouplist[groupcounter]
  704. del grouplist[groupcounter]
  705. groupcounter = groupcounter - 1 # end interface
  706. grouplist[groupcounter - 1].append(groupcache[groupcounter])
  707. grouplist[groupcounter - 1][-1]['body'] = grouplist[groupcounter]
  708. del grouplist[groupcounter]
  709. groupcounter = groupcounter - 1 # end module
  710. neededmodule = -1
  711. return
  712. if line == '':
  713. return
  714. flag = 0
  715. for pat in [dimensionpattern, externalpattern, intentpattern, optionalpattern,
  716. requiredpattern,
  717. parameterpattern, datapattern, publicpattern, privatepattern,
  718. intrinsicpattern,
  719. endifpattern, endpattern,
  720. formatpattern,
  721. beginpattern, functionpattern, subroutinepattern,
  722. implicitpattern, typespattern, commonpattern,
  723. callpattern, usepattern, containspattern,
  724. entrypattern,
  725. f2pyenhancementspattern,
  726. multilinepattern,
  727. moduleprocedurepattern
  728. ]:
  729. m = pat[0].match(line)
  730. if m:
  731. break
  732. flag = flag + 1
  733. if not m:
  734. re_1 = crackline_re_1
  735. if 0 <= skipblocksuntil <= groupcounter:
  736. return
  737. if 'externals' in groupcache[groupcounter]:
  738. for name in groupcache[groupcounter]['externals']:
  739. if name in invbadnames:
  740. name = invbadnames[name]
  741. if 'interfaced' in groupcache[groupcounter] and name in groupcache[groupcounter]['interfaced']:
  742. continue
  743. m1 = re.match(
  744. r'(?P<before>[^"]*)\b%s\b\s*@\(@(?P<args>[^@]*)@\)@.*\Z' % name, markouterparen(line), re.I)
  745. if m1:
  746. m2 = re_1.match(m1.group('before'))
  747. a = _simplifyargs(m1.group('args'))
  748. if m2:
  749. line = 'callfun %s(%s) result (%s)' % (
  750. name, a, m2.group('result'))
  751. else:
  752. line = 'callfun %s(%s)' % (name, a)
  753. m = callfunpattern[0].match(line)
  754. if not m:
  755. outmess(
  756. 'crackline: could not resolve function call for line=%s.\n' % repr(line))
  757. return
  758. analyzeline(m, 'callfun', line)
  759. return
  760. if verbose > 1 or (verbose == 1 and currentfilename.lower().endswith('.pyf')):
  761. previous_context = None
  762. outmess('crackline:%d: No pattern for line\n' % (groupcounter))
  763. return
  764. elif pat[1] == 'end':
  765. if 0 <= skipblocksuntil < groupcounter:
  766. groupcounter = groupcounter - 1
  767. if skipblocksuntil <= groupcounter:
  768. return
  769. if groupcounter <= 0:
  770. raise Exception('crackline: groupcounter(=%s) is nonpositive. '
  771. 'Check the blocks.'
  772. % (groupcounter))
  773. m1 = beginpattern[0].match((line))
  774. if (m1) and (not m1.group('this') == groupname[groupcounter]):
  775. raise Exception('crackline: End group %s does not match with '
  776. 'previous Begin group %s\n\t%s' %
  777. (repr(m1.group('this')), repr(groupname[groupcounter]),
  778. filepositiontext)
  779. )
  780. if skipblocksuntil == groupcounter:
  781. skipblocksuntil = -1
  782. grouplist[groupcounter - 1].append(groupcache[groupcounter])
  783. grouplist[groupcounter - 1][-1]['body'] = grouplist[groupcounter]
  784. del grouplist[groupcounter]
  785. groupcounter = groupcounter - 1
  786. if not skipemptyends:
  787. expectbegin = 1
  788. elif pat[1] == 'begin':
  789. if 0 <= skipblocksuntil <= groupcounter:
  790. groupcounter = groupcounter + 1
  791. return
  792. gotnextfile = 0
  793. analyzeline(m, pat[1], line)
  794. expectbegin = 0
  795. elif pat[1] == 'endif':
  796. pass
  797. elif pat[1] == 'moduleprocedure':
  798. analyzeline(m, pat[1], line)
  799. elif pat[1] == 'contains':
  800. if ignorecontains:
  801. return
  802. if 0 <= skipblocksuntil <= groupcounter:
  803. return
  804. skipblocksuntil = groupcounter
  805. else:
  806. if 0 <= skipblocksuntil <= groupcounter:
  807. return
  808. analyzeline(m, pat[1], line)
  809. def markouterparen(line):
  810. l = ''
  811. f = 0
  812. for c in line:
  813. if c == '(':
  814. f = f + 1
  815. if f == 1:
  816. l = l + '@(@'
  817. continue
  818. elif c == ')':
  819. f = f - 1
  820. if f == 0:
  821. l = l + '@)@'
  822. continue
  823. l = l + c
  824. return l
  825. def markoutercomma(line, comma=','):
  826. l = ''
  827. f = 0
  828. before, after = split_by_unquoted(line, comma + '()')
  829. l += before
  830. while after:
  831. if (after[0] == comma) and (f == 0):
  832. l += '@' + comma + '@'
  833. else:
  834. l += after[0]
  835. if after[0] == '(':
  836. f += 1
  837. elif after[0] == ')':
  838. f -= 1
  839. before, after = split_by_unquoted(after[1:], comma + '()')
  840. l += before
  841. assert not f, repr((f, line, l))
  842. return l
  843. def unmarkouterparen(line):
  844. r = line.replace('@(@', '(').replace('@)@', ')')
  845. return r
  846. def appenddecl(decl, decl2, force=1):
  847. if not decl:
  848. decl = {}
  849. if not decl2:
  850. return decl
  851. if decl is decl2:
  852. return decl
  853. for k in list(decl2.keys()):
  854. if k == 'typespec':
  855. if force or k not in decl:
  856. decl[k] = decl2[k]
  857. elif k == 'attrspec':
  858. for l in decl2[k]:
  859. decl = setattrspec(decl, l, force)
  860. elif k == 'kindselector':
  861. decl = setkindselector(decl, decl2[k], force)
  862. elif k == 'charselector':
  863. decl = setcharselector(decl, decl2[k], force)
  864. elif k in ['=', 'typename']:
  865. if force or k not in decl:
  866. decl[k] = decl2[k]
  867. elif k == 'note':
  868. pass
  869. elif k in ['intent', 'check', 'dimension', 'optional',
  870. 'required', 'depend']:
  871. errmess('appenddecl: "%s" not implemented.\n' % k)
  872. else:
  873. raise Exception('appenddecl: Unknown variable definition key: ' +
  874. str(k))
  875. return decl
  876. selectpattern = re.compile(
  877. r'\s*(?P<this>(@\(@.*?@\)@|\*[\d*]+|\*\s*@\(@.*?@\)@|))(?P<after>.*)\Z', re.I)
  878. typedefpattern = re.compile(
  879. r'(?:,(?P<attributes>[\w(),]+))?(::)?(?P<name>\b[a-z$_][\w$]*\b)'
  880. r'(?:\((?P<params>[\w,]*)\))?\Z', re.I)
  881. nameargspattern = re.compile(
  882. 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)
  883. operatorpattern = re.compile(
  884. r'\s*(?P<scheme>(operator|assignment))'
  885. r'@\(@\s*(?P<name>[^)]+)\s*@\)@\s*\Z', re.I)
  886. callnameargspattern = re.compile(
  887. r'\s*(?P<name>\b[\w$]+\b)\s*@\(@\s*(?P<args>.*)\s*@\)@\s*\Z', re.I)
  888. real16pattern = re.compile(
  889. r'([-+]?(?:\d+(?:\.\d*)?|\d*\.\d+))[dD]((?:[-+]?\d+)?)')
  890. real8pattern = re.compile(
  891. r'([-+]?((?:\d+(?:\.\d*)?|\d*\.\d+))[eE]((?:[-+]?\d+)?)|(\d+\.\d*))')
  892. _intentcallbackpattern = re.compile(r'intent\s*\(.*?\bcallback\b', re.I)
  893. def _is_intent_callback(vdecl):
  894. for a in vdecl.get('attrspec', []):
  895. if _intentcallbackpattern.match(a):
  896. return 1
  897. return 0
  898. def _resolvetypedefpattern(line):
  899. line = ''.join(line.split()) # removes whitespace
  900. m1 = typedefpattern.match(line)
  901. print(line, m1)
  902. if m1:
  903. attrs = m1.group('attributes')
  904. attrs = [a.lower() for a in attrs.split(',')] if attrs else []
  905. return m1.group('name'), attrs, m1.group('params')
  906. return None, [], None
  907. def _resolvenameargspattern(line):
  908. line = markouterparen(line)
  909. m1 = nameargspattern.match(line)
  910. if m1:
  911. return m1.group('name'), m1.group('args'), m1.group('result'), m1.group('bind')
  912. m1 = operatorpattern.match(line)
  913. if m1:
  914. name = m1.group('scheme') + '(' + m1.group('name') + ')'
  915. return name, [], None, None
  916. m1 = callnameargspattern.match(line)
  917. if m1:
  918. return m1.group('name'), m1.group('args'), None, None
  919. return None, [], None, None
  920. def analyzeline(m, case, line):
  921. global groupcounter, groupname, groupcache, grouplist, filepositiontext
  922. global currentfilename, f77modulename, neededinterface, neededmodule
  923. global expectbegin, gotnextfile, previous_context
  924. block = m.group('this')
  925. if case != 'multiline':
  926. previous_context = None
  927. if expectbegin and case not in ['begin', 'call', 'callfun', 'type'] \
  928. and not skipemptyends and groupcounter < 1:
  929. newname = os.path.basename(currentfilename).split('.')[0]
  930. outmess(
  931. 'analyzeline: no group yet. Creating program group with name "%s".\n' % newname)
  932. gotnextfile = 0
  933. groupcounter = groupcounter + 1
  934. groupname[groupcounter] = 'program'
  935. groupcache[groupcounter] = {}
  936. grouplist[groupcounter] = []
  937. groupcache[groupcounter]['body'] = []
  938. groupcache[groupcounter]['vars'] = {}
  939. groupcache[groupcounter]['block'] = 'program'
  940. groupcache[groupcounter]['name'] = newname
  941. groupcache[groupcounter]['from'] = 'fromsky'
  942. expectbegin = 0
  943. if case in ['begin', 'call', 'callfun']:
  944. # Crack line => block,name,args,result
  945. block = block.lower()
  946. if re.match(r'block\s*data', block, re.I):
  947. block = 'block data'
  948. elif re.match(r'python\s*module', block, re.I):
  949. block = 'python module'
  950. elif re.match(r'abstract\s*interface', block, re.I):
  951. block = 'abstract interface'
  952. if block == 'type':
  953. name, attrs, _ = _resolvetypedefpattern(m.group('after'))
  954. groupcache[groupcounter]['vars'][name] = dict(attrspec = attrs)
  955. args = []
  956. result = None
  957. else:
  958. name, args, result, _ = _resolvenameargspattern(m.group('after'))
  959. if name is None:
  960. if block == 'block data':
  961. name = '_BLOCK_DATA_'
  962. else:
  963. name = ''
  964. if block not in ['interface', 'block data', 'abstract interface']:
  965. outmess('analyzeline: No name/args pattern found for line.\n')
  966. previous_context = (block, name, groupcounter)
  967. if args:
  968. args = rmbadname([x.strip()
  969. for x in markoutercomma(args).split('@,@')])
  970. else:
  971. args = []
  972. if '' in args:
  973. while '' in args:
  974. args.remove('')
  975. outmess(
  976. 'analyzeline: argument list is malformed (missing argument).\n')
  977. # end of crack line => block,name,args,result
  978. needmodule = 0
  979. needinterface = 0
  980. if case in ['call', 'callfun']:
  981. needinterface = 1
  982. if 'args' not in groupcache[groupcounter]:
  983. return
  984. if name not in groupcache[groupcounter]['args']:
  985. return
  986. for it in grouplist[groupcounter]:
  987. if it['name'] == name:
  988. return
  989. if name in groupcache[groupcounter]['interfaced']:
  990. return
  991. block = {'call': 'subroutine', 'callfun': 'function'}[case]
  992. if f77modulename and neededmodule == -1 and groupcounter <= 1:
  993. neededmodule = groupcounter + 2
  994. needmodule = 1
  995. if block not in ['interface', 'abstract interface']:
  996. needinterface = 1
  997. # Create new block(s)
  998. groupcounter = groupcounter + 1
  999. groupcache[groupcounter] = {}
  1000. grouplist[groupcounter] = []
  1001. if needmodule:
  1002. if verbose > 1:
  1003. outmess('analyzeline: Creating module block %s\n' %
  1004. repr(f77modulename), 0)
  1005. groupname[groupcounter] = 'module'
  1006. groupcache[groupcounter]['block'] = 'python module'
  1007. groupcache[groupcounter]['name'] = f77modulename
  1008. groupcache[groupcounter]['from'] = ''
  1009. groupcache[groupcounter]['body'] = []
  1010. groupcache[groupcounter]['externals'] = []
  1011. groupcache[groupcounter]['interfaced'] = []
  1012. groupcache[groupcounter]['vars'] = {}
  1013. groupcounter = groupcounter + 1
  1014. groupcache[groupcounter] = {}
  1015. grouplist[groupcounter] = []
  1016. if needinterface:
  1017. if verbose > 1:
  1018. outmess('analyzeline: Creating additional interface block (groupcounter=%s).\n' % (
  1019. groupcounter), 0)
  1020. groupname[groupcounter] = 'interface'
  1021. groupcache[groupcounter]['block'] = 'interface'
  1022. groupcache[groupcounter]['name'] = 'unknown_interface'
  1023. groupcache[groupcounter]['from'] = '%s:%s' % (
  1024. groupcache[groupcounter - 1]['from'], groupcache[groupcounter - 1]['name'])
  1025. groupcache[groupcounter]['body'] = []
  1026. groupcache[groupcounter]['externals'] = []
  1027. groupcache[groupcounter]['interfaced'] = []
  1028. groupcache[groupcounter]['vars'] = {}
  1029. groupcounter = groupcounter + 1
  1030. groupcache[groupcounter] = {}
  1031. grouplist[groupcounter] = []
  1032. groupname[groupcounter] = block
  1033. groupcache[groupcounter]['block'] = block
  1034. if not name:
  1035. name = 'unknown_' + block.replace(' ', '_')
  1036. groupcache[groupcounter]['prefix'] = m.group('before')
  1037. groupcache[groupcounter]['name'] = rmbadname1(name)
  1038. groupcache[groupcounter]['result'] = result
  1039. if groupcounter == 1:
  1040. groupcache[groupcounter]['from'] = currentfilename
  1041. else:
  1042. if f77modulename and groupcounter == 3:
  1043. groupcache[groupcounter]['from'] = '%s:%s' % (
  1044. groupcache[groupcounter - 1]['from'], currentfilename)
  1045. else:
  1046. groupcache[groupcounter]['from'] = '%s:%s' % (
  1047. groupcache[groupcounter - 1]['from'], groupcache[groupcounter - 1]['name'])
  1048. for k in list(groupcache[groupcounter].keys()):
  1049. if not groupcache[groupcounter][k]:
  1050. del groupcache[groupcounter][k]
  1051. groupcache[groupcounter]['args'] = args
  1052. groupcache[groupcounter]['body'] = []
  1053. groupcache[groupcounter]['externals'] = []
  1054. groupcache[groupcounter]['interfaced'] = []
  1055. groupcache[groupcounter]['vars'] = {}
  1056. groupcache[groupcounter]['entry'] = {}
  1057. # end of creation
  1058. if block == 'type':
  1059. groupcache[groupcounter]['varnames'] = []
  1060. if case in ['call', 'callfun']: # set parents variables
  1061. if name not in groupcache[groupcounter - 2]['externals']:
  1062. groupcache[groupcounter - 2]['externals'].append(name)
  1063. groupcache[groupcounter]['vars'] = copy.deepcopy(
  1064. groupcache[groupcounter - 2]['vars'])
  1065. try:
  1066. del groupcache[groupcounter]['vars'][name][
  1067. groupcache[groupcounter]['vars'][name]['attrspec'].index('external')]
  1068. except Exception:
  1069. pass
  1070. if block in ['function', 'subroutine']: # set global attributes
  1071. try:
  1072. groupcache[groupcounter]['vars'][name] = appenddecl(
  1073. groupcache[groupcounter]['vars'][name], groupcache[groupcounter - 2]['vars'][''])
  1074. except Exception:
  1075. pass
  1076. if case == 'callfun': # return type
  1077. if result and result in groupcache[groupcounter]['vars']:
  1078. if not name == result:
  1079. groupcache[groupcounter]['vars'][name] = appenddecl(
  1080. groupcache[groupcounter]['vars'][name], groupcache[groupcounter]['vars'][result])
  1081. # if groupcounter>1: # name is interfaced
  1082. try:
  1083. groupcache[groupcounter - 2]['interfaced'].append(name)
  1084. except Exception:
  1085. pass
  1086. if block == 'function':
  1087. t = typespattern[0].match(m.group('before') + ' ' + name)
  1088. if t:
  1089. typespec, selector, attr, edecl = cracktypespec0(
  1090. t.group('this'), t.group('after'))
  1091. updatevars(typespec, selector, attr, edecl)
  1092. if case in ['call', 'callfun']:
  1093. grouplist[groupcounter - 1].append(groupcache[groupcounter])
  1094. grouplist[groupcounter - 1][-1]['body'] = grouplist[groupcounter]
  1095. del grouplist[groupcounter]
  1096. groupcounter = groupcounter - 1 # end routine
  1097. grouplist[groupcounter - 1].append(groupcache[groupcounter])
  1098. grouplist[groupcounter - 1][-1]['body'] = grouplist[groupcounter]
  1099. del grouplist[groupcounter]
  1100. groupcounter = groupcounter - 1 # end interface
  1101. elif case == 'entry':
  1102. name, args, result, bind = _resolvenameargspattern(m.group('after'))
  1103. if name is not None:
  1104. if args:
  1105. args = rmbadname([x.strip()
  1106. for x in markoutercomma(args).split('@,@')])
  1107. else:
  1108. args = []
  1109. assert result is None, repr(result)
  1110. groupcache[groupcounter]['entry'][name] = args
  1111. previous_context = ('entry', name, groupcounter)
  1112. elif case == 'type':
  1113. typespec, selector, attr, edecl = cracktypespec0(
  1114. block, m.group('after'))
  1115. last_name = updatevars(typespec, selector, attr, edecl)
  1116. if last_name is not None:
  1117. previous_context = ('variable', last_name, groupcounter)
  1118. elif case in ['dimension', 'intent', 'optional', 'required', 'external', 'public', 'private', 'intrinsic']:
  1119. edecl = groupcache[groupcounter]['vars']
  1120. ll = m.group('after').strip()
  1121. i = ll.find('::')
  1122. if i < 0 and case == 'intent':
  1123. i = markouterparen(ll).find('@)@') - 2
  1124. ll = ll[:i + 1] + '::' + ll[i + 1:]
  1125. i = ll.find('::')
  1126. if ll[i:] == '::' and 'args' in groupcache[groupcounter]:
  1127. outmess('All arguments will have attribute %s%s\n' %
  1128. (m.group('this'), ll[:i]))
  1129. ll = ll + ','.join(groupcache[groupcounter]['args'])
  1130. if i < 0:
  1131. i = 0
  1132. pl = ''
  1133. else:
  1134. pl = ll[:i].strip()
  1135. ll = ll[i + 2:]
  1136. ch = markoutercomma(pl).split('@,@')
  1137. if len(ch) > 1:
  1138. pl = ch[0]
  1139. outmess('analyzeline: cannot handle multiple attributes without type specification. Ignoring %r.\n' % (
  1140. ','.join(ch[1:])))
  1141. last_name = None
  1142. for e in [x.strip() for x in markoutercomma(ll).split('@,@')]:
  1143. m1 = namepattern.match(e)
  1144. if not m1:
  1145. if case in ['public', 'private']:
  1146. k = ''
  1147. else:
  1148. print(m.groupdict())
  1149. outmess('analyzeline: no name pattern found in %s statement for %s. Skipping.\n' % (
  1150. case, repr(e)))
  1151. continue
  1152. else:
  1153. k = rmbadname1(m1.group('name'))
  1154. if case in ['public', 'private'] and \
  1155. (k == 'operator' or k == 'assignment'):
  1156. k += m1.group('after')
  1157. if k not in edecl:
  1158. edecl[k] = {}
  1159. if case == 'dimension':
  1160. ap = case + m1.group('after')
  1161. if case == 'intent':
  1162. ap = m.group('this') + pl
  1163. if _intentcallbackpattern.match(ap):
  1164. if k not in groupcache[groupcounter]['args']:
  1165. if groupcounter > 1:
  1166. if '__user__' not in groupcache[groupcounter - 2]['name']:
  1167. outmess(
  1168. 'analyzeline: missing __user__ module (could be nothing)\n')
  1169. # fixes ticket 1693
  1170. if k != groupcache[groupcounter]['name']:
  1171. outmess('analyzeline: appending intent(callback) %s'
  1172. ' to %s arguments\n' % (k, groupcache[groupcounter]['name']))
  1173. groupcache[groupcounter]['args'].append(k)
  1174. else:
  1175. errmess(
  1176. 'analyzeline: intent(callback) %s is ignored\n' % (k))
  1177. else:
  1178. errmess('analyzeline: intent(callback) %s is already'
  1179. ' in argument list\n' % (k))
  1180. if case in ['optional', 'required', 'public', 'external', 'private', 'intrinsic']:
  1181. ap = case
  1182. if 'attrspec' in edecl[k]:
  1183. edecl[k]['attrspec'].append(ap)
  1184. else:
  1185. edecl[k]['attrspec'] = [ap]
  1186. if case == 'external':
  1187. if groupcache[groupcounter]['block'] == 'program':
  1188. outmess('analyzeline: ignoring program arguments\n')
  1189. continue
  1190. if k not in groupcache[groupcounter]['args']:
  1191. continue
  1192. if 'externals' not in groupcache[groupcounter]:
  1193. groupcache[groupcounter]['externals'] = []
  1194. groupcache[groupcounter]['externals'].append(k)
  1195. last_name = k
  1196. groupcache[groupcounter]['vars'] = edecl
  1197. if last_name is not None:
  1198. previous_context = ('variable', last_name, groupcounter)
  1199. elif case == 'moduleprocedure':
  1200. groupcache[groupcounter]['implementedby'] = \
  1201. [x.strip() for x in m.group('after').split(',')]
  1202. elif case == 'parameter':
  1203. edecl = groupcache[groupcounter]['vars']
  1204. ll = m.group('after').strip()[1:-1]
  1205. last_name = None
  1206. for e in markoutercomma(ll).split('@,@'):
  1207. try:
  1208. k, initexpr = [x.strip() for x in e.split('=')]
  1209. except Exception:
  1210. outmess(
  1211. 'analyzeline: could not extract name,expr in parameter statement "%s" of "%s"\n' % (e, ll))
  1212. continue
  1213. params = get_parameters(edecl)
  1214. k = rmbadname1(k)
  1215. if k not in edecl:
  1216. edecl[k] = {}
  1217. if '=' in edecl[k] and (not edecl[k]['='] == initexpr):
  1218. outmess('analyzeline: Overwriting the value of parameter "%s" ("%s") with "%s".\n' % (
  1219. k, edecl[k]['='], initexpr))
  1220. t = determineexprtype(initexpr, params)
  1221. if t:
  1222. if t.get('typespec') == 'real':
  1223. tt = list(initexpr)
  1224. for m in real16pattern.finditer(initexpr):
  1225. tt[m.start():m.end()] = list(
  1226. initexpr[m.start():m.end()].lower().replace('d', 'e'))
  1227. initexpr = ''.join(tt)
  1228. elif t.get('typespec') == 'complex':
  1229. initexpr = initexpr[1:].lower().replace('d', 'e').\
  1230. replace(',', '+1j*(')
  1231. try:
  1232. v = eval(initexpr, {}, params)
  1233. except (SyntaxError, NameError, TypeError) as msg:
  1234. errmess('analyzeline: Failed to evaluate %r. Ignoring: %s\n'
  1235. % (initexpr, msg))
  1236. continue
  1237. edecl[k]['='] = repr(v)
  1238. if 'attrspec' in edecl[k]:
  1239. edecl[k]['attrspec'].append('parameter')
  1240. else:
  1241. edecl[k]['attrspec'] = ['parameter']
  1242. last_name = k
  1243. groupcache[groupcounter]['vars'] = edecl
  1244. if last_name is not None:
  1245. previous_context = ('variable', last_name, groupcounter)
  1246. elif case == 'implicit':
  1247. if m.group('after').strip().lower() == 'none':
  1248. groupcache[groupcounter]['implicit'] = None
  1249. elif m.group('after'):
  1250. if 'implicit' in groupcache[groupcounter]:
  1251. impl = groupcache[groupcounter]['implicit']
  1252. else:
  1253. impl = {}
  1254. if impl is None:
  1255. outmess(
  1256. 'analyzeline: Overwriting earlier "implicit none" statement.\n')
  1257. impl = {}
  1258. for e in markoutercomma(m.group('after')).split('@,@'):
  1259. decl = {}
  1260. m1 = re.match(
  1261. r'\s*(?P<this>.*?)\s*(\(\s*(?P<after>[a-z-, ]+)\s*\)\s*|)\Z', e, re.I)
  1262. if not m1:
  1263. outmess(
  1264. 'analyzeline: could not extract info of implicit statement part "%s"\n' % (e))
  1265. continue
  1266. m2 = typespattern4implicit.match(m1.group('this'))
  1267. if not m2:
  1268. outmess(
  1269. 'analyzeline: could not extract types pattern of implicit statement part "%s"\n' % (e))
  1270. continue
  1271. typespec, selector, attr, edecl = cracktypespec0(
  1272. m2.group('this'), m2.group('after'))
  1273. kindselect, charselect, typename = cracktypespec(
  1274. typespec, selector)
  1275. decl['typespec'] = typespec
  1276. decl['kindselector'] = kindselect
  1277. decl['charselector'] = charselect
  1278. decl['typename'] = typename
  1279. for k in list(decl.keys()):
  1280. if not decl[k]:
  1281. del decl[k]
  1282. for r in markoutercomma(m1.group('after')).split('@,@'):
  1283. if '-' in r:
  1284. try:
  1285. begc, endc = [x.strip() for x in r.split('-')]
  1286. except Exception:
  1287. outmess(
  1288. 'analyzeline: expected "<char>-<char>" instead of "%s" in range list of implicit statement\n' % r)
  1289. continue
  1290. else:
  1291. begc = endc = r.strip()
  1292. if not len(begc) == len(endc) == 1:
  1293. outmess(
  1294. 'analyzeline: expected "<char>-<char>" instead of "%s" in range list of implicit statement (2)\n' % r)
  1295. continue
  1296. for o in range(ord(begc), ord(endc) + 1):
  1297. impl[chr(o)] = decl
  1298. groupcache[groupcounter]['implicit'] = impl
  1299. elif case == 'data':
  1300. ll = []
  1301. dl = ''
  1302. il = ''
  1303. f = 0
  1304. fc = 1
  1305. inp = 0
  1306. for c in m.group('after'):
  1307. if not inp:
  1308. if c == "'":
  1309. fc = not fc
  1310. if c == '/' and fc:
  1311. f = f + 1
  1312. continue
  1313. if c == '(':
  1314. inp = inp + 1
  1315. elif c == ')':
  1316. inp = inp - 1
  1317. if f == 0:
  1318. dl = dl + c
  1319. elif f == 1:
  1320. il = il + c
  1321. elif f == 2:
  1322. dl = dl.strip()
  1323. if dl.startswith(','):
  1324. dl = dl[1:].strip()
  1325. ll.append([dl, il])
  1326. dl = c
  1327. il = ''
  1328. f = 0
  1329. if f == 2:
  1330. dl = dl.strip()
  1331. if dl.startswith(','):
  1332. dl = dl[1:].strip()
  1333. ll.append([dl, il])
  1334. vars = {}
  1335. if 'vars' in groupcache[groupcounter]:
  1336. vars = groupcache[groupcounter]['vars']
  1337. last_name = None
  1338. for l in ll:
  1339. l = [x.strip() for x in l]
  1340. if l[0][0] == ',':
  1341. l[0] = l[0][1:]
  1342. if l[0][0] == '(':
  1343. outmess(
  1344. 'analyzeline: implied-DO list "%s" is not supported. Skipping.\n' % l[0])
  1345. continue
  1346. i = 0
  1347. j = 0
  1348. llen = len(l[1])
  1349. for v in rmbadname([x.strip() for x in markoutercomma(l[0]).split('@,@')]):
  1350. if v[0] == '(':
  1351. outmess(
  1352. 'analyzeline: implied-DO list "%s" is not supported. Skipping.\n' % v)
  1353. # XXX: subsequent init expressions may get wrong values.
  1354. # Ignoring since data statements are irrelevant for
  1355. # wrapping.
  1356. continue
  1357. fc = 0
  1358. while (i < llen) and (fc or not l[1][i] == ','):
  1359. if l[1][i] == "'":
  1360. fc = not fc
  1361. i = i + 1
  1362. i = i + 1
  1363. if v not in vars:
  1364. vars[v] = {}
  1365. if '=' in vars[v] and not vars[v]['='] == l[1][j:i - 1]:
  1366. outmess('analyzeline: changing init expression of "%s" ("%s") to "%s"\n' % (
  1367. v, vars[v]['='], l[1][j:i - 1]))
  1368. vars[v]['='] = l[1][j:i - 1]
  1369. j = i
  1370. last_name = v
  1371. groupcache[groupcounter]['vars'] = vars
  1372. if last_name is not None:
  1373. previous_context = ('variable', last_name, groupcounter)
  1374. elif case == 'common':
  1375. line = m.group('after').strip()
  1376. if not line[0] == '/':
  1377. line = '//' + line
  1378. cl = []
  1379. f = 0
  1380. bn = ''
  1381. ol = ''
  1382. for c in line:
  1383. if c == '/':
  1384. f = f + 1
  1385. continue
  1386. if f >= 3:
  1387. bn = bn.strip()
  1388. if not bn:
  1389. bn = '_BLNK_'
  1390. cl.append([bn, ol])
  1391. f = f - 2
  1392. bn = ''
  1393. ol = ''
  1394. if f % 2:
  1395. bn = bn + c
  1396. else:
  1397. ol = ol + c
  1398. bn = bn.strip()
  1399. if not bn:
  1400. bn = '_BLNK_'
  1401. cl.append([bn, ol])
  1402. commonkey = {}
  1403. if 'common' in groupcache[groupcounter]:
  1404. commonkey = groupcache[groupcounter]['common']
  1405. for c in cl:
  1406. if c[0] not in commonkey:
  1407. commonkey[c[0]] = []
  1408. for i in [x.strip() for x in markoutercomma(c[1]).split('@,@')]:
  1409. if i:
  1410. commonkey[c[0]].append(i)
  1411. groupcache[groupcounter]['common'] = commonkey
  1412. previous_context = ('common', bn, groupcounter)
  1413. elif case == 'use':
  1414. m1 = re.match(
  1415. r'\A\s*(?P<name>\b\w+\b)\s*((,(\s*\bonly\b\s*:|(?P<notonly>))\s*(?P<list>.*))|)\s*\Z', m.group('after'), re.I)
  1416. if m1:
  1417. mm = m1.groupdict()
  1418. if 'use' not in groupcache[groupcounter]:
  1419. groupcache[groupcounter]['use'] = {}
  1420. name = m1.group('name')
  1421. groupcache[groupcounter]['use'][name] = {}
  1422. isonly = 0
  1423. if 'list' in mm and mm['list'] is not None:
  1424. if 'notonly' in mm and mm['notonly'] is None:
  1425. isonly = 1
  1426. groupcache[groupcounter]['use'][name]['only'] = isonly
  1427. ll = [x.strip() for x in mm['list'].split(',')]
  1428. rl = {}
  1429. for l in ll:
  1430. if '=' in l:
  1431. m2 = re.match(
  1432. r'\A\s*(?P<local>\b\w+\b)\s*=\s*>\s*(?P<use>\b\w+\b)\s*\Z', l, re.I)
  1433. if m2:
  1434. rl[m2.group('local').strip()] = m2.group(
  1435. 'use').strip()
  1436. else:
  1437. outmess(
  1438. 'analyzeline: Not local=>use pattern found in %s\n' % repr(l))
  1439. else:
  1440. rl[l] = l
  1441. groupcache[groupcounter]['use'][name]['map'] = rl
  1442. else:
  1443. pass
  1444. else:
  1445. print(m.groupdict())
  1446. outmess('analyzeline: Could not crack the use statement.\n')
  1447. elif case in ['f2pyenhancements']:
  1448. if 'f2pyenhancements' not in groupcache[groupcounter]:
  1449. groupcache[groupcounter]['f2pyenhancements'] = {}
  1450. d = groupcache[groupcounter]['f2pyenhancements']
  1451. if m.group('this') == 'usercode' and 'usercode' in d:
  1452. if isinstance(d['usercode'], str):
  1453. d['usercode'] = [d['usercode']]
  1454. d['usercode'].append(m.group('after'))
  1455. else:
  1456. d[m.group('this')] = m.group('after')
  1457. elif case == 'multiline':
  1458. if previous_context is None:
  1459. if verbose:
  1460. outmess('analyzeline: No context for multiline block.\n')
  1461. return
  1462. gc = groupcounter
  1463. appendmultiline(groupcache[gc],
  1464. previous_context[:2],
  1465. m.group('this'))
  1466. else:
  1467. if verbose > 1:
  1468. print(m.groupdict())
  1469. outmess('analyzeline: No code implemented for line.\n')
  1470. def appendmultiline(group, context_name, ml):
  1471. if 'f2pymultilines' not in group:
  1472. group['f2pymultilines'] = {}
  1473. d = group['f2pymultilines']
  1474. if context_name not in d:
  1475. d[context_name] = []
  1476. d[context_name].append(ml)
  1477. return
  1478. def cracktypespec0(typespec, ll):
  1479. selector = None
  1480. attr = None
  1481. if re.match(r'double\s*complex', typespec, re.I):
  1482. typespec = 'double complex'
  1483. elif re.match(r'double\s*precision', typespec, re.I):
  1484. typespec = 'double precision'
  1485. else:
  1486. typespec = typespec.strip().lower()
  1487. m1 = selectpattern.match(markouterparen(ll))
  1488. if not m1:
  1489. outmess(
  1490. 'cracktypespec0: no kind/char_selector pattern found for line.\n')
  1491. return
  1492. d = m1.groupdict()
  1493. for k in list(d.keys()):
  1494. d[k] = unmarkouterparen(d[k])
  1495. if typespec in ['complex', 'integer', 'logical', 'real', 'character', 'type']:
  1496. selector = d['this']
  1497. ll = d['after']
  1498. i = ll.find('::')
  1499. if i >= 0:
  1500. attr = ll[:i].strip()
  1501. ll = ll[i + 2:]
  1502. return typespec, selector, attr, ll
  1503. #####
  1504. namepattern = re.compile(r'\s*(?P<name>\b\w+\b)\s*(?P<after>.*)\s*\Z', re.I)
  1505. kindselector = re.compile(
  1506. r'\s*(\(\s*(kind\s*=)?\s*(?P<kind>.*)\s*\)|\*\s*(?P<kind2>.*?))\s*\Z', re.I)
  1507. charselector = re.compile(
  1508. r'\s*(\((?P<lenkind>.*)\)|\*\s*(?P<charlen>.*))\s*\Z', re.I)
  1509. lenkindpattern = re.compile(
  1510. r'\s*(kind\s*=\s*(?P<kind>.*?)\s*(@,@\s*len\s*=\s*(?P<len>.*)|)'
  1511. r'|(len\s*=\s*|)(?P<len2>.*?)\s*(@,@\s*(kind\s*=\s*|)(?P<kind2>.*)'
  1512. r'|(f2py_len\s*=\s*(?P<f2py_len>.*))|))\s*\Z', re.I)
  1513. lenarraypattern = re.compile(
  1514. r'\s*(@\(@\s*(?!/)\s*(?P<array>.*?)\s*@\)@\s*\*\s*(?P<len>.*?)|(\*\s*(?P<len2>.*?)|)\s*(@\(@\s*(?!/)\s*(?P<array2>.*?)\s*@\)@|))\s*(=\s*(?P<init>.*?)|(@\(@|)/\s*(?P<init2>.*?)\s*/(@\)@|)|)\s*\Z', re.I)
  1515. def removespaces(expr):
  1516. expr = expr.strip()
  1517. if len(expr) <= 1:
  1518. return expr
  1519. expr2 = expr[0]
  1520. for i in range(1, len(expr) - 1):
  1521. if (expr[i] == ' ' and
  1522. ((expr[i + 1] in "()[]{}=+-/* ") or
  1523. (expr[i - 1] in "()[]{}=+-/* "))):
  1524. continue
  1525. expr2 = expr2 + expr[i]
  1526. expr2 = expr2 + expr[-1]
  1527. return expr2
  1528. def markinnerspaces(line):
  1529. """
  1530. The function replace all spaces in the input variable line which are
  1531. surrounded with quotation marks, with the triplet "@_@".
  1532. For instance, for the input "a 'b c'" the function returns "a 'b@_@c'"
  1533. Parameters
  1534. ----------
  1535. line : str
  1536. Returns
  1537. -------
  1538. str
  1539. """
  1540. fragment = ''
  1541. inside = False
  1542. current_quote = None
  1543. escaped = ''
  1544. for c in line:
  1545. if escaped == '\\' and c in ['\\', '\'', '"']:
  1546. fragment += c
  1547. escaped = c
  1548. continue
  1549. if not inside and c in ['\'', '"']:
  1550. current_quote = c
  1551. if c == current_quote:
  1552. inside = not inside
  1553. elif c == ' ' and inside:
  1554. fragment += '@_@'
  1555. continue
  1556. fragment += c
  1557. escaped = c # reset to non-backslash
  1558. return fragment
  1559. def updatevars(typespec, selector, attrspec, entitydecl):
  1560. global groupcache, groupcounter
  1561. last_name = None
  1562. kindselect, charselect, typename = cracktypespec(typespec, selector)
  1563. if attrspec:
  1564. attrspec = [x.strip() for x in markoutercomma(attrspec).split('@,@')]
  1565. l = []
  1566. c = re.compile(r'(?P<start>[a-zA-Z]+)')
  1567. for a in attrspec:
  1568. if not a:
  1569. continue
  1570. m = c.match(a)
  1571. if m:
  1572. s = m.group('start').lower()
  1573. a = s + a[len(s):]
  1574. l.append(a)
  1575. attrspec = l
  1576. el = [x.strip() for x in markoutercomma(entitydecl).split('@,@')]
  1577. el1 = []
  1578. for e in el:
  1579. for e1 in [x.strip() for x in markoutercomma(removespaces(markinnerspaces(e)), comma=' ').split('@ @')]:
  1580. if e1:
  1581. el1.append(e1.replace('@_@', ' '))
  1582. for e in el1:
  1583. m = namepattern.match(e)
  1584. if not m:
  1585. outmess(
  1586. 'updatevars: no name pattern found for entity=%s. Skipping.\n' % (repr(e)))
  1587. continue
  1588. ename = rmbadname1(m.group('name'))
  1589. edecl = {}
  1590. if ename in groupcache[groupcounter]['vars']:
  1591. edecl = groupcache[groupcounter]['vars'][ename].copy()
  1592. not_has_typespec = 'typespec' not in edecl
  1593. if not_has_typespec:
  1594. edecl['typespec'] = typespec
  1595. elif typespec and (not typespec == edecl['typespec']):
  1596. outmess('updatevars: attempt to change the type of "%s" ("%s") to "%s". Ignoring.\n' % (
  1597. ename, edecl['typespec'], typespec))
  1598. if 'kindselector' not in edecl:
  1599. edecl['kindselector'] = copy.copy(kindselect)
  1600. elif kindselect:
  1601. for k in list(kindselect.keys()):
  1602. if k in edecl['kindselector'] and (not kindselect[k] == edecl['kindselector'][k]):
  1603. outmess('updatevars: attempt to change the kindselector "%s" of "%s" ("%s") to "%s". Ignoring.\n' % (
  1604. k, ename, edecl['kindselector'][k], kindselect[k]))
  1605. else:
  1606. edecl['kindselector'][k] = copy.copy(kindselect[k])
  1607. if 'charselector' not in edecl and charselect:
  1608. if not_has_typespec:
  1609. edecl['charselector'] = charselect
  1610. else:
  1611. errmess('updatevars:%s: attempt to change empty charselector to %r. Ignoring.\n'
  1612. % (ename, charselect))
  1613. elif charselect:
  1614. for k in list(charselect.keys()):
  1615. if k in edecl['charselector'] and (not charselect[k] == edecl['charselector'][k]):
  1616. outmess('updatevars: attempt to change the charselector "%s" of "%s" ("%s") to "%s". Ignoring.\n' % (
  1617. k, ename, edecl['charselector'][k], charselect[k]))
  1618. else:
  1619. edecl['charselector'][k] = copy.copy(charselect[k])
  1620. if 'typename' not in edecl:
  1621. edecl['typename'] = typename
  1622. elif typename and (not edecl['typename'] == typename):
  1623. outmess('updatevars: attempt to change the typename of "%s" ("%s") to "%s". Ignoring.\n' % (
  1624. ename, edecl['typename'], typename))
  1625. if 'attrspec' not in edecl:
  1626. edecl['attrspec'] = copy.copy(attrspec)
  1627. elif attrspec:
  1628. for a in attrspec:
  1629. if a not in edecl['attrspec']:
  1630. edecl['attrspec'].append(a)
  1631. else:
  1632. edecl['typespec'] = copy.copy(typespec)
  1633. edecl['kindselector'] = copy.copy(kindselect)
  1634. edecl['charselector'] = copy.copy(charselect)
  1635. edecl['typename'] = typename
  1636. edecl['attrspec'] = copy.copy(attrspec)
  1637. if 'external' in (edecl.get('attrspec') or []) and e in groupcache[groupcounter]['args']:
  1638. if 'externals' not in groupcache[groupcounter]:
  1639. groupcache[groupcounter]['externals'] = []
  1640. groupcache[groupcounter]['externals'].append(e)
  1641. if m.group('after'):
  1642. m1 = lenarraypattern.match(markouterparen(m.group('after')))
  1643. if m1:
  1644. d1 = m1.groupdict()
  1645. for lk in ['len', 'array', 'init']:
  1646. if d1[lk + '2'] is not None:
  1647. d1[lk] = d1[lk + '2']
  1648. del d1[lk + '2']
  1649. for k in list(d1.keys()):
  1650. if d1[k] is not None:
  1651. d1[k] = unmarkouterparen(d1[k])
  1652. else:
  1653. del d1[k]
  1654. if 'len' in d1:
  1655. if typespec in ['complex', 'integer', 'logical', 'real']:
  1656. if ('kindselector' not in edecl) or (not edecl['kindselector']):
  1657. edecl['kindselector'] = {}
  1658. edecl['kindselector']['*'] = d1['len']
  1659. del d1['len']
  1660. elif typespec == 'character':
  1661. if ('charselector' not in edecl) or (not edecl['charselector']):
  1662. edecl['charselector'] = {}
  1663. if 'len' in edecl['charselector']:
  1664. del edecl['charselector']['len']
  1665. edecl['charselector']['*'] = d1['len']
  1666. del d1['len']
  1667. if 'init' in d1:
  1668. if '=' in edecl and (not edecl['='] == d1['init']):
  1669. outmess('updatevars: attempt to change the init expression of "%s" ("%s") to "%s". Ignoring.\n' % (
  1670. ename, edecl['='], d1['init']))
  1671. else:
  1672. edecl['='] = d1['init']
  1673. if 'len' in d1 and 'array' in d1:
  1674. if d1['len'] == '':
  1675. d1['len'] = d1['array']
  1676. del d1['array']
  1677. else:
  1678. d1['array'] = d1['array'] + ',' + d1['len']
  1679. del d1['len']
  1680. errmess('updatevars: "%s %s" is mapped to "%s %s(%s)"\n' % (
  1681. typespec, e, typespec, ename, d1['array']))
  1682. if 'array' in d1:
  1683. dm = 'dimension(%s)' % d1['array']
  1684. if 'attrspec' not in edecl or (not edecl['attrspec']):
  1685. edecl['attrspec'] = [dm]
  1686. else:
  1687. edecl['attrspec'].append(dm)
  1688. for dm1 in edecl['attrspec']:
  1689. if dm1[:9] == 'dimension' and dm1 != dm:
  1690. del edecl['attrspec'][-1]
  1691. errmess('updatevars:%s: attempt to change %r to %r. Ignoring.\n'
  1692. % (ename, dm1, dm))
  1693. break
  1694. else:
  1695. outmess('updatevars: could not crack entity declaration "%s". Ignoring.\n' % (
  1696. ename + m.group('after')))
  1697. for k in list(edecl.keys()):
  1698. if not edecl[k]:
  1699. del edecl[k]
  1700. groupcache[groupcounter]['vars'][ename] = edecl
  1701. if 'varnames' in groupcache[groupcounter]:
  1702. groupcache[groupcounter]['varnames'].append(ename)
  1703. last_name = ename
  1704. return last_name
  1705. def cracktypespec(typespec, selector):
  1706. kindselect = None
  1707. charselect = None
  1708. typename = None
  1709. if selector:
  1710. if typespec in ['complex', 'integer', 'logical', 'real']:
  1711. kindselect = kindselector.match(selector)
  1712. if not kindselect:
  1713. outmess(
  1714. 'cracktypespec: no kindselector pattern found for %s\n' % (repr(selector)))
  1715. return
  1716. kindselect = kindselect.groupdict()
  1717. kindselect['*'] = kindselect['kind2']
  1718. del kindselect['kind2']
  1719. for k in list(kindselect.keys()):
  1720. if not kindselect[k]:
  1721. del kindselect[k]
  1722. for k, i in list(kindselect.items()):
  1723. kindselect[k] = rmbadname1(i)
  1724. elif typespec == 'character':
  1725. charselect = charselector.match(selector)
  1726. if not charselect:
  1727. outmess(
  1728. 'cracktypespec: no charselector pattern found for %s\n' % (repr(selector)))
  1729. return
  1730. charselect = charselect.groupdict()
  1731. charselect['*'] = charselect['charlen']
  1732. del charselect['charlen']
  1733. if charselect['lenkind']:
  1734. lenkind = lenkindpattern.match(
  1735. markoutercomma(charselect['lenkind']))
  1736. lenkind = lenkind.groupdict()
  1737. for lk in ['len', 'kind']:
  1738. if lenkind[lk + '2']:
  1739. lenkind[lk] = lenkind[lk + '2']
  1740. charselect[lk] = lenkind[lk]
  1741. del lenkind[lk + '2']
  1742. if lenkind['f2py_len'] is not None:
  1743. # used to specify the length of assumed length strings
  1744. charselect['f2py_len'] = lenkind['f2py_len']
  1745. del charselect['lenkind']
  1746. for k in list(charselect.keys()):
  1747. if not charselect[k]:
  1748. del charselect[k]
  1749. for k, i in list(charselect.items()):
  1750. charselect[k] = rmbadname1(i)
  1751. elif typespec == 'type':
  1752. typename = re.match(r'\s*\(\s*(?P<name>\w+)\s*\)', selector, re.I)
  1753. if typename:
  1754. typename = typename.group('name')
  1755. else:
  1756. outmess('cracktypespec: no typename found in %s\n' %
  1757. (repr(typespec + selector)))
  1758. else:
  1759. outmess('cracktypespec: no selector used for %s\n' %
  1760. (repr(selector)))
  1761. return kindselect, charselect, typename
  1762. ######
  1763. def setattrspec(decl, attr, force=0):
  1764. if not decl:
  1765. decl = {}
  1766. if not attr:
  1767. return decl
  1768. if 'attrspec' not in decl:
  1769. decl['attrspec'] = [attr]
  1770. return decl
  1771. if force:
  1772. decl['attrspec'].append(attr)
  1773. if attr in decl['attrspec']:
  1774. return decl
  1775. if attr == 'static' and 'automatic' not in decl['attrspec']:
  1776. decl['attrspec'].append(attr)
  1777. elif attr == 'automatic' and 'static' not in decl['attrspec']:
  1778. decl['attrspec'].append(attr)
  1779. elif attr == 'public':
  1780. if 'private' not in decl['attrspec']:
  1781. decl['attrspec'].append(attr)
  1782. elif attr == 'private':
  1783. if 'public' not in decl['attrspec']:
  1784. decl['attrspec'].append(attr)
  1785. else:
  1786. decl['attrspec'].append(attr)
  1787. return decl
  1788. def setkindselector(decl, sel, force=0):
  1789. if not decl:
  1790. decl = {}
  1791. if not sel:
  1792. return decl
  1793. if 'kindselector' not in decl:
  1794. decl['kindselector'] = sel
  1795. return decl
  1796. for k in list(sel.keys()):
  1797. if force or k not in decl['kindselector']:
  1798. decl['kindselector'][k] = sel[k]
  1799. return decl
  1800. def setcharselector(decl, sel, force=0):
  1801. if not decl:
  1802. decl = {}
  1803. if not sel:
  1804. return decl
  1805. if 'charselector' not in decl:
  1806. decl['charselector'] = sel
  1807. return decl
  1808. for k in list(sel.keys()):
  1809. if force or k not in decl['charselector']:
  1810. decl['charselector'][k] = sel[k]
  1811. return decl
  1812. def getblockname(block, unknown='unknown'):
  1813. if 'name' in block:
  1814. return block['name']
  1815. return unknown
  1816. # post processing
  1817. def setmesstext(block):
  1818. global filepositiontext
  1819. try:
  1820. filepositiontext = 'In: %s:%s\n' % (block['from'], block['name'])
  1821. except Exception:
  1822. pass
  1823. def get_usedict(block):
  1824. usedict = {}
  1825. if 'parent_block' in block:
  1826. usedict = get_usedict(block['parent_block'])
  1827. if 'use' in block:
  1828. usedict.update(block['use'])
  1829. return usedict
  1830. def get_useparameters(block, param_map=None):
  1831. global f90modulevars
  1832. if param_map is None:
  1833. param_map = {}
  1834. usedict = get_usedict(block)
  1835. if not usedict:
  1836. return param_map
  1837. for usename, mapping in list(usedict.items()):
  1838. usename = usename.lower()
  1839. if usename not in f90modulevars:
  1840. outmess('get_useparameters: no module %s info used by %s\n' %
  1841. (usename, block.get('name')))
  1842. continue
  1843. mvars = f90modulevars[usename]
  1844. params = get_parameters(mvars)
  1845. if not params:
  1846. continue
  1847. # XXX: apply mapping
  1848. if mapping:
  1849. errmess('get_useparameters: mapping for %s not impl.\n' % (mapping))
  1850. for k, v in list(params.items()):
  1851. if k in param_map:
  1852. outmess('get_useparameters: overriding parameter %s with'
  1853. ' value from module %s\n' % (repr(k), repr(usename)))
  1854. param_map[k] = v
  1855. return param_map
  1856. def postcrack2(block, tab='', param_map=None):
  1857. global f90modulevars
  1858. if not f90modulevars:
  1859. return block
  1860. if isinstance(block, list):
  1861. ret = [postcrack2(g, tab=tab + '\t', param_map=param_map)
  1862. for g in block]
  1863. return ret
  1864. setmesstext(block)
  1865. outmess('%sBlock: %s\n' % (tab, block['name']), 0)
  1866. if param_map is None:
  1867. param_map = get_useparameters(block)
  1868. if param_map is not None and 'vars' in block:
  1869. vars = block['vars']
  1870. for n in list(vars.keys()):
  1871. var = vars[n]
  1872. if 'kindselector' in var:
  1873. kind = var['kindselector']
  1874. if 'kind' in kind:
  1875. val = kind['kind']
  1876. if val in param_map:
  1877. kind['kind'] = param_map[val]
  1878. new_body = [postcrack2(b, tab=tab + '\t', param_map=param_map)
  1879. for b in block['body']]
  1880. block['body'] = new_body
  1881. return block
  1882. def postcrack(block, args=None, tab=''):
  1883. """
  1884. TODO:
  1885. function return values
  1886. determine expression types if in argument list
  1887. """
  1888. global usermodules, onlyfunctions
  1889. if isinstance(block, list):
  1890. gret = []
  1891. uret = []
  1892. for g in block:
  1893. setmesstext(g)
  1894. g = postcrack(g, tab=tab + '\t')
  1895. # sort user routines to appear first
  1896. if 'name' in g and '__user__' in g['name']:
  1897. uret.append(g)
  1898. else:
  1899. gret.append(g)
  1900. return uret + gret
  1901. setmesstext(block)
  1902. if not isinstance(block, dict) and 'block' not in block:
  1903. raise Exception('postcrack: Expected block dictionary instead of ' +
  1904. str(block))
  1905. if 'name' in block and not block['name'] == 'unknown_interface':
  1906. outmess('%sBlock: %s\n' % (tab, block['name']), 0)
  1907. block = analyzeargs(block)
  1908. block = analyzecommon(block)
  1909. block['vars'] = analyzevars(block)
  1910. block['sortvars'] = sortvarnames(block['vars'])
  1911. if 'args' in block and block['args']:
  1912. args = block['args']
  1913. block['body'] = analyzebody(block, args, tab=tab)
  1914. userisdefined = []
  1915. if 'use' in block:
  1916. useblock = block['use']
  1917. for k in list(useblock.keys()):
  1918. if '__user__' in k:
  1919. userisdefined.append(k)
  1920. else:
  1921. useblock = {}
  1922. name = ''
  1923. if 'name' in block:
  1924. name = block['name']
  1925. # and not userisdefined: # Build a __user__ module
  1926. if 'externals' in block and block['externals']:
  1927. interfaced = []
  1928. if 'interfaced' in block:
  1929. interfaced = block['interfaced']
  1930. mvars = copy.copy(block['vars'])
  1931. if name:
  1932. mname = name + '__user__routines'
  1933. else:
  1934. mname = 'unknown__user__routines'
  1935. if mname in userisdefined:
  1936. i = 1
  1937. while '%s_%i' % (mname, i) in userisdefined:
  1938. i = i + 1
  1939. mname = '%s_%i' % (mname, i)
  1940. interface = {'block': 'interface', 'body': [],
  1941. 'vars': {}, 'name': name + '_user_interface'}
  1942. for e in block['externals']:
  1943. if e in interfaced:
  1944. edef = []
  1945. j = -1
  1946. for b in block['body']:
  1947. j = j + 1
  1948. if b['block'] == 'interface':
  1949. i = -1
  1950. for bb in b['body']:
  1951. i = i + 1
  1952. if 'name' in bb and bb['name'] == e:
  1953. edef = copy.copy(bb)
  1954. del b['body'][i]
  1955. break
  1956. if edef:
  1957. if not b['body']:
  1958. del block['body'][j]
  1959. del interfaced[interfaced.index(e)]
  1960. break
  1961. interface['body'].append(edef)
  1962. else:
  1963. if e in mvars and not isexternal(mvars[e]):
  1964. interface['vars'][e] = mvars[e]
  1965. if interface['vars'] or interface['body']:
  1966. block['interfaced'] = interfaced
  1967. mblock = {'block': 'python module', 'body': [
  1968. interface], 'vars': {}, 'name': mname, 'interfaced': block['externals']}
  1969. useblock[mname] = {}
  1970. usermodules.append(mblock)
  1971. if useblock:
  1972. block['use'] = useblock
  1973. return block
  1974. def sortvarnames(vars):
  1975. indep = []
  1976. dep = []
  1977. for v in list(vars.keys()):
  1978. if 'depend' in vars[v] and vars[v]['depend']:
  1979. dep.append(v)
  1980. else:
  1981. indep.append(v)
  1982. n = len(dep)
  1983. i = 0
  1984. while dep: # XXX: How to catch dependence cycles correctly?
  1985. v = dep[0]
  1986. fl = 0
  1987. for w in dep[1:]:
  1988. if w in vars[v]['depend']:
  1989. fl = 1
  1990. break
  1991. if fl:
  1992. dep = dep[1:] + [v]
  1993. i = i + 1
  1994. if i > n:
  1995. errmess('sortvarnames: failed to compute dependencies because'
  1996. ' of cyclic dependencies between '
  1997. + ', '.join(dep) + '\n')
  1998. indep = indep + dep
  1999. break
  2000. else:
  2001. indep.append(v)
  2002. dep = dep[1:]
  2003. n = len(dep)
  2004. i = 0
  2005. return indep
  2006. def analyzecommon(block):
  2007. if not hascommon(block):
  2008. return block
  2009. commonvars = []
  2010. for k in list(block['common'].keys()):
  2011. comvars = []
  2012. for e in block['common'][k]:
  2013. m = re.match(
  2014. r'\A\s*\b(?P<name>.*?)\b\s*(\((?P<dims>.*?)\)|)\s*\Z', e, re.I)
  2015. if m:
  2016. dims = []
  2017. if m.group('dims'):
  2018. dims = [x.strip()
  2019. for x in markoutercomma(m.group('dims')).split('@,@')]
  2020. n = rmbadname1(m.group('name').strip())
  2021. if n in block['vars']:
  2022. if 'attrspec' in block['vars'][n]:
  2023. block['vars'][n]['attrspec'].append(
  2024. 'dimension(%s)' % (','.join(dims)))
  2025. else:
  2026. block['vars'][n]['attrspec'] = [
  2027. 'dimension(%s)' % (','.join(dims))]
  2028. else:
  2029. if dims:
  2030. block['vars'][n] = {
  2031. 'attrspec': ['dimension(%s)' % (','.join(dims))]}
  2032. else:
  2033. block['vars'][n] = {}
  2034. if n not in commonvars:
  2035. commonvars.append(n)
  2036. else:
  2037. n = e
  2038. errmess(
  2039. 'analyzecommon: failed to extract "<name>[(<dims>)]" from "%s" in common /%s/.\n' % (e, k))
  2040. comvars.append(n)
  2041. block['common'][k] = comvars
  2042. if 'commonvars' not in block:
  2043. block['commonvars'] = commonvars
  2044. else:
  2045. block['commonvars'] = block['commonvars'] + commonvars
  2046. return block
  2047. def analyzebody(block, args, tab=''):
  2048. global usermodules, skipfuncs, onlyfuncs, f90modulevars
  2049. setmesstext(block)
  2050. body = []
  2051. for b in block['body']:
  2052. b['parent_block'] = block
  2053. if b['block'] in ['function', 'subroutine']:
  2054. if args is not None and b['name'] not in args:
  2055. continue
  2056. else:
  2057. as_ = b['args']
  2058. if b['name'] in skipfuncs:
  2059. continue
  2060. if onlyfuncs and b['name'] not in onlyfuncs:
  2061. continue
  2062. b['saved_interface'] = crack2fortrangen(
  2063. b, '\n' + ' ' * 6, as_interface=True)
  2064. else:
  2065. as_ = args
  2066. b = postcrack(b, as_, tab=tab + '\t')
  2067. if b['block'] in ['interface', 'abstract interface'] and \
  2068. not b['body'] and not b.get('implementedby'):
  2069. if 'f2pyenhancements' not in b:
  2070. continue
  2071. if b['block'].replace(' ', '') == 'pythonmodule':
  2072. usermodules.append(b)
  2073. else:
  2074. if b['block'] == 'module':
  2075. f90modulevars[b['name']] = b['vars']
  2076. body.append(b)
  2077. return body
  2078. def buildimplicitrules(block):
  2079. setmesstext(block)
  2080. implicitrules = defaultimplicitrules
  2081. attrrules = {}
  2082. if 'implicit' in block:
  2083. if block['implicit'] is None:
  2084. implicitrules = None
  2085. if verbose > 1:
  2086. outmess(
  2087. 'buildimplicitrules: no implicit rules for routine %s.\n' % repr(block['name']))
  2088. else:
  2089. for k in list(block['implicit'].keys()):
  2090. if block['implicit'][k].get('typespec') not in ['static', 'automatic']:
  2091. implicitrules[k] = block['implicit'][k]
  2092. else:
  2093. attrrules[k] = block['implicit'][k]['typespec']
  2094. return implicitrules, attrrules
  2095. def myeval(e, g=None, l=None):
  2096. """ Like `eval` but returns only integers and floats """
  2097. r = eval(e, g, l)
  2098. if type(r) in [int, float]:
  2099. return r
  2100. raise ValueError('r=%r' % (r))
  2101. getlincoef_re_1 = re.compile(r'\A\b\w+\b\Z', re.I)
  2102. def getlincoef(e, xset): # e = a*x+b ; x in xset
  2103. """
  2104. Obtain ``a`` and ``b`` when ``e == "a*x+b"``, where ``x`` is a symbol in
  2105. xset.
  2106. >>> getlincoef('2*x + 1', {'x'})
  2107. (2, 1, 'x')
  2108. >>> getlincoef('3*x + x*2 + 2 + 1', {'x'})
  2109. (5, 3, 'x')
  2110. >>> getlincoef('0', {'x'})
  2111. (0, 0, None)
  2112. >>> getlincoef('0*x', {'x'})
  2113. (0, 0, 'x')
  2114. >>> getlincoef('x*x', {'x'})
  2115. (None, None, None)
  2116. This can be tricked by sufficiently complex expressions
  2117. >>> getlincoef('(x - 0.5)*(x - 1.5)*(x - 1)*x + 2*x + 3', {'x'})
  2118. (2.0, 3.0, 'x')
  2119. """
  2120. try:
  2121. c = int(myeval(e, {}, {}))
  2122. return 0, c, None
  2123. except Exception:
  2124. pass
  2125. if getlincoef_re_1.match(e):
  2126. return 1, 0, e
  2127. len_e = len(e)
  2128. for x in xset:
  2129. if len(x) > len_e:
  2130. continue
  2131. if re.search(r'\w\s*\([^)]*\b' + x + r'\b', e):
  2132. # skip function calls having x as an argument, e.g max(1, x)
  2133. continue
  2134. re_1 = re.compile(r'(?P<before>.*?)\b' + x + r'\b(?P<after>.*)', re.I)
  2135. m = re_1.match(e)
  2136. if m:
  2137. try:
  2138. m1 = re_1.match(e)
  2139. while m1:
  2140. ee = '%s(%s)%s' % (
  2141. m1.group('before'), 0, m1.group('after'))
  2142. m1 = re_1.match(ee)
  2143. b = myeval(ee, {}, {})
  2144. m1 = re_1.match(e)
  2145. while m1:
  2146. ee = '%s(%s)%s' % (
  2147. m1.group('before'), 1, m1.group('after'))
  2148. m1 = re_1.match(ee)
  2149. a = myeval(ee, {}, {}) - b
  2150. m1 = re_1.match(e)
  2151. while m1:
  2152. ee = '%s(%s)%s' % (
  2153. m1.group('before'), 0.5, m1.group('after'))
  2154. m1 = re_1.match(ee)
  2155. c = myeval(ee, {}, {})
  2156. # computing another point to be sure that expression is linear
  2157. m1 = re_1.match(e)
  2158. while m1:
  2159. ee = '%s(%s)%s' % (
  2160. m1.group('before'), 1.5, m1.group('after'))
  2161. m1 = re_1.match(ee)
  2162. c2 = myeval(ee, {}, {})
  2163. if (a * 0.5 + b == c and a * 1.5 + b == c2):
  2164. return a, b, x
  2165. except Exception:
  2166. pass
  2167. break
  2168. return None, None, None
  2169. word_pattern = re.compile(r'\b[a-z][\w$]*\b', re.I)
  2170. def _get_depend_dict(name, vars, deps):
  2171. if name in vars:
  2172. words = vars[name].get('depend', [])
  2173. if '=' in vars[name] and not isstring(vars[name]):
  2174. for word in word_pattern.findall(vars[name]['=']):
  2175. # The word_pattern may return values that are not
  2176. # only variables, they can be string content for instance
  2177. if word not in words and word in vars and word != name:
  2178. words.append(word)
  2179. for word in words[:]:
  2180. for w in deps.get(word, []) \
  2181. or _get_depend_dict(word, vars, deps):
  2182. if w not in words:
  2183. words.append(w)
  2184. else:
  2185. outmess('_get_depend_dict: no dependence info for %s\n' % (repr(name)))
  2186. words = []
  2187. deps[name] = words
  2188. return words
  2189. def _calc_depend_dict(vars):
  2190. names = list(vars.keys())
  2191. depend_dict = {}
  2192. for n in names:
  2193. _get_depend_dict(n, vars, depend_dict)
  2194. return depend_dict
  2195. def get_sorted_names(vars):
  2196. """
  2197. """
  2198. depend_dict = _calc_depend_dict(vars)
  2199. names = []
  2200. for name in list(depend_dict.keys()):
  2201. if not depend_dict[name]:
  2202. names.append(name)
  2203. del depend_dict[name]
  2204. while depend_dict:
  2205. for name, lst in list(depend_dict.items()):
  2206. new_lst = [n for n in lst if n in depend_dict]
  2207. if not new_lst:
  2208. names.append(name)
  2209. del depend_dict[name]
  2210. else:
  2211. depend_dict[name] = new_lst
  2212. return [name for name in names if name in vars]
  2213. def _kind_func(string):
  2214. # XXX: return something sensible.
  2215. if string[0] in "'\"":
  2216. string = string[1:-1]
  2217. if real16pattern.match(string):
  2218. return 8
  2219. elif real8pattern.match(string):
  2220. return 4
  2221. return 'kind(' + string + ')'
  2222. def _selected_int_kind_func(r):
  2223. # XXX: This should be processor dependent
  2224. m = 10 ** r
  2225. if m <= 2 ** 8:
  2226. return 1
  2227. if m <= 2 ** 16:
  2228. return 2
  2229. if m <= 2 ** 32:
  2230. return 4
  2231. if m <= 2 ** 63:
  2232. return 8
  2233. if m <= 2 ** 128:
  2234. return 16
  2235. return -1
  2236. def _selected_real_kind_func(p, r=0, radix=0):
  2237. # XXX: This should be processor dependent
  2238. # This is only good for 0 <= p <= 20
  2239. if p < 7:
  2240. return 4
  2241. if p < 16:
  2242. return 8
  2243. machine = platform.machine().lower()
  2244. if machine.startswith(('aarch64', 'power', 'ppc', 'riscv', 's390x', 'sparc')):
  2245. if p <= 20:
  2246. return 16
  2247. else:
  2248. if p < 19:
  2249. return 10
  2250. elif p <= 20:
  2251. return 16
  2252. return -1
  2253. def get_parameters(vars, global_params={}):
  2254. params = copy.copy(global_params)
  2255. g_params = copy.copy(global_params)
  2256. for name, func in [('kind', _kind_func),
  2257. ('selected_int_kind', _selected_int_kind_func),
  2258. ('selected_real_kind', _selected_real_kind_func), ]:
  2259. if name not in g_params:
  2260. g_params[name] = func
  2261. param_names = []
  2262. for n in get_sorted_names(vars):
  2263. if 'attrspec' in vars[n] and 'parameter' in vars[n]['attrspec']:
  2264. param_names.append(n)
  2265. kind_re = re.compile(r'\bkind\s*\(\s*(?P<value>.*)\s*\)', re.I)
  2266. selected_int_kind_re = re.compile(
  2267. r'\bselected_int_kind\s*\(\s*(?P<value>.*)\s*\)', re.I)
  2268. selected_kind_re = re.compile(
  2269. r'\bselected_(int|real)_kind\s*\(\s*(?P<value>.*)\s*\)', re.I)
  2270. for n in param_names:
  2271. if '=' in vars[n]:
  2272. v = vars[n]['=']
  2273. if islogical(vars[n]):
  2274. v = v.lower()
  2275. for repl in [
  2276. ('.false.', 'False'),
  2277. ('.true.', 'True'),
  2278. # TODO: test .eq., .neq., etc replacements.
  2279. ]:
  2280. v = v.replace(*repl)
  2281. v = kind_re.sub(r'kind("\1")', v)
  2282. v = selected_int_kind_re.sub(r'selected_int_kind(\1)', v)
  2283. # We need to act according to the data.
  2284. # The easy case is if the data has a kind-specifier,
  2285. # then we may easily remove those specifiers.
  2286. # However, it may be that the user uses other specifiers...(!)
  2287. is_replaced = False
  2288. if 'kindselector' in vars[n]:
  2289. if 'kind' in vars[n]['kindselector']:
  2290. orig_v_len = len(v)
  2291. v = v.replace('_' + vars[n]['kindselector']['kind'], '')
  2292. # Again, this will be true if even a single specifier
  2293. # has been replaced, see comment above.
  2294. is_replaced = len(v) < orig_v_len
  2295. if not is_replaced:
  2296. if not selected_kind_re.match(v):
  2297. v_ = v.split('_')
  2298. # In case there are additive parameters
  2299. if len(v_) > 1:
  2300. v = ''.join(v_[:-1]).lower().replace(v_[-1].lower(), '')
  2301. # Currently this will not work for complex numbers.
  2302. # There is missing code for extracting a complex number,
  2303. # which may be defined in either of these:
  2304. # a) (Re, Im)
  2305. # b) cmplx(Re, Im)
  2306. # c) dcmplx(Re, Im)
  2307. # d) cmplx(Re, Im, <prec>)
  2308. if isdouble(vars[n]):
  2309. tt = list(v)
  2310. for m in real16pattern.finditer(v):
  2311. tt[m.start():m.end()] = list(
  2312. v[m.start():m.end()].lower().replace('d', 'e'))
  2313. v = ''.join(tt)
  2314. elif iscomplex(vars[n]):
  2315. outmess(f'get_parameters[TODO]: '
  2316. f'implement evaluation of complex expression {v}\n')
  2317. # Handle _dp for gh-6624
  2318. # Also fixes gh-20460
  2319. if real16pattern.search(v):
  2320. v = 8
  2321. elif real8pattern.search(v):
  2322. v = 4
  2323. try:
  2324. params[n] = eval(v, g_params, params)
  2325. except Exception as msg:
  2326. params[n] = v
  2327. outmess('get_parameters: got "%s" on %s\n' % (msg, repr(v)))
  2328. if isstring(vars[n]) and isinstance(params[n], int):
  2329. params[n] = chr(params[n])
  2330. nl = n.lower()
  2331. if nl != n:
  2332. params[nl] = params[n]
  2333. else:
  2334. print(vars[n])
  2335. outmess(
  2336. 'get_parameters:parameter %s does not have value?!\n' % (repr(n)))
  2337. return params
  2338. def _eval_length(length, params):
  2339. if length in ['(:)', '(*)', '*']:
  2340. return '(*)'
  2341. return _eval_scalar(length, params)
  2342. _is_kind_number = re.compile(r'\d+_').match
  2343. def _eval_scalar(value, params):
  2344. if _is_kind_number(value):
  2345. value = value.split('_')[0]
  2346. try:
  2347. # TODO: use symbolic from PR #19805
  2348. value = eval(value, {}, params)
  2349. value = (repr if isinstance(value, str) else str)(value)
  2350. except (NameError, SyntaxError, TypeError):
  2351. return value
  2352. except Exception as msg:
  2353. errmess('"%s" in evaluating %r '
  2354. '(available names: %s)\n'
  2355. % (msg, value, list(params.keys())))
  2356. return value
  2357. def analyzevars(block):
  2358. global f90modulevars
  2359. setmesstext(block)
  2360. implicitrules, attrrules = buildimplicitrules(block)
  2361. vars = copy.copy(block['vars'])
  2362. if block['block'] == 'function' and block['name'] not in vars:
  2363. vars[block['name']] = {}
  2364. if '' in block['vars']:
  2365. del vars['']
  2366. if 'attrspec' in block['vars']['']:
  2367. gen = block['vars']['']['attrspec']
  2368. for n in set(vars) | set(b['name'] for b in block['body']):
  2369. for k in ['public', 'private']:
  2370. if k in gen:
  2371. vars[n] = setattrspec(vars.get(n, {}), k)
  2372. svars = []
  2373. args = block['args']
  2374. for a in args:
  2375. try:
  2376. vars[a]
  2377. svars.append(a)
  2378. except KeyError:
  2379. pass
  2380. for n in list(vars.keys()):
  2381. if n not in args:
  2382. svars.append(n)
  2383. params = get_parameters(vars, get_useparameters(block))
  2384. dep_matches = {}
  2385. name_match = re.compile(r'[A-Za-z][\w$]*').match
  2386. for v in list(vars.keys()):
  2387. m = name_match(v)
  2388. if m:
  2389. n = v[m.start():m.end()]
  2390. try:
  2391. dep_matches[n]
  2392. except KeyError:
  2393. dep_matches[n] = re.compile(r'.*\b%s\b' % (v), re.I).match
  2394. for n in svars:
  2395. if n[0] in list(attrrules.keys()):
  2396. vars[n] = setattrspec(vars[n], attrrules[n[0]])
  2397. if 'typespec' not in vars[n]:
  2398. if not('attrspec' in vars[n] and 'external' in vars[n]['attrspec']):
  2399. if implicitrules:
  2400. ln0 = n[0].lower()
  2401. for k in list(implicitrules[ln0].keys()):
  2402. if k == 'typespec' and implicitrules[ln0][k] == 'undefined':
  2403. continue
  2404. if k not in vars[n]:
  2405. vars[n][k] = implicitrules[ln0][k]
  2406. elif k == 'attrspec':
  2407. for l in implicitrules[ln0][k]:
  2408. vars[n] = setattrspec(vars[n], l)
  2409. elif n in block['args']:
  2410. outmess('analyzevars: typespec of variable %s is not defined in routine %s.\n' % (
  2411. repr(n), block['name']))
  2412. if 'charselector' in vars[n]:
  2413. if 'len' in vars[n]['charselector']:
  2414. l = vars[n]['charselector']['len']
  2415. try:
  2416. l = str(eval(l, {}, params))
  2417. except Exception:
  2418. pass
  2419. vars[n]['charselector']['len'] = l
  2420. if 'kindselector' in vars[n]:
  2421. if 'kind' in vars[n]['kindselector']:
  2422. l = vars[n]['kindselector']['kind']
  2423. try:
  2424. l = str(eval(l, {}, params))
  2425. except Exception:
  2426. pass
  2427. vars[n]['kindselector']['kind'] = l
  2428. dimension_exprs = {}
  2429. if 'attrspec' in vars[n]:
  2430. attr = vars[n]['attrspec']
  2431. attr.reverse()
  2432. vars[n]['attrspec'] = []
  2433. dim, intent, depend, check, note = None, None, None, None, None
  2434. for a in attr:
  2435. if a[:9] == 'dimension':
  2436. dim = (a[9:].strip())[1:-1]
  2437. elif a[:6] == 'intent':
  2438. intent = (a[6:].strip())[1:-1]
  2439. elif a[:6] == 'depend':
  2440. depend = (a[6:].strip())[1:-1]
  2441. elif a[:5] == 'check':
  2442. check = (a[5:].strip())[1:-1]
  2443. elif a[:4] == 'note':
  2444. note = (a[4:].strip())[1:-1]
  2445. else:
  2446. vars[n] = setattrspec(vars[n], a)
  2447. if intent:
  2448. if 'intent' not in vars[n]:
  2449. vars[n]['intent'] = []
  2450. for c in [x.strip() for x in markoutercomma(intent).split('@,@')]:
  2451. # Remove spaces so that 'in out' becomes 'inout'
  2452. tmp = c.replace(' ', '')
  2453. if tmp not in vars[n]['intent']:
  2454. vars[n]['intent'].append(tmp)
  2455. intent = None
  2456. if note:
  2457. note = note.replace('\\n\\n', '\n\n')
  2458. note = note.replace('\\n ', '\n')
  2459. if 'note' not in vars[n]:
  2460. vars[n]['note'] = [note]
  2461. else:
  2462. vars[n]['note'].append(note)
  2463. note = None
  2464. if depend is not None:
  2465. if 'depend' not in vars[n]:
  2466. vars[n]['depend'] = []
  2467. for c in rmbadname([x.strip() for x in markoutercomma(depend).split('@,@')]):
  2468. if c not in vars[n]['depend']:
  2469. vars[n]['depend'].append(c)
  2470. depend = None
  2471. if check is not None:
  2472. if 'check' not in vars[n]:
  2473. vars[n]['check'] = []
  2474. for c in [x.strip() for x in markoutercomma(check).split('@,@')]:
  2475. if c not in vars[n]['check']:
  2476. vars[n]['check'].append(c)
  2477. check = None
  2478. if dim and 'dimension' not in vars[n]:
  2479. vars[n]['dimension'] = []
  2480. for d in rmbadname([x.strip() for x in markoutercomma(dim).split('@,@')]):
  2481. star = ':' if d == ':' else '*'
  2482. # Evaluate `d` with respect to params
  2483. if d in params:
  2484. d = str(params[d])
  2485. for p in params:
  2486. re_1 = re.compile(r'(?P<before>.*?)\b' + p + r'\b(?P<after>.*)', re.I)
  2487. m = re_1.match(d)
  2488. while m:
  2489. d = m.group('before') + \
  2490. str(params[p]) + m.group('after')
  2491. m = re_1.match(d)
  2492. if d == star:
  2493. dl = [star]
  2494. else:
  2495. dl = markoutercomma(d, ':').split('@:@')
  2496. if len(dl) == 2 and '*' in dl: # e.g. dimension(5:*)
  2497. dl = ['*']
  2498. d = '*'
  2499. if len(dl) == 1 and dl[0] != star:
  2500. dl = ['1', dl[0]]
  2501. if len(dl) == 2:
  2502. d1, d2 = map(symbolic.Expr.parse, dl)
  2503. dsize = d2 - d1 + 1
  2504. d = dsize.tostring(language=symbolic.Language.C)
  2505. # find variables v that define d as a linear
  2506. # function, `d == a * v + b`, and store
  2507. # coefficients a and b for further analysis.
  2508. solver_and_deps = {}
  2509. for v in block['vars']:
  2510. s = symbolic.as_symbol(v)
  2511. if dsize.contains(s):
  2512. try:
  2513. a, b = dsize.linear_solve(s)
  2514. def solve_v(s, a=a, b=b):
  2515. return (s - b) / a
  2516. all_symbols = set(a.symbols())
  2517. all_symbols.update(b.symbols())
  2518. except RuntimeError as msg:
  2519. # d is not a linear function of v,
  2520. # however, if v can be determined
  2521. # from d using other means,
  2522. # implement the corresponding
  2523. # solve_v function here.
  2524. solve_v = None
  2525. all_symbols = set(dsize.symbols())
  2526. v_deps = set(
  2527. s.data for s in all_symbols
  2528. if s.data in vars)
  2529. solver_and_deps[v] = solve_v, list(v_deps)
  2530. # Note that dsize may contain symbols that are
  2531. # not defined in block['vars']. Here we assume
  2532. # these correspond to Fortran/C intrinsic
  2533. # functions or that are defined by other
  2534. # means. We'll let the compiler validate the
  2535. # definiteness of such symbols.
  2536. dimension_exprs[d] = solver_and_deps
  2537. vars[n]['dimension'].append(d)
  2538. if 'check' not in vars[n] and 'args' in block and n in block['args']:
  2539. # n is an argument that has no checks defined. Here we
  2540. # generate some consistency checks for n, and when n is an
  2541. # array, generate checks for its dimensions and construct
  2542. # initialization expressions.
  2543. n_deps = vars[n].get('depend', [])
  2544. n_checks = []
  2545. n_is_input = l_or(isintent_in, isintent_inout,
  2546. isintent_inplace)(vars[n])
  2547. if isarray(vars[n]): # n is array
  2548. for i, d in enumerate(vars[n]['dimension']):
  2549. coeffs_and_deps = dimension_exprs.get(d)
  2550. if coeffs_and_deps is None:
  2551. # d is `:` or `*` or a constant expression
  2552. pass
  2553. elif n_is_input:
  2554. # n is an input array argument and its shape
  2555. # may define variables used in dimension
  2556. # specifications.
  2557. for v, (solver, deps) in coeffs_and_deps.items():
  2558. def compute_deps(v, deps):
  2559. for v1 in coeffs_and_deps.get(v, [None, []])[1]:
  2560. if v1 not in deps:
  2561. deps.add(v1)
  2562. compute_deps(v1, deps)
  2563. all_deps = set()
  2564. compute_deps(v, all_deps)
  2565. if ((v in n_deps
  2566. or '=' in vars[v]
  2567. or 'depend' in vars[v])):
  2568. # Skip a variable that
  2569. # - n depends on
  2570. # - has user-defined initialization expression
  2571. # - has user-defined dependencies
  2572. continue
  2573. if solver is not None and v not in all_deps:
  2574. # v can be solved from d, hence, we
  2575. # make it an optional argument with
  2576. # initialization expression:
  2577. is_required = False
  2578. init = solver(symbolic.as_symbol(
  2579. f'shape({n}, {i})'))
  2580. init = init.tostring(
  2581. language=symbolic.Language.C)
  2582. vars[v]['='] = init
  2583. # n needs to be initialized before v. So,
  2584. # making v dependent on n and on any
  2585. # variables in solver or d.
  2586. vars[v]['depend'] = [n] + deps
  2587. if 'check' not in vars[v]:
  2588. # add check only when no
  2589. # user-specified checks exist
  2590. vars[v]['check'] = [
  2591. f'shape({n}, {i}) == {d}']
  2592. else:
  2593. # d is a non-linear function on v,
  2594. # hence, v must be a required input
  2595. # argument that n will depend on
  2596. is_required = True
  2597. if 'intent' not in vars[v]:
  2598. vars[v]['intent'] = []
  2599. if 'in' not in vars[v]['intent']:
  2600. vars[v]['intent'].append('in')
  2601. # v needs to be initialized before n
  2602. n_deps.append(v)
  2603. n_checks.append(
  2604. f'shape({n}, {i}) == {d}')
  2605. v_attr = vars[v].get('attrspec', [])
  2606. if not ('optional' in v_attr
  2607. or 'required' in v_attr):
  2608. v_attr.append(
  2609. 'required' if is_required else 'optional')
  2610. if v_attr:
  2611. vars[v]['attrspec'] = v_attr
  2612. if coeffs_and_deps is not None:
  2613. # extend v dependencies with ones specified in attrspec
  2614. for v, (solver, deps) in coeffs_and_deps.items():
  2615. v_deps = vars[v].get('depend', [])
  2616. for aa in vars[v].get('attrspec', []):
  2617. if aa.startswith('depend'):
  2618. aa = ''.join(aa.split())
  2619. v_deps.extend(aa[7:-1].split(','))
  2620. if v_deps:
  2621. vars[v]['depend'] = list(set(v_deps))
  2622. if n not in v_deps:
  2623. n_deps.append(v)
  2624. elif isstring(vars[n]):
  2625. if 'charselector' in vars[n]:
  2626. if '*' in vars[n]['charselector']:
  2627. length = _eval_length(vars[n]['charselector']['*'],
  2628. params)
  2629. vars[n]['charselector']['*'] = length
  2630. elif 'len' in vars[n]['charselector']:
  2631. length = _eval_length(vars[n]['charselector']['len'],
  2632. params)
  2633. del vars[n]['charselector']['len']
  2634. vars[n]['charselector']['*'] = length
  2635. if n_checks:
  2636. vars[n]['check'] = n_checks
  2637. if n_deps:
  2638. vars[n]['depend'] = list(set(n_deps))
  2639. if '=' in vars[n]:
  2640. if 'attrspec' not in vars[n]:
  2641. vars[n]['attrspec'] = []
  2642. if ('optional' not in vars[n]['attrspec']) and \
  2643. ('required' not in vars[n]['attrspec']):
  2644. vars[n]['attrspec'].append('optional')
  2645. if 'depend' not in vars[n]:
  2646. vars[n]['depend'] = []
  2647. for v, m in list(dep_matches.items()):
  2648. if m(vars[n]['=']):
  2649. vars[n]['depend'].append(v)
  2650. if not vars[n]['depend']:
  2651. del vars[n]['depend']
  2652. if isscalar(vars[n]):
  2653. vars[n]['='] = _eval_scalar(vars[n]['='], params)
  2654. for n in list(vars.keys()):
  2655. if n == block['name']: # n is block name
  2656. if 'note' in vars[n]:
  2657. block['note'] = vars[n]['note']
  2658. if block['block'] == 'function':
  2659. if 'result' in block and block['result'] in vars:
  2660. vars[n] = appenddecl(vars[n], vars[block['result']])
  2661. if 'prefix' in block:
  2662. pr = block['prefix']
  2663. pr1 = pr.replace('pure', '')
  2664. ispure = (not pr == pr1)
  2665. pr = pr1.replace('recursive', '')
  2666. isrec = (not pr == pr1)
  2667. m = typespattern[0].match(pr)
  2668. if m:
  2669. typespec, selector, attr, edecl = cracktypespec0(
  2670. m.group('this'), m.group('after'))
  2671. kindselect, charselect, typename = cracktypespec(
  2672. typespec, selector)
  2673. vars[n]['typespec'] = typespec
  2674. if kindselect:
  2675. if 'kind' in kindselect:
  2676. try:
  2677. kindselect['kind'] = eval(
  2678. kindselect['kind'], {}, params)
  2679. except Exception:
  2680. pass
  2681. vars[n]['kindselector'] = kindselect
  2682. if charselect:
  2683. vars[n]['charselector'] = charselect
  2684. if typename:
  2685. vars[n]['typename'] = typename
  2686. if ispure:
  2687. vars[n] = setattrspec(vars[n], 'pure')
  2688. if isrec:
  2689. vars[n] = setattrspec(vars[n], 'recursive')
  2690. else:
  2691. outmess(
  2692. 'analyzevars: prefix (%s) were not used\n' % repr(block['prefix']))
  2693. if not block['block'] in ['module', 'pythonmodule', 'python module', 'block data']:
  2694. if 'commonvars' in block:
  2695. neededvars = copy.copy(block['args'] + block['commonvars'])
  2696. else:
  2697. neededvars = copy.copy(block['args'])
  2698. for n in list(vars.keys()):
  2699. if l_or(isintent_callback, isintent_aux)(vars[n]):
  2700. neededvars.append(n)
  2701. if 'entry' in block:
  2702. neededvars.extend(list(block['entry'].keys()))
  2703. for k in list(block['entry'].keys()):
  2704. for n in block['entry'][k]:
  2705. if n not in neededvars:
  2706. neededvars.append(n)
  2707. if block['block'] == 'function':
  2708. if 'result' in block:
  2709. neededvars.append(block['result'])
  2710. else:
  2711. neededvars.append(block['name'])
  2712. if block['block'] in ['subroutine', 'function']:
  2713. name = block['name']
  2714. if name in vars and 'intent' in vars[name]:
  2715. block['intent'] = vars[name]['intent']
  2716. if block['block'] == 'type':
  2717. neededvars.extend(list(vars.keys()))
  2718. for n in list(vars.keys()):
  2719. if n not in neededvars:
  2720. del vars[n]
  2721. return vars
  2722. analyzeargs_re_1 = re.compile(r'\A[a-z]+[\w$]*\Z', re.I)
  2723. def expr2name(a, block, args=[]):
  2724. orig_a = a
  2725. a_is_expr = not analyzeargs_re_1.match(a)
  2726. if a_is_expr: # `a` is an expression
  2727. implicitrules, attrrules = buildimplicitrules(block)
  2728. at = determineexprtype(a, block['vars'], implicitrules)
  2729. na = 'e_'
  2730. for c in a:
  2731. c = c.lower()
  2732. if c not in string.ascii_lowercase + string.digits:
  2733. c = '_'
  2734. na = na + c
  2735. if na[-1] == '_':
  2736. na = na + 'e'
  2737. else:
  2738. na = na + '_e'
  2739. a = na
  2740. while a in block['vars'] or a in block['args']:
  2741. a = a + 'r'
  2742. if a in args:
  2743. k = 1
  2744. while a + str(k) in args:
  2745. k = k + 1
  2746. a = a + str(k)
  2747. if a_is_expr:
  2748. block['vars'][a] = at
  2749. else:
  2750. if a not in block['vars']:
  2751. if orig_a in block['vars']:
  2752. block['vars'][a] = block['vars'][orig_a]
  2753. else:
  2754. block['vars'][a] = {}
  2755. if 'externals' in block and orig_a in block['externals'] + block['interfaced']:
  2756. block['vars'][a] = setattrspec(block['vars'][a], 'external')
  2757. return a
  2758. def analyzeargs(block):
  2759. setmesstext(block)
  2760. implicitrules, _ = buildimplicitrules(block)
  2761. if 'args' not in block:
  2762. block['args'] = []
  2763. args = []
  2764. for a in block['args']:
  2765. a = expr2name(a, block, args)
  2766. args.append(a)
  2767. block['args'] = args
  2768. if 'entry' in block:
  2769. for k, args1 in list(block['entry'].items()):
  2770. for a in args1:
  2771. if a not in block['vars']:
  2772. block['vars'][a] = {}
  2773. for b in block['body']:
  2774. if b['name'] in args:
  2775. if 'externals' not in block:
  2776. block['externals'] = []
  2777. if b['name'] not in block['externals']:
  2778. block['externals'].append(b['name'])
  2779. if 'result' in block and block['result'] not in block['vars']:
  2780. block['vars'][block['result']] = {}
  2781. return block
  2782. determineexprtype_re_1 = re.compile(r'\A\(.+?,.+?\)\Z', re.I)
  2783. determineexprtype_re_2 = re.compile(r'\A[+-]?\d+(_(?P<name>\w+)|)\Z', re.I)
  2784. determineexprtype_re_3 = re.compile(
  2785. r'\A[+-]?[\d.]+[-\d+de.]*(_(?P<name>\w+)|)\Z', re.I)
  2786. determineexprtype_re_4 = re.compile(r'\A\(.*\)\Z', re.I)
  2787. determineexprtype_re_5 = re.compile(r'\A(?P<name>\w+)\s*\(.*?\)\s*\Z', re.I)
  2788. def _ensure_exprdict(r):
  2789. if isinstance(r, int):
  2790. return {'typespec': 'integer'}
  2791. if isinstance(r, float):
  2792. return {'typespec': 'real'}
  2793. if isinstance(r, complex):
  2794. return {'typespec': 'complex'}
  2795. if isinstance(r, dict):
  2796. return r
  2797. raise AssertionError(repr(r))
  2798. def determineexprtype(expr, vars, rules={}):
  2799. if expr in vars:
  2800. return _ensure_exprdict(vars[expr])
  2801. expr = expr.strip()
  2802. if determineexprtype_re_1.match(expr):
  2803. return {'typespec': 'complex'}
  2804. m = determineexprtype_re_2.match(expr)
  2805. if m:
  2806. if 'name' in m.groupdict() and m.group('name'):
  2807. outmess(
  2808. 'determineexprtype: selected kind types not supported (%s)\n' % repr(expr))
  2809. return {'typespec': 'integer'}
  2810. m = determineexprtype_re_3.match(expr)
  2811. if m:
  2812. if 'name' in m.groupdict() and m.group('name'):
  2813. outmess(
  2814. 'determineexprtype: selected kind types not supported (%s)\n' % repr(expr))
  2815. return {'typespec': 'real'}
  2816. for op in ['+', '-', '*', '/']:
  2817. for e in [x.strip() for x in markoutercomma(expr, comma=op).split('@' + op + '@')]:
  2818. if e in vars:
  2819. return _ensure_exprdict(vars[e])
  2820. t = {}
  2821. if determineexprtype_re_4.match(expr): # in parenthesis
  2822. t = determineexprtype(expr[1:-1], vars, rules)
  2823. else:
  2824. m = determineexprtype_re_5.match(expr)
  2825. if m:
  2826. rn = m.group('name')
  2827. t = determineexprtype(m.group('name'), vars, rules)
  2828. if t and 'attrspec' in t:
  2829. del t['attrspec']
  2830. if not t:
  2831. if rn[0] in rules:
  2832. return _ensure_exprdict(rules[rn[0]])
  2833. if expr[0] in '\'"':
  2834. return {'typespec': 'character', 'charselector': {'*': '*'}}
  2835. if not t:
  2836. outmess(
  2837. 'determineexprtype: could not determine expressions (%s) type.\n' % (repr(expr)))
  2838. return t
  2839. ######
  2840. def crack2fortrangen(block, tab='\n', as_interface=False):
  2841. global skipfuncs, onlyfuncs
  2842. setmesstext(block)
  2843. ret = ''
  2844. if isinstance(block, list):
  2845. for g in block:
  2846. if g and g['block'] in ['function', 'subroutine']:
  2847. if g['name'] in skipfuncs:
  2848. continue
  2849. if onlyfuncs and g['name'] not in onlyfuncs:
  2850. continue
  2851. ret = ret + crack2fortrangen(g, tab, as_interface=as_interface)
  2852. return ret
  2853. prefix = ''
  2854. name = ''
  2855. args = ''
  2856. blocktype = block['block']
  2857. if blocktype == 'program':
  2858. return ''
  2859. argsl = []
  2860. if 'name' in block:
  2861. name = block['name']
  2862. if 'args' in block:
  2863. vars = block['vars']
  2864. for a in block['args']:
  2865. a = expr2name(a, block, argsl)
  2866. if not isintent_callback(vars[a]):
  2867. argsl.append(a)
  2868. if block['block'] == 'function' or argsl:
  2869. args = '(%s)' % ','.join(argsl)
  2870. f2pyenhancements = ''
  2871. if 'f2pyenhancements' in block:
  2872. for k in list(block['f2pyenhancements'].keys()):
  2873. f2pyenhancements = '%s%s%s %s' % (
  2874. f2pyenhancements, tab + tabchar, k, block['f2pyenhancements'][k])
  2875. intent_lst = block.get('intent', [])[:]
  2876. if blocktype == 'function' and 'callback' in intent_lst:
  2877. intent_lst.remove('callback')
  2878. if intent_lst:
  2879. f2pyenhancements = '%s%sintent(%s) %s' %\
  2880. (f2pyenhancements, tab + tabchar,
  2881. ','.join(intent_lst), name)
  2882. use = ''
  2883. if 'use' in block:
  2884. use = use2fortran(block['use'], tab + tabchar)
  2885. common = ''
  2886. if 'common' in block:
  2887. common = common2fortran(block['common'], tab + tabchar)
  2888. if name == 'unknown_interface':
  2889. name = ''
  2890. result = ''
  2891. if 'result' in block:
  2892. result = ' result (%s)' % block['result']
  2893. if block['result'] not in argsl:
  2894. argsl.append(block['result'])
  2895. body = crack2fortrangen(block['body'], tab + tabchar, as_interface=as_interface)
  2896. vars = vars2fortran(
  2897. block, block['vars'], argsl, tab + tabchar, as_interface=as_interface)
  2898. mess = ''
  2899. if 'from' in block and not as_interface:
  2900. mess = '! in %s' % block['from']
  2901. if 'entry' in block:
  2902. entry_stmts = ''
  2903. for k, i in list(block['entry'].items()):
  2904. entry_stmts = '%s%sentry %s(%s)' \
  2905. % (entry_stmts, tab + tabchar, k, ','.join(i))
  2906. body = body + entry_stmts
  2907. if blocktype == 'block data' and name == '_BLOCK_DATA_':
  2908. name = ''
  2909. ret = '%s%s%s %s%s%s %s%s%s%s%s%s%send %s %s' % (
  2910. tab, prefix, blocktype, name, args, result, mess, f2pyenhancements, use, vars, common, body, tab, blocktype, name)
  2911. return ret
  2912. def common2fortran(common, tab=''):
  2913. ret = ''
  2914. for k in list(common.keys()):
  2915. if k == '_BLNK_':
  2916. ret = '%s%scommon %s' % (ret, tab, ','.join(common[k]))
  2917. else:
  2918. ret = '%s%scommon /%s/ %s' % (ret, tab, k, ','.join(common[k]))
  2919. return ret
  2920. def use2fortran(use, tab=''):
  2921. ret = ''
  2922. for m in list(use.keys()):
  2923. ret = '%s%suse %s,' % (ret, tab, m)
  2924. if use[m] == {}:
  2925. if ret and ret[-1] == ',':
  2926. ret = ret[:-1]
  2927. continue
  2928. if 'only' in use[m] and use[m]['only']:
  2929. ret = '%s only:' % (ret)
  2930. if 'map' in use[m] and use[m]['map']:
  2931. c = ' '
  2932. for k in list(use[m]['map'].keys()):
  2933. if k == use[m]['map'][k]:
  2934. ret = '%s%s%s' % (ret, c, k)
  2935. c = ','
  2936. else:
  2937. ret = '%s%s%s=>%s' % (ret, c, k, use[m]['map'][k])
  2938. c = ','
  2939. if ret and ret[-1] == ',':
  2940. ret = ret[:-1]
  2941. return ret
  2942. def true_intent_list(var):
  2943. lst = var['intent']
  2944. ret = []
  2945. for intent in lst:
  2946. try:
  2947. f = globals()['isintent_%s' % intent]
  2948. except KeyError:
  2949. pass
  2950. else:
  2951. if f(var):
  2952. ret.append(intent)
  2953. return ret
  2954. def vars2fortran(block, vars, args, tab='', as_interface=False):
  2955. """
  2956. TODO:
  2957. public sub
  2958. ...
  2959. """
  2960. setmesstext(block)
  2961. ret = ''
  2962. nout = []
  2963. for a in args:
  2964. if a in block['vars']:
  2965. nout.append(a)
  2966. if 'commonvars' in block:
  2967. for a in block['commonvars']:
  2968. if a in vars:
  2969. if a not in nout:
  2970. nout.append(a)
  2971. else:
  2972. errmess(
  2973. 'vars2fortran: Confused?!: "%s" is not defined in vars.\n' % a)
  2974. if 'varnames' in block:
  2975. nout.extend(block['varnames'])
  2976. if not as_interface:
  2977. for a in list(vars.keys()):
  2978. if a not in nout:
  2979. nout.append(a)
  2980. for a in nout:
  2981. if 'depend' in vars[a]:
  2982. for d in vars[a]['depend']:
  2983. if d in vars and 'depend' in vars[d] and a in vars[d]['depend']:
  2984. errmess(
  2985. 'vars2fortran: Warning: cross-dependence between variables "%s" and "%s"\n' % (a, d))
  2986. if 'externals' in block and a in block['externals']:
  2987. if isintent_callback(vars[a]):
  2988. ret = '%s%sintent(callback) %s' % (ret, tab, a)
  2989. ret = '%s%sexternal %s' % (ret, tab, a)
  2990. if isoptional(vars[a]):
  2991. ret = '%s%soptional %s' % (ret, tab, a)
  2992. if a in vars and 'typespec' not in vars[a]:
  2993. continue
  2994. cont = 1
  2995. for b in block['body']:
  2996. if a == b['name'] and b['block'] == 'function':
  2997. cont = 0
  2998. break
  2999. if cont:
  3000. continue
  3001. if a not in vars:
  3002. show(vars)
  3003. outmess('vars2fortran: No definition for argument "%s".\n' % a)
  3004. continue
  3005. if a == block['name']:
  3006. if block['block'] != 'function' or block.get('result'):
  3007. # 1) skip declaring a variable that name matches with
  3008. # subroutine name
  3009. # 2) skip declaring function when its type is
  3010. # declared via `result` construction
  3011. continue
  3012. if 'typespec' not in vars[a]:
  3013. if 'attrspec' in vars[a] and 'external' in vars[a]['attrspec']:
  3014. if a in args:
  3015. ret = '%s%sexternal %s' % (ret, tab, a)
  3016. continue
  3017. show(vars[a])
  3018. outmess('vars2fortran: No typespec for argument "%s".\n' % a)
  3019. continue
  3020. vardef = vars[a]['typespec']
  3021. if vardef == 'type' and 'typename' in vars[a]:
  3022. vardef = '%s(%s)' % (vardef, vars[a]['typename'])
  3023. selector = {}
  3024. if 'kindselector' in vars[a]:
  3025. selector = vars[a]['kindselector']
  3026. elif 'charselector' in vars[a]:
  3027. selector = vars[a]['charselector']
  3028. if '*' in selector:
  3029. if selector['*'] in ['*', ':']:
  3030. vardef = '%s*(%s)' % (vardef, selector['*'])
  3031. else:
  3032. vardef = '%s*%s' % (vardef, selector['*'])
  3033. else:
  3034. if 'len' in selector:
  3035. vardef = '%s(len=%s' % (vardef, selector['len'])
  3036. if 'kind' in selector:
  3037. vardef = '%s,kind=%s)' % (vardef, selector['kind'])
  3038. else:
  3039. vardef = '%s)' % (vardef)
  3040. elif 'kind' in selector:
  3041. vardef = '%s(kind=%s)' % (vardef, selector['kind'])
  3042. c = ' '
  3043. if 'attrspec' in vars[a]:
  3044. attr = [l for l in vars[a]['attrspec']
  3045. if l not in ['external']]
  3046. if as_interface and 'intent(in)' in attr and 'intent(out)' in attr:
  3047. # In Fortran, intent(in, out) are conflicting while
  3048. # intent(in, out) can be specified only via
  3049. # `!f2py intent(out) ..`.
  3050. # So, for the Fortran interface, we'll drop
  3051. # intent(out) to resolve the conflict.
  3052. attr.remove('intent(out)')
  3053. if attr:
  3054. vardef = '%s, %s' % (vardef, ','.join(attr))
  3055. c = ','
  3056. if 'dimension' in vars[a]:
  3057. vardef = '%s%sdimension(%s)' % (
  3058. vardef, c, ','.join(vars[a]['dimension']))
  3059. c = ','
  3060. if 'intent' in vars[a]:
  3061. lst = true_intent_list(vars[a])
  3062. if lst:
  3063. vardef = '%s%sintent(%s)' % (vardef, c, ','.join(lst))
  3064. c = ','
  3065. if 'check' in vars[a]:
  3066. vardef = '%s%scheck(%s)' % (vardef, c, ','.join(vars[a]['check']))
  3067. c = ','
  3068. if 'depend' in vars[a]:
  3069. vardef = '%s%sdepend(%s)' % (
  3070. vardef, c, ','.join(vars[a]['depend']))
  3071. c = ','
  3072. if '=' in vars[a]:
  3073. v = vars[a]['=']
  3074. if vars[a]['typespec'] in ['complex', 'double complex']:
  3075. try:
  3076. v = eval(v)
  3077. v = '(%s,%s)' % (v.real, v.imag)
  3078. except Exception:
  3079. pass
  3080. vardef = '%s :: %s=%s' % (vardef, a, v)
  3081. else:
  3082. vardef = '%s :: %s' % (vardef, a)
  3083. ret = '%s%s%s' % (ret, tab, vardef)
  3084. return ret
  3085. ######
  3086. # We expose post_processing_hooks as global variable so that
  3087. # user-libraries could register their own hooks to f2py.
  3088. post_processing_hooks = []
  3089. def crackfortran(files):
  3090. global usermodules, post_processing_hooks
  3091. outmess('Reading fortran codes...\n', 0)
  3092. readfortrancode(files, crackline)
  3093. outmess('Post-processing...\n', 0)
  3094. usermodules = []
  3095. postlist = postcrack(grouplist[0])
  3096. outmess('Applying post-processing hooks...\n', 0)
  3097. for hook in post_processing_hooks:
  3098. outmess(f' {hook.__name__}\n', 0)
  3099. postlist = traverse(postlist, hook)
  3100. outmess('Post-processing (stage 2)...\n', 0)
  3101. postlist = postcrack2(postlist)
  3102. return usermodules + postlist
  3103. def crack2fortran(block):
  3104. global f2py_version
  3105. pyf = crack2fortrangen(block) + '\n'
  3106. header = """! -*- f90 -*-
  3107. ! Note: the context of this file is case sensitive.
  3108. """
  3109. footer = """
  3110. ! This file was auto-generated with f2py (version:%s).
  3111. ! See:
  3112. ! https://web.archive.org/web/20140822061353/http://cens.ioc.ee/projects/f2py2e
  3113. """ % (f2py_version)
  3114. return header + pyf + footer
  3115. def _is_visit_pair(obj):
  3116. return (isinstance(obj, tuple)
  3117. and len(obj) == 2
  3118. and isinstance(obj[0], (int, str)))
  3119. def traverse(obj, visit, parents=[], result=None, *args, **kwargs):
  3120. '''Traverse f2py data structure with the following visit function:
  3121. def visit(item, parents, result, *args, **kwargs):
  3122. """
  3123. parents is a list of key-"f2py data structure" pairs from which
  3124. items are taken from.
  3125. result is a f2py data structure that is filled with the
  3126. return value of the visit function.
  3127. item is 2-tuple (index, value) if parents[-1][1] is a list
  3128. item is 2-tuple (key, value) if parents[-1][1] is a dict
  3129. The return value of visit must be None, or of the same kind as
  3130. item, that is, if parents[-1] is a list, the return value must
  3131. be 2-tuple (new_index, new_value), or if parents[-1] is a
  3132. dict, the return value must be 2-tuple (new_key, new_value).
  3133. If new_index or new_value is None, the return value of visit
  3134. is ignored, that is, it will not be added to the result.
  3135. If the return value is None, the content of obj will be
  3136. traversed, otherwise not.
  3137. """
  3138. '''
  3139. if _is_visit_pair(obj):
  3140. if obj[0] == 'parent_block':
  3141. # avoid infinite recursion
  3142. return obj
  3143. new_result = visit(obj, parents, result, *args, **kwargs)
  3144. if new_result is not None:
  3145. assert _is_visit_pair(new_result)
  3146. return new_result
  3147. parent = obj
  3148. result_key, obj = obj
  3149. else:
  3150. parent = (None, obj)
  3151. result_key = None
  3152. if isinstance(obj, list):
  3153. new_result = []
  3154. for index, value in enumerate(obj):
  3155. new_index, new_item = traverse((index, value), visit,
  3156. parents=parents + [parent],
  3157. result=result, *args, **kwargs)
  3158. if new_index is not None:
  3159. new_result.append(new_item)
  3160. elif isinstance(obj, dict):
  3161. new_result = dict()
  3162. for key, value in obj.items():
  3163. new_key, new_value = traverse((key, value), visit,
  3164. parents=parents + [parent],
  3165. result=result, *args, **kwargs)
  3166. if new_key is not None:
  3167. new_result[new_key] = new_value
  3168. else:
  3169. new_result = obj
  3170. if result_key is None:
  3171. return new_result
  3172. return result_key, new_result
  3173. def character_backward_compatibility_hook(item, parents, result,
  3174. *args, **kwargs):
  3175. """Previously, Fortran character was incorrectly treated as
  3176. character*1. This hook fixes the usage of the corresponding
  3177. variables in `check`, `dimension`, `=`, and `callstatement`
  3178. expressions.
  3179. The usage of `char*` in `callprotoargument` expression can be left
  3180. unchanged because C `character` is C typedef of `char`, although,
  3181. new implementations should use `character*` in the corresponding
  3182. expressions.
  3183. See https://github.com/numpy/numpy/pull/19388 for more information.
  3184. """
  3185. parent_key, parent_value = parents[-1]
  3186. key, value = item
  3187. def fix_usage(varname, value):
  3188. value = re.sub(r'[*]\s*\b' + varname + r'\b', varname, value)
  3189. value = re.sub(r'\b' + varname + r'\b\s*[\[]\s*0\s*[\]]',
  3190. varname, value)
  3191. return value
  3192. if parent_key in ['dimension', 'check']:
  3193. assert parents[-3][0] == 'vars'
  3194. vars_dict = parents[-3][1]
  3195. elif key == '=':
  3196. assert parents[-2][0] == 'vars'
  3197. vars_dict = parents[-2][1]
  3198. else:
  3199. vars_dict = None
  3200. new_value = None
  3201. if vars_dict is not None:
  3202. new_value = value
  3203. for varname, vd in vars_dict.items():
  3204. if ischaracter(vd):
  3205. new_value = fix_usage(varname, new_value)
  3206. elif key == 'callstatement':
  3207. vars_dict = parents[-2][1]['vars']
  3208. new_value = value
  3209. for varname, vd in vars_dict.items():
  3210. if ischaracter(vd):
  3211. # replace all occurrences of `<varname>` with
  3212. # `&<varname>` in argument passing
  3213. new_value = re.sub(
  3214. r'(?<![&])\b' + varname + r'\b', '&' + varname, new_value)
  3215. if new_value is not None:
  3216. if new_value != value:
  3217. # We report the replacements here so that downstream
  3218. # software could update their source codes
  3219. # accordingly. However, such updates are recommended only
  3220. # when BC with numpy 1.21 or older is not required.
  3221. outmess(f'character_bc_hook[{parent_key}.{key}]:'
  3222. f' replaced `{value}` -> `{new_value}`\n', 1)
  3223. return (key, new_value)
  3224. post_processing_hooks.append(character_backward_compatibility_hook)
  3225. if __name__ == "__main__":
  3226. files = []
  3227. funcs = []
  3228. f = 1
  3229. f2 = 0
  3230. f3 = 0
  3231. showblocklist = 0
  3232. for l in sys.argv[1:]:
  3233. if l == '':
  3234. pass
  3235. elif l[0] == ':':
  3236. f = 0
  3237. elif l == '-quiet':
  3238. quiet = 1
  3239. verbose = 0
  3240. elif l == '-verbose':
  3241. verbose = 2
  3242. quiet = 0
  3243. elif l == '-fix':
  3244. if strictf77:
  3245. outmess(
  3246. 'Use option -f90 before -fix if Fortran 90 code is in fix form.\n', 0)
  3247. skipemptyends = 1
  3248. sourcecodeform = 'fix'
  3249. elif l == '-skipemptyends':
  3250. skipemptyends = 1
  3251. elif l == '--ignore-contains':
  3252. ignorecontains = 1
  3253. elif l == '-f77':
  3254. strictf77 = 1
  3255. sourcecodeform = 'fix'
  3256. elif l == '-f90':
  3257. strictf77 = 0
  3258. sourcecodeform = 'free'
  3259. skipemptyends = 1
  3260. elif l == '-h':
  3261. f2 = 1
  3262. elif l == '-show':
  3263. showblocklist = 1
  3264. elif l == '-m':
  3265. f3 = 1
  3266. elif l[0] == '-':
  3267. errmess('Unknown option %s\n' % repr(l))
  3268. elif f2:
  3269. f2 = 0
  3270. pyffilename = l
  3271. elif f3:
  3272. f3 = 0
  3273. f77modulename = l
  3274. elif f:
  3275. try:
  3276. open(l).close()
  3277. files.append(l)
  3278. except OSError as detail:
  3279. errmess(f'OSError: {detail!s}\n')
  3280. else:
  3281. funcs.append(l)
  3282. if not strictf77 and f77modulename and not skipemptyends:
  3283. outmess("""\
  3284. Warning: You have specified module name for non Fortran 77 code that
  3285. should not need one (expect if you are scanning F90 code for non
  3286. module blocks but then you should use flag -skipemptyends and also
  3287. be sure that the files do not contain programs without program
  3288. statement).
  3289. """, 0)
  3290. postlist = crackfortran(files)
  3291. if pyffilename:
  3292. outmess('Writing fortran code to file %s\n' % repr(pyffilename), 0)
  3293. pyf = crack2fortran(postlist)
  3294. with open(pyffilename, 'w') as f:
  3295. f.write(pyf)
  3296. if showblocklist:
  3297. show(postlist)