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
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
let debug = ref false
let set_debug x = debug := x
let get_debug () = !debug
type t =
| Int of int64
| Int32 of int32
| Bool of bool
| Float of float
| String of string
| DateTime of string
| Enum of t list
| Dict of (string * t) list
| Base64 of string
| Null
module Version = struct
type t = int * int * int
let compare (x, y, z) (x', y', z') =
let cmp a b fn () =
let c = compare a b in
if c <> 0 then c else fn ()
in
cmp x x' (cmp y y' (cmp z z' (fun () -> 0))) ()
end
module Types = struct
type _ basic =
| Int : int basic
| Int32 : int32 basic
| Int64 : int64 basic
| Bool : bool basic
| Float : float basic
| String : string basic
| Char : char basic
type _ typ =
| Basic : 'a basic -> 'a typ
| DateTime : string typ
| Base64 : string typ
| Array : 'a typ -> 'a array typ
| List : 'a typ -> 'a list typ
| Dict : 'a basic * 'b typ -> ('a * 'b) list typ
| Unit : unit typ
| Option : 'a typ -> 'a option typ
| Tuple : 'a typ * 'b typ -> ('a * 'b) typ
| Tuple3 : 'a typ * 'b typ * 'c typ -> ('a * 'b * 'c) typ
| Tuple4 : 'a typ * 'b typ * 'c typ * 'd typ -> ('a * 'b * 'c * 'd) typ
| Struct : 'a structure -> 'a typ
| Variant : 'a variant -> 'a typ
| Abstract : 'a abstract -> 'a typ
and 'a def =
{ name : string
; description : string list
; ty : 'a typ
}
and boxed_def = BoxedDef : 'a def -> boxed_def
and ('a, 's) field =
{ fname : string
; fdescription : string list
; fversion : Version.t option
; field : 'a typ
; fdefault : 'a option
; fget : 's -> 'a
;
fset : 'a -> 's -> 's
}
and 'a boxed_field = BoxedField : ('a, 's) field -> 's boxed_field
and field_getter =
{ field_get : 'a. string -> 'a typ -> ('a, Rresult.R.msg) Result.t }
and 'a structure =
{ sname : string
; fields : 'a boxed_field list
; version : Version.t option
; constructor : field_getter -> ('a, Rresult.R.msg) Result.t
}
and ('a, 's) tag =
{ tname : string
; tdescription : string list
; tversion : Version.t option
; tcontents : 'a typ
; tpreview : 's -> 'a option
; treview : 'a -> 's
}
and 'a boxed_tag = BoxedTag : ('a, 's) tag -> 's boxed_tag
and tag_getter = { tget : 'a. 'a typ -> ('a, Rresult.R.msg) Result.t }
and 'a variant =
{ vname : string
; variants : 'a boxed_tag list
; vdefault : 'a option
; vversion : Version.t option
; vconstructor : string -> tag_getter -> ('a, Rresult.R.msg) Result.t
}
and 'a abstract =
{ aname : string
; test_data : 'a list
; rpc_of : 'a -> t
; of_rpc : t -> ('a, Rresult.R.msg) Result.t
}
let int = { name = "int"; ty = Basic Int; description = [ "Native integer" ] }
let int32 = { name = "int32"; ty = Basic Int32; description = [ "32-bit integer" ] }
let int64 = { name = "int64"; ty = Basic Int64; description = [ "64-bit integer" ] }
let bool = { name = "bool"; ty = Basic Bool; description = [ "Boolean" ] }
let float =
{ name = "float"; ty = Basic Float; description = [ "Floating-point number" ] }
let string = { name = "string"; ty = Basic String; description = [ "String" ] }
let char = { name = "char"; ty = Basic Char; description = [ "Char" ] }
let unit = { name = "unit"; ty = Unit; description = [ "Unit" ] }
let default_types =
[ BoxedDef int
; BoxedDef int32
; BoxedDef int64
; BoxedDef bool
; BoxedDef float
; BoxedDef string
; BoxedDef char
; BoxedDef unit
]
end
exception Runtime_error of string * t
exception Runtime_exception of string * string
let map_strings sep fn l = String.concat sep (List.map fn l)
let rec to_string t =
let open Printf in
match t with
| Int i -> sprintf "I(%Li)" i
| Int32 i -> sprintf "I32(%li)" i
| Bool b -> sprintf "B(%b)" b
| Float f -> sprintf "F(%g)" f
| String s -> sprintf "S(%s)" s
| DateTime s -> sprintf "D(%s)" s
| Enum ts -> sprintf "[%s]" (map_strings ";" to_string ts)
| Dict ts ->
sprintf "{%s}" (map_strings ";" (fun (s, t) -> sprintf "%s:%s" s (to_string t)) ts)
| Base64 s -> sprintf "B64(%s)" s
| Null -> "N"
let rpc_of_t x = x
let rpc_of_int64 i = Int i
let rpc_of_int32 i = Int (Int64.of_int32 i)
let rpc_of_int i = Int (Int64.of_int i)
let rpc_of_bool b = Bool b
let rpc_of_float f = Float f
let rpc_of_string s = String s
let rpc_of_dateTime s = DateTime s
let rpc_of_base64 s = Base64 s
let rpc_of_unit () = Null
let rpc_of_char x = Int (Int64.of_int (Char.code x))
let int64_of_rpc = function
| Int i -> i
| String s -> Int64.of_string s
| x -> failwith (Printf.sprintf "Expected int64, got '%s'" (to_string x))
let int32_of_rpc = function
| Int i -> Int64.to_int32 i
| String s -> Int32.of_string s
| x -> failwith (Printf.sprintf "Expected int32, got '%s'" (to_string x))
let int_of_rpc = function
| Int i -> Int64.to_int i
| String s -> int_of_string s
| x -> failwith (Printf.sprintf "Expected int, got '%s'" (to_string x))
let bool_of_rpc = function
| Bool b -> b
| x -> failwith (Printf.sprintf "Expected bool, got '%s'" (to_string x))
let float_of_rpc = function
| Float f -> f
| Int i -> Int64.to_float i
| Int32 i -> Int32.to_float i
| String s -> float_of_string s
| x -> failwith (Printf.sprintf "Expected float, got '%s'" (to_string x))
let string_of_rpc = function
| String s -> s
| x -> failwith (Printf.sprintf "Expected string, got '%s'" (to_string x))
let dateTime_of_rpc = function
| DateTime s -> s
| x -> failwith (Printf.sprintf "Expected DateTime, got '%s'" (to_string x))
let base64_of_rpc = function
| Base64 s -> Base64.decode_exn s
| x -> failwith (Printf.sprintf "Expected base64, got '%s'" (to_string x))
let unit_of_rpc = function
| Null -> ()
| x -> failwith (Printf.sprintf "Expected unit, got '%s'" (to_string x))
let char_of_rpc x =
let x = int_of_rpc x in
if x < 0 || x > 255
then failwith (Printf.sprintf "Char out of range (%d)" x)
else Char.chr x
let t_of_rpc t = t
let lowerfn = function
| String s -> String (String.lowercase_ascii s)
| Enum (String s :: ss) -> Enum (String (String.lowercase_ascii s) :: ss)
| x -> x
module ResultUnmarshallers = struct
open Rresult
let int64_of_rpc = function
| Int i -> R.ok i
| String s ->
(try R.ok (Int64.of_string s) with
| _ -> R.error_msg (Printf.sprintf "Expected int64, got string '%s'" s))
| x -> R.error_msg (Printf.sprintf "Expected int64, got '%s'" (to_string x))
let int32_of_rpc = function
| Int i -> R.ok (Int64.to_int32 i)
| String s ->
(try R.ok (Int32.of_string s) with
| _ -> R.error_msg (Printf.sprintf "Expected int32, got string '%s'" s))
| x -> R.error_msg (Printf.sprintf "Expected int32, got '%s'" (to_string x))
let int_of_rpc = function
| Int i -> R.ok (Int64.to_int i)
| String s ->
(try R.ok (int_of_string s) with
| _ -> R.error_msg (Printf.sprintf "Expected int, got string '%s'" s))
| x -> R.error_msg (Printf.sprintf "Expected int, got '%s'" (to_string x))
let bool_of_rpc = function
| Bool b -> R.ok b
| x -> R.error_msg (Printf.sprintf "Expected bool, got '%s'" (to_string x))
let float_of_rpc = function
| Float f -> R.ok f
| Int i -> R.ok (Int64.to_float i)
| Int32 i -> R.ok (Int32.to_float i)
| String s ->
(try R.ok (float_of_string s) with
| _ -> R.error_msg (Printf.sprintf "Expected float, got string '%s'" s))
| x -> R.error_msg (Printf.sprintf "Expected float, got '%s'" (to_string x))
let string_of_rpc = function
| String s -> R.ok s
| x -> R.error_msg (Printf.sprintf "Expected string, got '%s'" (to_string x))
let dateTime_of_rpc = function
| DateTime s -> R.ok s
| x -> R.error_msg (Printf.sprintf "Expected DateTime, got '%s'" (to_string x))
let base64_of_rpc = function
| Base64 s -> R.ok s
| x -> R.error_msg (Printf.sprintf "Expected base64, got '%s'" (to_string x))
let unit_of_rpc = function
| Null -> R.ok ()
| x -> R.error_msg (Printf.sprintf "Expected unit, got '%s'" (to_string x))
let char_of_rpc x =
Rresult.R.bind (int_of_rpc x) (fun x ->
if x < 0 || x > 255
then R.error_msg (Printf.sprintf "Char out of range (%d)" x)
else R.ok (Char.chr x))
let t_of_rpc t = R.ok t
end
let struct_extend rpc default_rpc =
match rpc, default_rpc with
| Dict real, Dict default_fields ->
Dict
(List.fold_left
(fun real (f, default) ->
if List.mem_assoc f real then real else (f, default) :: real)
real
default_fields)
| _, _ -> rpc
type callback = string list -> t -> unit
type call =
{ name : string
; params : t list
; is_notification : bool
}
let call name params = { name; params; is_notification = false }
let notification name params = { name; params; is_notification = true }
let string_of_call call =
Printf.sprintf
"-> %s(%s)"
call.name
(String.concat "," (List.map to_string call.params))
type response =
{ success : bool
; contents : t
; is_notification : bool
}
let string_of_response response =
Printf.sprintf
"<- %s(%s)"
(if response.success then "success" else "failure")
(to_string response.contents)
let success v = { success = true; contents = v; is_notification = false }
let failure v = { success = false; contents = v; is_notification = false }