12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191(*s: common.ml *)(* Yoann Padioleau
*
* Copyright (C) 1998-2009 Yoann Padioleau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation, with the
* special exception on linking described in file license.txt.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file
* license.txt for more details.
*)(*x: common.ml *)(*###########################################################################*)(* Prelude *)(*###########################################################################*)(*****************************************************************************)(* Prelude *)(*****************************************************************************)(* The following functions should be in their respective sections but
* because some functions in some sections use functions in other
* sections, and because I don't want to take care of the order of
* those sections, of those dependencies, I put the functions causing
* dependency problem here. C is better than caml on this with the
* ability to declare prototype, enabling some form of forward
* reference.
*)let(+>)of=folet(|>)of=fo(*let (++) = (@) *)exceptionTimeoutexceptionUnixExitofintletrec(do_n:int->(unit->unit)->unit)=funif->ifi=0then()else(f();do_n(i-1)f)letrec(foldn:('a->int->'a)->'a->int->'a)=funfacci->ifi=0thenaccelsefoldnf(facci)(i-1)letsum_int=List.fold_left(+)0(* could really call it 'for' :) *)letfold_left_with_indexfacc=letrecfold_lwi_auxaccn=function|[]->acc|x::xs->fold_lwi_aux(faccxn)(n+1)xsinfold_lwi_auxacc0letrecdropnxs=match(n,xs)with|(0,_)->xs|(_,[])->failwith"drop: not enough"|(n,x::xs)->drop(n-1)xsletrecenum_origxn=ifx=nthen[n]elsex::enum_orig(x+1)nletenumxn=ifnot(x<=n)thenfailwith(Printf.sprintf"bad values in enum, expect %d <= %d"xn);letrecenum_auxaccxn=ifx=nthenn::accelseenum_aux(x::acc)(x+1)ninList.rev(enum_aux[]xn)letenum_safexn=ifx>nthen[]elseenumxnletrectakenxs=match(n,xs)with|(0,_)->[]|(_,[])->failwith"Common.take: not enough"|(n,x::xs)->x::take(n-1)xsletexcludepxs=List.filter(funx->not(px))xsletlast_nnl=List.rev(taken(List.revl))(*let last l = List.hd (last_n 1 l) *)letreclist_last=function|[]->raiseNot_found|[x]->x|x::y::xs->list_last(y::xs)let(list_of_string:string->charlist)=function""->[]|s->(enum0((String.lengths)-1)+>List.map(String.gets))let(lines:string->stringlist)=funs->letreclines_aux=function|[]->[]|[x]->ifx=""then[]else[x]|x::xs->x::lines_auxxsinStr.split_delim(Str.regexp"\n")s+>lines_auxletpushvl=l:=v::!lletnullxs=matchxswith[]->true|_->falseletcommand2s=ignore(Sys.commands)let(matched:int->string->string)=funis->Str.matched_groupisletmatched1=funs->matched1sletmatched2=funs->(matched1s,matched2s)letmatched3=funs->(matched1s,matched2s,matched3s)letmatched4=funs->(matched1s,matched2s,matched3s,matched4s)letmatched5=funs->(matched1s,matched2s,matched3s,matched4s,matched5s)letmatched6=funs->(matched1s,matched2s,matched3s,matched4s,matched5s,matched6s)letmatched7=funs->(matched1s,matched2s,matched3s,matched4s,matched5s,matched6s,matched7s)let(with_open_stringbuf:(((string->unit)*Buffer.t)->unit)->string)=funf->letbuf=Buffer.create1000inletprs=Buffer.add_stringbuf(s^"\n")inf(pr,buf);Buffer.contentsbufletfoldl1pxs=matchxswith|x::xs->List.fold_leftpxxs|[]->failwith"foldl1: empty list"letrecrepeaten=letrecrepeat_auxacc=function|0->acc|nwhenn<0->failwith"repeat"|n->repeat_aux(e::acc)(n-1)inrepeat_aux[]n(*###########################################################################*)(* Basic features *)(*###########################################################################*)(*****************************************************************************)(* Debugging/logging *)(*****************************************************************************)(* I used this in coccinelle where the huge logging of stuff ask for
* a more organized solution that use more visual indentation hints.
*
* todo? could maybe use log4j instead ? or use Format module more
* consistently ?
*)let_tab_level_print=ref0let_tab_indent=5let_prefix_pr=ref""letindent_dof=_tab_level_print:=!_tab_level_print+_tab_indent;Common.finalizef(fun()->_tab_level_print:=!_tab_level_print-_tab_indent;)letprs=print_string!_prefix_pr;do_n!_tab_level_print(fun()->print_string" ");print_strings;print_string"\n";flushstdoutletpr_no_nls=print_string!_prefix_pr;do_n!_tab_level_print(fun()->print_string" ");print_strings;flushstdoutlet_chan_pr2=ref(None:out_channeloption)letout_chan_pr2?(newline=true)s=match!_chan_pr2with|None->()|Somechan->output_stringchan(s^(ifnewlinethen"\n"else""));flushchanletpr2s=prerr_string!_prefix_pr;do_n!_tab_level_print(fun()->prerr_string" ");prerr_strings;prerr_string"\n";flushstderr;out_chan_pr2s;()letpr2_no_nls=prerr_string!_prefix_pr;do_n!_tab_level_print(fun()->prerr_string" ");prerr_strings;flushstderr;out_chan_pr2~newline:falses;()letpr_xxxxxxxxxxxxxxxxx()=pr"-----------------------------------------------------------------------"letpr2_xxxxxxxxxxxxxxxxx()=pr2"-----------------------------------------------------------------------"letreset_pr_indent()=_tab_level_print:=0(* old:
* let pr s = (print_string s; print_string "\n"; flush stdout)
* let pr2 s = (prerr_string s; prerr_string "\n"; flush stderr)
*)(* ---------------------------------------------------------------------- *)(* I can not use the _xxx ref tech that I use for common_extra.ml here because
* ocaml don't like the polymorphism of Dumper mixed with refs.
*
* let (_dump_func : ('a -> string) ref) = ref
* (fun x -> failwith "no dump yet, have you included common_extra.cmo?")
* let (dump : 'a -> string) = fun x ->
* !_dump_func x
*
* So I have included directly dumper.ml in common.ml. It's more practical
* when want to give script that use my common.ml, I just have to give
* this file.
*)(* start of dumper.ml *)(* Dump an OCaml value into a printable string.
* By Richard W.M. Jones (rich@annexia.org).
* dumper.ml 1.2 2005/02/06 12:38:21 rich Exp
*)openPrintfopenObjletrecdump2r=ifis_intrthenstring_of_int(magicr:int)else((* Block. *)letrecget_fieldsacc=function|0->acc|n->letn=n-1inget_fields(fieldrn::acc)ninletrecis_listr=ifis_intrthen(if(magicr:int)=0thentrue(* [] *)elsefalse)else(lets=sizerandt=tagrinift=0&&s=2thenis_list(fieldr1)(* h :: t *)elsefalse)inletrecget_listr=ifis_intrthen[]elseleth=fieldr0andt=get_list(fieldr1)inh::tinletopaquename=(* XXX In future, print the address of value 'r'. Not possible in
* pure OCaml at the moment.
*)"<"^name^">"inlets=sizerandt=tagrin(* From the tag, determine the type of block. *)ifis_listrthen((* List. *)letfields=get_listrin"["^String.concat"; "(List.mapdump2fields)^"]")elseift=0then((* Tuple, array, record. *)letfields=get_fields[]sin"("^String.concat", "(List.mapdump2fields)^")")(* Note that [lazy_tag .. forward_tag] are < no_scan_tag. Not
* clear if very large constructed values could have the same
* tag. XXX *)elseift=lazy_tagthenopaque"lazy"elseift=closure_tagthenopaque"closure"elseift=object_tagthen((* Object. *)letfields=get_fields[]sinletclasz,id,slots=matchfieldswithh::h'::t->h,h',t|_->assertfalsein(* No information on decoding the class (first field). So just print
* out the ID and the slots.
*)"Object #"^dump2id^" ("^String.concat", "(List.mapdump2slots)^")")elseift=infix_tagthenopaque"infix"elseift=forward_tagthenopaque"forward"elseift<no_scan_tagthen((* Constructed value. *)letfields=get_fields[]sin"Tag"^string_of_intt^" ("^String.concat", "(List.mapdump2fields)^")")elseift=string_tagthen("\""^String.escaped(magicr:string)^"\"")elseift=double_tagthen(string_of_float(magicr:float))elseift=abstract_tagthenopaque"abstract"elseift=custom_tagthenopaque"custom"elsefailwith("dump: impossible tag ("^string_of_intt^")"))letdumpv=dump2(reprv)(* end of dumper.ml *)(*
let (dump : 'a -> string) = fun x ->
Dumper.dump x
*)(* ---------------------------------------------------------------------- *)letpr2_genx=pr2(dumpx)(* ---------------------------------------------------------------------- *)letxxx_oncefs=if!Common.disable_pr2_oncethenpr2selseifnot(Hashtbl.memCommon._already_printeds)thenbeginHashtbl.addCommon._already_printedstrue;f("(ONCE) "^s);endletpr2_onces=xxx_oncepr2s(* ---------------------------------------------------------------------- *)letmk_pr2_wrappersaref=letfpr2s=if!arefthenpr2selse(* just to the log file *)out_chan_pr2sinletfpr2_onces=if!arefthenpr2_onceselsexxx_onceout_chan_pr2sinfpr2,fpr2_once(* ---------------------------------------------------------------------- *)(* could also be in File section *)letredirect_stdoutfilef=beginletchan=open_outfileinletdescr=Unix.descr_of_out_channelchaninletsaveout=Unix.dupUnix.stdoutinUnix.dup2descrUnix.stdout;flushstdout;letres=f()influshstdout;Unix.dup2saveoutUnix.stdout;close_outchan;resendletredirect_stdout_optoptfilef=matchoptfilewith|None->f()|Someoutfile->redirect_stdoutoutfilefletredirect_stdout_stderrfilef=beginletchan=open_outfileinletdescr=Unix.descr_of_out_channelchaninletsaveout=Unix.dupUnix.stdoutinletsaveerr=Unix.dupUnix.stderrinUnix.dup2descrUnix.stdout;Unix.dup2descrUnix.stderr;flushstdout;flushstderr;f();flushstdout;flushstderr;Unix.dup2saveoutUnix.stdout;Unix.dup2saveerrUnix.stderr;close_outchan;endletredirect_stdinfilef=beginletchan=open_infileinletdescr=Unix.descr_of_in_channelchaninletsavein=Unix.dupUnix.stdininUnix.dup2descrUnix.stdin;f();Unix.dup2saveinUnix.stdin;close_inchan;endletredirect_stdin_optoptfilef=matchoptfilewith|None->f()|Someinfile->redirect_stdininfilef(* cf end
let with_pr2_to_string f =
*)(* ---------------------------------------------------------------------- *)(* old: include Printf, include are evil and graph_code_cmt does not like them*)(* cf common.mli, fprintf, printf, eprintf, sprintf.
* also what is this ?
* val bprintf : Buffer.t -> ('a, Buffer.t, unit) format -> 'a
* val kprintf : (string -> 'a) -> ('b, unit, string, 'a) format4 -> 'b
*)(* ex of printf:
* printf "%02d" i
* for padding
*)letspf=Printf.sprintf(* ---------------------------------------------------------------------- *)let_chan=refstderrletstart_log_file()=letfilename=(spf"/tmp/debugml%d:%d"(Unix.getuid())(Unix.getpid()))inpr2(spf"now using %s for logging"filename);_chan:=open_outfilenameletdologs=output_string!_chan(s^"\n");flush!_chanletverbose_level=ref1letlogs=if!verbose_level>=1thendologsletlog2s=if!verbose_level>=2thendologsletlog3s=if!verbose_level>=3thendologsletlog4s=if!verbose_level>=4thendologsletif_logf=if!verbose_level>=1thenf()letif_log2f=if!verbose_level>=2thenf()letif_log3f=if!verbose_level>=3thenf()letif_log4f=if!verbose_level>=4thenf()(* ---------------------------------------------------------------------- *)letpause()=(pr2"pause: type return";ignore(read_line()))(* src: from getopt from frish *)letbip()=Printf.printf"\007";flushstdoutletwait()=Unix.sleep1(* was used by fix_caml *)let_trace_var=ref0letadd_var()=incr_trace_varletdec_var()=decr_trace_varletget_var()=!_trace_varlet(print_n:int->string->unit)=funis->do_ni(fun()->print_strings)let(printerr_n:int->string->unit)=funis->do_ni(fun()->prerr_strings)let_debug=reftrueletdebugon()=_debug:=trueletdebugoff()=_debug:=falseletdebugf=if!_debugthenf()else()(*****************************************************************************)(* Profiling *)(*****************************************************************************)(* now near cmd_to_list: let get_mem() = *)letmemory_stat()=letstat=Gc.stat()inletconv_mox=x*4/1000000inPrintf.sprintf"maximal = %d Mo\n"(conv_mostat.Gc.top_heap_words)^Printf.sprintf"current = %d Mo\n"(conv_mostat.Gc.heap_words)^Printf.sprintf"lives = %d Mo\n"(conv_mostat.Gc.live_words)(* Printf.printf "fragments = %d Mo\n" (conv_mo stat.Gc.fragments); *)lettimenow()="sys:"^(string_of_float(Sys.time()))^" seconds"^":real:"^(lettm=Unix.time()+>Unix.gmtimeintm.Unix.tm_min+>string_of_int^" min:"^tm.Unix.tm_sec+>string_of_int^".00 seconds")let_count1=ref0let_count2=ref0let_count3=ref0let_count4=ref0let_count5=ref0letcount1()=incr_count1letcount2()=incr_count2letcount3()=incr_count3letcount4()=incr_count4letcount5()=incr_count5letprofile_diagnostic_basic()=Printf.sprintf"count1 = %d\ncount2 = %d\ncount3 = %d\ncount4 = %d\ncount5 = %d\n"!_count1!_count2!_count3!_count4!_count5lettime_funcf=(* let _ = Timing () in *)letx=f()in(* let _ = Timing () in *)x(*****************************************************************************)(* Test *)(*****************************************************************************)(* See also OUnit *)(* commented because does not play well with js_of_ocaml
*)letexampleb=ifbthen()elsefailwith("ASSERT FAILURE: "^(Printexc.get_backtrace()))let_ex1=assert(enum14=[1;2;3;4])letassert_equalab=ifnot(a=b)thenfailwith("assert_equal: those 2 values are not equal:\n\t"^(dumpa)^"\n\t"^(dumpb)^"\n")let(example2:string->bool->unit)=funsb->tryassertbwithx->failwiths(*-------------------------------------------------------------------*)let_list_bool=ref[]let(example3:string->bool->unit)=funsb->_list_bool:=(s,b)::(!_list_bool)(* could introduce a fun () otherwise the calculus is made at compile time
* and this can be long. This would require to redefine test_all.
* let (example3: string -> (unit -> bool) -> unit) = fun s func ->
* _list_bool := (s,func):: (!_list_bool)
*
* I would like to do as a func that take 2 terms, and make an = over it
* avoid to add this ugly fun (), but pb of type, cant do that :(
*)let(test_all:unit->unit)=fun()->List.iter(fun(s,b)->Printf.printf"%s: %s\n"s(ifbthen"passed"else"failed"))!_list_boollet(test:string->unit)=funs->Printf.printf"%s: %s\n"s(if(List.assocs(!_list_bool))then"passed"else"failed")let(++)ab=Common.profile_code"++"(fun()->a@b)let_ex=example3"++"([1;2]@[3;4;5]=[1;2;3;4;5])(*-------------------------------------------------------------------*)(* Regression testing *)(*-------------------------------------------------------------------*)(* cf end of file. It uses too many other common functions so I
* have put the code at the end of this file.
*)(* todo? take code from julien signoles in calendar-2.0.2/tests *)(*
(* Generic functions used in the tests. *)
val reset : unit -> unit
val nb_ok : unit -> int
val nb_bug : unit -> int
val test : bool -> string -> unit
val test_exn : 'a Lazy.t -> string -> unit
let ok_ref = ref 0
let ok () = incr ok_ref
let nb_ok () = !ok_ref
let bug_ref = ref 0
let bug () = incr bug_ref
let nb_bug () = !bug_ref
let reset () =
ok_ref := 0;
bug_ref := 0
let test x s =
if x then ok () else begin Printf.printf "%s\n" s; bug () end;;
let test_exn x s =
try
ignore (Lazy.force x);
Printf.printf "%s\n" s;
bug ()
with _ ->
ok ();;
*)(*****************************************************************************)(* Quickcheck like (sfl) *)(*****************************************************************************)(* related work:
* - http://cedeela.fr/quickcheck-for-ocaml.html
*)(*---------------------------------------------------------------------------*)(* generators *)(*---------------------------------------------------------------------------*)type'agen=unit->'alet(ig:intgen)=fun()->Random.int10let(lg:('agen)->('alist)gen)=fungen()->foldn(funacci->(gen())::acc)[](Random.int10)let(pg:('agen)->('bgen)->('a*'b)gen)=fungen1gen2()->(gen1(),gen2())letpolyg=iglet(ng:(stringgen))=fun()->"a"^(string_of_int(ig()))let(oneofl:('alist)->'agen)=funxs()->List.nthxs(Random.int(List.lengthxs))(* let oneofl l = oneof (List.map always l) *)let(oneof:(('agen)list)->'agen)=funxs->List.nthxs(Random.int(List.lengthxs))let(always:'a->'agen)=fune()->elet(frequency:((int*('agen))list)->'agen)=funxs->letsums=sum_int(List.mapfstxs)inleti=Random.intsumsinletrecfreq_auxacc=function|(x,g)::xs->ifi<acc+xthengelsefreq_aux(acc+x)xs|_->failwith"frequency"infreq_aux0xsletfrequencyll=frequency(List.map(fun(i,e)->(i,alwayse))l)(*
let b = oneof [always true; always false] ()
let b = frequency [3, always true; 2, always false] ()
*)(* cant do this:
* let rec (lg: ('a gen) -> ('a list) gen) = fun gen -> oneofl [[]; lg gen ()]
* nor
* let rec (lg: ('a gen) -> ('a list) gen) = fun gen -> oneof [always []; lg gen]
*
* because caml is not as lazy as haskell :( fix the pb by introducing a size
* limit. take the bounds/size as parameter. morover this is needed for
* more complex type.
*
* how make a bintreeg ?? we need recursion
*
* let rec (bintreeg: ('a gen) -> ('a bintree) gen) = fun gen () ->
* let rec aux n =
* if n = 0 then (Leaf (gen ()))
* else frequencyl [1, Leaf (gen ()); 4, Branch ((aux (n / 2)), aux (n / 2))]
* ()
* in aux 20
*
*)(*---------------------------------------------------------------------------*)(* property *)(*---------------------------------------------------------------------------*)(* todo: a test_all_laws, better syntax (done already a little with ig in
* place of intg. En cas d'erreur, print the arg that not respect
*
* todo: with monitoring, as in haskell, laws = laws2, no need for 2 func,
* but hard i found
*
* todo classify, collect, forall
*)(* return None when good, and Just the_problematic_case when bad *)let(laws:string->('a->bool)->('agen)->'aoption)=funsfuncgen->letres=foldn(funacci->letn=gen()in(n,funcn)::acc)[]1000inletres=List.filter(fun(x,b)->notb)resinifres=[]thenNoneelseSome(fst(List.hdres))letrec(statistic_number:('alist)->(int*'a)list)=function|[]->[]|x::xs->let(splitg,splitd)=List.partition(funy->y=x)xsin(1+(List.lengthsplitg),x)::(statistic_numbersplitd)(* in pourcentage *)let(statistic:('alist)->(int*'a)list)=funxs->letstat_num=statistic_numberxsinlettotals=sum_int(List.mapfststat_num)inList.map(fun(i,v)->((i*100)/totals),v)stat_numlet(laws2:string->('a->(bool*'b))->('agen)->('aoption*((int*'b)list)))=funsfuncgen->letres=foldn(funacci->letn=gen()in(n,funcn)::acc)[]1000inletstat=statistic(List.map(fun(x,(b,v))->v)res)inletres=List.filter(fun(x,(b,v))->notb)resinifres=[]then(None,stat)else(Some(fst(List.hdres)),stat)(* todo, do with coarbitrary ?? idea is that given a 'a, generate a 'b
* depending of 'a and gen 'b, that is modify gen 'b, what is important is
* that each time given the same 'a, we must get the same 'b !!!
*)(*
let (fg: ('a gen) -> ('b gen) -> ('a -> 'b) gen) = fun gen1 gen2 () ->
let b = laws "funs" (fun (f,g,h) -> x <= y ==> (max x y = y) )(pg ig ig)
*)(*
let one_of xs = List.nth xs (Random.int (List.length xs))
let take_one xs =
if empty xs then failwith "Take_one: empty list"
else
let i = Random.int (List.length xs) in
List.nth xs i, filter_index (fun j _ -> i <> j) xs
*)(*****************************************************************************)(* Persistence *)(*****************************************************************************)letget_valuefilename=letchan=open_infilenameinletx=input_valuechanin(* <=> Marshal.from_channel *)(close_inchan;x)letwrite_valuevalufilename=letchan=open_outfilenamein(output_valuechanvalu;(* <=> Marshal.to_channel *)(* Marshal.to_channel chan valu [Marshal.Closures]; *)close_outchan)letwrite_backfuncfilename=write_value(func(get_valuefilename))filenameletread_valuef=get_valuefletmarshal__to_string2vflags=Marshal.to_stringvflagsletmarshal__to_stringab=Common.profile_code"Marshalling"(fun()->marshal__to_string2ab)letmarshal__from_string2vflags=Marshal.from_stringvflagsletmarshal__from_stringab=Common.profile_code"Marshalling"(fun()->marshal__from_string2ab)(*****************************************************************************)(* Counter *)(*****************************************************************************)let_counter=ref0letcounter()=(_counter:=!_counter+1;!_counter)let_counter2=ref0letcounter2()=(_counter2:=!_counter2+1;!_counter2)let_counter3=ref0letcounter3()=(_counter3:=!_counter3+1;!_counter3)typetimestamp=int(*****************************************************************************)(* String_of *)(*****************************************************************************)(* To work with the macro system autogenerated string_of and print_ function
(kind of deriving a la haskell) *)(* int, bool, char, float, ref ?, string *)letstring_of_strings="\""^s"\""letstring_of_listfxs="["^(xs+>List.mapf+>String.concat";")^"]"letstring_of_unit()="()"letstring_of_arrayfxs="[|"^(xs+>Array.to_list+>List.mapf+>String.concat";")^"|]"letstring_of_optionf=function|None->"None "|Somex->"Some "^(fx)letprint_boolx=print_string(ifxthen"True"else"False")letprint_optionpr=function|None->print_string"None"|Somex->print_string"Some (";prx;print_string")"letprint_listprxs=beginprint_string"[";List.iter(funx->prx;print_string",")xs;print_string"]";end(* specialised
let (string_of_list: char list -> string) =
List.fold_left (fun acc x -> acc^(Char.escaped x)) ""
*)letrecprint_betweenbetweenfn=function|[]->()|[x]->fnx|x::xs->fnx;between();print_betweenbetweenfnxsletadjust_pp_with_indentf=Format.open_box!_tab_level_print;(*Format.force_newline();*)f();Format.close_box();Format.print_newline()letadjust_pp_with_indent_and_headersf=Format.open_box(!_tab_level_print+String.lengths);do_n!_tab_level_print(fun()->Format.print_string" ");Format.print_strings;f();Format.close_box();Format.print_newline()letpp_do_in_boxf=Format.open_box1;f();Format.close_box()letpp_do_in_zero_boxf=Format.open_box0;f();Format.close_box()letpp_f_in_boxf=Format.open_box1;letres=f()inFormat.close_box();resletpps=Format.print_strings(*
* use as
* let category__str_conv = [
* BackGround, "background";
* ForeGround, "ForeGround";
* ...
* ]
*
* let (category_of_string, str_of_category) =
* Common.mk_str_func_of_assoc_conv category__str_conv
*
*)letmk_str_func_of_assoc_convxs=letswap(x,y)=(y,x)in(funs->letxs'=List.mapswapxsinList.assocsxs'),(funa->List.assocaxs)(* julia: convert something printed using format to print into a string *)(* now at bottom of file
let format_to_string f =
...
*)(*****************************************************************************)(* Macro *)(*****************************************************************************)(* put your macro in macro.ml4, and you can test it interactivly as in lisp *)letmacro_expands=letc=open_out"/tmp/ttttt.ml"inbeginoutput_stringcs;close_outc;command2("ocamlc -c -pp 'camlp4o pa_extend.cmo q_MLast.cmo -impl' "^"-I +camlp4 -impl macro.ml4");command2"camlp4o ./macro.cmo pr_o.cmo /tmp/ttttt.ml";command2"rm -f /tmp/ttttt.ml";end(*
let t = macro_expand "{ x + y | (x,y) <- [(1,1);(2,2);(3,3)] and x>2 and y<3}"
let x = { x + y | (x,y) <- [(1,1);(2,2);(3,3)] and x > 2 and y < 3}
let t = macro_expand "{1 .. 10}"
let x = {1 .. 10} +> List.map (fun i -> i)
let t = macro_expand "[1;2] to append to [2;4]"
let t = macro_expand "{x = 2; x = 3}"
let t = macro_expand "type 'a bintree = Leaf of 'a | Branch of ('a bintree * 'a bintree)"
*)(*****************************************************************************)(* Composition/Control *)(*****************************************************************************)(* now in prelude:
* let (+>) o f = f o
*)let(+!>)refof=refo:=f!refo(* alternatives:
* let ((@): 'a -> ('a -> 'b) -> 'b) = fun a b -> b a
* let o f g x = f (g x)
*)let($)fgx=g(fx)letcomposefgx=f(gx)(* dont work :( let ( rond_utf_symbol ) f g x = f(g(x)) *)(* trick to have something similar to the 1 `max` 4 haskell infix notation.
by Keisuke Nakano on the caml mailing list.
> let ( /* ) x y = y x
> and ( */ ) x y = x y
or
let ( <| ) x y = y x
and ( |> ) x y = x y
> Then we can make an infix operator <| f |> for a binary function f.
*)letflipf=funab->fbaletcurryfxy=f(x,y)letuncurryf(a,b)=fabletid=funx->xletconstx=(funy->x)letdo_nothing()=()letrecapplynnfo=ifn=0thenoelseapplyn(n-1)f(fo)letforeverf=whiletruedof();doneclass['a]shared_variable_hook(x:'a)=object(self)valmutabledata=xvalmutableregistered=[]methodsetx=begindata<-x;pr"refresh registered";registered+>List.iter(funf->f());endmethodget=datamethodmodifyf=self#set(fself#get)methodregisterf=registered<-f::registeredend(* src: from aop project. was called ptFix *)letrecfixpointtranselem=letimage=transeleminif(image=elem)thenelem(* point fixe *)elsefixpointtransimage(* le point fixe pour les objets. was called ptFixForObjetct *)letrecfixpoint_for_objecttranselem=letimage=transeleminif(image#equalelem)thenelem(* point fixe *)elsefixpoint_for_objecttransimagelet(add_hook:('a->('a->'b)->'b)ref->('a->('a->'b)->'b)->unit)=funvarf->letoldvar=!varinvar:=funargk->farg(funx->oldvarxk)let(add_hook_action:('a->unit)->('a->unit)listref->unit)=funfhooks->pushfhookslet(run_hooks_action:'a->('a->unit)listref->unit)=funobjhooks->!hooks+>List.iter(funf->tryfobjwith_->())type'amylazy=(unit->'a)(* a la emacs.
* bugfix: add finalize, otherwise exns can mess up the reference
*)letsave_excursionreferencenewvf=letold=!referenceinreference:=newv;Common.finalizef(fun_->reference:=old;)letsave_excursion_and_disablereferencef=save_excursionreferencefalse(fun()->f())letsave_excursion_and_enablereferencef=save_excursionreferencetrue(fun()->f())letmemoized?(use_cache=true)hkf=ifnotuse_cachethenf()elsetryHashtbl.findhkwithNot_found->letv=f()inbeginHashtbl.addhkv;vendletcache_in_refmyreff=match!myrefwith|Somee->e|None->lete=f()inmyref:=Somee;eletonceff=letalready=reffalsein(funx->ifnot!alreadythenbeginalready:=true;fxend)letonceareff=if!arefthen()elsebeginaref:=true;f()end(* cache_file, cf below *)letbefore_leavingfx=fx;x(* finalize, cf prelude *)(* cheat *)letrecyf=funx->f(yf)x(*****************************************************************************)(* Concurrency *)(*****************************************************************************)(* from http://en.wikipedia.org/wiki/File_locking
*
* "When using file locks, care must be taken to ensure that operations
* are atomic. When creating the lock, the process must verify that it
* does not exist and then create it, but without allowing another
* process the opportunity to create it in the meantime. Various
* schemes are used to implement this, such as taking advantage of
* system calls designed for this purpose (but such system calls are
* not usually available to shell scripts) or by creating the lock file
* under a temporary name and then attempting to move it into place."
*
* => can't use 'if(not (file_exist xxx)) then create_file xxx' because
* file_exist/create_file are not in atomic section (classic problem).
*
* from man open:
*
* "O_EXCL When used with O_CREAT, if the file already exists it
* is an error and the open() will fail. In this context, a
* symbolic link exists, regardless of where it points to.
* O_EXCL is broken on NFS file systems; programs which
* rely on it for performing locking tasks will contain a
* race condition. The solution for performing atomic file
* locking using a lockfile is to create a unique file on
* the same file system (e.g., incorporating host- name and
* pid), use link(2) to make a link to the lockfile. If
* link(2) returns 0, the lock is successful. Otherwise,
* use stat(2) on the unique file to check if its link
* count has increased to 2, in which case the lock is also
* successful."
*)exceptionFileAlreadyLocked(* Racy if lock file on NFS!!! But still racy with recent Linux ? *)letacquire_file_lockfilename=pr2("Locking file: "^filename);trylet_fd=Unix.openfilefilename[Unix.O_CREAT;Unix.O_EXCL]0o777in()withUnix.Unix_error(e,fm,argm)->pr2(spf"exn Unix_error: %s %s %s\n"(Unix.error_messagee)fmargm);raiseFileAlreadyLockedletrelease_file_lockfilename=pr2("Releasing file: "^filename);Unix.unlinkfilename;()(*****************************************************************************)(* Error managment *)(*****************************************************************************)exceptionHereexceptionReturnExnexceptionWrongFormatofstring(* old: let _TODO () = failwith "TODO", now via fix_caml with raise Todo *)letinternal_errors=failwith("internal error: "^s)leterror_cant_havex=internal_error("cant have this case"^(dumpx))letmyassertcond=ifcondthen()elsefailwith"assert error"(* before warning I was forced to do stuff like this:
*
* let (fixed_int_to_posmap: fixed_int -> posmap) = fun fixed ->
* let v = ((fix_to_i fixed) / (power 2 16)) in
* let _ = Printf.printf "coord xy = %d\n" v in
* v
*
* The need for printf make me force to name stuff :(
* How avoid ? use 'it' special keyword ?
* In fact dont have to name it, use +> (fun v -> ...) so when want
* erase debug just have to erase one line.
*)letwarningsv=(pr2("Warning: "^s^"; value = "^(dumpv));v)letexn_to_sexn=Printexc.to_stringexnletexn_to_s_with_backtraceexn=Printexc.to_stringexn^"\n"^Printexc.get_backtrace()(* alias *)letstring_of_exnexn=exn_to_sexn(* want or of merd, but cant cos cant put die ... in b (strict call) *)let(|||)ab=tryawith_->b(* emacs/lisp inspiration, (vouillon does that too in unison I think) *)(* now in Prelude:
* let unwind_protect f cleanup = ...
* let finalize f cleanup = ...
*)typeerror=Errorofstring(* sometimes to get help from ocaml compiler to tell me places where
* I should update, we sometimes need to change some type from pair
* to triple, hence this kind of fake type.
*)typeevotype=unitletevoval=()(*****************************************************************************)(* Environment *)(*****************************************************************************)let_check_stack=reftrueletcheck_stack_sizelimit=if!_check_stackthenbeginpr2"checking stack size (do ulimit -s 40000 if problem)";letrecauxi=ifi=limitthen0else1+aux(i+1)inassert(aux0=limit);()endlettest_check_stack_sizelimit=(* bytecode: 100000000 *)(* native: 10000000 *)check_stack_size(int_of_stringlimit)(* only relevant in bytecode, in native the stacklimit is the os stacklimit
* (adjustable by ulimit -s)
*)let_init_gc_stack=()(* commented because cause pbs with js_of_ocaml
Gc.set {(Gc.get ()) with Gc.stack_limit = 100 * 1024 * 1024}
*)(* if process a big set of files then dont want get overflow in the middle
* so for this we are ready to spend some extra time at the beginning that
* could save far more later.
*
* On Centos 5.2 with ulimit -s 40000 I can only go up to 2000000 in
* native mode (and it crash with ulimit -s 10000, which is what we want).
*)letcheck_stack_nbfilesnbfiles=ifnbfiles>200thencheck_stack_size2000000(*****************************************************************************)(* Equality *)(*****************************************************************************)(* src: caml mailing list ? *)let(=|=):int->int->bool=(=)let(=<=):char->char->bool=(=)let(=$=):string->string->bool=(=)let(=:=):bool->bool->bool=(=)let(=*=)=(=)(* if really want to forbid to use '='
let (=) = (=|=)
*)let(=)()()=false(*x: common.ml *)(*###########################################################################*)(* Basic types *)(*###########################################################################*)(*****************************************************************************)(* Bool *)(*****************************************************************************)let(==>)b1b2=ifb1thenb2elsetrue(* could use too => *)(* superseded by another <=> below
let (<=>) a b = if a =*= b then 0 else if a < b then -1 else 1
*)letxorab=not(a=*=b)(*****************************************************************************)(* Char *)(*****************************************************************************)letstring_of_charc=String.make1cletis_single=String.contains",;()[]{}_`"letis_symbol=String.contains"!@#$%&*+./<=>?\\^|:-~"letis_space=String.contains"\n\t "letcbetweenminmaxc=(int_of_charc)<=(int_of_charmax)&&(int_of_charc)>=(int_of_charmin)letis_upper=cbetween'A''Z'letis_lower=cbetween'a''z'letis_alphac=is_upperc||is_lowercletis_digit=cbetween'0''9'letstring_of_charscs=cs+>List.map(String.make1)+>String.concat""(*****************************************************************************)(* Num *)(*****************************************************************************)(* since 3.08, div by 0 raise Div_by_rezo, and not anymore a hardware trap :)*)let(/!)xy=ify=|=0then(log"common.ml: div by 0";0)elsex/y(* now in prelude
* let rec (do_n: int -> (unit -> unit) -> unit) = fun i f ->
* if i = 0 then () else (f (); do_n (i-1) f)
*)lettimesfn=do_nnf(* now in prelude
* let rec (foldn: ('a -> int -> 'a) -> 'a -> int -> 'a) = fun f acc i ->
* if i = 0 then acc else foldn f (f acc i) (i-1)
*)letsum_float=List.fold_left(+.)0.0(* in prelude: let sum_int = List.fold_left (+) 0 *)letpi=3.14159265358979323846letpi2=pi/.2.0letpi4=pi/.4.0(* 180 = pi *)let(deg_to_rad:float->float)=fundeg->(deg*.pi)/.180.0letclampf=function|nwhenn<0.0->0.0|nwhenn>1.0->1.0|n->nletsquarex=x*.xletrecpowerxn=ifn=|=0then1elsex*powerx(n-1)letbetweeniminmax=i>min&&i<maxlet(between_strict:int->int->int->bool)=funabc->a<b&&b<cletborne~min~maxx=ifx>maxthenmaxelseifx<minthenminelsexletbitrangexp=letv=power2pinbetweenx(-v)v(* descendant *)let(prime1:int->intoption)=funx->letrecprime1_auxn=ifn=|=1thenNoneelseif(x/n)*n=|=xthenSomenelseprime1_aux(n-1)inifx=|=1thenNoneelseifx<0thenfailwith"negative"elseprime1_aux(x-1)(* montant, better *)let(prime:int->intoption)=funx->letrecprime_auxn=ifn=|=xthenNoneelseif(x/n)*n=|=xthenSomenelseprime_aux(n+1)inifx=|=1thenNoneelseifx<0thenfailwith"negative"elseprime_aux2letsumxs=List.fold_left(+)0xsletproduct=List.fold_left(*)1letdecomposex=letrecdecomposex=ifx=|=1then[]else(matchprimexwith|None->[x]|Somen->n::decompose(x/n))inassert(product(decomposex)=|=x);decomposexletmysquarex=x*xletsqra=a*.atypecompare=Equal|Inf|Suplet(<=>)ab=ifa=*=bthenEqualelseifa<bthenInfelseSuplet(<==>)ab=ifa=*=bthen0elseifa<bthen-1else1typeuint=intletint_of_stringchars=fold_left_with_index(funaccei->acc+(Char.codee*(power8i)))0(List.rev(list_of_strings))letint_of_basesbase=fold_left_with_index(funaccei->letj=Char.codee-Char.code'0'inifj>=basethenfailwith"not in good base"elseacc+(j*(powerbasei)))0(List.rev(list_of_strings))letint_of_stringbitss=int_of_bases2let_=assert(int_of_stringbits"1011"=|=1*8+1*2+1*1)letint_of_octals=int_of_bases8let_=assert(int_of_octal"017"=|=15)(* let int_of_hex s = int_of_base s 16, NONONONO cos 'A' - '0' does not give 10 !! *)letint_of_alls=ifString.lengths>=2&&(String.gets0=<='0')&&is_digit(String.gets1)thenint_of_octalselseint_of_stringslet(+=)refv=ref:=!ref+vlet(-=)refv=ref:=!ref-vletpourcentxtotal=(x*100)/totalletpourcent_floatxtotal=((float_of_intx)*.100.0)/.(float_of_inttotal)letpourcent_float_of_floatsxtotal=(x*.100.0)/.totalletpourcent_good_badgoodbad=(good*100)/(good+bad)letpourcent_good_bad_floatgoodbad=(float_of_intgood*.100.0)/.(float_of_intgood+.float_of_intbad)type'amax_with_elem=intref*'arefletupdate_max_with_elem(aref,aelem)~is_better(newv,newelem)=ifis_betternewvarefthenbeginaref:=newv;aelem:=newelem;end(*****************************************************************************)(* Numeric/overloading *)(*****************************************************************************)type'anumdict=NumDictof(('a->'a->'a)*('a->'a->'a)*('a->'a->'a)*('a->'a));;letadd(NumDict(a,m,d,n))=a;;letmul(NumDict(a,m,d,n))=m;;letdiv(NumDict(a,m,d,n))=d;;letneg(NumDict(a,m,d,n))=n;;letnumd_int=NumDict((+),(*),(/),(~-));;letnumd_float=NumDict((+.),(*.),(/.),(~-.));;lettestddictn=let(*)xy=muldictxyinlet(/)xy=divdictxyinlet(+)xy=adddictxyin(* Now you can define all sorts of things in terms of *, /, + *)letfnum=(num*num)/(num+num)infn;;moduleArithFloatInfix=structlet(+..)=(+)let(-..)=(-)let(/..)=(/)let(*..)=(*)let(+)=(+.)let(-)=(-.)let(/)=(/.)let(*)=(*.)let(+=)refv=ref:=!ref+vlet(-=)refv=ref:=!ref-vend(*****************************************************************************)(* Tuples *)(*****************************************************************************)type'apair='a*'atype'atriple='a*'a*'aletfst3(x,_,_)=xletsnd3(_,y,_)=yletthd3(_,_,z)=zletsndthd(a,b,c)=(b,c)letmap_fstf(x,y)=fx,yletmap_sndf(x,y)=x,fyletpairf(x,y)=(fx,fy)lettriplef(x,y,z)=(fx,fy,fz)(* for my ocamlbeautify script *)(*
let snd = snd
let fst = fst
*)letdoublea=a,aletswap(x,y)=(y,x)lettuple_of_list1=function[a]->a|_->failwith"tuple_of_list1"lettuple_of_list2=function[a;b]->a,b|_->failwith"tuple_of_list2"lettuple_of_list3=function[a;b;c]->a,b,c|_->failwith"tuple_of_list3"lettuple_of_list4=function[a;b;c;d]->a,b,c,d|_->failwith"tuple_of_list4"lettuple_of_list5=function[a;b;c;d;e]->a,b,c,d,e|_->failwith"tuple_of_list5"lettuple_of_list6=function[a;b;c;d;e;f]->a,b,c,d,e,f|_->failwith"tuple_of_list6"(*****************************************************************************)(* Maybe *)(*****************************************************************************)(* type 'a maybe = Just of 'a | None *)type('a,'b)either=Leftof'a|Rightof'b(* with sexp *)type('a,'b,'c)either3=Left3of'a|Middle3of'b|Right3of'c(* with sexp *)letjust=function|(Somex)->x|_->failwith"just: pb"letsome=justletfmapf=function|None->None|Somex->Some(fx)letmap_option=fmapletdo_optionf=function|None->()|Somex->fxletopt=do_optionletoptionisef=trySome(f())withNot_found->None(* pixel *)letsome_or=function|None->id|Somee->fun_->eletoption_to_list=function|None->[]|Somex->[x]letpartition_eitherfl=letrecpart_eitherleftright=function|[]->(List.revleft,List.revright)|x::l->(matchfxwith|Lefte->part_either(e::left)rightl|Righte->part_eitherleft(e::right)l)inpart_either[][]lletpartition_either3fl=letrecpart_eitherleftmiddleright=function|[]->(List.revleft,List.revmiddle,List.revright)|x::l->(matchfxwith|Left3e->part_either(e::left)middlerightl|Middle3e->part_eitherleft(e::middle)rightl|Right3e->part_eitherleftmiddle(e::right)l)inpart_either[][][]l(* pixel *)letrecfilter_some=function|[]->[]|None::l->filter_somel|Somee::l->e::filter_somelletmap_filterfxs=xs+>List.mapf+>filter_someletrecfind_somep=function|[]->raiseNot_found|x::l->matchpxwith|Somev->v|None->find_someplletrecfind_some_optp=function|[]->None|x::l->matchpxwith|Somev->Somev|None->find_some_optpl(* same
let map_find f xs =
xs +> List.map f +> List.find (function Some x -> true | None -> false)
+> (function Some x -> x | None -> raise Impossible)
*)letlist_to_single_or_exnxs=matchxswith|[]->raiseNot_found|x::y::zs->raiseCommon.Multi_found|[x]->xletrec(while_some:gen:(unit->'aoption)->f:('a->'b)->unit->'blist)=fun~gen~f()->matchgen()with|None->[]|Somex->lete=fxinletrest=while_somegenf()ine::rest(* perl idiom *)let(||=)arefvf=match!arefwith|None->aref:=Some(vf())|Some_->()let(>>=)m1m2=matchm1with|None->None|Somex->m2x(* http://roscidus.com/blog/blog/2013/10/13/ocaml-tips/#handling-option-types*)let(|?)maybedefault=matchmaybewith|Somev->v|None->Lazy.forcedefault(*****************************************************************************)(* TriBool *)(*****************************************************************************)typebool3=True3|False3|TrueFalsePb3ofstring(*****************************************************************************)(* Regexp, can also use PCRE *)(*****************************************************************************)(* put before String section because String section use some =~ *)(* let gsubst = global_replace *)let(==~)sre=Common.profile_code"Common.==~"(fun()->Str.string_matchres0)let_memo_compiled_regexp=Hashtbl.create101letcandidate_match_funcsre=(* old: Str.string_match (Str.regexp re) s 0 *)letcompile_re=memoized_memo_compiled_regexpre(fun()->Str.regexpre)inStr.string_matchcompile_res0letmatch_funcsre=Common.profile_code"Common.=~"(fun()->candidate_match_funcsre)let(=~)sre=match_funcsreletstring_match_substringres=trylet_i=Str.search_forwardres0intruewithNot_found->false(*
let _ =
example(string_match_substring (Str.regexp "foo") "a foo b")
let _ =
example(string_match_substring (Str.regexp "\\bfoo\\b") "a foo b")
let _ =
example(string_match_substring (Str.regexp "\\bfoo\\b") "a\n\nfoo b")
let _ =
example(string_match_substring (Str.regexp "\\bfoo_bar\\b") "a\n\nfoo_bar b")
*)(* does not work :(
let _ =
example(string_match_substring (Str.regexp "\\bfoo_bar2\\b") "a\n\nfoo_bar2 b")
*)let(regexp_match:string->string->string)=funsre->assert(s=~re);Str.matched_group1s(* beurk, side effect code, but hey, it is convenient *)(* now in prelude
* let (matched: int -> string -> string) = fun i s ->
* Str.matched_group i s
*
* let matched1 = fun s -> matched 1 s
* let matched2 = fun s -> (matched 1 s, matched 2 s)
* let matched3 = fun s -> (matched 1 s, matched 2 s, matched 3 s)
* let matched4 = fun s -> (matched 1 s, matched 2 s, matched 3 s, matched 4 s)
* let matched5 = fun s -> (matched 1 s, matched 2 s, matched 3 s, matched 4 s, matched 5 s)
* let matched6 = fun s -> (matched 1 s, matched 2 s, matched 3 s, matched 4 s, matched 5 s, matched 6 s)
*)letsplitseps=Str.split(Str.regexpsep)s(*
let _ = example (split "/" "" =*= [])
let _ = example (split ":" ":a:b" =*= ["a";"b"])
*)letjoinsepxs=String.concatsepxs(*
let _ = example (join "/" ["toto"; "titi"; "tata"] =$= "toto/titi/tata")
*)(*
let rec join str = function
| [] -> ""
| [x] -> x
| x::xs -> x ^ str ^ (join str xs)
*)letsplit_list_regexp_noheading="__noheading__"let(split_list_regexp:string->stringlist->(string*stringlist)list)=funrexs->letrecsplit_lr_aux(heading,accu)=function|[]->[(heading,List.revaccu)]|x::xs->ifx=~rethen(heading,List.revaccu)::split_lr_aux(x,[])xselsesplit_lr_aux(heading,x::accu)xsinsplit_lr_aux("__noheading__",[])xs+>(funxs->if(List.hdxs)=*=("__noheading__",[])thenList.tlxselsexs)letregexp_alpha=Str.regexp"^[a-zA-Z_][A-Za-z_0-9]*$"letall_matchres=letregexp=Str.regexpreinletres=ref[]inlet_=Str.global_substituteregexp(fun_s->letsubstr=Str.matched_stringsinassert(substr==~regexp);(* @Effect: also use it's side effect *)letparen_matched=matched1substrinpushparen_matchedres;""(* @Dummy *))sinList.rev!res(*
let _ = example (all_match "\\(@[A-Za-z]+\\)" "ca va @Et toi @Comment"
=*= ["@Et";"@Comment"])
*)letglobal_replace_regexpref_on_substrs=letregexp=Str.regexpreinStr.global_substituteregexp(fun_wholestr->letsubstr=Str.matched_stringsinf_on_substrsubstr)sletregexp_word_str="\\([a-zA-Z_][A-Za-z_0-9]*\\)"letregexp_word=Str.regexpregexp_word_strletregular_wordss=all_matchregexp_word_strsletcontain_regular_words=letxs=regular_wordssinList.lengthxs>=1(* This type allows to combine a serie of "regexps" to form a big
* one representing its union which should then be optimized by the Str
* module.
*)typeregexp=|Containofstring|Startofstring|Endofstring|Exactofstringletregexp_string_of_regexpx=matchxwith|Contains->".*"^s^".*"|Starts->"^"^s|Ends->".*"^s^"$"|Exacts->sletstr_regexp_of_regexpx=Str.regexp(regexp_string_of_regexpx)letcompile_regexp_unionxs=xs+>List.map(funx->regexp_string_of_regexpx)+>join"\\|"+>Str.regexp(*****************************************************************************)(* Strings *)(*****************************************************************************)letslength=String.lengthletconcat=String.concat(* ruby *)leti_to_s=string_of_intlets_to_i=int_of_string(* strings take space in memory. Better when can share the space used by
similar strings *)let_shareds=Hashtbl.create100let(shared_string:string->string)=funs->tryHashtbl.find_sharedsswithNot_found->(Hashtbl.add_sharedsss;s)letchop=function|""->""|s->String.subs0(String.lengths-1)(* remove trailing / *)letchop_dirsymbol=function|swhens=~"\\(.*\\)/$"->matched1s|s->slet(<!!>)s(i,j)=String.subsi(ifj<0thenString.lengths-i+j+1elsej-i)(* let _ = example ( "tototati"<!!>(3,-2) = "otat" ) *)let(<!>)si=String.getsi(* pixel *)letrecsplit_on_charcs=tryletsp=String.indexscinString.subs0sp::split_on_charc(String.subs(sp+1)(String.lengths-sp-1))withNot_found->[s]letquotes="\""^s^"\""letunquotes=ifs=~"\"\\(.*\\)\""thenmatched1selsefailwith("unquote: the string has no quote: "^s)(* easier to have this to be passed as hof, because ocaml dont have
* haskell "section" operators
*)letnull_strings=s=$=""letis_blank_strings=s=~"^\\([ \t]\\)*$"(* src: lablgtk2/examples/entrycompletion.ml *)letis_string_prefixs1s2=(String.lengths1<=String.lengths2)&&(String.subs20(String.lengths1)=$=s1)letpluralis=ifi=|=1thenPrintf.sprintf"%d %s"iselsePrintf.sprintf"%d %ss"isletshowCodeHexxs=List.iter(funi->Printf.printf"%02x"i)xslettake_stringns=String.subs0(n-1)lettake_string_safens=ifn>String.lengthsthenselsetake_stringns(* used by LFS *)letsize_mo_koi=letko=(i/1024)mod1024inletmo=(i/1024)/1024in(ifmo>0thenPrintf.sprintf"%dMo%dKo"mokoelsePrintf.sprintf"%dKo"ko)letsize_koi=letko=i/1024inPrintf.sprintf"%dKo"ko(* done in summer 2007 for julia
* Reference: P216 of gusfeld book
* For two strings S1 and S2, D(i,j) is defined to be the edit distance of S1[1..i] to S2[1..j]
* So edit distance of S1 (of length n) and S2 (of length m) is D(n,m)
*
* Dynamic programming technique
* base:
* D(i,0) = i for all i (cos to go from S1[1..i] to 0 characteres of S2 you have to delete all characters from S1[1..i]
* D(0,j) = j for all j (cos j characters must be inserted)
* recurrence:
* D(i,j) = min([D(i-1, j)+1, D(i, j - 1 + 1), D(i-1, j-1) + t(i,j)])
* where t(i,j) is equal to 1 if S1(i) != S2(j) and 0 if equal
* intuition = there is 4 possible action = deletion, insertion, substitution, or match
* so Lemma =
*
* D(i,j) must be one of the three
* D(i, j-1) + 1
* D(i-1, j)+1
* D(i-1, j-1) +
* t(i,j)
*
*
*)letmatrix_distances1s2=letn=(String.lengths1)inletm=(String.lengths2)inletmat=Array.make_matrix(n+1)(m+1)0inlettij=ifString.gets1(i-1)=<=String.gets2(j-1)then0else1inletmin3abc=min(minab)cinbeginfori=0tondomat.(i).(0)<-idone;forj=0tomdomat.(0).(j)<-j;done;fori=1tondoforj=1tomdomat.(i).(j)<-min3(mat.(i).(j-1)+1)(mat.(i-1).(j)+1)(mat.(i-1).(j-1)+tij)donedone;matendletedit_distances1s2=(matrix_distances1s2).(String.lengths1).(String.lengths2)lettest_edit=edit_distance"vintner""writers"let_=assert(edit_distance"winter""winter"=|=0)let_=assert(edit_distance"vintner""writers"=|=5)(* src: http://pleac.sourceforge.net/pleac_ocaml/strings.html *)(* We can emulate the Perl wrap function with the following function *)letwrap?(width=80)s=letl=Str.split(Str.regexp" ")sinFormat.pp_set_marginFormat.str_formatterwidth;Format.pp_open_boxFormat.str_formatter0;List.iter(funx->Format.pp_print_stringFormat.str_formatterx;Format.pp_print_breakFormat.str_formatter10;)l;Format.flush_str_formatter();;(*****************************************************************************)(* Filenames *)(*****************************************************************************)letdirname=Filename.dirnameletbasename=Filename.basenametypefilename=string(* TODO could check that exist :) type sux *)(* with sexp *)typedirname=string(* TODO could check that exist :) type sux *)(* with sexp *)(* file or dir *)typepath=stringmoduleBasicType=structtypefilename=stringend(* updated: added '-' in filesuffix because of file like foo.c-- *)let(filesuffix:filename->string)=funs->(tryregexp_matchs".+\\.\\([a-zA-Z0-9_-]+\\)$"with_->"NOEXT")let(fileprefix:filename->string)=funs->(tryregexp_matchs"\\(.+\\)\\.\\([a-zA-Z0-9_]+\\)?$"with_->s)(*
let _ = example (filesuffix "toto.c" =$= "c")
let _ = example (fileprefix "toto.c" =$= "toto")
*)(*
assert (s = fileprefix s ^ filesuffix s)
let withoutExtension s = global_replace (regexp "\\..*$") "" s
let () = example "without"
(withoutExtension "toto.s.toto" = "toto")
*)letadjust_ext_if_neededfilenameext=ifString.getext0<>'.'thenfailwith"I need an extension such as .c not just c";ifnot(filename=~(".*\\"^ext))thenfilename^extelsefilenameletdb_of_filenamefile=dirnamefile,basenamefileletfilename_of_db(basedir,file)=Filename.concatbasedirfileletdbe_of_filenamefile=(* raise Invalid_argument if no ext, so safe to use later the unsafe
* fileprefix and filesuffix functions (well filesuffix is safe by default)
*)ignore(Filename.chop_extensionfile);Filename.dirnamefile,Filename.basenamefile+>fileprefix,Filename.basenamefile+>filesuffixletfilename_of_dbe(dir,base,ext)=ifext=$=""thenFilename.concatdirbaseelseFilename.concatdir(base^"."^ext)letdbe_of_filename_safefile=tryLeft(dbe_of_filenamefile)withInvalid_argument_->Right(Filename.dirnamefile,Filename.basenamefile)letdbe_of_filename_nodotfile=let(d,b,e)=dbe_of_filenamefileinletd=ifd=$="."then""elsedind,b,e(* old:
* let re_be = Str.regexp "\\([^.]*\\)\\.\\(.*\\)"
* let dbe_of_filename_noext_ok file =
* ...
* if Str.string_match re_be base 0
* then
* let (b, e) = matched2 base in
* (dir, b, e)
*
* That way files like foo.md5sum.c would not be considered .c
* but .md5sum.c, but then it has too many disadvantages because
* then regular files like qemu.root.c would not be considered
* .c files, so it's better instead to fix syncweb to not generate
* .md5sum.c but .md5sum_c files!
*)letdbe_of_filename_noext_okfile=letdir=Filename.dirnamefileinletbase=Filename.basenamefileindir,fileprefixbase,filesuffixbaseletreplace_extfileoldextnewext=let(d,b,e)=dbe_of_filenamefileinassert(e=$=oldext);filename_of_dbe(d,b,newext)letnormalize_pathfile=let(dir,filename)=Filename.dirnamefile,Filename.basenamefileinletxs=split"/"dirinletrecauxacc=function|[]->List.revacc|x::xs->(matchxwith|"."->auxaccxs|".."->aux(List.tlacc)xs|x->aux(x::acc)xs)inletxs'=aux[]xsinFilename.concat(join"/"xs')filename(*
let relative_to_absolute s =
if Filename.is_relative s
then
begin
let old = Sys.getcwd () in
Sys.chdir s;
let current = Sys.getcwd () in
Sys.chdir old;
s
end
else s
*)letrelative_to_absolutes=ifs=$="."thenSys.getcwd()elseifFilename.is_relativesthenSys.getcwd()^"/"^selsesletis_relatives=Filename.is_relativesletis_absolutes=not(is_relatives)(* pre: prj_path must not contain regexp symbol *)letfilename_without_leading_pathprj_paths=letprj_path=chop_dirsymbolprj_pathinifs=$=prj_paththen"."elseifs=~("^"^prj_path^"/\\(.*\\)$")thenmatched1selsefailwith(spf"cant find filename_without_project_path: %s %s"prj_paths)(* realpath: see end of file *)(* basic file position *)typefilepos={l:int;c:int;}(*****************************************************************************)(* i18n *)(*****************************************************************************)typelangage=|English|Francais|Deutsch(* gettext ? *)(*****************************************************************************)(* Dates *)(*****************************************************************************)(* maybe I should use ocamlcalendar, but I don't like all those functors ... *)typemonth=|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dectypeyear=Yearofinttypeday=Dayofinttypewday=Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturdaytypedate_dmy=DMYofday*month*yeartypehour=Hourofinttypeminute=Minofinttypesecond=Secofinttypetime_hms=HMSofhour*minute*secondtypefull_date=date_dmy*time_hms(* intervalle *)typedays=Daysofinttypetime_dmy=TimeDMYofday*month*yeartypefloat_time=floatletcheck_date_dmy(DMY(day,month,year))=raiseCommon.Todoletcheck_time_dmy(TimeDMY(day,month,year))=raiseCommon.Todoletcheck_time_hms(HMS(x,y,a))=raiseCommon.Todo(* ---------------------------------------------------------------------- *)(* older code *)letint_to_monthi=assert(i<=12&&i>=1);matchiwith|1->"Jan"|2->"Feb"|3->"Mar"|4->"Apr"|5->"May"|6->"Jun"|7->"Jul"|8->"Aug"|9->"Sep"|10->"Oct"|11->"Nov"|12->"Dec"(*
| 1 -> "January"
| 2 -> "February"
| 3 -> "March"
| 4 -> "April"
| 5 -> "May"
| 6 -> "June"
| 7 -> "July"
| 8 -> "August"
| 9 -> "September"
| 10 -> "October"
| 11 -> "November"
| 12 -> "December"
*)|_->raiseCommon.Impossibleletmonth_info=[1,Jan,"Jan","January",31;2,Feb,"Feb","February",28;3,Mar,"Mar","March",31;4,Apr,"Apr","April",30;5,May,"May","May",31;6,Jun,"Jun","June",30;7,Jul,"Jul","July",31;8,Aug,"Aug","August",31;9,Sep,"Sep","September",30;10,Oct,"Oct","October",31;11,Nov,"Nov","November",30;12,Dec,"Dec","December",31;]letweek_day_info=[0,Sunday,"Sun","Dim","Sunday";1,Monday,"Mon","Lun","Monday";2,Tuesday,"Tue","Mar","Tuesday";3,Wednesday,"Wed","Mer","Wednesday";4,Thursday,"Thu","Jeu","Thursday";5,Friday,"Fri","Ven","Friday";6,Saturday,"Sat","Sam","Saturday";]leti_to_month_h=month_info+>List.map(fun(i,month,monthstr,mlong,days)->i,month)lets_to_month_h=month_info+>List.map(fun(i,month,monthstr,mlong,days)->monthstr,month)letslong_to_month_h=month_info+>List.map(fun(i,month,monthstr,mlong,days)->mlong,month)letmonth_to_s_h=month_info+>List.map(fun(i,month,monthstr,mlong,days)->month,monthstr)letmonth_to_i_h=month_info+>List.map(fun(i,month,monthstr,mlong,days)->month,i)leti_to_wday_h=week_day_info+>List.map(fun(i,day,dayen,dayfr,daylong)->i,day)letwday_to_en_h=week_day_info+>List.map(fun(i,day,dayen,dayfr,daylong)->day,dayen)letwday_to_fr_h=week_day_info+>List.map(fun(i,day,dayen,dayfr,daylong)->day,dayfr)letmonth_of_strings=List.assocss_to_month_hletmonth_of_string_longs=List.assocsslong_to_month_hletstring_of_months=List.assocsmonth_to_s_hletmonth_of_inti=List.associi_to_month_hletint_of_monthm=List.assocmmonth_to_i_hletwday_of_inti=List.associi_to_wday_hletstring_en_of_wdaywday=List.assocwdaywday_to_en_hletstring_fr_of_wdaywday=List.assocwdaywday_to_fr_h(* ---------------------------------------------------------------------- *)letwday_str_of_int~langagei=letwday=wday_of_intiinmatchlangagewith|English->string_en_of_wdaywday|Francais->string_fr_of_wdaywday|Deutsch->raiseCommon.Todoletstring_of_date_dmy(DMY(Dayn,month,Yeary))=(spf"%02d-%s-%d"n(string_of_monthmonth)y)letdate_dmy_of_strings=ifs=~"\\([0-9]+\\)-\\([A-Za-z]+\\)-\\([0-9]+\\)"thenlet(day,month,year)=matched3sinDMY(Day(int_of_stringday),month_of_stringmonth,Year(int_of_stringyear))elsefailwith("wrong dmy string: "^s)letstring_of_unix_time?(langage=English)tm=lety=tm.Unix.tm_year+1900inletmon=string_of_month(month_of_int(tm.Unix.tm_mon+1))inletd=tm.Unix.tm_mdayinleth=tm.Unix.tm_hourinletmin=tm.Unix.tm_mininlets=tm.Unix.tm_secinletwday=wday_str_of_int~langagetm.Unix.tm_wdayinspf"%02d/%3s/%04d (%s) %02d:%02d:%02d"dmonywdayhmins(* ex: 21/Jul/2008 (Lun) 21:25:12 *)letunix_time_of_strings=ifs=~("\\([0-9][0-9]\\)/\\(...\\)/\\([0-9][0-9][0-9][0-9]\\) "^"\\(.*\\) \\([0-9][0-9]\\):\\([0-9][0-9]\\):\\([0-9][0-9]\\)")thenlet(sday,smonth,syear,_sday,shour,smin,ssec)=matched7sinlety=s_to_isyear-1900inletmon=smonth+>month_of_string+>int_of_month+>(funi->i-1)inlettm=Unix.localtime(Unix.time())in{tmwithUnix.tm_year=y;Unix.tm_mon=mon;Unix.tm_mday=s_to_isday;Unix.tm_hour=s_to_ishour;Unix.tm_min=s_to_ismin;Unix.tm_sec=s_to_issec;}elsefailwith("unix_time_of_string: "^s)letshort_string_of_unix_time?(langage=English)tm=lety=tm.Unix.tm_year+1900inletmon=string_of_month(month_of_int(tm.Unix.tm_mon+1))inletd=tm.Unix.tm_mdayinlet_h=tm.Unix.tm_hourinlet_min=tm.Unix.tm_mininlet_s=tm.Unix.tm_secinletwday=wday_str_of_int~langagetm.Unix.tm_wdayinspf"%02d/%3s/%04d (%s)"dmonywdayletstring_of_unix_time_lfstime=spf"%02d--%s--%d"time.Unix.tm_mday(int_to_month(time.Unix.tm_mon+1))(time.Unix.tm_year+1900)(* ---------------------------------------------------------------------- *)letstring_of_floattime?langagei=lettm=Unix.localtimeiinstring_of_unix_time?langagetmletshort_string_of_floattime?langagei=lettm=Unix.localtimeiinshort_string_of_unix_time?langagetmletfloattime_of_strings=lettm=unix_time_of_stringsinlet(sec,_tm)=Unix.mktimetminsec(* ---------------------------------------------------------------------- *)letdays_in_week_of_dayday=lettm=Unix.localtimedayinletwday=tm.Unix.tm_wdayinletwday=ifwday=|=0then6elsewday-1inletmday=tm.Unix.tm_mdayinletstart_d=mday-wdayinletend_d=mday+(6-wday)inenumstart_dend_d+>List.map(funmday->Unix.mktime{tmwithUnix.tm_mday=mday}+>fst)letfirst_day_in_week_of_dayday=List.hd(days_in_week_of_dayday)letlast_day_in_week_of_dayday=list_last(days_in_week_of_dayday)(* ---------------------------------------------------------------------- *)(* (modified) copy paste from ocamlcalendar/src/date.ml *)letdays_month=[|0;31;59;90;120;151;181;212;243;273;304;334(*; 365*)|]letrough_days_since_jesus(DMY(Daynday,month,Yearyear))=letn=nday+(days_month.(int_of_monthmonth-1))+year*365inDaysnletis_more_recentd1d2=let(Daysn1)=rough_days_since_jesusd1inlet(Daysn2)=rough_days_since_jesusd2in(n1>n2)letmax_dmyd1d2=ifis_more_recentd1d2thend1elsed2letmin_dmyd1d2=ifis_more_recentd1d2thend2elsed1letmaximum_dmyds=foldl1max_dmydsletminimum_dmyds=foldl1min_dmydsletrough_days_between_datesd1d2=let(Daysn1)=rough_days_since_jesusd1inlet(Daysn2)=rough_days_since_jesusd2inDays(n2-n1)let_=assert(rough_days_between_dates(DMY(Day7,Jan,Year1977))(DMY(Day13,Jan,Year1977))=*=Days6)(* because of rough days, it is a bit buggy, here it should return 1 *)(*
let _ = assert_equal
(rough_days_between_dates
(DMY (Day 29, Feb, Year 1977))
(DMY (Day 1, Mar , Year 1977)))
(Days 1)
*)(* from julia, in gitsort.ml *)(*
let antimonths =
[(1,31);(2,28);(3,31);(4,30);(5,31); (6,6);(7,7);(8,31);(9,30);(10,31);
(11,30);(12,31);(0,31)]
let normalize (year,month,day,hour,minute,second) =
if hour < 0
then
let (day,hour) = (day - 1,hour + 24) in
if day = 0
then
let month = month - 1 in
let day = List.assoc month antimonths in
let day =
if month = 2 && year / 4 * 4 = year && not (year / 100 * 100 = year)
then 29
else day in
if month = 0
then (year-1,12,day,hour,minute,second)
else (year,month,day,hour,minute,second)
else (year,month,day,hour,minute,second)
else (year,month,day,hour,minute,second)
*)letmk_date_dmydaymonthyear=letdate=DMY(Dayday,month_of_intmonth,Yearyear)in(* check_date_dmy date *)date(* ---------------------------------------------------------------------- *)(* conversion to unix.tm *)letdmy_to_unixtime(DMY(Dayn,month,Yearyear))=lettm={Unix.tm_sec=0;(** Seconds 0..60 *)tm_min=0;(** Minutes 0..59 *)tm_hour=12;(** Hours 0..23 *)tm_mday=n;(** Day of month 1..31 *)tm_mon=(int_of_monthmonth-1);(** Month of year 0..11 *)tm_year=year-1900;(** Year - 1900 *)tm_wday=0;(** Day of week (Sunday is 0) *)tm_yday=0;(** Day of year 0..365 *)tm_isdst=false;(** Daylight time savings in effect *)}inUnix.mktimetmletunixtime_to_dmytm=letn=tm.Unix.tm_mdayinletmonth=month_of_int(tm.Unix.tm_mon+1)inletyear=tm.Unix.tm_year+1900inDMY(Dayn,month,Yearyear)letunixtime_to_floattimetm=Unix.mktimetm+>fstletfloattime_to_unixtimesec=Unix.localtimesecletfloattime_to_dmysec=sec+>floattime_to_unixtime+>unixtime_to_dmyletsec_to_dayssec=letminfactor=60inlethourfactor=60*60inletdayfactor=60*60*24inletdays=sec/dayfactorinlethours=(secmoddayfactor)/hourfactorinletmins=(secmodhourfactor)/minfactorinletsec=(secmod60)in(* old: Printf.sprintf "%d days, %d hours, %d minutes" days hours mins *)(ifdays>0thenpluraldays"day"^" "else"")^(ifhours>0thenpluralhours"hour"^" "else"")^(ifmins>0thenpluralmins"min"^" "else"")^(spf"%dsec"sec)letsec_to_hourssec=letminfactor=60inlethourfactor=60*60inlethours=sec/hourfactorinletmins=(secmodhourfactor)/minfactorinletsec=(secmod60)in(* old: Printf.sprintf "%d days, %d hours, %d minutes" days hours mins *)(ifhours>0thenpluralhours"hour"^" "else"")^(ifmins>0thenpluralmins"min"^" "else"")^(spf"%dsec"sec)lettest_date_1()=letdate=DMY(Day17,Sep,Year1991)inletfloat,tm=dmy_to_unixtimedateinpr2(spf"date: %.0f"float);()(* src: ferre in logfun/.../date.ml *)letday_secs:float=86400.lettoday:unit->float=fun()->(Unix.time())letyesterday:unit->float=fun()->(Unix.time()-.day_secs)lettomorrow:unit->float=fun()->(Unix.time()+.day_secs)letlastweek:unit->float=fun()->(Unix.time()-.(7.0*.day_secs))letlastmonth:unit->float=fun()->(Unix.time()-.(30.0*.day_secs))letweek_before:float_time->float_time=fund->(d-.(7.0*.day_secs))letmonth_before:float_time->float_time=fund->(d-.(30.0*.day_secs))letweek_after:float_time->float_time=fund->(d+.(7.0*.day_secs))lettimestamp()=letnow=Unix.time()inlettm=floattime_to_unixtimenowinletd=tm.Unix.tm_mdayinleth=tm.Unix.tm_hourinletmin=tm.Unix.tm_mininlets=tm.Unix.tm_secin(* old: string_of_unix_time tm *)spf"%02d %02d:%02d:%02d"dhmins(*****************************************************************************)(* Lines/words/strings *)(*****************************************************************************)(* now in prelude:
* let (list_of_string: string -> char list) = fun s ->
* (enum 0 ((String.length s) - 1) +> List.map (String.get s))
*)let_=assert(list_of_string"abcd"=*=['a';'b';'c';'d'])(*
let rec (list_of_stream: ('a Stream.t) -> 'a list) =
parser
| [< 'c ; stream >] -> c :: list_of_stream stream
| [<>] -> []
let (list_of_string: string -> char list) =
Stream.of_string $ list_of_stream
*)(* now in prelude:
* let (lines: string -> string list) = fun s -> ...
*)let(lines_with_nl:string->stringlist)=funs->letreclines_aux=function|[]->[]|[x]->ifx=$=""then[]else[x^"\n"](* old: [x] *)|x::xs->lete=x^"\n"ine::lines_auxxsin(time_func(fun()->Str.split_delim(Str.regexp"\n")s))+>lines_aux(* in fact better make it return always complete lines, simplify *)(* Str.split, but lines "\n1\n2\n" dont return the \n and forget the first \n => split_delim better than split *)(* +> List.map (fun s -> s ^ "\n") but add an \n even at the end => lines_aux *)(* old: slow
let chars = list_of_string s in
chars +> List.fold_left (fun (acc, lines) char ->
let newacc = acc ^ (String.make 1 char) in
if char = '\n'
then ("", newacc::lines)
else (newacc, lines)
) ("", [])
+> (fun (s, lines) -> List.rev (s::lines))
*)(* CHECK: unlines (lines x) = x *)let(unlines:stringlist->string)=funs->(String.concat"\n"s)^"\n"let(words:string->stringlist)=funs->Str.split(Str.regexp"[ \t()\";]+")slet(unwords:stringlist->string)=funs->String.concat""slet(split_space:string->stringlist)=funs->Str.split(Str.regexp"[ \t\n]+")sletn_spacen=repeat" "n+>join""letindent_stringns=letxs=linessinxs+>List.map(funs->n_spacen^s)+>unlines(* todo opti ? *)letnbliness=liness+>List.length(*
let _ = example (nblines "" =|= 0)
let _ = example (nblines "toto" =|= 1)
let _ = example (nblines "toto\n" =|= 1)
let _ = example (nblines "toto\ntata" =|= 2)
let _ = example (nblines "toto\ntata\n" =|= 2)
*)(* old: fork sucks.
* (* note: on MacOS wc outputs some spaces before the number of lines *)
*)letnblines_eff2file=letres=ref0inletfinished=reffalseinletch=open_infileinwhilenot!finisheddotrylet_=input_linechinincrreswithEnd_of_file->finished:=truedone;close_inch;!resletnblines_effa=Common.profile_code"Nblines_eff"(fun()->nblines_eff2a)(* could be in h_files-format *)letwords_of_string_with_newliness=liness+>List.mapwords+>List.flatten+>excludenull_stringletlines_with_nl_eithers=letxs=Str.full_split(Str.regexp"\n")sinxs+>List.map(function|Str.Delims->Right()|Str.Texts->Lefts)(*
let _ = example (lines_with_nl_either "ab\n\nc" =*=
[Left "ab"; Right (); Right (); Left "c"])
*)(*****************************************************************************)(* Process/Files *)(*****************************************************************************)letcat_origfile=letchan=open_infileinletreccat_orig_aux()=try(* cant do input_line chan::aux() cos ocaml eval from right to left ! *)letl=input_linechaninl::cat_orig_aux()withEnd_of_file->[]incat_orig_aux()(* tail recursive efficient version *)letcatfile=letchan=open_infileinletreccat_auxacc()=(* cant do input_line chan::aux() cos ocaml eval from right to left ! *)let(b,l)=try(true,input_linechan)withEnd_of_file->(false,"")inifbthencat_aux(l::acc)()elseaccincat_aux[]()+>List.rev+>(funx->close_inchan;x)letcat_arrayfile=(""::catfile)+>Array.of_list(* Spec for cat_excerpts:
let cat_excerpts file lines =
let arr = cat_array file in
lines |> List.map (fun i -> arr.(i))
*)letcat_excerptsfilelines=Common.with_open_infilefile(funchan->letlines=List.sortcomparelinesinletrecauxacclinescount=let(b,l)=try(true,input_linechan)withEnd_of_file->(false,"")inif(notb)thenaccelsematchlineswith|[]->acc|c::cdrwhen(c==count)->aux(l::acc)cdr(count+1)|_->auxacclines(count+1)inaux[]lines1+>List.rev)letinterpolatestr=begincommand2("printf \"%s\\n\" "^str^">/tmp/caml");cat"/tmp/caml"end(* could do a print_string but printf dont like print_string *)letechos=Printf.printf"%s"s;flushstdout;sletusleeps=fori=1tosdo()doneletsleep_little()=(*old: *)Unix.sleep1(*ignore(Sys.command ("usleep " ^ !_sleep_time))*)(* now in prelude:
* let command2 s = ignore(Sys.command s)
*)letdo_in_forkf=letpid=Unix.fork()inifpid=|=0thenbegin(* Unix.setsid(); *)Sys.set_signalSys.sigint(Sys.Signal_handle(fun_->pr2"being killed";Unix.kill0Sys.sigkill;));f();exit0;endelsepidexceptionCmdErrorofUnix.process_status*stringletprocess_output_to_list2?(verbose=false)command=letchan=Unix.open_process_incommandinletres=ref([]:stringlist)inletrecprocess_otl_aux()=lete=input_linechaninres:=e::!res;ifverbosethenpr2e;process_otl_aux()intryprocess_otl_aux()withEnd_of_file->letstat=Unix.close_process_inchanin(List.rev!res,stat)letcmd_to_list?verbosecommand=let(l,exit_status)=process_output_to_list2?verbosecommandinmatchexit_statuswith|Unix.WEXITED0->l|_->raise(CmdError(exit_status,(spf"CMD = %s, RESULT = %s"command(String.concat"\n"l))))letprocess_output_to_list=cmd_to_listletcmd_to_list_and_status=process_output_to_list2letnblines_with_wc2file=matchcmd_to_list(spf"wc -l %s"file)with|[s]whens=~"^[ \t]*\\([0-9]+\\) "->s_to_i(matched1s)|_->failwith"pb in output of wc"letnblines_with_wca=Common.profile_code"Common.nblines_with_wc"(fun()->nblines_eff2a)letunix_difffile1file2=let(xs,_status)=cmd_to_list_and_status(spf"diff -u %s %s"file1file2)inxs(* see also unix_diff_strings at the bottom *)letget_mem()=cmd_to_list("grep VmData /proc/"^string_of_int(Unix.getpid())^"/status")+>join""(* now in prelude:
* let command2 s = ignore(Sys.command s)
*)let_batch_mode=reffalselety_or_nomsg=pr2(msg^" [y/n] ?");if!_batch_modethentrueelsebeginletrecaux()=matchread_line()with|"y"|"yes"|"Y"->true|"n"|"no"|"N"->false|_->pr2"answer by 'y' or 'n'";aux()inaux()endletcommand2_y_or_nocmd=if!_batch_modethenbegincommand2cmd;trueendelsebeginpr2(cmd^" [y/n] ?");matchread_line()with|"y"|"yes"|"Y"->command2cmd;true|"n"|"no"|"N"->false|_->failwith"answer by yes or no"endletcommand2_y_or_no_exit_if_nocmd=letres=command2_y_or_nocmdinifresthen()elseraise(UnixExit(1))letcommand_safe?(verbose=false)programargs=letpid=Unix.fork()inletcmd_str=(program::args)+>join" "inifpid=|=0thenbeginpr2("running: "^cmd_str);Unix.execvprogram(Array.of_list(program::args))endelselet(pid2,status)=Unix.waitpid[]pidinmatchstatuswith|Unix.WEXITEDretcode->retcode|Unix.WSIGNALED_|Unix.WSTOPPED_->failwith("problem running: "^cmd_str)letmkdir?(mode=0o770)file=Unix.mkdirfilemodeletread_file_origfile=catfile|>unlinesletread_filefile=letic=open_infileinletsize=in_channel_lengthicinletbuf=Bytes.createsizeinreally_inputicbuf0size;close_inic;buf|>Bytes.to_stringletwrite_file~files=letchan=open_outfilein(output_stringchans;close_outchan)letunix_statfile=Common.profile_code"Unix.stat"(fun()->Unix.statfile)letfilesizefile=(unix_statfile).Unix.st_sizeletfilemtimefile=(unix_statfile).Unix.st_mtime(* opti? use wc -l ? *)letnblines_filefile=catfile+>List.lengthletlfile_existsfilename=try(match(Unix.lstatfilename).Unix.st_kindwith|(Unix.S_REG|Unix.S_LNK)->true|_->false)withUnix.Unix_error(Unix.ENOENT,_,_)->falseletis_directoryfile=(unix_statfile).Unix.st_kind=*=Unix.S_DIRletis_filefile=(unix_statfile).Unix.st_kind=*=Unix.S_REGletis_symlinkfile=(Unix.lstatfile).Unix.st_kind=*=Unix.S_LNKletis_executablefile=letstat=unix_statfileinletperms=stat.Unix.st_perminstat.Unix.st_kind=*=Unix.S_REG&&(permsland0o011<>0)(* ---------------------------------------------------------------------- *)(* _eff variant *)(* ---------------------------------------------------------------------- *)let_hmemo_unix_lstat_eff=Hashtbl.create101let_hmemo_unix_stat_eff=Hashtbl.create101letunix_lstat_efffile=Common.profile_code"Unix.lstat_eff"(fun()->ifis_absolutefilethenmemoized_hmemo_unix_lstat_efffile(fun()->Unix.lstatfile)else(* this is for efficieny reason to be able to memoize the stats *)failwith"must pass absolute path to unix_lstat_eff")letunix_stat_efffile=Common.profile_code"Unix.stat_eff"(fun()->ifis_absolutefilethenmemoized_hmemo_unix_stat_efffile(fun()->Unix.statfile)else(* this is for efficieny reason to be able to memoize the stats *)failwith"must pass absolute path to unix_stat_eff")letfilesize_efffile=(unix_lstat_efffile).Unix.st_sizeletfilemtime_efffile=(unix_lstat_efffile).Unix.st_mtimeletlfile_exists_efffilename=try(match(unix_lstat_efffilename).Unix.st_kindwith|(Unix.S_REG|Unix.S_LNK)->true|_->false)withUnix.Unix_error(Unix.ENOENT,_,_)->falseletis_directory_efffile=(unix_lstat_efffile).Unix.st_kind=*=Unix.S_DIRletis_file_efffile=(unix_lstat_efffile).Unix.st_kind=*=Unix.S_REGletis_executable_efffile=letstat=unix_lstat_efffileinletperms=stat.Unix.st_perminstat.Unix.st_kind=*=Unix.S_REG&&(permsland0o011<>0)(* ---------------------------------------------------------------------- *)(* src: from chailloux et al book *)letcapsule_unixfargs=try(fargs)withUnix.Unix_error(e,fm,argm)->log(Printf.sprintf"exn Unix_error: %s %s %s\n"(Unix.error_messagee)fmargm)let(readdir_to_kind_list:string->Unix.file_kind->stringlist)=funpathkind->Sys.readdirpath+>Array.to_list+>List.filter(funs->tryletstat=Unix.lstat(path^"/"^s)instat.Unix.st_kind=*=kindwithe->pr2("EXN pb stating file: "^s);false)let(readdir_to_dir_list:string->stringlist)=funpath->readdir_to_kind_listpathUnix.S_DIRlet(readdir_to_file_list:string->stringlist)=funpath->readdir_to_kind_listpathUnix.S_REGlet(readdir_to_link_list:string->stringlist)=funpath->readdir_to_kind_listpathUnix.S_LNKlet(readdir_to_dir_size_list:string->(string*int)list)=funpath->Sys.readdirpath+>Array.to_list+>map_filter(funs->letstat=Unix.lstat(path^"/"^s)inifstat.Unix.st_kind=*=Unix.S_DIRthenSome(s,stat.Unix.st_size)elseNone)letunixname()=letuid=Unix.getuid()inletentry=Unix.getpwuiduidinentry.Unix.pw_name(* could be in control section too *)(* Why a use_cache argument ? because sometimes want disable it but dont
* want put the cache_computation funcall in comment, so just easier to
* pass this extra option.
*)letcache_computation2?(verbose=false)?(use_cache=true)fileext_cachef=ifnotuse_cachethenf()elsebeginifnot(Sys.file_existsfile)thenbeginpr2("WARNING: cache_computation: can't find file "^file);pr2("defaulting to calling the function");f()endelsebeginletfile_cache=(file^ext_cache)inifSys.file_existsfile_cache&&filemtimefile_cache>=filemtimefilethenbeginifverbosethenpr2("using cache: "^file_cache);get_valuefile_cacheendelsebeginletres=f()inwrite_valueresfile_cache;resendendendletcache_computation?verbose?use_cacheabc=Common.profile_code"Common.cache_computation"(fun()->cache_computation2?verbose?use_cacheabc)letcache_computation_robust2fileext_cache(need_no_changed_files,need_no_changed_variables)ext_dependf=ifnot(Sys.file_existsfile)thenfailwith("can't find: "^file);letfile_cache=(file^ext_cache)inletdependencies_cache=(file^ext_depend)inletdependencies=(* could do md5sum too *)((file::need_no_changed_files)+>List.map(funf->f,filemtimef),need_no_changed_variables)inifSys.file_existsdependencies_cache&&get_valuedependencies_cache=*=dependenciesthenget_valuefile_cacheelsebeginpr2("cache computation recompute "^file);letres=f()inwrite_valuedependenciesdependencies_cache;write_valueresfile_cache;resendletcache_computation_robustabcde=Common.profile_code"Common.cache_computation_robust"(fun()->cache_computation_robust2abcde)(* dont forget that cmd_to_list call bash and so pattern may contain
* '*' symbols that will be expanded, so can do glob "*.c"
*)letglobpattern=cmd_to_list("ls -1 "^pattern)letdirs_of_dirdir=assert(is_directorydir);letxs=cmd_to_list(spf"find \"%s\" -type d"dir)inmatchxswith|[]->failwith("dirs_of_dir: pb with"^dir)|x::xs->xs(* TODO: do a files_of_dir_or_files ?no_vcs ?filter:Ext|Reg|Filter
*)(* update: have added the -type f, so normally need less the sanity_check_xxx
* function below *)letfiles_of_dir_or_filesextxs=xs+>List.map(funx->ifis_directoryxthencmd_to_list("find "^x^" -noleaf -type f -name \"*."^ext^"\"")else[x])+>List.concatletgrep_dash_v_str="| grep -v /.hg/ |grep -v /CVS/ | grep -v /.git/ |grep -v /_darcs/"^"| grep -v /.svn/ | grep -v .git_annot | grep -v .marshall"letarg_symlink()=if!Common.follow_symlinksthen" -L "else""letfiles_of_dir_or_files_no_vcsextxs=xs+>List.map(funx->ifis_directoryxthencmd_to_list("find "^arg_symlink()^x^" -noleaf -type f -name \"*."^ext^"\""^grep_dash_v_str)else[x])+>List.concatletfiles_of_dir_or_files_no_vcs_nofilterxs=xs+>List.map(funx->ifis_directoryxthencmd_to_list_and_status("find "^arg_symlink()^x^" -noleaf -type f "^grep_dash_v_str)+>fstelse[x])+>List.concatletfiles_of_dir_or_files_no_vcs_post_filterregexxs=xs+>List.map(funx->ifis_directoryxthencmd_to_list("find "^arg_symlink()^x^" -noleaf -type f "^grep_dash_v_str)+>List.filter(funs->s=~regex)else[x])+>List.concatletsanity_check_files_and_adjustextfiles=letfiles=files+>List.filter(funfile->ifnot(file=~(".*\\."^ext))thenbeginpr2("warning: seems not a ."^ext^" file");falseendelseifis_directoryfilethenbeginpr2(spf"warning: %s is a directory"file);falseendelsetrue)infiles(* taken from mlfuse, the predecessor of ocamlfuse *)typerwx=[`R|`W|`X]listletfile_perm_of:u:rwx->g:rwx->o:rwx->Unix.file_perm=fun~u~g~o->letto_octl=List.fold_left(funaccp->acclor((function`R->4|`W->2|`X->1)p))0linletperm=((to_octu)lsl6)lor((to_octg)lsl3)lor(to_octo)inperm(* pixel *)lethas_envvar=failwith"Common.has_env, TODO"(*
try
let _ = Sys.getenv var in true
with Not_found -> false
*)let(with_open_outfile_append:filename->(((string->unit)*out_channel)->'a)->'a)=funfilef->letchan=open_out_gen[Open_creat;Open_append]0o666fileinletprs=output_stringchansinCommon.unwind_protect(fun()->letres=f(pr,chan)inclose_outchan;res)(fune->close_outchan)(* now in prelude:
* exception Timeout
*)(* it seems that the toplevel block such signals, even with this explicit
* command :(
* let _ = Unix.sigprocmask Unix.SIG_UNBLOCK [Sys.sigalrm]
*)(* could be in Control section *)(* subtil: have to make sure that timeout is not intercepted before here, so
* avoid exn handle such as try (...) with _ -> cos timeout will not bubble up
* enough. In such case, add a case before such as
* with Timeout -> raise Timeout | _ -> ...
*
* question: can we have a signal and so exn when in a exn handler ?
*)lettimeout_function?(verbose=false)timeoutval=funf->trybeginSys.set_signalSys.sigalrm(Sys.Signal_handle(fun_->raiseTimeout));ignore(Unix.alarmtimeoutval);letx=f()inignore(Unix.alarm0);xendwithTimeout->beginifverbosethenlog"timeout (we abort)";raiseTimeout;end|e->(* subtil: important to disable the alarm before relaunching the exn,
* otherwise the alarm is still running.
*
* robust?: and if alarm launched after the log (...) ?
* Maybe signals are disabled when process an exception handler ?
*)beginignore(Unix.alarm0);(* log ("exn while in transaction (we abort too, even if ...) = " ^
Printexc.to_string e);
*)ifverbosethenlog"exn while in timeout_function";raiseeendlettimeout_function_opttimeoutvaloptf=matchtimeoutvaloptwith|None->f()|Somex->timeout_functionxfletwith_tmp_file~str~extf=lettmpfile=Common.new_temp_file"tmp"("."^ext)inwrite_file~file:tmpfilestr;ftmpfileletwith_tmp_dirf=lettmp_dir=Filename.temp_file(spf"with-tmp-dir-%d"(Unix.getpid()))""inUnix.unlinktmp_dir;(* who cares about race *)Unix.mkdirtmp_dir0o755;Common.finalize(fun()->ftmp_dir)(fun()->command2(spf"rm -f %s/*"tmp_dir);Unix.rmdirtmp_dir)(* now in prelude: exception UnixExit of int *)letexn_to_real_unixexitf=tryf()withUnixExitx->exitxletuncatxsfile=Common.with_open_outfilefile(fun(pr,_chan)->xs+>List.iter(funs->prs;pr"\n");)(*###########################################################################*)(* Collection-like types *)(*###########################################################################*)(*x: common.ml *)(*****************************************************************************)(* List *)(*****************************************************************************)(* pixel *)letunconsl=(List.hdl,List.tll)(* pixel *)letsafe_tll=tryList.tllwith_->[](* in prelude
let push l v =
l := v :: !l
*)letreczipxsys=match(xs,ys)with|([],[])->[]|([],_)->failwith"zip: not same length"|(_,[])->failwith"zip: not same length"|(x::xs,y::ys)->(x,y)::zipxsysletreczip_safexsys=match(xs,ys)with|([],_)->[]|(_,[])->[]|(x::xs,y::ys)->(x,y)::zip_safexsysletrecunzipzs=List.fold_right(fune(xs,ys)->(fste::xs),(snde::ys))zs([],[])letmap_withkeepfxs=xs+>List.map(funx->fx,x)(* now in prelude
* let rec take n xs =
* match (n,xs) with
* | (0,_) -> []
* | (_,[]) -> failwith "take: not enough"
* | (n,x::xs) -> x::take (n-1) xs
*)letrectake_safenxs=match(n,xs)with|(0,_)->[]|(_,[])->[]|(n,x::xs)->x::take_safe(n-1)xsletrectake_untilp=function|[]->[]|x::xs->ifpxthen[]elsex::(take_untilpxs)lettake_whilep=take_until(p$not)(* now in prelude: let rec drop n xs = ... *)let_=assert(drop3[1;2;3;4]=*=[4])letrecdrop_whilep=function|[]->[]|x::xs->ifpxthendrop_whilepxselsex::xsletrecdrop_untilpxs=drop_while(funx->not(px))xslet_=assert(drop_until(funx->x=|=3)[1;2;3;4;5]=*=[3;4;5])letspanpxs=(take_whilepxs,drop_whilepxs)letrec(span:('a->bool)->'alist->'alist*'alist)=funp->function|[]->([],[])|x::xs->ifpxthenlet(l1,l2)=spanpxsin(x::l1,l2)else([],x::xs)let_=assert((span(funx->x<=3)[1;2;3;4;1;2]=*=([1;2;3],[4;1;2])))letrec(span_tail_call:('a->bool)->'alist->'alist*'alist)=funpxs->letrecauxaccxs=matchxswith|[]->(List.revacc,[])|x::xs->ifpxthenaux(x::acc)xselse(List.revacc,x::xs)inaux[]xslet_=assert((span_tail_call(funx->x<=3)[1;2;3;4;1;2]=*=([1;2;3],[4;1;2])))letrecgroupByeql=matchlwith|[]->[]|x::xs->let(xs1,xs2)=List.partition(funx'->eqxx')xsin(x::xs1)::(groupByeqxs2)(* you should really use group_assoc_bykey_eff *)letrecgroup_by_mapped_keyfkeyl=matchlwith|[]->[]|x::xs->letk=fkeyxinlet(xs1,xs2)=List.partition(funx'->letk2=fkeyx'ink=*=k2)xsin(k,(x::xs1))::(group_by_mapped_keyfkeyxs2)letgroup_and_countxs=xs+>groupBy(=*=)+>List.map(funxs->matchxswith|x::rest->x,List.lengthxs|[]->raiseCommon.Impossible)let(exclude_but_keep_attached:('a->bool)->'alist->('a*'alist)list)=funfxs->letrecaux_filteracc=function|[]->[](* drop what was accumulated because nothing to attach to *)|x::xs->iffxthenaux_filter(x::acc)xselse(x,List.revacc)::aux_filter[]xsinaux_filter[]xslet_=assert(exclude_but_keep_attached(funx->x=|=3)[3;3;1;3;2;3;3;3]=*=[(1,[3;3]);(2,[3])])let(group_by_post:('a->bool)->'alist->('alist*'a)list*'alist)=funfxs->letrecaux_filtergrouped_accacc=function|[]->List.revgrouped_acc,List.revacc|x::xs->iffxthenaux_filter((List.revacc,x)::grouped_acc)[]xselseaux_filtergrouped_acc(x::acc)xsinaux_filter[][]xslet_=assert(group_by_post(funx->x=|=3)[1;1;3;2;3;4;5;3;6;6;6]=*=([([1;1],3);([2],3);[4;5],3],[6;6;6]))let(group_by_pre:('a->bool)->'alist->'alist*('a*'alist)list)=funfxs->letxs'=List.revxsinlet(ys,unclassified)=group_by_postfxs'inList.revunclassified,ys+>List.rev+>List.map(fun(xs,x)->x,List.revxs)let_=assert(group_by_pre(funx->x=|=3)[1;1;3;2;3;4;5;3;6;6;6]=*=([1;1],[(3,[2]);(3,[4;5]);(3,[6;6;6])]))letrec(split_when:('a->bool)->'alist->'alist*'a*'alist)=funp->function|[]->raiseNot_found|x::xs->ifpxthen[],x,xselselet(l1,a,l2)=split_whenpxsin(x::l1,a,l2)let_=assert(split_when(funx->x=|=3)[1;2;3;4;1;2]=*=([1;2],3,[4;1;2]))(* not so easy to come up with ... used in aComment for split_paragraph *)letrecsplit_gen_when_auxfaccxs=matchxswith|[]->ifnullaccthen[]else[List.revacc]|(x::xs)->(matchf(x::xs)with|None->split_gen_when_auxf(x::acc)xs|Some(rest)->letbefore=List.revaccinifnullbeforethensplit_gen_when_auxf[]restelsebefore::split_gen_when_auxf[]rest)(* could avoid introduce extra aux function by using ?(acc = []) *)letsplit_gen_whenfxs=split_gen_when_auxf[]xslet_=assert(split_gen_when(function(42::xs)->Somexs|_->None)[1;2;42;4;5;6;42;7]=*=[[1;2];[4;5;6];[7]])(* generate exception (Failure "tl") if there is no element satisfying p *)letrec(skip_until:('alist->bool)->'alist->'alist)=funpxs->ifpxsthenxselseskip_untilp(List.tlxs)let_=assert(skip_until(function1::2::xs->true|_->false)[1;3;4;1;2;4;5]=*=[1;2;4;5])letrecskipfirste=function|[]->[]|e'::lwhene=*=e'->skipfirstel|l->l(* now in prelude:
* let rec enum x n = ...
*)letindex_listxs=ifnullxsthen[](* enum 0 (-1) generate an exception *)elsezipxs(enum0((List.lengthxs)-1))(* if you want to use this to show the progress while processing huge list,
* consider instead Common_extra.progress
*)letindex_list_and_totalxs=lettotal=List.lengthxsinifnullxsthen[](* enum 0 (-1) generate an exception *)elsezipxs(enum0((List.lengthxs)-1))+>List.map(fun(a,b)->(a,b,total))letindex_list_0xs=index_listxsletindex_list_1xs=xs+>index_list+>List.map(fun(x,i)->x,i+1)letavg_listxs=letsum=sum_intxsin(float_of_intsum)/.(float_of_int(List.lengthxs))letsnocxxs=xs@[x]letconsxxs=x::xslethead_middle_tailxs=matchxswith|x::y::xs->lethead=xinletreversed=List.rev(y::xs)inlettail=List.hdreversedinletmiddle=List.rev(List.tlreversed)inhead,middle,tail|_->failwith"head_middle_tail, too small list"let_=assert_equal(head_middle_tail[1;2;3])(1,[2],3)let_=assert_equal(head_middle_tail[1;3])(1,[],3)(* now in prelude
* let (++) = (@)
*)(* let (++) = (@), could do that, but if load many times the common, then pb *)(* let (++) l1 l2 = List.fold_right (fun x acc -> x::acc) l1 l2 *)letremovexxs=letnewxs=List.filter(funy->y<>x)xsinassert(List.lengthnewxs=|=List.lengthxs-1);newxsletrecremove_firstexs=matchxswith|[]->raiseNot_found|x::xs->ifx=*=ethenxselsex::(remove_firstexs)(* now in prelude
let exclude p xs =
List.filter (fun x -> not (p x)) xs
*)(* now in prelude
*)letfold_kflastkaccxs=letrecfold_k_auxacc=function|[]->lastkacc|x::xs->faccx(funacc->fold_k_auxaccxs)infold_k_auxaccxsletreclist_init=function|[]->raiseNot_found|[x]->[]|x::y::xs->x::(list_init(y::xs))(* now in prelude:
* let rec list_last = function
* | [] -> raise Not_found
* | [x] -> x
* | x::y::xs -> list_last (y::xs)
*)(* pixel *)(* now in prelude
* let last_n n l = List.rev (take n (List.rev l))
* let last l = List.hd (last_n 1 l)
*)letrecjoin_gena=function|[]->[]|[x]->[x]|x::xs->x::a::(join_genaxs)(* todo: foldl, foldr (a more consistent foldr) *)(* start pixel *)letiter_indexfl=letreciter_n=function|[]->()|e::l->fen;iter_(n+1)liniter_0lletmap_indexfl=letrecmap_n=function|[]->[]|e::l->fen::map_(n+1)linmap_0l(* pixel *)letfilter_indexfl=letrecfilti=function|[]->[]|e::l->iffiethene::filt(i+1)lelsefilt(i+1)linfilt0l(* pixel *)letdo_withenvdoitfenvl=letr_env=refenvinletl'=doit(fune->lete',env'=f!r_enveinr_env:=env';e')linl',!r_env(* now in prelude:
* let fold_left_with_index f acc = ...
*)letmap_withenvfenve=do_withenvList.mapfenveletreccollect_accufaccu=function|[]->accu|e::l->collect_accuf(List.rev_append(fe)accu)lletcollectfl=List.rev(collect_accuf[]l)(* cf also List.partition *)letrecfpartitionpl=letrecpartyesno=function|[]->(List.revyes,List.revno)|x::l->(matchpxwith|None->partyes(x::no)l|Somev->part(v::yes)nol)inpart[][]l(* end pixel *)letrecremovelast=function|[]->failwith"removelast"|[_]->[]|e::l->e::removelastlletemptylist=nulllistletrecinits=function|[]->[[]]|e::l->[]::List.map(funl->e::l)(initsl)letrectails=function|[]->[[]]|(_::xs)asxxs->xxs::tailsxsletreverse=List.revletrev=List.revletnth=List.nthletfold_left=List.fold_leftletrev_map=List.rev_map(* pixel *)letrecfold_right1f=function|[]->failwith"fold_right1"|[e]->e|e::l->fe(fold_right1fl)letmaximuml=foldl1maxlletminimuml=foldl1minl(* do a map tail recursive, and result is reversed, it is a tail recursive map => efficient *)letmap_eff_rev=funfl->letrecmap_eff_auxacc=function|[]->acc|x::xs->map_eff_aux((fx)::acc)xsinmap_eff_aux[]lletacc_mapfl=letrecloopacc=function[]->List.revacc|x::xs->loop((fx)::acc)xsinloop[]lletrec(generate:int->'a->'alist)=funiel->ifi=|=0then[]elseel::(generate(i-1)el)letrecuniq=function|[]->[]|e::l->ifList.memelthenuniqlelsee::uniqllethas_no_duplicatexs=List.lengthxs=|=List.length(uniqxs)letis_set_as_list=has_no_duplicateletrecget_duplicatesxs=matchxswith|[]->[]|x::xs->ifList.memxxsthenx::get_duplicatesxs(* todo? could x from xs to avoid double dups?*)elseget_duplicatesxsletrecall_assoce=function|[]->[]|(e',v)::lwhene=*=e'->v::all_assocel|_::l->all_assocelletprepare_want_all_assocl=List.map(funn->n,uniq(all_assocnl))(uniq(List.mapfstl))letrotatelist=List.tllist@[(List.hdlist)]letor_list=List.fold_left(||)falseletand_list=List.fold_left(&&)trueletrec(return_when:('a->'boption)->'alist->'b)=funp->function|[]->raiseNot_found|x::xs->(matchpxwithNone->return_whenpxs|Someb->b)letrecsplitAtnxs=ifn=|=0then([],xs)else(matchxswith|[]->([],[])|(x::xs)->let(a,b)=splitAt(n-1)xsin(x::a,b))letpacknxs=letrecpack_auxli=function|[]->failwith"not on a boundary"|[x]->ifi=|=nthen[l@[x]]elsefailwith"not on a boundary"|x::xs->ifi=|=nthen(l@[x])::(pack_aux[]1xs)elsepack_aux(l@[x])(i+1)xsinpack_aux[]1xsletrecpack_safenxs=matchxswith|[]->[]|y::ys->let(a,b)=splitAtnxsina::pack_safenblet_=assert(pack_safe2[1;2;3;4;5]=*=[[1;2];[3;4];[5]])letchunksnxs=letsize=List.lengthxsinletchunksize=ifsizemodn=|=0thensize/nelse1+(size/n)inletxxs=pack_safechunksizexsinifList.lengthxxs<>nthenfailwith"chunks: impossible, wrong size";xxslet_=assert(chunks2[1;2;3;4]=*=[[1;2];[3;4]])let_=assert(chunks2[1;2;3;4;5]=*=[[1;2;3];[4;5]])letmin_withf=function|[]->raiseNot_found|e::l->letrecmin_with_min_valmin_elt=function|[]->min_elt|e::l->letval_=feinifval_<min_valthenmin_with_val_elelsemin_with_min_valmin_eltlinmin_with_(fe)ellettwo_mins_withf=function|e1::e2::l->letrecmin_with_min_valmin_eltmin_val2min_elt2=function|[]->min_elt,min_elt2|e::l->letval_=feinifval_<min_val2thenifval_<min_valthenmin_with_val_emin_valmin_eltlelsemin_with_min_valmin_eltval_elelsemin_with_min_valmin_eltmin_val2min_elt2linletv1=fe1inletv2=fe2inifv1<v2thenmin_with_v1e1v2e2lelsemin_with_v2e2v1e1l|_->raiseNot_foundletgrep_with_previousf=function|[]->[]|e::l->letrecgrep_with_previous_previous=function|[]->[]|e::l->iffpreviousethene::grep_with_previous_elelsegrep_with_previous_previousline::grep_with_previous_elletiter_with_previousf=function|[]->()|e::l->letreciter_with_previous_previous=function|[]->()|e::l->fpreviouse;iter_with_previous_eliniter_with_previous_elletiter_with_previous_optf=function|[]->()|e::l->fNonee;letreciter_with_previous_previous=function|[]->()|e::l->f(Someprevious)e;iter_with_previous_eliniter_with_previous_elletiter_with_before_afterfxs=letrecauxbefore_revafter=matchafterwith|[]->()|x::xs->fbefore_revxxs;aux(x::before_rev)xsinaux[]xs(* kind of cartesian product of x*x *)letrec(get_pair:('alist)->(('a*'a)list))=function|[]->[]|x::xs->(List.map(funy->(x,y))xs)@(get_pairxs)(* retourne le rang dans une liste d'un element *)letrangelemliste=letrecrang_recelemaccu=function|[]->raiseNot_found|a::l->ifa=*=elemthenaccuelserang_recelem(accu+1)linrang_recelem1liste(* retourne vrai si une liste contient des doubles *)letrecdoublon=function|[]->false|a::l->ifList.memalthentrueelsedoublonlletrec(insert_in:'a->'alist->'alistlist)=funx->function|[]->[[x]]|y::ys->(x::y::ys)::(List.map(funxs->y::xs)(insert_inxys))(* insert_in 3 [1;2] = [[3; 1; 2]; [1; 3; 2]; [1; 2; 3]] *)letrec(permutation:'alist->'alistlist)=function|[]->[]|[x]->[[x]]|x::xs->List.flatten(List.map(insert_inx)(permutationxs))(* permutation [1;2;3] =
* [[1; 2; 3]; [2; 1; 3]; [2; 3; 1]; [1; 3; 2]; [3; 1; 2]; [3; 2; 1]]
*)letrecremove_elem_posposxs=match(pos,xs)with|_,[]->failwith"remove_elem_pos"|0,x::xs->xs|n,x::xs->x::(remove_elem_pos(n-1)xs)letrecinsert_elem_pos(e,pos)xs=match(pos,xs)with|0,xs->e::xs|n,x::xs->x::(insert_elem_pos(e,(n-1))xs)|n,[]->failwith"insert_elem_pos"letrecuncons_permutxs=letindexed=index_listxsinindexed+>List.map(fun(x,pos)->(x,pos),remove_elem_posposxs)let_=assert(uncons_permut['a';'b';'c']=*=[('a',0),['b';'c'];('b',1),['a';'c'];('c',2),['a';'b']])letrecuncons_permut_lazyxs=letindexed=index_listxsinindexed+>List.map(fun(x,pos)->(x,pos),lazy(remove_elem_posposxs))(* pixel *)letrecmap_flattenfl=letrecmap_flatten_auxaccu=function|[]->accu|e::l->map_flatten_aux(List.rev(fe)@accu)linList.rev(map_flatten_aux[]l)(* now in prelude: let rec repeat e n *)letrecmap2f=function|[]->[]|x::xs->letr=fxinr::map2fxsletrecmap3fl=letrecmap3_auxacc=function|[]->acc|x::xs->map3_aux(fx::acc)xsinmap3_aux[]l(*
let tails2 xs = map rev (inits (rev xs))
let res = tails2 [1;2;3;4]
let res = tails [1;2;3;4]
let id x = x
*)letpack_sortedsamexs=letrecpack_s_auxaccxs=match(acc,xs)with|((cur,rest),[])->cur::rest|((cur,rest),y::ys)->ifsame(List.hdcur)ythenpack_s_aux(y::cur,rest)yselsepack_s_aux([y],cur::rest)ysinpack_s_aux([List.hdxs],[])(List.tlxs)+>List.revlettest_pack=pack_sorted(=*=)[1;1;1;2;2;3;4]letreckeep_bestf=letrecpartitione=function|[]->e,[]|e'::l->matchf(e,e')with|None->let(e'',l')=partitioneline'',e'::l'|Somee''->partitione''linfunction|[]->[]|e::l->let(e',l')=partitioneline'::keep_bestfl'letrecsorted_keep_bestf=function|[]->[]|[a]->[a]|a::b::l->matchfabwith|None->a::sorted_keep_bestf(b::l)|Somee->sorted_keep_bestf(e::l)let(cartesian_product:'alist->'blist->('a*'b)list)=funxsys->xs+>List.map(funx->ys+>List.map(funy->(x,y)))+>List.flattenlet_=assert_equal(cartesian_product[1;2]["3";"4";"5"])[1,"3";1,"4";1,"5";2,"3";2,"4";2,"5"]letsort_profab=Common.profile_code"Common.sort_by_xxx"(fun()->List.sortab)typeorder=HighFirst|LowFirstletcompare_orderorderab=matchorderwith|HighFirst->compareba|LowFirst->compareabletsort_by_val_highfirstxs=sort_prof(fun(k1,v1)(k2,v2)->comparev2v1)xsletsort_by_val_lowfirstxs=sort_prof(fun(k1,v1)(k2,v2)->comparev1v2)xsletsort_by_key_highfirstxs=sort_prof(fun(k1,v1)(k2,v2)->comparek2k1)xsletsort_by_key_lowfirstxs=sort_prof(fun(k1,v1)(k2,v2)->comparek1k2)xslet_=assert(sort_by_key_lowfirst[4,();7,()]=*=[4,();7,()])let_=assert(sort_by_key_highfirst[4,();7,()]=*=[7,();4,()])letsortgen_by_key_highfirstxs=sort_prof(fun(k1,v1)(k2,v2)->comparek2k1)xsletsortgen_by_key_lowfirstxs=sort_prof(fun(k1,v1)(k2,v2)->comparek1k2)xs(*----------------------------------*)(* sur surEnsemble [p1;p2] [[p1;p2;p3] [p1;p2] ....] -> [[p1;p2;p3] ... *)(* mais pas p2;p3 *)(* (aop) *)letsurEnsembleliste_elliste_liste_el=List.filter(functionliste_elbis->List.for_all(functionel->List.memelliste_elbis)liste_el)liste_liste_el;;(*----------------------------------*)(* combinaison/product/.... (aop) *)(* 123 -> 123 12 13 23 1 2 3 *)letrecrealCombinaison=function|[]->[]|[a]->[[a]]|a::l->letres=realCombinaisonlinletres2=List.map(functionx->a::x)resinres2@res@[[a]](* genere toutes les combinaisons possible de paire *)(* par exemple combinaison [1;2;4] -> [1, 2; 1, 4; 2, 4] *)letreccombinaison=function|[]->[]|[a]->[]|[a;b]->[(a,b)]|a::b::l->(List.map(functionelem->(a,elem))(b::l))@(combinaison(b::l))(*----------------------------------*)(* list of list(aop) *)(* insere elem dans la liste de liste (si elem est deja present dans une de *)(* ces listes, on ne fait rien *)letrecinsereelem=function|[]->[[elem]]|a::l->if(List.memelema)thena::lelsea::(insereeleml)letrecinsereListeContenantlisel=function|[]->[el::lis]|a::l->ifList.memelathen(List.appendlisa)::lelsea::(insereListeContenantlisell)(* fusionne les listes contenant et1 et et2 dans la liste de liste*)letrecfusionneListeContenant(et1,et2)=function|[]->[[et1;et2]]|a::l->(* si les deux sont deja dedans alors rien faire *)ifList.memet1athenifList.memet2athena::lelseinsereListeContenantaet2lelseifList.memet2atheninsereListeContenantaet1lelsea::(fusionneListeContenant(et1,et2)l)(*****************************************************************************)(* Arrays *)(*****************************************************************************)(* do bound checking ? *)letarray_find_indexfa=letrecarray_find_index_i=iffithenielsearray_find_index_(i+1)intryarray_find_index_0with_->raiseNot_foundletarray_find_index_via_elemfa=letrecarray_find_index_i=iffa.(i)thenielsearray_find_index_(i+1)intryarray_find_index_0with_->raiseNot_foundtypeidx=Idxofintletnext_idx(Idxi)=(Idx(i+1))letint_of_idx(Idxi)=iletarray_find_index_typedfa=letrecarray_find_index_i=iffithenielsearray_find_index_(next_idxi)intryarray_find_index_(Idx0)with_->raiseNot_found(*****************************************************************************)(* Matrix *)(*****************************************************************************)type'amatrix='aarrayarrayletmap_matrixfmat=mat+>Array.map(funarr->arr+>Array.mapf)let(make_matrix_init:nrow:int->ncolumn:int->(int->int->'a)->'amatrix)=fun~nrow~ncolumnf->Array.initnrow(funi->Array.initncolumn(funj->fij))letiter_matrixfm=Array.iteri(funie->Array.iteri(funjx->fijx)e)mletnb_rows_matrixm=Array.lengthmletnb_columns_matrixm=assert(Array.lengthm>0);Array.lengthm.(0)(* check all nested arrays have the same size *)letinvariant_matrixm=raiseCommon.Todolet(rows_of_matrix:'amatrix->'alistlist)=funm->Array.to_listm+>List.mapArray.to_listlet(columns_of_matrix:'amatrix->'alistlist)=funm->letnbcols=nb_columns_matrixminletnbrows=nb_rows_matrixmin(enum0(nbcols-1))+>List.map(funj->(enum0(nbrows-1))+>List.map(funi->m.(i).(j)))letall_elems_matrix_by_rowm=rows_of_matrixm+>List.flattenletex_matrix1=[|[|0;1;2|];[|3;4;5|];[|6;7;8|];|]letex_rows1=[[0;1;2];[3;4;5];[6;7;8];]letex_columns1=[[0;3;6];[1;4;7];[2;5;8];]let_=assert(rows_of_matrixex_matrix1=*=ex_rows1)let_=assert(columns_of_matrixex_matrix1=*=ex_columns1)(*****************************************************************************)(* Fast array *)(*****************************************************************************)(*
module B_Array = Bigarray.Array2
*)(*
open B_Array
open Bigarray
*)(* for the string_of auto generation of camlp4
val b_array_string_of_t : 'a -> 'b -> string
val bigarray_string_of_int16_unsigned_elt : 'a -> string
val bigarray_string_of_c_layout : 'a -> string
let b_array_string_of_t f a = "<>"
let bigarray_string_of_int16_unsigned_elt a = "<>"
let bigarray_string_of_c_layout a = "<>"
*)(*****************************************************************************)(* Set. Have a look too at set*.mli *)(*****************************************************************************)type'aset='alist(* with sexp *)let(empty_set:'aset)=[]let(insert_set:'a->'aset->'aset)=funxxs->ifList.memxxsthen(* let _ = print_string "warning insert: already exist" in *)xselsex::xsletis_setxs=has_no_duplicatexslet(single_set:'a->'aset)=funx->insert_setxempty_setlet(set:'alist->'aset)=funxs->xs+>List.fold_left(flipinsert_set)empty_set+>List.sortcomparelet(exists_set:('a->bool)->'aset->bool)=List.existslet(forall_set:('a->bool)->'aset->bool)=List.for_alllet(filter_set:('a->bool)->'aset->'aset)=List.filterlet(fold_set:('a->'b->'a)->'a->'bset->'a)=List.fold_leftlet(map_set:('a->'b)->'aset->'bset)=List.maplet(member_set:'a->'aset->bool)=List.memletfind_set=List.findletsort_set=List.sortletiter_set=List.iterlet(top_set:'aset->'a)=List.hdlet(inter_set:'aset->'aset->'aset)=funs1s2->s1+>fold_set(funaccx->ifmember_setxs2theninsert_setxaccelseacc)empty_setlet(union_set:'aset->'aset->'aset)=funs1s2->s2+>fold_set(funaccx->ifmember_setxs1thenaccelseinsert_setxacc)s1let(minus_set:'aset->'aset->'aset)=funs1s2->s1+>filter_set(funx->not(member_setxs2))letunion_alll=List.fold_leftunion_set[]lletbig_union_setfxs=xs+>map_setf+>fold_setunion_setempty_setlet(card_set:'aset->int)=List.lengthlet(include_set:'aset->'aset->bool)=funs1s2->(s1+>forall_set(funp->member_setps2))letequal_sets1s2=include_sets1s2&&include_sets2s1let(include_set_strict:'aset->'aset->bool)=funs1s2->(card_sets1<card_sets2)&&(include_sets1s2)let($*$)=inter_setlet($+$)=union_setlet($-$)=minus_setlet($?$)ab=Common.profile_code"$?$"(fun()->member_setab)let($<$)=include_set_strictlet($<=$)=include_setlet($=$)=equal_set(* as $+$ but do not check for memberness, allow to have set of func *)let($@$)=funab->a@bletrecnub=function[]->[]|x::xs->ifList.memxxsthennubxselsex::(nubxs)(*****************************************************************************)(* Set as normal list *)(*****************************************************************************)(*
let (union: 'a list -> 'a list -> 'a list) = fun l1 l2 ->
List.fold_left (fun acc x -> if List.mem x l1 then acc else x::acc) l1 l2
let insert_normal x xs = union xs [x]
(* retourne lis1 - lis2 *)
let minus l1 l2 = List.filter (fun x -> not (List.mem x l2)) l1
let inter l1 l2 = List.fold_left (fun acc x -> if List.mem x l2 then x::acc else acc) [] l1
let union_list = List.fold_left union []
let uniq lis =
List.fold_left (function acc -> function el -> union [el] acc) [] lis
(* pixel *)
let rec non_uniq = function
| [] -> []
| e::l -> if mem e l then e :: non_uniq l else non_uniq l
let rec inclu lis1 lis2 =
List.for_all (function el -> List.mem el lis2) lis1
let equivalent lis1 lis2 =
(inclu lis1 lis2) && (inclu lis2 lis1)
*)(*****************************************************************************)(* Set as sorted list *)(*****************************************************************************)(* liste trie, cos we need to do intersection, and insertion (it is a set
cos when introduce has, if we create a new has => must do a recurse_rep
and another categ can have to this has => must do an union
*)(*
let rec insert x = function
| [] -> [x]
| y::ys ->
if x = y then y::ys
else (if x < y then x::y::ys else y::(insert x ys))
(* same, suppose sorted list *)
let rec intersect x y =
match(x,y) with
| [], y -> []
| x, [] -> []
| x::xs, y::ys ->
if x = y then x::(intersect xs ys)
else
(if x < y then intersect xs (y::ys)
else intersect (x::xs) ys
)
(* intersect [1;3;7] [2;3;4;7;8];; *)
*)(*****************************************************************************)(* Sets specialized *)(*****************************************************************************)(* people often do that *)moduleStringSetOrig=Set.Make(structtypet=stringletcompare=compareend)moduleStringSet=structincludeStringSetOrigletof_listxs=xs+>List.fold_left(funacce->StringSetOrig.addeacc)StringSetOrig.emptyletto_listt=StringSetOrig.elementstend(*****************************************************************************)(* Assoc *)(*****************************************************************************)type('a,'b)assoc=('a*'b)list(* with sexp *)let(assoc_to_function:('a,'b)assoc->('a->'b))=funxs->xs+>List.fold_left(funacc(k,v)->(funk'->ifk=*=k'thenvelseacck'))(funk->failwith"no key in this assoc")(* simpler:
let (assoc_to_function: ('a, 'b) assoc -> ('a -> 'b)) = fun xs ->
fun k -> List.assoc k xs
*)let(empty_assoc:('a,'b)assoc)=[]letfold_assoc=List.fold_leftletinsert_assoc=funxxs->x::xsletmap_assoc=List.mapletfilter_assoc=List.filterletassoc=List.assocletkeysxs=List.mapfstxsletlookup=assoc(* assert unique key ?*)letdel_assockeyxs=xs+>List.filter(fun(k,v)->k<>key)letreplace_assoc(key,v)xs=insert_assoc(key,v)(del_assockeyxs)letapply_assockeyfxs=letold=assockeyxsinreplace_assoc(key,fold)xsletbig_union_assocfxs=xs+>map_assocf+>fold_assocunion_setempty_set(* todo: pb normally can suppr fun l -> .... l but if do that, then strange type _a
=> assoc_map is strange too => equal dont work
*)let(assoc_reverse:(('a*'b)list)->(('b*'a)list))=funl->List.map(fun(x,y)->(y,x))llet(assoc_map:(('a*'b)list)->(('a*'b)list)->(('a*'a)list))=funl1l2->let(l1bis,l2bis)=(assoc_reversel1,assoc_reversel2)inList.map(fun(x,y)->(y,List.assocxl2bis))l1bisletrec(lookup_list:'a->('a,'b)assoclist->'b)=funel->function|[]->raiseNot_found|(xs::xxs)->tryList.assocelxswithNot_found->lookup_listelxxslet(lookup_list2:'a->('a,'b)assoclist->('b*int))=funelxxs->letreclookup_l_auxi=function|[]->raiseNot_found|(xs::xxs)->tryletres=List.assocelxsin(res,i)withNot_found->lookup_l_aux(i+1)xxsinlookup_l_aux0xxslet_=assert(lookup_list2"c"[["a",1;"b",2];["a",1;"b",3];["a",1;"c",7]]=*=(7,2))letassoc_optkl=optionise(fun()->List.assockl)letassoc_with_err_msgkl=tryList.assocklwithNot_found->pr2(spf"pb assoc_with_err_msg: %s"(dumpk));raiseNot_found(*****************************************************************************)(* Assoc int -> xxx with binary tree. Have a look too at Mapb.mli *)(*****************************************************************************)(* ex: type robot_list = robot_info IntMap.t *)moduleIntMap=Map.Make(structtypet=intletcompare=compareend)letintmap_to_listm=IntMap.fold(funidvacc->(id,v)::acc)m[]letintmap_string_of_tfa="<Not Yet>"moduleIntIntMap=Map.Make(structtypet=int*intletcompare=compareend)letintintmap_to_listm=IntIntMap.fold(funidvacc->(id,v)::acc)m[]letintintmap_string_of_tfa="<Not Yet>"(*****************************************************************************)(* Hash *)(*****************************************************************************)(* il parait que better when choose a prime *)lethcreate()=Hashtbl.create401lethadd(k,v)h=Hashtbl.addhkvlethmemkh=Hashtbl.memhklethfindkh=Hashtbl.findhklethreplace(k,v)h=Hashtbl.replacehkvlethiter=Hashtbl.iterlethfold=Hashtbl.foldlethremovekh=Hashtbl.removehklethash_to_listh=Hashtbl.fold(funkvacc->(k,v)::acc)h[]+>List.sortcomparelethash_to_list_unsortedh=Hashtbl.fold(funkvacc->(k,v)::acc)h[]lethash_of_listxs=leth=Hashtbl.create101inbegin(* replace or add? depends the semantic of hashtbl you want *)xs+>List.iter(fun(k,v)->Hashtbl.replacehkv);hend(*
let _ =
let h = Hashtbl.create 101 in
Hashtbl.add h "toto" 1;
Hashtbl.add h "toto" 1;
assert(hash_to_list h =*= ["toto",1; "toto",1])
*)lethfind_defaultkeyvalue_if_not_foundh=tryHashtbl.findhkeywithNot_found->(Hashtbl.addhkey(value_if_not_found());Hashtbl.findhkey)(* not as easy as Perl $h->{key}++; but still possible *)lethupdate_defaultkey~update:op~default:value_if_not_foundh=letold=hfind_defaultkeyvalue_if_not_foundhinHashtbl.replacehkey(opold)letadd1old=old+1letcst_zero()=0lethfind_optionkeyh=optionise(fun()->Hashtbl.findhkey)(* see below: let hkeys h = ... *)letcount_elements_sorted_highfirstxs=leth=Hashtbl.create101inxs+>List.iter(fune->hupdate_defaulte(funold->old+1)(fun()->0)h);letxs=hash_to_listhin(* not very efficient ... but simpler. use max_with_elem stuff ? *)sort_by_val_highfirstxsletmost_recurring_elementxs=letxs'=count_elements_sorted_highfirstxsinmatchxs'with|(e,count)::_->e|[]->failwith"most_recurring_element: empty list"(*****************************************************************************)(* Hash sets *)(*****************************************************************************)type'ahashset=('a,bool)Hashtbl.t(* with sexp *)lethash_hashset_addkeh=matchoptionise(fun()->Hashtbl.findhk)with|Somehset->Hashtbl.replacehsetetrue|None->lethset=Hashtbl.create11inbeginHashtbl.addhkhset;Hashtbl.replacehsetetrue;endlethashset_to_setbaseseth=h+>hash_to_list+>List.mapfst+>(funxs->baseset#fromlistxs)lethashset_to_listh=hash_to_listh+>List.mapfstlethashset_of_listxs=xs+>List.map(funx->x,true)+>hash_of_listlethashset_unionh1h2=h2+>Hashtbl.iter(funk_bool->Hashtbl.replaceh1ktrue)lethashset_interh1h2=h1+>Hashtbl.iter(funk_bool->ifnot(Hashtbl.memh2k)thenHashtbl.removeh1k)lethkeysh=lethkey=Hashtbl.create101inh+>Hashtbl.iter(funkv->Hashtbl.replacehkeyktrue);hashset_to_listhkeylethunionh1h2=h2+>Hashtbl.iter(funkv->Hashtbl.addh1kv)letgroup_assoc_bykey_eff2xs=leth=Hashtbl.create101inxs+>List.iter(fun(k,v)->Hashtbl.addhkv);letkeys=hkeyshinkeys+>List.map(funk->k,Hashtbl.find_allhk)letgroup_assoc_bykey_effxs=Common.profile_code"Common.group_assoc_bykey_eff"(fun()->group_assoc_bykey_eff2xs)lettest_group_assoc()=letxs=enum010000+>List.map(funi->i_to_si,i)inletxs=("0",2)::xsin(* let _ys = xs +> Common.groupBy (fun (a,resa) (b,resb) -> a =$= b) *)letys=xs+>group_assoc_bykey_effinpr2_genysletuniq_effxs=leth=Hashtbl.create101inxs+>List.iter(funk->Hashtbl.replacehktrue);hkeyshletbig_union_effxxs=leth=Hashtbl.create101inxxs+>List.iter(funxs->xs+>List.iter(funk->Hashtbl.replacehktrue););hkeyshletrecuniq_from_sortedacc=function|[]->acc|[x]->x::acc|x::(y::_asrl)whenx=y->uniq_from_sortedaccrl|x::rl->uniq_from_sorted(x::acc)rlletuniq_more_effxs=letl=List.sort(funxy->-comparexy)xsinuniq_from_sorted[]lletdiff_set_effxs1xs2=leth1=hashset_of_listxs1inleth2=hashset_of_listxs2inlethcommon=Hashtbl.create101inlethonly_in_h1=Hashtbl.create101inlethonly_in_h2=Hashtbl.create101inh1+>Hashtbl.iter(funk_->ifHashtbl.memh2kthenHashtbl.replacehcommonktrueelseHashtbl.addhonly_in_h1ktrue);h2+>Hashtbl.iter(funk_->ifHashtbl.memh1kthenHashtbl.replacehcommonktrueelseHashtbl.addhonly_in_h2ktrue);hashset_to_listhcommon,hashset_to_listhonly_in_h1,hashset_to_listhonly_in_h2(*****************************************************************************)(* Hash with default value *)(*****************************************************************************)(*
type ('k,'v) hash_with_default = {
h: ('k, 'v) Hashtbl.t;
default_value: unit -> 'v;
}
*)type('a,'b)hash_with_default=<add:'a->'b->unit;to_list:('a*'b)list;to_h:('a,'b)Hashtbl.t;update:'a->('b->'b)->unit;assoc:'a->'b;>lethash_with_defaultfv=objectvalh=Hashtbl.create101methodto_list=hash_to_listhmethodto_h=hmethodaddkv=Hashtbl.replacehkvmethodassock=Hashtbl.findhkmethodupdatekf=hupdate_defaultk~update:f~default:fvhend(*****************************************************************************)(* Stack *)(*****************************************************************************)type'astack='alist(* with sexp *)let(empty_stack:'astack)=[](*let (push: 'a -> 'a stack -> 'a stack) = fun x xs -> x::xs *)let(top:'astack->'a)=List.hdlet(pop:'astack->'astack)=List.tllettop_option=function|[]->None|x::xs->Somex(* now in prelude:
* let push2 v l = l := v :: !l
*)letpop2l=letv=List.hd!linbeginl:=List.tl!l;vend(*****************************************************************************)(* Undoable Stack *)(*****************************************************************************)(* Okasaki use such structure also for having efficient data structure
* supporting fast append.
*)type'aundo_stack='alist*'alist(* redo *)let(empty_undo_stack:'aundo_stack)=[],[](* push erase the possible redo *)let(push_undo:'a->'aundo_stack->'aundo_stack)=funx(undo,redo)->x::undo,[]let(top_undo:'aundo_stack->'a)=fun(undo,redo)->List.hdundolet(pop_undo:'aundo_stack->'aundo_stack)=fun(undo,redo)->matchundowith|[]->failwith"empty undo stack"|x::xs->xs,x::redolet(undo_pop:'aundo_stack->'aundo_stack)=fun(undo,redo)->matchredowith|[]->failwith"empty redo, nothing to redo"|x::xs->x::undo,xsletredo_undox=undo_popxlettop_undo_option=fun(undo,redo)->matchundowith|[]->None|x::xs->Somex(*****************************************************************************)(* Binary tree *)(*****************************************************************************)(* type 'a bintree = Leaf of 'a | Branch of ('a bintree * 'a bintree) *)(*****************************************************************************)(* N-ary tree *)(*****************************************************************************)(* no empty tree, must have one root at list *)type'atree2=Treeof'a*('atree2)listletrec(tree2_iter:('a->unit)->'atree2->unit)=funftree->matchtreewith|Tree(node,xs)->fnode;xs+>List.iter(tree2_iterf)type('a,'b)tree=|Nodeof'a*('a,'b)treelist|Leafof'b(* with tarzan *)letrecmap_tree~fnode~fleaftree=matchtreewith|Leafx->Leaf(fleafx)|Node(x,xs)->Node(fnodex,xs+>List.map(map_tree~fnode~fleaf))(*****************************************************************************)(* N-ary tree with updatable childrens *)(*****************************************************************************)(* no empty tree, must have one root at list *)type'atreeref=|NodeRefof'a*'atreereflistreflettreeref_children_reftree=matchtreewith|NodeRef(n,x)->xletrec(treeref_node_iter:(* (('a * ('a, 'b) treeref list ref) -> unit) ->
('a, 'b) treeref -> unit
*)'a)=funftree->matchtreewith(* | LeafRef _ -> ()*)|NodeRef(n,xs)->f(n,xs);!xs+>List.iter(treeref_node_iterf)letfind_treerefftree=letres=ref[]intree+>treeref_node_iter(fun(n,xs)->iff(n,xs)thenpush(n,xs)res;);match!reswith|[n,xs]->NodeRef(n,xs)|[]->raiseNot_found|x::y::zs->raiseCommon.Multi_foundletrec(treeref_node_iter_with_parents:(* (('a * ('a, 'b) treeref list ref) -> ('a list) -> unit) ->
('a, 'b) treeref -> unit)
*)'a)=funftree->letrecauxacctree=matchtreewith(* | LeafRef _ -> ()*)|NodeRef(n,xs)->f(n,xs)acc;!xs+>List.iter(aux(n::acc))inaux[]tree(* ---------------------------------------------------------------------- *)(* Leaf can seem redundant, but sometimes want to directly see if
* a children is a leaf without looking if the list is empty.
*)type('a,'b)treeref2=|NodeRef2of'a*('a,'b)treeref2listref|LeafRef2of'blettreeref2_children_reftree=matchtreewith|LeafRef2_->failwith"treeref_tail: leaf"|NodeRef2(n,x)->xletrec(treeref_node_iter2:(('a*('a,'b)treeref2listref)->unit)->('a,'b)treeref2->unit)=funftree->matchtreewith|LeafRef2_->()|NodeRef2(n,xs)->f(n,xs);!xs+>List.iter(treeref_node_iter2f)letfind_treeref2ftree=letres=ref[]intree+>treeref_node_iter2(fun(n,xs)->iff(n,xs)thenpush(n,xs)res;);match!reswith|[n,xs]->NodeRef2(n,xs)|[]->raiseNot_found|x::y::zs->raiseCommon.Multi_foundletrec(treeref_node_iter_with_parents2:(('a*('a,'b)treeref2listref)->('alist)->unit)->('a,'b)treeref2->unit)=funftree->letrecauxacctree=matchtreewith|LeafRef2_->()|NodeRef2(n,xs)->f(n,xs)acc;!xs+>List.iter(aux(n::acc))inaux[]treeletfind_treeref_with_parents_someftree=letres=ref[]intree+>treeref_node_iter_with_parents(fun(n,xs)parents->matchf(n,xs)parentswith|Somev->pushvres;|None->());match!reswith|[v]->v|[]->raiseNot_found|x::y::zs->raiseCommon.Multi_foundletfind_multi_treeref_with_parents_someftree=letres=ref[]intree+>treeref_node_iter_with_parents(fun(n,xs)parents->matchf(n,xs)parentswith|Somev->pushvres;|None->());match!reswith|[v]->!res|[]->raiseNot_found|x::y::zs->!res(*****************************************************************************)(* Graph. Have a look too at Ograph_*.mli *)(*****************************************************************************)(*
* Very simple implementation of a (directed) graph by list of pairs.
* Could also use a matrix, or adjacent list, or pointer(ref).
* todo: do some check (dont exist already, ...)
* todo: generalise to put in common (need 'edge (and 'c ?),
* and take in param a display func, cos caml sux, no overloading of show :(
*)type'nodegraph=('nodeset)*(('node*'node)set)let(add_node:'a->'agraph->'agraph)=funnode(nodes,arcs)->(node::nodes,arcs)let(del_node:'a->'agraph->'agraph)=funnode(nodes,arcs)->(nodes$-$set[node],arcs)(* could do more job:
let _ = assert (successors node (nodes, arcs) = empty) in
+> List.filter (fun (src, dst) -> dst != node))
*)let(add_arc:('a*'a)->'agraph->'agraph)=funarc(nodes,arcs)->(nodes,set[arc]$+$arcs)let(del_arc:('a*'a)->'agraph->'agraph)=funarc(nodes,arcs)->(nodes,arcs+>List.filter(funa->not(arc=*=a)))let(successors:'a->'agraph->'aset)=funx(nodes,arcs)->arcs+>List.filter(fun(src,dst)->src=*=x)+>List.mapsndlet(predecessors:'a->'agraph->'aset)=funx(nodes,arcs)->arcs+>List.filter(fun(src,dst)->dst=*=x)+>List.mapfstlet(nodes:'agraph->'aset)=fun(nodes,arcs)->nodes(* pre: no cycle *)letrec(fold_upward:('b->'a->'b)->'aset->'b->'agraph->'b)=funfxsaccgraph->matchxswith|[]->acc|x::xs->(faccx)+>(funnewacc->fold_upwardf(graph+>predecessorsx)newaccgraph)+>(funnewacc->fold_upwardfxsnewaccgraph)(* TODO avoid already visited *)letempty_graph=([],[])(*
let (add_arcs_toward: int -> (int list) -> 'a graph -> 'a graph) = fun i xs ->
function
(nodes, arcs) -> (nodes, (List.map (fun j -> (j,i) ) xs)++arcs)
let (del_arcs_toward: int -> (int list) -> 'a graph -> 'a graph)= fun i xs g ->
List.fold_left (fun acc el -> del_arc (el, i) acc) g xs
let (add_arcs_from: int -> (int list) -> 'a graph -> 'a graph) = fun i xs ->
function
(nodes, arcs) -> (nodes, (List.map (fun j -> (i,j) ) xs)++arcs)
let (del_node: (int * 'node) -> 'node graph -> 'node graph) = fun node ->
function (nodes, arcs) ->
let newnodes = List.filter (fun a -> not (node = a)) nodes in
if newnodes = nodes then (raise Not_found) else (newnodes, arcs)
let (replace_node: int -> 'node -> 'node graph -> 'node graph) = fun i n ->
function (nodes, arcs) ->
let newnodes = List.filter (fun (j,_) -> not (i = j)) nodes in
((i,n)::newnodes, arcs)
let (get_node: int -> 'node graph -> 'node) = fun i -> function
(nodes, arcs) -> List.assoc i nodes
let (get_free: 'a graph -> int) = function
(nodes, arcs) -> (maximum (List.map fst nodes))+1
(* require no cycle !!
TODO if cycle check that we have already visited a node *)
let rec (succ_all: int -> 'a graph -> (int list)) = fun i -> function
(nodes, arcs) as g ->
let direct = succ i g in
union direct (union_list (List.map (fun i -> succ_all i g) direct))
let rec (pred_all: int -> 'a graph -> (int list)) = fun i -> function
(nodes, arcs) as g ->
let direct = pred i g in
union direct (union_list (List.map (fun i -> pred_all i g) direct))
(* require that the nodes are different !! *)
let rec (equal: 'a graph -> 'a graph -> bool) = fun g1 g2 ->
let ((nodes1, arcs1),(nodes2, arcs2)) = (g1,g2) in
try
(* do 2 things, check same length and to assoc *)
let conv = assoc_map nodes1 nodes2 in
List.for_all (fun (i1,i2) ->
List.mem (List.assoc i1 conv, List.assoc i2 conv) arcs2)
arcs1
&& (List.length arcs1 = List.length arcs2)
(* could think that only forall is needed, but need check same lenth too*)
with _ -> false
let (display: 'a graph -> ('a -> unit) -> unit) = fun g display_func ->
let rec aux depth i =
print_n depth " ";
print_int i; print_string "->"; display_func (get_node i g);
print_string "\n";
List.iter (aux (depth+2)) (succ i g)
in aux 0 1
let (display_dot: 'a graph -> ('a -> string) -> unit)= fun (nodes,arcs) func ->
let file = open_out "test.dot" in
output_string file "digraph misc {\n" ;
List.iter (fun (n, node) ->
output_int file n; output_string file " [label=\"";
output_string file (func node); output_string file " \"];\n"; ) nodes;
List.iter (fun (i1,i2) -> output_int file i1 ; output_string file " -> " ;
output_int file i2 ; output_string file " ;\n"; ) arcs;
output_string file "}\n" ;
close_out file;
let status = Unix.system "viewdot test.dot" in
()
(* todo: faire = graphe (int can change !!! => cant make simply =)
reassign number first !!
*)
(* todo: mettre diff(modulo = !!) en rouge *)
let (display_dot2: 'a graph -> 'a graph -> ('a -> string) -> unit) =
fun (nodes1, arcs1) (nodes2, arcs2) func ->
let file = open_out "test.dot" in
output_string file "digraph misc {\n" ;
output_string file "rotate = 90;\n";
List.iter (fun (n, node) ->
output_string file "100"; output_int file n;
output_string file " [label=\"";
output_string file (func node); output_string file " \"];\n"; ) nodes1;
List.iter (fun (n, node) ->
output_string file "200"; output_int file n;
output_string file " [label=\"";
output_string file (func node); output_string file " \"];\n"; ) nodes2;
List.iter (fun (i1,i2) ->
output_string file "100"; output_int file i1 ; output_string file " -> " ;
output_string file "100"; output_int file i2 ; output_string file " ;\n";
)
arcs1;
List.iter (fun (i1,i2) ->
output_string file "200"; output_int file i1 ; output_string file " -> " ;
output_string file "200"; output_int file i2 ; output_string file " ;\n"; )
arcs2;
(* output_string file "500 -> 1001; 500 -> 2001}\n" ; *)
output_string file "}\n" ;
close_out file;
let status = Unix.system "viewdot test.dot" in
()
*)(*****************************************************************************)(* Generic op *)(*****************************************************************************)(* overloading *)letmap=List.map(* note: really really slow, use rev_map if possible *)letfilter=List.filterletfold=List.fold_leftletmember=List.memletiter=List.iterletfind=List.findletexists=List.existsletforall=List.for_allletbig_unionfxs=xs+>mapf+>foldunion_setempty_set(* let empty = [] *)letempty_list=[]letsortxs=List.sortcomparexsletlength=List.length(* in prelude now: let null xs = match xs with [] -> true | _ -> false *)lethead=List.hdlettail=List.tlletis_singleton=funxs->List.lengthxs=|=1(*x: common.ml *)(*###########################################################################*)(* Misc functions *)(*###########################################################################*)(*****************************************************************************)(* Geometry (raytracer) *)(*****************************************************************************)typevector=(float*float*float)typepoint=vectortypecolor=vector(* color(0-1) *)(* todo: factorise *)let(dotproduct:vector*vector->float)=fun((x1,y1,z1),(x2,y2,z2))->(x1*.x2+.y1*.y2+.z1*.z2)let(vector_length:vector->float)=fun(x,y,z)->sqrt(squarex+.squarey+.squarez)let(minus_point:point*point->vector)=fun((x1,y1,z1),(x2,y2,z2))->((x1-.x2),(y1-.y2),(z1-.z2))let(distance:point*point->float)=fun(x1,x2)->vector_length(minus_point(x2,x1))let(normalise:vector->vector)=fun(x,y,z)->letlen=vector_length(x,y,z)in(x/.len,y/.len,z/.len)let(mult_coeff:vector->float->vector)=fun(x,y,z)c->(x*.c,y*.c,z*.c)let(add_vector:vector->vector->vector)=funv1v2->let((x1,y1,z1),(x2,y2,z2))=(v1,v2)in(x1+.x2,y1+.y2,z1+.z2)let(mult_vector:vector->vector->vector)=funv1v2->let((x1,y1,z1),(x2,y2,z2))=(v1,v2)in(x1*.x2,y1*.y2,z1*.z2)letsum_vector=List.fold_leftadd_vector(0.0,0.0,0.0)(*****************************************************************************)(* Pics (raytracer) *)(*****************************************************************************)typepixel=(int*int*int)(* RGB *)(* required pixel list in row major order, line after line *)let(write_ppm:int->int->(pixellist)->string->unit)=funwidthheightxsfilename->letchan=open_outfilenameinbeginoutput_stringchan"P6\n";output_stringchan((string_of_intwidth)^"\n");output_stringchan((string_of_intheight)^"\n");output_stringchan"255\n";List.iter(fun(r,g,b)->List.iter(funbyt->output_bytechanbyt)[r;g;b])xs;close_outchanendlettest_ppm1()=write_ppm100100((generate(50*100)(1,45,100))@(generate(50*100)(1,1,100)))"img.ppm"(*****************************************************************************)(* Diff (lfs) *)(*****************************************************************************)typediff=Match|BnotinA|AnotinBlet(diff:(int->int->diff->unit)->(stringlist*stringlist)->unit)=funf(xs,ys)->letfile1="/tmp/diff1-"^(string_of_int(Unix.getuid()))inletfile2="/tmp/diff2-"^(string_of_int(Unix.getuid()))inletfileresult="/tmp/diffresult-"^(string_of_int(Unix.getuid()))inwrite_filefile1(unwordsxs);write_filefile2(unwordsys);command2("diff --side-by-side -W 1 "^file1^" "^file2^" > "^fileresult);letres=catfileresultinleta=ref0inletb=ref0inres+>List.iter(funs->matchswith|(""|" ")->f!a!bMatch;incra;incrb;|">"->f!a!bBnotinA;incrb;|("|"|"/"|"\\")->f!a!bBnotinA;f!a!bAnotinB;incra;incrb;|"<"->f!a!bAnotinB;incra;|_->raiseCommon.Impossible)(*
let _ =
diff
["0";"a";"b";"c";"d"; "f";"g";"h";"j";"q"; "z"]
[ "a";"b";"c";"d";"e";"f";"g";"i";"j";"k";"r";"x";"y";"z"]
(fun x y -> pr "match")
(fun x y -> pr "a_not_in_b")
(fun x y -> pr "b_not_in_a")
*)let(diff2:(int->int->diff->unit)->(string*string)->unit)=funf(xstr,ystr)->write_file"/tmp/diff1"xstr;write_file"/tmp/diff2"ystr;command2("diff --side-by-side --left-column -W 1 "^"/tmp/diff1 /tmp/diff2 > /tmp/diffresult");letres=cat"/tmp/diffresult"inleta=ref0inletb=ref0inres+>List.iter(funs->matchswith|"("->f!a!bMatch;incra;incrb;|">"->f!a!bBnotinA;incrb;|"|"->f!a!bBnotinA;f!a!bAnotinB;incra;incrb;|"<"->f!a!bAnotinB;incra;|_->raiseCommon.Impossible)(*****************************************************************************)(* Grep *)(*****************************************************************************)(* src: coccinelle *)letcontain_any_token_with_egreptokensfile=lettokens=tokens+>List.map(funs->match()with|_whens=~"^[A-Za-z_][A-Za-z_0-9]*$"->"\\b"^s^"\\b"|_whens=~"^[A-Za-z_]"->"\\b"^s|_whens=~".*[A-Za-z_]$"->s^"\\b"|_->s)inletcmd=spf"egrep -q '(%s)' %s"(join"|"tokens)filein(matchSys.commandcmdwith|0(* success *)->true|_(* failure *)->false(* no match, so not worth trying *))(*****************************************************************************)(* Parsers (aop-colcombet) *)(*****************************************************************************)letparserCommonlexbufparsererlexer=tryletresult=parsererlexerlexbufinresultwithParsing.Parse_error->print_string"buf: ";print_byteslexbuf.Lexing.lex_buffer;print_string"\n";print_string"current: ";print_intlexbuf.Lexing.lex_curr_pos;print_string"\n";raiseParsing.Parse_error(* marche pas ca neuneu *)(*
let getDoubleParser parserer lexer string =
let lexbuf1 = Lexing.from_string string in
let chan = open_in string in
let lexbuf2 = Lexing.from_channel chan in
(parserCommon lexbuf1 parserer lexer , parserCommon lexbuf2 parserer lexer )
*)letgetDoubleParserparsererlexer=((functionstring->letlexbuf1=Lexing.from_stringstringinparserCommonlexbuf1parsererlexer),(functionstring->letchan=open_instringinletlexbuf2=Lexing.from_channelchaninparserCommonlexbuf2parsererlexer))(*****************************************************************************)(* parser combinators *)(*****************************************************************************)(* cf parser_combinators.ml
*
* Could also use ocaml stream. but not backtrack and forced to do LL,
* so combinators are better.
*
*)(*****************************************************************************)(* Parser related (cocci) *)(*****************************************************************************)(* now in h_program-lang/parse_info.ml *)(*x: common.ml *)(*****************************************************************************)(* Regression testing bis (cocci) *)(*****************************************************************************)(* todo: keep also size of file, compute md5sum ? cos maybe the file
* has changed!.
*
* todo: could also compute the date, or some version info of the program,
* can record the first date when was found a OK, the last date where
* was ok, and then first date when found fail. So the
* Common.Ok would have more information that would be passed
* to the Common.Pb of date * date * date * string peut etre.
*
* todo? maybe use plain text file instead of marshalling.
*)typescore_result=Ok|Pbofstring(* with sexp *)typescore=(string(* usually a filename *),score_result)Hashtbl.t(* with sexp *)typescore_list=(string(* usually a filename *)*score_result)list(* with sexp *)letempty_score()=(Hashtbl.create101:score)letregression_testing_vsnewscorebestscore=letnewbestscore=empty_score()inletallres=(hash_to_listnewscore+>List.mapfst)$+$(hash_to_listbestscore+>List.mapfst)inbeginallres+>List.iter(funres->matchoptionise(fun()->Hashtbl.findnewscoreres),optionise(fun()->Hashtbl.findbestscoreres)with|None,None->raiseCommon.Impossible|Somex,None->Printf.printf"new test file appeared: %s\n"res;Hashtbl.addnewbestscoreresx;|None,Somex->Printf.printf"old test file disappeared: %s\n"res;|Somenewone,Somebestone->(matchnewone,bestonewith|Ok,Ok->Hashtbl.addnewbestscoreresOk|Pbx,Ok->Printf.printf"PBBBBBBBB: a test file does not work anymore!!! : %s\n"res;Printf.printf"Error : %s\n"x;Hashtbl.addnewbestscoreresOk|Ok,Pbx->Printf.printf"Great: a test file now works: %s\n"res;Hashtbl.addnewbestscoreresOk|Pbx,Pby->Hashtbl.addnewbestscoreres(Pbx);ifnot(x=$=y)thenbeginPrintf.printf"Semipb: still error but not same error : %s\n"res;Printf.printf"%s\n"(chop("Old error: "^y));Printf.printf"New error: %s\n"x;end));flushstdout;flushstderr;newbestscoreendletregression_testingnewscorebest_score_file=pr2("regression file: "^best_score_file);let(bestscore:score)=ifnot(Sys.file_existsbest_score_file)thenwrite_value(empty_score())best_score_file;get_valuebest_score_fileinletnewbestscore=regression_testing_vsnewscorebestscoreinwrite_valuenewbestscore(best_score_file^".old");write_valuenewbestscorebest_score_file;()letstring_of_score_resultv=matchvwith|Ok->"Ok"|Pbs->"Pb: "^slettotal_scoresscore=lettotal=hash_to_listscore+>List.lengthinletgood=hash_to_listscore+>List.filter(fun(s,v)->v=*=Ok)+>List.lengthingood,totalletprint_total_scorescore=pr2"--------------------------------";pr2"total score";pr2"--------------------------------";let(good,total)=total_scoresscoreinpr2(Printf.sprintf"good = %d/%d"goodtotal)letprint_scorescore=score+>hash_to_list+>List.iter(fun(k,v)->pr2(Printf.sprintf"%s --> %s"k(string_of_score_resultv)));print_total_scorescore;()(*x: common.ml *)(*****************************************************************************)(* Scope managment (cocci) *)(*****************************************************************************)(* could also make a function Common.make_scope_functions that return
* the new_scope, del_scope, do_in_scope, add_env. Kind of functor :)
*)type('a,'b)scoped_env=('a,'b)assoclist(*
let rec lookup_env f env =
match env with
| [] -> raise Not_found
| []::zs -> lookup_env f zs
| (x::xs)::zs ->
match f x with
| None -> lookup_env f (xs::zs)
| Some y -> y
let member_env_key k env =
try
let _ = lookup_env (fun (k',v) -> if k = k' then Some v else None) env in
true
with Not_found -> false
*)letreclookup_envkenv=matchenvwith|[]->raiseNot_found|[]::zs->lookup_envkzs|((k',v)::xs)::zs->ifk=*=k'thenvelselookup_envk(xs::zs)letmember_env_keykenv=matchoptionise(fun()->lookup_envkenv)with|None->false|Some_->trueletnew_scopescoped_env=scoped_env:=[]::!scoped_envletdel_scopescoped_env=scoped_env:=List.tl!scoped_envletdo_in_new_scopescoped_envf=beginnew_scopescoped_env;letres=f()indel_scopescoped_env;resendletadd_in_scopescoped_envdef=let(current,older)=uncons!scoped_envinscoped_env:=(def::current)::older(* note that ocaml hashtbl store also old value of a binding when add
* add a newbinding; that's why del_scope works
*)type('a,'b)scoped_h_env={scoped_h:('a,'b)Hashtbl.t;scoped_list:('a,'b)assoclist;}letempty_scoped_h_env()={scoped_h=Hashtbl.create101;scoped_list=[[]];}letclone_scoped_h_envx={scoped_h=Hashtbl.copyx.scoped_h;scoped_list=x.scoped_list;}letreclookup_h_envkenv=Hashtbl.findenv.scoped_hkletmember_h_env_keykenv=matchoptionise(fun()->lookup_h_envkenv)with|None->false|Some_->trueletnew_scope_hscoped_env=scoped_env:={!scoped_envwithscoped_list=[]::!scoped_env.scoped_list}letdel_scope_hscoped_env=beginList.hd!scoped_env.scoped_list+>List.iter(fun(k,v)->Hashtbl.remove!scoped_env.scoped_hk);scoped_env:={!scoped_envwithscoped_list=List.tl!scoped_env.scoped_list}endletdo_in_new_scope_hscoped_envf=beginnew_scope_hscoped_env;letres=f()indel_scope_hscoped_env;resend(*
let add_in_scope scoped_env def =
let (current, older) = uncons !scoped_env in
scoped_env := (def::current)::older
*)letadd_in_scope_hx(k,v)=beginHashtbl.add!x.scoped_hkv;x:={!xwithscoped_list=((k,v)::(List.hd!x.scoped_list))::(List.tl!x.scoped_list);};end(*****************************************************************************)(* Terminal *)(*****************************************************************************)(* See console.ml *)(*****************************************************************************)(* Gc optimisation (pfff) *)(*****************************************************************************)(* opti: to avoid stressing the GC with a huge graph, we sometimes
* change a big AST into a string, which reduces the size of the graph
* to explore when garbage collecting.
*)type'acached='aserialized_mayberefand'aserialized_maybe=|Serialofstring|Unfoldof'aletserialx=ref(Serial(Marshal.to_stringx[]))letunserialx=match!xwith|Unfoldc->c|Serials->letres=Marshal.from_strings0in(* x := Unfold res; *)res(*****************************************************************************)(* Random *)(*****************************************************************************)let_init_random=Random.self_init()(*
let random_insert i l =
let p = Random.int (length l +1)
in let rec insert i p l =
if (p = 0) then i::l else (hd l)::insert i (p-1) (tl l)
in insert i p l
let rec randomize_list = function
[] -> []
| a::l -> random_insert a (randomize_list l)
*)letrandom_listxs=List.nthxs(Random.int(lengthxs))(* todo_opti: use fisher/yates algorithm.
* ref: http://en.wikipedia.org/wiki/Knuth_shuffle
*
* public static void shuffle (int[] array)
* {
* Random rng = new Random ();
* int n = array.length;
* while (--n > 0)
* {
* int k = rng.nextInt(n + 1); // 0 <= k <= n (!)
* int temp = array[n];
* array[n] = array[k];
* array[k] = temp;
* }
* }
*)letrandomize_listxs=letpermut=permutationxsinrandom_listpermutletrandom_subset_of_listnumxs=letarray=Array.of_listxsinletlen=Array.lengtharrayinleth=Hashtbl.create101inletcnt=refnuminwhile!cnt>0doletx=Random.intleninifnot(Hashtbl.memh(array.(x)))(* bugfix2: not just x :) *)thenbeginHashtbl.addh(array.(x))true;(* bugfix1: not just x :) *)decrcnt;enddone;letobjs=hash_to_listh+>List.mapfstinobjs(*x: common.ml *)(*###########################################################################*)(* Postlude *)(*###########################################################################*)(*****************************************************************************)(* Flags and actions *)(*****************************************************************************)(*s: common.ml cmdline *)(* I put it inside a func as it can help to give a chance to
* change the globals before getting the options as some
* options sometimes may want to show the default value.
*)letcmdline_flags_devel()=["-debugger",Arg.SetCommon.debugger," option to set if launched inside ocamldebug";"-profile",Arg.Unit(fun()->Common.profile:=Common.ProfAll)," output profiling information";]letcmdline_flags_verbose()=["-verbose_level",Arg.Set_intverbose_level," <int> guess what";"-disable_pr2_once",Arg.SetCommon.disable_pr2_once," to print more messages";"-show_trace_profile",Arg.SetCommon.show_trace_profile," show trace";]letcmdline_flags_other()=["-nocheck_stack",Arg.Clear_check_stack," ";"-batch_mode",Arg.Set_batch_mode," no interactivity";"-keep_tmp_files",Arg.SetCommon.save_tmp_files," ";](* potentially other common options but not yet integrated:
"-timeout", Arg.Set_int timeout,
" <sec> interrupt LFS or buggy external plugins";
(* can't be factorized because of the $ cvs stuff, we want the date
* of the main.ml file, not common.ml
*)
"-version", Arg.Unit (fun () ->
pr2 "version: _dollar_Date: 2008/06/14 00:54:22 _dollar_";
raise (Common.UnixExit 0)
),
" guess what";
"-shorthelp", Arg.Unit (fun () ->
!short_usage_func();
raise (Common.UnixExit 0)
),
" see short list of options";
"-longhelp", Arg.Unit (fun () ->
!long_usage_func();
raise (Common.UnixExit 0)
),
"-help", Arg.Unit (fun () ->
!long_usage_func();
raise (Common.UnixExit 0)
),
" ";
"--help", Arg.Unit (fun () ->
!long_usage_func();
raise (Common.UnixExit 0)
),
" ";
*)letcmdline_actions()=["-test_check_stack"," <limit>",Common.mk_action_1_argtest_check_stack_size;](*e: common.ml cmdline *)(*x: common.ml *)(*****************************************************************************)(* Postlude *)(*****************************************************************************)(* stuff put here cos of of forward definition limitation of ocaml *)(* Infix trick, seen in jane street lib and harrop's code, and maybe in GMP *)moduleInfix=structlet(+>)=(+>)let(|>)=(|>)let(==~)=(==~)let(=~)=(=~)end(* based on code found in cameleon from maxence guesdon
* alt: use Digest.string! far faster!
*)letmd5sum_of_strings=letcom=spf"echo %s | md5sum | cut -d\" \" -f 1"(Filename.quotes)inmatchcmd_to_listcomwith|[s]->(*pr2 s;*)s|_->failwith"md5sum_of_string wrong output"(* less: could also use the realpath C binding in Jane Street Core library. *)letrealpathpath=matchcmd_to_list(spf"realpath %s"path)with|[s]->s|xs->failwith(spf"problem with realpath on %s: %s "path(unlinesxs))letwith_pr2_to_stringf=letfile=Common.new_temp_file"pr2""out"inredirect_stdout_stderrfilef;catfile(* julia: convert something printed using format to print into a string *)letformat_to_stringf=let(nm,o)=Filename.open_temp_file"format_to_s"".out"in(* to avoid interference with other code using Format.printf, e.g.
* Ounit.run_tt
*)Format.print_flush();Format.set_formatter_out_channelo;let_=f()inFormat.print_newline();Format.print_flush();Format.set_formatter_out_channelstdout;close_outo;leti=open_innminletlines=ref[]inletrecloop_=letcur=input_lineiinlines:=cur::!lines;loop()in(tryloop()withEnd_of_file->());close_ini;command2("rm -f "^nm);String.concat"\n"(List.rev!lines)(*---------------------------------------------------------------------------*)(* Directories part 2 *)(*---------------------------------------------------------------------------*)(* todo? vs common_prefix_of_files_or_dirs? *)letfind_common_rootfiles=letdirs_part=files+>List.mapfstinletrecauxcurrent_candidatexs=trylettopsubdirs=xs+>List.mapList.hd+>uniq_effin(matchtopsubdirswith|[x]->aux(x::current_candidate)(xs+>List.mapList.tl)|_->List.revcurrent_candidate)with_->List.revcurrent_candidateinaux[]dirs_part(*
let _ = example
(find_common_root
[(["home";"pad"], "foo.php");
(["home";"pad";"bar"], "bar.php");
]
=*= ["home";"pad"])
*)letdirs_and_base_of_filefile=let(dir,base)=db_of_filenamefileinletdirs=split"/"dirinletdirs=matchdirswith|["."]->[]|_->dirsindirs,base(*
let _ = example
(dirs_and_base_of_file "/home/pad/foo.php" =*= (["home";"pad"], "foo.php"))
*)letinits_of_absolute_dirdir=ifnot(is_absolutedir)thenfailwith(spf"inits_of_absolute_dir: %s is not an absolute path"dir);ifnot(is_directorydir)thenfailwith(spf"inits_of_absolute_dir: %s is not a directory"dir);letdir=chop_dirsymboldirinletdirs=split"/"dirinletdirs=matchdirswith|["."]->[]|_->dirsininitsdirs+>List.map(funxs->"/"^join"/"xs)letinits_of_relative_dirdir=ifnot(is_relativedir)thenfailwith(spf"inits_of_relative_dir: %s is not a relative dir"dir);letdir=chop_dirsymboldirinletdirs=split"/"dirinletdirs=matchdirswith|["."]->[]|_->dirsininitsdirs+>List.tl+>List.map(funxs->join"/"xs)(*
let _ = example
(inits_of_absolute_dir "/usr/bin" =*= (["/"; "/usr"; "/usr/bin"]))
let _ = example
(inits_of_relative_dir "usr/bin" =*= (["usr"; "usr/bin"]))
*)(* main entry *)let(tree_of_files:filenamelist->(string,(string*filename))tree)=funfiles->letfiles_fullpath=filesin(* extract dirs and file from file, e.g. ["home";"pad"], "__flib.php", path *)letfiles=files+>List.mapdirs_and_base_of_filein(* find root, eg ["home";"pad"] *)letroot=find_common_rootfilesinletfiles=zipfilesfiles_fullpathin(* remove the root part *)letfiles=files+>List.map(fun((dirs,base),path)->letn=List.lengthrootinlet(root',rest)=takendirs,dropndirsinassert(root'=*=root);(rest,base),path)in(* now ready to build the tree recursively *)letrecaux(xs:((stringlist*string)*filename)list)=letfiles_here,rest=xs+>List.partition(fun((dirs,base),_)->nulldirs)inletgroups=rest+>group_by_mapped_key(fun((dirs,base),_)->(* would be a file if null dirs *)assert(not(nulldirs));List.hddirs)inletnodes=groups+>List.map(fun(k,xs)->letxs'=xs+>List.map(fun((dirs,base),path)->(List.tldirs,base),path)inNode(k,auxxs'))inletleaves=files_here+>List.map(fun((_dir,base),path)->Leaf(base,path))innodes@leavesinNode(join"/"root,auxfiles)(* finding the common root *)letcommon_prefix_of_files_or_dirs2xs=letxs=xs+>List.maprelative_to_absoluteinmatchxswith|[]->failwith"common_prefix_of_files_or_dirs: empty list"|[x]->x|y::ys->(* todo: work when dirs ?*)letxs=xs+>List.mapdirs_and_base_of_fileinletdirs=find_common_rootxsin"/"^join"/"dirsletcommon_prefix_of_files_or_dirsxs=Common.profile_code"Common.common_prefix_of"(fun()->common_prefix_of_files_or_dirs2xs)(*
let _ =
example
(common_prefix_of_files_or_dirs ["/home/pad/pfff/visual";
"/home/pad/pfff/commons";]
=*= "/home/pad/pfff"
)
*)letunix_diff_stringss1s2=lettmp1=Common.new_temp_file"s1"""inwrite_filetmp1s1;lettmp2=Common.new_temp_file"s2"""inwrite_filetmp2s2;unix_difftmp1tmp2(*****************************************************************************)(* Misc/test *)(*****************************************************************************)let(generic_print:'a->string->string)=funvtyp->write_valuev"/tmp/generic_print";command2("printf 'let (v:"^typ^")= Common.get_value \"/tmp/generic_print\" "^" in v;;' "^" | calc.top > /tmp/result_generic_print");cat"/tmp/result_generic_print"+>drop_while(fune->not(e=~"^#.*"))+>tail+>unlines+>(funs->if(s=~".*= \\(.+\\)")thenmatched1selse"error in generic_print, not good format:"^s)(* let main () = pr (generic_print [1;2;3;4] "int list") *)class['a]olist(ys:'alist)=object(o)valxs=ysmethodview=xs(* method fold f a = List.fold_left f a xs *)methodfold:'b.('b->'a->'b)->'b->'b=funfaccu->List.fold_leftfaccuxsend(* let _ = write_value ((new setb[])#add 1) "/tmp/test" *)lettyping_sux_test()=letx=Obj.magic[1;2;3]inletf1xs=List.iterprint_intxsinletf2xs=List.iterprint_stringxsin(f1x;f2x)(* let (test: 'a osetb -> 'a ocollection) = fun o -> (o :> 'a ocollection) *)(* let _ = test (new osetb (Setb.empty)) *)(*e: common.ml *)