Skip to content

Commit

Permalink
core: rename <> to ^^
Browse files Browse the repository at this point in the history
`<>` is actually the built-in "not equal" operator in OCaml,
so when the printer module is open, the built-in operator will
be shadowed. This commit renames <> to ^^, while still providing
<> under another module/functor `MakeCompat`.
  • Loading branch information
sorawee committed Nov 29, 2023
1 parent b2d3af6 commit 7274ee7
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 55 deletions.
7 changes: 7 additions & 0 deletions CHANGES.md
@@ -1,3 +1,10 @@
## 0.2 (2023-11-28)

* Change `<>` to `^^` to avoid shadowing the not equal operator
(thanks to @EmileTrotignon who made the suggestion in
[the OCaml forum](https://discuss.ocaml.org/t/ann-first-release-of-pretty-expressive-a-pretty-expressive-printer/13516/2)).
* Improve documentation

## 0.1 (2023-11-26)

* Initial release
22 changes: 11 additions & 11 deletions doc/index.mld
Expand Up @@ -23,12 +23,12 @@ let print_doc (w : int): unit =
let module P = Printer.Make (val cf) in
let open P in

let d = text "while (true) {" <>
let d = text "while (true) {" ^^
nest 4
(nl <> text "f();" <> nl <> text "if (done())" <>
(nl ^^ text "f();" ^^ nl ^^ text "if (done())" ^^
(let exit_d = text "exit();" in
(space <> exit_d) <|> nest 4 (nl <> exit_d))) <>
nl <> text "}"
(space ^^ exit_d) <|> nest 4 (nl ^^ exit_d))) ^^
nl ^^ text "}"
in
pretty_print d
]}
Expand All @@ -37,10 +37,10 @@ There is a lot of code above, so let's unpack it.

{2 Document Construction}

In the above code, [let d = text "while (true) {" <> ....] defines a document [d].
In the above code, [let d = text "while (true) {" ^^ ....] defines a document [d].

