src/value.bend

emerging-ezjson@1.2.0.0 · 527 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.2.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# the elements of a chain of array cells, in order
435def items.go(cells: Json) -> List<&2, Json>:
436  match cells:
437    case JCons{hd, tl}:
438      hd <> items.go(tl)
439    case _:
440      []
441
442# an array's elements, in order; [] for anything else
443def items(val: Json) -> List<&2, Json>:
444  match val:
445    case JArr{cells}:
446      items.go(cells)
447    case _:
448      []
449
450# whether a value is an array
451def is_arr(val: Json) -> Bool:
452  match val:
453    case JArr{_}:
454      True{}
455    case _:
456      False{}
457
458# whether a value is an object
459def is_obj(val: Json) -> Bool:
460  match val:
461    case JObj{_}:
462      True{}
463    case _:
464      False{}
465
466# a string, or none when the value is not one
467def as_str(val: Json) -> Maybe<&2, String>:
468  match val:
469    case JStr{s}:
470      Some{s}
471    case JSpan{src, +n}:
472      Some{span.str(src, n, U32.is_zero(n))}
473    case other:
474      None{}
475
476# a boolean, or none when the value is not one
477def as_bool(val: Json) -> Maybe<&2, Bool>:
478  match val:
479    case JBool{b}:
480      Some{b}
481    case other:
482      None{}
483
484# a number's text, or none when the value is not a number
485def as_num(val: Json) -> Maybe<&2, String>:
486  match val:
487    case JNum{raw}:
488      Some{raw}
489    case other:
490      None{}
491
492# a number as a U32, or none when it is not one or does not fit
493def as_u32(val: Json) -> Maybe<&2, U32>:
494  match val:
495    case JNum{raw}:
496      U32.read(raw)
497    case other:
498      None{}
499
500# the largest finite F32
501def f32.top() -> F32:
502  3.4028235e38
503
504# a read that stayed finite; none when it did not. A JSON number never spells
505# infinity, so an infinite read is a number too large for F32
506def f32.fit.go(xx: F32, finite: Bool) -> Maybe<&2, F32>:
507  match finite:
508    case True{}:
509      Some{xx}
510    case False{}:
511      None{}
512
513# a read that stayed finite, or none
514def f32.fit(got: Maybe<&2, F32>) -> Maybe<&2, F32>:
515  match got:
516    case Some{+xx}:
517      f32.fit.go(xx, F32.is_le(F32.abs(xx), f32.top()))
518    case None{}:
519      None{}
520
521# a number as an F32, or none when it is not one or does not fit
522def as_f32(val: Json) -> Maybe<&2, F32>:
523  match val:
524    case JNum{raw}:
525      f32.fit(F32.read(raw))
526    case other:
527      None{}