1 import Base 2 import ./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 16 type 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. 28 def 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 35 def arr(+xs: List<&2, Json>) -> Json: 36 JArr{arr.chain(xs)} 37 38 def field.of(kv: String & Json, +rest: Json) -> Json: 39 (k, v) = kv 40 JField{k, v, rest} 41 42 def 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. 50 def obj(kvs: List<(String & Json)>) -> Json: 51 JObj{obj.chain(kvs)} 52 53 # Number from its decimal text. 54 def num(+t: String) -> Json: 55 JNum{t} 56 57 def str(+s: String) -> Json: 58 JStr{s} 59 60 # ---- equality ------------------------------------------------------------------------ 61 62 def 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 71 def 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 80 def 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 91 def 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. 102 def 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)