Source file writer_monad.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
module type IO = Io.IO
module type Writer_monad = sig
module IO : IO
val json_writer
: writer:(string -> unit IO.t)
-> eol:string
-> incr:int
-> psep:string
-> 'a Json_internal.constrained
-> unit IO.t
val write_json : writer:(string -> unit IO.t) -> 'a Json_internal.constrained -> unit IO.t
val write_json_hum : writer:(string -> unit IO.t) -> 'a Json_internal.constrained -> unit IO.t
end
module Make (Compliance : Compliance.S) (IO : IO) : Writer_monad with module IO := IO = struct
open IO
let nibble_to_hex i = char_of_int (if i > 9 then 65 + i - 10 else 48 + i)
let add_hex_byte buf i =
Buffer.add_char buf (nibble_to_hex ((i lsr 4) land 0x0f));
Buffer.add_char buf (nibble_to_hex (i land 0x0f))
let escape s =
let buf = Buffer.create 100 in
let add_char = Buffer.add_char buf in
let add_string = Buffer.add_string buf in
let l = String.length s in
for i = 0 to l - 1 do
match s.[i] with
| '"' -> add_string "\\\""
| '\\' -> add_string "\\\\"
| '\b' -> add_string "\\b"
| '\012' -> add_string "\\f"
| '\n' -> add_string "\\n"
| '\r' -> add_string "\\r"
| '\t' -> add_string "\\t"
| '\x00'..'\x1F'
| '\x7F' as c ->
add_string "\\u00"; add_hex_byte buf (int_of_char c)
| _ -> add_char s.[i]
done;
Buffer.contents buf
let json_writer ~writer ~eol ~incr ~psep json =
let psep = ":" ^ psep in
let string_of_char c = String.make 1 c in
let write_char c = writer (string_of_char c) in
let write_string = writer in
let write_quote_string s =
write_char '"'
>>= fun () -> writer (escape s)
>>= fun () -> write_char '"'
in
let write_int i = write_string (string_of_int i) in
let write_float f = write_string (Compliance.number_to_string f) in
let write_list off f l =
let ldr = String.make off ' ' in
let rec loop = function
| [] -> return ()
| hd::tl -> write_string ("," ^ eol ^ ldr) >>= fun () -> f hd >>= fun () -> loop tl
in
let first = function
| [] -> return ()
| hd::tl -> write_string (eol ^ ldr) >>= fun () -> f hd >>= fun () -> loop tl
in
first l
in
let rec fmt off value =
match value with
| `Assoc o ->
let ldr = String.make off ' ' in
write_string "{"
>>= fun () -> json_assoc (off + incr) o
>>= fun () -> write_string (eol ^ ldr ^ "}")
| `List l ->
let ldr = String.make off ' ' in
write_string "["
>>= fun () -> json_list (off + incr) l;
>>= fun () -> write_string (eol ^ ldr ^ "]")
| `Null -> write_string "null"
| `Bool b -> write_string (string_of_bool b)
| `Int i -> write_int i
| `Intlit s -> write_string s
| `Float f -> write_float f
| `Floatlit s -> write_string s
| `String s -> write_quote_string s
| `Stringlit s -> write_string s
| `Tuple t ->
let ldr = String.make off ' ' in
write_string ("(" ^ eol)
>>= fun () -> json_list (off + incr) t
>>= fun () -> write_string (eol ^ ldr ^ ")")
| `Variant v ->
let ldr = String.make off ' ' in
write_string ("<" ^ eol)
>>= fun () -> variant (off + incr) v
>>= fun () -> write_string (eol ^ ldr ^ ">")
and json_assoc off o =
write_list off (fun v -> pair off v) o
and pair off (k, v) = write_quote_string k >>= fun () -> write_string psep >>= fun () -> fmt off v
and json_list off l =
write_list off (fun v -> fmt off v) l
and variant off (k, j) =
write_quote_string k
>>= fun () ->
match j with
| Some j -> write_string psep >>= fun () -> fmt (off + incr) j
| None -> return ()
in
fmt 0 json >>= fun () -> write_string eol
let write_json ~writer json = json_writer ~writer ~eol:"" ~incr:0 ~psep:"" json
let write_json_hum ~writer json = json_writer ~writer ~eol:"\n" ~incr:2 ~psep:" " json
end