123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812(*---------------------------------------------------------------------------
Copyright (c) 2014 The fmt programmers. All rights reserved.
SPDX-License-Identifier: ISC
---------------------------------------------------------------------------*)letinvalid_arg'=invalid_arg(* Errors *)leterr_str_formatter="Format.str_formatter can't be set."(* Standard outputs *)letstdout=Format.std_formatterletstderr=Format.err_formatter(* Formatting *)letpf=Format.fprintfletpr=Format.printfletepr=Format.eprintfletstr=Format.asprintfletkpf=Format.kfprintfletkstr=Format.kasprintfletfailwithfmt=kstrfailwithfmtletfailwith_notracefmt=kstr(funs->raise_notrace(Failures))fmtletinvalid_argfmt=kstrinvalid_argfmtleterrorfmt=kstr(funs->Errors)fmtleterror_msgfmt=kstr(funs->Error(`Msgs))fmt(* Formatters *)type'at=Format.formatter->'a->unitletflushppf_=Format.pp_print_flushppf()letnopfmtppf=()letanyfmtppf_=pfppffmtletusingfppppfv=ppppf(fv)letconstpp_vvppf_=pp_vppfvletif'boolpp=ifboolthenppelsenopletfmtfmtppf=pfppffmt(* Separators *)letcutppf_=Format.pp_print_cutppf()letspppf_=Format.pp_print_spaceppf()letspsnppf_=Format.pp_print_breakppfn0letcommappf_=Format.pp_print_stringppf",";spppf()letsemippf_=Format.pp_print_stringppf";";spppf()(* Sequencing *)letiter?sep:(pp_sep=cut)iterpp_eltppfv=letis_first=reftrueinletpp_eltv=if!is_firstthen(is_first:=false)elsepp_sepppf();pp_eltppfviniterpp_eltvletiter_bindings?sep:(pp_sep=cut)iterpp_bindingppfv=letis_first=reftrueinletpp_bindingkv=if!is_firstthen(is_first:=false)elsepp_sepppf();pp_bindingppf(k,v)initerpp_bindingvletappendpp_v0pp_v1ppfv=pp_v0ppfv;pp_v1ppfvlet(++)=appendletconcat?sepppsppfv=iter?sepList.iter(funppfpp->ppppfv)ppfpps(* Boxes *)letbox?(indent=0)pp_vppfv=Format.(pp_open_boxppfindent;pp_vppfv;pp_close_boxppf())lethboxpp_vppfv=Format.(pp_open_hboxppf();pp_vppfv;pp_close_boxppf())letvbox?(indent=0)pp_vppfv=Format.(pp_open_vboxppfindent;pp_vppfv;pp_close_boxppf())lethvbox?(indent=0)pp_vppfv=Format.(pp_open_hvboxppfindent;pp_vppfv;pp_close_boxppf())lethovbox?(indent=0)pp_vppfv=Format.(pp_open_hovboxppfindent;pp_vppfv;pp_close_boxppf())(* Brackets *)letsurrounds1s2pp_vppfv=Format.(pp_print_stringppfs1;pp_vppfv;pp_print_stringppfs2)letparenspp_v=box~indent:1(surround"("")"pp_v)letbracketspp_v=box~indent:1(surround"[""]"pp_v)letoxford_bracketspp_v=box~indent:2(surround"[|""|]"pp_v)letbracespp_v=box~indent:1(surround"{""}"pp_v)letquote?(mark="\"")pp_v=letpp_markppf_=Format.pp_print_asppf1markinbox~indent:1(pp_mark++pp_v++pp_mark)(* Stdlib types formatters *)letbool=Format.pp_print_boolletint=Format.pp_print_intletnativeintppfv=pfppf"%nd"vletint32ppfv=pfppf"%ld"vletint64ppfv=pfppf"%Ld"vletuintppfv=pfppf"%u"vletuint32ppfv=pfppf"%lu"vletuint64ppfv=pfppf"%Lu"vletunativeintppfv=pfppf"%nu"vletchar=Format.pp_print_charletstring=Format.pp_print_stringletbufferppfb=stringppf(Buffer.contentsb)letexnppfe=stringppf(Printexc.to_stringe)letexn_backtraceppf(e,bt)=letpp_backtrace_strppfs=letstop=String.lengths-1(* there's a newline at the end *)inletrecloopleftright=ifright=stopthenstringppf(String.subsleft(right-left))elseifs.[right]<>'\n'thenloopleft(right+1)elsebeginstringppf(String.subsleft(right-left));cutppf();loop(right+1)(right+1)endinifs=""then(stringppf"No backtrace available.")elseloop00inpfppf"@[<v>Exception: %a@,%a@]"exnepp_backtrace_str(Printexc.raw_backtrace_to_stringbt)letfloatppfv=pfppf"%g"vletroundx=floor(x+.0.5)letround_dfracdx=ifx-.(roundx)=0.thenxelse(* x is an integer. *)letm=10.**(float_of_intd)in(* m moves 10^-d to 1. *)(floor((x*.m)+.0.5))/.mletround_dsigdx=ifx=0.then0.elseletm=10.**(floor(log10(abs_floatx)))in(* to normalize x. *)(round_dfracd(x/.m))*.mletfloat_dfracdppff=pfppf"%g"(round_dfracdf)letfloat_dsigdppff=pfppf"%g"(round_dsigdf)letpair?sep:(pp_sep=cut)pp_fstpp_sndppf(fst,snd)=pp_fstppffst;pp_sepppf();pp_sndppfsndletoption?none:(pp_none=nop)pp_vppf=function|None->pp_noneppf()|Somev->pp_vppfvletresult~ok~errorppf=function|Okv->okppfv|Errore->errorppfeletlist?seppp_elt=iter?sepList.iterpp_eltletarray?seppp_elt=iter?sepArray.iterpp_eltletseq?seppp_elt=iter?sepSeq.iterpp_eltlethashtbl?seppp_binding=iter_bindings?sepHashtbl.iterpp_bindingletqueue?seppp_elt=iterQueue.iterpp_eltletstack?seppp_elt=iterStack.iterpp_elt(* Stdlib type dumpers *)moduleDump=struct(* Stdlib types *)letsig_names=Sys.[sigabrt,"SIGABRT";sigalrm,"SIGALRM";sigfpe,"SIGFPE";sighup,"SIGHUP";sigill,"SIGILL";sigint,"SIGINT";sigkill,"SIGKILL";sigpipe,"SIGPIPE";sigquit,"SIGQUIT";sigsegv,"SIGSEGV";sigterm,"SIGTERM";sigusr1,"SIGUSR1";sigusr2,"SIGUSR2";sigchld,"SIGCHLD";sigcont,"SIGCONT";sigstop,"SIGSTOP";sigtstp,"SIGTSTP";sigttin,"SIGTTIN";sigttou,"SIGTTOU";sigvtalrm,"SIGVTALRM";sigprof,"SIGPROF";sigbus,"SIGBUS";sigpoll,"SIGPOLL";sigsys,"SIGSYS";sigtrap,"SIGTRAP";sigurg,"SIGURG";sigxcpu,"SIGXCPU";sigxfsz,"SIGXFSZ";]letsignalppfs=matchList.assq_optssig_nameswith|Somename->stringppfname|None->pfppf"SIG(%d)"sletucharppfu=pfppf"U+%04X"(Uchar.to_intu)letstringppfs=pfppf"%S"sletpairpp_fstpp_snd=parens(usingfst(boxpp_fst)++comma++usingsnd(boxpp_snd))letoptionpp_vppf=function|None->pfppf"None"|Somev->pfppf"@[<2>Some@ @[%a@]@]"pp_vvletresult~ok~errorppf=function|Okv->pfppf"@[<2>Ok@ @[%a@]@]"okv|Errore->pfppf"@[<2>Error@ @[%a@]@]"errore(* Sequencing *)letiteriter_fpp_namepp_elt=letpp_v=iter~sep:spiter_f(boxpp_elt)inparens(pp_name++sp++pp_v)letiter_bindingsiter_fpp_namepp_kpp_v=letpp_v=iter_bindings~sep:spiter_f(pairpp_kpp_v)inparens(pp_name++sp++pp_v)(* Stdlib data structures *)letlistpp_elt=brackets(list~sep:semi(boxpp_elt))letarraypp_elt=oxford_brackets(array~sep:semi(boxpp_elt))letseqpp_elt=brackets(seq~sep:semi(boxpp_elt))lethashtblpp_kpp_v=iter_bindingsHashtbl.iter(any"hashtbl")pp_kpp_vletstackpp_elt=iterStack.iter(any"stack")pp_eltletqueuepp_elt=iterQueue.iter(any"queue")pp_elt(* Records *)letfield?(label=string)lprjpp_vppfv=pfppf"@[<1>%a =@ %a@]"labellpp_v(prjv)letrecordpps=box~indent:2(surround"{ "" }"@@vbox(concat~sep:(any";@,")pps))end(* Magnitudes *)letilog10x=letreclooppx=ifx=0thenpelseloop(p+1)(x/10)inloop(-1)xletipow10n=letrecloopaccn=ifn=0thenaccelseloop(acc*10)(n-1)inloop1nletsi_symb_max=16letsi_symb=[|"y";"z";"a";"f";"p";"n";"u";"m";"";"k";"M";"G";"T";"P";"E";"Z";"Y"|]letrecpp_at_factor~scaleusymbfactorppfs=letm=s/factorinletn=smodfactorinmatchmwith|mwhenm>=100->(* No fractional digit *)letm_up=ifn>0thenm+1elseminifm_up>=1000thensi_size~scaleuppf(m_up*factor)elsepfppf"%d%s%s"m_upsymbu|mwhenm>=10->(* One fractional digit w.o. trailing 0 *)letf_factor=factor/10inletf_m=n/f_factorinletf_n=nmodf_factorinletf_m_up=iff_n>0thenf_m+1elsef_minbeginmatchf_m_upwith|0->pfppf"%d%s%s"msymbu|fwhenf>=10->si_size~scaleuppf(m*factor+f*f_factor)|f->pfppf"%d.%d%s%s"mfsymbuend|m->(* Two or zero fractional digits w.o. trailing 0 *)letf_factor=factor/100inletf_m=n/f_factorinletf_n=nmodf_factorinletf_m_up=iff_n>0thenf_m+1elsef_minmatchf_m_upwith|0->pfppf"%d%s%s"msymbu|fwhenf>=100->si_size~scaleuppf(m*factor+f*f_factor)|fwhenfmod10=0->pfppf"%d.%d%s%s"m(f/10)symbu|f->pfppf"%d.%02d%s%s"mfsymbuandsi_size~scaleuppfs=matchscale<-8||scale>8with|true->invalid_arg"~scale is %d, must be in [-8;8]"scale|false->letpow_div_3=ifs=0then0else(ilog10s/3)inletsymb=(scale+8)+pow_div_3inletsymb,factor=matchsymb>si_symb_maxwith|true->si_symb_max,ipow10((8-scale)*3)|false->symb,ipow10(pow_div_3*3)iniffactor=1thenpfppf"%d%s%s"ssi_symb.(symb)uelsepp_at_factor~scaleusi_symb.(symb)factorppfsletbyte_sizeppfs=si_size~scale:0"B"ppfsletbi_byte_sizeppfs=(* XXX we should get rid of this. *)let_pp_byte_sizekippfs=letpp_frac=float_dfrac1inletdiv_round_upmn=(m+n-1)/ninletfloat=float_of_intinifs<kthenpfppf"%dB"selseletm=k*kinifs<mthenbeginletkstr=ifi=""then"k"(* SI *)else"K"(* IEC *)inletsk=s/kinifsk<10thenpfppf"%a%s%sB"pp_frac(floats/.floatk)kstrielsepfppf"%d%s%sB"(div_round_upsk)kstriendelseletg=k*minifs<gthenbeginletsm=s/minifsm<10thenpfppf"%aM%sB"pp_frac(floats/.floatm)ielsepfppf"%dM%sB"(div_round_upsm)iendelselett=k*ginifs<tthenbeginletsg=s/ginifsg<10thenpfppf"%aG%sB"pp_frac(floats/.floatg)ielsepfppf"%dG%sB"(div_round_upsg)iendelseletp=k*tinifs<pthenbeginletst=s/tinifst<10thenpfppf"%aT%sB"pp_frac(floats/.floatt)ielsepfppf"%dT%sB"(div_round_upst)iendelsebeginletsp=s/pinifsp<10thenpfppf"%aP%sB"pp_frac(floats/.floatp)ielsepfppf"%dP%sB"(div_round_upsp)iendin_pp_byte_size1024"i"ppfs(* XXX From 4.08 on use Int64.unsigned_*
See Hacker's Delight for the implementation of these unsigned_* funs *)letunsigned_comparex0x1=Int64.(compare(subx0min_int)(subx1min_int))letunsigned_divnd=matchd<Int64.zerowith|true->ifunsigned_comparend<0thenInt64.zeroelseInt64.one|false->letq=Int64.(shift_left(div(shift_right_logicaln1)d)1)inletr=Int64.(subn(mulqd))inifunsigned_comparerd>=0thenInt64.succqelseqletunsigned_remnd=Int64.(subn(mul(unsigned_divnd)d))letus_span=1_000Lletms_span=1_000_000Lletsec_span=1_000_000_000Lletmin_span=60_000_000_000Llethour_span=3600_000_000_000Lletday_span=86_400_000_000_000Lletyear_span=31_557_600_000_000_000Lletrecpp_si_spanunit_strsi_unitsi_higher_unitppfspan=letgeqxy=unsigned_comparexy>=0inletm=unsigned_divspansi_unitinletn=unsigned_remspansi_unitinmatchmwith|mwhengeqm100L->(* No fractional digit *)letm_up=ifInt64.equaln0LthenmelseInt64.succminletspan'=Int64.mulm_upsi_unitinifgeqspan'si_higher_unitthenuint64_ns_spanppfspan'elsepfppf"%Ld%s"m_upunit_str|mwhengeqm10L->(* One fractional digit w.o. trailing zero *)letf_factor=unsigned_divsi_unit10Linletf_m=unsigned_divnf_factorinletf_n=unsigned_remnf_factorinletf_m_up=ifInt64.equalf_n0Lthenf_melseInt64.succf_minbeginmatchf_m_upwith|0L->pfppf"%Ld%s"munit_str|fwhengeqf10L->uint64_ns_spanppfInt64.(add(mulmsi_unit)(mulff_factor))|f->pfppf"%Ld.%Ld%s"mfunit_strend|m->(* Two or zero fractional digits w.o. trailing zero *)letf_factor=unsigned_divsi_unit100Linletf_m=unsigned_divnf_factorinletf_n=unsigned_remnf_factorinletf_m_up=ifInt64.equalf_n0Lthenf_melseInt64.succf_minmatchf_m_upwith|0L->pfppf"%Ld%s"munit_str|fwhengeqf100L->uint64_ns_spanppfInt64.(add(mulmsi_unit)(mulff_factor))|fwhenInt64.equal(Int64.remf10L)0L->pfppf"%Ld.%Ld%s"m(Int64.divf10L)unit_str|f->pfppf"%Ld.%02Ld%s"mfunit_strandpp_non_siunit_strunitunit_lo_strunit_lounit_lo_sizeppfspan=letgeqxy=unsigned_comparexy>=0inletm=unsigned_divspanunitinletn=unsigned_remspanunitinifInt64.equaln0Lthenpfppf"%Ld%s"munit_strelseletf_m=unsigned_divnunit_loinletf_n=unsigned_remnunit_loinletf_m_up=ifInt64.equalf_n0Lthenf_melseInt64.succf_minmatchf_m_upwith|fwhengeqfunit_lo_size->uint64_ns_spanppfInt64.(add(mulmunit)(mulfunit_lo))|f->pfppf"%Ld%s%Ld%s"munit_strfunit_lo_stranduint64_ns_spanppfspan=letgeqxy=unsigned_comparexy>=0inletltxy=unsigned_comparexy=-1inmatchspanwith|swhenltsus_span->pfppf"%Ldns"s|swhenltsms_span->pp_si_span"us"us_spanms_spanppfs|swhenltssec_span->pp_si_span"ms"ms_spansec_spanppfs|swhenltsmin_span->pp_si_span"s"sec_spanmin_spanppfs|swhenltshour_span->pp_non_si"min"min_span"s"sec_span60Lppfs|swhenltsday_span->pp_non_si"h"hour_span"min"min_span60Lppfs|swhenltsyear_span->pp_non_si"d"day_span"h"hour_span24Lppfs|s->letm=unsigned_divsyear_spaninletn=unsigned_remsyear_spaninifInt64.equaln0Lthenpfppf"%Lda"melseletf_m=unsigned_divnday_spaninletf_n=unsigned_remnday_spaninletf_m_up=ifInt64.equalf_n0Lthenf_melseInt64.succf_minmatchf_m_upwith|fwhengeqf366L->pfppf"%Lda"(Int64.succm)|f->pfppf"%Lda%Ldd"mf(* Binary formatting *)type'avec=int*(int->'a)letiter_vecf(n,get)=fori=0ton-1dofi(geti)doneletvec?sep=iter_bindings?sepiter_vecleton_string=usingString.(funs->lengths,gets)leton_bytes=usingBytes.(funb->lengthb,getb)letsub_vecsw(n,get)=(n-1)/w+1,funj->letoff=w*jinminw(n-off),funi->get(i+off)letprefix0x=[0xf,fmt"%01x";0xff,fmt"%02x";0xfff,fmt"%03x";0xffff,fmt"%04x";0xfffff,fmt"%05x";0xffffff,fmt"%06x";0xfffffff,fmt"%07x";]letpadded0x~max=matchList.find_opt(fun(x,_)->max<=x)prefix0xwith|Some(_,pp)->pp|None->fmt"%08x"letascii?(w=0)?(subst=constchar'.')()ppf(n,_asv)=letpp_charppf(_,c)=if'\x20'<=c&&c<'\x7f'thencharppfcelsesubstppf()invecpp_charppfv;ifn<wthensps(w-n)ppf()letoctets?(w=0)?(sep=sp)()ppf(n,_asv)=letpp_sepppfi=ifi>0&&imod2=0thensepppf()inletpp_charppf(i,c)=pp_sepppfi;pfppf"%02x"(Char.codec)invec~sep:noppp_charppfv;fori=ntow-1dopp_sepppfi;sps2ppf()doneletaddresses?addr?(w=16)pp_vecppf(n,_asv)=letaddr=matchaddrwith|Somepp->pp|_->padded0x~max:(((n-1)/w)*w)++conststring": "inletpp_subppf(i,sub)=addrppf(i*w);boxpp_vecppfsubinvbox(vecpp_sub)ppf(sub_vecswv)lethex?(w=16)()=addresses~w((octets~w()|>box)++sps2++(ascii~w()|>box))(* Text and lines *)letis_nlc=c='\n'letis_nl_or_spc=is_nlc||c=' 'letis_white=function' '|'\t'..'\r'->true|_->falseletnot_whitec=not(is_whitec)letnot_white_or_nlc=is_nlc||not_whitecletrecstop_atsat~start~maxs=ifstart>maxthenstartelseifsats.[start]thenstartelsestop_atsat~start:(start+1)~maxsletsubsstartstop~max=ifstart=stopthen""elseifstart=0&&stop>maxthenselseString.subsstart(stop-start)letwordsppfs=letmax=String.lengths-1inletrecloopstarts=matchstop_atis_white~start~maxswith|stopwhenstop>max->Format.pp_print_stringppf(subsstartstop~max)|stop->Format.pp_print_stringppf(subsstartstop~max);matchstop_atnot_white~start:stop~maxswith|stopwhenstop>max->()|stop->Format.pp_print_spaceppf();loopstopsinletstart=stop_atnot_white~start:0~maxsinifstart>maxthen()elseloopstartsletparagraphsppfs=letmax=String.lengths-1inletrecloopstarts=matchstop_atis_white~start~maxswith|stopwhenstop>max->Format.pp_print_stringppf(subsstartstop~max)|stop->Format.pp_print_stringppf(subsstartstop~max);matchstop_atnot_white_or_nl~start:stop~maxswith|stopwhenstop>max->()|stop->ifs.[stop]<>'\n'then(Format.pp_print_spaceppf();loopstops)elsematchstop_atnot_white_or_nl~start:(stop+1)~maxswith|stopwhenstop>max->()|stop->ifs.[stop]<>'\n'then(Format.pp_print_spaceppf();loopstops)elsematchstop_atnot_white~start:(stop+1)~maxswith|stopwhenstop>max->()|stop->Format.pp_force_newlineppf();Format.pp_force_newlineppf();loopstopsinletstart=stop_atnot_white~start:0~maxsinifstart>maxthen()elseloopstartslettextppfs=letmax=String.lengths-1inletrecloopstarts=matchstop_atis_nl_or_sp~start~maxswith|stopwhenstop>max->Format.pp_print_stringppf(subsstartstop~max)|stop->Format.pp_print_stringppf(subsstartstop~max);beginmatchs.[stop]with|' '->Format.pp_print_spaceppf()|'\n'->Format.pp_force_newlineppf()|_->assertfalseend;loop(stop+1)sinloop0sletlinesppfs=letmax=String.lengths-1inletrecloopstarts=matchstop_atis_nl~start~maxswith|stopwhenstop>max->Format.pp_print_stringppf(subsstartstop~max)|stop->Format.pp_print_stringppf(subsstartstop~max);Format.pp_force_newlineppf();loop(stop+1)sinloop0slettruncated~maxppfs=matchString.lengths<=maxwith|true->Format.pp_print_stringppfs|false->fori=0tomax-4doFormat.pp_print_charppfs.[i]done;Format.pp_print_stringppf"..."lettext_locppf((l0,c0),(l1,c1))=if(l0:int)==(l1:int)&&(c0:int)==(c1:int)thenpfppf"%d.%d"l0c0elsepfppf"%d.%d-%d.%d"l0c0l1c1(* HCI fragments *)letone_of?(empty=nop)pp_vppf=function|[]->emptyppf()|[v]->pp_vppfv|[v0;v1]->pfppf"@[either %a or@ %a@]"pp_vv0pp_vv1|_::_asvs->letrecloopppf=function|[v]->pfppf"or@ %a"pp_vv|v::vs->pfppf"%a,@ "pp_vv;loopppfvs|[]->assertfalseinpfppf"@[one@ of@ %a@]"loopvsletdid_you_mean?(pre=any"Unknown")?(post=nop)~kindpp_vppf(v,hints)=matchhintswith|[]->pfppf"@[%a %s %a%a.@]"pre()kindpp_vvpost()|hints->pfppf"@[%a %s %a%a.@ Did you mean %a ?@]"pre()kindpp_vvpost()(one_ofpp_v)hintsletcardinal?zero~one?other()=letother=matchotherwith|Someother->other|None->funppfi->oneppfi;charppf's'inletzero=Option.value~default:otherzeroinfunppfi->matchInt.absiwith|0->zeroppf0|1->oneppf1|n->otherppfiletordinal=letoneppfi=intppfi;stringppf"st"inlettwoppfi=intppfi;stringppf"nd"inletthreeppfi=intppfi;stringppf"rd"inletotherppfi=intppfi;stringppf"th"infun?zero?(one=one)?(two=two)?(three=three)?(other=other)()->letzero=Option.value~default:otherzeroinfunppfi->ifi=0thenzeroppfielseletn=Int.absiinletmod10=nmod10inletmod100=nmod100inifmod10=1&&mod100<>11thenoneppfielseifmod10=2&&mod100<>12thentwoppfielseifmod10=3&&mod100<>13thenthreeppfielseotherppfi(* Conditional UTF-8 and styled formatting. *)moduleImap=Map.Make(Int)type'aattr=int*('a->string)*(string->'a)letid=ref0letattr(typea)encdec=incrid;(!id,enc,dec)typeFormat.stag+=|Fmt_store_get:'aattr->Format.stag|Fmt_store_set:'aattr*'a->Format.stagletstore()=lets=refImap.emptyinfun~other->function|Fmt_store_get(id,_,_)->Option.value~default:""(Imap.find_optid!s)|Fmt_store_set((id,enc,_),v)->s:=Imap.addid(encv)!s;"ok"|stag->otherstagletsetup_storeppf=letfuns=Format.pp_get_formatter_stag_functionsppf()inletmark_open_stag=store()~other:funs.mark_open_staginFormat.pp_set_formatter_stag_functionsppf{funswithmark_open_stag}letstore_opopppf=letfuns=Format.pp_get_formatter_stag_functionsppf()infuns.mark_open_stagopletget(_,_,decasattr)ppf=matchstore_op(Fmt_store_getattr)ppfwith|""->None|s->Some(decs)letrecsetattrvppf=matchstore_op(Fmt_store_set(attr,v))ppfwith|"ok"->()|_->setup_storeppf;setattrvppfletdefx=functionSomey->y|_->xletutf_8_attr=letenc=functiontrue->"t"|false->"f"inletdec=function"t"->true|"f"->false|_->assertfalseinattrencdecletutf_8ppf=getutf_8_attrppf|>deftrueletset_utf_8ppfx=setutf_8_attrxppftypestyle_renderer=[`Ansi_tty|`None]letstyle_renderer_attr=letenc=function`Ansi_tty->"A"|`None->"N"inletdec=function"A"->`Ansi_tty|"N"->`None|_->assertfalseinattrencdecletstyle_rendererppf=getstyle_renderer_attrppf|>def`Noneletset_style_rendererppfx=setstyle_renderer_attrxppfletwith_buffer?likebuf=letppf=Format.formatter_of_bufferbufin(* N.B. this does slighty more it also makes buf use other installed
semantic tag actions. *)matchlikewith|None->ppf|Somelike->letfuns=Format.pp_get_formatter_stag_functionslike()inFormat.pp_set_formatter_stag_functionsppffuns;ppfletstr_likeppffmt=letbuf=Buffer.create64inletbppf=with_buffer~like:ppfbufinletflushppf=Format.pp_print_flushppf();lets=Buffer.contentsbufinBuffer.resetbuf;sinFormat.kfprintfflushbppffmt(* Conditional UTF-8 formatting *)letif_utf_8pp_upp=funppfv->(ifutf_8ppfthenpp_uelsepp)ppfv(* Styled formatting *)typecolor=[`Black|`Blue|`Cyan|`Green|`Magenta|`Red|`White|`Yellow]typestyle=[`None|`Bold|`Faint|`Italic|`Underline|`Reverse|`Fgof[color|`Hiofcolor]|`Bgof[color|`Hiofcolor]|color(** deprecated *)]letansi_style_code=function|`Bold->"1"|`Faint->"2"|`Italic->"3"|`Underline->"4"|`Reverse->"7"|`Fg`Black->"30"|`Fg`Red->"31"|`Fg`Green->"32"|`Fg`Yellow->"33"|`Fg`Blue->"34"|`Fg`Magenta->"35"|`Fg`Cyan->"36"|`Fg`White->"37"|`Bg`Black->"40"|`Bg`Red->"41"|`Bg`Green->"42"|`Bg`Yellow->"43"|`Bg`Blue->"44"|`Bg`Magenta->"45"|`Bg`Cyan->"46"|`Bg`White->"47"|`Fg(`Hi`Black)->"90"|`Fg(`Hi`Red)->"91"|`Fg(`Hi`Green)->"92"|`Fg(`Hi`Yellow)->"93"|`Fg(`Hi`Blue)->"94"|`Fg(`Hi`Magenta)->"95"|`Fg(`Hi`Cyan)->"96"|`Fg(`Hi`White)->"97"|`Bg(`Hi`Black)->"100"|`Bg(`Hi`Red)->"101"|`Bg(`Hi`Green)->"102"|`Bg(`Hi`Yellow)->"103"|`Bg(`Hi`Blue)->"104"|`Bg(`Hi`Magenta)->"105"|`Bg(`Hi`Cyan)->"106"|`Bg(`Hi`White)->"107"|`None->"0"(* deprecated *)|`Black->"30"|`Red->"31"|`Green->"32"|`Yellow->"33"|`Blue->"34"|`Magenta->"35"|`Cyan->"36"|`White->"37"letpp_sgrppfstyle=Format.pp_print_asppf0"\027[";Format.pp_print_asppf0style;Format.pp_print_asppf0"m"letcurr_style=attrFun.idFun.idletstyledstylepp_vppfv=matchstyle_rendererppfwith|`None->pp_vppfv|`Ansi_tty->letprev=matchgetcurr_styleppfwith|None->letzero="0"insetcurr_stylezeroppf;zero|Somes->sinlethere=ansi_style_codestyleinletcurr=matchstylewith|`None->here|_->String.concat";"[prev;here]inletfinally()=setcurr_styleprevppfinsetcurr_stylecurrppf;Fun.protect~finally@@fun()->pp_sgrppfhere;pp_vppfv;pp_sgrppfprev(* Records *)letid=Fun.idletlabel=styled(`Fg`Yellow)stringletfield?(label=label)?(sep=any":@ ")lprjpp_vppfv=pfppf"@[<1>%a%a%a@]"labellsep()pp_v(prjv)letrecord?(sep=cut)pps=vbox(concat~seppps)(* Converting with string converters. *)letof_to_stringfppfv=stringppf(fv)letto_to_stringpp_vv=str"%a"pp_vv(* Deprecated *)letstrf=strletkstrf=kstrletstrf_like=str_likeletalways=anyletunit=anyletprefixpp_ppp_vppfv=pp_pppf();pp_vppfvletsuffixpp_spp_vppfv=pp_vppfv;pp_sppf()letstyled_unitstylefmt=styledstyle(anyfmt)