Library
Module
Module type
Parameter
Class
Class type
The pretty printer and document combinators, parameterized by a cost factory.
module C : Signature.CostFactory
type cost = C.t
The cost
type
Examples in the rest of this section assume that the program begins with
open Pretty_expressive
let cf = Printer.default_cost_factory ~page_width:10 ()
module P = Printer.Make (val cf)
open P
val pretty_print_info :
?init_c:int ->
Signature.renderer ->
doc ->
cost Util.info
pretty_print_info renderer d
prints the document d
by repeatedly calling renderer
and outputs the debugging information as an info
record.The optional ~init_c
can be used to indicate that the printing begins at a non-zero column position.
pretty_format_info
is similar to pretty_print_info
, but it prints to a string instead of a renderer.
val pretty_print : ?init_c:int -> Signature.renderer -> doc -> unit
pretty_print d
is similar to pretty_print_info
without debugging information.
Examples:
# print_string "Languages: ";
pretty_print
print_string
(align (text "Racket" ^^ nl ^^
text "OCaml" ^^ nl ^^
text "Pyret"));;
Languages: Racket
OCaml
Pyret
- : unit = ()
# print_string "Languages: ";
pretty_print
~init_c:11
print_string
(align (text "Racket" ^^ nl ^^
text "OCaml" ^^ nl ^^
text "Pyret"));;
Languages: Racket
OCaml
Pyret
- : unit = ()
val pretty_format : ?init_c:int -> doc -> string
pretty_format
is similar to pretty_format
, without debugging information.
Examples:
# pretty_format (text "Hello World" ^^ nl ^^
text "Hi All!") |> print_endline;;
Hello World
Hi All!
- : unit = ()
val pretty_format_debug : ?init_c:int -> doc -> string
pretty_format_debug
is similar to pretty_format_info
, but the debugging information is included as a part of the output string. The format is customizable via debug_format
.
Examples:
# pretty_format_debug (text "Hello World" ^^ nl ^^
text "Hi All!") |> print_endline;;
1234567890
Hello Worl│d
Hi All! │
is_tainted: false
cost: (1 0 1 0)
- : unit = ()
val text : string -> doc
text s
is a document for textual content s
; s
must not contain a newline.
Examples:
# pretty_print print_string (text "Portal");;
Portal
- : unit = ()
val newline : string option -> doc
a ^^ b
is a document for concatenation of documents a
and b
without alignment. In the paper, the symbol <>
is used for the operator. We use ^^
in the OCaml implementation instead to avoid shadowing the built-in not equal operator. This operator also known as the unaligned concatenation, which is widely used in traditional pretty printers.
See also Printer.MakeCompat
for a functor that provides this operator under the symbol <>
.
Examples:
let left_doc = text "Splatoon" ^^ nl ^^ text "Nier";;
let right_doc = text "Automata" ^^ nl ^^ text "FEZ";;
# pretty_print print_string (left_doc ^^ right_doc);;
Splatoon
NierAutomata
FEZ
- : unit = ()
By "without alignment," we mean that the right document is not treated as as box with a rigid structure. This makes it easy to format code in C-like languages, whose array expression, function call, and curly braces should not be rigid.
a <|> b
is a document for a choice between document a
and b
.
# let print_doc w =
let cf = Printer.default_cost_factory ~page_width:w () in
let module P = Printer.Make (val cf) in
let open P in
pretty_print
print_string
(text "Chrono Trigger" <|>
(text "Octopath" ^^ nl ^^ text "Traveler"));;
val print_doc : int -> unit = <fun>
# print_doc 10;;
Octopath
Traveler
- : unit = ()
# print_doc 15;;
Chrono Trigger
- : unit = ()
See also Best Practice for Document Construction
align d
is a document that aligns d
at the column position.
Examples:
# pretty_print print_string (left_doc ^^ align right_doc);;
Splatoon
NierAutomata
FEZ
- : unit = ()
The aligned concatenation operator (<+>)
is a derived combinator that composes (^^)
and align
together. It is especially useful for languages that uses the the box model for code styling.
nest n d
is a document that increments the indentation level by n
when rendering d
.
Examples:
# pretty_print
print_string
(text "when 1 = 2:" ^^ nest 4 (nl ^^ text "print 'oh no!'"));;
when 1 = 2:
print 'oh no!'
- : unit = ()
The increment does not affect content on the current line. In the following example, when 1 = 2:
is not further indented.
# pretty_print
print_string
(nest 4 (text "when 1 = 2:" ^^ nl ^^ text "print 'oh no!'"));;
when 1 = 2:
print 'oh no!'
- : unit = ()
reset d
is a document that resets indentation level to 0 in d
. This is especially useful for formatting multi-line strings and multi-line comments.
Examples:
# let s_d = reset (text "#<<EOF" ^^ nl ^^
text "Zelda" ^^ nl ^^
text "Baba is you" ^^ nl ^^
text "EOF");;
val s_d : doc = <abstr>
# pretty_print
print_string
(text "when 1 = 2:" ^^ nest 4 (nl ^^ text "print " ^^ s_d));;
when 1 = 2:
print #<<EOF
Zelda
Baba is you
EOF
- : unit = ()
cost c d
is a document that artificially adds cost c
to d
.
In the below example, we artificially adds overflow to text "CrossCode"
, making it a non-optimal choice, even though text "CrossCode"
would have been the optimal choice had cost
not been used.
Examples:
# pretty_format_debug (cost (1, 0, 0, 0) (text "CrossCode") <|>
(text "Final" ^^ nl ^^ text "Fantasy")) |> print_endline;;
1234567890
Final │
Fantasy │
is_tainted: false
cost: (0 0 1 0)
- : unit = ()
# pretty_format_debug (text "CrossCode" <|>
(text "Final" ^^ nl ^^ text "Fantasy")) |> print_endline;;
1234567890
CrossCode │
is_tainted: false
cost: (0 0 0 0)
- : unit = ()
cost
is especially useful in combination with a custom cost factory. See the section for further details.
two_columns ds
is a document that lays out the documents in ds
in two columns.
Note that this is not quite a table layout, because in each row, the right column will start at the same line as the last line of the left column.
Also note that some rows may overflow the column separator (e.g. in order to avoid the global overflow over the page width limit). The function two_columns_overflow
can be used to customize this behavior.
The indentation level is set to the initial current column position (in the same manner as align
) so that on entering a new line, the left column of the next row starts at the right position.
Unlike fill
or fillBreak
from Wadler/Leijen's pretty printer, which requires users to specify a fixed position of the column separator, two_columns
will find the position automatically, and will find the leftmost one.
Examples:
Following example is taken from https://hackage.haskell.org/package/wl-pprint-1.2.1/docs/Text-PrettyPrint-Leijen.html
# let types = [ ("empty", "Doc") ;
("nest", "Int -> Doc -> Doc") ;
("linebreak", "Doc") ];;
val types : (string * string) list =
[("empty", "Doc"); ("nest", "Int -> Doc -> Doc"); ("linebreak", "Doc")]
# let print_doc w =
let cf = Printer.default_cost_factory ~page_width:w () in
let module P = Printer.Make (val cf) in
let open P in
let d = text "let " ^^
two_columns (List.map
(fun (n, t) ->
(text n, text " :: " ^^ text t))
types) in
pretty_format_debug d |> print_endline;;
val print_doc : int -> unit = <fun>
# print_doc 34;;
1234567890123456789012345678901234
let empty :: Doc │
nest :: Int -> Doc -> Doc│
linebreak :: Doc │
is_tainted: false
cost: (0 0 2 2)
- : unit = ()
# print_doc 33;;
123456789012345678901234567890123
let empty :: Doc │
nest :: Int -> Doc -> Doc │
linebreak :: Doc │
is_tainted: false
cost: (0 4 2 1)
- : unit = ()
# print_doc 28;;
1234567890123456789012345678
let empty :: Doc │
nest :: Int -> Doc -> Do│c
linebreak :: Doc │
is_tainted: false
cost: (1 6 2 0)
- : unit = ()
# let print_doc_nl w =
let cf = Printer.default_cost_factory ~page_width:w () in
let module P = Printer.Make (val cf) in
let open P in
let d = text "let " ^^
two_columns (List.map
(fun (n, t) ->
(text n ^^ (nl <|> empty),
text " :: " ^^ text t))
types) in
pretty_format_debug d |> print_endline;;
val print_doc_nl : int -> unit = <fun>
# print_doc_nl 34;;
1234567890123456789012345678901234
let empty :: Doc │
nest :: Int -> Doc -> Doc│
linebreak :: Doc │
is_tainted: false
cost: (0 0 2 3)
- : unit = ()
# print_doc_nl 33;;
123456789012345678901234567890123
let empty :: Doc │
nest :: Int -> Doc -> Doc │
linebreak │
:: Doc │
is_tainted: false
cost: (0 0 3 2)
- : unit = ()
# print_doc_nl 28;;
1234567890123456789012345678
let empty │
:: Doc │
nest │
:: Int -> Doc -> Doc │
linebreak │
:: Doc │
is_tainted: false
cost: (0 0 5 0)
- : unit = ()
val fail : doc
A document that always fails. It interacts with (<|>)
: failing branches are pruned away.
Examples:
# pretty_print print_string (text "Sea of Stars" ^^ fail);;
Exception: Failure "fails to render".
# pretty_print print_string ((text "Sea of Stars" ^^ fail) <|> text "Hades");;
Hades
- : unit = ()
flatten d
is a document that replaces newlines and indentation spaces with what's specified in newline
when rendering d
.
Examples:
# pretty_print
print_string
(flatten (text "Fire Emblem" ^^ nl ^^ text "Awakening"));;
Fire Emblem Awakening
- : unit = ()
# pretty_print
print_string
(flatten (text "Mario + Rabbids" ^^ break ^^ text "Kingdom Battle"));;
Mario + RabbidsKingdom Battle
- : unit = ()
# pretty_print
print_string
(flatten (text "XCOM 2" ^^ hard_nl ^^ text "War of the Chosen"));;
Exception: Failure "fails to render".
# pretty_print
print_string
(flatten (text "Tactics Ogre" ^^
newline (Some ": ") ^^
text "Reborn"));;
Tactics Ogre: Reborn
- : unit = ()
group d
is a shorthand for d <|> flatten d
. This combinator is a part of most traditional pretty printers.
a <+> b
is a shorthand for a ^^ align b
. It is also known as the aligned concatenation.
fold_doc (++) ds
is a shorthand for d_1 ++ d_2 ++ ... ++ d_n
where d_1 d_2 ... d_n
are drawn from ds
.
vcat ds
is a shorthand for d_1 <$> d_2 <$> ... <$> d_n
where d_1 d_2 ... d_n
are drawn from ds
.
vcat ds
is a shorthand for d_1 <-> d_2 <-> ... <-> d_n
where d_1 d_2 ... d_n
are drawn from ds
.
val empty : doc
Equivalent to text ""
val space : doc
Equivalent to text " "
val comma : doc
Equivalent to text ","
val lbrack : doc
Equivalent to text "["
val rbrack : doc
Equivalent to text "]"
val lbrace : doc
Equivalent to text "{"
val rbrace : doc
Equivalent to text "}"
val lparen : doc
Equivalent to text "("
val rparen : doc
Equivalent to text ")"
val dquote : doc
Equivalent to text "\""