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