Library
Module
Module type
Parameter
Class
Class type
This functor is similar to Make
, but it provides operators that are compatible with the paper. Using open
on it will shadow built-in identifiers.
module C : Signature.CostFactory
include Signature.PrinterT with type cost = C.t
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)
- : unit = ()
val text : string -> doc
text s
is a document for textual content s
; s
must not contain a newline. The cost for the text placement can be adjusted via CostFactory.text
.
Examples:
# pretty_print print_string (text "Portal");;
Portal
- : unit = ()
val newline : string option -> doc
newline s
is a document for a newline. When s
is None
, it flatten
s to fail
. When s
is not None
, it flatten
s to text s
. See flatten
for more details. After the newline is inserted, indentation will be added, according to the current indentation level. The cost of the newline and indentation spaces can be adjusted via CostFactory.newline
.
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. The cost for the concatenation can be adjusted via CostFactory.combine
.
See also Printer.MakeCompat
for a functor that provides this operator under the symbol <>
.
Examples:
# let left_doc = text "Splatoon" ^^ nl ^^ text "Nier";;
val left_doc : doc = <abstr>
# let right_doc = text "Automata" ^^ nl ^^ text "FEZ";;
val right_doc : doc = <abstr>
# 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) (text "CrossCode") <|>
(text "Final" ^^ nl ^^ text "Fantasy")) |> print_endline;;
1234567890
Final │
Fantasy │
is_tainted: false
cost: (0 0 1)
- : unit = ()
# pretty_format_debug (text "CrossCode" <|>
(text "Final" ^^ nl ^^ text "Fantasy")) |> print_endline;;
1234567890
CrossCode │
is_tainted: false
cost: (0 0 0)
- : unit = ()
cost
is especially useful in combination with a custom cost factory. See the section for further details.
(experimental) two_columns ds
is a document that lays out the documents in ds
in two columns by inserting spaces between the two columns in each row.
Note that this is not quite a table layout, because:
print_doc_let_nl 33
example below demonstrates this.print_doc_let_nl 33
example below demonstrates this.align
to restrict the right column content from flowing back). The print_doc_match 54
example below demonstrates this.Also note that some rows may overflow the column separator anyway (e.g. in order to avoid the global overflow over the page width limit).
The cost of the overflow over the column separator can be adjusted via CostFactory.two_columns_overflow
(this particularly can be used to strongly discourage overflow over the column separator). The cost to prefer the leftmost column separator can be adjusted via CostFactory.two_columns_bias
. Meanwhile, the cost for the inserted spaces between the two columns is always fixed to CostFactory.text 0 0
.
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 a "fitting" one.
This document is expensive. Internally, it expands to a very large document. Don't use it if the input list is long!
Examples:
The types
example is taken from https://hackage.haskell.org/package/wl-pprint-1.2.1/docs/Text-PrettyPrint-Leijen.html#v:fill
# 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_let 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_let : int -> unit = <fun>
# print_doc_let 34;;
1234567890123456789012345678901234
let empty :: Doc │
nest :: Int -> Doc -> Doc│
linebreak :: Doc │
is_tainted: false
cost: (0 0 2)
- : unit = ()
# print_doc_let 33;;
123456789012345678901234567890123
let empty :: Doc │
nest :: Int -> Doc -> Doc │
linebreak :: Doc │
is_tainted: false
cost: (0 4 2)
- : unit = ()
# print_doc_let 28;;
1234567890123456789012345678
let empty :: Doc │
nest :: Int -> Doc -> Do│c
linebreak :: Doc │
is_tainted: false
cost: (1 6 2)
- : unit = ()
# let print_doc_let_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 ^^ group break,
text " :: " ^^ text t)
types) in
pretty_format_debug d |> print_endline;;
val print_doc_let_nl : int -> unit = <fun>
# print_doc_let_nl 34;;
1234567890123456789012345678901234
let empty :: Doc │
nest :: Int -> Doc -> Doc│
linebreak :: Doc │
is_tainted: false
cost: (0 0 2)
- : unit = ()
# print_doc_let_nl 33;;
123456789012345678901234567890123
let empty :: Doc │
nest :: Int -> Doc -> Doc │
linebreak │
:: Doc │
is_tainted: false
cost: (0 0 3)
- : unit = ()
# print_doc_let_nl 28;;
1234567890123456789012345678
let empty │
:: Doc │
nest │
:: Int -> Doc -> Doc │
linebreak │
:: Doc │
is_tainted: false
cost: (0 0 5)
- : unit = ()
# let table = [ "[]", "false";
"hd :: _ when hd = to_find", "true";
"_ :: tl", "find_member to_find tl" ];;
val table : (string * string) list =
[("[]", "false"); ("hd :: _ when hd = to_find", "true");
("_ :: tl", "find_member to_find tl")]
# let print_doc_match 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 rec find_member to_find xs =" ^^
nest 2 (
nl ^^ text "match xs with" ^^ nl ^^
two_columns
(List.map
(fun (n, t) ->
(text "| " ^^ text n,
text " ->" ^^ group (nest 2 nl) ^^
text t))
table)) in
pretty_format_debug d |> print_endline;;
val print_doc_match : int -> unit = <fun>
# print_doc_match 55;;
1234567890123456789012345678901234567890123456789012345
let rec find_member to_find xs = │
match xs with │
| [] -> false │
| hd :: _ when hd = to_find -> true │
| _ :: tl -> find_member to_find tl│
is_tainted: false
cost: (0 0 4)
- : unit = ()
# print_doc_match 54;;
123456789012345678901234567890123456789012345678901234
let rec find_member to_find xs = │
match xs with │
| [] -> false │
| hd :: _ when hd = to_find -> true │
| _ :: tl -> │
find_member to_find tl │
is_tainted: false
cost: (0 0 5)
- : 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 "\""