# A JSON text (RFC 8259) read into a tree of values by recursive descent, then walked for a digest of it: how many of # each kind, the numbers' sum, the strings' and the keys' bytes. A text that is no JSON fails the event. variant Json { null bool(bool) num(number) str(text) arr(list of Json) obj(list of Member) } record Member { name: text value: Json } state valid = false state nulls = 0 state trues = 0 state falses = 0 state numbers = 0 state strings = 0 state arrays = 0 state objects = 0 state sum = 0 state text_bytes = 0 state key_bytes = 0 input check = message { data: bytes } fn space(b) = b == 0x20 || b == 0x09 || b == 0x0a || b == 0x0d fn digit(b) = b >= 0x30 && b <= 0x39 fn hex(b) = digit(b) || (b >= 0x61 && b <= 0x66) || (b >= 0x41 && b <= 0x46) fn nibble(b) = if digit(b) { b - 0x30 } else if b >= 0x61 { b - 0x57 } else { b - 0x37 } fn code(d, i) = nibble(d[i]) << 12 | nibble(d[i + 1]) << 8 | nibble(d[i + 2]) << 4 | nibble(d[i + 3]) fn escape(d, i) = d[i] == 0x5c && d[i + 1] == 0x75 && hex(d[i + 2]) && hex(d[i + 3]) && hex(d[i + 4]) && hex(d[i + 5]) fn skip(d, i) { let j = i while j < d.bytes && space(d[j]) { j = j + 1 } return j } # The string whose quote is at i, and where it ends. fn string(d, i) { require d[i] == 0x22 let s = d[i .. i] let j = i + 1 while d[j] != 0x22 { let start = j while d[j] != 0x22 && d[j] != 0x5c && d[j] >= 0x20 { j = j + 1 } s = s + d[start .. j] require d[j] >= 0x20 if d[j] == 0x5c { let e = d[j + 1] if e == 0x75 { require escape(d, j) let u = code(d, j + 2) j = j + 6 if u >= 0xd800 && u < 0xdc00 && j + 5 < d.bytes && escape(d, j) && code(d, j + 2) >= 0xdc00 && code(d, j + 2) < 0xe000 { u = 0x10000 + ((u - 0xd800) << 10 | code(d, j + 2) - 0xdc00) j = j + 6 } s = s + bytes(char(u)) } else { require e == 0x22 || e == 0x5c || e == 0x2f || e == 0x62 || e == 0x66 || e == 0x6e || e == 0x72 || e == 0x74 s = s + byte(if e == 0x62 { 0x08 } else if e == 0x66 { 0x0c } else if e == 0x6e { 0x0a } else if e == 0x72 { 0x0d } else if e == 0x74 { 0x09 } else { e }) j = j + 2 } } } return text(s), j + 1 } # The value from i (past white space), and where it ends. fn value(d, i) -> Json, number { let items: list of Json # each call's own let members: list of Member let j = skip(d, i) let b = d[j] if b == 0x5b || b == 0x7b { j = skip(d, j + 1) if d[j] != b + 2 { while true { if b == 0x5b { let v, k = value(d, j) items.append(v) j = skip(d, k) } else { let name, k = string(d, j) k = skip(d, k) require d[k] == 0x3a let v, after = value(d, k + 1) members.append(Member { name: name, value: v }) j = skip(d, after) } if d[j] != 0x2c { break } j = skip(d, j + 1) } require d[j] == b + 2 } if b == 0x5b { return Json.arr(items), j + 1 } return Json.obj(members), j + 1 } if b == 0x22 { let s, k = string(d, j) return Json.str(s), k } if b == 0x2d || digit(b) { # -?(0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?, read as a number let start = j j = j + 1 while j < d.bytes && (digit(d[j]) || d[j] == 0x2e || d[j] == 0x65 || d[j] == 0x45 || d[j] == 0x2b || d[j] == 0x2d) { j = j + 1 } return Json.num(number(text(d[start .. j]))), j } let word = if b == 0x74 { "true" } else if b == 0x66 { "false" } else { "null" } require j + word.bytes <= d.bytes && text(d[j .. j + word.bytes]) == word if b == 0x6e { return Json.null, j + 4 } return Json.bool(b == 0x74), j + word.bytes } fn walk(x) { match x { null => nulls = nulls + 1 bool(t) => if t { trues = trues + 1 } else { falses = falses + 1 } num(n) => { numbers = numbers + 1 sum = sum + n } str(t) => { strings = strings + 1 text_bytes = text_bytes + t.bytes } arr(items) => { arrays = arrays + 1 for y in items { walk(y) } } obj(members) => { objects = objects + 1 for m in members { key_bytes = key_bytes + m.name.bytes walk(m.value) } } } } on check { nulls, trues, falses, numbers, strings, arrays, objects, sum, text_bytes, key_bytes = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 let d = bytes(text(check.data)) let root, end = value(d, 0) require skip(d, end) == d.bytes walk(root) valid = true }