123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684openFormat(** Shadow map and split with tailrecursive variants. *)moduleList=structincludeList(** Tail recursive of map *)letmapfl=List.rev_mapfl|>List.rev(** Tail recursive version of split *)letrev_splitl=letrecinnerxsys=function|(x,y)::xys->inner(x::xs)(y::ys)xys|[]->(xs,ys)ininner[][]lletsplitl=rev_split(List.revl)endtypewrap=[|`Wrap_atoms|`Always_wrap|`Never_wrap|`Force_breaks|`Force_breaks_rec|`No_breaks]typelabel_break=[|`Auto|`Always|`Always_rec|`Never]typestyle_name=stringtypestyle={tag_open:string;tag_close:string}typeatom_param={atom_style:style_nameoption;}letatom={atom_style=None}typelist_param={space_after_opening:bool;space_after_separator:bool;space_before_separator:bool;separators_stick_left:bool;space_before_closing:bool;stick_to_label:bool;align_closing:bool;wrap_body:wrap;indent_body:int;list_style:style_nameoption;opening_style:style_nameoption;body_style:style_nameoption;separator_style:style_nameoption;closing_style:style_nameoption;}letlist={space_after_opening=true;space_after_separator=true;space_before_separator=false;separators_stick_left=true;space_before_closing=true;stick_to_label=true;align_closing=true;wrap_body=`Wrap_atoms;indent_body=2;list_style=None;opening_style=None;body_style=None;separator_style=None;closing_style=None;}typelabel_param={label_break:label_break;space_after_label:bool;indent_after_label:int;label_style:style_nameoption;}letlabel={label_break=`Auto;space_after_label=true;indent_after_label=2;label_style=None;}typet=Atomofstring*atom_param|Listof(string*string*string*list_param)*tlist|Labelof(t*label_param)*t|Customof(formatter->unit)typeescape=[`None|`Escapeof((string->int->int->unit)->string->int->int->unit)|`Escape_stringof(string->string)]typestyles=(style_name*style)list(*
Transform a tree starting from the leaves, propagating and merging
accumulators until reaching the root.
*)letpropagate_from_leaf_to_root~init_acc(* create initial accumulator for a leaf *)~merge_acc(* merge two accumulators coming from child nodes *)~map_node(* (node, acc) -> (node, acc) *)x=letrecauxx=matchxwith|Atom_->letacc=init_accxinmap_nodexacc|List(param,children)->letnew_children,accs=List.rev_split(List.rev_mapauxchildren)inletacc=List.fold_leftmerge_acc(init_accx)accsinmap_node(List(param,new_children))acc|Label((x1,param),x2)->letacc0=init_accxinletnew_x1,acc1=auxx1inletnew_x2,acc2=auxx2inletacc=merge_acc(merge_accacc0acc1)acc2inmap_node(Label((new_x1,param),new_x2))acc|Custom_->letacc=init_accxinmap_nodexaccinauxx(*
Convert wrappable lists into vertical lists if any of their descendants
has the attribute wrap_body = `Force_breaks_rec.
*)letpropagate_forced_breaksx=(* acc = whether to force breaks in wrappable lists or labels *)letinit_acc=function|List((_,_,_,{wrap_body=`Force_breaks_rec}),_)|Label((_,{label_break=`Always_rec}),_)->true|Atom_|Label_|Custom_|List_->falseinletmerge_accforce_breaks1force_breaks2=force_breaks1||force_breaks2inletmap_nodexforce_breaks=matchxwith|List((_,_,_,{wrap_body=`Force_breaks_rec}),_)->x,true|List((_,_,_,{wrap_body=`Force_breaks}),_)->x,force_breaks|List((op,sep,cl,({wrap_body=(`Wrap_atoms|`Never_wrap|`Always_wrap)}asp)),children)->ifforce_breaksthenletp={pwithwrap_body=`Force_breaks}inList((op,sep,cl,p),children),trueelsex,false|Label((a,({label_break=`Auto}aslp)),b)->ifforce_breaksthenletlp={lpwithlabel_break=`Always}inLabel((a,lp),b),trueelsex,false|List((_,_,_,{wrap_body=`No_breaks}),_)|Label((_,{label_break=(`Always|`Always_rec|`Never)}),_)|Atom_|Custom_->x,force_breaksinletnew_x,forced_breaks=propagate_from_leaf_to_root~init_acc~merge_acc~map_nodexinnew_xmodulePretty=struct(*
Rewrite the tree to be printed.
Currently, this is used only to handle `Force_breaks_rec.
*)letrewritex=propagate_forced_breaksx(*
Relies on the fact that mark_open_tag and mark_close_tag
are called exactly once before calling pp_output_string once.
It's a reasonable assumption although not guaranteed by the
documentation of the Format module.
*)letset_escapefmtescape=letprint0,flush0=pp_get_formatter_output_functionsfmt()inlettagf0=(pp_get_formatter_tag_functions[@warning"-3"])fmt()inletis_tag=reffalseinletmottag=is_tag:=true;tagf0.mark_open_tagtaginletmcttag=is_tag:=true;tagf0.mark_close_tagtaginletprintspn=if!is_tagthen(print0spn;is_tag:=false)elseescapeprint0spninlettagf={tagf0withmark_open_tag=mot;mark_close_tag=mct}inpp_set_formatter_output_functionsfmtprintflush0;(pp_set_formatter_tag_functions[@warning"-3"])fmttagfletset_escape_stringfmtesc=letescapeprintspn=lets0=String.subspninlets1=escs0inprints10(String.lengths1)inset_escapefmtescapeletdefine_stylesfmtescapel=ifl<>[]then(pp_set_tagsfmttrue;lettbl1=Hashtbl.create(2*List.lengthl)inlettbl2=Hashtbl.create(2*List.lengthl)inList.iter(fun(style_name,style)->Hashtbl.addtbl1style_namestyle.tag_open;Hashtbl.addtbl2style_namestyle.tag_close)l;letmark_open_tagstyle_name=tryHashtbl.findtbl1style_namewithNot_found->""inletmark_close_tagstyle_name=tryHashtbl.findtbl2style_namewithNot_found->""inlettagf={((pp_get_formatter_tag_functions[@warning"-3"])fmt())withmark_open_tag=mark_open_tag;mark_close_tag=mark_close_tag}in(pp_set_formatter_tag_functions[@warning"-3"])fmttagf);(matchescapewith`None->()|`Escapeesc->set_escapefmtesc|`Escape_stringesc->set_escape_stringfmtesc)letpp_open_xboxfmtpindent=matchp.wrap_bodywith`Always_wrap|`Never_wrap|`Wrap_atoms->pp_open_hvboxfmtindent|`Force_breaks|`Force_breaks_rec->pp_open_vboxfmtindent|`No_breaks->pp_open_hboxfmt()letextra_boxpl=letwrap=matchp.wrap_bodywith`Always_wrap->true|`Never_wrap|`Force_breaks|`Force_breaks_rec|`No_breaks->false|`Wrap_atoms->List.for_all(functionAtom_->true|_->false)linifwrapthen((funfmt->pp_open_hovboxfmt0),(funfmt->pp_close_boxfmt()))else((funfmt->()),(funfmt->()))letpp_open_nonaligned_boxfmtpindentl=matchp.wrap_bodywith`Always_wrap->pp_open_hovboxfmtindent|`Never_wrap->pp_open_hvboxfmtindent|`Wrap_atoms->ifList.for_all(functionAtom_->true|_->false)lthenpp_open_hovboxfmtindentelsepp_open_hvboxfmtindent|`Force_breaks|`Force_breaks_rec->pp_open_vboxfmtindent|`No_breaks->pp_open_hboxfmt()letopen_tagfmt=functionNone->()|Somes->(pp_open_tag[@warning"-3"])fmtsletclose_tagfmt=functionNone->()|Some_->(pp_close_tag[@warning"-3"])fmt()lettag_stringfmtos=matchowithNone->pp_print_stringfmts|Sometag->(pp_open_tag[@warning"-3"])fmttag;pp_print_stringfmts;(pp_close_tag[@warning"-3"])fmt()letrecfprint_tfmt=functionAtom(s,p)->tag_stringfmtp.atom_styles;|List((_,_,_,p)asparam,l)->open_tagfmtp.list_style;ifp.align_closingthenfprint_listfmtNoneparamlelsefprint_list2fmtparaml;close_tagfmtp.list_style|Label(label,x)->fprint_pairfmtlabelx|Customf->ffmtandfprint_list_body_stick_leftfmtpsephdtl=open_tagfmtp.body_style;fprint_tfmthd;List.iter(funx->ifp.space_before_separatorthenpp_print_stringfmt" ";tag_stringfmtp.separator_stylesep;ifp.space_after_separatorthenpp_print_spacefmt()elsepp_print_cutfmt();fprint_tfmtx)tl;close_tagfmtp.body_styleandfprint_list_body_stick_rightfmtpsephdtl=open_tagfmtp.body_style;fprint_tfmthd;List.iter(funx->ifp.space_before_separatorthenpp_print_spacefmt()elsepp_print_cutfmt();tag_stringfmtp.separator_stylesep;ifp.space_after_separatorthenpp_print_stringfmt" ";fprint_tfmtx)tl;close_tagfmtp.body_styleandfprint_opt_labelfmt=functionNone->()|Some(lab,lp)->open_tagfmtlp.label_style;fprint_tfmtlab;close_tagfmtlp.label_style;iflp.space_after_labelthenpp_print_stringfmt" "(* Either horizontal or vertical list *)andfprint_listfmtlabel((op,sep,cl,p)asparam)=function[]->fprint_opt_labelfmtlabel;tag_stringfmtp.opening_styleop;ifp.space_after_opening||p.space_before_closingthenpp_print_stringfmt" ";tag_stringfmtp.closing_stylecl|hd::tlasl->iftl=[]||p.separators_stick_leftthenfprint_list_stick_leftfmtlabelparamhdtllelsefprint_list_stick_rightfmtlabelparamhdtllandfprint_list_stick_leftfmtlabel(op,sep,cl,p)hdtll=letindent=p.indent_bodyinpp_open_xboxfmtpindent;fprint_opt_labelfmtlabel;tag_stringfmtp.opening_styleop;ifp.space_after_openingthenpp_print_spacefmt()elsepp_print_cutfmt();letopen_extra,close_extra=extra_boxplinopen_extrafmt;fprint_list_body_stick_leftfmtpsephdtl;close_extrafmt;ifp.space_before_closingthenpp_print_breakfmt1(-indent)elsepp_print_breakfmt0(-indent);tag_stringfmtp.closing_stylecl;pp_close_boxfmt()andfprint_list_stick_rightfmtlabel(op,sep,cl,p)hdtll=letbase_indent=p.indent_bodyinletsep_indent=String.lengthsep+(ifp.space_after_separatorthen1else0)inletindent=base_indent+sep_indentinpp_open_xboxfmtpindent;fprint_opt_labelfmtlabel;tag_stringfmtp.opening_styleop;ifp.space_after_openingthenpp_print_spacefmt()elsepp_print_cutfmt();letopen_extra,close_extra=extra_boxplinopen_extrafmt;fprint_tfmthd;List.iter(funx->ifp.space_before_separatorthenpp_print_breakfmt1(-sep_indent)elsepp_print_breakfmt0(-sep_indent);tag_stringfmtp.separator_stylesep;ifp.space_after_separatorthenpp_print_stringfmt" ";fprint_tfmtx)tl;close_extrafmt;ifp.space_before_closingthenpp_print_breakfmt1(-indent)elsepp_print_breakfmt0(-indent);tag_stringfmtp.closing_stylecl;pp_close_boxfmt()(* align_closing = false *)andfprint_list2fmt(op,sep,cl,p)=function[]->tag_stringfmtp.opening_styleop;ifp.space_after_opening||p.space_before_closingthenpp_print_stringfmt" ";tag_stringfmtp.closing_stylecl|hd::tlasl->tag_stringfmtp.opening_styleop;ifp.space_after_openingthenpp_print_stringfmt" ";pp_open_nonaligned_boxfmtp0l;ifp.separators_stick_leftthenfprint_list_body_stick_leftfmtpsephdtlelsefprint_list_body_stick_rightfmtpsephdtl;pp_close_boxfmt();ifp.space_before_closingthenpp_print_stringfmt" ";tag_stringfmtp.closing_stylecl(* Printing a label:value pair.
The opening bracket stays on the same line as the key, no matter what,
and the closing bracket is either on the same line
or vertically aligned with the beginning of the key.
*)andfprint_pairfmt((lab,lp)aslabel)x=matchxwithList((op,sep,cl,p),l)whenp.stick_to_label&&p.align_closing->fprint_listfmt(Somelabel)(op,sep,cl,p)l|_->letindent=lp.indent_after_labelinpp_open_hvboxfmt0;open_tagfmtlp.label_style;fprint_tfmtlab;close_tagfmtlp.label_style;(matchlp.label_breakwith|`Auto->iflp.space_after_labelthenpp_print_breakfmt1indentelsepp_print_breakfmt0indent|`Always|`Always_rec->pp_force_newlinefmt();pp_print_stringfmt(String.makeindent' ')|`Never->iflp.space_after_labelthenpp_print_charfmt' 'else());fprint_tfmtx;pp_close_boxfmt()letto_formatterfmtx=letx=rewritexinfprint_tfmtx;pp_print_flushfmt()letto_buffer?(escape=`None)?(styles=[])bufx=letfmt=Format.formatter_of_bufferbufindefine_stylesfmtescapestyles;to_formatterfmtxletto_string?escape?stylesx=letbuf=Buffer.create500into_buffer?escape?stylesbufx;Buffer.contentsbufletto_channel?(escape=`None)?(styles=[])ocx=letfmt=formatter_of_out_channelocindefine_stylesfmtescapestyles;to_formatterfmtxletto_stdout?escape?stylesx=to_channel?escape?stylesstdoutxletto_stderr?escape?stylesx=to_channel?escape?stylesstderrxendmoduleCompact=structopenPrintfletrecfprint_tbuf=functionAtom(s,_)->Buffer.add_stringbufs|List(param,l)->fprint_listbufparaml|Label(label,x)->fprint_pairbuflabelx|Customf->(* Will most likely not be compact *)letfmt=formatter_of_bufferbufinffmt;pp_print_flushfmt()andfprint_listbuf(op,sep,cl,_)=function[]->bprintfbuf"%s%s"opcl|x::tl->Buffer.add_stringbufop;fprint_tbufx;List.iter(funx->Buffer.add_stringbufsep;fprint_tbufx)tl;Buffer.add_stringbufclandfprint_pairbuf(label,_)x=fprint_tbuflabel;fprint_tbufxletto_bufferbufx=fprint_tbufxletto_stringx=letbuf=Buffer.create500into_bufferbufx;Buffer.contentsbufletto_formatterfmtx=lets=to_stringxinFormat.fprintffmt"%s"s;pp_print_flushfmt()letto_channelocx=letbuf=Buffer.create500into_bufferbufx;Buffer.output_bufferocbufletto_stdoutx=to_channelstdoutxletto_stderrx=to_channelstderrxend(* Obsolete *)moduleParam=structletlist_true={space_after_opening=true;space_after_separator=true;space_before_separator=true;separators_stick_left=true;space_before_closing=true;stick_to_label=true;align_closing=true;wrap_body=`Wrap_atoms;indent_body=2;list_style=None;opening_style=None;body_style=None;separator_style=None;closing_style=None;}letlist_false={space_after_opening=false;space_after_separator=false;space_before_separator=false;separators_stick_left=false;space_before_closing=false;stick_to_label=false;align_closing=false;wrap_body=`Wrap_atoms;indent_body=2;list_style=None;opening_style=None;body_style=None;separator_style=None;closing_style=None;}letlabel_true={label_break=`Auto;space_after_label=true;indent_after_label=2;label_style=None;}letlabel_false={label_break=`Auto;space_after_label=false;indent_after_label=2;label_style=None;}end