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
module List = ListLabels
type 'number t =
[ `Null
| `False
| `True
| `String of string
| `Number of 'number
| `Object of (string * 'number t) list
| `Array of 'number t list
]
module Or_raw = struct
type ('number, 't) t =
[< `Raw_json_string of string
| `Null
| `False
| `True
| `String of string
| `Number of 'number
| `Object of (string * 't) list
| `Array of 't list
]
as
't
end
module Parser = struct
type 'a json = 'a t
open Angstrom
let string_ignored s = string s *> return ()
let ws =
skip_while (function
| '\x20' | '\x0a' | '\x0d' | '\x09' -> true
| _ -> false)
;;
let fail_word =
take_while1 (function
| 'a' .. 'z' -> true
| _ -> false)
>>= fun s -> fail (Printf.sprintf "unexpected string: '%s'" s)
;;
let lchar c = ws *> char c
let rsb = lchar ']'
let rcb = lchar '}'
let ns, vs = lchar ':', lchar ','
let quo = lchar '"'
let word s (res : [< `True | `False | `Null ]) =
(string_ignored s >>| (fun () -> res) <|> fail_word
: [< `True | `False | `Null ] Angstrom.t
:> _ json Angstrom.t)
;;
let _false = word "false" `False
let _true = word "true" `True
let _null = word "null" `Null
let num number =
let%bind.Angstrom s =
take_while1 (function
| '-' | '+' | '0' .. '9' | '.' | 'e' | 'E' -> true
| _ -> false)
in
match number s with
| Ok x -> return (`Number x)
| Error msg -> fail msg
;;
let create_without_trailing_whitespace (type number) number =
let advance1 = advance 1 in
let pair (x : string) (y : number json) = x, y in
let str = Json_string.parse in
fix (fun (json : number json Angstrom.t) ->
let mem = lift2 pair (quo *> str <* ns) json in
let obj : number json t =
advance1 *> sep_by vs mem <* rcb >>| fun ms -> `Object ms
in
let obj = obj <?> "object" in
let arr : number json t =
advance1 *> sep_by vs json <* rsb >>| fun vs -> `Array vs
in
let arr = arr <?> "array" in
let str : number json t = advance1 *> str >>| fun s -> `String s in
let str = str <?> "string" in
let num = num number <?> "number" in
let fail_char ?hint char =
let message = Printf.sprintf "unexpected character: '%c'" char in
match hint with
| Some hint -> fail (Printf.sprintf "%s (%s)" message hint)
| None -> fail message
in
commit *> ws *> peek_char_fail
>>= function
| 'f' -> _false
| 'n' -> _null
| 't' -> _true
| '{' -> obj
| '[' -> arr
| '"' -> str
| '-' | '+' | '0' .. '9' | '.' ->
num
| '<' -> fail_char '<' ~hint:"does your string contain HTML instead of JSON?"
| c -> fail_char c)
<?> "json"
;;
let create number = create_without_trailing_whitespace number <* ws
end
module Serializer = struct
let spaces = String.init 100 (fun _ -> ' ')
let rec write_spaces faraday num =
if num > String.length spaces
then (
Faraday.write_string faraday spaces;
write_spaces faraday (num - String.length spaces))
else Faraday.write_string faraday spaces ~len:num
;;
let maybe_newline_and_indent ~spaces faraday indent =
if spaces > 0
then (
Faraday.write_char faraday '\n';
write_spaces faraday indent)
;;
let rec serialize_hum' ~indent ~spaces serialize_number t faraday =
match t with
| `Null -> Faraday.write_string faraday "null"
| `False -> Faraday.write_string faraday "false"
| `True -> Faraday.write_string faraday "true"
| `String string -> Json_string.serialize faraday string
| `Number number -> serialize_number faraday number
| `Object items ->
serialize_list ~indent ~spaces serialize_number faraday "{}" serialize_kv items
| `Array items ->
serialize_list ~indent ~spaces serialize_number faraday "[]" serialize_hum' items
| `Raw_json_string string -> Faraday.write_string faraday string
and serialize_list
: 'a 'b.
indent:int
-> spaces:int
-> 'a
-> Faraday.t
-> string
-> (indent:int -> spaces:int -> 'a -> 'b -> Faraday.t -> unit)
-> 'b list
-> unit
=
fun ~indent ~spaces serialize_number faraday brackets serialize_item items ->
match items with
| [] -> Faraday.write_string faraday brackets
| item :: items ->
Faraday.write_char faraday brackets.[0];
let indent = indent + spaces in
maybe_newline_and_indent ~spaces faraday indent;
serialize_item ~indent ~spaces serialize_number item faraday;
List.iter items ~f:(fun item ->
Faraday.write_char faraday ',';
maybe_newline_and_indent ~spaces faraday indent;
serialize_item ~indent ~spaces serialize_number item faraday);
let indent = indent - spaces in
maybe_newline_and_indent ~spaces faraday indent;
Faraday.write_char faraday brackets.[1]
and serialize_kv ~indent ~spaces serialize_number (k, v) faraday =
Json_string.serialize faraday k;
Faraday.write_char faraday ':';
if spaces > 0 then Faraday.write_char faraday ' ';
serialize_hum' ~indent ~spaces serialize_number v faraday
;;
let create s t = serialize_hum' ~indent:0 ~spaces:0 s t
let create_hum ~spaces s t = serialize_hum' ~indent:0 ~spaces s t
end