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
358
open Sexplib0
type error =
| Decoder_error of string * Sexp.t option
| Decoder_errors of error list
| Decoder_tag of string * error
type 'a t = Sexp.t -> ('a, error) Result.t
let pp_sexp fmt value = Format.fprintf fmt "@[%a@]" Sexp.pp_hum value
let rec pp_error fmt = function
| Decoder_error (msg, Some t) ->
Format.fprintf fmt "@[%s, but got@ @[%a@]@]" msg pp_sexp t
| Decoder_error (msg, None) ->
Format.fprintf fmt "@[%s@]" msg
| Decoder_errors errors ->
Format.fprintf
fmt
"@[%a@]"
(Format.pp_print_list ~pp_sep:Format.pp_print_space pp_error)
errors
| Decoder_tag (msg, error) ->
Format.fprintf fmt "@[<2>%s:@ @[%a@]@]" msg pp_error error
let string_of_error error = Format.asprintf "@[<2>%a@?@]" pp_error error
let merge_errors e1 e2 =
match e1, e2 with
| Decoder_errors e1s, Decoder_errors e2s ->
Decoder_errors (e1s @ e2s)
| Decoder_errors e1s, _ ->
Decoder_errors (e1s @ [ e2 ])
| _, Decoder_errors e2s ->
Decoder_errors ([ e1 ] @ e2s)
| _ ->
Decoder_errors [ e1; e2 ]
let combine_errors results =
let rec aux combined = function
| [] ->
(match combined with
| Ok xs ->
Ok (List.rev xs)
| Error es ->
Error (List.rev es))
| result :: rest ->
let combined =
match result, combined with
| Ok x, Ok xs ->
Ok (x :: xs)
| Error e, Error es ->
Error (e :: es)
| Error e, Ok _ ->
Error [ e ]
| Ok _, Error es ->
Error es
in
aux combined rest
in
aux (Ok []) results
let decoder_error ~msg sexp = Decoder_error (msg, Some sexp)
let tag_error ~msg error = Decoder_tag (msg, error)
let tag_errors ~msg errors = Decoder_tag (msg, Decoder_errors errors)
let of_string s =
let result =
try Ok (Sexplib.Sexp.of_string s) with Failure msg -> Error msg
in
Result.map_error
(fun msg ->
Decoder_tag ("S-Expression parsing error", Decoder_error (msg, None)))
result
let of_sexps_string (s : string) =
let s = "(" ^ s ^ ")" in
of_string s
let of_file s =
let content = Sys.read_file s in
of_string content
let of_sexps_file s =
let content = Sys.read_file s in
of_sexps_string content
let decode_primitive f ~err = function
| Sexp.Atom _ as sexp ->
(try Ok (f sexp) with
| Sexp_conv.Of_sexp_error _ ->
Error (decoder_error ~msg:err sexp))
| sexp ->
Error (decoder_error ~msg:"Expected a single value" sexp)
let string = decode_primitive Sexp_conv.string_of_sexp ~err:"Expected a string"
let int = decode_primitive Sexp_conv.int_of_sexp ~err:"Expected an int"
let float = decode_primitive Sexp_conv.float_of_sexp ~err:"Expected a float"
let bool = decode_primitive Sexp_conv.bool_of_sexp ~err:"Expected a bool"
let null = decode_primitive Sexp_conv.unit_of_sexp ~err:"Expected a unit"
let string_matching ~regex ~err sexp =
let validate_format s =
let regexp = Str.regexp regex in
Str.string_match regexp s 0
in
match sexp with
| Sexp.Atom atom ->
if validate_format atom then
Ok atom
else
Error
(decoder_error
sexp
~msg:(Printf.sprintf "Invalid value %S. %s" atom err))
| sexp ->
Error (decoder_error sexp ~msg:"Expected a single value")
let list decoder sexp =
match sexp with
| Sexp.Atom _ ->
Error (decoder_error ~msg:"Expected a list" sexp)
| Sexp.List sexps ->
List.mapi
(fun i x ->
decoder x
|> Result.map_error (fun error ->
tag_error ~msg:(Printf.sprintf "element %i" i) error))
sexps
|> combine_errors
|> Result.map_error (fun errors ->
tag_errors ~msg:"while decoding a list" errors)
let key_value_pairs = function
| Sexp.List l ->
let kv_pairs_opt =
List.map
(function
| Sexp.List [ key; value ] ->
Some (key, value)
| Sexp.List (key :: values) ->
Some (key, Sexp.List values)
| _ ->
None)
l
in
let all_some l =
try Some (List.map (function Some x -> x | None -> raise Exit) l) with
| Exit ->
None
in
all_some kv_pairs_opt
| _ ->
None
let field_opt key f sexp =
let value =
match sexp with
| Sexp.List [ Sexp.Atom s; value ] when String.equal s key ->
Some value
| Sexp.List (Sexp.Atom s :: values) when String.equal s key ->
Some (Sexp.List values)
| _ ->
(match key_value_pairs sexp with
| Some kv_pairs ->
List.find_map
(fun (k, v) ->
match string k with
| Ok s when String.equal s key ->
Some v
| _ ->
None)
kv_pairs
| _ ->
None)
in
match value with
| Some value ->
f value
|> Result.map Option.some
|> Result.map_error (fun error ->
tag_error ~msg:(Printf.sprintf "Error in field %S" key) error)
| None ->
Ok None
let field key f sexp =
match field_opt key f sexp with
| Ok None ->
Error
(decoder_error
~msg:(Printf.sprintf "Expected an s-expression with a field %S" key)
sexp)
| Ok (Some v) ->
Ok v
| Error e ->
Error e
let fields key f sexp =
let values =
match sexp with
| Sexp.List [ Sexp.Atom s; value ] when String.equal s key ->
[ value ]
| Sexp.List (Sexp.Atom s :: values) when String.equal s key ->
[ Sexp.List values ]
| _ ->
(match key_value_pairs sexp with
| Some kv_pairs ->
List.fold_left
(fun acc (k, v) ->
match string k with
| Ok s when String.equal s key ->
v :: acc
| _ ->
acc)
[]
kv_pairs
| _ ->
[])
in
let rec aux acc = function
| [] ->
Ok acc
| el :: rest ->
(match f el with
| Ok v ->
aux (v :: acc) rest
| Error e ->
Error (tag_error ~msg:(Printf.sprintf "Error in field %S" key) e))
in
aux [] values
let one_of_opt decoders sexp =
let rec go = function
| (_, decoder) :: rest ->
(match decoder sexp with
| Ok result ->
Ok (Some result)
| Error _ ->
go rest)
| [] ->
Ok None
in
go decoders
let one_of decoders sexp =
let rec go errors = function
| (name, decoder) :: rest ->
(match decoder sexp with
| Ok result ->
Ok result
| Error error ->
go
(tag_errors ~msg:(Printf.sprintf "%S decoder" name) [ error ]
:: errors)
rest)
| [] ->
Error
(tag_errors
~msg:"I tried the following decoders but they all failed"
errors)
in
go [] decoders
let return v _ = Ok v
let map f decoder sexp = Result.map f (decoder sexp)
let bind decoder f sexp =
Result.bind (decoder sexp) (fun result -> f result sexp)
let apply f decoder sexp =
match f sexp, decoder sexp with
| Error e1, Error e2 ->
Error (merge_errors e1 e2)
| Error e, _ ->
Error e
| _, Error e ->
Error e
| Ok g, Ok x ->
Ok (g x)
let product d1 d2 sexp =
match d1 sexp, d2 sexp with
| Error e1, Error e2 ->
Error (merge_errors e1 e2)
| Error e, _ ->
Error e
| _, Error e ->
Error e
| Ok a, Ok b ->
Ok (a, b)
module Infix = struct
let ( >|= ) decoder f = map f decoder
let ( >>= ) decoder f = bind decoder f
let ( <*> ) f decoder = apply f decoder
end
include Infix
module Syntax = struct
let ( let* ) decoder f = bind decoder f
let ( let+ ) decoder f = map f decoder
let ( and+ ) d1 d2 = product d1 d2
end
include Syntax
let decode_sexp sexp f = f sexp
let decode_string s f =
let open Result.Syntax in
let* sexp = of_string s in
decode_sexp sexp f
let decode_sexps_string s f =
let open Result.Syntax in
let* sexp = of_sexps_string s in
decode_sexp sexp f
let decode_file file f =
let open Result.Syntax in
let* sexp = of_file file in
decode_sexp sexp f
let decode_sexps_file file f =
let open Result.Syntax in
let* sexp = of_sexps_file file in
decode_sexp sexp f