Source file ser.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
open Ppxlib
module Ast = Ast_builder.Default
(** helpers *)
let loc ~ctxt = Expansion_context.Deriver.derived_item_loc ctxt
let var ~ctxt name =
let loc = loc ~ctxt in
Loc.make ~loc name
(** implementation *)
let rec ser_fun ~ctxt ~v (t : core_type) =
let loc = loc ~ctxt in
match t.ptyp_desc with
| Ptyp_constr (name, _) -> (
match name.txt |> Longident.name with
| "bool" -> [%expr Ser.serialize_bool [%e v]]
| "char" -> [%expr Ser.serialize_char [%e v]]
| "float" -> [%expr Ser.serialize_float [%e v]]
| "int" -> [%expr Ser.serialize_int [%e v]]
| "string" -> [%expr Ser.serialize_string [%e v]]
| "unit" -> [%expr Ser.serialize_unit [%e v]]
| _ ->
let ser_fn_name =
match name.txt |> Longident.flatten_exn |> List.rev with
| name :: [] -> "serialize_" ^ name
| name :: path ->
("serialize_" ^ name) :: path |> List.rev |> String.concat "."
| _ -> "unknown"
in
let fn =
ser_fn_name |> Longident.parse |> var ~ctxt |> Ast.pexp_ident ~loc
in
Ast.pexp_apply ~loc fn [ (Nolabel, v) ])
| Ptyp_tuple parts ->
let pats =
List.mapi
(fun i part ->
let f_idx = "f_" ^ Int.to_string i in
let pat = f_idx |> var ~ctxt |> Ast.ppat_var ~loc in
let exp =
f_idx |> Longident.parse |> var ~ctxt |> Ast.pexp_ident ~loc
in
(pat, (exp, part)))
parts
in
let destruct, _exprs = List.split pats in
let vb =
let destruct = destruct |> Ast.ppat_tuple ~loc in
[ Ast.value_binding ~loc ~pat:destruct ~expr:v ]
in
let keys, exprs =
parts
|> List.mapi (fun idx ctyp ->
let f_idx = "f_" ^ Int.to_string idx in
let pat = f_idx |> var ~ctxt |> Ast.ppat_var ~loc in
let var =
f_idx |> Longident.parse |> var ~ctxt |> Ast.pexp_ident ~loc
in
let fn = ser_fun ~ctxt ctyp ~v:var in
(var, (pat, fn)))
|> List.split
in
let ser_call =
[%expr
Ser.serialize_tuple
~size:[%e List.length parts |> Ast.eint ~loc]
~elements]
in
let field_list =
Ast.pexp_let ~loc Nonrecursive
[
Ast.value_binding ~loc
~pat:[%pat? elements]
~expr:(Ast.elist ~loc keys);
]
ser_call
in
Ast.pexp_let ~loc Nonrecursive vb
(List.fold_left
(fun body (pat, exp) ->
let op = var ~ctxt "let*" in
let let_ = Ast.binding_op ~op ~loc ~pat ~exp in
Ast.letop ~let_ ~ands:[] ~body |> Ast.pexp_letop ~loc)
field_list (List.rev exprs))
| Ptyp_any | Ptyp_var _
| Ptyp_object (_, _)
| Ptyp_class (_, _)
| Ptyp_alias (_, _)
| Ptyp_variant (_, _, _)
| Ptyp_poly (_, _)
| Ptyp_package _ | Ptyp_extension _
| Ptyp_arrow (_, _, _) ->
[%expr ()]
let gen_record_field_impl ~ctxt i ldecl =
let loc = loc ~ctxt in
let f_idx = "f_" ^ Int.to_string i in
let pat = f_idx |> var ~ctxt |> Ast.ppat_var ~loc in
let var = f_idx |> Longident.parse |> var ~ctxt |> Ast.pexp_ident ~loc in
let fn = ser_fun ~ctxt ldecl.pld_type ~v:var in
let kv = [%expr [%e ldecl.pld_name.txt |> Ast.estring ~loc], [%e var]] in
(kv, (pat, fn))
let gen_serialize_record_variant_impl ~ctxt ~typename ~variant_name ~idx fields
=
let loc = loc ~ctxt in
let keys, exprs =
fields |> List.mapi (gen_record_field_impl ~ctxt) |> List.split
in
let ser_call =
[%expr
Ser.serialize_record_variant
~typename:[%e typename.txt |> Ast.estring ~loc]
~variant_idx:[%e idx + 1 |> Ast.eint ~loc]
~variant_name:[%e variant_name.txt |> Ast.estring ~loc]
~variant_size:[%e List.length keys |> Ast.eint ~loc]
~fields]
in
let field_list =
Ast.pexp_let ~loc Nonrecursive
[ Ast.value_binding ~loc ~pat:[%pat? fields] ~expr:(Ast.elist ~loc keys) ]
ser_call
in
List.fold_left
(fun body (pat, exp) ->
let op = var ~ctxt "let*" in
let let_ = Ast.binding_op ~op ~loc ~pat ~exp in
Ast.letop ~let_ ~ands:[] ~body |> Ast.pexp_letop ~loc)
field_list (List.rev exprs)
let gen_tuple_field_impl ~ctxt i ctyp =
let loc = loc ~ctxt in
let f_idx = "f_" ^ Int.to_string i in
let pat = f_idx |> var ~ctxt |> Ast.ppat_var ~loc in
let var = f_idx |> Longident.parse |> var ~ctxt |> Ast.pexp_ident ~loc in
let fn = ser_fun ~ctxt ctyp ~v:var in
(var, (pat, fn))
let gen_serialize_tuple_variant_impl ~ctxt ~typename ~variant_name ~idx parts =
let loc = loc ~ctxt in
let keys, exprs =
parts |> List.mapi (gen_tuple_field_impl ~ctxt) |> List.split
in
let ser_call =
[%expr
Ser.serialize_tuple_variant
~typename:[%e typename.txt |> Ast.estring ~loc]
~variant_idx:[%e idx + 1 |> Ast.eint ~loc]
~variant_name:[%e variant_name.txt |> Ast.estring ~loc]
~variant_size:[%e List.length keys |> Ast.eint ~loc]
~fields]
in
let field_list =
Ast.pexp_let ~loc Nonrecursive
[ Ast.value_binding ~loc ~pat:[%pat? fields] ~expr:(Ast.elist ~loc keys) ]
ser_call
in
List.fold_left
(fun body (pat, exp) ->
let op = var ~ctxt "let*" in
let let_ = Ast.binding_op ~op ~loc ~pat ~exp in
Ast.letop ~let_ ~ands:[] ~body |> Ast.pexp_letop ~loc)
field_list (List.rev exprs)
let gen_serialize_unit_variant_impl ~ctxt ~typename ~variant_name ~idx =
let loc = loc ~ctxt in
[%expr
Ser.serialize_unit_variant
~typename:[%e typename.txt |> Ast.estring ~loc]
~variant_idx:[%e idx + 1 |> Ast.eint ~loc]
~variant_name:[%e variant_name.txt |> Ast.estring ~loc]]
let gen_serialize_variant_ctr_impl ~ctxt ~typename idx ctr =
let variant_name = ctr.pcd_name in
match ctr.pcd_args with
| Pcstr_tuple [] ->
gen_serialize_unit_variant_impl ~ctxt ~typename ~variant_name ~idx
| Pcstr_tuple parts ->
gen_serialize_tuple_variant_impl ~ctxt ~typename ~variant_name ~idx parts
| Pcstr_record fields ->
gen_serialize_record_variant_impl ~ctxt ~typename ~variant_name ~idx
fields
let gen_serialize_variant_impl ~ctxt typename constructors =
let loc = Expansion_context.Deriver.derived_item_loc ctxt in
let cases =
List.mapi
(fun idx ctr ->
let args =
match ctr.pcd_args with
| Pcstr_tuple [] -> None
| Pcstr_tuple fields ->
let fields =
List.mapi
(fun i _ ->
let f_idx = "f_" ^ Int.to_string i in
let pat = f_idx |> var ~ctxt |> Ast.ppat_var ~loc in
pat)
fields
in
Some (Ast.ppat_tuple ~loc fields)
| Pcstr_record fields ->
let fields =
List.mapi
(fun i field ->
let f_idx = "f_" ^ Int.to_string i in
let pat = f_idx |> var ~ctxt |> Ast.ppat_var ~loc in
(field.pld_name.txt |> Longident.parse |> Loc.make ~loc, pat))
fields
in
Some (Ast.ppat_record ~loc fields Closed)
in
let lhs =
Ast.ppat_construct ~loc
(ctr.pcd_name.txt |> Longident.parse |> Loc.make ~loc)
args
in
let rhs = gen_serialize_variant_ctr_impl ~ctxt ~typename idx ctr in
Ast.case ~lhs ~rhs ~guard:None)
constructors
in
Ast.pexp_match ~loc [%expr t] cases
let gen_serialize_record_impl ~ctxt typename fields =
let loc = Expansion_context.Deriver.derived_item_loc ctxt in
let =
let fields =
List.mapi
(fun i field ->
let f_idx = "f_" ^ Int.to_string i in
let pat = f_idx |> var ~ctxt |> Ast.ppat_var ~loc in
(field.pld_name.txt |> Longident.parse |> Loc.make ~loc, pat))
fields
in
Ast.ppat_record ~loc fields Closed
in
let keys, exprs =
fields |> List.mapi (gen_record_field_impl ~ctxt) |> List.split
in
let ser_call =
[%expr
Ser.serialize_record
~typename:[%e typename.txt |> Ast.estring ~loc]
~size:[%e List.length keys |> Ast.eint ~loc]
~fields]
in
let field_list =
Ast.pexp_let ~loc Nonrecursive
[ Ast.value_binding ~loc ~pat:[%pat? fields] ~expr:(Ast.elist ~loc keys) ]
ser_call
in
let body =
List.fold_left
(fun body (pat, exp) ->
let op = var ~ctxt "let*" in
let let_ = Ast.binding_op ~op ~loc ~pat ~exp in
Ast.letop ~let_ ~ands:[] ~body |> Ast.pexp_letop ~loc)
field_list (List.rev exprs)
in
let t = "t" |> Longident.parse |> var ~ctxt |> Ast.pexp_ident ~loc in
let vb = [ Ast.value_binding ~loc ~pat:extract_fields ~expr:t ] in
Ast.pexp_let ~loc Nonrecursive vb body
let gen_serialize_abstract_impl ~ctxt _typename core_type =
let loc = loc ~ctxt in
let v = "t" |> Longident.parse |> var ~ctxt |> Ast.pexp_ident ~loc in
ser_fun ~ctxt ~v core_type
let gen_serialize_impl ~ctxt type_decl =
let loc = loc ~ctxt in
let typename = type_decl.ptype_name.txt in
let body =
match type_decl with
| { ptype_kind = Ptype_variant constructors; ptype_name; _ } ->
gen_serialize_variant_impl ~ctxt ptype_name constructors
| { ptype_kind = Ptype_record label_declarations; ptype_name; _ } ->
gen_serialize_record_impl ~ctxt ptype_name label_declarations
| {
ptype_kind = Ptype_abstract;
ptype_name;
ptype_manifest = Some manifest;
_;
} ->
gen_serialize_abstract_impl ~ctxt ptype_name manifest
| { ptype_kind; ptype_name; _ } ->
let err =
match ptype_kind with
| Ptype_abstract -> "unsupported abstract type"
| Ptype_variant _ -> "unsupported variant type"
| Ptype_record _ -> "unsupported record type"
| Ptype_open -> "unsupported open type"
in
[%expr
[%e ptype_name.txt |> Ast.estring ~loc] [%e err |> Ast.estring ~loc]]
in
let serializer_name =
"serialize_" ^ typename |> var ~ctxt |> Ast.ppat_var ~loc
in
[%stri
(** Serialize a value of this type into Serde.data *)
let [%p serializer_name] =
fun t ->
let ( let* ) = Result.bind in
let* () = Ok () in
[%e body]]
let generate_impl ~ctxt (_rec_flag, type_declarations) =
List.map (gen_serialize_impl ~ctxt) type_declarations
let impl_generator = Deriving.Generator.V2.make_noarg generate_impl
(** interface *)
let generate_intf ~ctxt:_ (_rec_flag, _type_declarations) = []
let intf_generator = Deriving.Generator.V2.make_noarg generate_intf
(** registration *)
let register =
Deriving.add "serializer" ~str_type_decl:impl_generator
~sig_type_decl:intf_generator