123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454(* Julien Verlaguet, Yoann Padioleau
*
* Copyright (C) 2011 Facebook
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation, with the
* special exception on linking described in file license.txt.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file
* license.txt for more details.
*)(*****************************************************************************)(* Prelude *)(*****************************************************************************)(* julien: this is a copy/paste of the original pp.ml.
* It is slightly modified, and I don't know how much these modifications
* affect xhpizer. I hope to be able to merge these two files back together.
*)(*****************************************************************************)(* Types *)(*****************************************************************************)(* we use a backtracking model *)exceptionFailtypeenv={(* the actual printing hook, sometimes temporarily set to do_nothing()
* when trying something before actually printing it. *)print:(string->unit);(* stack of margin, push'ed and pop'ed when processing {} *)mutablemargin:intlist;(* current column *)mutablecmargin:int;(* current line *)mutableline:int;(* depth in the tree of try_ *)mutablelevel:int;(* for the parenthesis automatic insertion *)mutablepriority:int;(* pad: ?? *)mutablelast_nl:bool;mutableemptyl:bool;mutablefailed:bool;mutablepushed:bool;}(*****************************************************************************)(* Helpers *)(*****************************************************************************)letemptyo={print=o;margin=[0];priority=0;cmargin=0;level=0;pushed=false;last_nl=false;emptyl=false;failed=false;line=0;}letdebugenvf=letbuf=Buffer.create256inletenv'={envwithprint=(funs->Buffer.add_stringbufs)}in(tryfenv'with_->());Printf.printf"Debug %s\n"(Buffer.contentsbuf)letdo_nothing_=()(*****************************************************************************)(* Newlines and spaces *)(*****************************************************************************)letprintenvx=(* todo: this is the case right now for comments.
* we should normalize those comments?
* if (String.contains x '\n')
* then failwith (Printf.sprintf "%s contains a newline\n" x);
*)env.last_nl<-false;env.emptyl<-false;env.cmargin<-env.cmargin+String.lengthx;ifenv.cmargin>=80then(* there is nothing to backtrack on, so just print it *)ifenv.level=0thenbeginenv.printx;env.failed<-trueendelseraiseFailelseenv.printxletspacesenv=for_i=1toList.hdenv.margindoprintenv" ";doneletnewlineenv=env.pushed<-false;ifenv.last_nlthenenv.emptyl<-true;env.last_nl<-true;env.cmargin<-0;env.line<-env.line+1;env.print"\n"letnewline_optenv=ifenv.last_nlthen()elsenewlineenvletspace_or_nlenv=ifenv.cmargin<75thenprintenv" "else(newlineenv;spacesenv)(*****************************************************************************)(* Margins *)(*****************************************************************************)letmargin_offset=ref2letpushenv=env.pushed<-true;env.margin<-List.hdenv.margin+!margin_offset::env.marginletpopenv=env.margin<-List.tlenv.marginletnestenvf=pushenv;fenv;popenvletnest_optenvf=ifenv.pushedthenfenvelsebeginpushenv;fenv;popenvendletnestcenvf=env.margin<-env.cmargin::env.margin;fenv;popenvletnest_blockenvf=printenv"{";newlineenv;nestenvf;spacesenv;printenv"}"letnest_block_nlenvf=nest_blockenvf;newlineenv(*****************************************************************************)(* Lists *)(*****************************************************************************)letrecsimpl_listenvfsep=function|[]->()|[x]->fenvx|x::rl->fenvx;printenvsep;simpl_listenvfseprlletreclist_sepenvfsep=function|[]->()|[x]->fenvx|x::rl->fenvx;sepenv;list_sepenvfseprlletflat_listenvfoparlsepcpar=printenvopar;list_sepenvf(funenv->printenvsep;printenv" ")l;printenvcpar(* pad: used to take a last_nl parameter, but it was not used *)letnl_nested_listenvfoparlsepcpar=printenvopar;nestenv(funenv->newlineenv;spacesenv;list_sepenvf(funenv->printenvsep;newlineenv;spacesenv)l;newlineenv;);ifcpar<>""thenbeginspacesenv;printenvcparend(*****************************************************************************)(* Backtracking combinators *)(*****************************************************************************)letfail()=raiseFaillettry_envf=f{envwithprint=do_nothing;level=env.level+1};fenvletchoice_leftenvf1f2=trytry_envf1with|Failwhenenv.level=0->(tryf2envwithFail->assertfalse)(* otherwise, just let the exception bubble up more *)letchoice_rightenvf1f2=trytry_envf1withFail->f2envlettry_hardenvf=tryf{envwithprint=do_nothing;level=1};fenvwithFail->letenv'={envwithfailed=false;print=do_nothing;level=0}infenv';ifenv'.failedthenraiseFailelsef{envwithlevel=0}letcut_listenvfl=List.iter(funx->choice_rightenv(funenv->fenvx)(funenv->newlineenv;spacesenv;fenvx))lletlistenvfoparlsepcpar=letsimple=(funenv->flat_listenvfoparlsepcpar)inletnested=(funenv->nl_nested_listenvfoparlsepcpar)inchoice_rightenvsimplenestedletlist_leftenvfoparlsepcpar=letsimple=(funenv->ifl<>[]thenprintenv" ";flat_listenvfoparlsepcpar)inletnested=(funenv->nl_nested_listenvfoparlsepcpar)inchoice_leftenvsimplenestedletnested_argenvfoparlsepcpar=letrecelt=function|[]->assertfalse|[x]->fenvx;newlineenv;|x::rl->fenvx;printenvsep;newlineenv;spacesenv;eltrlinnestcenv(funenv->printenvopar;nestcenv(fun_env->eltl;);spacesenv;printenvcpar;)letfun_argsenvfoparlsepcpar=letsimple=(funenv->letline=env.lineinflat_listenvfoparlsepcpar;ifline<>env.linethenfail();)inletnl_nested=(funenv->nl_nested_listenvfoparlsepcpar)inchoice_rightenvsimplenl_nestedletnested_listenvfoparlsepcparlast_nl=env.margin<-env.cmargin::env.margin;printenvopar;env.margin<-env.cmargin::env.margin;list_sepenvf(funenv->printenvsep;newlineenv;spacesenv)l;iflast_nlthenbeginprintenvsep;newlineenv;popenv;spacesenv;printenvcpar;endelsebeginprintenvcpar;popenv;end;popenvletfun_paramsenvfl=letopar="("inletsep=","inletcpar=")"inletsimple=(funenv->letline=env.lineinflat_listenvfoparlsepcpar;ifline<>env.linethenfail();)inletnl_nested=(funenv->nested_listenvfoparlsepcpartrue)inchoice_rightenvsimplenl_nested(*****************************************************************************)(* Parenthesis handling *)(*****************************************************************************)letparenprioenvf=letold_prio=env.priorityinenv.priority<-prio;if(prio>=old_prio)||(prio=-1)thenfenvelsebeginprintenv"(";fenv;printenv")";end;env.priority<-old_prio(*****************************************************************************)(* String helpers *)(*****************************************************************************)(* module PpString = struct *)letchar_is_space=function|' '|'\t'|'\n'|'\r'->true|_->falseletis_spacesi=char_is_spaces.[i]letrecis_only_spacesi=ifi>=String.lengthsthentrueelseis_spacesi&&is_only_spaces(i+1)letstrips=letc1=ref0inletc2=ref(String.lengths-1)inwhileis_spaces!c1doincrc1;done;whileis_spaces!c2dodecrc2;done;letc2=String.lengths-1-!c2inString.subs!c1(String.lengths-!c1-c2)letspace=function|' '|'\t'->true|_->falseletrecfind_cutxstarti=ifi<20thenstartelseifspacex.[i]thenielsefind_cutxstart(i-1)letrecstringquotesepenvx=choice_leftenv(funenv->printenvx)(funenv->letsize=80-env.cmargin-String.lengthsep-1inletsize=find_cutxsizesizeinlets=String.subx0sizeinletrest=String.subxsize(String.lengthx-size)inprintenvs;printenvquote;printenvsep;newlineenv;spacesenv;printenvquote;stringquotesepenvrest)letstringquotesepenvx=ifenv.cmargin>=20thenbeginprintenvquote;printenvx;printenvquoteendelsenestcenv(funenv->printenvquote;stringquotesepenvx;printenvquote;)letfirst_char_escapeenvs=ifs=""then0elsematchs.[0]with|'A'..'Z'|'a'..'z'|'&'|' '|'\n'|'<'|'>'->0|_c->printenv"{'";letsize=ref1inwhile!size<String.lengths&¬(char_is_spaces.[!size])doincrsizedone;letsize=!sizeinprintenv(String.subs0size);printenv"'}";ifsize<String.lengthsthenprintenv" ";sizeletprint_textenvs=letsize=ref(String.lengths-1)inwhile!size>=0&&char_is_spaces.[!size]dodecrsize;done;letsize=!sizeinletbuf=Buffer.create80inletlast_is_space=reftrueinnestcenv(funenv->leti=first_char_escapeenvsinfori=itosizedoifCommon2.is_spaces.[i]thenif!last_is_spacethen()elsebeginlast_is_space:=true;printenv(Buffer.contentsbuf);Buffer.clearbuf;space_or_nlenv;endelse(last_is_space:=false;Buffer.add_charbufs.[i])done;printenv(Buffer.contentsbuf);Buffer.clearbuf;)