1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995(**************************************************************************)(* *)(* OCaml *)(* *)(* Benoit Vaugon, ENSTA *)(* *)(* Copyright 2014 Institut National de Recherche en Informatique et *)(* en Automatique. *)(* *)(* All rights reserved. This file is distributed under the terms of *)(* the GNU Lesser General Public License version 2.1, with the *)(* special exception on linking described in the file LICENSE. *)(* *)(**************************************************************************)openCamlinternalFormatBasics(******************************************************************************)(* Tools to manipulate scanning set of chars (see %[...]) *)typemutable_char_set=bytes(* Create a fresh, empty, mutable char set. *)letcreate_char_set()=Bytes.make32'\000'(* Add a char in a mutable char set. *)letadd_in_char_setchar_setc=letind=int_of_charcinletstr_ind=indlsr3andmask=1lsl(indland0b111)inBytes.setchar_setstr_ind(char_of_int(int_of_char(Bytes.getchar_setstr_ind)lormask))letfreeze_char_setchar_set=Bytes.to_stringchar_set(* Compute the complement of a char set. *)letrev_char_setchar_set=letchar_set'=create_char_set()infori=0to31doBytes.setchar_set'i(char_of_int(int_of_char(String.getchar_seti)lxor0xFF));done;Bytes.unsafe_to_stringchar_set'(* Return true if a `c' is in `char_set'. *)letis_in_char_setchar_setc=letind=int_of_charcinletstr_ind=indlsr3andmask=1lsl(indland0b111)in(int_of_char(String.getchar_setstr_ind)landmask)<>0(******************************************************************************)(* Ignored param conversion *)(* GADT used to abstract an existential type parameter. *)(* See param_format_of_ignored_format. *)type('a,'b,'c,'d,'e,'f)param_format_ebb=Param_format_EBB:('x->'a,'b,'c,'d,'e,'f)fmt->('a,'b,'c,'d,'e,'f)param_format_ebb(* Compute a padding associated to a pad_option (see "%_42d"). *)letpad_of_pad_optpad_opt=matchpad_optwith|None->No_padding|Somewidth->Lit_padding(Right,width)(* Compute a precision associated to a prec_option (see "%_.42f"). *)letprec_of_prec_optprec_opt=matchprec_optwith|None->No_precision|Somendec->Lit_precisionndec(* Turn an ignored param into its equivalent not-ignored format node. *)(* Used for format pretty-printing and Scanf. *)letparam_format_of_ignored_format:typeabcdefxy.(a,b,c,d,y,x)ignored->(x,b,c,y,e,f)fmt->(a,b,c,d,e,f)param_format_ebb=funignfmt->matchignwith|Ignored_char->Param_format_EBB(Charfmt)|Ignored_caml_char->Param_format_EBB(Caml_charfmt)|Ignored_stringpad_opt->Param_format_EBB(String(pad_of_pad_optpad_opt,fmt))|Ignored_caml_stringpad_opt->Param_format_EBB(Caml_string(pad_of_pad_optpad_opt,fmt))|Ignored_int(iconv,pad_opt)->Param_format_EBB(Int(iconv,pad_of_pad_optpad_opt,No_precision,fmt))|Ignored_int32(iconv,pad_opt)->Param_format_EBB(Int32(iconv,pad_of_pad_optpad_opt,No_precision,fmt))|Ignored_nativeint(iconv,pad_opt)->Param_format_EBB(Nativeint(iconv,pad_of_pad_optpad_opt,No_precision,fmt))|Ignored_int64(iconv,pad_opt)->Param_format_EBB(Int64(iconv,pad_of_pad_optpad_opt,No_precision,fmt))|Ignored_float(pad_opt,prec_opt)->Param_format_EBB(Float((Float_flag_,Float_f),pad_of_pad_optpad_opt,prec_of_prec_optprec_opt,fmt))|Ignored_boolpad_opt->Param_format_EBB(Bool(pad_of_pad_optpad_opt,fmt))|Ignored_format_arg(pad_opt,fmtty)->Param_format_EBB(Format_arg(pad_opt,fmtty,fmt))|Ignored_format_subst(pad_opt,fmtty)->Param_format_EBB(Format_subst(pad_opt,fmtty,fmt))|Ignored_reader->Param_format_EBB(Readerfmt)|Ignored_scan_char_set(width_opt,char_set)->Param_format_EBB(Scan_char_set(width_opt,char_set,fmt))|Ignored_scan_get_countercounter->Param_format_EBB(Scan_get_counter(counter,fmt))|Ignored_scan_next_char->Param_format_EBB(Scan_next_charfmt)(******************************************************************************)(* Types *)type('b,'c)acc_formatting_gen=|Acc_open_tagof('b,'c)acc|Acc_open_boxof('b,'c)acc(* Reversed list of printing atoms. *)(* Used to accumulate printf arguments. *)and('b,'c)acc=|Acc_formatting_litof('b,'c)acc*formatting_lit(* Special fmtting (box) *)|Acc_formatting_genof('b,'c)acc*('b,'c)acc_formatting_gen(* Special fmtting (box) *)|Acc_string_literalof('b,'c)acc*string(* Literal string *)|Acc_char_literalof('b,'c)acc*char(* Literal char *)|Acc_data_stringof('b,'c)acc*string(* Generated string *)|Acc_data_charof('b,'c)acc*char(* Generated char *)|Acc_delayof('b,'c)acc*('b->'c)(* Delayed printing (%a, %t) *)|Acc_flushof('b,'c)acc(* Flush *)|Acc_invalid_argof('b,'c)acc*string(* Raise Invalid_argument msg *)|End_of_acc(* List of heterogeneous values. *)(* Used to accumulate scanf callback arguments. *)type('a,'b)heter_list=|Cons:'c*('a,'b)heter_list->('c->'a,'b)heter_list|Nil:('b,'b)heter_list(* Existential Black Boxes. *)(* Used to abstract some existential type parameters. *)(* GADT type associating a padding and an fmtty. *)(* See the type_padding function. *)type('a,'b,'c,'d,'e,'f)padding_fmtty_ebb=Padding_fmtty_EBB:('x,'y)padding*('y,'b,'c,'d,'e,'f)fmtty->('x,'b,'c,'d,'e,'f)padding_fmtty_ebb(* GADT type associating a padding, a precision and an fmtty. *)(* See the type_padprec function. *)type('a,'b,'c,'d,'e,'f)padprec_fmtty_ebb=Padprec_fmtty_EBB:('x,'y)padding*('y,'z)precision*('z,'b,'c,'d,'e,'f)fmtty->('x,'b,'c,'d,'e,'f)padprec_fmtty_ebb(* GADT type associating a padding and an fmt. *)(* See make_padding_fmt_ebb and parse_format functions. *)type('a,'b,'c,'e,'f)padding_fmt_ebb=Padding_fmt_EBB:(_,'x->'a)padding*('a,'b,'c,'d,'e,'f)fmt->('x,'b,'c,'e,'f)padding_fmt_ebb(* GADT type associating a precision and an fmt. *)(* See make_precision_fmt_ebb and parse_format functions. *)type('a,'b,'c,'e,'f)precision_fmt_ebb=Precision_fmt_EBB:(_,'x->'a)precision*('a,'b,'c,'d,'e,'f)fmt->('x,'b,'c,'e,'f)precision_fmt_ebb(* GADT type associating a padding, a precision and an fmt. *)(* See make_padprec_fmt_ebb and parse_format functions. *)type('p,'b,'c,'e,'f)padprec_fmt_ebb=Padprec_fmt_EBB:('x,'y)padding*('y,'p->'a)precision*('a,'b,'c,'d,'e,'f)fmt->('p,'b,'c,'e,'f)padprec_fmt_ebb(* Abstract the 'a and 'd parameters of an fmt. *)(* Output type of the format parsing function. *)type('b,'c,'e,'f)fmt_ebb=Fmt_EBB:('a,'b,'c,'d,'e,'f)fmt->('b,'c,'e,'f)fmt_ebb(* GADT type associating an fmtty and an fmt. *)(* See the type_format_gen function. *)type('a,'b,'c,'d,'e,'f)fmt_fmtty_ebb=Fmt_fmtty_EBB:('a,'b,'c,'d,'y,'x)fmt*('x,'b,'c,'y,'e,'f)fmtty->('a,'b,'c,'d,'e,'f)fmt_fmtty_ebb(* GADT type associating an fmtty and an fmt. *)(* See the type_ignored_format_substitution function. *)type('a,'b,'c,'d,'e,'f)fmtty_fmt_ebb=Fmtty_fmt_EBB:('a,'b,'c,'d,'y,'x)fmtty*('x,'b,'c,'y,'e,'f)fmt_fmtty_ebb->('a,'b,'c,'d,'e,'f)fmtty_fmt_ebb(* Abstract all fmtty type parameters. *)(* Used to compare format types. *)typefmtty_ebb=Fmtty_EBB:('a,'b,'c,'d,'e,'f)fmtty->fmtty_ebb(* Abstract all padding type parameters. *)(* Used to compare paddings. *)typepadding_ebb=Padding_EBB:('a,'b)padding->padding_ebb(* Abstract all precision type parameters. *)(* Used to compare precisions. *)typeprecision_ebb=Precision_EBB:('a,'b)precision->precision_ebb(******************************************************************************)(* Constants *)(* Default precision for float printing. *)letdefault_float_precisionfconv=matchsndfconvwith|Float_f|Float_e|Float_E|Float_g|Float_G|Float_h|Float_H->-6(* For %h and %H formats, a negative precision means "as many digits as
necessary". For the other FP formats, we take the absolute value
of the precision, hence 6 digits by default. *)|Float_F->12(* Default precision for OCaml float printing (%F). *)(******************************************************************************)(* Externals *)externalformat_float:string->float->string="caml_format_float"externalformat_int:string->int->string="caml_format_int"externalformat_int32:string->int32->string="caml_int32_format"externalformat_nativeint:string->nativeint->string="caml_nativeint_format"externalformat_int64:string->int64->string="caml_int64_format"externalhexstring_of_float:float->int->char->string="caml_hexstring_of_float"(******************************************************************************)(* Tools to pretty-print formats *)(* Type of extensible character buffers. *)typebuffer={mutableind:int;mutablebytes:bytes;}(* Create a fresh buffer. *)letbuffer_createinit_size={ind=0;bytes=Bytes.createinit_size}(* Check size of the buffer and grow it if needed. *)letbuffer_check_sizebufoverhead=letlen=Bytes.lengthbuf.bytesinletmin_len=buf.ind+overheadinifmin_len>lenthen(letnew_len=max(len*2)min_leninletnew_str=Bytes.createnew_leninBytes.blitbuf.bytes0new_str0len;buf.bytes<-new_str;)(* Add the character `c' to the buffer `buf'. *)letbuffer_add_charbufc=buffer_check_sizebuf1;Bytes.setbuf.bytesbuf.indc;buf.ind<-buf.ind+1(* Add the string `s' to the buffer `buf'. *)letbuffer_add_stringbufs=letstr_len=String.lengthsinbuffer_check_sizebufstr_len;String.blits0buf.bytesbuf.indstr_len;buf.ind<-buf.ind+str_len(* Get the content of the buffer. *)letbuffer_contentsbuf=Bytes.sub_stringbuf.bytes0buf.ind(***)(* Convert an integer conversion to char. *)letchar_of_iconviconv=matchiconvwith|Int_d|Int_pd|Int_sd|Int_Cd->'d'|Int_i|Int_pi|Int_si|Int_Ci->'i'|Int_x|Int_Cx->'x'|Int_X|Int_CX->'X'|Int_o|Int_Co->'o'|Int_u|Int_Cu->'u'(* Convert a float conversion to char. *)(* `cF' will be 'F' for displaying format and 'g' to call libc printf *)letchar_of_fconv?(cF='F')fconv=matchsndfconvwith|Float_f->'f'|Float_e->'e'|Float_E->'E'|Float_g->'g'|Float_G->'G'|Float_F->cF|Float_h->'h'|Float_H->'H'(* Convert a scanning counter to char. *)letchar_of_countercounter=matchcounterwith|Line_counter->'l'|Char_counter->'n'|Token_counter->'N'(***)(* Print a char_set in a buffer with the OCaml format lexical convention. *)letbprint_char_setbufchar_set=letrecprint_startset=letis_alonec=letbefore,after=Char.(chr(codec-1),chr(codec+1))inis_in_char_setsetc&¬(is_in_char_setsetbefore&&is_in_char_setsetafter)inifis_alone']'thenbuffer_add_charbuf']';print_outset1;ifis_alone'-'thenbuffer_add_charbuf'-';andprint_outseti=ifi<256thenifis_in_char_setset(char_of_inti)thenprint_firstsetielseprint_outset(i+1)andprint_firstseti=matchchar_of_intiwith|'\255'->print_charbuf255;|']'|'-'->print_outset(i+1);|_->print_secondset(i+1);andprint_secondseti=ifis_in_char_setset(char_of_inti)thenmatchchar_of_intiwith|'\255'->print_charbuf254;print_charbuf255;|']'|'-'whennot(is_in_char_setset(char_of_int(i+1)))->print_charbuf(i-1);print_outset(i+1);|_whennot(is_in_char_setset(char_of_int(i+1)))->print_charbuf(i-1);print_charbufi;print_outset(i+2);|_->print_inset(i-1)(i+2);else(print_charbuf(i-1);print_outset(i+1);)andprint_insetij=ifj=256||not(is_in_char_setset(char_of_intj))then(print_charbufi;print_charbuf(int_of_char'-');print_charbuf(j-1);ifj<256thenprint_outset(j+1);)elseprint_inseti(j+1);andprint_charbufi=matchchar_of_intiwith|'%'->buffer_add_charbuf'%';buffer_add_charbuf'%';|'@'->buffer_add_charbuf'%';buffer_add_charbuf'@';|c->buffer_add_charbufc;inbuffer_add_charbuf'[';print_start(ifis_in_char_setchar_set'\000'then(buffer_add_charbuf'^';rev_char_setchar_set)elsechar_set);buffer_add_charbuf']'(***)(* Print a padty in a buffer with the format-like syntax. *)letbprint_padtybufpadty=matchpadtywith|Left->buffer_add_charbuf'-'|Right->()|Zeros->buffer_add_charbuf'0'(* Print the '_' of an ignored flag if needed. *)letbprint_ignored_flagbufign_flag=ifign_flagthenbuffer_add_charbuf'_'(***)letbprint_pad_optbufpad_opt=matchpad_optwith|None->()|Somewidth->buffer_add_stringbuf(Int.to_stringwidth)(***)(* Print padding in a buffer with the format-like syntax. *)letbprint_padding:typeab.buffer->(a,b)padding->unit=funbufpad->matchpadwith|No_padding->()|Lit_padding(padty,n)->bprint_padtybufpadty;buffer_add_stringbuf(Int.to_stringn);|Arg_paddingpadty->bprint_padtybufpadty;buffer_add_charbuf'*'(* Print precision in a buffer with the format-like syntax. *)letbprint_precision:typeab.buffer->(a,b)precision->unit=funbufprec->matchprecwith|No_precision->()|Lit_precisionn->buffer_add_charbuf'.';buffer_add_stringbuf(Int.to_stringn);|Arg_precision->buffer_add_stringbuf".*"(***)(* Print the optional '+', ' ' or '#' associated to an int conversion. *)letbprint_iconv_flagbuficonv=matchiconvwith|Int_pd|Int_pi->buffer_add_charbuf'+'|Int_sd|Int_si->buffer_add_charbuf' '|Int_Cx|Int_CX|Int_Co|Int_Cd|Int_Ci|Int_Cu->buffer_add_charbuf'#'|Int_d|Int_i|Int_x|Int_X|Int_o|Int_u->()(* Print an complete int format in a buffer (ex: "%3.*d"). *)letbprint_int_fmtbufign_flagiconvpadprec=buffer_add_charbuf'%';bprint_ignored_flagbufign_flag;bprint_iconv_flagbuficonv;bprint_paddingbufpad;bprint_precisionbufprec;buffer_add_charbuf(char_of_iconviconv)(* Print a complete int32, nativeint or int64 format in a buffer. *)letbprint_altint_fmtbufign_flagiconvpadprecc=buffer_add_charbuf'%';bprint_ignored_flagbufign_flag;bprint_iconv_flagbuficonv;bprint_paddingbufpad;bprint_precisionbufprec;buffer_add_charbufc;buffer_add_charbuf(char_of_iconviconv)(***)(* Print the optional '+' associated to a float conversion. *)letbprint_fconv_flagbuffconv=matchfstfconvwith|Float_flag_p->buffer_add_charbuf'+'|Float_flag_s->buffer_add_charbuf' '|Float_flag_->()(* Print a complete float format in a buffer (ex: "%+*.3f"). *)letbprint_float_fmtbufign_flagfconvpadprec=buffer_add_charbuf'%';bprint_ignored_flagbufign_flag;bprint_fconv_flagbuffconv;bprint_paddingbufpad;bprint_precisionbufprec;buffer_add_charbuf(char_of_fconvfconv)(* Compute the literal string representation of a formatting_lit. *)(* Also used by Printf and Scanf where formatting is not interpreted. *)letstring_of_formatting_litformatting_lit=matchformatting_litwith|Close_box->"@]"|Close_tag->"@}"|Break(str,_,_)->str|FFlush->"@?"|Force_newline->"@\n"|Flush_newline->"@."|Magic_size(str,_)->str|Escaped_at->"@@"|Escaped_percent->"@%"|Scan_indicc->"@"^(String.make1c)(* Compute the literal string representation of a formatting. *)(* Also used by Printf and Scanf where formatting is not interpreted. *)letstring_of_formatting_gen:typeabcdef.(a,b,c,d,e,f)formatting_gen->string=funformatting_gen->matchformatting_genwith|Open_tag(Format(_,str))->str|Open_box(Format(_,str))->str(***)(* Print a literal char in a buffer, escape '%' by "%%". *)letbprint_char_literalbufchr=matchchrwith|'%'->buffer_add_stringbuf"%%"|_->buffer_add_charbufchr(* Print a literal string in a buffer, escape all '%' by "%%". *)letbprint_string_literalbufstr=fori=0toString.lengthstr-1dobprint_char_literalbufstr.[i]done(******************************************************************************)(* Format pretty-printing *)(* Print a complete format type (an fmtty) in a buffer. *)letrecbprint_fmtty:typeabcdefghijkl.buffer->(a,b,c,d,e,f,g,h,i,j,k,l)fmtty_rel->unit=funbuffmtty->matchfmttywith|Char_tyrest->buffer_add_stringbuf"%c";bprint_fmttybufrest;|String_tyrest->buffer_add_stringbuf"%s";bprint_fmttybufrest;|Int_tyrest->buffer_add_stringbuf"%i";bprint_fmttybufrest;|Int32_tyrest->buffer_add_stringbuf"%li";bprint_fmttybufrest;|Nativeint_tyrest->buffer_add_stringbuf"%ni";bprint_fmttybufrest;|Int64_tyrest->buffer_add_stringbuf"%Li";bprint_fmttybufrest;|Float_tyrest->buffer_add_stringbuf"%f";bprint_fmttybufrest;|Bool_tyrest->buffer_add_stringbuf"%B";bprint_fmttybufrest;|Alpha_tyrest->buffer_add_stringbuf"%a";bprint_fmttybufrest;|Theta_tyrest->buffer_add_stringbuf"%t";bprint_fmttybufrest;|Any_tyrest->buffer_add_stringbuf"%?";bprint_fmttybufrest;|Reader_tyrest->buffer_add_stringbuf"%r";bprint_fmttybufrest;|Ignored_reader_tyrest->buffer_add_stringbuf"%_r";bprint_fmttybufrest;|Format_arg_ty(sub_fmtty,rest)->buffer_add_stringbuf"%{";bprint_fmttybufsub_fmtty;buffer_add_stringbuf"%}";bprint_fmttybufrest;|Format_subst_ty(sub_fmtty,_,rest)->buffer_add_stringbuf"%(";bprint_fmttybufsub_fmtty;buffer_add_stringbuf"%)";bprint_fmttybufrest;|End_of_fmtty->()(***)letrecint_of_custom_arity:typeabc.(a,b,c)custom_arity->int=function|Custom_zero->0|Custom_succx->1+int_of_custom_arityx(* Print a complete format in a buffer. *)letbprint_fmtbuffmt=letrecfmtiter:typeabcdef.(a,b,c,d,e,f)fmt->bool->unit=funfmtign_flag->matchfmtwith|String(pad,rest)->buffer_add_charbuf'%';bprint_ignored_flagbufign_flag;bprint_paddingbufpad;buffer_add_charbuf's';fmtiterrestfalse;|Caml_string(pad,rest)->buffer_add_charbuf'%';bprint_ignored_flagbufign_flag;bprint_paddingbufpad;buffer_add_charbuf'S';fmtiterrestfalse;|Int(iconv,pad,prec,rest)->bprint_int_fmtbufign_flagiconvpadprec;fmtiterrestfalse;|Int32(iconv,pad,prec,rest)->bprint_altint_fmtbufign_flagiconvpadprec'l';fmtiterrestfalse;|Nativeint(iconv,pad,prec,rest)->bprint_altint_fmtbufign_flagiconvpadprec'n';fmtiterrestfalse;|Int64(iconv,pad,prec,rest)->bprint_altint_fmtbufign_flagiconvpadprec'L';fmtiterrestfalse;|Float(fconv,pad,prec,rest)->bprint_float_fmtbufign_flagfconvpadprec;fmtiterrestfalse;|Charrest->buffer_add_charbuf'%';bprint_ignored_flagbufign_flag;buffer_add_charbuf'c';fmtiterrestfalse;|Caml_charrest->buffer_add_charbuf'%';bprint_ignored_flagbufign_flag;buffer_add_charbuf'C';fmtiterrestfalse;|Bool(pad,rest)->buffer_add_charbuf'%';bprint_ignored_flagbufign_flag;bprint_paddingbufpad;buffer_add_charbuf'B';fmtiterrestfalse;|Alpharest->buffer_add_charbuf'%';bprint_ignored_flagbufign_flag;buffer_add_charbuf'a';fmtiterrestfalse;|Thetarest->buffer_add_charbuf'%';bprint_ignored_flagbufign_flag;buffer_add_charbuf't';fmtiterrestfalse;|Custom(arity,_,rest)->for_i=1toint_of_custom_arityaritydobuffer_add_charbuf'%';bprint_ignored_flagbufign_flag;buffer_add_charbuf'?';done;fmtiterrestfalse;|Readerrest->buffer_add_charbuf'%';bprint_ignored_flagbufign_flag;buffer_add_charbuf'r';fmtiterrestfalse;|Flushrest->buffer_add_stringbuf"%!";fmtiterrestign_flag;|String_literal(str,rest)->bprint_string_literalbufstr;fmtiterrestign_flag;|Char_literal(chr,rest)->bprint_char_literalbufchr;fmtiterrestign_flag;|Format_arg(pad_opt,fmtty,rest)->buffer_add_charbuf'%';bprint_ignored_flagbufign_flag;bprint_pad_optbufpad_opt;buffer_add_charbuf'{';bprint_fmttybuffmtty;buffer_add_charbuf'%';buffer_add_charbuf'}';fmtiterrestfalse;|Format_subst(pad_opt,fmtty,rest)->buffer_add_charbuf'%';bprint_ignored_flagbufign_flag;bprint_pad_optbufpad_opt;buffer_add_charbuf'(';bprint_fmttybuffmtty;buffer_add_charbuf'%';buffer_add_charbuf')';fmtiterrestfalse;|Scan_char_set(width_opt,char_set,rest)->buffer_add_charbuf'%';bprint_ignored_flagbufign_flag;bprint_pad_optbufwidth_opt;bprint_char_setbufchar_set;fmtiterrestfalse;|Scan_get_counter(counter,rest)->buffer_add_charbuf'%';bprint_ignored_flagbufign_flag;buffer_add_charbuf(char_of_countercounter);fmtiterrestfalse;|Scan_next_charrest->buffer_add_charbuf'%';bprint_ignored_flagbufign_flag;bprint_string_literalbuf"0c";fmtiterrestfalse;|Ignored_param(ign,rest)->letParam_format_EBBfmt'=param_format_of_ignored_formatignrestinfmtiterfmt'true;|Formatting_lit(fmting_lit,rest)->bprint_string_literalbuf(string_of_formatting_litfmting_lit);fmtiterrestign_flag;|Formatting_gen(fmting_gen,rest)->bprint_string_literalbuf"@{";bprint_string_literalbuf(string_of_formatting_genfmting_gen);fmtiterrestign_flag;|End_of_format->()infmtiterfmtfalse(***)(* Convert a format to string. *)letstring_of_fmtfmt=letbuf=buffer_create16inbprint_fmtbuffmt;buffer_contentsbuf(******************************************************************************)(* Type extraction *)type(_,_)eq=Refl:('a,'a)eq(* Invariant: this function is the identity on values.
In particular, if (ty1, ty2) have equal values, then
(trans (symm ty1) ty2) respects the 'trans' precondition. *)letrecsymm:typea1b1c1d1e1f1a2b2c2d2e2f2.(a1,b1,c1,d1,e1,f1,a2,b2,c2,d2,e2,f2)fmtty_rel->(a2,b2,c2,d2,e2,f2,a1,b1,c1,d1,e1,f1)fmtty_rel=function|Char_tyrest->Char_ty(symmrest)|Int_tyrest->Int_ty(symmrest)|Int32_tyrest->Int32_ty(symmrest)|Int64_tyrest->Int64_ty(symmrest)|Nativeint_tyrest->Nativeint_ty(symmrest)|Float_tyrest->Float_ty(symmrest)|Bool_tyrest->Bool_ty(symmrest)|String_tyrest->String_ty(symmrest)|Theta_tyrest->Theta_ty(symmrest)|Alpha_tyrest->Alpha_ty(symmrest)|Any_tyrest->Any_ty(symmrest)|Reader_tyrest->Reader_ty(symmrest)|Ignored_reader_tyrest->Ignored_reader_ty(symmrest)|Format_arg_ty(ty,rest)->Format_arg_ty(ty,symmrest)|Format_subst_ty(ty1,ty2,rest)->Format_subst_ty(ty2,ty1,symmrest)|End_of_fmtty->End_of_fmttyletrecfmtty_rel_det:typea1bcd1e1f1a2d2e2f2.(a1,b,c,d1,e1,f1,a2,b,c,d2,e2,f2)fmtty_rel->((f1,f2)eq->(a1,a2)eq)*((a1,a2)eq->(f1,f2)eq)*((e1,e2)eq->(d1,d2)eq)*((d1,d2)eq->(e1,e2)eq)=function|End_of_fmtty->(funRefl->Refl),(funRefl->Refl),(funRefl->Refl),(funRefl->Refl)|Char_tyrest->letfa,af,ed,de=fmtty_rel_detrestin(funRefl->letRefl=faReflinRefl),(funRefl->letRefl=afReflinRefl),ed,de|String_tyrest->letfa,af,ed,de=fmtty_rel_detrestin(funRefl->letRefl=faReflinRefl),(funRefl->letRefl=afReflinRefl),ed,de|Int_tyrest->letfa,af,ed,de=fmtty_rel_detrestin(funRefl->letRefl=faReflinRefl),(funRefl->letRefl=afReflinRefl),ed,de|Int32_tyrest->letfa,af,ed,de=fmtty_rel_detrestin(funRefl->letRefl=faReflinRefl),(funRefl->letRefl=afReflinRefl),ed,de|Int64_tyrest->letfa,af,ed,de=fmtty_rel_detrestin(funRefl->letRefl=faReflinRefl),(funRefl->letRefl=afReflinRefl),ed,de|Nativeint_tyrest->letfa,af,ed,de=fmtty_rel_detrestin(funRefl->letRefl=faReflinRefl),(funRefl->letRefl=afReflinRefl),ed,de|Float_tyrest->letfa,af,ed,de=fmtty_rel_detrestin(funRefl->letRefl=faReflinRefl),(funRefl->letRefl=afReflinRefl),ed,de|Bool_tyrest->letfa,af,ed,de=fmtty_rel_detrestin(funRefl->letRefl=faReflinRefl),(funRefl->letRefl=afReflinRefl),ed,de|Theta_tyrest->letfa,af,ed,de=fmtty_rel_detrestin(funRefl->letRefl=faReflinRefl),(funRefl->letRefl=afReflinRefl),ed,de|Alpha_tyrest->letfa,af,ed,de=fmtty_rel_detrestin(funRefl->letRefl=faReflinRefl),(funRefl->letRefl=afReflinRefl),ed,de|Any_tyrest->letfa,af,ed,de=fmtty_rel_detrestin(funRefl->letRefl=faReflinRefl),(funRefl->letRefl=afReflinRefl),ed,de|Reader_tyrest->letfa,af,ed,de=fmtty_rel_detrestin(funRefl->letRefl=faReflinRefl),(funRefl->letRefl=afReflinRefl),(funRefl->letRefl=edReflinRefl),(funRefl->letRefl=deReflinRefl)|Ignored_reader_tyrest->letfa,af,ed,de=fmtty_rel_detrestin(funRefl->letRefl=faReflinRefl),(funRefl->letRefl=afReflinRefl),(funRefl->letRefl=edReflinRefl),(funRefl->letRefl=deReflinRefl)|Format_arg_ty(_ty,rest)->letfa,af,ed,de=fmtty_rel_detrestin(funRefl->letRefl=faReflinRefl),(funRefl->letRefl=afReflinRefl),ed,de|Format_subst_ty(ty1,ty2,rest)->letfa,af,ed,de=fmtty_rel_detrestinletty=trans(symmty1)ty2inletag,ga,dj,jd=fmtty_rel_dettyin(funRefl->letRefl=faReflinletRefl=agReflinRefl),(funRefl->letRefl=gaReflinletRefl=afReflinRefl),(funRefl->letRefl=edReflinletRefl=djReflinRefl),(funRefl->letRefl=jdReflinletRefl=deReflinRefl)(* Precondition: we assume that the two fmtty_rel arguments have equal
values (at possibly distinct types); this invariant comes from the way
fmtty_rel witnesses are produced by the type-checker
The code below uses (assert false) when this assumption is broken. The
code pattern is the following:
| Foo x, Foo y ->
(* case where indeed both values
start with constructor Foo *)
| Foo _, _
| _, Foo _ ->
(* different head constructors: broken precondition *)
assert false
*)andtrans:typea1b1c1d1e1f1a2b2c2d2e2f2a3b3c3d3e3f3.(a1,b1,c1,d1,e1,f1,a2,b2,c2,d2,e2,f2)fmtty_rel->(a2,b2,c2,d2,e2,f2,a3,b3,c3,d3,e3,f3)fmtty_rel->(a1,b1,c1,d1,e1,f1,a3,b3,c3,d3,e3,f3)fmtty_rel=funty1ty2->matchty1,ty2with|Char_tyrest1,Char_tyrest2->Char_ty(transrest1rest2)|String_tyrest1,String_tyrest2->String_ty(transrest1rest2)|Bool_tyrest1,Bool_tyrest2->Bool_ty(transrest1rest2)|Int_tyrest1,Int_tyrest2->Int_ty(transrest1rest2)|Int32_tyrest1,Int32_tyrest2->Int32_ty(transrest1rest2)|Int64_tyrest1,Int64_tyrest2->Int64_ty(transrest1rest2)|Nativeint_tyrest1,Nativeint_tyrest2->Nativeint_ty(transrest1rest2)|Float_tyrest1,Float_tyrest2->Float_ty(transrest1rest2)|Alpha_tyrest1,Alpha_tyrest2->Alpha_ty(transrest1rest2)|Alpha_ty_,_->assertfalse|_,Alpha_ty_->assertfalse|Theta_tyrest1,Theta_tyrest2->Theta_ty(transrest1rest2)|Theta_ty_,_->assertfalse|_,Theta_ty_->assertfalse|Any_tyrest1,Any_tyrest2->Any_ty(transrest1rest2)|Any_ty_,_->assertfalse|_,Any_ty_->assertfalse|Reader_tyrest1,Reader_tyrest2->Reader_ty(transrest1rest2)|Reader_ty_,_->assertfalse|_,Reader_ty_->assertfalse|Ignored_reader_tyrest1,Ignored_reader_tyrest2->Ignored_reader_ty(transrest1rest2)|Ignored_reader_ty_,_->assertfalse|_,Ignored_reader_ty_->assertfalse|Format_arg_ty(ty1,rest1),Format_arg_ty(ty2,rest2)->Format_arg_ty(transty1ty2,transrest1rest2)|Format_arg_ty_,_->assertfalse|_,Format_arg_ty_->assertfalse|Format_subst_ty(ty11,ty12,rest1),Format_subst_ty(ty21,ty22,rest2)->letty=trans(symmty12)ty21inlet_,f2,_,f4=fmtty_rel_dettyinletRefl=f2ReflinletRefl=f4ReflinFormat_subst_ty(ty11,ty22,transrest1rest2)|Format_subst_ty_,_->assertfalse|_,Format_subst_ty_->assertfalse|End_of_fmtty,End_of_fmtty->End_of_fmtty|End_of_fmtty,_->assertfalse|_,End_of_fmtty->assertfalseletrecfmtty_of_formatting_gen:typeabcdef.(a,b,c,d,e,f)formatting_gen->(a,b,c,d,e,f)fmtty=funformatting_gen->matchformatting_genwith|Open_tag(Format(fmt,_))->fmtty_of_fmtfmt|Open_box(Format(fmt,_))->fmtty_of_fmtfmt(* Extract the type representation (an fmtty) of a format. *)andfmtty_of_fmt:typeabcdef.(a,b,c,d,e,f)fmt->(a,b,c,d,e,f)fmtty=funfmtty->matchfmttywith|String(pad,rest)->fmtty_of_padding_fmttypad(String_ty(fmtty_of_fmtrest))|Caml_string(pad,rest)->fmtty_of_padding_fmttypad(String_ty(fmtty_of_fmtrest))|Int(_,pad,prec,rest)->letty_rest=fmtty_of_fmtrestinletprec_ty=fmtty_of_precision_fmttyprec(Int_tyty_rest)infmtty_of_padding_fmttypadprec_ty|Int32(_,pad,prec,rest)->letty_rest=fmtty_of_fmtrestinletprec_ty=fmtty_of_precision_fmttyprec(Int32_tyty_rest)infmtty_of_padding_fmttypadprec_ty|Nativeint(_,pad,prec,rest)->letty_rest=fmtty_of_fmtrestinletprec_ty=fmtty_of_precision_fmttyprec(Nativeint_tyty_rest)infmtty_of_padding_fmttypadprec_ty|Int64(_,pad,prec,rest)->letty_rest=fmtty_of_fmtrestinletprec_ty=fmtty_of_precision_fmttyprec(Int64_tyty_rest)infmtty_of_padding_fmttypadprec_ty|Float(_,pad,prec,rest)->letty_rest=fmtty_of_fmtrestinletprec_ty=fmtty_of_precision_fmttyprec(Float_tyty_rest)infmtty_of_padding_fmttypadprec_ty|Charrest->Char_ty(fmtty_of_fmtrest)|Caml_charrest->Char_ty(fmtty_of_fmtrest)|Bool(pad,rest)->fmtty_of_padding_fmttypad(Bool_ty(fmtty_of_fmtrest))|Alpharest->Alpha_ty(fmtty_of_fmtrest)|Thetarest->Theta_ty(fmtty_of_fmtrest)|Custom(arity,_,rest)->fmtty_of_customarity(fmtty_of_fmtrest)|Readerrest->Reader_ty(fmtty_of_fmtrest)|Format_arg(_,ty,rest)->Format_arg_ty(ty,fmtty_of_fmtrest)|Format_subst(_,ty,rest)->Format_subst_ty(ty,ty,fmtty_of_fmtrest)|Flushrest->fmtty_of_fmtrest|String_literal(_,rest)->fmtty_of_fmtrest|Char_literal(_,rest)->fmtty_of_fmtrest|Scan_char_set(_,_,rest)->String_ty(fmtty_of_fmtrest)|Scan_get_counter(_,rest)->Int_ty(fmtty_of_fmtrest)|Scan_next_charrest->Char_ty(fmtty_of_fmtrest)|Ignored_param(ign,rest)->fmtty_of_ignored_formatignrest|Formatting_lit(_,rest)->fmtty_of_fmtrest|Formatting_gen(fmting_gen,rest)->concat_fmtty(fmtty_of_formatting_genfmting_gen)(fmtty_of_fmtrest)|End_of_format->End_of_fmttyandfmtty_of_custom:typexyabcdef.(a,x,y)custom_arity->(a,b,c,d,e,f)fmtty->(y,b,c,d,e,f)fmtty=funarityfmtty->matcharitywith|Custom_zero->fmtty|Custom_succarity->Any_ty(fmtty_of_customarityfmtty)(* Extract the fmtty of an ignored parameter followed by the rest of
the format. *)andfmtty_of_ignored_format:typexyabcdef.(a,b,c,d,y,x)ignored->(x,b,c,y,e,f)fmt->(a,b,c,d,e,f)fmtty=funignfmt->matchignwith|Ignored_char->fmtty_of_fmtfmt|Ignored_caml_char->fmtty_of_fmtfmt|Ignored_string_->fmtty_of_fmtfmt|Ignored_caml_string_->fmtty_of_fmtfmt|Ignored_int(_,_)->fmtty_of_fmtfmt|Ignored_int32(_,_)->fmtty_of_fmtfmt|Ignored_nativeint(_,_)->fmtty_of_fmtfmt|Ignored_int64(_,_)->fmtty_of_fmtfmt|Ignored_float(_,_)->fmtty_of_fmtfmt|Ignored_bool_->fmtty_of_fmtfmt|Ignored_format_arg_->fmtty_of_fmtfmt|Ignored_format_subst(_,fmtty)->concat_fmttyfmtty(fmtty_of_fmtfmt)|Ignored_reader->Ignored_reader_ty(fmtty_of_fmtfmt)|Ignored_scan_char_set_->fmtty_of_fmtfmt|Ignored_scan_get_counter_->fmtty_of_fmtfmt|Ignored_scan_next_char->fmtty_of_fmtfmt(* Add an Int_ty node if padding is taken as an extra argument (ex: "%*s"). *)andfmtty_of_padding_fmtty:typexabcdef.(x,a)padding->(a,b,c,d,e,f)fmtty->(x,b,c,d,e,f)fmtty=funpadfmtty->matchpadwith|No_padding->fmtty|Lit_padding_->fmtty|Arg_padding_->Int_tyfmtty(* Add an Int_ty node if precision is taken as an extra argument (ex: "%.*f").*)andfmtty_of_precision_fmtty:typexabcdef.(x,a)precision->(a,b,c,d,e,f)fmtty->(x,b,c,d,e,f)fmtty=funprecfmtty->matchprecwith|No_precision->fmtty|Lit_precision_->fmtty|Arg_precision->Int_tyfmtty(******************************************************************************)(* Format typing *)(* Exception raised when a format does not match a given format type. *)exceptionType_mismatch(* Type a padding. *)(* Take an Int_ty from the fmtty if the integer should be kept as argument. *)(* Raise Type_mismatch in case of type mismatch. *)lettype_padding:typeabcdefxy.(x,y)padding->(a,b,c,d,e,f)fmtty->(a,b,c,d,e,f)padding_fmtty_ebb=funpadfmtty->matchpad,fmttywith|No_padding,_->Padding_fmtty_EBB(No_padding,fmtty)|Lit_padding(padty,w),_->Padding_fmtty_EBB(Lit_padding(padty,w),fmtty)|Arg_paddingpadty,Int_tyrest->Padding_fmtty_EBB(Arg_paddingpadty,rest)|_->raiseType_mismatch(* Convert a (upadding, uprecision) to a (padding, precision). *)(* Take one or two Int_ty from the fmtty if needed. *)(* Raise Type_mismatch in case of type mismatch. *)lettype_padprec:typeabcdefxyz.(x,y)padding->(y,z)precision->(a,b,c,d,e,f)fmtty->(a,b,c,d,e,f)padprec_fmtty_ebb=funpadprecfmtty->matchprec,type_paddingpadfmttywith|No_precision,Padding_fmtty_EBB(pad,rest)->Padprec_fmtty_EBB(pad,No_precision,rest)|Lit_precisionp,Padding_fmtty_EBB(pad,rest)->Padprec_fmtty_EBB(pad,Lit_precisionp,rest)|Arg_precision,Padding_fmtty_EBB(pad,Int_tyrest)->Padprec_fmtty_EBB(pad,Arg_precision,rest)|_,Padding_fmtty_EBB(_,_)->raiseType_mismatch(* Type a format according to an fmtty. *)(* If typing succeed, generate a copy of the format with the same
type parameters as the fmtty. *)(* Raise [Failure] with an error message in case of type mismatch. *)letrectype_format:typea1b1c1d1e1f1a2b2c2d2e2f2.(a1,b1,c1,d1,e1,f1)fmt->(a2,b2,c2,d2,e2,f2)fmtty->(a2,b2,c2,d2,e2,f2)fmt=funfmtfmtty->matchtype_format_genfmtfmttywith|Fmt_fmtty_EBB(fmt',End_of_fmtty)->fmt'|_->raiseType_mismatchandtype_format_gen:typea1b1c1d1e1f1a2b2c2d2e2f2.(a1,b1,c1,d1,e1,f1)fmt->(a2,b2,c2,d2,e2,f2)fmtty->(a2,b2,c2,d2,e2,f2)fmt_fmtty_ebb=funfmtfmtty->matchfmt,fmttywith|Charfmt_rest,Char_tyfmtty_rest->letFmt_fmtty_EBB(fmt',fmtty')=type_format_genfmt_restfmtty_restinFmt_fmtty_EBB(Charfmt',fmtty')|Caml_charfmt_rest,Char_tyfmtty_rest->letFmt_fmtty_EBB(fmt',fmtty')=type_format_genfmt_restfmtty_restinFmt_fmtty_EBB(Caml_charfmt',fmtty')|String(pad,fmt_rest),_->(matchtype_paddingpadfmttywith|Padding_fmtty_EBB(pad,String_tyfmtty_rest)->letFmt_fmtty_EBB(fmt',fmtty')=type_format_genfmt_restfmtty_restinFmt_fmtty_EBB(String(pad,fmt'),fmtty')|Padding_fmtty_EBB(_,_)->raiseType_mismatch)|Caml_string(pad,fmt_rest),_->(matchtype_paddingpadfmttywith|Padding_fmtty_EBB(pad,String_tyfmtty_rest)->letFmt_fmtty_EBB(fmt',fmtty')=type_format_genfmt_restfmtty_restinFmt_fmtty_EBB(Caml_string(pad,fmt'),fmtty')|Padding_fmtty_EBB(_,_)->raiseType_mismatch)|Int(iconv,pad,prec,fmt_rest),_->(matchtype_padprecpadprecfmttywith|Padprec_fmtty_EBB(pad,prec,Int_tyfmtty_rest)->letFmt_fmtty_EBB(fmt',fmtty')=type_format_genfmt_restfmtty_restinFmt_fmtty_EBB(Int(iconv,pad,prec,fmt'),fmtty')|Padprec_fmtty_EBB(_,_,_)->raiseType_mismatch)|Int32(iconv,pad,prec,fmt_rest),_->(matchtype_padprecpadprecfmttywith|Padprec_fmtty_EBB(pad,prec,Int32_tyfmtty_rest)->letFmt_fmtty_EBB(fmt',fmtty')=type_format_genfmt_restfmtty_restinFmt_fmtty_EBB(Int32(iconv,pad,prec,fmt'),fmtty')|Padprec_fmtty_EBB(_,_,_)->raiseType_mismatch)|Nativeint(iconv,pad,prec,fmt_rest),_->(matchtype_padprecpadprecfmttywith|Padprec_fmtty_EBB(pad,prec,Nativeint_tyfmtty_rest)->letFmt_fmtty_EBB(fmt',fmtty')=type_format_genfmt_restfmtty_restinFmt_fmtty_EBB(Nativeint(iconv,pad,prec,fmt'),fmtty')|Padprec_fmtty_EBB(_,_,_)->raiseType_mismatch)|Int64(iconv,pad,prec,fmt_rest),_->(matchtype_padprecpadprecfmttywith|Padprec_fmtty_EBB(pad,prec,Int64_tyfmtty_rest)->letFmt_fmtty_EBB(fmt',fmtty')=type_format_genfmt_restfmtty_restinFmt_fmtty_EBB(Int64(iconv,pad,prec,fmt'),fmtty')|Padprec_fmtty_EBB(_,_,_)->raiseType_mismatch)|Float(fconv,pad,prec,fmt_rest),_->(matchtype_padprecpadprecfmttywith|Padprec_fmtty_EBB(pad,prec,Float_tyfmtty_rest)->letFmt_fmtty_EBB(fmt',fmtty')=type_format_genfmt_restfmtty_restinFmt_fmtty_EBB(Float(fconv,pad,prec,fmt'),fmtty')|Padprec_fmtty_EBB(_,_,_)->raiseType_mismatch)|Bool(pad,fmt_rest),_->(matchtype_paddingpadfmttywith|Padding_fmtty_EBB(pad,Bool_tyfmtty_rest)->letFmt_fmtty_EBB(fmt',fmtty')=type_format_genfmt_restfmtty_restinFmt_fmtty_EBB(Bool(pad,fmt'),fmtty')|Padding_fmtty_EBB(_,_)->raiseType_mismatch)|Flushfmt_rest,fmtty_rest->letFmt_fmtty_EBB(fmt',fmtty')=type_format_genfmt_restfmtty_restinFmt_fmtty_EBB(Flushfmt',fmtty')|String_literal(str,fmt_rest),fmtty_rest->letFmt_fmtty_EBB(fmt',fmtty')=type_format_genfmt_restfmtty_restinFmt_fmtty_EBB(String_literal(str,fmt'),fmtty')|Char_literal(chr,fmt_rest),fmtty_rest->letFmt_fmtty_EBB(fmt',fmtty')=type_format_genfmt_restfmtty_restinFmt_fmtty_EBB(Char_literal(chr,fmt'),fmtty')|Format_arg(pad_opt,sub_fmtty,fmt_rest),Format_arg_ty(sub_fmtty',fmtty_rest)->ifFmtty_EBBsub_fmtty<>Fmtty_EBBsub_fmtty'thenraiseType_mismatch;letFmt_fmtty_EBB(fmt',fmtty')=type_format_genfmt_restfmtty_restinFmt_fmtty_EBB(Format_arg(pad_opt,sub_fmtty',fmt'),fmtty')|Format_subst(pad_opt,sub_fmtty,fmt_rest),Format_subst_ty(sub_fmtty1,_sub_fmtty2,fmtty_rest)->ifFmtty_EBB(erase_relsub_fmtty)<>Fmtty_EBB(erase_relsub_fmtty1)thenraiseType_mismatch;letFmt_fmtty_EBB(fmt',fmtty')=type_format_genfmt_rest(erase_relfmtty_rest)inFmt_fmtty_EBB(Format_subst(pad_opt,sub_fmtty1,fmt'),fmtty')(* Printf and Format specific constructors: *)|Alphafmt_rest,Alpha_tyfmtty_rest->letFmt_fmtty_EBB(fmt',fmtty')=type_format_genfmt_restfmtty_restinFmt_fmtty_EBB(Alphafmt',fmtty')|Thetafmt_rest,Theta_tyfmtty_rest->letFmt_fmtty_EBB(fmt',fmtty')=type_format_genfmt_restfmtty_restinFmt_fmtty_EBB(Thetafmt',fmtty')(* Format specific constructors: *)|Formatting_lit(formatting_lit,fmt_rest),fmtty_rest->letFmt_fmtty_EBB(fmt',fmtty')=type_format_genfmt_restfmtty_restinFmt_fmtty_EBB(Formatting_lit(formatting_lit,fmt'),fmtty')|Formatting_gen(formatting_gen,fmt_rest),fmtty_rest->type_formatting_genformatting_genfmt_restfmtty_rest(* Scanf specific constructors: *)|Readerfmt_rest,Reader_tyfmtty_rest->letFmt_fmtty_EBB(fmt',fmtty')=type_format_genfmt_restfmtty_restinFmt_fmtty_EBB(Readerfmt',fmtty')|Scan_char_set(width_opt,char_set,fmt_rest),String_tyfmtty_rest->letFmt_fmtty_EBB(fmt',fmtty')=type_format_genfmt_restfmtty_restinFmt_fmtty_EBB(Scan_char_set(width_opt,char_set,fmt'),fmtty')|Scan_get_counter(counter,fmt_rest),Int_tyfmtty_rest->letFmt_fmtty_EBB(fmt',fmtty')=type_format_genfmt_restfmtty_restinFmt_fmtty_EBB(Scan_get_counter(counter,fmt'),fmtty')|Ignored_param(ign,rest),fmtty_rest->type_ignored_paramignrestfmtty_rest|End_of_format,fmtty_rest->Fmt_fmtty_EBB(End_of_format,fmtty_rest)|_->raiseType_mismatchandtype_formatting_gen:typea1a3b1b3c1c3d1d3e1e2e3f1f2f3.(a1,b1,c1,d1,e1,f1)formatting_gen->(f1,b1,c1,e1,e2,f2)fmt->(a3,b3,c3,d3,e3,f3)fmtty->(a3,b3,c3,d3,e3,f3)fmt_fmtty_ebb=funformatting_genfmt0fmtty0->matchformatting_genwith|Open_tag(Format(fmt1,str))->letFmt_fmtty_EBB(fmt2,fmtty2)=type_format_genfmt1fmtty0inletFmt_fmtty_EBB(fmt3,fmtty3)=type_format_genfmt0fmtty2inFmt_fmtty_EBB(Formatting_gen(Open_tag(Format(fmt2,str)),fmt3),fmtty3)|Open_box(Format(fmt1,str))->letFmt_fmtty_EBB(fmt2,fmtty2)=type_format_genfmt1fmtty0inletFmt_fmtty_EBB(fmt3,fmtty3)=type_format_genfmt0fmtty2inFmt_fmtty_EBB(Formatting_gen(Open_box(Format(fmt2,str)),fmt3),fmtty3)(* Type an Ignored_param node according to an fmtty. *)andtype_ignored_param:typepqxyztuvabcdef.(x,y,z,t,q,p)ignored->(p,y,z,q,u,v)fmt->(a,b,c,d,e,f)fmtty->(a,b,c,d,e,f)fmt_fmtty_ebb=funignfmtfmtty->matchignwith|Ignored_charasign'->type_ignored_param_oneign'fmtfmtty|Ignored_caml_charasign'->type_ignored_param_oneign'fmtfmtty|Ignored_string_asign'->type_ignored_param_oneign'fmtfmtty|Ignored_caml_string_asign'->type_ignored_param_oneign'fmtfmtty|Ignored_int_asign'->type_ignored_param_oneign'fmtfmtty|Ignored_int32_asign'->type_ignored_param_oneign'fmtfmtty|Ignored_nativeint_asign'->type_ignored_param_oneign'fmtfmtty|Ignored_int64_asign'->type_ignored_param_oneign'fmtfmtty|Ignored_float_asign'->type_ignored_param_oneign'fmtfmtty|Ignored_bool_asign'->type_ignored_param_oneign'fmtfmtty|Ignored_scan_char_set_asign'->type_ignored_param_oneign'fmtfmtty|Ignored_scan_get_counter_asign'->type_ignored_param_oneign'fmtfmtty|Ignored_scan_next_charasign'->type_ignored_param_oneign'fmtfmtty|Ignored_format_arg(pad_opt,sub_fmtty)->type_ignored_param_one(Ignored_format_arg(pad_opt,sub_fmtty))fmtfmtty|Ignored_format_subst(pad_opt,sub_fmtty)->letFmtty_fmt_EBB(sub_fmtty',Fmt_fmtty_EBB(fmt',fmtty'))=type_ignored_format_substitutionsub_fmttyfmtfmttyinFmt_fmtty_EBB(Ignored_param(Ignored_format_subst(pad_opt,sub_fmtty'),fmt'),fmtty')|Ignored_reader->(matchfmttywith|Ignored_reader_tyfmtty_rest->letFmt_fmtty_EBB(fmt',fmtty')=type_format_genfmtfmtty_restinFmt_fmtty_EBB(Ignored_param(Ignored_reader,fmt'),fmtty')|_->raiseType_mismatch)andtype_ignored_param_one:typea1a2b1b2c1c2d1d2e1e2f1f2.(a2,b2,c2,d2,d2,a2)ignored->(a1,b1,c1,d1,e1,f1)fmt->(a2,b2,c2,d2,e2,f2)fmtty->(a2,b2,c2,d2,e2,f2)fmt_fmtty_ebb=funignfmtfmtty->letFmt_fmtty_EBB(fmt',fmtty')=type_format_genfmtfmttyinFmt_fmtty_EBB(Ignored_param(ign,fmt'),fmtty')(* Typing of the complex case: "%_(...%)". *)andtype_ignored_format_substitution:typewxyzpstuabcdef.(w,x,y,z,s,p)fmtty->(p,x,y,s,t,u)fmt->(a,b,c,d,e,f)fmtty->(a,b,c,d,e,f)fmtty_fmt_ebb=funsub_fmttyfmtfmtty->matchsub_fmtty,fmttywith|Char_tysub_fmtty_rest,Char_tyfmtty_rest->letFmtty_fmt_EBB(sub_fmtty_rest',fmt')=type_ignored_format_substitutionsub_fmtty_restfmtfmtty_restinFmtty_fmt_EBB(Char_tysub_fmtty_rest',fmt')|String_tysub_fmtty_rest,String_tyfmtty_rest->letFmtty_fmt_EBB(sub_fmtty_rest',fmt')=type_ignored_format_substitutionsub_fmtty_restfmtfmtty_restinFmtty_fmt_EBB(String_tysub_fmtty_rest',fmt')|Int_tysub_fmtty_rest,Int_tyfmtty_rest->letFmtty_fmt_EBB(sub_fmtty_rest',fmt')=type_ignored_format_substitutionsub_fmtty_restfmtfmtty_restinFmtty_fmt_EBB(Int_tysub_fmtty_rest',fmt')|Int32_tysub_fmtty_rest,Int32_tyfmtty_rest->letFmtty_fmt_EBB(sub_fmtty_rest',fmt')=type_ignored_format_substitutionsub_fmtty_restfmtfmtty_restinFmtty_fmt_EBB(Int32_tysub_fmtty_rest',fmt')|Nativeint_tysub_fmtty_rest,Nativeint_tyfmtty_rest->letFmtty_fmt_EBB(sub_fmtty_rest',fmt')=type_ignored_format_substitutionsub_fmtty_restfmtfmtty_restinFmtty_fmt_EBB(Nativeint_tysub_fmtty_rest',fmt')|Int64_tysub_fmtty_rest,Int64_tyfmtty_rest->letFmtty_fmt_EBB(sub_fmtty_rest',fmt')=type_ignored_format_substitutionsub_fmtty_restfmtfmtty_restinFmtty_fmt_EBB(Int64_tysub_fmtty_rest',fmt')|Float_tysub_fmtty_rest,Float_tyfmtty_rest->letFmtty_fmt_EBB(sub_fmtty_rest',fmt')=type_ignored_format_substitutionsub_fmtty_restfmtfmtty_restinFmtty_fmt_EBB(Float_tysub_fmtty_rest',fmt')|Bool_tysub_fmtty_rest,Bool_tyfmtty_rest->letFmtty_fmt_EBB(sub_fmtty_rest',fmt')=type_ignored_format_substitutionsub_fmtty_restfmtfmtty_restinFmtty_fmt_EBB(Bool_tysub_fmtty_rest',fmt')|Alpha_tysub_fmtty_rest,Alpha_tyfmtty_rest->letFmtty_fmt_EBB(sub_fmtty_rest',fmt')=type_ignored_format_substitutionsub_fmtty_restfmtfmtty_restinFmtty_fmt_EBB(Alpha_tysub_fmtty_rest',fmt')|Theta_tysub_fmtty_rest,Theta_tyfmtty_rest->letFmtty_fmt_EBB(sub_fmtty_rest',fmt')=type_ignored_format_substitutionsub_fmtty_restfmtfmtty_restinFmtty_fmt_EBB(Theta_tysub_fmtty_rest',fmt')|Reader_tysub_fmtty_rest,Reader_tyfmtty_rest->letFmtty_fmt_EBB(sub_fmtty_rest',fmt')=type_ignored_format_substitutionsub_fmtty_restfmtfmtty_restinFmtty_fmt_EBB(Reader_tysub_fmtty_rest',fmt')|Ignored_reader_tysub_fmtty_rest,Ignored_reader_tyfmtty_rest->letFmtty_fmt_EBB(sub_fmtty_rest',fmt')=type_ignored_format_substitutionsub_fmtty_restfmtfmtty_restinFmtty_fmt_EBB(Ignored_reader_tysub_fmtty_rest',fmt')|Format_arg_ty(sub2_fmtty,sub_fmtty_rest),Format_arg_ty(sub2_fmtty',fmtty_rest)->ifFmtty_EBBsub2_fmtty<>Fmtty_EBBsub2_fmtty'thenraiseType_mismatch;letFmtty_fmt_EBB(sub_fmtty_rest',fmt')=type_ignored_format_substitutionsub_fmtty_restfmtfmtty_restinFmtty_fmt_EBB(Format_arg_ty(sub2_fmtty',sub_fmtty_rest'),fmt')|Format_subst_ty(sub1_fmtty,sub2_fmtty,sub_fmtty_rest),Format_subst_ty(sub1_fmtty',sub2_fmtty',fmtty_rest)->(* TODO define Fmtty_rel_EBB to remove those erase_rel *)ifFmtty_EBB(erase_relsub1_fmtty)<>Fmtty_EBB(erase_relsub1_fmtty')thenraiseType_mismatch;ifFmtty_EBB(erase_relsub2_fmtty)<>Fmtty_EBB(erase_relsub2_fmtty')thenraiseType_mismatch;letsub_fmtty'=trans(symmsub1_fmtty')sub2_fmtty'inlet_,f2,_,f4=fmtty_rel_detsub_fmtty'inletRefl=f2ReflinletRefl=f4ReflinletFmtty_fmt_EBB(sub_fmtty_rest',fmt')=type_ignored_format_substitution(erase_relsub_fmtty_rest)fmtfmtty_restinFmtty_fmt_EBB(Format_subst_ty(sub1_fmtty',sub2_fmtty',symmsub_fmtty_rest'),fmt')|End_of_fmtty,fmtty->Fmtty_fmt_EBB(End_of_fmtty,type_format_genfmtfmtty)|_->raiseType_mismatch(* This implementation of `recast` is a bit disappointing. The
invariant provided by the type are very strong: the input format's
type is in relation to the output type's as witnessed by the
fmtty_rel argument. One would at first expect this function to be
total, and implementable by exhaustive pattern matching. Instead,
we reuse the highly partial and much less well-defined function
`type_format` that has lost all knowledge of the correspondence
between the argument's types.
Besides the fact that this function reuses a lot of the
`type_format` logic (eg.: seeing Int_ty in the fmtty parameter does
not let you match on Int only, as you may in fact have Float
(Arg_padding, ...) ("%.*d") beginning with an Int_ty), it is also
a partial function, because the typing information in a format is
not quite enough to reconstruct it unambiguously. For example, the
format types of "%d%_r" and "%_r%d" have the same format6
parameters, but they are not at all exchangeable, and putting one
in place of the other must result in a dynamic failure.
Given that:
- we'd have to duplicate a lot of non-trivial typing logic from type_format
- this wouldn't even eliminate (all) the dynamic failures
we decided to just reuse type_format directly for now.
*)letrecast:typea1b1c1d1e1f1a2b2c2d2e2f2.(a1,b1,c1,d1,e1,f1)fmt->(a1,b1,c1,d1,e1,f1,a2,b2,c2,d2,e2,f2)fmtty_rel->(a2,b2,c2,d2,e2,f2)fmt=funfmtfmtty->type_formatfmt(erase_rel(symmfmtty))(******************************************************************************)(* Printing tools *)(* Add padding spaces around a string. *)letfix_paddingpadtywidthstr=letlen=String.lengthstrinletwidth,padty=abswidth,(* while literal padding widths are always non-negative,
dynamically-set widths (Arg_padding, eg. %*d) may be negative;
we interpret those as specifying a padding-to-the-left; this
means that '0' may get dropped even if it was explicitly set,
but:
- this is what the legacy implementation does, and
we preserve compatibility if possible
- we could only signal this issue by failing at runtime,
which is not very nice... *)ifwidth<0thenLeftelsepadtyinifwidth<=lenthenstrelseletres=Bytes.makewidth(ifpadty=Zerosthen'0'else' ')inbeginmatchpadtywith|Left->String.blitstr0res0len|Right->String.blitstr0res(width-len)len|Zeroswhenlen>0&&(str.[0]='+'||str.[0]='-'||str.[0]=' ')->Bytes.setres0str.[0];String.blitstr1res(width-len+1)(len-1)|Zeroswhenlen>1&&str.[0]='0'&&(str.[1]='x'||str.[1]='X')->Bytes.setres1str.[1];String.blitstr2res(width-len+2)(len-2)|Zeros->String.blitstr0res(width-len)lenend;Bytes.unsafe_to_stringres(* Add '0' padding to int, int32, nativeint or int64 string representation. *)letfix_int_precisionprecstr=letprec=absprecinletlen=String.lengthstrinmatchstr.[0]with|('+'|'-'|' ')ascwhenprec+1>len->letres=Bytes.make(prec+1)'0'inBytes.setres0c;String.blitstr1res(prec-len+2)(len-1);Bytes.unsafe_to_stringres|'0'whenprec+2>len&&len>1&&(str.[1]='x'||str.[1]='X')->letres=Bytes.make(prec+2)'0'inBytes.setres1str.[1];String.blitstr2res(prec-len+4)(len-2);Bytes.unsafe_to_stringres|'0'..'9'|'a'..'f'|'A'..'F'whenprec>len->letres=Bytes.makeprec'0'inString.blitstr0res(prec-len)len;Bytes.unsafe_to_stringres|_->str(* Escape a string according to the OCaml lexing convention. *)letstring_to_caml_stringstr=letstr=String.escapedstrinletl=String.lengthstrinletres=Bytes.make(l+2)'\"'inString.unsafe_blitstr0res1l;Bytes.unsafe_to_stringres(* Generate the format_int/int32/nativeint/int64 first argument
from an int_conv. *)letformat_of_iconv=function|Int_d|Int_Cd->"%d"|Int_pd->"%+d"|Int_sd->"% d"|Int_i|Int_Ci->"%i"|Int_pi->"%+i"|Int_si->"% i"|Int_x->"%x"|Int_Cx->"%#x"|Int_X->"%X"|Int_CX->"%#X"|Int_o->"%o"|Int_Co->"%#o"|Int_u|Int_Cu->"%u"letformat_of_iconvL=function|Int_d|Int_Cd->"%Ld"|Int_pd->"%+Ld"|Int_sd->"% Ld"|Int_i|Int_Ci->"%Li"|Int_pi->"%+Li"|Int_si->"% Li"|Int_x->"%Lx"|Int_Cx->"%#Lx"|Int_X->"%LX"|Int_CX->"%#LX"|Int_o->"%Lo"|Int_Co->"%#Lo"|Int_u|Int_Cu->"%Lu"letformat_of_iconvl=function|Int_d|Int_Cd->"%ld"|Int_pd->"%+ld"|Int_sd->"% ld"|Int_i|Int_Ci->"%li"|Int_pi->"%+li"|Int_si->"% li"|Int_x->"%lx"|Int_Cx->"%#lx"|Int_X->"%lX"|Int_CX->"%#lX"|Int_o->"%lo"|Int_Co->"%#lo"|Int_u|Int_Cu->"%lu"letformat_of_iconvn=function|Int_d|Int_Cd->"%nd"|Int_pd->"%+nd"|Int_sd->"% nd"|Int_i|Int_Ci->"%ni"|Int_pi->"%+ni"|Int_si->"% ni"|Int_x->"%nx"|Int_Cx->"%#nx"|Int_X->"%nX"|Int_CX->"%#nX"|Int_o->"%no"|Int_Co->"%#no"|Int_u|Int_Cu->"%nu"(* Generate the format_float first argument from a float_conv. *)letformat_of_fconvfconvprec=letprec=absprecinletsymb=char_of_fconv~cF:'g'fconvinletbuf=buffer_create16inbuffer_add_charbuf'%';bprint_fconv_flagbuffconv;buffer_add_charbuf'.';buffer_add_stringbuf(Int.to_stringprec);buffer_add_charbufsymb;buffer_contentsbuflettransform_int_alticonvs=matchiconvwith|Int_Cd|Int_Ci|Int_Cu->letdigits=letn=ref0infori=0toString.lengths-1domatchString.unsafe_getsiwith|'0'..'9'->incrn|_->()done;!ninletbuf=Bytes.create(String.lengths+(digits-1)/3)inletpos=ref0inletputc=Bytes.setbuf!posc;incrposinletleft=ref((digits-1)mod3+1)infori=0toString.lengths-1domatchString.unsafe_getsiwith|'0'..'9'asc->if!left=0then(put'_';left:=3);decrleft;putc|c->putcdone;Bytes.unsafe_to_stringbuf|_->s(* Convert an integer to a string according to a conversion. *)letconvert_inticonvn=transform_int_alticonv(format_int(format_of_iconviconv)n)letconvert_int32iconvn=transform_int_alticonv(format_int32(format_of_iconvliconv)n)letconvert_nativeinticonvn=transform_int_alticonv(format_nativeint(format_of_iconvniconv)n)letconvert_int64iconvn=transform_int_alticonv(format_int64(format_of_iconvLiconv)n)(* Convert a float to string. *)(* Fix special case of "OCaml float format". *)letconvert_floatfconvprecx=matchsndfconvwith|Float_h|Float_H->letsign=matchfstfconvwith|Float_flag_p->'+'|Float_flag_s->' '|_->'-'inletstr=hexstring_of_floatxprecsigninbeginmatchsndfconvwith|Float_H->String.uppercase_asciistr|_->strend|_->letstr=format_float(format_of_fconvfconvprec)xinifsndfconv<>Float_Fthenstrelseletlen=String.lengthstrinletrecis_validi=ifi=lenthenfalseelsematchstr.[i]with|'.'|'e'|'E'->true|_->is_valid(i+1)inmatchclassify_floatxwith|FP_normal|FP_subnormal|FP_zero->ifis_valid0thenstrelsestr^"."|FP_infinite->ifx<0.0then"neg_infinity"else"infinity"|FP_nan->"nan"(* Convert a char to a string according to the OCaml lexical convention. *)letformat_caml_charc=letstr=Char.escapedcinletl=String.lengthstrinletres=Bytes.make(l+2)'\''inString.unsafe_blitstr0res1l;Bytes.unsafe_to_stringres(* Convert a format type to string *)letstring_of_fmttyfmtty=letbuf=buffer_create16inbprint_fmttybuffmtty;buffer_contentsbuf(******************************************************************************)(* Generic printing function *)(* Make a generic printing function. *)(* Used to generate Printf and Format printing functions. *)(* Parameters:
k: a continuation finally applied to the output stream and the accumulator.
o: the output stream (see k, %a and %t).
acc: rev list of printing entities (string, char, flush, formatting, ...).
fmt: the format. *)letrecmake_printf:typeabcdef.((b,c)acc->f)->(b,c)acc->(a,b,c,d,e,f)fmt->a=funkaccfmt->matchfmtwith|Charrest->func->letnew_acc=Acc_data_char(acc,c)inmake_printfknew_accrest|Caml_charrest->func->letnew_acc=Acc_data_string(acc,format_caml_charc)inmake_printfknew_accrest|String(pad,rest)->make_paddingkaccrestpad(funstr->str)|Caml_string(pad,rest)->make_paddingkaccrestpadstring_to_caml_string|Int(iconv,pad,prec,rest)->make_int_padding_precisionkaccrestpadprecconvert_inticonv|Int32(iconv,pad,prec,rest)->make_int_padding_precisionkaccrestpadprecconvert_int32iconv|Nativeint(iconv,pad,prec,rest)->make_int_padding_precisionkaccrestpadprecconvert_nativeinticonv|Int64(iconv,pad,prec,rest)->make_int_padding_precisionkaccrestpadprecconvert_int64iconv|Float(fconv,pad,prec,rest)->make_float_padding_precisionkaccrestpadprecfconv|Bool(pad,rest)->make_paddingkaccrestpadstring_of_bool|Alpharest->funfx->make_printfk(Acc_delay(acc,funo->fox))rest|Thetarest->funf->make_printfk(Acc_delay(acc,f))rest|Custom(arity,f,rest)->make_customkaccrestarity(f())|Reader_->(* This case is impossible, by typing of formats. *)(* Indeed, since printf and co. take a format4 as argument, the 'd and 'e
type parameters of fmt are obviously equals. The Reader is the
only constructor which touch 'd and 'e type parameters of the format
type, it adds an (->) to the 'd parameters. Consequently, a format4
cannot contain a Reader node, except in the sub-format associated to
an %{...%}. It's not a problem because make_printf do not call
itself recursively on the sub-format associated to %{...%}. *)assertfalse|Flushrest->make_printfk(Acc_flushacc)rest|String_literal(str,rest)->make_printfk(Acc_string_literal(acc,str))rest|Char_literal(chr,rest)->make_printfk(Acc_char_literal(acc,chr))rest|Format_arg(_,sub_fmtty,rest)->letty=string_of_fmttysub_fmttyin(funstr->ignorestr;make_printfk(Acc_data_string(acc,ty))rest)|Format_subst(_,fmtty,rest)->fun(Format(fmt,_))->make_printfkacc(concat_fmt(recastfmtfmtty)rest)|Scan_char_set(_,_,rest)->letnew_acc=Acc_invalid_arg(acc,"Printf: bad conversion %[")infun_->make_printfknew_accrest|Scan_get_counter(_,rest)->(* This case should be refused for Printf. *)(* Accepted for backward compatibility. *)(* Interpret %l, %n and %L as %u. *)funn->letnew_acc=Acc_data_string(acc,format_int"%u"n)inmake_printfknew_accrest|Scan_next_charrest->func->letnew_acc=Acc_data_char(acc,c)inmake_printfknew_accrest|Ignored_param(ign,rest)->make_ignored_paramkaccignrest|Formatting_lit(fmting_lit,rest)->make_printfk(Acc_formatting_lit(acc,fmting_lit))rest|Formatting_gen(Open_tag(Format(fmt',_)),rest)->letk'kacc=make_printfk(Acc_formatting_gen(acc,Acc_open_tagkacc))restinmake_printfk'End_of_accfmt'|Formatting_gen(Open_box(Format(fmt',_)),rest)->letk'kacc=make_printfk(Acc_formatting_gen(acc,Acc_open_boxkacc))restinmake_printfk'End_of_accfmt'|End_of_format->kacc(* Delay the error (Invalid_argument "Printf: bad conversion %_"). *)(* Generate functions to take remaining arguments (after the "%_"). *)andmake_ignored_param:typexyabcdef.((b,c)acc->f)->(b,c)acc->(a,b,c,d,y,x)ignored->(x,b,c,y,e,f)fmt->a=funkaccignfmt->matchignwith|Ignored_char->make_invalid_argkaccfmt|Ignored_caml_char->make_invalid_argkaccfmt|Ignored_string_->make_invalid_argkaccfmt|Ignored_caml_string_->make_invalid_argkaccfmt|Ignored_int(_,_)->make_invalid_argkaccfmt|Ignored_int32(_,_)->make_invalid_argkaccfmt|Ignored_nativeint(_,_)->make_invalid_argkaccfmt|Ignored_int64(_,_)->make_invalid_argkaccfmt|Ignored_float(_,_)->make_invalid_argkaccfmt|Ignored_bool_->make_invalid_argkaccfmt|Ignored_format_arg_->make_invalid_argkaccfmt|Ignored_format_subst(_,fmtty)->make_from_fmttykaccfmttyfmt|Ignored_reader->assertfalse|Ignored_scan_char_set_->make_invalid_argkaccfmt|Ignored_scan_get_counter_->make_invalid_argkaccfmt|Ignored_scan_next_char->make_invalid_argkaccfmt(* Special case of printf "%_(". *)andmake_from_fmtty:typexyabcdef.((b,c)acc->f)->(b,c)acc->(a,b,c,d,y,x)fmtty->(x,b,c,y,e,f)fmt->a=funkaccfmttyfmt->matchfmttywith|Char_tyrest->fun_->make_from_fmttykaccrestfmt|String_tyrest->fun_->make_from_fmttykaccrestfmt|Int_tyrest->fun_->make_from_fmttykaccrestfmt|Int32_tyrest->fun_->make_from_fmttykaccrestfmt|Nativeint_tyrest->fun_->make_from_fmttykaccrestfmt|Int64_tyrest->fun_->make_from_fmttykaccrestfmt|Float_tyrest->fun_->make_from_fmttykaccrestfmt|Bool_tyrest->fun_->make_from_fmttykaccrestfmt|Alpha_tyrest->fun__->make_from_fmttykaccrestfmt|Theta_tyrest->fun_->make_from_fmttykaccrestfmt|Any_tyrest->fun_->make_from_fmttykaccrestfmt|Reader_ty_->assertfalse|Ignored_reader_ty_->assertfalse|Format_arg_ty(_,rest)->fun_->make_from_fmttykaccrestfmt|End_of_fmtty->make_invalid_argkaccfmt|Format_subst_ty(ty1,ty2,rest)->letty=trans(symmty1)ty2infun_->make_from_fmttykacc(concat_fmttytyrest)fmt(* Insert an Acc_invalid_arg in the accumulator and continue to generate
closures to get the remaining arguments. *)andmake_invalid_arg:typeabcdef.((b,c)acc->f)->(b,c)acc->(a,b,c,d,e,f)fmt->a=funkaccfmt->make_printfk(Acc_invalid_arg(acc,"Printf: bad conversion %_"))fmt(* Fix padding, take it as an extra integer argument if needed. *)andmake_padding:typexzabcdef.((b,c)acc->f)->(b,c)acc->(a,b,c,d,e,f)fmt->(x,z->a)padding->(z->string)->x=funkaccfmtpadtrans->matchpadwith|No_padding->funx->letnew_acc=Acc_data_string(acc,transx)inmake_printfknew_accfmt|Lit_padding(padty,width)->funx->letnew_acc=Acc_data_string(acc,fix_paddingpadtywidth(transx))inmake_printfknew_accfmt|Arg_paddingpadty->funwx->letnew_acc=Acc_data_string(acc,fix_paddingpadtyw(transx))inmake_printfknew_accfmt(* Fix padding and precision for int, int32, nativeint or int64. *)(* Take one or two extra integer arguments if needed. *)andmake_int_padding_precision:typexyzabcdef.((b,c)acc->f)->(b,c)acc->(a,b,c,d,e,f)fmt->(x,y)padding->(y,z->a)precision->(int_conv->z->string)->int_conv->x=funkaccfmtpadprectransiconv->matchpad,precwith|No_padding,No_precision->funx->letstr=transiconvxinmake_printfk(Acc_data_string(acc,str))fmt|No_padding,Lit_precisionp->funx->letstr=fix_int_precisionp(transiconvx)inmake_printfk(Acc_data_string(acc,str))fmt|No_padding,Arg_precision->funpx->letstr=fix_int_precisionp(transiconvx)inmake_printfk(Acc_data_string(acc,str))fmt|Lit_padding(padty,w),No_precision->funx->letstr=fix_paddingpadtyw(transiconvx)inmake_printfk(Acc_data_string(acc,str))fmt|Lit_padding(padty,w),Lit_precisionp->funx->letstr=fix_paddingpadtyw(fix_int_precisionp(transiconvx))inmake_printfk(Acc_data_string(acc,str))fmt|Lit_padding(padty,w),Arg_precision->funpx->letstr=fix_paddingpadtyw(fix_int_precisionp(transiconvx))inmake_printfk(Acc_data_string(acc,str))fmt|Arg_paddingpadty,No_precision->funwx->letstr=fix_paddingpadtyw(transiconvx)inmake_printfk(Acc_data_string(acc,str))fmt|Arg_paddingpadty,Lit_precisionp->funwx->letstr=fix_paddingpadtyw(fix_int_precisionp(transiconvx))inmake_printfk(Acc_data_string(acc,str))fmt|Arg_paddingpadty,Arg_precision->funwpx->letstr=fix_paddingpadtyw(fix_int_precisionp(transiconvx))inmake_printfk(Acc_data_string(acc,str))fmt(* Convert a float, fix padding and precision if needed. *)(* Take the float argument and one or two extra integer arguments if needed. *)andmake_float_padding_precision:typexyabcdef.((b,c)acc->f)->(b,c)acc->(a,b,c,d,e,f)fmt->(x,y)padding->(y,float->a)precision->float_conv->x=funkaccfmtpadprecfconv->matchpad,precwith|No_padding,No_precision->funx->letstr=convert_floatfconv(default_float_precisionfconv)xinmake_printfk(Acc_data_string(acc,str))fmt|No_padding,Lit_precisionp->funx->letstr=convert_floatfconvpxinmake_printfk(Acc_data_string(acc,str))fmt|No_padding,Arg_precision->funpx->letstr=convert_floatfconvpxinmake_printfk(Acc_data_string(acc,str))fmt|Lit_padding(padty,w),No_precision->funx->letstr=convert_floatfconv(default_float_precisionfconv)xinletstr'=fix_paddingpadtywstrinmake_printfk(Acc_data_string(acc,str'))fmt|Lit_padding(padty,w),Lit_precisionp->funx->letstr=fix_paddingpadtyw(convert_floatfconvpx)inmake_printfk(Acc_data_string(acc,str))fmt|Lit_padding(padty,w),Arg_precision->funpx->letstr=fix_paddingpadtyw(convert_floatfconvpx)inmake_printfk(Acc_data_string(acc,str))fmt|Arg_paddingpadty,No_precision->funwx->letstr=convert_floatfconv(default_float_precisionfconv)xinletstr'=fix_paddingpadtywstrinmake_printfk(Acc_data_string(acc,str'))fmt|Arg_paddingpadty,Lit_precisionp->funwx->letstr=fix_paddingpadtyw(convert_floatfconvpx)inmake_printfk(Acc_data_string(acc,str))fmt|Arg_paddingpadty,Arg_precision->funwpx->letstr=fix_paddingpadtyw(convert_floatfconvpx)inmake_printfk(Acc_data_string(acc,str))fmtandmake_custom:typexyabcdef.((b,c)acc->f)->(b,c)acc->(a,b,c,d,e,f)fmt->(a,x,y)custom_arity->x->y=funkaccrestarityf->matcharitywith|Custom_zero->make_printfk(Acc_data_string(acc,f))rest|Custom_succarity->funx->make_customkaccrestarity(fx)letconstx_=xletrecmake_iprintf:typeabcdefstate.(state->f)->state->(a,b,c,d,e,f)fmt->a=funkofmt->matchfmtwith|Charrest->const(make_iprintfkorest)|Caml_charrest->const(make_iprintfkorest)|String(No_padding,rest)->const(make_iprintfkorest)|String(Lit_padding_,rest)->const(make_iprintfkorest)|String(Arg_padding_,rest)->const(const(make_iprintfkorest))|Caml_string(No_padding,rest)->const(make_iprintfkorest)|Caml_string(Lit_padding_,rest)->const(make_iprintfkorest)|Caml_string(Arg_padding_,rest)->const(const(make_iprintfkorest))|Int(_,pad,prec,rest)->fn_of_padding_precisionkorestpadprec|Int32(_,pad,prec,rest)->fn_of_padding_precisionkorestpadprec|Nativeint(_,pad,prec,rest)->fn_of_padding_precisionkorestpadprec|Int64(_,pad,prec,rest)->fn_of_padding_precisionkorestpadprec|Float(_,pad,prec,rest)->fn_of_padding_precisionkorestpadprec|Bool(No_padding,rest)->const(make_iprintfkorest)|Bool(Lit_padding_,rest)->const(make_iprintfkorest)|Bool(Arg_padding_,rest)->const(const(make_iprintfkorest))|Alpharest->const(const(make_iprintfkorest))|Thetarest->const(make_iprintfkorest)|Custom(arity,_,rest)->fn_of_custom_aritykorestarity|Reader_->(* This case is impossible, by typing of formats. See the
note in the corresponding case for make_printf. *)assertfalse|Flushrest->make_iprintfkorest|String_literal(_,rest)->make_iprintfkorest|Char_literal(_,rest)->make_iprintfkorest|Format_arg(_,_,rest)->const(make_iprintfkorest)|Format_subst(_,fmtty,rest)->fun(Format(fmt,_))->make_iprintfko(concat_fmt(recastfmtfmtty)rest)|Scan_char_set(_,_,rest)->const(make_iprintfkorest)|Scan_get_counter(_,rest)->const(make_iprintfkorest)|Scan_next_charrest->const(make_iprintfkorest)|Ignored_param(ign,rest)->make_ignored_param(fun_->ko)(End_of_acc)ignrest|Formatting_lit(_,rest)->make_iprintfkorest|Formatting_gen(Open_tag(Format(fmt',_)),rest)->make_iprintf(funkoc->make_iprintfkkocrest)ofmt'|Formatting_gen(Open_box(Format(fmt',_)),rest)->make_iprintf(funkoc->make_iprintfkkocrest)ofmt'|End_of_format->koandfn_of_padding_precision:typexyzabcdefstate.(state->f)->state->(a,b,c,d,e,f)fmt->(x,y)padding->(y,z->a)precision->x=funkofmtpadprec->matchpad,precwith|No_padding,No_precision->const(make_iprintfkofmt)|No_padding,Lit_precision_->const(make_iprintfkofmt)|No_padding,Arg_precision->const(const(make_iprintfkofmt))|Lit_padding_,No_precision->const(make_iprintfkofmt)|Lit_padding_,Lit_precision_->const(make_iprintfkofmt)|Lit_padding_,Arg_precision->const(const(make_iprintfkofmt))|Arg_padding_,No_precision->const(const(make_iprintfkofmt))|Arg_padding_,Lit_precision_->const(const(make_iprintfkofmt))|Arg_padding_,Arg_precision->const(const(const(make_iprintfkofmt)))andfn_of_custom_arity:typexyabcdefstate.(state->f)->state->(a,b,c,d,e,f)fmt->(a,x,y)custom_arity->y=funkofmt->function|Custom_zero->make_iprintfkofmt|Custom_succarity->const(fn_of_custom_aritykofmtarity)(******************************************************************************)(* Continuations for make_printf *)(* Recursively output an "accumulator" containing a reversed list of
printing entities (string, char, flus, ...) in an output_stream. *)(* Used as a continuation of make_printf. *)letrecoutput_accoacc=matchaccwith|Acc_formatting_lit(p,fmting_lit)->lets=string_of_formatting_litfmting_litinoutput_accop;output_stringos;|Acc_formatting_gen(p,Acc_open_tagacc')->output_accop;output_stringo"@{";output_accoacc';|Acc_formatting_gen(p,Acc_open_boxacc')->output_accop;output_stringo"@[";output_accoacc';|Acc_string_literal(p,s)|Acc_data_string(p,s)->output_accop;output_stringos|Acc_char_literal(p,c)|Acc_data_char(p,c)->output_accop;output_charoc|Acc_delay(p,f)->output_accop;fo|Acc_flushp->output_accop;flusho|Acc_invalid_arg(p,msg)->output_accop;invalid_argmsg;|End_of_acc->()(* Recursively output an "accumulator" containing a reversed list of
printing entities (string, char, flus, ...) in a buffer. *)(* Used as a continuation of make_printf. *)letrecbufput_accbacc=matchaccwith|Acc_formatting_lit(p,fmting_lit)->lets=string_of_formatting_litfmting_litinbufput_accbp;Buffer.add_stringbs;|Acc_formatting_gen(p,Acc_open_tagacc')->bufput_accbp;Buffer.add_stringb"@{";bufput_accbacc';|Acc_formatting_gen(p,Acc_open_boxacc')->bufput_accbp;Buffer.add_stringb"@[";bufput_accbacc';|Acc_string_literal(p,s)|Acc_data_string(p,s)->bufput_accbp;Buffer.add_stringbs|Acc_char_literal(p,c)|Acc_data_char(p,c)->bufput_accbp;Buffer.add_charbc|Acc_delay(p,f)->bufput_accbp;fb|Acc_flushp->bufput_accbp;|Acc_invalid_arg(p,msg)->bufput_accbp;invalid_argmsg;|End_of_acc->()(* Recursively output an "accumulator" containing a reversed list of
printing entities (string, char, flus, ...) in a buffer. *)(* Differ from bufput_acc by the interpretation of %a and %t. *)(* Used as a continuation of make_printf. *)letrecstrput_accbacc=matchaccwith|Acc_formatting_lit(p,fmting_lit)->lets=string_of_formatting_litfmting_litinstrput_accbp;Buffer.add_stringbs;|Acc_formatting_gen(p,Acc_open_tagacc')->strput_accbp;Buffer.add_stringb"@{";strput_accbacc';|Acc_formatting_gen(p,Acc_open_boxacc')->strput_accbp;Buffer.add_stringb"@[";strput_accbacc';|Acc_string_literal(p,s)|Acc_data_string(p,s)->strput_accbp;Buffer.add_stringbs|Acc_char_literal(p,c)|Acc_data_char(p,c)->strput_accbp;Buffer.add_charbc|Acc_delay(p,f)->strput_accbp;Buffer.add_stringb(f())|Acc_flushp->strput_accbp;|Acc_invalid_arg(p,msg)->strput_accbp;invalid_argmsg;|End_of_acc->()(******************************************************************************)(* Error management *)(* Raise [Failure] with a pretty-printed error message. *)letfailwith_message(Format(fmt,_))=letbuf=Buffer.create256inletkacc=strput_accbufacc;failwith(Buffer.contentsbuf)inmake_printfkEnd_of_accfmt(******************************************************************************)(* Formatting tools *)(* Convert a string to an open block description (indent, block_type) *)letopen_box_of_stringstr=ifstr=""then(0,Pp_box)elseletlen=String.lengthstrinletinvalid_box()=failwith_message"invalid box description %S"strinletrecparse_spacesi=ifi=lenthenielsematchstr.[i]with|' '|'\t'->parse_spaces(i+1)|_->iandparse_lwordij=ifj=lenthenjelsematchstr.[j]with|'a'..'z'->parse_lwordi(j+1)|_->jandparse_intij=ifj=lenthenjelsematchstr.[j]with|'0'..'9'|'-'->parse_inti(j+1)|_->jinletwstart=parse_spaces0inletwend=parse_lwordwstartwstartinletbox_name=String.substrwstart(wend-wstart)inletnstart=parse_spaceswendinletnend=parse_intnstartnstartinletindent=ifnstart=nendthen0elsetryint_of_string(String.substrnstart(nend-nstart))withFailure_->invalid_box()inletexp_end=parse_spacesnendinifexp_end<>lentheninvalid_box();letbox_type=matchbox_namewith|""|"b"->Pp_box|"h"->Pp_hbox|"v"->Pp_vbox|"hv"->Pp_hvbox|"hov"->Pp_hovbox|_->invalid_box()in(indent,box_type)(******************************************************************************)(* Parsing tools *)(* Create a padding_fmt_ebb from a padding and a format. *)(* Copy the padding to disjoin the type parameters of argument and result. *)letmake_padding_fmt_ebb:typexy.(x,y)padding->(_,_,_,_,_,_)fmt->(_,_,_,_,_)padding_fmt_ebb=funpadfmt->matchpadwith|No_padding->Padding_fmt_EBB(No_padding,fmt)|Lit_padding(s,w)->Padding_fmt_EBB(Lit_padding(s,w),fmt)|Arg_paddings->Padding_fmt_EBB(Arg_paddings,fmt)(* Create a precision_fmt_ebb from a precision and a format. *)(* Copy the precision to disjoin the type parameters of argument and result. *)letmake_precision_fmt_ebb:typexy.(x,y)precision->(_,_,_,_,_,_)fmt->(_,_,_,_,_)precision_fmt_ebb=funprecfmt->matchprecwith|No_precision->Precision_fmt_EBB(No_precision,fmt)|Lit_precisionp->Precision_fmt_EBB(Lit_precisionp,fmt)|Arg_precision->Precision_fmt_EBB(Arg_precision,fmt)(* Create a padprec_fmt_ebb from a padding, a precision and a format. *)(* Copy the padding and the precision to disjoin type parameters of arguments
and result. *)letmake_padprec_fmt_ebb:typexyzt.(x,y)padding->(z,t)precision->(_,_,_,_,_,_)fmt->(_,_,_,_,_)padprec_fmt_ebb=funpadprecfmt->letPrecision_fmt_EBB(prec,fmt')=make_precision_fmt_ebbprecfmtinmatchpadwith|No_padding->Padprec_fmt_EBB(No_padding,prec,fmt')|Lit_padding(s,w)->Padprec_fmt_EBB(Lit_padding(s,w),prec,fmt')|Arg_paddings->Padprec_fmt_EBB(Arg_paddings,prec,fmt')(******************************************************************************)(* Format parsing *)(* Parse a string representing a format and create a fmt_ebb. *)(* Raise [Failure] in case of invalid format. *)letfmt_ebb_of_string?legacy_behaviorstr=(* Parameters naming convention: *)(* - lit_start: start of the literal sequence. *)(* - str_ind: current index in the string. *)(* - end_ind: end of the current (sub-)format. *)(* - pct_ind: index of the '%' in the current micro-format. *)(* - zero: is the '0' flag defined in the current micro-format. *)(* - minus: is the '-' flag defined in the current micro-format. *)(* - plus: is the '+' flag defined in the current micro-format. *)(* - hash: is the '#' flag defined in the current micro-format. *)(* - space: is the ' ' flag defined in the current micro-format. *)(* - ign: is the '_' flag defined in the current micro-format. *)(* - pad: padding of the current micro-format. *)(* - prec: precision of the current micro-format. *)(* - symb: char representing the conversion ('c', 's', 'd', ...). *)(* - char_set: set of characters as bitmap (see scanf %[...]). *)letlegacy_behavior=matchlegacy_behaviorwith|Someflag->flag|None->true(* When this flag is enabled, the format parser tries to behave as
the <4.02 implementations, in particular it ignores most benine
nonsensical format. When the flag is disabled, it will reject any
format that is not accepted by the specification.
A typical example would be "%+ d": specifying both '+' (if the
number is positive, pad with a '+' to get the same width as
negative numbers) and ' ' (if the number is positive, pad with
a space) does not make sense, but the legacy (< 4.02)
implementation was happy to just ignore the space.
*)in(* Raise [Failure] with a friendly error message. *)letinvalid_format_messagestr_indmsg=failwith_message"invalid format %S: at character number %d, %s"strstr_indmsgin(* Used when the end of the format (or the current sub-format) was encountered
unexpectedly. *)letunexpected_end_of_formatend_ind=invalid_format_messageend_ind"unexpected end of format"in(* Used for %0c: no other widths are implemented *)letinvalid_nonnull_char_widthstr_ind=invalid_format_messagestr_ind"non-zero widths are unsupported for %c conversions"in(* Raise [Failure] with a friendly error message about an option dependency
problem. *)letinvalid_format_withoutstr_indcs=failwith_message"invalid format %S: at character number %d, '%c' without %s"strstr_indcsin(* Raise [Failure] with a friendly error message about an unexpected
character. *)letexpected_characterstr_indexpectedread=failwith_message"invalid format %S: at character number %d, %s expected, read %C"strstr_indexpectedreadin(* Parse the string from beg_ind (included) to end_ind (excluded). *)letrecparse:typeef.int->int->(_,_,e,f)fmt_ebb=funbeg_indend_ind->parse_literalbeg_indbeg_indend_ind(* Read literal characters up to '%' or '@' special characters. *)andparse_literal:typeef.int->int->int->(_,_,e,f)fmt_ebb=funlit_startstr_indend_ind->ifstr_ind=end_indthenadd_literallit_startstr_indEnd_of_formatelsematchstr.[str_ind]with|'%'->letFmt_EBBfmt_rest=parse_formatstr_indend_indinadd_literallit_startstr_indfmt_rest|'@'->letFmt_EBBfmt_rest=parse_after_at(str_ind+1)end_indinadd_literallit_startstr_indfmt_rest|_->parse_literallit_start(str_ind+1)end_ind(* Parse a format after '%' *)andparse_format:typeef.int->int->(_,_,e,f)fmt_ebb=funpct_indend_ind->parse_ignpct_ind(pct_ind+1)end_indandparse_ign:typeef.int->int->int->(_,_,e,f)fmt_ebb=funpct_indstr_indend_ind->ifstr_ind=end_indthenunexpected_end_of_formatend_ind;matchstr.[str_ind]with|'_'->parse_flagspct_ind(str_ind+1)end_indtrue|_->parse_flagspct_indstr_indend_indfalseandparse_flags:typeef.int->int->int->bool->(_,_,e,f)fmt_ebb=funpct_indstr_indend_indign->letzero=reffalseandminus=reffalseandplus=reffalseandspace=reffalseandhash=reffalseinletset_flagstr_indflag=(* in legacy mode, duplicate flags are accepted *)if!flag&¬legacy_behaviorthenfailwith_message"invalid format %S: at character number %d, duplicate flag %C"strstr_indstr.[str_ind];flag:=true;inletrecread_flagsstr_ind=ifstr_ind=end_indthenunexpected_end_of_formatend_ind;beginmatchstr.[str_ind]with|'0'->set_flagstr_indzero;read_flags(str_ind+1)|'-'->set_flagstr_indminus;read_flags(str_ind+1)|'+'->set_flagstr_indplus;read_flags(str_ind+1)|'#'->set_flagstr_indhash;read_flags(str_ind+1)|' '->set_flagstr_indspace;read_flags(str_ind+1)|_->parse_paddingpct_indstr_indend_ind!zero!minus!plus!hash!spaceignendinread_flagsstr_ind(* Try to read a digital or a '*' padding. *)andparse_padding:typeef.int->int->int->bool->bool->bool->bool->bool->bool->(_,_,e,f)fmt_ebb=funpct_indstr_indend_indzerominusplushashspaceign->ifstr_ind=end_indthenunexpected_end_of_formatend_ind;letpadty=matchzero,minuswith|false,false->Right|false,true->Left|true,false->Zeros|true,true->iflegacy_behaviorthenLeftelseincompatible_flagpct_indstr_ind'-'"0"inmatchstr.[str_ind]with|'0'..'9'->letnew_ind,width=parse_positivestr_indend_ind0inparse_after_paddingpct_indnew_indend_indminusplushashspaceign(Lit_padding(padty,width))|'*'->parse_after_paddingpct_ind(str_ind+1)end_indminusplushashspaceign(Arg_paddingpadty)|_->beginmatchpadtywith|Left->ifnotlegacy_behaviortheninvalid_format_without(str_ind-1)'-'"padding";parse_after_paddingpct_indstr_indend_indminusplushashspaceignNo_padding|Zeros->(* a '0' padding indication not followed by anything should
be interpreted as a Right padding of width 0. This is used
by scanning conversions %0s and %0c *)parse_after_paddingpct_indstr_indend_indminusplushashspaceign(Lit_padding(Right,0))|Right->parse_after_paddingpct_indstr_indend_indminusplushashspaceignNo_paddingend(* Is precision defined? *)andparse_after_padding:typexef.int->int->int->bool->bool->bool->bool->bool->(x,_)padding->(_,_,e,f)fmt_ebb=funpct_indstr_indend_indminusplushashspaceignpad->ifstr_ind=end_indthenunexpected_end_of_formatend_ind;matchstr.[str_ind]with|'.'->parse_precisionpct_ind(str_ind+1)end_indminusplushashspaceignpad|symb->parse_conversionpct_ind(str_ind+1)end_indplushashspaceignpadNo_precisionpadsymb(* Read the digital or '*' precision. *)andparse_precision:typexef.int->int->int->bool->bool->bool->bool->bool->(x,_)padding->(_,_,e,f)fmt_ebb=funpct_indstr_indend_indminusplushashspaceignpad->ifstr_ind=end_indthenunexpected_end_of_formatend_ind;letparse_literalminusstr_ind=letnew_ind,prec=parse_positivestr_indend_ind0inparse_after_precisionpct_indnew_indend_indminusplushashspaceignpad(Lit_precisionprec)inmatchstr.[str_ind]with|'0'..'9'->parse_literalminusstr_ind|('+'|'-')assymbwhenlegacy_behavior->(* Legacy mode would accept and ignore '+' or '-' before the
integer describing the desired precision; note that this
cannot happen for padding width, as '+' and '-' already have
a semantics there.
That said, the idea (supported by this tweak) that width and
precision literals are "integer literals" in the OCaml sense is
still blatantly wrong, as 123_456 or 0xFF are rejected. *)parse_literal(minus||symb='-')(str_ind+1)|'*'->parse_after_precisionpct_ind(str_ind+1)end_indminusplushashspaceignpadArg_precision|_->iflegacy_behaviorthen(* note that legacy implementation did not ignore '.' without
a number (as it does for padding indications), but
interprets it as '.0' *)parse_after_precisionpct_indstr_indend_indminusplushashspaceignpad(Lit_precision0)elseinvalid_format_without(str_ind-1)'.'"precision"(* Try to read the conversion. *)andparse_after_precision:typexyztef.int->int->int->bool->bool->bool->bool->bool->(x,y)padding->(z,t)precision->(_,_,e,f)fmt_ebb=funpct_indstr_indend_indminusplushashspaceignpadprec->ifstr_ind=end_indthenunexpected_end_of_formatend_ind;letparse_conv(typeu)(typev)(padprec:(u,v)padding)=parse_conversionpct_ind(str_ind+1)end_indplushashspaceignpadprecpadprecstr.[str_ind]in(* in legacy mode, some formats (%s and %S) accept a weird mix of
padding and precision, which is merged as a single padding
information. For example, in %.10s the precision is implicitly
understood as padding %10s, but the left-padding component may
be specified either as a left padding or a negative precision:
%-.3s and %.-3s are equivalent to %-3s *)matchpadwith|No_padding->(matchminus,precwith|_,No_precision->parse_convNo_padding|false,Lit_precisionn->parse_conv(Lit_padding(Right,n))|true,Lit_precisionn->parse_conv(Lit_padding(Left,n))|false,Arg_precision->parse_conv(Arg_paddingRight)|true,Arg_precision->parse_conv(Arg_paddingLeft))|pad->parse_convpad(* Case analysis on conversion. *)andparse_conversion:typexyztuvef.int->int->int->bool->bool->bool->bool->(x,y)padding->(z,t)precision->(u,v)padding->char->(_,_,e,f)fmt_ebb=funpct_indstr_indend_indplushashspaceignpadprecpadprecsymb->(* Flags used to check option usages/compatibilities. *)letplus_used=reffalseandhash_used=reffalseandspace_used=reffalseandign_used=reffalseandpad_used=reffalseandprec_used=reffalsein(* Access to options, update flags. *)letget_plus()=plus_used:=true;plusandget_hash()=hash_used:=true;hashandget_space()=space_used:=true;spaceandget_ign()=ign_used:=true;ignandget_pad()=pad_used:=true;padandget_prec()=prec_used:=true;precandget_padprec()=pad_used:=true;padprecinletget_int_pad()=(* %5.3d is accepted and meaningful: pad to length 5 with
spaces, but first pad with zeros upto length 3 (0-padding
is the interpretation of "precision" for integer formats).
%05.3d is redundant: pad to length 5 *with zeros*, but
first pad with zeros... To add insult to the injury, the
legacy implementation ignores the 0-padding indication and
does the 5 padding with spaces instead. We reuse this
interpretation for compatibility, but statically reject this
format when the legacy mode is disabled, to protect strict
users from this corner case. *)matchget_pad(),get_prec()with|pad,No_precision->pad|No_padding,_->No_padding|Lit_padding(Zeros,n),_->iflegacy_behaviorthenLit_padding(Right,n)elseincompatible_flagpct_indstr_ind'0'"precision"|Arg_paddingZeros,_->iflegacy_behaviorthenArg_paddingRightelseincompatible_flagpct_indstr_ind'0'"precision"|Lit_padding_aspad,_->pad|Arg_padding_aspad,_->padin(* Check that padty <> Zeros. *)letcheck_no_0symb(typea)(typeb)(pad:(a,b)padding)=matchpadwith|No_padding->pad|Lit_padding((Left|Right),_)->pad|Arg_padding(Left|Right)->pad|Lit_padding(Zeros,width)->iflegacy_behaviorthenLit_padding(Right,width)elseincompatible_flagpct_indstr_indsymb"0"|Arg_paddingZeros->iflegacy_behaviorthenArg_paddingRightelseincompatible_flagpct_indstr_indsymb"0"in(* Get padding as a pad_option (see "%_", "%{", "%(" and "%[").
(no need for legacy mode tweaking, those were rejected by the
legacy parser as well) *)letopt_of_padc(typea)(typeb)(pad:(a,b)padding)=matchpadwith|No_padding->None|Lit_padding(Right,width)->Somewidth|Lit_padding(Zeros,width)->iflegacy_behaviorthenSomewidthelseincompatible_flagpct_indstr_indc"'0'"|Lit_padding(Left,width)->iflegacy_behaviorthenSomewidthelseincompatible_flagpct_indstr_indc"'-'"|Arg_padding_->incompatible_flagpct_indstr_indc"'*'"inletget_pad_optc=opt_of_padc(get_pad())inletget_padprec_optc=opt_of_padc(get_padprec())in(* Get precision as a prec_option (see "%_f").
(no need for legacy mode tweaking, those were rejected by the
legacy parser as well) *)letget_prec_opt()=matchget_prec()with|No_precision->None|Lit_precisionndec->Somendec|Arg_precision->incompatible_flagpct_indstr_ind'_'"'*'"inletfmt_result=matchsymbwith|','->parsestr_indend_ind|'c'->letchar_formatfmt_rest=(* %c *)ifget_ign()thenFmt_EBB(Ignored_param(Ignored_char,fmt_rest))elseFmt_EBB(Charfmt_rest)inletscan_formatfmt_rest=(* %0c *)ifget_ign()thenFmt_EBB(Ignored_param(Ignored_scan_next_char,fmt_rest))elseFmt_EBB(Scan_next_charfmt_rest)inletFmt_EBBfmt_rest=parsestr_indend_indinbeginmatchget_pad_opt'c'with|None->char_formatfmt_rest|Some0->scan_formatfmt_rest|Some_n->ifnotlegacy_behaviortheninvalid_nonnull_char_widthstr_indelse(* legacy ignores %c widths *)char_formatfmt_restend|'C'->letFmt_EBBfmt_rest=parsestr_indend_indinifget_ign()thenFmt_EBB(Ignored_param(Ignored_caml_char,fmt_rest))elseFmt_EBB(Caml_charfmt_rest)|'s'->letpad=check_no_0symb(get_padprec())inletFmt_EBBfmt_rest=parsestr_indend_indinifget_ign()thenletignored=Ignored_string(get_padprec_opt'_')inFmt_EBB(Ignored_param(ignored,fmt_rest))elseletPadding_fmt_EBB(pad',fmt_rest')=make_padding_fmt_ebbpadfmt_restinFmt_EBB(String(pad',fmt_rest'))|'S'->letpad=check_no_0symb(get_padprec())inletFmt_EBBfmt_rest=parsestr_indend_indinifget_ign()thenletignored=Ignored_caml_string(get_padprec_opt'_')inFmt_EBB(Ignored_param(ignored,fmt_rest))elseletPadding_fmt_EBB(pad',fmt_rest')=make_padding_fmt_ebbpadfmt_restinFmt_EBB(Caml_string(pad',fmt_rest'))|'d'|'i'|'x'|'X'|'o'|'u'->leticonv=compute_int_convpct_indstr_ind(get_plus())(get_hash())(get_space())symbinletFmt_EBBfmt_rest=parsestr_indend_indinifget_ign()thenletignored=Ignored_int(iconv,get_pad_opt'_')inFmt_EBB(Ignored_param(ignored,fmt_rest))elseletPadprec_fmt_EBB(pad',prec',fmt_rest')=make_padprec_fmt_ebb(get_int_pad())(get_prec())fmt_restinFmt_EBB(Int(iconv,pad',prec',fmt_rest'))|'N'->letFmt_EBBfmt_rest=parsestr_indend_indinletcounter=Token_counterinifget_ign()thenletignored=Ignored_scan_get_countercounterinFmt_EBB(Ignored_param(ignored,fmt_rest))elseFmt_EBB(Scan_get_counter(counter,fmt_rest))|'l'|'n'|'L'whenstr_ind=end_ind||not(is_int_basestr.[str_ind])->letFmt_EBBfmt_rest=parsestr_indend_indinletcounter=counter_of_charsymbinifget_ign()thenletignored=Ignored_scan_get_countercounterinFmt_EBB(Ignored_param(ignored,fmt_rest))elseFmt_EBB(Scan_get_counter(counter,fmt_rest))|'l'->leticonv=compute_int_convpct_ind(str_ind+1)(get_plus())(get_hash())(get_space())str.[str_ind]inletFmt_EBBfmt_rest=parse(str_ind+1)end_indinifget_ign()thenletignored=Ignored_int32(iconv,get_pad_opt'_')inFmt_EBB(Ignored_param(ignored,fmt_rest))elseletPadprec_fmt_EBB(pad',prec',fmt_rest')=make_padprec_fmt_ebb(get_int_pad())(get_prec())fmt_restinFmt_EBB(Int32(iconv,pad',prec',fmt_rest'))|'n'->leticonv=compute_int_convpct_ind(str_ind+1)(get_plus())(get_hash())(get_space())str.[str_ind]inletFmt_EBBfmt_rest=parse(str_ind+1)end_indinifget_ign()thenletignored=Ignored_nativeint(iconv,get_pad_opt'_')inFmt_EBB(Ignored_param(ignored,fmt_rest))elseletPadprec_fmt_EBB(pad',prec',fmt_rest')=make_padprec_fmt_ebb(get_int_pad())(get_prec())fmt_restinFmt_EBB(Nativeint(iconv,pad',prec',fmt_rest'))|'L'->leticonv=compute_int_convpct_ind(str_ind+1)(get_plus())(get_hash())(get_space())str.[str_ind]inletFmt_EBBfmt_rest=parse(str_ind+1)end_indinifget_ign()thenletignored=Ignored_int64(iconv,get_pad_opt'_')inFmt_EBB(Ignored_param(ignored,fmt_rest))elseletPadprec_fmt_EBB(pad',prec',fmt_rest')=make_padprec_fmt_ebb(get_int_pad())(get_prec())fmt_restinFmt_EBB(Int64(iconv,pad',prec',fmt_rest'))|'f'|'e'|'E'|'g'|'G'|'F'|'h'|'H'->letfconv=compute_float_convpct_indstr_ind(get_plus())(get_space())symbinletFmt_EBBfmt_rest=parsestr_indend_indinifget_ign()thenletignored=Ignored_float(get_pad_opt'_',get_prec_opt())inFmt_EBB(Ignored_param(ignored,fmt_rest))elseletPadprec_fmt_EBB(pad',prec',fmt_rest')=make_padprec_fmt_ebb(get_pad())(get_prec())fmt_restinFmt_EBB(Float(fconv,pad',prec',fmt_rest'))|'b'|'B'->letpad=check_no_0symb(get_padprec())inletFmt_EBBfmt_rest=parsestr_indend_indinifget_ign()thenletignored=Ignored_bool(get_padprec_opt'_')inFmt_EBB(Ignored_param(ignored,fmt_rest))elseletPadding_fmt_EBB(pad',fmt_rest')=make_padding_fmt_ebbpadfmt_restinFmt_EBB(Bool(pad',fmt_rest'))|'a'->letFmt_EBBfmt_rest=parsestr_indend_indinFmt_EBB(Alphafmt_rest)|'t'->letFmt_EBBfmt_rest=parsestr_indend_indinFmt_EBB(Thetafmt_rest)|'r'->letFmt_EBBfmt_rest=parsestr_indend_indinifget_ign()thenFmt_EBB(Ignored_param(Ignored_reader,fmt_rest))elseFmt_EBB(Readerfmt_rest)|'!'->letFmt_EBBfmt_rest=parsestr_indend_indinFmt_EBB(Flushfmt_rest)|('%'|'@')asc->letFmt_EBBfmt_rest=parsestr_indend_indinFmt_EBB(Char_literal(c,fmt_rest))|'{'->letsub_end=search_subformat_endstr_indend_ind'}'inletFmt_EBBsub_fmt=parsestr_indsub_endinletFmt_EBBfmt_rest=parse(sub_end+2)end_indinletsub_fmtty=fmtty_of_fmtsub_fmtinifget_ign()thenletignored=Ignored_format_arg(get_pad_opt'_',sub_fmtty)inFmt_EBB(Ignored_param(ignored,fmt_rest))elseFmt_EBB(Format_arg(get_pad_opt'{',sub_fmtty,fmt_rest))|'('->letsub_end=search_subformat_endstr_indend_ind')'inletFmt_EBBfmt_rest=parse(sub_end+2)end_indinletFmt_EBBsub_fmt=parsestr_indsub_endinletsub_fmtty=fmtty_of_fmtsub_fmtinifget_ign()thenletignored=Ignored_format_subst(get_pad_opt'_',sub_fmtty)inFmt_EBB(Ignored_param(ignored,fmt_rest))elseFmt_EBB(Format_subst(get_pad_opt'(',sub_fmtty,fmt_rest))|'['->letnext_ind,char_set=parse_char_setstr_indend_indinletFmt_EBBfmt_rest=parsenext_indend_indinifget_ign()thenletignored=Ignored_scan_char_set(get_pad_opt'_',char_set)inFmt_EBB(Ignored_param(ignored,fmt_rest))elseFmt_EBB(Scan_char_set(get_pad_opt'[',char_set,fmt_rest))|'-'|'+'|'#'|' '|'_'->failwith_message"invalid format %S: at character number %d, \
flag %C is only allowed after the '%%', before padding and precision"strpct_indsymb|_->failwith_message"invalid format %S: at character number %d, \
invalid conversion \"%%%c\""str(str_ind-1)symbin(* Check for unused options, and reject them as incompatible.
Such checks need to be disabled in legacy mode, as the legacy
parser silently ignored incompatible flags. *)ifnotlegacy_behaviorthenbeginifnot!plus_used&&plusthenincompatible_flagpct_indstr_indsymb"'+'";ifnot!hash_used&&hashthenincompatible_flagpct_indstr_indsymb"'#'";ifnot!space_used&&spacethenincompatible_flagpct_indstr_indsymb"' '";ifnot!pad_used&&Padding_EBBpad<>Padding_EBBNo_paddingthenincompatible_flagpct_indstr_indsymb"`padding'";ifnot!prec_used&&Precision_EBBprec<>Precision_EBBNo_precisionthenincompatible_flagpct_indstr_ind(ifignthen'_'elsesymb)"`precision'";ifign&&plusthenincompatible_flagpct_indstr_ind'_'"'+'";end;(* this last test must not be disabled in legacy mode,
as ignoring it would typically result in a different typing
than what the legacy parser used *)ifnot!ign_used&&ignthenbeginmatchsymbwith(* argument-less formats can safely be ignored in legacy mode *)|('@'|'%'|'!'|',')whenlegacy_behavior->()|_->incompatible_flagpct_indstr_indsymb"'_'"end;fmt_result(* Parse formatting information (after '@'). *)andparse_after_at:typeef.int->int->(_,_,e,f)fmt_ebb=funstr_indend_ind->ifstr_ind=end_indthenFmt_EBB(Char_literal('@',End_of_format))elsematchstr.[str_ind]with|'['->parse_tagfalse(str_ind+1)end_ind|']'->letFmt_EBBfmt_rest=parse(str_ind+1)end_indinFmt_EBB(Formatting_lit(Close_box,fmt_rest))|'{'->parse_tagtrue(str_ind+1)end_ind|'}'->letFmt_EBBfmt_rest=parse(str_ind+1)end_indinFmt_EBB(Formatting_lit(Close_tag,fmt_rest))|','->letFmt_EBBfmt_rest=parse(str_ind+1)end_indinFmt_EBB(Formatting_lit(Break("@,",0,0),fmt_rest))|' '->letFmt_EBBfmt_rest=parse(str_ind+1)end_indinFmt_EBB(Formatting_lit(Break("@ ",1,0),fmt_rest))|';'->parse_good_break(str_ind+1)end_ind|'?'->letFmt_EBBfmt_rest=parse(str_ind+1)end_indinFmt_EBB(Formatting_lit(FFlush,fmt_rest))|'\n'->letFmt_EBBfmt_rest=parse(str_ind+1)end_indinFmt_EBB(Formatting_lit(Force_newline,fmt_rest))|'.'->letFmt_EBBfmt_rest=parse(str_ind+1)end_indinFmt_EBB(Formatting_lit(Flush_newline,fmt_rest))|'<'->parse_magic_size(str_ind+1)end_ind|'@'->letFmt_EBBfmt_rest=parse(str_ind+1)end_indinFmt_EBB(Formatting_lit(Escaped_at,fmt_rest))|'%'whenstr_ind+1<end_ind&&str.[str_ind+1]='%'->letFmt_EBBfmt_rest=parse(str_ind+2)end_indinFmt_EBB(Formatting_lit(Escaped_percent,fmt_rest))|'%'->letFmt_EBBfmt_rest=parsestr_indend_indinFmt_EBB(Char_literal('@',fmt_rest))|c->letFmt_EBBfmt_rest=parse(str_ind+1)end_indinFmt_EBB(Formatting_lit(Scan_indicc,fmt_rest))andcheck_open_box:typeabcdef.(a,b,c,d,e,f)fmt->unit=funfmt->matchfmtwith|String_literal(str,End_of_format)->(tryignore(open_box_of_stringstr)withFailure_->((* Emit warning: invalid open box *)))|_->()(* Try to read the optional <name> after "@{" or "@[". *)andparse_tag:typeef.bool->int->int->(_,_,e,f)fmt_ebb=funis_open_tagstr_indend_ind->tryifstr_ind=end_indthenraiseNot_found;matchstr.[str_ind]with|'<'->letind=String.index_fromstr(str_ind+1)'>'inifind>=end_indthenraiseNot_found;letsub_str=String.substrstr_ind(ind-str_ind+1)inletFmt_EBBfmt_rest=parse(ind+1)end_indinletFmt_EBBsub_fmt=parsestr_ind(ind+1)inletsub_format=Format(sub_fmt,sub_str)inletformatting=ifis_open_tagthenOpen_tagsub_formatelse(check_open_boxsub_fmt;Open_boxsub_format)inFmt_EBB(Formatting_gen(formatting,fmt_rest))|_->raiseNot_foundwithNot_found->letFmt_EBBfmt_rest=parsestr_indend_indinletsub_format=Format(End_of_format,"")inletformatting=ifis_open_tagthenOpen_tagsub_formatelseOpen_boxsub_formatinFmt_EBB(Formatting_gen(formatting,fmt_rest))(* Try to read the optional <width offset> after "@;". *)andparse_good_break:typeef.int->int->(_,_,e,f)fmt_ebb=funstr_indend_ind->letnext_ind,formatting_lit=tryifstr_ind=end_ind||str.[str_ind]<>'<'thenraiseNot_found;letstr_ind_1=parse_spaces(str_ind+1)end_indinmatchstr.[str_ind_1]with|'0'..'9'|'-'->(letstr_ind_2,width=parse_integerstr_ind_1end_indinletstr_ind_3=parse_spacesstr_ind_2end_indinmatchstr.[str_ind_3]with|'>'->lets=String.substr(str_ind-2)(str_ind_3-str_ind+3)instr_ind_3+1,Break(s,width,0)|'0'..'9'|'-'->letstr_ind_4,offset=parse_integerstr_ind_3end_indinletstr_ind_5=parse_spacesstr_ind_4end_indinifstr.[str_ind_5]<>'>'thenraiseNot_found;lets=String.substr(str_ind-2)(str_ind_5-str_ind+3)instr_ind_5+1,Break(s,width,offset)|_->raiseNot_found)|_->raiseNot_foundwithNot_found|Failure_->str_ind,Break("@;",1,0)inletFmt_EBBfmt_rest=parsenext_indend_indinFmt_EBB(Formatting_lit(formatting_lit,fmt_rest))(* Parse the size in a <n>. *)andparse_magic_size:typeef.int->int->(_,_,e,f)fmt_ebb=funstr_indend_ind->matchtryletstr_ind_1=parse_spacesstr_indend_indinmatchstr.[str_ind_1]with|'0'..'9'|'-'->letstr_ind_2,size=parse_integerstr_ind_1end_indinletstr_ind_3=parse_spacesstr_ind_2end_indinifstr.[str_ind_3]<>'>'thenraiseNot_found;lets=String.substr(str_ind-2)(str_ind_3-str_ind+3)inSome(str_ind_3+1,Magic_size(s,size))|_->NonewithNot_found|Failure_->Nonewith|Some(next_ind,formatting_lit)->letFmt_EBBfmt_rest=parsenext_indend_indinFmt_EBB(Formatting_lit(formatting_lit,fmt_rest))|None->letFmt_EBBfmt_rest=parsestr_indend_indinFmt_EBB(Formatting_lit(Scan_indic'<',fmt_rest))(* Parse and construct a char set. *)andparse_char_setstr_indend_ind=ifstr_ind=end_indthenunexpected_end_of_formatend_ind;letchar_set=create_char_set()inletadd_charc=add_in_char_setchar_setc;inletadd_rangecc'=fori=int_of_charctoint_of_charc'doadd_in_char_setchar_set(char_of_inti);done;inletfail_single_percentstr_ind=failwith_message"invalid format %S: '%%' alone is not accepted in character sets, \
use %%%% instead at position %d."strstr_indin(* Parse the first character of a char set. *)letrecparse_char_set_startstr_indend_ind=ifstr_ind=end_indthenunexpected_end_of_formatend_ind;letc=str.[str_ind]inparse_char_set_after_char(str_ind+1)end_indc(* Parse the content of a char set until the first ']'. *)andparse_char_set_contentstr_indend_ind=ifstr_ind=end_indthenunexpected_end_of_formatend_ind;matchstr.[str_ind]with|']'->str_ind+1|'-'->add_char'-';parse_char_set_content(str_ind+1)end_ind|c->parse_char_set_after_char(str_ind+1)end_indc(* Test for range in char set. *)andparse_char_set_after_charstr_indend_indc=ifstr_ind=end_indthenunexpected_end_of_formatend_ind;matchstr.[str_ind]with|']'->add_charc;str_ind+1|'-'->parse_char_set_after_minus(str_ind+1)end_indc|('%'|'@')asc'whenc='%'->add_charc';parse_char_set_content(str_ind+1)end_ind|c'->ifc='%'thenfail_single_percentstr_ind;(* note that '@' alone is accepted, as done by the legacy
implementation; the documentation specifically requires %@
so we could warn on that *)add_charc;parse_char_set_after_char(str_ind+1)end_indc'(* Manage range in char set (except if the '-' the last char before ']') *)andparse_char_set_after_minusstr_indend_indc=ifstr_ind=end_indthenunexpected_end_of_formatend_ind;matchstr.[str_ind]with|']'->add_charc;add_char'-';str_ind+1|'%'->ifstr_ind+1=end_indthenunexpected_end_of_formatend_ind;beginmatchstr.[str_ind+1]with|('%'|'@')asc'->add_rangecc';parse_char_set_content(str_ind+2)end_ind|_->fail_single_percentstr_indend|c'->add_rangecc';parse_char_set_content(str_ind+1)end_indinletstr_ind,reverse=ifstr_ind=end_indthenunexpected_end_of_formatend_ind;matchstr.[str_ind]with|'^'->str_ind+1,true|_->str_ind,falseinletnext_ind=parse_char_set_startstr_indend_indinletchar_set=freeze_char_setchar_setinnext_ind,(ifreversethenrev_char_setchar_setelsechar_set)(* Consume all next spaces, raise an Failure if end_ind is reached. *)andparse_spacesstr_indend_ind=ifstr_ind=end_indthenunexpected_end_of_formatend_ind;ifstr.[str_ind]=' 'thenparse_spaces(str_ind+1)end_indelsestr_ind(* Read a positive integer from the string, raise a Failure if end_ind is
reached. *)andparse_positivestr_indend_indacc=ifstr_ind=end_indthenunexpected_end_of_formatend_ind;matchstr.[str_ind]with|'0'..'9'asc->letnew_acc=acc*10+(int_of_charc-int_of_char'0')inifnew_acc>Sys.max_string_lengththenfailwith_message"invalid format %S: integer %d is greater than the limit %d"strnew_accSys.max_string_lengthelseparse_positive(str_ind+1)end_indnew_acc|_->str_ind,acc(* Read a positive or negative integer from the string, raise a Failure
if end_ind is reached. *)andparse_integerstr_indend_ind=ifstr_ind=end_indthenunexpected_end_of_formatend_ind;matchstr.[str_ind]with|'0'..'9'->parse_positivestr_indend_ind0|'-'->(ifstr_ind+1=end_indthenunexpected_end_of_formatend_ind;matchstr.[str_ind+1]with|'0'..'9'->letnext_ind,n=parse_positive(str_ind+1)end_ind0innext_ind,-n|c->expected_character(str_ind+1)"digit"c)|_->assertfalse(* Add a literal to a format from a literal character sub-sequence. *)andadd_literal:typeadef.int->int->(a,_,_,d,e,f)fmt->(_,_,e,f)fmt_ebb=funlit_startstr_indfmt->matchstr_ind-lit_startwith|0->Fmt_EBBfmt|1->Fmt_EBB(Char_literal(str.[lit_start],fmt))|size->Fmt_EBB(String_literal(String.substrlit_startsize,fmt))(* Search the end of the current sub-format
(i.e. the corresponding "%}" or "%)") *)andsearch_subformat_endstr_indend_indc=ifstr_ind=end_indthenfailwith_message"invalid format %S: unclosed sub-format, \
expected \"%%%c\" at character number %d"strcend_ind;matchstr.[str_ind]with|'%'->ifstr_ind+1=end_indthenunexpected_end_of_formatend_ind;ifstr.[str_ind+1]=cthen(* End of format found *)str_indelsebeginmatchstr.[str_ind+1]with|'_'->(* Search for "%_(" or "%_{". *)ifstr_ind+2=end_indthenunexpected_end_of_formatend_ind;beginmatchstr.[str_ind+2]with|'{'->letsub_end=search_subformat_end(str_ind+3)end_ind'}'insearch_subformat_end(sub_end+2)end_indc|'('->letsub_end=search_subformat_end(str_ind+3)end_ind')'insearch_subformat_end(sub_end+2)end_indc|_->search_subformat_end(str_ind+3)end_indcend|'{'->(* %{...%} sub-format found. *)letsub_end=search_subformat_end(str_ind+2)end_ind'}'insearch_subformat_end(sub_end+2)end_indc|'('->(* %(...%) sub-format found. *)letsub_end=search_subformat_end(str_ind+2)end_ind')'insearch_subformat_end(sub_end+2)end_indc|'}'->(* Error: %(...%}. *)expected_character(str_ind+1)"character ')'"'}'|')'->(* Error: %{...%). *)expected_character(str_ind+1)"character '}'"')'|_->search_subformat_end(str_ind+2)end_indcend|_->search_subformat_end(str_ind+1)end_indc(* Check if symb is a valid int conversion after "%l", "%n" or "%L" *)andis_int_basesymb=matchsymbwith|'d'|'i'|'x'|'X'|'o'|'u'->true|_->false(* Convert a char (l, n or L) to its associated counter. *)andcounter_of_charsymb=matchsymbwith|'l'->Line_counter|'n'->Char_counter|'L'->Token_counter|_->assertfalse(* Convert (plus, symb) to its associated int_conv. *)andcompute_int_convpct_indstr_indplushashspacesymb=matchplus,hash,space,symbwith|false,false,false,'d'->Int_d|false,false,false,'i'->Int_i|false,false,true,'d'->Int_sd|false,false,true,'i'->Int_si|true,false,false,'d'->Int_pd|true,false,false,'i'->Int_pi|false,false,false,'x'->Int_x|false,false,false,'X'->Int_X|false,true,false,'x'->Int_Cx|false,true,false,'X'->Int_CX|false,false,false,'o'->Int_o|false,true,false,'o'->Int_Co|false,false,false,'u'->Int_u|false,true,false,'d'->Int_Cd|false,true,false,'i'->Int_Ci|false,true,false,'u'->Int_Cu|_,true,_,'x'whenlegacy_behavior->Int_Cx|_,true,_,'X'whenlegacy_behavior->Int_CX|_,true,_,'o'whenlegacy_behavior->Int_Co|_,true,_,('d'|'i'|'u')->iflegacy_behaviorthen(* ignore *)compute_int_convpct_indstr_indplusfalsespacesymbelseincompatible_flagpct_indstr_indsymb"'#'"|true,_,true,_->iflegacy_behaviorthen(* plus and space: legacy implementation prefers plus *)compute_int_convpct_indstr_indplushashfalsesymbelseincompatible_flagpct_indstr_ind' '"'+'"|false,_,true,_->iflegacy_behaviorthen(* ignore *)compute_int_convpct_indstr_indplushashfalsesymbelseincompatible_flagpct_indstr_indsymb"' '"|true,_,false,_->iflegacy_behaviorthen(* ignore *)compute_int_convpct_indstr_indfalsehashspacesymbelseincompatible_flagpct_indstr_indsymb"'+'"|false,_,false,_->assertfalse(* Convert (plus, space, symb) to its associated float_conv. *)andcompute_float_convpct_indstr_indplusspacesymb=letflag=matchplus,spacewith|false,false->Float_flag_|false,true->Float_flag_s|true,false->Float_flag_p|true,true->(* plus and space: legacy implementation prefers plus *)iflegacy_behaviorthenFloat_flag_pelseincompatible_flagpct_indstr_ind' '"'+'"inletkind=matchsymbwith|'f'->Float_f|'e'->Float_e|'E'->Float_E|'g'->Float_g|'G'->Float_G|'h'->Float_h|'H'->Float_H|'F'->Float_F|_->assertfalseinflag,kind(* Raise [Failure] with a friendly error message about incompatible options.*)andincompatible_flag:typea.int->int->char->string->a=funpct_indstr_indsymboption->letsubfmt=String.substrpct_ind(str_ind-pct_ind)infailwith_message"invalid format %S: at character number %d, \
%s is incompatible with '%c' in sub-format %S"strpct_indoptionsymbsubfmtinparse0(String.lengthstr)(******************************************************************************)(* Guarded string to format conversions *)(* Convert a string to a format according to an fmtty. *)(* Raise [Failure] with an error message in case of type mismatch. *)letformat_of_string_fmttystrfmtty=letFmt_EBBfmt=fmt_ebb_of_stringstrintryFormat(type_formatfmtfmtty,str)withType_mismatch->failwith_message"bad input: format type mismatch between %S and %S"str(string_of_fmttyfmtty)(* Convert a string to a format compatible with an other format. *)(* Raise [Failure] with an error message in case of type mismatch. *)letformat_of_string_formatstr(Format(fmt',str'))=letFmt_EBBfmt=fmt_ebb_of_stringstrintryFormat(type_formatfmt(fmtty_of_fmtfmt'),str)withType_mismatch->failwith_message"bad input: format type mismatch between %S and %S"strstr'