Skip to content

Commit 448c059

Browse files
authored
Merge pull request #379 from shym/pp
Improve `Util.Pp` pretty-printers
2 parents 582f514 + 13d87f8 commit 448c059

10 files changed

+659
-27
lines changed

CHANGES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Next
44

5+
- #379: Extend the set of `Util.Pp` pretty-printers and teach them to
6+
add break hints similar to `ppx_deriving.show`; teach `to_show` to
7+
generate truncated strings when `$MCTUTILS_TRUNCATE` environment
8+
variable is set
59
- #368: Switch `STM_domain.agree_prop_par_asym` from using
610
`Semaphore.Binary` to using an `int Atomic.t` which improves
711
the error rate across platforms and backends

dune

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
(env
2-
(debug-runtime (link_flags :standard -runtime-variant=d))
2+
(debug-runtime
3+
(link_flags :standard -runtime-variant=d)
4+
(env-vars
5+
(MCTUTILS_TRUNCATE 50)))
6+
(_
7+
(env-vars
8+
(MCTUTILS_TRUNCATE 50)))
39
)
410

511
;; make `dune build` target a recursive default target

lib/util.ml

Lines changed: 174 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -80,28 +80,77 @@ module Pp = struct
8080

8181
type 'a t = bool -> Format.formatter -> 'a -> unit
8282

