Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file batPrintf.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521(*
* BatPrintf - Extended Printf module
* Copyright (C) 2008 David Teller (contributor)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version,
* with the special exception on linking described in file LICENSE.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*)(**
{6 Printf}
A reimplementation of Printf (with a few additional functions) based
on [output]. We provide an internal signature to limit the dangers
of {!Obj.magic}.
{b Note} this module is inlined because of circular dependencies (themselves
caused by the legacy definition of a function {!printf} in module {!BatIO}).
*)openBatInnerIOexternalformat_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"moduleSformat=structtypeindex;;externalunsafe_index_of_int:int->index="%identity";;letindex_of_inti=ifi>=0thenunsafe_index_of_intielsefailwith("index_of_int: negative argument "^string_of_inti);;externalint_of_index:index->int="%identity";;letadd_int_indexiidx=index_of_int(i+int_of_index idx);;letsucc_index=add_int_index1;;letlengthfmt=String.length(string_of_formatfmt)letgetfmti=String.get(string_of_format fmt)iletunsafe_getfmt i=String.unsafe_get(string_of_formatfmt)iletunsafe_to_string=string_of_formatletsubfmtidxlen=String.sub(unsafe_to_stringfmt)(int_of_indexidx)len;;letto_stringfmt=subfmt(unsafe_index_of_int 0)(lengthfmt);;end;;letbad_conversionsfmtic=invalid_arg("printf: bad conversion %"^String.make1c^", at char number "^string_of_inti^" in format string ``"^sfmt^"''");;letbad_conversion_formatfmtic=bad_conversion(Sformat.to_stringfmt)ic;;letincomplete_formatfmt=invalid_arg("printf: premature end of format string ``" ^Sformat.to_stringfmt^"''");;(* Parses a string conversion to return the specified length and the padding direction. *)letparse_string_conversionsfmt=letrecparsenegi=ifi>=String.lengthsfmtthen(0,neg)elsematchString.unsafe_getsfmtiwith|'1'..'9'->(int_of_string(String.subsfmti(String.lengthsfmt-i-1)),neg)|'-'->parsetrue(succi)|_->parseneg(succi)intryparsefalse1withFailure_->bad_conversionsfmt0's'(* Pad a (sub) string into a blank string of length [p],
on the right if [neg] is true, on the left otherwise. *)letpad_stringpad_charpnegsilen=ifp=len&&i=0thenselseifp<=lenthenString.subsilen elseletres=Bytes.makeppad_charinifnegthenBytes.blit_stringsires0lenelseBytes.blit_stringsires(p-len)len;Bytes.unsafe_to_stringres(* Format a string given a %s format, e.g. %40s or %-20s.
To do: ignore other flags (#, +, etc)? *)letformat_stringsfmts=let(p,neg)=parse_string_conversion sfmtinpad_string' 'pnegs0(String.lengths);;(* Extract a format string out of [fmt] between [start] and [stop] inclusive.
'*' in the format are replaced by integers taken from the [widths] list.
extract_format returns a string. *)letextract_formatfmtstartstopwidths=letstart=succstart inletb=Buffer.create (stop-start+10)inBuffer.add_char b'%';letrecfill_format iwidths=ifi<=stopthenmatch(Sformat.unsafe_getfmti,widths)with|('*',h::t)->Buffer.add_stringb(string_of_inth);leti=succiinfill_formatit|('*',[])->assertfalse(* should not happen *)|(c,_)->Buffer.add_charbc;fill_format(succi)widthsinfill_formatstart(List.revwidths);Buffer.contents b;;letextract_format_intconvfmtstartstopwidths =letsfmt=extract_formatfmtstartstopwidthsinmatchconvwith|'n'|'N'->letsfmt=Bytes.of_stringsfmtinBytes.setsfmt(Bytes.lengthsfmt-1)'u';Bytes.unsafe_to_string sfmt|_->sfmt;;(* Returns the position of the next character following the meta format
string, starting from position [i], inside a given format [fmt].
According to the character [conv], the meta format string is
enclosed by the delimitors %{ and %} (when [conv = '{']) or %( and
%) (when [conv = '(']). Hence, [sub_format] returns the index of
the character following the [')'] or ['}'] that ends the meta format,
according to the character [conv]. *)letsub_formatincomplete_formatbad_conversion_formatconvfmti=letlen=Sformat.lengthfmtinletrecsub_fmtci=letclose=ifc='('then')'else(* '{' *)'}'inletrecsubj=ifj>=lenthenincomplete_formatfmtelsematchSformat.getfmtjwith|'%'->sub_sub(succj)|_-> sub(succj)andsub_subj=ifj>=lenthenincomplete_formatfmtelsematchSformat.getfmtjwith|'('|'{'asc->letj=sub_fmtc(succj)insub(succj)|'}'|')'asc->ifc=closethensuccjelsebad_conversion_formatfmtic|_->sub(succj)insubiinsub_fmt convi;;letsub_format_for_printfconv=sub_formatincomplete_formatbad_conversion_formatconv;;letiter_on_format_argsfmtadd_convadd_char=letlim=Sformat.lengthfmt-1inletrecscan_flagsskipi=ifi>limthenincomplete_formatfmtelsematchSformat.unsafe_getfmtiwith|'*'->scan_flagsskip(add_convskipi'i')|'#'|'-'|' '|'+'->scan_flagsskip(succi)|'_'->scan_flagstrue(succi)|'0'..'9'|'.'->scan_flagsskip(succi)|_->scan_convskipiandscan_convskipi=ifi>limthenincomplete_formatfmtelsematchSformat.unsafe_getfmtiwith|'%'|'!'|','->succi|'s'|'S'|'['->add_convskipi's'|'c'|'C'->add_convskipi'c'|'d'|'i'|'o'|'u'|'x'|'X'|'N'->add_convskipi'i'|'f'|'e'|'E'|'g'|'G'|'F'->add_convskipi'f'|'B'|'b'->add_convskipi'B'|'a'|'r'|'t'asconv->add_convskipiconv|'l'|'n'|'L' asconv->letj=succ iinifj>limthenadd_convskipi'i' elsebeginmatchSformat.getfmtjwith|'d'|'i'|'o'|'u'|'x'|'X'->add_char(add_convskipiconv)'i'|_c->add_convskipi'i'end|'{'asconv->(* Just get a regular argument, skipping the specification. *)leti=add_convskipiconvin(* Togo on, find the index of the next char after the meta format. *)letj=sub_format_for_printfconvfmtiin(* Add themeta specification to the summary anyway. *)letrecloopi=ifi<j-2thenloop(add_chari(Sformat.getfmti))inloopi;(* Go on, starting at the closing brace to properly close the meta
specification in the summary. *)scan_convskip(j-1)|'(' asconv->(* Use the static format argument specification instead of
the runtime format argument value: they must have the same type
anyway. *)scan_fmt(add_convskipiconv)|'}' |')'asconv->add_convskipiconv|conv ->bad_conversion_formatfmticonvandscan_fmti=ifi<limthenifSformat.getfmti='%'thenscan_fmt(scan_flagsfalse(succi))elsescan_fmt(succi)elseiinignore (scan_fmt0);;(* Returns a string that summarizes the typing information that a given
format string contains.
For instance, [summarize_format_type "A number %d\n"] is "%i".
It also checks the well-formedness of the format string. *)letsummarize_format_typefmt=letlen=Sformat.lengthfmtinletb=Buffer.create leninletadd_charic=Buffer.add_charbc;succiinletadd_convskipic=ifskipthenBuffer.add_stringb"%_"elseBuffer.add_charb'%';add_chariciniter_on_format_argsfmtadd_convadd_char;Buffer.contents b;;moduleAc=structtypeac={mutableac_rglr:int;mutableac_skip:int;mutableac_rdrs:int;}end;;openAc;;(* Computes the number of arguments of a format (including flag
arguments if any). *)letac_of_formatfmt=letac={ac_rglr=0;ac_skip=0;ac_rdrs=0;}inletincr_acskipc=letinc=ifc='a'then2else 1inifc='r'thenac.ac_rdrs<-ac.ac_rdrs+1;ifskipthenac.ac_skip<-ac.ac_skip+incelseac.ac_rglr<-ac.ac_rglr+incinletadd_convskipic=(* Justfinishing a meta format: no additional argument to record. *)ifc<>')'&&c<>'}'thenincr_ac skipc;succiandadd_chari_c=succiiniter_on_format_args fmtadd_convadd_char;ac;;letcount_arguments_of_formatfmt=letac=ac_of_formatfmtinac.ac_rglr+ac.ac_skip+ac.ac_rdrs;;letlist_iter_ifl=letrecloopi=function|[]->()|[x]->fix(* Tail calling [f]*)|x::xs->fix;loop(succi)xsinloop0l;;(* ``Abstracting''version of kprintf: returns a (curried) function that
will print when totally applied.
Note: in the following, we are careful not to be badly caught
by the compiler optimizations on the representation of arrays. *)letkaprkprfmt=matchcount_arguments_of_formatfmtwith|0->kprfmt[||]|1->Obj.magic(funx->let a=Array.make1(Obj.repr0)ina.(0)<-x;kprfmta)|2->Obj.magic (funxy->leta=Array.make2(Obj.repr0)ina.(0)<-x;a.(1)<-y;kprfmta)|3->Obj.magic (funxyz->leta=Array.make3(Obj.repr0)ina.(0)<-x;a.(1)<-y;a.(2)<-z;kprfmta)|4->Obj.magic (funxyzt->leta=Array.make4(Obj.repr0)ina.(0)<-x;a.(1)<-y;a.(2)<-z;a.(3)<-t;kprfmta)|5->Obj.magic (funxyztu->leta=Array.make5(Obj.repr0)ina.(0)<-x;a.(1)<-y;a.(2)<-z;a.(3)<-t;a.(4)<-u;kprfmta)|6->Obj.magic (funxyztuv->leta=Array.make6(Obj.repr0)ina.(0)<-x;a.(1)<-y;a.(2)<-z;a.(3)<-t;a.(4)<-u;a.(5)<-v;kprfmta)|nargs->letrecloopiargs=ifi>=nargsthenleta=Array.makenargs(Obj.repr0)inlist_iter_i(funiarg->a.(nargs-i-1)<- arg)args;kprfmtaelseObj.magic(funx->loop(succi)(x::args))inloop0[];;(* Get the index of the next argument to printf. *)letnext_indexn=Sformat.succ_indexn;;(* Decode a formatstring and act on it.
[fmt] is the printf format string, and [pos] points to a [%] character.
After consuming the appropriate number of arguments and formatting
them, one of the five continuations is called:
[cont_s] for outputting a string (args: arg num, string, next pos)
[cont_a] for performing a %a action (args: arg num, fn, arg, next pos)
[cont_t] for performing a %t action (args: arg num, fn, next pos)
[cont_f] for performing a flush action (args: arg num, next pos)
[cont_m] for performing a %( action (args: arg num, sfmt, next pos)
"arg num" is the index in array args of the next argument to printf.
"next pos" is the position in [fmt] of the first character following
the %conversion specification in [fmt]. *)(* Note: here, rather than test explicitly against [Sformat.length fmt]
to detect the end of the format, we use [Sformat.unsafe_get] and
rely on the fact that we'll get a "nul" character if we access
one past the end of the string. These "nul" characters are then
caught by the [_ -> bad_conversion] clauses below.
Don't do this at home, kids. *)letscan_formatfmtargsnposcont_scont_acont_tcont_fcont_m=letget_argn=Obj.magic(args.(Sformat.int_of_indexn))inletrecscan_flagsnwidthsi=matchSformat.unsafe_getfmtiwith|'*'->let(width:int)=get_argninscan_flags(next_indexn)(width::widths)(succi)|'0'..'9'|'.'|'#'|'-'|' '|'+'->scan_flagsnwidths(succi)|_->scan_convnwidthsiandscan_conv nwidths i=matchSformat.unsafe_getfmtiwith|'%'->cont_sn"%"(succi)|'s'|'S'asconv->let(x:string)=get_argninletx=ifconv='s'thenxelse"\""^String.escapedx^"\""inlets=(* optimizefor common case %s *)ifi=succposthenxelseformat_string(extract_formatfmtposiwidths)xincont_s (next_index n)s(succi)|'c'|'C'asconv->let(x:char)=get_argninlets=ifconv='c'thenString.make1xelse"'"^Char.escapedx^"'"incont_s(next_indexn)s(succi)|'d'|'i'|'o'|'u'|'x'|'X'|'N'asconv->let(x:int)=get_argninlets=format_int(extract_format_intconvfmtposiwidths)xincont_s (next_index n)s(succi)|'f'|'e'|'E'|'g'|'G'->let(x:float)=get_argninlets=format_float (extract_formatfmtposiwidths)xincont_s (next_index n)s(succi)|'F'->let(x:float)=get_argnincont_s(next_indexn)(string_of_floatx)(succi)|'B'|'b'->let(x:bool)=get_argnincont_s(next_indexn)(string_of_boolx)(succi)|'a'->letprinter=get_argninlet n=Sformat.succ_indexninletarg=get_argnincont_a (next_index n)printerarg(succi)|'t'->letprinter=get_argnincont_t (next_index n)printer(succi)|'l'|'n' |'L'asconv->beginmatchSformat.unsafe_getfmt(succi)with|'d'|'i'|'o'|'u'|'x'|'X'->leti=succiinlets=matchconvwith|'l'->let(x:int32)=get_argninformat_int32 (extract_formatfmtposiwidths)x|'n'->let(x:nativeint)=get_argninformat_nativeint (extract_formatfmtposiwidths)x|_->let(x:int64)=get_argninformat_int64 (extract_formatfmtposiwidths)xincont_s(next_indexn)s(succi)|_->let(x:int)=get_argninlets=format_int(extract_format_int'n'fmtposiwidths)xincont_s(next_indexn)s(succi)end|','->cont_sn""(succi)|'!'->cont_fn(succi)|'{'|'('asconv(* ')' '}' *)->let(xf:('a,'b,'c,'d,'e,'f)format6)=get_argninleti=succiinletj=sub_format_for_printf convfmtiinifconv='{'(* '}' *)then(* Just print the format argument as a specification. *)cont_s(next_indexn)(summarize_format_typexf)jelse(* Use the format argument instead of the format specification. *)cont_m(next_indexn)xfj|(* '(' *)')'->cont_sn""(succi)|conv->bad_conversion_formatfmticonvinscan_flagsn[](succpos);;(*Trimmed-down version of the legacy lib's [mkprintf]. Most of the generality
is lifted to [output] rather than [mkprintf] itself.*)letmkprintfkoutfmt=letrecprknfmt v=letlen=Sformat.lengthfmtinletrecdoprnni=ifi>=lenthenObj.magic(kout)elsematchSformat.unsafe_getfmtiwith|'%'->scan_formatfmtvnicont_scont_acont_tcont_fcont_m|c->writeoutc;doprnn(succi)andcont_snsi=nwriteouts;doprnniandcont_anprinterargi=printeroutarg;doprnniand cont_tnprinteri=printer out;doprnniand cont_fni=flushout;doprnniand cont_mnxfi=letm=Sformat.add_int_index(count_arguments_of_formatxf)ninpr(Obj.magic(fun_->doprn mi))nxfvindoprnn0inletkpr=prk(Sformat.index_of_int0)inkaprkprfmt;;externalidentity:'a->'a="%identity"(*Inlined from [Std] to avoid cyclic dependencies*)letfprintfoutfmt=mkprintfignoreoutfmtletprintffmt=fprintfstdoutfmtleteprintffmt=fprintfstderrfmtletifprintf_fmt=fprintfstdnullfmtletksprintf2kfmt=letout=output_string ()inmkprintf(funout->k(close_outout))outfmtletkbprintf2 kbuffmt =letout=BatBuffer.output_bufferbufinmkprintf(fun_out->kbuf)outfmtletsprintf2fmt=ksprintf2(identity)fmtletbprintf2buffmt=kbprintf2ignorebuffmt(*
Other possible implementation of [sprintf2],
left as example:
[
let sprintf2 fmt =
let out = output_string () in
mkprintf (fun out -> close_out out) out fmt
]
*)(*
Other possible implementation of [bprintf2],
left as example:
[
let bprintf2 buf fmt =
let out = output_buffer buf in
mkprintf ignore out fmt
]*)type('a,'b,'c)t=('a,'b,'c)formatletkfprintf=mkprintfletbprintf=Printf.bprintfletsprintf=Printf.sprintfletksprintf=Printf.ksprintfletkbprintf=Printf.kbprintfletkprintf=Printf.kprintf##V<4.2##moduleCamlinternalPr =Printf.CamlinternalPr