src/json.bend

bend-toon-codec@0.1.0.0 · 113 lines · 13 definitions

1import Base
2import bend-toon-codec@0.1.0.0/src/num.bend as N
3
4# The JSON data model TOON speaks.
5#
6# The shape is the one cswap-bend uses (`src/json.bend` there): arrays and
7# objects are chains inside Json itself (JNil, JCons, JField) rather than
8# Lists, so every traversal is one structurally recursive def. Json is Data
9# (reusable). Converting to or from cswap-bend's Json is a constructor-for-
10# constructor map, one def of seven arms.
11#
12# Numbers keep their source text (JNum{"1.50"}); encode and decode write the
13# canonical decimal form. A JNum whose text is not a number (NaN, Infinity)
14# encodes as null (spec section 3).
15
16type Json is Data:
17  JNull{}
18  JBool{b: Bool}
19  JNum{text: String}
20  JStr{s: String}
21  JArr{items: Json}
22  JObj{fields: Json}
23  JNil{}
24  JCons{head: Json, tail: Json}
25  JField{key: String, val: Json, tail: Json}
26
27# Array of the values, in order.
28def arr.chain(+xs: List<&2, Json>) -> Json:
29  match xs:
30    case Nil{}:
31      JNil{}
32    case Con{h, t}:
33      JCons{h, arr.chain(t)}
34
35def arr(+xs: List<&2, Json>) -> Json:
36  JArr{arr.chain(xs)}
37
38def field.of(kv: String & Json, +rest: Json) -> Json:
39  (k, v) = kv
40  JField{k, v, rest}
41
42def obj.chain(kvs: List<(String & Json)>) -> Json:
43  match kvs:
44    case Nil{}:
45      JNil{}
46    case Con{kv, t}:
47      field.of(kv, obj.chain(t))
48
49# Object from ordered (key, value) pairs.
50def obj(kvs: List<(String & Json)>) -> Json:
51  JObj{obj.chain(kvs)}
52
53# Number from its decimal text.
54def num(+t: String) -> Json:
55  JNum{t}
56
57def str(+s: String) -> Json:
58  JStr{s}
59
60# ---- equality ------------------------------------------------------------------------
61
62def num.eq(+a: Maybe<&2, String>, +b: Maybe<&2, String>) -> Bool:
63  match a b:
64    case Some{x} Some{y}:
65      String.eq(x, y)
66    case None{} None{}:
67      True{}
68    case _ _:
69      False{}
70
71def bool.eq(+a: Bool, +b: Bool) -> Bool:
72  match a b:
73    case True{} True{}:
74      True{}
75    case False{} False{}:
76      True{}
77    case _ _:
78      False{}
79
80def lit.eq(+a: Json, +b: Json) -> Bool:
81  match a b:
82    case JNull{} JNull{}:
83      True{}
84    case JBool{x} JBool{y}:
85      bool.eq(x, y)
86    case JNil{} JNil{}:
87      True{}
88    case _ _:
89      False{}
90
91def prim.eq(+a: Json, +b: Json) -> Bool:
92  match a b:
93    case JNum{x} JNum{y}:
94      num.eq(N.canon(x), N.canon(y))
95    case JStr{x} JStr{y}:
96      String.eq(x, y)
97    case _ _:
98      lit.eq(a, b)
99
100# JSON-model equality (spec section 2): same shape, same ordered keys, numbers
101# equal by value (`1.0` equals `1`), strings equal by code points.
102def eq(+a: Json, +b: Json) -> Bool:
103  match a b:
104    case JArr{x} JArr{y}:
105      eq(x, y)
106    case JObj{x} JObj{y}:
107      eq(x, y)
108    case JCons{h, t} JCons{h2, t2}:
109      eq(h, h2) && eq(t, t2)
110    case JField{k, v, t} JField{k2, v2, t2}:
111      String.eq(k, k2) && eq(v, v2) && eq(t, t2)
112    case _ _:
113      prim.eq(a, b)