src/value.bend
emerging-ezjson@1.1.0.0 · 495 lines · 0 definitions
1# src/value: a JSON value. Null, bool, number, string, array, and object.
2# An array is a JArr of JCons cells ending in JNil; an object is a JObj of
3# JPair cells ending in JNil. A number keeps its source text.
4import Base
5import emerging-ezjson@1.1.0.0/src/lazy.bend as Lazy
6
7# a JSON value; JNil, JCons and JPair are the cells of arrays and objects.
8# JSpan and JBind are the same string and key, as the first `n` chars of a
9# source suffix, so a parse does not copy that text.
10type Json is Data:
11 JNull{}
12 JBool{b: Bool}
13 JNum{raw: String}
14 JStr{s: String}
15 JArr{items: Json}
16 JObj{pairs: Json}
17 JNil{}
18 JCons{head: Json, tail: Json}
19 JPair{key: String, val: Json, rest: Json}
20 JSpan{src: String, n: U32}
21 JBind{src: String, n: U32, val: Json, rest: Json}
22
23# where a scan of a JSON number is
24type Num is Data:
25 NStart{}
26 NInt{}
27 NZero{}
28 NDig{}
29 NDot{}
30 NFrac{}
31 NExp{}
32 NExpN{}
33 NExpD{}
34 NBad{}
35
36# the first `nn` chars of `src` as their own string
37def span.str(src: String, +nn: U32, zero: Bool) -> String:
38 match src zero:
39 case s True{}:
40 ""
41 case SNil{} False{}:
42 ""
43 case SCon{h, t} False{}:
44 SCon{h, span.str(t, U32.sub(nn, 1), U32.is_zero(U32.sub(nn, 1)))}
45
46# whether the first `nn` chars of `src` equal `key`, stopping at the first miss.
47# A count past the end of `src` covers what there is, as span.str reads it
48def span.eq.go(src: String, same: Bool, zero: Bool, key: String, +nn: U32) -> Bool:
49 match src same zero key:
50 case s False{} zero k:
51 False{}
52 case s True{} True{} SNil{}:
53 True{}
54 case s True{} True{} k:
55 False{}
56 case SCon{+a, as} True{} False{} SCon{+b, bs}:
57 span.eq.go(as, Char.is_eq(a, b), U32.is_zero(U32.sub(nn, 1)), bs, U32.sub(nn, 1))
58 case SNil{} True{} False{} SNil{}:
59 True{}
60 case s True{} False{} k:
61 False{}
62
63# whether the first `nn` chars of `src` are `key`
64def span.eq(+src: String, +nn: U32, +key: String) -> Bool:
65 span.eq.go(src, True{}, U32.is_zero(nn), key, nn)
66
67# reverses a chain of JCons or JPair cells onto acc
68def reverse(cells: Json, acc: Json) -> Json:
69 match cells:
70 case JCons{h, t}:
71 reverse(t, JCons{h, acc})
72 case JPair{k, v, r}:
73 reverse(r, JPair{k, v, acc})
74 case JBind{src, n, v, r}:
75 reverse(r, JBind{src, n, v, acc})
76 case _:
77 acc
78
79def arr.go(items: List<&2, Json>) -> Json:
80 match items:
81 case Nil{}:
82 JNil{}
83 case Con{h, t}:
84 JCons{h, arr.go(t)}
85
86# an array from a list
87def arr(items: List<&2, Json>) -> Json:
88 JArr{arr.go(items)}
89
90def obj.go(pairs: List<&1, String & Json>) -> Json:
91 match pairs:
92 case Nil{}:
93 JNil{}
94 case Con{(k, v), t}:
95 JPair{k, v, obj.go(t)}
96
97# an object from pairs
98def obj(pairs: List<&1, String & Json>) -> Json:
99 JObj{obj.go(pairs)}
100
101# null
102def null() -> Json:
103 JNull{}
104
105# a boolean
106def bool(flag: Bool) -> Json:
107 JBool{flag}
108
109# a string
110def str(txt: String) -> Json:
111 JStr{txt}
112
113def num.go(raw: String, ok: Bool) -> Json:
114 match ok:
115 case True{}:
116 JNum{raw}
117 case False{}:
118 JNull{}
119
120def num.ok.digit(+cp: U32) -> Bool:
121 Bool.and(U32.is_ge(cp, 48), U32.is_le(cp, 57))
122
123def num.ok.expmark(+cp: U32) -> Bool:
124 Bool.or(U32.is_eq(cp, 101), U32.is_eq(cp, 69))
125
126def num.ok.lead(+cp: U32) -> Bool:
127 Bool.and(U32.is_ge(cp, 49), U32.is_le(cp, 57))
128
129def num.ok.step.lead(ok: Bool) -> Num:
130 match ok:
131 case True{}:
132 NDig{}
133 case False{}:
134 NBad{}
135
136def num.ok.step.int.go(+cp: U32, zero: Bool) -> Num:
137 match zero:
138 case True{}:
139 NZero{}
140 case False{}:
141 num.ok.step.lead(num.ok.lead(cp))
142
143def num.ok.step.int(+cp: U32) -> Num:
144 num.ok.step.int.go(cp, U32.is_eq(cp, 48))
145
146def num.ok.step.start.go(+cp: U32, minus: Bool) -> Num:
147 match minus:
148 case True{}:
149 NInt{}
150 case False{}:
151 num.ok.step.int(cp)
152
153def num.ok.step.start(+cp: U32) -> Num:
154 num.ok.step.start.go(cp, U32.is_eq(cp, 45))
155
156def num.ok.step.mark.go(mark: Bool) -> Num:
157 match mark:
158 case True{}:
159 NExp{}
160 case False{}:
161 NBad{}
162
163def num.ok.step.mark(+cp: U32) -> Num:
164 num.ok.step.mark.go(num.ok.expmark(cp))
165
166def num.ok.step.body.digit(more: Bool) -> Num:
167 match more:
168 case True{}:
169 NDig{}
170 case False{}:
171 NBad{}
172
173def num.ok.step.body.other.go(+cp: U32, dot: Bool) -> Num:
174 match dot:
175 case True{}:
176 NDot{}
177 case False{}:
178 num.ok.step.mark(cp)
179
180def num.ok.step.body.other(+cp: U32) -> Num:
181 num.ok.step.body.other.go(cp, U32.is_eq(cp, 46))
182
183def num.ok.step.body.go(+cp: U32, more: Bool, digit: Bool) -> Num:
184 match digit:
185 case True{}:
186 num.ok.step.body.digit(more)
187 case False{}:
188 num.ok.step.body.other(cp)
189
190def num.ok.step.body(+cp: U32, more: Bool) -> Num:
191 num.ok.step.body.go(cp, more, num.ok.digit(cp))
192
193def num.ok.step.frac.other(+cp: U32, have: Bool) -> Num:
194 match have:
195 case True{}:
196 num.ok.step.mark(cp)
197 case False{}:
198 NBad{}
199
200def num.ok.step.frac.go(+cp: U32, have: Bool, digit: Bool) -> Num:
201 match digit:
202 case True{}:
203 NFrac{}
204 case False{}:
205 num.ok.step.frac.other(cp, have)
206
207def num.ok.step.frac(+cp: U32, have: Bool) -> Num:
208 num.ok.step.frac.go(cp, have, num.ok.digit(cp))
209
210def num.ok.step.expd.go(digit: Bool) -> Num:
211 match digit:
212 case True{}:
213 NExpD{}
214 case False{}:
215 NBad{}
216
217def num.ok.step.expd(+cp: U32) -> Num:
218 num.ok.step.expd.go(num.ok.digit(cp))
219
220def num.ok.step.exps.minus.go(+cp: U32, minus: Bool) -> Num:
221 match minus:
222 case True{}:
223 NExpN{}
224 case False{}:
225 num.ok.step.expd(cp)
226
227def num.ok.step.exps.minus(+cp: U32) -> Num:
228 num.ok.step.exps.minus.go(cp, U32.is_eq(cp, 45))
229
230def num.ok.step.exps.go(+cp: U32, plus: Bool) -> Num:
231 match plus:
232 case True{}:
233 NExpN{}
234 case False{}:
235 num.ok.step.exps.minus(cp)
236
237def num.ok.step.exps(+cp: U32) -> Num:
238 num.ok.step.exps.go(cp, U32.is_eq(cp, 43))
239
240def num.ok.step(st: Num, +cp: U32) -> Num:
241 match st:
242 case NStart{}:
243 num.ok.step.start(cp)
244 case NInt{}:
245 num.ok.step.int(cp)
246 case NZero{}:
247 num.ok.step.body(cp, False{})
248 case NDig{}:
249 num.ok.step.body(cp, True{})
250 case NDot{}:
251 num.ok.step.frac(cp, False{})
252 case NFrac{}:
253 num.ok.step.frac(cp, True{})
254 case NExp{}:
255 num.ok.step.exps(cp)
256 case NExpN{}:
257 num.ok.step.expd(cp)
258 case NExpD{}:
259 num.ok.step.expd(cp)
260 case NBad{}:
261 NBad{}
262
263def num.ok.done(st: Num) -> Bool:
264 match st:
265 case NZero{}:
266 True{}
267 case NDig{}:
268 True{}
269 case NFrac{}:
270 True{}
271 case NExpD{}:
272 True{}
273 case other:
274 False{}
275
276def num.ok.go(cs: List<&2, Char>, st: Num) -> Bool:
277 match cs:
278 case Nil{}:
279 num.ok.done(st)
280 case Con{c, t}:
281 num.ok.go(t, num.ok.step(st, Char.to_u32(c)))
282
283# whether text is a JSON number
284def num.ok(txt: String) -> Bool:
285 num.ok.go(String.to_list(txt), NStart{})
286
287# a JSON number from its text, or null when that text is not one
288def num(+raw: String) -> Json:
289 num.go(raw, num.ok(raw))
290
291# character-for-character equality. `String.eq` rebuilds both strings and
292# discards the copies; a search only needs the bool. A mismatch stops.
293def find.eq.go(left: String, right: String, same: Bool) -> Bool:
294 match left right same:
295 case SNil{} SNil{} True{}:
296 True{}
297 case SCon{+x, xs} SCon{+y, ys} True{}:
298 find.eq.go(xs, ys, Char.is_eq(x, y))
299 case l r s:
300 False{}
301
302def find.eq(lhs: String, rhs: String) -> Bool:
303 find.eq.go(lhs, rhs, True{})
304
305# the matched value, or the next pair when the key differs
306def find.at(rest: Json, +key: String, eq: Bool, vv: Json) -> Json:
307 match rest key eq vv:
308 case r k True{} val:
309 val
310 case JPair{k2, v2, r2} k False{} val:
311 find.at(r2, k, find.eq(k2, k), v2)
312 case JBind{src, n, v2, r2} k False{} val:
313 find.at(r2, k, span.eq(src, n, k), v2)
314 case r k s val:
315 JNull{}
316
317# the value of a key in a chain of pairs; JNull when the key is absent
318def find(cells: Json, +key: String) -> Json:
319 match cells:
320 case JPair{k, v, r}:
321 find.at(r, key, find.eq(k, key), v)
322 case JBind{src, n, v, r}:
323 find.at(r, key, span.eq(src, n, key), v)
324 case other:
325 JNull{}
326
327# an object's value at a key; JNull when it is not an object or has no such
328# key. A repeated key reads as the first value.
329def get(val: Json, key: String) -> Json:
330 match val:
331 case JObj{pairs}:
332 find(pairs, key)
333 case other:
334 JNull{}
335
336def at.go(cells: Json, +idx: U32) -> Json:
337 match cells:
338 case JCons{h, t}:
339 Lazy.stop(Json, U32.is_zero(idx), h, _u => at.go(t, U32.sub(idx, 1)))
340 case other:
341 JNull{}
342
343# an array's item at an index; JNull when it is not an array or has none
344def at(val: Json, idx: U32) -> Json:
345 match val:
346 case JArr{items}:
347 at.go(items, idx)
348 case other:
349 JNull{}
350
351# what wf.go checks: a value, the cells of an array, or the cells of an object
352type Shape is Data:
353 WVal{}
354 WArr{}
355 WObj{}
356
357# whether a value, or a chain of cells, has the shape the parser and the
358# builders give: arrays and objects are chains of cells ending in JNil, no
359# cell stands where a value belongs, and a number's text is a JSON number
360def wf.go(val: Json, shape: Shape) -> Bool:
361 match val shape:
362 case JNull{} WVal{}:
363 True{}
364 case JBool{_} WVal{}:
365 True{}
366 case JNum{raw} WVal{}:
367 num.ok(raw)
368 case JStr{_} WVal{}:
369 True{}
370 case JSpan{_, _} WVal{}:
371 True{}
372 case JArr{items} WVal{}:
373 wf.go(items, WArr{})
374 case JObj{pairs} WVal{}:
375 wf.go(pairs, WObj{})
376 case JNil{} WArr{}:
377 True{}
378 case JCons{hd, tl} WArr{}:
379 Lazy.stop(Bool, Bool.not(wf.go(hd, WVal{})), False{}, _u => wf.go(tl, WArr{}))
380 case JNil{} WObj{}:
381 True{}
382 case JPair{_, vv, rr} WObj{}:
383 Lazy.stop(Bool, Bool.not(wf.go(vv, WVal{})), False{}, _u => wf.go(rr, WObj{}))
384 case JBind{_, _, vv, rr} WObj{}:
385 Lazy.stop(Bool, Bool.not(wf.go(vv, WVal{})), False{}, _u => wf.go(rr, WObj{}))
386 case _ _:
387 False{}
388
389# whether a value is well-formed: the shape every value from the builders and
390# from parse has. print writes JSON text for exactly these
391def wf(val: Json) -> Bool:
392 wf.go(val, WVal{})
393
394# whether a chain of pairs has the key, stopping at the first match
395def has.go(cells: Json, +key: String) -> Bool:
396 match cells:
397 case JPair{k2, _, rr}:
398 Lazy.stop(Bool, find.eq(k2, key), True{}, _u => has.go(rr, key))
399 case JBind{src, nn, _, rr}:
400 Lazy.stop(Bool, span.eq(src, nn, key), True{}, _u => has.go(rr, key))
401 case _:
402 False{}
403
404# whether an object has the key; false when the value is not an object
405def has(val: Json, key: String) -> Bool:
406 match val:
407 case JObj{pairs}:
408 has.go(pairs, key)
409 case _:
410 False{}
411
412# how many cells a chain of array or object cells holds
413def len.go(cells: Json) -> Nat:
414 match cells:
415 case JCons{_, tl}:
416 1n+len.go(tl)
417 case JPair{_, _, rr}:
418 1n+len.go(rr)
419 case JBind{_, _, _, rr}:
420 1n+len.go(rr)
421 case _:
422 0n
423
424# how many elements an array, or members an object, has; 0 for anything else
425def len(val: Json) -> U32:
426 match val:
427 case JArr{items}:
428 U32.from_nat(len.go(items))
429 case JObj{pairs}:
430 U32.from_nat(len.go(pairs))
431 case _:
432 0
433
434# a string, or none when the value is not one
435def as_str(val: Json) -> Maybe<&2, String>:
436 match val:
437 case JStr{s}:
438 Some{s}
439 case JSpan{src, +n}:
440 Some{span.str(src, n, U32.is_zero(n))}
441 case other:
442 None{}
443
444# a boolean, or none when the value is not one
445def as_bool(val: Json) -> Maybe<&2, Bool>:
446 match val:
447 case JBool{b}:
448 Some{b}
449 case other:
450 None{}
451
452# a number's text, or none when the value is not a number
453def as_num(val: Json) -> Maybe<&2, String>:
454 match val:
455 case JNum{raw}:
456 Some{raw}
457 case other:
458 None{}
459
460# a number as a U32, or none when it is not one or does not fit
461def as_u32(val: Json) -> Maybe<&2, U32>:
462 match val:
463 case JNum{raw}:
464 U32.read(raw)
465 case other:
466 None{}
467
468# the largest finite F32
469def f32.top() -> F32:
470 3.4028235e38
471
472# a read that stayed finite; none when it did not. A JSON number never spells
473# infinity, so an infinite read is a number too large for F32
474def f32.fit.go(xx: F32, finite: Bool) -> Maybe<&2, F32>:
475 match finite:
476 case True{}:
477 Some{xx}
478 case False{}:
479 None{}
480
481# a read that stayed finite, or none
482def f32.fit(got: Maybe<&2, F32>) -> Maybe<&2, F32>:
483 match got:
484 case Some{+xx}:
485 f32.fit.go(xx, F32.is_le(F32.abs(xx), f32.top()))
486 case None{}:
487 None{}
488
489# a number as an F32, or none when it is not one or does not fit
490def as_f32(val: Json) -> Maybe<&2, F32>:
491 match val:
492 case JNum{raw}:
493 f32.fit(F32.read(raw))
494 case other:
495 None{}