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
type id = int
let id_k = ref 0
let id = fun () -> incr id_k; !id_k
let unshared = 0
type var = id
type t =
| Unit
| Nil
| Bool of bool
| Char of char
| Int of int
| Int32 of int32
| Int64 of int64
| Nativeint of nativeint
| Float of float
| Constant of string
| Cons of {id: id; car: t; cdr: t}
| String of {id: id; data: string}
| Tuple of {id: id; data: t list}
| Record of {id: id; data: (string * t) list}
| Constructor of {id: id; tag: string; data: t}
| Array of {id: id; data: t array}
| Lazy of {id: id; data: t lazy_t}
| Var of var
| Let of {id: id; recursive: bool; bindings: (var * t) list; body: t}
let nil = Nil
let unit = Unit
let bool data = Bool data
let char data = Char data
let int data = Int data
let int32 data = Int32 data
let int64 data = Int64 data
let nativeint data = Nativeint data
let float data = Float data
let string data = String {id=id(); data}
let constant tag = Constant tag
let constructor tag data = Constructor {id=id(); tag; data}
let tuple data = Tuple {id=id(); data}
let record data = Record {id=id(); data}
let cons car cdr = Cons {id=id(); car; cdr}
let unshared_string data = String{id=unshared; data}
let unshared_constructor tag data = Constructor {id=unshared; tag; data}
let unshared_tuple data = Tuple {id=unshared; data}
let unshared_record data = Record {id=unshared; data}
let unshared_cons car cdr = Cons {id=unshared; car; cdr}
let list xs = List.fold_right cons xs nil
let list_map f xs = list (List.map f xs)
let unshared_list xs = List.fold_right unshared_cons xs nil
let array data = Array{id=id(); data}
let array_map f xs = array (Array.map f xs)
let unshared_array data = Array {id=unshared; data}
let construct tag = function
| [] -> constant tag
| [x] -> constructor tag x
| xs -> constructor tag (unshared_tuple xs)
let unshared_construct tag = function
| [] -> constant tag
| [x] -> unshared_constructor tag x
| xs -> unshared_constructor tag (unshared_tuple xs)
let crecord tag data = constructor tag (unshared_record data)
let unshared_crecord tag data = unshared_constructor tag (unshared_record data)
let id_of = function
| Bool _ | Char _ | Int _ | Int32 _ | Int64 _ | Nativeint _
| Float _ | Nil | Unit | Constant _ | Var _ -> unshared
| Tuple {id; _} | Record {id; _} | Constructor {id; _} | Cons {id; _}
| String {id; _} | Let {id; _} | Array {id; _} | Lazy {id; _} -> id
let graph : t Fastdom.graph = {
successors = begin fun f acc ->
let rec aux acc = function
| Bool _ | Char _ | Int _ | Int32 _ | Int64 _ | Nativeint _
| Float _ | Nil | Unit | Constant _
| String _ | Var _ | Let _ -> acc
| Lazy {data = lazy t; _} -> f_ acc t
| Tuple {data; _} -> List.fold_left f_ acc data
| Record {data; _} -> List.fold_left f_field acc data
| Array {data; _} -> Array.fold_left f_ acc data
| Constructor {data; _} -> f_ acc data
| Cons {car; cdr; _} -> f_ (f_ acc car) cdr
and f_field acc (_, v) =
f_ acc v
and f_ acc self =
if id_of self <> unshared
then f acc self
else aux acc self
in
aux acc
end;
memoize = begin fun (type b) (f : _ -> b) ->
let table : (id, b) Hashtbl.t = Hashtbl.create 7 in
fun x ->
let id = id_of x in
if id = unshared then f x else
try Hashtbl.find table id
with Not_found ->
let y = f x in
Hashtbl.add table id y;
y
end;
}
let binding_structure : (t, int) Binder_introducer.binding_structure = {
name_term = (fun _ -> id ());
var_term = (fun id -> Var id);
map_subterms = begin fun f t ->
let rec sub_map = function
| Bool _ | Char _ | Int _ | Int32 _ | Int64 _ | Nativeint _
| Float _ | Nil | Unit | Constant _
| String _ | Var _ | Let _ as t -> t
| Lazy {data = lazy t; _} -> f' t
| Tuple t ->
unshared_tuple (List.map f' t.data)
| Record t ->
unshared_record (List.map (fun (k,v) -> k, f' v) t.data)
| Constructor t ->
unshared_constructor t.tag (f' t.data)
| Cons t ->
unshared_cons (f' t.car) (f' t.cdr)
| Array t ->
unshared_array (Array.map f' t.data)
and f' t =
if id_of t = unshared then
sub_map t
else
f t
in
sub_map t
end;
introduce_let = begin fun ~recursive bindings body ->
Let {id=id(); recursive; bindings; body}
end;
}
let explicit_sharing t =
Binder_introducer.explicit_sharing graph binding_structure t
let rec list_of_cons acc = function
| Cons {id = _; car; cdr} -> list_of_cons (car :: acc) cdr
| Nil -> List.rev acc, None
| other -> List.rev acc, Some other
let print_record f fields =
let add_field acc x =
let k, v = f x in
PPrint.(acc ^/^ group (group(string k ^/^ char '=') ^^
nest 2 (break 1 ^^ v) ^^ char ';'))
in
let fields = List.fold_left add_field PPrint.empty fields in
PPrint.(group (string "{" ^^ nest 2 fields ^/^ string "}"))
let print_as_is var_name doc =
let open PPrint in
let rec sub_print_as_is = function
| Unit -> true, string "()"
| Nil -> true, string "[]"
| Constant tag -> true, string tag
| Bool b -> true, OCaml.bool b
| Char c -> true, OCaml.char c
| Int i -> true, OCaml.int i
| Int32 i -> true, OCaml.int32 i
| Int64 i -> true, OCaml.int64 i
| Nativeint i -> true, OCaml.nativeint i
| Float f -> true, OCaml.float f
| Lazy {id=_; data=lazy t} as t' ->
if t == t'
then (true, string "<cycle>")
else sub_print_as_is t
| Cons _ as self ->
begin match list_of_cons [] self with
| items, None ->
true, OCaml.list print_as_is items
| items, Some cdr ->
false,
group (
let print_one item =
group (string "::" ^/^ item)
in
let rec print = function
| [] -> print_one (print_as_is cdr)
| x :: xs -> print_one (print_as_is x) ^^ break 1 ^^ print xs
in
match items with
| x :: xs -> print_as_is x ^^ break 1 ^^ print xs
| [] -> assert false
)
end
| Array {id=_; data} -> true, OCaml.array print_as_is data
| String {id=_; data} -> true, OCaml.string data
| Tuple {id=_; data} ->
true, OCaml.tuple (List.map print_as_is data)
| Record {id=_; data} ->
true,
print_record (fun (k,v) -> k, print_as_is v) data
| Constructor {id=_; tag; data} ->
let delimited, sub_doc = sub_print_as_is data in
let doc =
if delimited
then sub_doc
else OCaml.tuple [sub_doc]
in
false, group (string tag ^^ blank 1 ^^ doc)
| Var id -> true, string (var_name id)
| Let {id=_; recursive; bindings; body} ->
let rec print_bindings prefix = function
| [] -> string "in"
| (id, value) :: values ->
let doc = print_as_is value in
let need_break = match value with Let _ -> true | _ -> false in
let doc =
group @@
if need_break
then group (string prefix ^/^ id ^/^ string "=") ^^
nest 2 (break 1 ^^ doc)
else group (string prefix ^/^ id ^/^ string "= ") ^^
nest 2 doc
in
doc ^/^ print_bindings "and" values
in
let name_binding (id, value) = (string (var_name id), value) in
let prefix = if recursive then "let rec" else "let" in
let bindings = List.map name_binding bindings in
let bindings = group (print_bindings prefix bindings) in
false,
bindings ^/^
print_as_is body
and print_as_is doc =
let _delim, doc = sub_print_as_is doc in
doc
in
print_as_is doc
let print_as_is doc =
let table = Hashtbl.create 7 in
let var_name id =
match Hashtbl.find_opt table id with
| Some name -> name
| None ->
let name = "v" ^ string_of_int (Hashtbl.length table) in
Hashtbl.replace table id name;
name
in
print_as_is var_name doc
let format_document ppf doc : unit =
let margin = Format.pp_get_margin ppf () in
Format.fprintf ppf "@[%a@]" (PPrint.ToFormatter.pretty 0.9 margin) doc
let format_as_is ppf t : unit =
format_document ppf (print_as_is t)
let print t : PPrint.document = print_as_is (explicit_sharing t)
let format ppf t : unit = format_as_is ppf (explicit_sharing t)
let of_lazy data = Lazy {id=id(); data}