# SPDX-License-Identifier: Apache-2.0 # JSON # ==== # Adapted from H4ad/bend-stdlib/packages/bend-json/json.bend (Apache-2.0; # see LICENSE). Packaged separately, without byte wrappers; Unicode decoding, # checked encoding, and linear output accumulation replace the originals. import Base type JSON is Data: Null{} Boolean{ok: Bool} Num{lex: String} Str{text: String} Arr{items: List<&2, JSON>} Obj{fields: List<&2, JSON>} Member{name: String, value: JSON} # Char classes # ------------ def JSON.is_ws(c: Char) -> Bool: match c: case Chr{+code}: Bool.or(U32.is_eq(code, 32), Bool.or(U32.is_eq(code, 9), Bool.or(U32.is_eq(code, 10), U32.is_eq(code, 13)))) def JSON.is_digit(+c: Char) -> Bool: Char.is_digit(c) def JSON.is_zero(c: Char) -> Bool: match c: case Chr{+code}: U32.is_eq(code, 48) def JSON.is_minus(c: Char) -> Bool: match c: case Chr{+code}: U32.is_eq(code, 45) def JSON.is_plus(c: Char) -> Bool: match c: case Chr{+code}: U32.is_eq(code, 43) def JSON.is_dot(c: Char) -> Bool: match c: case Chr{+code}: U32.is_eq(code, 46) def JSON.is_e(c: Char) -> Bool: match c: case Chr{+code}: Bool.or(U32.is_eq(code, 101), U32.is_eq(code, 69)) def JSON.is_quote(c: Char) -> Bool: match c: case Chr{+code}: U32.is_eq(code, 34) def JSON.is_slash(c: Char) -> Bool: match c: case Chr{+code}: U32.is_eq(code, 92) def JSON.is_lbrk(c: Char) -> Bool: match c: case Chr{+code}: U32.is_eq(code, 91) def JSON.is_rbrk(c: Char) -> Bool: match c: case Chr{+code}: U32.is_eq(code, 93) def JSON.is_lbra(c: Char) -> Bool: match c: case Chr{+code}: U32.is_eq(code, 123) def JSON.is_rbra(c: Char) -> Bool: match c: case Chr{+code}: U32.is_eq(code, 125) def JSON.is_comma(c: Char) -> Bool: match c: case Chr{+code}: U32.is_eq(code, 44) def JSON.is_colon(c: Char) -> Bool: match c: case Chr{+code}: U32.is_eq(code, 58) def JSON.is_ctl(c: Char) -> Bool: match c: case Chr{+code}: U32.is_lt(code, 32) def JSON.hex.lo(+code: U32, d: Bool) -> Maybe<&2, U32>: match d: case True{}: Some{U32.sub(code, 48)} case False{}: None{} def JSON.hex.up(+code: U32, a: Bool) -> Maybe<&2, U32>: match a: case True{}: Some{U32.add(10, U32.sub(code, 65))} case False{}: JSON.hex.lo(code, Bool.and(U32.is_ge(code, 48), U32.is_le(code, 57))) def JSON.hex.low(+code: U32, a: Bool) -> Maybe<&2, U32>: match a: case True{}: Some{U32.add(10, U32.sub(code, 97))} case False{}: JSON.hex.up(code, Bool.and(U32.is_ge(code, 65), U32.is_le(code, 70))) def JSON.hex.val(c: Char) -> Maybe<&2, U32>: match c: case Chr{+code}: JSON.hex.low(code, Bool.and(U32.is_ge(code, 97), U32.is_le(code, 102))) # Constructors # ------------ def JSON.null() -> JSON: Null{} def JSON.boolean(ok: Bool) -> JSON: Boolean{ok} def JSON.num(+n: U32) -> JSON: Num{U32.show(n)} def JSON.str(s: String) -> JSON: Str{s} def JSON.array(xs: List<&2, JSON>) -> JSON: Arr{xs} def JSON.object(fs: List<&2, JSON>) -> JSON: Obj{fs} # Show # ---- def JSON.hex.nib.if(+d: U32, small: Bool) -> Char: match small: case True{}: Chr{U32.add(48, d)} case False{}: Chr{U32.add(87, d)} def JSON.hex.nib(+d: U32) -> Char: JSON.hex.nib.if(d, U32.is_lt(d, 10)) def JSON.hex.pair(+n: U32) -> String: SCon{JSON.hex.nib(U32.div(n, 16)), SCon{JSON.hex.nib(U32.mod(n, 16)), SNil{}}} def JSON.esc.one(+c: Char, q: Bool, sl: Bool, n: Bool, r: Bool, t: Bool, b: Bool, f: Bool, ctl: Bool) -> String: match q: case True{}: "\\\"" case False{}: match sl: case True{}: "\\\\" case False{}: match n: case True{}: "\\n" case False{}: match r: case True{}: "\\r" case False{}: match t: case True{}: "\\t" case False{}: match b: case True{}: "\\b" case False{}: match f: case True{}: "\\f" case False{}: match ctl: case True{}: "\\u00" ++ JSON.hex.pair(Char.to_u32(c)) case False{}: SCon{c, SNil{}} def JSON.esc.chr(+c: Char) -> String: match c: case Chr{+code}: JSON.esc.one(c, U32.is_eq(code, 34), U32.is_eq(code, 92), U32.is_eq(code, 10), U32.is_eq(code, 13), U32.is_eq(code, 9), U32.is_eq(code, 8), U32.is_eq(code, 12), U32.is_lt(code, 32)) def JSON.esc.go(s: String, acc: String) -> String: match s: case SNil{}: String.reverse(acc) case SCon{h, rest}: JSON.esc.go(rest, String.append(String.reverse(JSON.esc.chr(h)), acc)) def JSON.esc(s: String) -> String: "\"" ++ JSON.esc.go(s, SNil{}) ++ "\"" type JSON.Job is Data: JLit{s: String} JVal{v: JSON} JArr0{xs: List<&2, JSON>} JArr1{xs: List<&2, JSON>} JObj0{fs: List<&2, JSON>} JObj1{fs: List<&2, JSON>} @unsafe def JSON.encode.go(jobs: List<&2, JSON.Job>, acc: List<&2, String>) -> Result<&1, &1, U32 & String, String>: match jobs: case Nil{}: Done{String.concat(List.reverse(&2, String, acc))} case h <> rest: match h: case JLit{s}: JSON.encode.go(rest, s <> acc) case JVal{v}: match v: case Null{}: JSON.encode.go(rest, "null" <> acc) case Boolean{ok}: JSON.encode.go(rest, Bool.pick(String, ok, "true", "false") <> acc) case Num{+lex}: JSON.encode.number(JSON.read(lex), rest, lex, acc) case Str{text}: JSON.encode.go(rest, JSON.esc(text) <> acc) case Arr{xs}: JSON.encode.go(JArr0{xs} <> JLit{"]"} <> rest, "[" <> acc) case Obj{fields}: JSON.encode.go(JObj0{fields} <> JLit{"}"} <> rest, "{" <> acc) case Member{name, val}: Fail{(22, "member outside an object")} case JArr0{xs}: match xs: case Nil{}: JSON.encode.go(rest, acc) case x <> xt: JSON.encode.go(JVal{x} <> JArr1{xt} <> rest, acc) case JArr1{xs}: match xs: case Nil{}: JSON.encode.go(rest, acc) case x <> xt: JSON.encode.go(JVal{x} <> JArr1{xt} <> rest, "," <> acc) case JObj0{fs}: match fs: case Nil{}: JSON.encode.go(rest, acc) case m <> ft: match m: case Member{name, val}: JSON.encode.go(JLit{JSON.esc(name) ++ ":"} <> JVal{val} <> JObj1{ft} <> rest, acc) case Null{}: Fail{(22, "object field is not a member")} case Boolean{ok}: Fail{(22, "object field is not a member")} case Num{lex}: Fail{(22, "object field is not a member")} case Str{text}: Fail{(22, "object field is not a member")} case Arr{xs}: Fail{(22, "object field is not a member")} case Obj{fields}: Fail{(22, "object field is not a member")} case JObj1{fs}: match fs: case Nil{}: JSON.encode.go(rest, acc) case m <> ft: match m: case Member{name, val}: JSON.encode.go(JLit{JSON.esc(name) ++ ":"} <> JVal{val} <> JObj1{ft} <> rest, "," <> acc) case Null{}: Fail{(22, "object field is not a member")} case Boolean{ok}: Fail{(22, "object field is not a member")} case Num{lex}: Fail{(22, "object field is not a member")} case Str{text}: Fail{(22, "object field is not a member")} case Arr{xs}: Fail{(22, "object field is not a member")} case Obj{fields}: Fail{(22, "object field is not a member")} @unsafe def JSON.encode.number(parsed: Result<&1, &1, U32 & String, JSON>, rest: List<&2, JSON.Job>, lex: String, acc: List<&2, String>) -> Result<&1, &1, U32 & String, String>: match parsed: case Fail{error}: Fail{(22, "invalid number lexeme")} case Done{value}: match value: case Num{n}: JSON.encode.go(rest, lex <> acc) case Null{}: Fail{(22, "invalid number lexeme")} case Boolean{ok}: Fail{(22, "invalid number lexeme")} case Str{text}: Fail{(22, "invalid number lexeme")} case Arr{items}: Fail{(22, "invalid number lexeme")} case Obj{fields}: Fail{(22, "invalid number lexeme")} case Member{name, val}: Fail{(22, "invalid number lexeme")} def JSON.write(v: JSON) -> Result<&1, &1, U32 & String, String>: JSON.encode.go([JVal{v}], Nil{}) # Parse # ----- type JSON.NSt is Data: NMinus{} NZero{} NInt{} NDot{} NFrac{} NE{} NESign{} NExp{} type JSON.Need is Data: NVal{} NKey{} NColon{} NAfter{} NEnd{v: JSON} type JSON.Frame is Data: FArr{rev: List<&2, JSON>} FObj{rev: List<&2, JSON>} FKey{rev: List<&2, JSON>, key: String} type JSON.Mode is Data: MNeed{need: JSON.Need} MStr{esc: Bool, hex: U32, hexv: U32, rev: String, as_key: Bool, hi: U32} MNum{rev: String, st: JSON.NSt} MLit{want: String, v: JSON} MFail{pos: U32, msg: String} MDone{v: JSON} type JSON.Walk is Data: Walk{mode: JSON.Mode, stk: List<&2, JSON.Frame>} def JSON.fail(+pos: U32, msg: String) -> JSON.Walk: Walk{MFail{pos, msg}, []} def JSON.nst.start(minus: Bool, zero: Bool) -> JSON.NSt: match minus: case True{}: NMinus{} case False{}: match zero: case True{}: NZero{} case False{}: NInt{} def JSON.nst.can_end(st: JSON.NSt) -> Bool: match st: case NZero{}: True{} case NInt{}: True{} case NFrac{}: True{} case NExp{}: True{} case NMinus{}: False{} case NDot{}: False{} case NE{}: False{} case NESign{}: False{} def JSON.nst.cont(st: JSON.NSt, +c: Char) -> Bool: match st: case NMinus{}: JSON.is_digit(c) case NZero{}: Bool.or(JSON.is_dot(c), JSON.is_e(c)) case NInt{}: Bool.or(JSON.is_digit(c), Bool.or(JSON.is_dot(c), JSON.is_e(c))) case NDot{}: JSON.is_digit(c) case NFrac{}: Bool.or(JSON.is_digit(c), JSON.is_e(c)) case NE{}: Bool.or(JSON.is_digit(c), Bool.or(JSON.is_minus(c), JSON.is_plus(c))) case NESign{}: JSON.is_digit(c) case NExp{}: JSON.is_digit(c) def JSON.nst.after_minus(+c: Char, zero: Bool) -> JSON.NSt: match zero: case True{}: NZero{} case False{}: NInt{} def JSON.nst.after_zero(+c: Char, dot: Bool) -> JSON.NSt: match dot: case True{}: NDot{} case False{}: NE{} def JSON.nst.after_int(+c: Char, digit: Bool, dot: Bool) -> JSON.NSt: match digit: case True{}: NInt{} case False{}: match dot: case True{}: NDot{} case False{}: NE{} def JSON.nst.after_frac(+c: Char, digit: Bool) -> JSON.NSt: match digit: case True{}: NFrac{} case False{}: NE{} def JSON.nst.after_e(+c: Char, digit: Bool) -> JSON.NSt: match digit: case True{}: NExp{} case False{}: NESign{} def JSON.nst.next(st: JSON.NSt, +c: Char) -> JSON.NSt: match st: case NMinus{}: JSON.nst.after_minus(c, JSON.is_zero(c)) case NZero{}: JSON.nst.after_zero(c, JSON.is_dot(c)) case NInt{}: JSON.nst.after_int(c, JSON.is_digit(c), JSON.is_dot(c)) case NDot{}: NFrac{} case NFrac{}: JSON.nst.after_frac(c, JSON.is_digit(c)) case NE{}: JSON.nst.after_e(c, JSON.is_digit(c)) case NESign{}: NExp{} case NExp{}: NExp{} def JSON.put.frame(+pos: U32, h: JSON.Frame, rest: List<&2, JSON.Frame>, v: JSON) -> JSON.Walk: match h: case FArr{rev}: Walk{MNeed{NAfter{}}, FArr{v <> rev} <> rest} case FKey{rev, key}: Walk{MNeed{NAfter{}}, FObj{Member{key, v} <> rev} <> rest} case FObj{rev}: JSON.fail(pos, "unexpected value") def JSON.put(+pos: U32, stk: List<&2, JSON.Frame>, v: JSON) -> JSON.Walk: match stk: case Nil{}: Walk{MNeed{NEnd{v}}, []} case h <> rest: JSON.put.frame(pos, h, rest, v) def JSON.close.arr.frame(+pos: U32, h: JSON.Frame, rest: List<&2, JSON.Frame>) -> JSON.Walk: match h: case FArr{rev}: JSON.put(pos, rest, Arr{List.reverse(&2, JSON, rev)}) case FObj{rev}: JSON.fail(pos, "unexpected ]") case FKey{rev, key}: JSON.fail(pos, "unexpected ]") def JSON.close.arr(+pos: U32, stk: List<&2, JSON.Frame>) -> JSON.Walk: match stk: case Nil{}: JSON.fail(pos, "unexpected ]") case h <> rest: JSON.close.arr.frame(pos, h, rest) def JSON.close.obj.frame(+pos: U32, h: JSON.Frame, rest: List<&2, JSON.Frame>) -> JSON.Walk: match h: case FObj{rev}: JSON.put(pos, rest, Obj{List.reverse(&2, JSON, rev)}) case FArr{rev}: JSON.fail(pos, "unexpected }") case FKey{rev, key}: JSON.fail(pos, "unexpected }") def JSON.close.obj(+pos: U32, stk: List<&2, JSON.Frame>) -> JSON.Walk: match stk: case Nil{}: JSON.fail(pos, "unexpected }") case h <> rest: JSON.close.obj.frame(pos, h, rest) def JSON.arr.empty.rev(+pos: U32, rev: List<&2, JSON>, rest: List<&2, JSON.Frame>) -> JSON.Walk: match rev: case Nil{}: JSON.put(pos, rest, Arr{[]}) case h <> t: JSON.fail(pos, "trailing comma") def JSON.arr.empty.frame(+pos: U32, h: JSON.Frame, rest: List<&2, JSON.Frame>) -> JSON.Walk: match h: case FArr{rev}: JSON.arr.empty.rev(pos, rev, rest) case FObj{rev}: JSON.fail(pos, "unexpected ]") case FKey{rev, key}: JSON.fail(pos, "unexpected ]") def JSON.arr.empty(+pos: U32, stk: List<&2, JSON.Frame>) -> JSON.Walk: match stk: case Nil{}: JSON.fail(pos, "unexpected ]") case h <> rest: JSON.arr.empty.frame(pos, h, rest) def JSON.obj.empty.rev(+pos: U32, rev: List<&2, JSON>, rest: List<&2, JSON.Frame>) -> JSON.Walk: match rev: case Nil{}: JSON.put(pos, rest, Obj{[]}) case h <> t: JSON.fail(pos, "trailing comma") def JSON.obj.empty.frame(+pos: U32, h: JSON.Frame, rest: List<&2, JSON.Frame>) -> JSON.Walk: match h: case FObj{rev}: JSON.obj.empty.rev(pos, rev, rest) case FArr{rev}: JSON.fail(pos, "unexpected }") case FKey{rev, key}: JSON.fail(pos, "unexpected }") def JSON.obj.empty(+pos: U32, stk: List<&2, JSON.Frame>) -> JSON.Walk: match stk: case Nil{}: JSON.fail(pos, "unexpected }") case h <> rest: JSON.obj.empty.frame(pos, h, rest) def JSON.after.comma.frame(+pos: U32, h: JSON.Frame, rest: List<&2, JSON.Frame>) -> JSON.Walk: match h: case FArr{rev}: Walk{MNeed{NVal{}}, FArr{rev} <> rest} case FObj{rev}: Walk{MNeed{NKey{}}, FObj{rev} <> rest} case FKey{rev, key}: JSON.fail(pos, "unexpected comma") def JSON.after.comma(+pos: U32, stk: List<&2, JSON.Frame>) -> JSON.Walk: match stk: case Nil{}: JSON.fail(pos, "unexpected comma") case h <> rest: JSON.after.comma.frame(pos, h, rest) def JSON.str.end_key.frame(+pos: U32, h: JSON.Frame, rest: List<&2, JSON.Frame>, key: String) -> JSON.Walk: match h: case FObj{rev}: Walk{MNeed{NColon{}}, FKey{rev, key} <> rest} case FArr{rev}: JSON.fail(pos, "bad key") case FKey{rev, k}: JSON.fail(pos, "bad key") def JSON.str.end_key(+pos: U32, stk: List<&2, JSON.Frame>, key: String) -> JSON.Walk: match stk: case Nil{}: JSON.fail(pos, "bad key") case h <> rest: JSON.str.end_key.frame(pos, h, rest, key) def JSON.str.end(+pos: U32, stk: List<&2, JSON.Frame>, text: String, as_key: Bool) -> JSON.Walk: match as_key: case True{}: JSON.str.end_key(pos, stk, text) case False{}: JSON.put(pos, stk, Str{text}) def JSON.esc.char(+c: Char, q: Bool, sl: Bool, sls: Bool, n: Bool, r: Bool, t: Bool, b: Bool, f: Bool, u: Bool) -> Maybe<&2, Char>: match q: case True{}: Some{Chr{34}} case False{}: match sl: case True{}: Some{Chr{92}} case False{}: match sls: case True{}: Some{Chr{47}} case False{}: match n: case True{}: Some{Chr{10}} case False{}: match r: case True{}: Some{Chr{13}} case False{}: match t: case True{}: Some{Chr{9}} case False{}: match b: case True{}: Some{Chr{8}} case False{}: match f: case True{}: Some{Chr{12}} case False{}: match u: case True{}: None{} case False{}: None{} def JSON.esc.read(+c: Char) -> Maybe<&2, Char>: match c: case Chr{+code}: JSON.esc.char(c, U32.is_eq(code, 34), U32.is_eq(code, 92), U32.is_eq(code, 47), U32.is_eq(code, 110), U32.is_eq(code, 114), U32.is_eq(code, 116), U32.is_eq(code, 98), U32.is_eq(code, 102), U32.is_eq(code, 117)) def JSON.esc.is_u(c: Char) -> Bool: match c: case Chr{+code}: U32.is_eq(code, 117) def JSON.is_high(+code: U32) -> Bool: Bool.and(U32.is_ge(code, 55296), U32.is_le(code, 56319)) def JSON.is_low(+code: U32) -> Bool: Bool.and(U32.is_ge(code, 56320), U32.is_le(code, 57343)) def JSON.is_scalar(+code: U32) -> Bool: Bool.and(U32.is_le(code, 1114111), Bool.not(Bool.and(U32.is_ge(code, 55296), U32.is_le(code, 57343)))) def JSON.hex.combine(+pos: U32, stk: List<&2, JSON.Frame>, rev: String, as_key: Bool, high: U32, low: U32, valid: Bool) -> JSON.Walk: match valid: case False{}: JSON.fail(pos, "unpaired high surrogate") case True{}: code = U32.add(65536, U32.add(U32.mul(U32.sub(high, 55296), 1024), U32.sub(low, 56320))) Walk{MStr{False{}, 0, 0, SCon{Chr{code}, rev}, as_key, 0}, stk} def JSON.hex.first(+pos: U32, stk: List<&2, JSON.Frame>, rev: String, as_key: Bool, code: U32, low: Bool, high: Bool) -> JSON.Walk: match low: case True{}: JSON.fail(pos, "unpaired low surrogate") case False{}: match high: case True{}: Walk{MStr{False{}, 0, 0, rev, as_key, code}, stk} case False{}: Walk{MStr{False{}, 0, 0, SCon{Chr{code}, rev}, as_key, 0}, stk} def JSON.hex.scalar.branch(+pos: U32, stk: List<&2, JSON.Frame>, rev: String, as_key: Bool, high: U32, +code: U32, zero: Bool) -> JSON.Walk: match zero: case True{}: JSON.hex.first(pos, stk, rev, as_key, code, JSON.is_low(code), JSON.is_high(code)) case False{}: JSON.hex.combine(pos, stk, rev, as_key, high, code, JSON.is_low(code)) def JSON.hex.scalar(+pos: U32, stk: List<&2, JSON.Frame>, rev: String, as_key: Bool, +high: U32, code: U32) -> JSON.Walk: JSON.hex.scalar.branch(pos, stk, rev, as_key, high, code, U32.is_zero(high)) def JSON.hex.push(+pos: U32, stk: List<&2, JSON.Frame>, +esc: Bool, +hex: U32, +hexv: U32, rev: String, as_key: Bool, high: U32, d: U32, last: Bool) -> JSON.Walk: match last: case True{}: JSON.hex.scalar(pos, stk, rev, as_key, high, U32.add(U32.mul(hexv, 16), d)) case False{}: Walk{MStr{False{}, U32.sub(hex, 1), U32.add(U32.mul(hexv, 16), d), rev, as_key, high}, stk} def JSON.hex.got(+pos: U32, stk: List<&2, JSON.Frame>, esc: Bool, +hex: U32, +hexv: U32, rev: String, as_key: Bool, high: U32, mb: Maybe<&2, U32>) -> JSON.Walk: match mb: case None{}: JSON.fail(pos, "bad unicode escape") case Some{d}: JSON.hex.push(pos, stk, esc, hex, hexv, rev, as_key, high, d, U32.is_eq(hex, 1)) def JSON.str.esc.other(+pos: U32, stk: List<&2, JSON.Frame>, rev: String, as_key: Bool, high: U32, zero: Bool, mb: Maybe<&2, Char>) -> JSON.Walk: match zero: case False{}: JSON.fail(pos, "unpaired high surrogate") case True{}: match mb: case None{}: JSON.fail(pos, "bad escape") case Some{ch}: Walk{MStr{False{}, 0, 0, SCon{ch, rev}, as_key, 0}, stk} def JSON.str.esc(+pos: U32, stk: List<&2, JSON.Frame>, rev: String, as_key: Bool, +high: U32, +c: Char, u: Bool, mb: Maybe<&2, Char>) -> JSON.Walk: match u: case True{}: Walk{MStr{False{}, 4, 0, rev, as_key, high}, stk} case False{}: JSON.str.esc.other(pos, stk, rev, as_key, high, U32.is_zero(high), mb) def JSON.str.body.scalar(+pos: U32, stk: List<&2, JSON.Frame>, rev: String, as_key: Bool, c: Char, valid: Bool) -> JSON.Walk: match valid: case False{}: JSON.fail(pos, "invalid unicode scalar") case True{}: Walk{MStr{False{}, 0, 0, SCon{c, rev}, as_key, 0}, stk} def JSON.str.body.normal(+pos: U32, stk: List<&2, JSON.Frame>, rev: String, as_key: Bool, +c: Char, quote: Bool, slash: Bool, ctl: Bool) -> JSON.Walk: match quote: case True{}: JSON.str.end(pos, stk, String.reverse(rev), as_key) case False{}: match slash: case True{}: Walk{MStr{True{}, 0, 0, rev, as_key, 0}, stk} case False{}: match ctl: case True{}: JSON.fail(pos, "unescaped control") case False{}: JSON.str.body.scalar(pos, stk, rev, as_key, c, JSON.is_scalar(Char.to_u32(c))) def JSON.str.body.pending(+pos: U32, stk: List<&2, JSON.Frame>, rev: String, as_key: Bool, high: U32, zero: Bool, c: Char, quote: Bool, slash: Bool, ctl: Bool) -> JSON.Walk: match zero: case False{}: match slash: case True{}: Walk{MStr{True{}, 0, 0, rev, as_key, high}, stk} case False{}: JSON.fail(pos, "unpaired high surrogate") case True{}: JSON.str.body.normal(pos, stk, rev, as_key, c, quote, slash, ctl) def JSON.str.body(+pos: U32, stk: List<&2, JSON.Frame>, rev: String, as_key: Bool, +high: U32, c: Char, quote: Bool, slash: Bool, ctl: Bool) -> JSON.Walk: JSON.str.body.pending(pos, stk, rev, as_key, high, U32.is_zero(high), c, quote, slash, ctl) def JSON.step.str.hex(+pos: U32, stk: List<&2, JSON.Frame>, +hex: U32, +hexv: U32, rev: String, as_key: Bool, high: U32, +c: Char, in_hex: Bool) -> JSON.Walk: match in_hex: case True{}: JSON.hex.got(pos, stk, False{}, hex, hexv, rev, as_key, high, JSON.hex.val(c)) case False{}: JSON.str.body(pos, stk, rev, as_key, high, c, JSON.is_quote(c), JSON.is_slash(c), JSON.is_ctl(c)) def JSON.step.str(+pos: U32, stk: List<&2, JSON.Frame>, esc: Bool, +hex: U32, +hexv: U32, rev: String, as_key: Bool, high: U32, +c: Char, in_hex: Bool) -> JSON.Walk: match esc: case True{}: JSON.str.esc(pos, stk, rev, as_key, high, c, JSON.esc.is_u(c), JSON.esc.read(c)) case False{}: JSON.step.str.hex(pos, stk, hex, hexv, rev, as_key, high, c, in_hex) def JSON.step.lit.last(+pos: U32, stk: List<&2, JSON.Frame>, rest: String, v: JSON, last: Bool) -> JSON.Walk: match last: case True{}: JSON.put(pos, stk, v) case False{}: Walk{MLit{rest, v}, stk} def JSON.step.lit.ok(+pos: U32, stk: List<&2, JSON.Frame>, +rest: String, v: JSON, ok: Bool) -> JSON.Walk: match ok: case False{}: JSON.fail(pos, "bad literal") case True{}: JSON.step.lit.last(pos, stk, rest, v, String.is_empty(rest)) def JSON.step.lit(+pos: U32, stk: List<&2, JSON.Frame>, want: String, v: JSON, +c: Char) -> JSON.Walk: match want: case SNil{}: JSON.fail(pos, "bad literal") case SCon{h, rest}: JSON.step.lit.ok(pos, stk, rest, v, Char.is_eq(h, c)) def JSON.is_t(c: Char) -> Bool: match c: case Chr{+code}: U32.is_eq(code, 116) def JSON.is_f(c: Char) -> Bool: match c: case Chr{+code}: U32.is_eq(code, 102) def JSON.is_n(c: Char) -> Bool: match c: case Chr{+code}: U32.is_eq(code, 110) def JSON.is_num.hd(+c: Char) -> Bool: Bool.or(JSON.is_minus(c), JSON.is_digit(c)) def JSON.val.start_num(+pos: U32, stk: List<&2, JSON.Frame>, +c: Char) -> JSON.Walk: Walk{MNum{SCon{c, SNil{}}, JSON.nst.start(JSON.is_minus(c), JSON.is_zero(c))}, stk} def JSON.val.start_str(+pos: U32, stk: List<&2, JSON.Frame>, as_key: Bool) -> JSON.Walk: Walk{MStr{False{}, 0, 0, SNil{}, as_key, 0}, stk} def JSON.val.lit(+pos: U32, stk: List<&2, JSON.Frame>, +c: Char, t: Bool, f: Bool, n: Bool) -> JSON.Walk: match t: case True{}: Walk{MLit{"rue", Boolean{True{}}}, stk} case False{}: match f: case True{}: Walk{MLit{"alse", Boolean{False{}}}, stk} case False{}: match n: case True{}: Walk{MLit{"ull", Null{}}, stk} case False{}: JSON.fail(pos, "unexpected char") def JSON.val.open(+pos: U32, stk: List<&2, JSON.Frame>, +c: Char, arr: Bool, obj: Bool, quote: Bool, num: Bool) -> JSON.Walk: match arr: case True{}: Walk{MNeed{NVal{}}, FArr{[]} <> stk} case False{}: match obj: case True{}: Walk{MNeed{NKey{}}, FObj{[]} <> stk} case False{}: match quote: case True{}: JSON.val.start_str(pos, stk, False{}) case False{}: match num: case True{}: JSON.val.start_num(pos, stk, c) case False{}: JSON.val.lit(pos, stk, c, JSON.is_t(c), JSON.is_f(c), JSON.is_n(c)) def JSON.step.val(+pos: U32, stk: List<&2, JSON.Frame>, +c: Char, ws: Bool, rbrk: Bool) -> JSON.Walk: match ws: case True{}: Walk{MNeed{NVal{}}, stk} case False{}: match rbrk: case True{}: JSON.arr.empty(pos, stk) case False{}: JSON.val.open(pos, stk, c, JSON.is_lbrk(c), JSON.is_lbra(c), JSON.is_quote(c), JSON.is_num.hd(c)) def JSON.step.key(+pos: U32, stk: List<&2, JSON.Frame>, +c: Char, ws: Bool, rbra: Bool, quote: Bool) -> JSON.Walk: match ws: case True{}: Walk{MNeed{NKey{}}, stk} case False{}: match rbra: case True{}: JSON.obj.empty(pos, stk) case False{}: match quote: case True{}: JSON.val.start_str(pos, stk, True{}) case False{}: JSON.fail(pos, "expected key") def JSON.step.colon(+pos: U32, stk: List<&2, JSON.Frame>, +c: Char, ws: Bool, col: Bool) -> JSON.Walk: match ws: case True{}: Walk{MNeed{NColon{}}, stk} case False{}: match col: case True{}: Walk{MNeed{NVal{}}, stk} case False{}: JSON.fail(pos, "expected colon") def JSON.step.after(+pos: U32, stk: List<&2, JSON.Frame>, +c: Char, ws: Bool, comma: Bool, rbrk: Bool, rbra: Bool) -> JSON.Walk: match ws: case True{}: Walk{MNeed{NAfter{}}, stk} case False{}: match comma: case True{}: JSON.after.comma(pos, stk) case False{}: match rbrk: case True{}: JSON.close.arr(pos, stk) case False{}: match rbra: case True{}: JSON.close.obj(pos, stk) case False{}: JSON.fail(pos, "expected comma") def JSON.step.end(+pos: U32, stk: List<&2, JSON.Frame>, v: JSON, +c: Char, ws: Bool) -> JSON.Walk: match ws: case True{}: Walk{MNeed{NEnd{v}}, stk} case False{}: JSON.fail(pos, "trailing junk") def JSON.step.need(+pos: U32, stk: List<&2, JSON.Frame>, need: JSON.Need, +c: Char) -> JSON.Walk: match need: case NVal{}: JSON.step.val(pos, stk, c, JSON.is_ws(c), JSON.is_rbrk(c)) case NKey{}: JSON.step.key(pos, stk, c, JSON.is_ws(c), JSON.is_rbra(c), JSON.is_quote(c)) case NColon{}: JSON.step.colon(pos, stk, c, JSON.is_ws(c), JSON.is_colon(c)) case NAfter{}: JSON.step.after(pos, stk, c, JSON.is_ws(c), JSON.is_comma(c), JSON.is_rbrk(c), JSON.is_rbra(c)) case NEnd{v}: JSON.step.end(pos, stk, v, c, JSON.is_ws(c)) def JSON.step.num.end.mode(+pos: U32, stk: List<&2, JSON.Frame>, mode: JSON.Mode, c: Char) -> JSON.Walk: match mode: case MNeed{need}: JSON.step.need(pos, stk, need, c) case MFail{p, msg}: Walk{MFail{p, msg}, stk} case MDone{v}: Walk{MDone{v}, stk} case MStr{e0, hex, hexv, rev, as_key, hi}: JSON.fail(pos, "bad number") case MNum{rev, st}: JSON.fail(pos, "bad number") case MLit{want, v}: JSON.fail(pos, "bad number") def JSON.step.num.end(+pos: U32, w: JSON.Walk, c: Char) -> JSON.Walk: match w: case Walk{mode, stk}: JSON.step.num.end.mode(pos, stk, mode, c) def JSON.step.num(+pos: U32, stk: List<&2, JSON.Frame>, +rev: String, st: JSON.NSt, +c: Char, cont: Bool) -> JSON.Walk: match cont: case True{}: Walk{MNum{SCon{c, rev}, JSON.nst.next(st, c)}, stk} case False{}: JSON.step.num.end(pos, JSON.put(pos, stk, Num{String.reverse(rev)}), c) def JSON.step.mode(+pos: U32, stk: List<&2, JSON.Frame>, mode: JSON.Mode, +c: Char) -> JSON.Walk: match mode: case MNeed{need}: JSON.step.need(pos, stk, need, c) case MStr{e0, +hex, hexv, rev, as_key, hi}: JSON.step.str(pos, stk, e0, hex, hexv, rev, as_key, hi, c, Bool.not(U32.is_zero(hex))) case MNum{rev, +st}: JSON.step.num(pos, stk, rev, st, c, JSON.nst.cont(st, c)) case MLit{want, v}: JSON.step.lit(pos, stk, want, v, c) case MFail{p, msg}: Walk{MFail{p, msg}, stk} case MDone{v}: Walk{MDone{v}, stk} def JSON.step(w: JSON.Walk, c: Char, pos: U32) -> JSON.Walk: match w: case Walk{mode, stk}: JSON.step.mode(pos, stk, mode, c) def JSON.mode.stop(mode: JSON.Mode) -> Bool: match mode: case MFail{p, msg}: True{} case MDone{v}: True{} case MNeed{need}: False{} case MStr{e0, hex, hexv, rev, as_key, hi}: False{} case MNum{rev, st}: False{} case MLit{want, v}: False{} def JSON.walk.stop(w: JSON.Walk) -> Bool: match w: case Walk{mode, stk}: JSON.mode.stop(mode) def JSON.eof.need(+pos: U32, stk: List<&2, JSON.Frame>, need: JSON.Need) -> JSON.Mode: match need: case NEnd{v}: MDone{v} case NVal{}: MFail{pos, "unexpected end"} case NKey{}: MFail{pos, "unexpected end"} case NColon{}: MFail{pos, "unexpected end"} case NAfter{}: MFail{pos, "unexpected end"} def JSON.eof.put(+pos: U32, w: JSON.Walk) -> JSON.Mode: match w: case Walk{mode, stk}: match mode: case MNeed{need}: JSON.eof.need(pos, stk, need) case MFail{p, msg}: MFail{p, msg} case MDone{v}: MDone{v} case MStr{e0, hex, hexv, rev, as_key, hi}: MFail{pos, "unterminated string"} case MNum{rev, st}: MFail{pos, "unexpected end"} case MLit{want, v}: MFail{pos, "unexpected end"} def JSON.eof.num(+pos: U32, stk: List<&2, JSON.Frame>, rev: String, ok: Bool) -> JSON.Mode: match ok: case True{}: JSON.eof.put(pos, JSON.put(pos, stk, Num{String.reverse(rev)})) case False{}: MFail{pos, "bad number"} def JSON.parse.eof(+pos: U32, w: JSON.Walk) -> JSON.Mode: match w: case Walk{mode, stk}: match mode: case MNum{rev, st}: JSON.eof.num(pos, stk, rev, JSON.nst.can_end(st)) case MNeed{need}: JSON.eof.need(pos, stk, need) case MStr{e0, hex, hexv, rev, as_key, hi}: MFail{pos, "unterminated string"} case MLit{want, v}: MFail{pos, "unexpected end"} case MFail{p, msg}: MFail{p, msg} case MDone{v}: MDone{v} def JSON.parse.go(s: String, +pos: U32, w: JSON.Walk, halt: Bool) -> JSON.Mode: match s: case SNil{}: JSON.parse.eof(pos, w) case SCon{h, rest}: match halt: case True{}: JSON.parse.eof(pos, w) case False{}: +n = JSON.step(w, h, pos) JSON.parse.go(rest, U32.inc(pos), n, JSON.walk.stop(n)) def JSON.parse.fin(mode: JSON.Mode) -> Result<&1, &1, U32 & String, JSON>: match mode: case MDone{v}: Done{v} case MFail{p, msg}: Fail{(p, msg)} case MNeed{need}: Fail{(0, "unexpected end")} case MStr{e0, hex, hexv, rev, as_key, hi}: Fail{(0, "unterminated string")} case MNum{rev, st}: Fail{(0, "bad number")} case MLit{want, v}: Fail{(0, "unexpected end")} def JSON.read(s: String) -> Result<&1, &1, U32 & String, JSON>: JSON.parse.fin(JSON.parse.go(s, 0, Walk{MNeed{NVal{}}, []}, False{}))