1 # src/parse: tokens to one JSON value. Commas separate elements, a colon 2 # separates a key from its value, and a text has one root. 3 import Base 4 import ./value.bend as V 5 import ./lex.bend as Lex 6 7 # where the next token of an open array or object has to be 8 type Slot is Data: 9 SValue{} 10 SComma{} 11 SKey{} 12 SColon{} 13 14 # an object key: owned text, or the first `n` chars of a source suffix 15 type Key is Data: 16 KOwn{s: String} 17 KCut{src: String, n: U32} 18 19 # an open array (its items, reversed) or an open object (its pairs, reversed, 20 # and the key awaiting its value) 21 type Frame is Data: 22 FArr{items: V.Json, slot: Slot} 23 FObj{pairs: V.Json, key: Key, slot: Slot} 24 25 # the parser's state: an error was seen, the finished root, the open frames 26 type P is Data: 27 P{bad: Bool, root: Maybe<&2, V.Json>, stack: List<&2, Frame>} 28 29 def word.ok(+raw: String) -> Bool: 30 Bool.or(String.eq(raw, "null"), 31 Bool.or(String.eq(raw, "true"), 32 Bool.or(String.eq(raw, "false"), V.num.ok(raw)))) 33 34 # a bare word as a value: null, true, false, else a number kept as text 35 def word(+raw: String) -> V.Json: 36 Bool.pick(V.Json, String.eq(raw, "null"), V.JNull{}, 37 Bool.pick(V.Json, String.eq(raw, "true"), V.JBool{True{}}, 38 Bool.pick(V.Json, String.eq(raw, "false"), V.JBool{False{}}, V.JNum{raw}))) 39 40 def step.ready(stack: List<&2, Frame>, root: Maybe<&2, V.Json>) -> Bool: 41 match stack root: 42 case Nil{} None{}: 43 True{} 44 case Con{FArr{items, SValue{}}, rest} r: 45 True{} 46 case Con{FObj{pairs, key, SValue{}}, rest} r: 47 True{} 48 case s r: 49 False{} 50 51 def put.pair(key: Key, val: V.Json, pairs: V.Json) -> V.Json: 52 match key: 53 case KOwn{s}: 54 V.JPair{s, val, pairs} 55 case KCut{src, n}: 56 V.JBind{src, n, val, pairs} 57 58 def put.text(key: Key) -> V.Json: 59 match key: 60 case KOwn{s}: 61 V.JStr{s} 62 case KCut{src, n}: 63 V.JSpan{src, n} 64 65 # a finished value lands in the innermost open frame, or becomes the root 66 def put(stack: List<&2, Frame>, root: Maybe<&2, V.Json>, vv: V.Json, bad: Bool) -> P: 67 match stack root vv: 68 case Nil{} None{} val: 69 P{bad, Some{val}, Nil{}} 70 case Nil{} Some{r} val: 71 P{True{}, Some{r}, Nil{}} 72 case Con{FArr{items, SValue{}}, rest} r val: 73 P{bad, r, FArr{V.JCons{val, items}, SComma{}} <> rest} 74 case Con{FObj{pairs, k, SValue{}}, rest} r val: 75 P{bad, r, FObj{put.pair(k, val, pairs), KOwn{""}, SComma{}} <> rest} 76 case s r val: 77 P{True{}, r, Nil{}} 78 79 def put.str(stack: List<&2, Frame>, root: Maybe<&2, V.Json>, key: Key, bad: Bool) -> P: 80 match stack: 81 case Con{FObj{pairs, k, SKey{}}, rest}: 82 P{bad, root, FObj{pairs, key, SColon{}} <> rest} 83 case other: 84 put(stack, root, put.text(key), bad) 85 86 def step.open_arr.go(bad: Bool, root: Maybe<&2, V.Json>, stack: List<&2, Frame>, ok: Bool) -> P: 87 match ok: 88 case True{}: 89 P{bad, root, FArr{V.JNil{}, SValue{}} <> stack} 90 case False{}: 91 P{True{}, root, Nil{}} 92 93 def step.open_arr(st: P) -> P: 94 P{bad, +root, +stack} = st 95 step.open_arr.go(bad, root, stack, step.ready(stack, root)) 96 97 def step.open_obj.go(bad: Bool, root: Maybe<&2, V.Json>, stack: List<&2, Frame>, ok: Bool) -> P: 98 match ok: 99 case True{}: 100 P{bad, root, FObj{V.JNil{}, KOwn{""}, SKey{}} <> stack} 101 case False{}: 102 P{True{}, root, Nil{}} 103 104 def step.open_obj(st: P) -> P: 105 P{bad, +root, +stack} = st 106 step.open_obj.go(bad, root, stack, step.ready(stack, root)) 107 108 def step.comma(stack: List<&2, Frame>, root: Maybe<&2, V.Json>, bad: Bool) -> P: 109 match stack: 110 case Con{FArr{items, SComma{}}, rest}: 111 P{bad, root, FArr{items, SValue{}} <> rest} 112 case Con{FObj{pairs, k, SComma{}}, rest}: 113 P{bad, root, FObj{pairs, KOwn{""}, SKey{}} <> rest} 114 case other: 115 P{True{}, root, Nil{}} 116 117 def step.colon(stack: List<&2, Frame>, root: Maybe<&2, V.Json>, bad: Bool) -> P: 118 match stack: 119 case Con{FObj{pairs, k, SColon{}}, rest}: 120 P{bad, root, FObj{pairs, k, SValue{}} <> rest} 121 case other: 122 P{True{}, root, Nil{}} 123 124 def step.word.go(raw: String, st: P, ok: Bool) -> P: 125 P{bad, root, stack} = st 126 match ok: 127 case True{}: 128 put(stack, root, word(raw), bad) 129 case False{}: 130 P{True{}, root, stack} 131 132 def step.word(+raw: String, st: P) -> P: 133 step.word.go(raw, st, word.ok(raw)) 134 135 # a `]`: the innermost frame must be an array, empty or just after a value 136 def close_arr(stack: List<&2, Frame>, root: Maybe<&2, V.Json>, bad: Bool) -> P: 137 match stack: 138 case Con{FArr{V.JNil{}, SValue{}}, rest}: 139 put(rest, root, V.JArr{V.JNil{}}, bad) 140 case Con{FArr{items, SComma{}}, rest}: 141 put(rest, root, V.JArr{V.reverse(items, V.JNil{})}, bad) 142 case other: 143 P{True{}, root, Nil{}} 144 145 # a `}`: the innermost frame must be an object, empty or just after a value 146 def close_obj(stack: List<&2, Frame>, root: Maybe<&2, V.Json>, bad: Bool) -> P: 147 match stack: 148 case Con{FObj{V.JNil{}, k, SKey{}}, rest}: 149 put(rest, root, V.JObj{V.JNil{}}, bad) 150 case Con{FObj{pairs, k, SComma{}}, rest}: 151 put(rest, root, V.JObj{V.reverse(pairs, V.JNil{})}, bad) 152 case other: 153 P{True{}, root, Nil{}} 154 155 def step.span.ok(+raw: String, ok: Bool) -> (V.Json & Bool): 156 match ok: 157 case True{}: 158 (V.JNum{raw}, True{}) 159 case False{}: 160 (V.JNull{}, False{}) 161 162 def step.span.use(+raw: String) -> (V.Json & Bool): 163 step.span.ok(raw, V.num.ok(raw)) 164 165 def step.span.chk(src: String, +nn: U32) -> (V.Json & Bool): 166 step.span.use(V.span.str(src, nn, U32.is_zero(nn))) 167 168 def step.span.go(src: String, nn: U32, is_null: Bool, is_true: Bool, is_false: Bool) -> (V.Json & Bool): 169 match is_null is_true is_false: 170 case True{} x y: 171 (V.JNull{}, True{}) 172 case False{} True{} y: 173 (V.JBool{True{}}, True{}) 174 case False{} False{} True{}: 175 (V.JBool{False{}}, True{}) 176 case False{} False{} False{}: 177 step.span.chk(src, nn) 178 179 def step.span.use2(st: P, val: V.Json, ok: Bool) -> P: 180 P{bad, root, stack} = st 181 match ok: 182 case True{}: 183 put(stack, root, val, bad) 184 case False{}: 185 P{True{}, root, stack} 186 187 def step.span.open(st: P, got: (V.Json & Bool)) -> P: 188 (v, ok) = got 189 step.span.use2(st, v, ok) 190 191 def step.span(+src: String, +nn: U32, st: P) -> P: 192 step.span.open(st, step.span.go(src, nn, V.span.eq(src, nn, "null"), 193 V.span.eq(src, nn, "true"), V.span.eq(src, nn, "false"))) 194 195 # one token 196 def step(tok: Lex.Tok, st: P) -> P: 197 match tok: 198 case Lex.TOpenArr{}: 199 step.open_arr(st) 200 case Lex.TOpenObj{}: 201 step.open_obj(st) 202 case Lex.TCloseArr{}: 203 P{bad, root, stack} = st 204 close_arr(stack, root, bad) 205 case Lex.TCloseObj{}: 206 P{bad, root, stack} = st 207 close_obj(stack, root, bad) 208 case Lex.TColon{}: 209 P{bad, root, stack} = st 210 step.colon(stack, root, bad) 211 case Lex.TComma{}: 212 P{bad, root, stack} = st 213 step.comma(stack, root, bad) 214 case Lex.TStr{s}: 215 P{bad, root, stack} = st 216 put.str(stack, root, KOwn{s}, bad) 217 case Lex.TWord{raw}: 218 step.word(raw, st) 219 case Lex.TStrS{src, n}: 220 P{bad, root, stack} = st 221 put.str(stack, root, KCut{src, n}, bad) 222 case Lex.TWordS{src, n}: 223 step.span(src, n, st) 224 case Lex.TBad{}: 225 P{bad, root, stack} = st 226 P{True{}, root, stack} 227 228 # every token, in order 229 def run(toks: List<&2, Lex.Tok>, st: P) -> P: 230 match toks: 231 case Nil{}: 232 st 233 case Con{t, rest}: 234 run(rest, step(t, st)) 235 236 # the root, when nothing went wrong and nothing stayed open 237 def result(bad: Bool, stack: List<&2, Frame>, root: Maybe<&2, V.Json>) -> Maybe<&2, V.Json>: 238 match bad stack root: 239 case False{} Nil{} Some{j}: 240 Some{j} 241 case b s r: 242 None{} 243 244 # the value once the tokens are over 245 def finish(st: P) -> Maybe<&2, V.Json>: 246 P{bad, root, stack} = st 247 result(bad, stack, root) 248 249 # a text as a value, or None when it is not JSON 250 def parse(txt: String) -> Maybe<&2, V.Json>: 251 finish(run(Lex.tokens(txt), P{False{}, None{}, Nil{}}))