1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223(*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.
*)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.gmtimein(tm.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)(* Given a file name, normalize it by removing "." and "..". This works
* exclusively by processing the string, not relying on any file system
* state.
*
* This is intended to work on both absolute and relative paths, but I
* have not tested the latter.
*
* Compared to a previous version of normalize_path,
* this function normalizes "a/." to "a" and does not drop the leading "/"
* on absolute paths. It also does not crash on "/..".
*
* author: Scott McPeak.
*)letnormalize_path(file:string):string=letis_rel=Filename.is_relativefileinletxs=split"/"fileinletrecauxacc=function|[]->List.revacc|x::xs->(matchxwith|"."->auxaccxs|".."->(matchaccwith|[]->ifis_relthen(* Keep leading ".." on relative paths. *)aux(x::acc)xselse(* Strip leading ".." on absolute paths. The ".."
* directory entry on "/" points to "/". *)auxaccxs|[".."]->(* If we kept "..", this must be a relative path that
* effectively starts with "..", so keep accumulating
* them. ("../.." does not become "".) *)assertis_rel;aux(x::acc)xs|_->aux(List.tlacc)xs)|x->aux(x::acc)xs)inletxs'=aux[]xsin(ifis_relthen""else"/")^(join"/"xs')(*
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:string)~(ext:string)(f:string->'a):'a=lettmpfile=Common.new_temp_file"tmp"("."^ext)inwrite_file~file:tmpfilestr;Common.finalize(fun()->ftmpfile)(fun()->Unix.unlinktmpfile)letwith_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 *)