83-
let to_show f x = asprintf "%a" (f false) x
83+
type pp_thunk = Format.formatter -> unit
84+
85+
let truncate_message = "... (truncated)"
86+
87+
let truncate_length =
88+
let truncate_env = "MCTUTILS_TRUNCATE" in
89+
let ( let* ) = Option.bind in
90+
let* l = Sys.getenv_opt truncate_env in
91+
let* l = int_of_string_opt l in
92+
(* it does not make sense to truncate at less than the length of
93+
[truncate_message] *)
94+
if l > 0 then Some (max l (String.length truncate_message - 1)) else None
95+
96+
let to_show f x =
97+
match truncate_length with
98+
| None ->
99+
let buf = Buffer.create 512 in
100+
let fmt = formatter_of_buffer buf in
101+
pp_set_margin fmt max_int;
102+
fprintf fmt "@[<h 0>%a@]@?" (f false) x;
103+
let s = Buffer.contents buf in
104+
Buffer.reset buf;
105+
s
106+
| Some trlen ->
107+
(* if we overflow, we'll have the [truncate_message] at the end of the
108+
buffer, filling it until [trlen + 1]: we'll use the fact that the
109+
buffer contains more than [trlen] to indicate that it has already
110+
overflown *)
111+
let buf = Buffer.create (trlen + 1) in
112+
let msglen = String.length truncate_message in
113+
let out str ofs len =
114+
let blen = Buffer.length buf in
115+
(* if we didn't overflow yet... *)
116+
if blen <= trlen then
117+
if blen + len > trlen then (
118+
let fits = trlen - blen - msglen + 1 in
119+
if fits > 0 then Buffer.add_substring buf str ofs fits
120+
else Buffer.truncate buf (trlen + 1 - msglen);
121+
Buffer.add_string buf truncate_message)
122+
else Buffer.add_substring buf str ofs len
123+
in
124+
let ppf = make_formatter out ignore in
125+
pp_set_margin ppf max_int;
126+
fprintf ppf "@[<h 0>%a@]@?" (f false) x;
127+
let s = Buffer.contents buf in
128+
Buffer.reset buf;
129+
s
84130

85131
let of_show f par fmt x =
86-
fprintf fmt (if par then "(%s)" else "%s") (f x)
132+
fprintf fmt (if par then "@[(%s)@]" else "@[%s@]") (f x)
87133

88134
let cst0 name fmt = pp_print_string fmt name
89135

90136
let cst1 (pp : 'a t) name par fmt x =
91-
fprintf fmt (if par then "(%s %a)" else "%s %a") name (pp true) x
137+
let o, c = if par then ("(", ")") else ("", "") in
138+
fprintf fmt "%s@[<2>%s@ %a@]%s" o name (pp true) x c
92139

93140
let cst2 (pp1 : 'a t) (pp2 : 'b t) name par fmt x y =
94-
fprintf fmt (if par then "(%s (%a, %a))" else "%s (%a, %a)") name (pp1 false) x (pp2 false) y
141+
let o, c = if par then ("(", ")") else ("", "") in
142+
fprintf fmt "%s@[<2>%s (@,%a,@ %a)@]%s" o name (pp1 false) x (pp2 false) y c
95143

96144
let cst3 (pp1 : 'a t) (pp2 : 'b t) (pp3 : 'c t) name par fmt x y z =
97-
fprintf fmt
98-
(if par then "(%s (%a, %a, %a))" else "%s (%a, %a, %a)")
99-
name (pp1 false) x (pp2 false) y (pp3 false) z
145+
let o, c = if par then ("(", ")") else ("", "") in
146+
fprintf fmt "%s@[<2>%s (@,%a,@ %a,@ %a)@]%s" o name (pp1 false) x
147+
(pp2 false) y (pp3 false) z c
100148

101149
let pp_exn = of_show Printexc.to_string
102150
let pp_unit _ fmt () = pp_print_string fmt "()"
103151
let pp_bool _ fmt b = fprintf fmt "%B" b
104152
let pp_int par fmt i = fprintf fmt (if par && i < 0 then "(%d)" else "%d") i
153+
let pp_int32 par fmt i = fprintf fmt (if par && i < 0l then "(%ldl)" else "%ldl") i
105154
let pp_int64 par fmt i = fprintf fmt (if par && i < 0L then "(%LdL)" else "%LdL") i
106155
let pp_float par fmt f = fprintf fmt (if par && f < 0.0 then "(%F)" else "%F") f
107156
let pp_char _ fmt c = fprintf fmt "%C" c
@@ -110,42 +159,144 @@ module Pp = struct
110159

111160
let pp_option (pp_s : 'a t) par fmt o =
112161
match o with
113-
| None -> pp_print_string fmt "None"
114-
| Some s -> fprintf fmt (if par then "(Some %a)" else "Some %a") (pp_s true) s
162+
| None -> cst0 "None" fmt
163+
| Some s -> cst1 pp_s "Some" par fmt s
115164

116165
let pp_result (pp_o : 'o t) (pp_e : 'e t) par fmt r =
117166
let open Result in
118167
match r with
119-
| Ok o -> fprintf fmt (if par then "(Ok %a)" else "Ok %a") (pp_o true) o
120-
| Error e -> fprintf fmt (if par then "(Error %a)" else "Error %a") (pp_e true) e
168+
| Ok o -> cst1 pp_o "Ok" par fmt o
169+
| Error e -> cst1 pp_e "Error" par fmt e
170+
171+
type pp_tuple_item = pp_thunk
172+
173+
let pp_tuple_item pp x fmt = pp false fmt x
174+
175+
let pp_tuple _ fmt items =
176+
fprintf fmt "(@[";
177+
pp_print_list ~pp_sep:(fun fmt () -> fprintf fmt ",@ ") (fun fmt ppf -> ppf fmt) fmt items;
178+
fprintf fmt "@])"
179+
180+
let pp_tuple2 pp1 pp2 p fmt (x1, x2) =
181+
pp_tuple p fmt [ pp_tuple_item pp1 x1; pp_tuple_item pp2 x2 ]
182+
183+
let pp_tuple3 pp1 pp2 pp3 p fmt (x1, x2, x3) =
184+
pp_tuple p fmt
185+
[ pp_tuple_item pp1 x1; pp_tuple_item pp2 x2; pp_tuple_item pp3 x3 ]
186+
187+
let pp_tuple4 pp1 pp2 pp3 pp4 p fmt (x1, x2, x3, x4) =
188+
pp_tuple p fmt
189+
[
190+
pp_tuple_item pp1 x1;
191+
pp_tuple_item pp2 x2;
192+
pp_tuple_item pp3 x3;
193+
pp_tuple_item pp4 x4;
194+
]
195+
196+
let pp_tuple5 pp1 pp2 pp3 pp4 pp5 p fmt (x1, x2, x3, x4, x5) =
197+
pp_tuple p fmt
198+
[
199+
pp_tuple_item pp1 x1;
200+
pp_tuple_item pp2 x2;
201+
pp_tuple_item pp3 x3;
202+
pp_tuple_item pp4 x4;
203+
pp_tuple_item pp5 x5;
204+
]
205+
206+
let pp_tuple6 pp1 pp2 pp3 pp4 pp5 pp6 p fmt (x1, x2, x3, x4, x5, x6) =
207+
pp_tuple p fmt
208+
[
209+
pp_tuple_item pp1 x1;
210+
pp_tuple_item pp2 x2;
211+
pp_tuple_item pp3 x3;
212+
pp_tuple_item pp4 x4;
213+
pp_tuple_item pp5 x5;
214+
pp_tuple_item pp6 x6;
215+
]
216+
217+
let pp_tuple7 pp1 pp2 pp3 pp4 pp5 pp6 pp7 p fmt (x1, x2, x3, x4, x5, x6, x7) =
218+
pp_tuple p fmt
219+
[
220+
pp_tuple_item pp1 x1;
221+
pp_tuple_item pp2 x2;
222+
pp_tuple_item pp3 x3;
223+
pp_tuple_item pp4 x4;
224+
pp_tuple_item pp5 x5;
225+
pp_tuple_item pp6 x6;
226+
pp_tuple_item pp7 x7;
227+
]
228+
229+
let pp_tuple8 pp1 pp2 pp3 pp4 pp5 pp6 pp7 pp8 p fmt
230+
(x1, x2, x3, x4, x5, x6, x7, x8) =
231+
pp_tuple p fmt
232+
[
233+
pp_tuple_item pp1 x1;
234+
pp_tuple_item pp2 x2;
235+
pp_tuple_item pp3 x3;
236+
pp_tuple_item pp4 x4;
237+
pp_tuple_item pp5 x5;
238+
pp_tuple_item pp6 x6;
239+
pp_tuple_item pp7 x7;
240+
pp_tuple_item pp8 x8;
241+
]
242+
243+
let pp_tuple9 pp1 pp2 pp3 pp4 pp5 pp6 pp7 pp8 pp9 p fmt
244+
(x1, x2, x3, x4, x5, x6, x7, x8, x9) =
245+
pp_tuple p fmt
246+
[
247+
pp_tuple_item pp1 x1;
248+
pp_tuple_item pp2 x2;
249+
pp_tuple_item pp3 x3;
250+
pp_tuple_item pp4 x4;
251+
pp_tuple_item pp5 x5;
252+
pp_tuple_item pp6 x6;
253+
pp_tuple_item pp7 x7;
254+
pp_tuple_item pp8 x8;
255+
pp_tuple_item pp9 x9;
256+
]
257+
258+
let pp_tuple10 pp1 pp2 pp3 pp4 pp5 pp6 pp7 pp8 pp9 pp10 p fmt
259+
(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10) =
260+
pp_tuple p fmt
261+
[
262+
pp_tuple_item pp1 x1;
263+
pp_tuple_item pp2 x2;
264+
pp_tuple_item pp3 x3;
265+
pp_tuple_item pp4 x4;
266+
pp_tuple_item pp5 x5;
267+
pp_tuple_item pp6 x6;
268+
pp_tuple_item pp7 x7;
269+
pp_tuple_item pp8 x8;
270+
pp_tuple_item pp9 x9;
271+
pp_tuple_item pp10 x10;
272+
]
121273

122-
let pp_pair (pp_f : 'a t) (pp_s : 'b t) _ fmt (x,y) =
123-
fprintf fmt "(%a, %a)" (pp_f false) x (pp_s false) y
274+
let pp_pair = pp_tuple2
124275

125276
let pp_list (pp_e : 'a t) _ fmt l =
126-
pp_print_string fmt "[";
277+
fprintf fmt "@[<2>[";
127278
pp_print_list ~pp_sep:(fun fmt () -> fprintf fmt ";@ ") (pp_e false) fmt l;
128-
pp_print_string fmt "]"
279+
fprintf fmt "@,]@]"
129280

130281
let pp_seq (pp_e : 'a t) _ fmt s =
131-
pp_print_string fmt "<";
282+
fprintf fmt "@[<2><";
132283
pp_print_seq ~pp_sep:(fun fmt () -> fprintf fmt ";@ ") (pp_e false) fmt s;
133-
pp_print_string fmt ">"
284+
fprintf fmt "@,>@]"
134285

135286
let pp_array (pp_e : 'a t) _ fmt a =
136-
pp_print_string fmt "[|";
287+
fprintf fmt "@[<2>[|";
137288
pp_print_seq ~pp_sep:(fun fmt () -> fprintf fmt ";@ ") (pp_e false) fmt (Array.to_seq a);
138-
pp_print_string fmt "|]"
289+
fprintf fmt "@,|]@]"
139290

140-
type pp_field = Format.formatter -> unit
291+
type pp_field = pp_thunk
141292

142293
let pp_field name (pp_c : 'a t) c fmt =
143-
fprintf fmt "%s =@ %a" name (pp_c false) c
294+
fprintf fmt "@[%s =@ %a@]" name (pp_c false) c
144295

145296
let pp_record _ fmt fields =
146-
pp_print_string fmt "{ ";
297+
fprintf fmt "@[<2>{ ";
147298
pp_print_list ~pp_sep:(fun fmt () -> fprintf fmt ";@ ") (fun fmt ppf -> ppf fmt) fmt fields;
148-
fprintf fmt "@ }"
299+
fprintf fmt "@ }@]"
149300
end
150301

151302
module Equal = struct

lib/util.mli

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@ module Pp : sig
5151
if it produces a non-atomic expression. *)
5252

5353
val to_show : 'a t -> 'a -> string
54-
(** [to_show pp] converts a pretty-printer to a simple ['a -> string] function. *)
54+
(** [to_show pp] converts a pretty-printer to a simple ['a -> string] function
55+
that generate everything on one line. If the environment variable
56+
[MCTUTILS_TRUNCATE] is set to a length, it will truncate the resulting
57+
string if it exceeds that length. *)
5558

5659
val of_show : ('a -> string) -> 'a t
5760
(** [of_show show] uses a simple ['a -> string] function as a pretty-printer.
@@ -90,6 +93,9 @@ module Pp : sig
9093
val pp_int : int t
9194
(** Pretty-printer for type [int] *)
9295

96+
val pp_int32 : int32 t
97+
(** Pretty-printer for type [int32] *)
98+
9399
val pp_int64 : int64 t
94100
(** Pretty-printer for type [int64] *)
95101

@@ -114,10 +120,93 @@ module Pp : sig
114120
using [pp_ok] to pretty-print values of type ['o] and [pp_error] for
115121
values of type ['e]. *)
116122

123+
type pp_tuple_item
124+
(** The abstract type for the pretty-printer of a tuple item *)
125+
126+
val pp_tuple_item : 'a t -> 'a -> pp_tuple_item
127+
(** [pp_tuple_item pp v] builds a pretty-printer for a tuple item using [pp]
128+
to pretty-print its value [v]. *)
129+
130+
val pp_tuple : pp_tuple_item list t
131+
(** [pp_tuple] pretty-prints a tuple taken as a list of [pp_tuple_item]s. *)
132+
117133
val pp_pair : 'a t -> 'b t -> ('a * 'b) t
118134
(** [pp_pair pp_a pp_b] pretty-prints a value of type ['a * 'b] using [pp_a]
119135
to pretty-print values of type ['a] and [pp_b] for values of type ['b]. *)
120136

137+
val pp_tuple2 : 'a t -> 'b t -> ('a * 'b) t
138+
(** [pp_tuple2] pretty-prints pairs, synonym for [pp_pair]. *)
139+
140+
val pp_tuple3 : 'a t -> 'b t -> 'c t -> ('a * 'b * 'c) t
141+
(** [pp_tuple3] pretty-prints triples. *)
142+
143+
val pp_tuple4 : 'a t -> 'b t -> 'c t -> 'd t -> ('a * 'b * 'c * 'd) t
144+
(** [pp_tuple4] pretty-prints tuples of 4 elements. *)
145+
146+
val pp_tuple5 :
147+
'a t -> 'b t -> 'c t -> 'd t -> 'e t -> ('a * 'b * 'c * 'd * 'e) t
148+
(** [pp_tuple5] pretty-prints tuples of 5 elements. *)
149+
150+
val pp_tuple6 :
151+
'a t ->
152+
'b t ->
153+
'c t ->
154+
'd t ->
155+
'e t ->
156+
'f t ->
157+
('a * 'b * 'c * 'd * 'e * 'f) t
158+
(** [pp_tuple6] pretty-prints tuples of 6 elements. *)
159+
160+
val pp_tuple7 :
161+
'a t ->
162+
'b t ->
163+
'c t ->
164+
'd t ->
165+
'e t ->
166+
'f t ->
167+
'g t ->
168+
('a * 'b * 'c * 'd * 'e * 'f * 'g) t
169+
(** [pp_tuple7] pretty-prints tuples of 7 elements. *)
170+
171+
val pp_tuple8 :
172+
'a t ->
173+
'b t ->
174+
'c t ->
175+
'd t ->
176+
'e t ->
177+
'f t ->
178+
'g t ->
179+
'h t ->
180+
('a * 'b * 'c * 'd * 'e * 'f * 'g * 'h) t
181+
(** [pp_tuple8] pretty-prints tuples of 8 elements. *)
182+
183+
val pp_tuple9 :
184+
'a t ->
185+
'b t ->
186+
'c t ->
187+
'd t ->
188+
'e t ->
189+
'f t ->
190+
'g t ->
191+
'h t ->
192+
'i t ->
193+
('a * 'b * 'c * 'd * 'e * 'f * 'g * 'h * 'i) t
194+
(** [pp_tuple9] pretty-prints tuples of 9 elements. *)
195+
196+
val pp_tuple10 :
197+
'a t ->
198+
'b t ->
199+
'c t ->
200+
'd t ->
201+
'e t ->
202+
'f t ->
203+
'g t ->
204+
'h t ->
205+
'i t ->
206+
'j t ->
207+
('a * 'b * 'c * 'd * 'e * 'f * 'g * 'h * 'i * 'j) t
208+
(** [pp_tuple10] pretty-prints tuples of 10 elements. *)
209+
121210
val pp_list : 'a t -> 'a list t
122211
(** [pp_list pp] pretty-prints a list using [pp] to pretty-print its elements. *)
123212

@@ -128,10 +217,10 @@ module Pp : sig
128217
(** [pp_array pp] pretty-prints an array using [pp] to pretty-print its elements. *)
129218

130219
type pp_field
131-
(** The abtract type for the pretty-printer of a record field *)
220+
(** The abstract type for the pretty-printer of a record field *)
132221

133222
val pp_field : string -> 'a t -> 'a -> pp_field
134-
(** [pp_field name pp v] build a pretty-printer for a record field of given
223+
(** [pp_field name pp v] builds a pretty-printer for a record field of given
135224
[name] using [pp] to pretty-print its content value [v]. *)
136225

137226
val pp_record : pp_field list t

0 commit comments

Comments
 (0)