1 import Base 2 import ./src/json.bend as I 3 import ./src/encode.bend as Enc 4 import ./src/decode.bend as Dec 5 6 # bend-toon: TOON (Token-Oriented Object Notation, spec v3.3) for Bend 2. 7 # 8 # import bend-toon@0.1.0/main.bend as Toon 9 # 10 # Toon.encode(value, Toon.EncodeOpts{2, Toon.Comma{}}) -> String 11 # Toon.decode(text, Toon.DecodeOpts{2, True{}}) -> Result 12 # 13 # The types below are this file's own, so a caller can name them (Bend does 14 # not expose the types of a module's imports). They have the shape of the 15 # Json in cswap-bend's src/json.bend, constructor for constructor, and the 16 # codec works on an internal copy of the same shape (src/json.bend). 17 # README.md has the API, the value model and the conformance report. 18 19 # JSON values. Arrays and objects are chains inside Json itself: an array is 20 # JArr{JCons{a, JCons{b, JNil{}}}}, an object is 21 # JObj{JField{"k", v, JField{"k2", v2, JNil{}}}}. Numbers keep their decimal 22 # text (JNum{"1.50"}); encode writes the canonical form. 23 type Json is Data: 24 JNull{} 25 JBool{b: Bool} 26 JNum{text: String} 27 JStr{s: String} 28 JArr{items: Json} 29 JObj{fields: Json} 30 JNil{} 31 JCons{head: Json, tail: Json} 32 JField{key: String, val: Json, tail: Json} 33 34 # Delimiter of arrays and tabular rows. 35 type Delim is Data: 36 Comma{} 37 Tab{} 38 Pipe{} 39 40 # Spaces per indent level, the document delimiter, and key folding: 0 is off 41 # (the default), n of 2 or more folds chains of single-key objects into dotted 42 # keys of at most n segments (`a.b.c: 1`), 4294967295 folds whole chains. 43 type EncodeOpts is Data: 44 EncodeOpts{indent: U32, delim: Delim, fold: U32} 45 46 # Spaces per indent level (used to compute line depth), strict mode (counts, 47 # indentation, blank lines, duplicate keys), and path expansion (an unquoted 48 # key `a.b.c` becomes nested objects; quoted keys stay literal). 49 type DecodeOpts is Data: 50 DecodeOpts{indent: U32, strict: Bool, expand: Bool} 51 52 # Indent 2, comma. 53 def encode_default() -> EncodeOpts: 54 EncodeOpts{2, Comma{}, 0} 55 56 # Indent 2, strict. 57 def decode_default() -> DecodeOpts: 58 DecodeOpts{2, True{}, False{}} 59 60 # ---- conversion to and from the codec's own value type ----------------------- 61 62 def to_inner(j: Json) -> I.Json: 63 match j: 64 case JNull{}: 65 I.JNull{} 66 case JBool{b}: 67 I.JBool{b} 68 case JNum{t}: 69 I.JNum{t} 70 case JStr{s}: 71 I.JStr{s} 72 case JArr{x}: 73 I.JArr{to_inner(x)} 74 case JObj{x}: 75 I.JObj{to_inner(x)} 76 case JNil{}: 77 I.JNil{} 78 case JCons{h, t}: 79 I.JCons{to_inner(h), to_inner(t)} 80 case JField{k, v, t}: 81 I.JField{k, to_inner(v), to_inner(t)} 82 83 def of_inner(j: I.Json) -> Json: 84 match j: 85 case I.JNull{}: 86 JNull{} 87 case I.JBool{b}: 88 JBool{b} 89 case I.JNum{t}: 90 JNum{t} 91 case I.JStr{s}: 92 JStr{s} 93 case I.JArr{x}: 94 JArr{of_inner(x)} 95 case I.JObj{x}: 96 JObj{of_inner(x)} 97 case I.JNil{}: 98 JNil{} 99 case I.JCons{h, t}: 100 JCons{of_inner(h), of_inner(t)} 101 case I.JField{k, v, t}: 102 JField{k, of_inner(v), of_inner(t)} 103 104 def of_inner_result(r: Result<&2, &2, String, I.Json>) -> Result<&2, &2, String, Json>: 105 match r: 106 case Fail{e}: 107 Fail{e} 108 case Done{v}: 109 Done{of_inner(v)} 110 111 def inner_delim(d: Delim) -> Enc.Delim: 112 match d: 113 case Comma{}: 114 Enc.Comma{} 115 case Tab{}: 116 Enc.Tab{} 117 case Pipe{}: 118 Enc.Pipe{} 119 120 # ---- API --------------------------------------------------------------------- 121 122 # TOON text of a value, LF line endings, no trailing newline. NaN and Infinity 123 # (a JNum whose text is not a number) encode as null. 124 def encode(j: Json, o: EncodeOpts) -> String: 125 match o: 126 case EncodeOpts{indent, d, fold}: 127 Enc.encode(to_inner(j), Enc.EOpts{indent, inner_delim(d), fold}) 128 129 # The value of TOON text, or the first error with its line number. 130 def decode(s: String, o: DecodeOpts) -> Result<&2, &2, String, Json>: 131 match o: 132 case DecodeOpts{indent, strict, expand}: 133 of_inner_result(Dec.decode(s, Dec.DOpts{indent, strict, expand})) 134 135 # JSON-model equality: numbers by value (`1.0` equals `1`), objects by ordered 136 # keys. 137 def eq(a: Json, b: Json) -> Bool: 138 I.eq(to_inner(a), to_inner(b))