json.bend
bend-anthropic-sdk@0.1.0.0 · 413 lines · 0 definitions
1import Base
2
3# Decimal numbers remain text; objects retain ordering and unknown fields.
4type Member<a, -A: Kind(a)> is Kind(a):
5 Field{key: String, value: A}
6
7type Json is Data:
8 Null{}
9 Boolean{value: Bool}
10 Number{text: String}
11 Text{value: String}
12 Array{items: List<&2, Json>}
13 Object{fields: List<&2, Member<&2, Json>>}
14
15def hex(n: U32) -> String:
16 String.take(String.drop("0123456789abcdef", U32.to_nat(n)), 1n)
17
18def escape_code(control: Bool, +c: U32) -> String:
19 match control:
20 case True{}:
21 "\\u00" ++ hex((c / 16 : U32)) ++ hex((c % 16 : U32))
22 case False{}:
23 SCon{Chr{c}, SNil{}}
24
25def escape_char(+c: U32) -> String:
26 escape_code((c < 32 : U32), c)
27
28def escape_go(s: String, acc: String) -> String:
29 match s:
30 case SNil{}: String.reverse(acc)
31 case SCon{'"', rest}: escape_go(rest, "\"\\" ++ acc)
32 case SCon{'\\', rest}: escape_go(rest, "\\\\" ++ acc)
33 case SCon{Chr{c}, rest}: escape_go(rest, String.reverse(escape_char(c)) ++ acc)
34
35def escape(s: String) -> String:
36 escape_go(s, "")
37
38def quote(s: String) -> String:
39 "\"" ++ escape(s) ++ "\""
40
41def comma(empty: Bool) -> String:
42 match empty:
43 case True{}:
44 ""
45 case False{}:
46 ","
47
48@unsafe
49def stringify(value: Json) -> String:
50 match value:
51 case Null{}:
52 "null"
53 case Boolean{True{}}:
54 "true"
55 case Boolean{False{}}:
56 "false"
57 case Number{text}:
58 text
59 case Text{value}:
60 quote(value)
61 case Array{Nil{}}:
62 "[]"
63 case Array{Con{head, +tail}}:
64 "[" ++ stringify(head) ++ comma(List.is_empty(&2, Json, tail)) ++ String.drop(stringify(Array{tail}), 1n)
65 case Object{Nil{}}:
66 "{}"
67 case Object{Con{Field{key, value}, +tail}}:
68 "{" ++ quote(key) ++ ":" ++ stringify(value) ++ comma(List.is_empty(&2, Member<&2, Json>, tail)) ++ String.drop(stringify(Object{tail}), 1n)
69
70def choose(found: Bool, value: Json, other: Maybe<&2, Json>) -> Maybe<&2, Json>:
71 match found:
72 case True{}:
73 Some{value}
74 case False{}:
75 other
76
77def get_fields(fields: List<&2, Member<&2, Json>>, +key: String) -> Maybe<&2, Json>:
78 match fields:
79 case Nil{}:
80 None{}
81 case Con{Field{name, value}, tail}:
82 choose(String.eq(name, key), value, get_fields(tail, key))
83
84def get(value: Json, key: String) -> Maybe<&2, Json>:
85 match value:
86 case Object{fields}:
87 get_fields(fields, key)
88 case _:
89 None{}
90
91def text(value: Maybe<&2, Json>) -> String:
92 match value:
93 case Some{Text{s}}:
94 s
95 case _:
96 ""
97
98type Token is Data:
99 Atom{value: Json}
100 OpenArray{}
101 CloseArray{}
102 OpenObject{}
103 CloseObject{}
104 Colon{}
105 Comma{}
106
107def select_u32(ok: Bool, yes: U32, no: U32) -> U32:
108 match ok:
109 case True{}: yes
110 case False{}: no
111
112def hex_digit(ch: Char) -> U32:
113 +c = Char.to_u32(ch)
114 select_u32((c >= 48 : U32) && (c <= 57 : U32), (c - 48 : U32),
115 select_u32((c >= 97 : U32) && (c <= 102 : U32), (c - 87 : U32),
116 select_u32((c >= 65 : U32) && (c <= 70 : U32), (c - 55 : U32), 256)))
117
118def checked_char(ok: Bool, code: U32) -> Result<&1, &1, String, Char>:
119 match ok:
120 case True{}: Done{Chr{code}}
121 case False{}: Fail{"invalid JSON Unicode escape"}
122
123def hex_checked(ok: Bool, code: U32) -> Result<&1, &1, String, U32>:
124 match ok:
125 case True{}: Done{code}
126 case False{}: Fail{"invalid JSON Unicode escape"}
127
128def hex4(+a: U32, +b: U32, +c: U32, +d: U32) -> Result<&1, &1, String, U32>:
129 hex_checked((a < 16 : U32) && (b < 16 : U32) && (c < 16 : U32) && (d < 16 : U32), (a * 4096 + b * 256 + c * 16 + d : U32))
130
131def unicode_escape(high: Bool, +code: U32, rest: String) -> Result<&1, &1, String, Char & String>:
132 match high:
133 case True{}:
134 match rest:
135 case SCon{'\\', SCon{'u', SCon{a, SCon{b, SCon{c, SCon{d, tail}}}}}}:
136 do Result<&1, &1, String, Char & String>:
137 +low : U32 <- hex4(hex_digit(a), hex_digit(b), hex_digit(c), hex_digit(d))
138 ch : Char <- checked_char((low >= 56320 : U32) && (low <= 57343 : U32), (65536 + (code - 55296) * 1024 + low - 56320 : U32))
139 return (ch, tail)
140 case _: Fail{"unpaired JSON surrogate"}
141 case False{}:
142 do Result<&1, &1, String, Char & String>:
143 ch : Char <- checked_char((code < 55296 : U32) || (code > 57343 : U32), code)
144 return (ch, rest)
145
146def unicode_escape_code(result: Result<&1, &1, String, U32>, rest: String) -> Result<&1, &1, String, Char & String>:
147 match result:
148 case Fail{e}: Fail{e}
149 case Done{+code}: unicode_escape((code >= 55296 : U32) && (code <= 56319 : U32), code, rest)
150
151def unescape(s: String) -> Result<&1, &1, String, Char & String>:
152 match s:
153 case SCon{'"', rest}: Done{('"', rest)}
154 case SCon{'\\', rest}: Done{('\\', rest)}
155 case SCon{'/', rest}: Done{('/', rest)}
156 case SCon{'b', rest}: Done{(Chr{8}, rest)}
157 case SCon{'f', rest}: Done{(Chr{12}, rest)}
158 case SCon{'n', rest}: Done{('\n', rest)}
159 case SCon{'r', rest}: Done{('\r', rest)}
160 case SCon{'t', rest}: Done{('\t', rest)}
161 case SCon{'u', SCon{a, SCon{b, SCon{c, SCon{d, rest}}}}}:
162 unicode_escape_code(hex4(hex_digit(a), hex_digit(b), hex_digit(c), hex_digit(d)), rest)
163 case _: Fail{"invalid JSON escape"}
164
165def string_char(ok: Bool, ch: Char, rest: String, acc: String,
166 next: String -> String -> Result<&1, &1, String, String & String>) -> Result<&1, &1, String, String & String>:
167 match ok:
168 case True{}: next(rest, SCon{ch, acc})
169 case False{}: Fail{"unescaped control character"}
170
171def string_escape(result: Result<&1, &1, String, Char & String>, acc: String,
172 next: String -> String -> Result<&1, &1, String, String & String>) -> Result<&1, &1, String, String & String>:
173 match result:
174 case Fail{e}: Fail{e}
175 case Done{(ch, rest)}: next(rest, SCon{ch, acc})
176
177def string_read(fuel: Nat, s: String, acc: String) -> Result<&1, &1, String, String & String>:
178 match fuel:
179 case 0n: Fail{"JSON string limit"}
180 case 1n+n:
181 match s:
182 case SNil{}: Fail{"unterminated JSON string"}
183 case SCon{'"', rest}: Done{(String.reverse(acc), rest)}
184 case SCon{'\\', rest}:
185 string_escape(unescape(rest), acc, r => a => string_read(n, r, a))
186 case SCon{Chr{+c}, rest}:
187 string_char((c >= 32 : U32), Chr{c}, rest, acc, r => a => string_read(n, r, a))
188
189# Number states avoid matching U32 bit constructors in the parser.
190type NumberState is Data:
191 Start{}
192 Minus{}
193 Zero{}
194 Integer{}
195 Dot{}
196 Fraction{}
197 Exponent{}
198 Sign{}
199 Digits{}
200 Bad{}
201
202def select_state(ok: Bool, yes: NumberState, no: NumberState) -> NumberState:
203 match ok:
204 case True{}: yes
205 case False{}: no
206
207def number_state(state: NumberState, +ch: Char) -> NumberState:
208 match state:
209 case Start{}: select_state(Char.is_eq(ch, '-'), Minus{}, select_state(Char.is_eq(ch, '0'), Zero{}, select_state(Char.is_digit(ch), Integer{}, Bad{})))
210 case Minus{}: select_state(Char.is_eq(ch, '0'), Zero{}, select_state(Char.is_digit(ch), Integer{}, Bad{}))
211 case Zero{}: select_state(Char.is_eq(ch, '.'), Dot{}, select_state(Char.is_eq(ch, 'e') || Char.is_eq(ch, 'E'), Exponent{}, Bad{}))
212 case Integer{}: select_state(Char.is_digit(ch), Integer{}, select_state(Char.is_eq(ch, '.'), Dot{}, select_state(Char.is_eq(ch, 'e') || Char.is_eq(ch, 'E'), Exponent{}, Bad{})))
213 case Dot{}: select_state(Char.is_digit(ch), Fraction{}, Bad{})
214 case Fraction{}: select_state(Char.is_digit(ch), Fraction{}, select_state(Char.is_eq(ch, 'e') || Char.is_eq(ch, 'E'), Exponent{}, Bad{}))
215 case Exponent{}: select_state(Char.is_eq(ch, '+') || Char.is_eq(ch, '-'), Sign{}, select_state(Char.is_digit(ch), Digits{}, Bad{}))
216 case Sign{}: select_state(Char.is_digit(ch), Digits{}, Bad{})
217 case Digits{}: select_state(Char.is_digit(ch), Digits{}, Bad{})
218 case Bad{}: Bad{}
219
220def number_end(state: NumberState) -> Bool:
221 match state:
222 case Zero{}: True{}
223 case Integer{}: True{}
224 case Fraction{}: True{}
225 case Digits{}: True{}
226 case _: False{}
227
228def number_valid(s: String, state: NumberState) -> Bool:
229 match s:
230 case SNil{}: number_end(state)
231 case SCon{ch, rest}: number_valid(rest, number_state(state, ch))
232
233def primitive(ok: Bool, raw: String) -> Result<&1, &1, String, Token>:
234 match ok:
235 case True{}: Done{Atom{Number{raw}}}
236 case False{}: Fail{"invalid JSON literal"}
237
238def literal_known(is_null: Bool, is_true: Bool, is_false: Bool, +raw: String) -> Result<&1, &1, String, Token>:
239 match is_null is_true is_false:
240 case True{} _ _: Done{Atom{Null{}}}
241 case _ True{} _: Done{Atom{Boolean{True{}}}}
242 case _ _ True{}: Done{Atom{Boolean{False{}}}}
243 case _ _ _: primitive(number_valid(raw, Start{}), raw)
244
245def literal(+raw: String) -> Result<&1, &1, String, Token>:
246 literal_known(String.eq(raw, "null"), String.eq(raw, "true"), String.eq(raw, "false"), raw)
247
248def delimiter(+c: Char) -> Bool:
249 Char.is_eq(c, ' ') || Char.is_eq(c, '\n') || Char.is_eq(c, '\r') || Char.is_eq(c, '\t') || Char.is_eq(c, ',') || Char.is_eq(c, ']') || Char.is_eq(c, '}')
250
251def word_step(stop: Bool, c: Char, rest: String, acc: String,
252 next: String -> String -> String & String) -> String & String:
253 match stop:
254 case True{}: (String.reverse(acc), SCon{c, rest})
255 case False{}: next(rest, SCon{c, acc})
256
257def word(s: String, acc: String) -> String & String:
258 match s:
259 case SNil{}: (String.reverse(acc), "")
260 case SCon{+c, +rest}: word_step(delimiter(c), c, rest, acc, r => a => word(rest, a))
261
262def lex_string(result: Result<&1, &1, String, String & String>,
263 next: String -> Maybe<&2, Token> -> Result<&1, &1, String, List<&2, Token>>) -> Result<&1, &1, String, List<&2, Token>>:
264 match result:
265 case Fail{e}: Fail{e}
266 case Done{(s, rest)}: next(rest, Some{Atom{Text{s}}})
267
268def lex_word(pair: String & String,
269 next: String -> Maybe<&2, Token> -> Result<&1, &1, String, List<&2, Token>>) -> Result<&1, &1, String, List<&2, Token>>:
270 (raw, rest) = pair
271 do Result<&1, &1, String, List<&2, Token>>:
272 token : Token <- literal(raw)
273 next(rest, Some{token})
274
275type LexKind is Data:
276 Space{}
277 LArray{}
278 RArray{}
279 LObject{}
280 RObject{}
281 LColon{}
282 LComma{}
283 Quote{}
284 Other{}
285
286def select_lex(ok: Bool, yes: LexKind, no: LexKind) -> LexKind:
287 match ok:
288 case True{}: yes
289 case False{}: no
290
291def lex_kind(+c: Char) -> LexKind:
292 select_lex(Char.is_eq(c, ' ') || Char.is_eq(c, '\n') || Char.is_eq(c, '\r') || Char.is_eq(c, '\t'), Space{},
293 select_lex(Char.is_eq(c, '['), LArray{}, select_lex(Char.is_eq(c, ']'), RArray{},
294 select_lex(Char.is_eq(c, '{'), LObject{}, select_lex(Char.is_eq(c, '}'), RObject{},
295 select_lex(Char.is_eq(c, ':'), LColon{}, select_lex(Char.is_eq(c, ','), LComma{},
296 select_lex(Char.is_eq(c, '"'), Quote{}, Other{}))))))))
297
298def lex_step(kind: LexKind, c: Char, +rest: String,
299 next: String -> Maybe<&2, Token> -> Result<&1, &1, String, List<&2, Token>>) -> Result<&1, &1, String, List<&2, Token>>:
300 match kind:
301 case Space{}: next(rest, None{})
302 case LArray{}: next(rest, Some{OpenArray{}})
303 case RArray{}: next(rest, Some{CloseArray{}})
304 case LObject{}: next(rest, Some{OpenObject{}})
305 case RObject{}: next(rest, Some{CloseObject{}})
306 case LColon{}: next(rest, Some{Colon{}})
307 case LComma{}: next(rest, Some{Comma{}})
308 case Quote{}: lex_string(string_read(1n+String.length(rest), rest, ""), next)
309 case Other{}: lex_word(word(SCon{c, rest}, ""), next)
310
311def lex_acc(token: Maybe<&2, Token>, acc: List<&2, Token>) -> List<&2, Token>:
312 match token:
313 case None{}: acc
314 case Some{t}: Con{t, acc}
315
316def lex(fuel: Nat, s: String, acc: List<&2, Token>) -> Result<&1, &1, String, List<&2, Token>>:
317 match fuel:
318 case 0n: Fail{"JSON token limit"}
319 case 1n+n:
320 match s:
321 case SNil{}: Done{List.reverse(&2, Token, acc)}
322 case SCon{+c, rest}: lex_step(lex_kind(c), c, rest, r => t => lex(n, r, lex_acc(t, acc)))
323
324type Parse is Type:
325 Parsed{value: Json, rest: List<&2, Token>}
326 Invalid{message: String}
327
328type Mode is Data:
329 Value{}
330 Elements{acc: List<&2, Json>, empty: Bool}
331 Fields{acc: List<&2, Member<&2, Json>>, empty: Bool}
332
333def array_end(ok: Bool, acc: List<&2, Json>, rest: List<&2, Token>) -> Parse:
334 match ok:
335 case True{}: Parsed{Array{List.reverse(&2, Json, acc)}, rest}
336 case False{}: Invalid{"trailing array comma"}
337
338def object_end(ok: Bool, acc: List<&2, Member<&2, Json>>, rest: List<&2, Token>) -> Parse:
339 match ok:
340 case True{}: Parsed{Object{List.reverse(&2, Member<&2, Json>, acc)}, rest}
341 case False{}: Invalid{"trailing object comma"}
342
343def array_after(result: Parse, acc: List<&2, Json>, next: Mode -> List<&2, Token> -> Parse) -> Parse:
344 match result:
345 case Invalid{e}: Invalid{e}
346 case Parsed{value, Con{Comma{}, rest}}: next(Elements{Con{value, acc}, False{}}, rest)
347 case Parsed{value, Con{CloseArray{}, rest}}: array_end(True{}, Con{value, acc}, rest)
348 case Parsed{value, rest}: Invalid{"expected array comma or closing bracket"}
349
350def object_after(result: Parse, key: String, acc: List<&2, Member<&2, Json>>, next: Mode -> List<&2, Token> -> Parse) -> Parse:
351 match result:
352 case Invalid{e}: Invalid{e}
353 case Parsed{value, Con{Comma{}, rest}}: next(Fields{Con{Field{key, value}, acc}, False{}}, rest)
354 case Parsed{value, Con{CloseObject{}, rest}}: object_end(True{}, Con{Field{key, value}, acc}, rest)
355 case Parsed{value, rest}: Invalid{"expected object comma or closing brace"}
356
357def parse(fuel: Nat, mode: Mode, tokens: List<&2, Token>) -> Parse:
358 match fuel:
359 case 0n: Invalid{"JSON nesting or token limit"}
360 case 1n+ +n:
361 match mode tokens:
362 case Value{} Con{Atom{value}, rest}: Parsed{value, rest}
363 case Value{} Con{OpenArray{}, rest}: parse(n, Elements{Nil{}, True{}}, rest)
364 case Value{} Con{OpenObject{}, rest}: parse(n, Fields{Nil{}, True{}}, rest)
365 case Elements{acc, empty} Con{CloseArray{}, rest}: array_end(empty, acc, rest)
366 case Elements{acc, empty} ts: array_after(parse(n, Value{}, ts), acc, m => t => parse(n, m, t))
367 case Fields{acc, empty} Con{CloseObject{}, rest}: object_end(empty, acc, rest)
368 case Fields{acc, empty} Con{Atom{Text{key}}, Con{Colon{}, rest}}:
369 object_after(parse(n, Value{}, rest), key, acc, m => t => parse(n, m, t))
370 case _ _: Invalid{"expected JSON value or object key"}
371
372def finish(result: Parse) -> Result<&1, &1, String, Json>:
373 match result:
374 case Invalid{e}: Fail{e}
375 case Parsed{value, Nil{}}: Done{value}
376 case Parsed{value, rest}: Fail{"trailing JSON tokens"}
377
378def depth_guard(ok: Bool, depth: U32, next: U32 -> Bool) -> Bool:
379 match ok:
380 case True{}: next(depth)
381 case False{}: False{}
382
383def depth_step(token: Token, +depth: U32, next: U32 -> Bool) -> Bool:
384 match token:
385 case OpenArray{}: depth_guard((depth < 64 : U32), (depth + 1 : U32), next)
386 case OpenObject{}: depth_guard((depth < 64 : U32), (depth + 1 : U32), next)
387 case CloseArray{}: depth_guard((depth > 0 : U32), (depth - 1 : U32), next)
388 case CloseObject{}: depth_guard((depth > 0 : U32), (depth - 1 : U32), next)
389 case _: next(depth)
390
391def depth_valid(tokens: List<&2, Token>, depth: U32) -> Bool:
392 match tokens:
393 case Nil{}: U32.is_eq(depth, 0)
394 case Con{token, rest}: depth_step(token, depth, d => depth_valid(rest, d))
395
396def token_count(tokens: List<&2, Token>, count: U32) -> U32:
397 match tokens:
398 case Nil{}: count
399 case Con{token, rest}: token_count(rest, (count + 1 : U32))
400
401def parse_checked(ok: Bool, count: U32, tokens: List<&2, Token>) -> Result<&1, &1, String, Json>:
402 match ok:
403 case False{}: Fail{"JSON exceeds 64 nesting levels or 65536 tokens, or has unmatched brackets"}
404 case True{}: finish(parse(1n+U32.to_nat(count), Value{}, tokens))
405
406def parse_tokens(+tokens: List<&2, Token>) -> Result<&1, &1, String, Json>:
407 +count = token_count(tokens, 0)
408 parse_checked(depth_valid(tokens, 0) && (count <= 65536 : U32), count, tokens)
409
410def read(+s: String) -> Result<&1, &1, String, Json>:
411 do Result<&1, &1, String, Json>:
412 tokens : List<&2, Token> <- lex(1n+String.length(s), s, Nil{})
413 parse_tokens(tokens)