Source file printer.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
open Spotlib.Spot
module Token = struct
(** Display machine with Format like capability *)
type t =
| String of string
| Box of int * t (** "@[<n>...@]" *)
| VBox of int * t (** "@[<vn>...@]" *)
| Cut (** "@," (known also as Good Break) *)
| Space (** "@ " *)
| Flush (** "@." *)
| Seq of t list
| NOP
open Format
let rec dump_self = function
| String s -> String ("String \"" ^ String.escaped s ^ "\"")
| Box (n, t) ->
Box (2,
Seq [ String "Box ("
; Cut
; String (string_of_int n ^ ",")
; Space
; dump_self t
; String ")"
])
| VBox (n, t) ->
Box (2,
Seq [ String "VBox ("
; Cut
; String (string_of_int n ^ ",")
; Space
; dump_self t
; String ")"
])
| Cut -> String "Cut"
| Space -> String "Space"
| Flush -> String "Flush"
| Seq ts ->
Box (2,
Seq ( [ String "Seq ["
; Cut
; Seq List.(intersperse (Seq [String ";"; Space]) (map dump_self ts))
; Cut
; String "]" ]))
| NOP -> String "NOP"
let rec format ppf = function
| String s -> string ppf s
| Box (n, tk) ->
box ppf n; format ppf tk; close_box ppf ()
| VBox (n ,tk) ->
vbox ppf n; format ppf tk; close_box ppf ()
| Cut -> cut ppf
| Space -> space ppf
| Flush -> flush ppf; newline ppf
| Seq tks -> List.iter (format ppf) tks
| NOP -> ()
let dump ppf t = format ppf & dump_self t
open Buffer
let rec buffer buf = function
| String s -> add_string buf s
| Box (_, tk) | VBox (_ ,tk) -> buffer buf tk
| Seq tks -> List.iter (buffer buf) tks
| Cut | NOP -> ()
| Space -> add_char buf ' '
| Flush -> add_char buf '\n'
let show token =
let buf = Buffer.create 100 in
buffer buf token;
Buffer.contents buf
end
type token = Token.t
open Token
(** Primitive operators *)
type assoc = Left | Right | Noassoc
type level = float
type 'a t = assoc -> level -> 'a
type 'a t_ = 'a t
include Monad.Make(struct
type 'a t = 'a t_
let bind at f = fun a l -> f (at a l) a l
let return a = fun _ _ -> a
end)
type ppr = token t
let box : int -> ppr -> ppr =
fun offset t a l -> Box (offset, t a l)
let vbox : int -> ppr -> ppr =
fun offset t a l -> VBox (offset, t a l)
let do_Seq xs =
Seq (List.concat_map (function
| Seq ys -> ys
| y -> [y]) xs)
let (++) : ppr -> ppr -> ppr =
fun p1 p2 a l -> do_Seq [p1 a l; p2 a l]
let cut : ppr =
fun _out_ops _out_lev -> Cut
let space : ppr =
fun _out_pos _out_lev -> Space
let flush : ppr =
fun _out_pos _out_lev -> Flush
let seq : ppr list -> ppr
= fun ps a l -> do_Seq (List.map (fun p -> p a l) ps)
let string : string -> ppr
= fun s _out_pos _out_lev -> String s
let nop : ppr = fun _out_pos _out_lev -> NOP
let left : 'a t -> 'a t = fun p _a l -> p Left l
let right : 'a t -> 'a t = fun p _a l -> p Right l
let noassoc : 'a t -> 'a t = fun p _a l -> p Noassoc l
let level : level -> 'a t -> 'a t = fun l p a _l -> p a l
let reset : 'a t -> 'a t = fun t -> noassoc (level 0.0 t)
let check_against_current_level : level -> [`Weaker | `Stronger | `Same] t = fun lev _out_pos out_lev ->
match compare out_lev lev with
| 1 -> `Weaker
| -1 -> `Stronger
| 0 -> `Same
| _ -> assert false
let need_paren : assoc -> level -> bool t = fun assoc lev out_pos out_lev ->
match compare out_lev lev with
| 1 -> true
| -1 -> false
| 0 ->
begin match out_pos, assoc with
| Left, Left -> false
| Right, Right -> false
| _ -> true
end
| _ -> assert false
type parens = string * string
let parens = "(", ")"
let parenbox : ?parens:parens -> assoc -> level -> ppr -> ppr = fun ?(parens=parens) assoc lev t ->
need_paren assoc lev >>= function
| true -> box 1 & string (fst parens) ++ reset t ++ string (snd parens)
| false -> t
(** Common utilities *)
let binop : ?parens:parens -> assoc -> level -> op:ppr -> ppr -> ppr -> ppr =
fun ?parens assoc lev ~op:sep l r ->
parenbox ?parens assoc lev (level lev (left l ++ sep ++ right r))
let list : ?parens:parens -> level -> ppr -> ppr list -> ppr =
fun ?parens lev sep f_elems ->
parenbox ?parens Noassoc lev (level lev (seq (List.intersperse sep f_elems)))
let prefix : ?parens:parens -> level -> op:ppr -> ppr -> ppr =
fun ?parens lev ~op:pref t ->
let t = parenbox ?parens Right lev (pref ++ level lev (right t)) in
check_against_current_level lev >>= function
| `Same -> space ++ t
| `Weaker | `Stronger -> t
let postfix : ?parens:parens -> level -> op:ppr -> ppr -> ppr =
fun ?parens lev ~op:postf t ->
let t = parenbox ?parens Left lev (level lev (left t) ++ postf) in
check_against_current_level lev >>= function
| `Same -> t ++ space
| `Weaker | `Stronger -> t
let parens left right ppr = string left ++ level (-1.0) ppr ++ string right
module OCaml = struct
let mbinop assoc lev sep = binop assoc lev ~op:(space ++ string sep ++ space)
let sequence = list 0.25 (string ";" ++ space)
let if_then_else e1 e2 e3 = list 0.5 space [string "if"; reset e1; string "then"; e2; string "else"; e3]
let if_then e1 e2 = list 0.5 space [string "if"; reset e1; string "then"; e2]
let ty_as = mbinop Noassoc 0.6 "as"
let tuple = list 0.8 (string "," ++ space )
let ( ^-> ) = mbinop Right 0.9 "->"
let (+) = mbinop Left 1.0 "+"
let (-) = mbinop Left 1.0 "-"
let ( * ) = mbinop Left 2.0 "*"
let ty_tuple = list 2.0 (space ++ string "* ")
let mprefix lev op = prefix lev ~op:(string op)
let uminus = mprefix 5.0 "-"
let app = binop Left 100.0 ~op:space
end
(** Drivers *)
let format ?(assoc=Noassoc) ?(level=0.0) ppr ppf v = Token.format ppf (ppr v assoc level)
let buffer ppr buf ?(assoc=Noassoc) ?(level=0.0) v = Token.buffer buf (ppr v assoc level)
let show ppr ?(assoc=Noassoc) ?(level=0.0) v = Token.show (ppr v assoc level)
module MakeDrivers(M : sig
type t
val ppr : t -> ppr
end) = struct
open M
let format ppf v = Token.format ppf (ppr v Noassoc 0.0)
let dump ppf v = Token.dump ppf (ppr v Noassoc 0.0)
let buffer buf ?(assoc=Noassoc) ?(level=0.0) v = Token.buffer buf (ppr v assoc level)
let show ?(assoc=Noassoc) ?(level=0.0) v = Token.show (ppr v assoc level)
end
open OCaml
module Test = struct
let num z = string (string_of_int z)
let id x = string x
let int = id "int"
let alpha = id "'a"
let test t answer =
let str = Token.show (t Noassoc 0.0) in
Format.eprintf "%s =?= %s@." str answer;
if str <> answer then failwith "FAILED";
Format.eprintf "%a@.@." Token.format (t Noassoc 0.0);
Format.eprintf "%a@.@." Token.dump (t Noassoc 0.0)
let test () =
test (num 0) "0";
test (num 0 + num 0) "0 + 0";
test (num 0 + num 0 * num 0) "0 + 0 * 0";
test (num 0 * (num 0 + num 0)) "0 * (0 + 0)";
test ((num 1 + num 2) * num 3) "(1 + 2) * 3";
test (num 1 + (num 2 + num 3)) "1 + (2 + 3)";
test (num 1 - (num 2 - num 3)) "1 - (2 - 3)";
test (num 1 - num 2 - num 3) "1 - 2 - 3";
test (uminus (num 1)) "-1";
test (uminus (num 1 + num 2 + num 3)) "-(1 + 2 + 3)";
test (num 1 + uminus (num 1)) "1 + -1";
test (uminus (uminus (num 1))) "- -1";
test (int ^-> int ^-> int) "int -> int -> int";
test ((int ^-> int) ^-> int) "(int -> int) -> int";
test (ty_as (int ^-> int ^-> int) alpha) "int -> int -> int as 'a";
test ((ty_as (int ^-> int) alpha) ^-> int) "(int -> int as 'a) -> int";
test (tuple [num 1; num 2; num 3]) "1, 2, 3";
test (tuple [num 1; tuple [num 2; num 3]; num 4]) "1, (2, 3), 4";
test (app (app (id "x") (id "y")) (id "z")) "x y z";
test (app (id "x") (app (id "y") (id "z"))) "x (y z)";
test (app (id "x") (num 1 * num 2)) "x (1 * 2)";
test (sequence [ num 1 + num 2; num 1 + num 2; num 1 + num 2 ]) "1 + 2; 1 + 2; 1 + 2";
test (if_then_else (num 1 + num 2) (num 1 + num 2) (num 1 + num 2)) "if 1 + 2 then 1 + 2 else 1 + 2";
test (if_then_else (num 1 + num 2) (num 1 + num 2) (num 1) + num 2) "(if 1 + 2 then 1 + 2 else 1) + 2";
test (app (if_then_else (num 1 + num 2) (num 1 + num 2) (num 1)) (num 2)) "(if 1 + 2 then 1 + 2 else 1) 2";
test (app (id "f") (if_then_else (num 1 + num 2) (num 1 + num 2) (num 1 + num 2))) "f (if 1 + 2 then 1 + 2 else 1 + 2)";
test (if_then_else
(sequence [ num 1 + num 2; num 1 + num 2 ])
(sequence [ num 1 + num 2; num 1 + num 2 ])
(sequence [ num 1 + num 2; num 1 + num 2 ])) "if 1 + 2; 1 + 2 then (1 + 2; 1 + 2) else (1 + 2; 1 + 2)";
test (sequence [if_then_else
(sequence [ num 1 + num 2; num 1 + num 2 ])
(sequence [ num 1 + num 2; num 1 + num 2 ])
(sequence [ num 1 + num 2; num 1 + num 2 ]);
num 1 + num 2 ]) "if 1 + 2; 1 + 2 then (1 + 2; 1 + 2) else (1 + 2; 1 + 2); 1 + 2";
test (if_then
(sequence [ num 1 + num 2; num 1 + num 2 ])
(sequence [ num 1 + num 2; num 1 + num 2 ])) "if 1 + 2; 1 + 2 then (1 + 2; 1 + 2)";
test (sequence [if_then
(sequence [ num 1 + num 2; num 1 + num 2 ])
(sequence [ num 1 + num 2; num 1 + num 2 ]);
num 1 + num 2 ]) "if 1 + 2; 1 + 2 then (1 + 2; 1 + 2); 1 + 2";
prerr_endline "done"
end