{ul {- {{!Pretty_expressive.Printer.Make.text}[text]} prints a string.}
{- {{!Pretty_expressive.Printer.Make.(<>)}[<>]} prints a concatenation of two documents.}
{- {{!Pretty_expressive.Printer.Make.(^^)}[^^]} prints a concatenation of two documents.}
{- {{!Pretty_expressive.Printer.Make.nl}[nl]} prints a newline.}
{- {{!Pretty_expressive.Printer.Make.nest}[nest]} adds indentation level so that
{{!Pretty_expressive.Printer.Make.nl}[nl]} adds indentation spaces.}
Expand Down Expand Up @@ -83,7 +83,7 @@ It is a functor that consumes a {{!Pretty_expressive.Signature.CostFactory} [Cos
Here, [let module P = Printer.Make (val cf)] creates a pretty printer [P]
with the above cost factory.

We then [let open P] so that combinators like {{!Pretty_expressive.Printer.Make.text}[text]}, {{!Pretty_expressive.Printer.Make.(<>)}[<>]}, and the pretty printing function {{!Pretty_expressive.Printer.Make.pretty_print}[pretty_print]}
We then [let open P] so that combinators like {{!Pretty_expressive.Printer.Make.text}[text]}, {{!Pretty_expressive.Printer.Make.(^^)}[^^]}, and the pretty printing function {{!Pretty_expressive.Printer.Make.pretty_print}[pretty_print]}
are in scope.

{2 Putting them all together}
Expand Down Expand Up @@ -130,11 +130,11 @@ Some may be easier than the other.
For example, another way to construct a document for the above example could be:

{[
let d = text "while (true) {" <>
let d = text "while (true) {" ^^
nest 4
(nl <> text "f();" <> nl <> text "if (done())" <>
group (nest 4 (nl <> text "exit();"))) <>
nl <> text "}"
(nl ^^ text "f();" ^^ nl ^^ text "if (done())" ^^
group (nest 4 (nl ^^ text "exit();"))) ^^
nl ^^ text "}"
]}

Here, the {{!Pretty_expressive.Printer.Make.group}[group]} combinator is used.
Expand Down
19 changes: 13 additions & 6 deletions lib/printer.ml
Expand Up @@ -84,14 +84,14 @@ module Core (C : Signature.CostFactory) = struct
nl_cnt = d.nl_cnt;
table = init_table memo_w }

let rec (<>) (d1 : doc) (d2 : doc) =
let rec (^^) (d1 : doc) (d2 : doc) =
match (d1.dc, d2.dc) with
| (Fail, _) | (_, Fail) -> fail
| (Text (_, 0), _) -> d2
| (_, Text (_, 0)) -> d1
| (Text (s1, l1), Text (s2, l2)) -> make_text (Cons (s1, s2)) (l1 + l2)
| (_, Cost (c, d2)) -> cost c (d1 <> d2)
| (Cost (c, d1), _) -> cost c (d1 <> d2)
| (_, Cost (c, d2)) -> cost c (d1 ^^ d2)
| (Cost (c, d1), _) -> cost c (d1 ^^ d2)
| _ ->
let memo_w = min (calc_weight d1) (calc_weight d2) in
{ dc = Concat (d1, d2);
Expand Down Expand Up @@ -304,7 +304,7 @@ module Make (C : Signature.CostFactory): (Signature.PrinterT with type cost = C.
| Concat (({ id = a_id; _ } as a), ({ id = b_id; _ } as b)) ->
let { id = a_idp; _ } as ap = flatten a in
let { id = b_idp; _ } as bp = flatten b in
if a_idp = a_id && b_idp = b_id then d else ap <> bp
if a_idp = a_id && b_idp = b_id then d else ap ^^ bp
| Choice (({ id = a_id; _ } as a), ({ id = b_id; _ } as b)) ->
let { id = a_idp; _ } as ap = flatten a in
let { id = b_idp; _ } as bp = flatten b in
Expand All @@ -318,8 +318,8 @@ module Make (C : Signature.CostFactory): (Signature.PrinterT with type cost = C.
out
in flatten

let (<+>) d1 d2 = d1 <> align d2
let (<$>) d1 d2 = d1 <> hard_nl <> d2
let (<+>) d1 d2 = d1 ^^ align d2
let (<$>) d1 d2 = d1 ^^ hard_nl ^^ d2
let group d = d <|> (flatten d)

let (<->) x y = (flatten x) <+> y
Expand All @@ -334,6 +334,13 @@ module Make (C : Signature.CostFactory): (Signature.PrinterT with type cost = C.

end


module MakeCompat (C : Signature.CostFactory): (Signature.PrinterCompatT with type cost = C.t) = struct
include Make (C)

let (<>) = (^^)
end

let default_cost_factory ~page_width ?computation_width () =
(module struct
type t = int * int
Expand Down
5 changes: 5 additions & 0 deletions lib/printer.mli
Expand Up @@ -3,6 +3,11 @@
module Make(C : Signature.CostFactory): (Signature.PrinterT with type cost = C.t)
(** The pretty printer and document combinators, parameterized by a cost factory. *)

module MakeCompat(C : Signature.CostFactory): (Signature.PrinterCompatT with type cost = C.t)
(** This functor is similar to {!Make}, but it provides operators
that are compatible with the paper.
{b Using [open] on it will shadow built-in identifiers.} *)

val default_cost_factory : page_width:int -> ?computation_width:int -> unit ->
(module Signature.CostFactory with type t = int * int)
(** The default cost factory, parameterized by the page width limit [page_width],
Expand Down
78 changes: 48 additions & 30 deletions lib/signature.mli
Expand Up @@ -104,7 +104,7 @@ Portal
let module P = Printer.Make (val cf) in
let open P in
pretty_print (text "Chrono Trigger" <|>
(text "Octopath" <> nl <> text "Traveler")) |> print_endline;;
(text "Octopath" ^^ nl ^^ text "Traveler")) |> print_endline;;
val print_doc : int -> unit = <fun>
# print_doc 10;;
Octopath
Expand All @@ -119,18 +119,26 @@ Chrono Trigger

(** {2 Concatenation document} *)

val (<>) : doc -> doc -> doc
(** [a <> b] is a document for concatenation of documents [a] and [b]
{i without} alignment. It's also known as the {i unaligned concatenation},
val (^^) : doc -> doc -> doc
(** [a ^^ b] is a document for concatenation of documents [a] and [b]
{i without} alignment.
In {{: https://dl.acm.org/doi/abs/10.1145/3622837 }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 {i unaligned concatenation},
which is widely used in traditional pretty printers.
See also {!Printer.MakeCompat} for a functor that provides this operator
under the symbol [<>].
{5 Examples:}
{[
# let left_doc = text "Splatoon" <> nl <> text "Nier";;
# let left_doc = text "Splatoon" ^^ nl ^^ text "Nier";;
val left_doc : doc = <abstr>
# let right_doc = text "Automata" <> nl <> text "FEZ";;
# let right_doc = text "Automata" ^^ nl ^^ text "FEZ";;
val right_doc : doc = <abstr>
# pretty_print (left_doc <> right_doc) |> print_endline;;
# pretty_print (left_doc ^^ right_doc) |> print_endline;;
Splatoon
NierAutomata
FEZ
Expand All @@ -150,15 +158,15 @@ FEZ
{5 Examples:}
{[
# pretty_print (left_doc <> align right_doc) |> print_endline;;
# pretty_print (left_doc ^^ align right_doc) |> print_endline;;
Splatoon
NierAutomata
FEZ
- : unit = ()
]}
The aligned concatenation operator {!(<+>)} is a derived combinator that
composes {!(<>)} and [align] together. It is especially useful for
composes {!(^^)} and [align] together. It is especially useful for
languages that uses the the box model for code styling. *)

val nest : int -> doc -> doc
Expand All @@ -167,7 +175,7 @@ NierAutomata
{5 Examples:}
{[
# pretty_print (text "when 1 = 2:" <> nest 4 (nl <> text "print 'oh no!'"))
# pretty_print (text "when 1 = 2:" ^^ nest 4 (nl ^^ text "print 'oh no!'"))
|> print_endline;;
when 1 = 2:
print 'oh no!'
Expand All @@ -178,7 +186,7 @@ when 1 = 2:
In the following example, [when 1 = 2:] is not further indented.
{[
# pretty_print (nest 4 (text "when 1 = 2:" <> nl <> text "print 'oh no!'"))
# pretty_print (nest 4 (text "when 1 = 2:" ^^ nl ^^ text "print 'oh no!'"))
|> print_endline;;
when 1 = 2:
print 'oh no!'
Expand All @@ -192,12 +200,12 @@ when 1 = 2:
{5 Examples:}
{[
# let s_d = reset (text "#<<EOF" <> nl <>
text "Zelda" <> nl <>
text "Baba is you" <> nl <>
# let s_d = reset (text "#<<EOF" ^^ nl ^^
text "Zelda" ^^ nl ^^
text "Baba is you" ^^ nl ^^
text "EOF");;
val s_d : doc = <abstr>
# pretty_print (text "when 1 = 2:" <> nest 4 (nl <> text "print " <> s_d))
# pretty_print (text "when 1 = 2:" ^^ nest 4 (nl ^^ text "print " ^^ s_d))
|> print_endline;;
when 1 = 2:
print #<<EOF
Expand All @@ -219,12 +227,12 @@ EOF
{5 Examples:}
{[
# pretty_print (cost (1, 0) (text "CrossCode") <|>
(text "Final" <> nl <> text "Fantasy")) |> print_endline;;
(text "Final" ^^ nl ^^ text "Fantasy")) |> print_endline;;
Final
Fantasy
- : unit = ()
# pretty_print (text "CrossCode" <|>
(text "Final" <> nl <> text "Fantasy")) |> print_endline;;
(text "Final" ^^ nl ^^ text "Fantasy")) |> print_endline;;
CrossCode
- : unit = ()
]}
Expand All @@ -241,9 +249,9 @@ CrossCode
{5 Examples:}
{[
# pretty_print (text "Sea of Stars" <> fail) |> print_endline;;
# pretty_print (text "Sea of Stars" ^^ fail) |> print_endline;;
Exception: Failure "fails to render".
# pretty_print ((text "Sea of Stars" <> fail) <|> text "Hades") |> print_endline;;
# pretty_print ((text "Sea of Stars" ^^ fail) <|> text "Hades") |> print_endline;;
Hades
- : unit = ()
]} *)
Expand All @@ -263,17 +271,17 @@ Hades
{5 Examples:}
{[
# print_string "Languages: ";
pretty_print (align (text "Racket" <> nl <>
text "OCaml" <> nl <>
pretty_print (align (text "Racket" ^^ nl ^^
text "OCaml" ^^ nl ^^
text "Pyret")) |> print_endline;;
Languages: Racket
OCaml
Pyret
- : unit = ()
# print_string "Languages: ";
pretty_print ~init_c:11
(align (text "Racket" <> nl <>
text "OCaml" <> nl <>
(align (text "Racket" ^^ nl ^^
text "OCaml" ^^ nl ^^
text "Pyret")) |> print_endline;;
Languages: Racket
OCaml
Expand All @@ -289,19 +297,19 @@ Languages: Racket
{5 Examples:}
{[
# pretty_print (flatten (text "Fire Emblem" <> nl <> text "Awakening"))
# pretty_print (flatten (text "Fire Emblem" ^^ nl ^^ text "Awakening"))
|> print_endline;;
Fire Emblem Awakening
- : unit = ()
# pretty_print (flatten (text "Mario + Rabbids" <> break <> text "Kingdom Battle"))
# pretty_print (flatten (text "Mario + Rabbids" ^^ break ^^ text "Kingdom Battle"))
|> print_endline;;
Mario + RabbidsKingdom Battle
- : unit = ()
# pretty_print (flatten (text "XCOM 2" <> hard_nl <> text "War of the Chosen"))
# pretty_print (flatten (text "XCOM 2" ^^ hard_nl ^^ text "War of the Chosen"))
|> print_endline;;
Exception: Failure "fails to render".
# pretty_print (flatten (text "Tactics Ogre" <>
newline (Some ": ") <>
# pretty_print (flatten (text "Tactics Ogre" ^^
newline (Some ": ") ^^
text "Reborn"))
|> print_endline;;
Tactics Ogre: Reborn
Expand All @@ -313,11 +321,11 @@ Tactics Ogre: Reborn
This combinator is a part of most traditional pretty printers. *)

val (<+>) : doc -> doc -> doc
(** [a <+> b] is a shorthand for [a <> align b].
(** [a <+> b] is a shorthand for [a ^^ align b].
It is also known as the {i aligned concatenation}. *)

val (<$>) : doc -> doc -> doc
(** [a <$> b] is a shorthand for [a <> hard_nl <> b]. *)
(** [a <$> b] is a shorthand for [a ^^ hard_nl ^^ b]. *)

val (<->) : doc -> doc -> doc
(** [a <-> b] is a shorthand for [flatten a <+> b].
Expand Down Expand Up @@ -367,3 +375,13 @@ Tactics Ogre: Reborn
val dquote : doc
(** Equivalent to [text "\""] *)
end


module type PrinterCompatT =
sig
include PrinterT
(** @closed *)

val (<>) : doc -> doc -> doc
(** [<>] is the same as {!(^^)} *)
end
16 changes: 8 additions & 8 deletions test/pretty_expressive.ml
Expand Up @@ -5,12 +5,12 @@ let print_doc_choice (w : int) =
let module P = Printer.Make (val cf) in
let open P in

let d = text "while (true) {" <>
let d = text "while (true) {" ^^
nest 4
(nl <> text "f();" <> nl <> text "if (done())" <>
(nl ^^ text "f();" ^^ nl ^^ text "if (done())" ^^
(let exit_d = text "exit();" in
(space <> exit_d) <|> nest 4 (nl <> exit_d))) <>
nl <> text "}"
(space ^^ exit_d) <|> nest 4 (nl ^^ exit_d))) ^^
nl ^^ text "}"
in
pretty_print d

Expand All @@ -19,11 +19,11 @@ let print_doc_group (w : int) =
let module P = Printer.Make (val cf) in
let open P in

let d = text "while (true) {" <>
let d = text "while (true) {" ^^
nest 4
(nl <> text "f();" <> nl <> text "if (done())" <>
group (nest 4 (nl <> text "exit();"))) <>
nl <> text "}"
(nl ^^ text "f();" ^^ nl ^^ text "if (done())" ^^
group (nest 4 (nl ^^ text "exit();"))) ^^
nl ^^ text "}"
in
pretty_print d

Expand Down

0 comments on commit 7274ee7

Please sign in to comment.