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. 4 import Base 5 import ./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. 10 type 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 24 type 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 37 def 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 48 def 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` 64 def 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 68 def 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 79 def 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 87 def arr(items: List<&2, Json>) -> Json: 88 JArr{arr.go(items)} 89 90 def 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 98 def obj(pairs: List<&1, String & Json>) -> Json: 99 JObj{obj.go(pairs)} 100 101 # null 102 def null() -> Json: 103 JNull{} 104 105 # a boolean 106 def bool(flag: Bool) -> Json: 107 JBool{flag} 108 109 # a string 110 def str(txt: String) -> Json: 111 JStr{txt} 112 113 def num.go(raw: String, ok: Bool) -> Json: 114 match ok: 115 case True{}: 116 JNum{raw} 117 case False{}: 118 JNull{} 119 120 def num.ok.digit(+cp: U32) -> Bool: 121 Bool.and(U32.is_ge(cp, 48), U32.is_le(cp, 57)) 122 123 def num.ok.expmark(+cp: U32) -> Bool: 124 Bool.or(U32.is_eq(cp, 101), U32.is_eq(cp, 69)) 125 126 def num.ok.lead(+cp: U32) -> Bool: 127 Bool.and(U32.is_ge(cp, 49), U32.is_le(cp, 57)) 128 129 def num.ok.step.lead(ok: Bool) -> Num: 130 match ok: 131 case True{}: 132 NDig{} 133 case False{}: 134 NBad{} 135 136 def 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 143 def num.ok.step.int(+cp: U32) -> Num: 144 num.ok.step.int.go(cp, U32.is_eq(cp, 48)) 145 146 def 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 153 def num.ok.step.start(+cp: U32) -> Num: 154 num.ok.step.start.go(cp, U32.is_eq(cp, 45)) 155 156 def num.ok.step.mark.go(mark: Bool) -> Num: 157 match mark: 158 case True{}: 159 NExp{} 160 case False{}: 161 NBad{} 162 163 def num.ok.step.mark(+cp: U32) -> Num: 164 num.ok.step.mark.go(num.ok.expmark(cp)) 165 166 def num.ok.step.body.digit(more: Bool) -> Num: 167 match more: 168 case True{}: 169 NDig{} 170 case False{}: 171 NBad{} 172 173 def 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 180 def num.ok.step.body.other(+cp: U32) -> Num: 181 num.ok.step.body.other.go(cp, U32.is_eq(cp, 46)) 182 183 def 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 190 def num.ok.step.body(+cp: U32, more: Bool) -> Num: 191 num.ok.step.body.go(cp, more, num.ok.digit(cp)) 192 193 def 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 200 def 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 207 def num.ok.step.frac(+cp: U32, have: Bool) -> Num: 208 num.ok.step.frac.go(cp, have, num.ok.digit(cp)) 209 210 def num.ok.step.expd.go(digit: Bool) -> Num: 211 match digit: 212 case True{}: 213 NExpD{} 214 case False{}: 215 NBad{} 216 217 def num.ok.step.expd(+cp: U32) -> Num: 218 num.ok.step.expd.go(num.ok.digit(cp)) 219 220 def 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 227 def num.ok.step.exps.minus(+cp: U32) -> Num: 228 num.ok.step.exps.minus.go(cp, U32.is_eq(cp, 45)) 229 230 def 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 237 def num.ok.step.exps(+cp: U32) -> Num: 238 num.ok.step.exps.go(cp, U32.is_eq(cp, 43)) 239 240 def 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 263 def 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 276 def 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 284 def 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 288 def 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. 293 def 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 302 def 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 306 def 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 318 def 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. 329 def get(val: Json, key: String) -> Json: 330 match val: 331 case JObj{pairs}: 332 find(pairs, key) 333 case other: 334 JNull{} 335 336 def 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 344 def 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 352 type 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 360 def 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 391 def wf(val: Json) -> Bool: 392 wf.go(val, WVal{}) 393 394 # whether a chain of pairs has the key, stopping at the first match 395 def 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 405 def 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 413 def 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 425 def 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 435 def 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 443 def 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 451 def 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 459 def 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 467 def 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 477 def 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 485 def 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 493 def 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 501 def 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 506 def 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 514 def 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 522 def 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{}