From 7274ee7f30967cd4bf85d3b809c9e725126afdc2 Mon Sep 17 00:00:00 2001 From: Sorawee Porncharoenwase Date: Tue, 28 Nov 2023 16:09:30 -0800 Subject: [PATCH] core: rename <> to ^^ `<>` 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`. --- CHANGES.md | 7 ++++ doc/index.mld | 22 +++++------ lib/printer.ml | 19 +++++++--- lib/printer.mli | 5 +++ lib/signature.mli | 78 ++++++++++++++++++++++++--------------- test/pretty_expressive.ml | 16 ++++---- 6 files changed, 92 insertions(+), 55 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 3d2c9cb..d35f5b7 100644 --- a/CHANGES.md +++ b/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 diff --git a/doc/index.mld b/doc/index.mld index 0a42d56..e323d0c 100644 --- a/doc/index.mld +++ b/doc/index.mld @@ -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 ]} @@ -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.} @@ -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} @@ -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. diff --git a/lib/printer.ml b/lib/printer.ml index 706997b..5178cdf 100644 --- a/lib/printer.ml +++ b/lib/printer.ml @@ -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); @@ -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 @@ -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 @@ -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 diff --git a/lib/printer.mli b/lib/printer.mli index 2b0378a..dcc5927 100644 --- a/lib/printer.mli +++ b/lib/printer.mli @@ -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], diff --git a/lib/signature.mli b/lib/signature.mli index 72f9961..f2a78e2 100644 --- a/lib/signature.mli +++ b/lib/signature.mli @@ -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 = # print_doc 10;; Octopath @@ -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 = -# let right_doc = text "Automata" <> nl <> text "FEZ";; +# let right_doc = text "Automata" ^^ nl ^^ text "FEZ";; val right_doc : doc = -# pretty_print (left_doc <> right_doc) |> print_endline;; +# pretty_print (left_doc ^^ right_doc) |> print_endline;; Splatoon NierAutomata FEZ @@ -150,7 +158,7 @@ FEZ {5 Examples:} {[ -# pretty_print (left_doc <> align right_doc) |> print_endline;; +# pretty_print (left_doc ^^ align right_doc) |> print_endline;; Splatoon NierAutomata FEZ @@ -158,7 +166,7 @@ NierAutomata ]} 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 @@ -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!' @@ -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!' @@ -192,12 +200,12 @@ when 1 = 2: {5 Examples:} {[ -# let s_d = reset (text "#< nl <> - text "Zelda" <> nl <> - text "Baba is you" <> nl <> +# let s_d = reset (text "#< -# 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 #< - (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 = () ]} @@ -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 = () ]} *) @@ -263,8 +271,8 @@ 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 @@ -272,8 +280,8 @@ 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 @@ -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 @@ -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]. @@ -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 diff --git a/test/pretty_expressive.ml b/test/pretty_expressive.ml index 4456dbf..b309bae 100644 --- a/test/pretty_expressive.ml +++ b/test/pretty_expressive.ml @@ -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 @@ -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