123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308(**************************************************************************)(* *)(* PPrint *)(* *)(* François Pottier, Inria Paris *)(* Nicolas Pouillard *)(* *)(* Copyright 2007-2019 Inria. All rights reserved. This file is *)(* distributed under the terms of the GNU Library General Public *)(* License, with an exception, as described in the file LICENSE. *)(**************************************************************************)openPPrintEngine(* ------------------------------------------------------------------------- *)(* Predefined single-character documents. *)letlparen=char'('letrparen=char')'letlangle=char'<'letrangle=char'>'letlbrace=char'{'letrbrace=char'}'letlbracket=char'['letrbracket=char']'letsquote=char'\''letdquote=char'"'letbquote=char'`'letsemi=char';'letcolon=char':'letcomma=char','letdot=char'.'letsharp=char'#'letslash=char'/'letbackslash=char'\\'letequals=char'='letqmark=char'?'lettilde=char'~'letat=char'@'letpercent=char'%'letdollar=char'$'letcaret=char'^'letampersand=char'&'letstar=char'*'letplus=char'+'letminus=char'-'letunderscore=char'_'letbang=char'!'letbar=char'|'(* ------------------------------------------------------------------------- *)(* Repetition. *)lettwicedoc=doc^^docletrepeatndoc=letrecloopndocaccu=ifn=0thenaccuelseloop(n-1)doc(doc^^accu)inloopndocempty(* ------------------------------------------------------------------------- *)(* Delimiters. *)letprecedelx=l^^xletterminaterx=x^^rletencloselrx=l^^x^^rletsquotes=enclosesquotesquoteletdquotes=enclosedquotedquoteletbquotes=enclosebquotebquoteletbraces=encloselbracerbraceletparens=encloselparenrparenletangles=encloselanglerangleletbrackets=encloselbracketrbracket(* ------------------------------------------------------------------------- *)(* Some functions on lists. *)(* A variant of [fold_left] that keeps track of the element index. *)letfoldli(f:int->'b->'a->'b)(accu:'b)(xs:'alist):'b=letr=ref0inList.fold_left(funaccux->leti=!rinr:=i+1;fiaccux)accuxs(* ------------------------------------------------------------------------- *)(* Working with lists of documents. *)letconcatdocs=(* We take advantage of the fact that [^^] operates in constant
time, regardless of the size of its arguments. The document
that is constructed is essentially a reversed list (i.e., a
tree that is biased towards the left). This is not a problem;
when pretty-printing this document, the engine will descend
along the left branch, pushing the nodes onto its stack as
it goes down, effectively reversing the list again. *)List.fold_left(^^)emptydocsletseparatesepdocs=foldli(funiaccudoc->ifi=0thendocelseaccu^^sep^^doc)emptydocsletconcat_mapfxs=List.fold_left(funaccux->accu^^fx)emptyxsletseparate_mapsepfxs=foldli(funiaccux->ifi=0thenfxelseaccu^^sep^^fx)emptyxsletseparate2seplast_sepdocs=letn=List.lengthdocsinfoldli(funiaccudoc->ifi=0thendocelseaccu^^(ifi<n-1thensepelselast_sep)^^doc)emptydocsletoptionalf=function|None->empty|Somex->fx(* ------------------------------------------------------------------------- *)(* Text. *)(* This variant of [String.index_from] returns an option. *)letindex_fromsic=trySome(String.index_fromsic)withNot_found->None(* [lines s] chops the string [s] into a list of lines, which are turned
into documents. *)letliness=letrecchopaccui=matchindex_fromsi'\n'with|Somej->letaccu=substringsi(j-i)::accuinchopaccu(j+1)|None->substringsi(String.lengths-i)::accuinList.rev(chop[]0)letarbitrary_strings=separate(break1)(liness)(* [split ok s] splits the string [s] at every occurrence of a character
that satisfies the predicate [ok]. The substrings thus obtained are
turned into documents, and a list of documents is returned. No information
is lost: the concatenation of the documents yields the original string.
This code is not UTF-8 aware. *)letsplitoks=letn=String.lengthsinletrecindex_fromi=ifi=nthenNoneelseifoks.[i]thenSomeielseindex_from(i+1)inletrecchopaccui=matchindex_fromiwith|Somej->letaccu=substringsi(j-i)::accuinletaccu=chars.[j]::accuinchopaccu(j+1)|None->substringsi(String.lengths-i)::accuinList.rev(chop[]0)(* [words s] chops the string [s] into a list of words, which are turned
into documents. *)letwordss=letn=String.lengthsin(* A two-state finite automaton. *)(* In this state, we have skipped at least one blank character. *)letrecskippingaccui=ifi=nthen(* There was whitespace at the end. Drop it. *)accuelsematchs.[i]with|' '|'\t'|'\n'|'\r'->(* Skip more whitespace. *)skippingaccu(i+1)|_->(* Begin a new word. *)wordaccui(i+1)(* In this state, we have skipped at least one non-blank character. *)andwordaccuij=ifj=nthen(* Final word. *)substringsi(j-i)::accuelsematchs.[j]with|' '|'\t'|'\n'|'\r'->(* A new word has been identified. *)letaccu=substringsi(j-i)::accuinskippingaccu(j+1)|_->(* Continue inside the current word. *)wordaccui(j+1)inList.rev(skipping[]0)letflow_mapsepfdocs=foldli(funiaccudoc->ifi=0thenfdocelseaccu^^(* This idiom allows beginning a new line if [doc] does not
fit on the current line. *)group(sep^^fdoc))emptydocsletflowsepdocs=flow_mapsep(funx->x)docsleturls=flow(break0)(split(function'/'|'.'->true|_->false)s)(* ------------------------------------------------------------------------- *)(* Alignment and indentation. *)lethangid=align(nestid)let(!^)=stringlet(^/^)xy=x^^break1^^yletprefixnbxy=group(x^^nestn(breakb^^y))let(^//^)=prefix21letjumpnby=group(nestn(breakb^^y))(* Deprecated.
let ( ^@^ ) x y = group (x ^/^ y)
let ( ^@@^ ) x y = group (nest 2 (x ^/^ y))
*)letinfixnbopxy=prefixnb(x^^blankb^^op)yletsurroundnbopeningcontentsclosing=group(opening^^nestn(breakb^^contents)^^breakb^^closing)letsoft_surroundnbopeningcontentsclosing=group(opening^^nestn(group(breakb)^^contents)^^group(breakb^^closing))letsurround_separatenbvoidopeningsepclosingdocs=matchdocswith|[]->void|_::_->surroundnbopening(separatesepdocs)closingletsurround_separate_mapnbvoidopeningsepclosingfxs=matchxswith|[]->void|_::_->surroundnbopening(separate_mapsepfxs)closing