diff options
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/json_tree.aup | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/examples/json_tree.aup b/examples/json_tree.aup new file mode 100644 index 0000000..4451c39 --- /dev/null +++ b/examples/json_tree.aup @@ -0,0 +1,171 @@ +# A JSON text (RFC 8259) read into a tree of values, then walked breadth first for a digest of it: how many of each +# kind, the numbers' sum, the strings' and the keys' bytes. Input that is no JSON text leaves valid false. +variant Json { + null + bool(bool) + num(number) + str(text) + arr(list of Json) + obj(list of Member) +} +record Member { + name: text + value: Json +} +record Open { + items: list of Json + members: list of Member + name: text + object = false +} +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 } + +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 n, i, ok, mode = d.bytes, 0, true, 0 # mode 0: a value, 1: after one, 2: a key, 3: a key or }, 4: a value or ] + let root: list of Json + let open: list of Open + let v: list of Json # the value just read, then placed + let q: list of Json # the tree's values, breadth first + while ok { + while i < n && space(d[i]) { i = i + 1 } + if i == n { + ok = mode == 1 && open.count == 0 + break + } + let b = d[i] + if mode == 1 { + if open.count == 0 { ok = false } + else if b == 0x2c { mode = if open.last.object { 2 } else { 0 } } + else if b == 0x5d && !open.last.object { v.append(Json.arr(open.last.items)) } + else if b == 0x7d && open.last.object { v.append(Json.obj(open.last.members)) } + else { ok = false } + if v.count > 0 { open.remove(open.last) } + i = i + 1 + } else if mode == 3 && b == 0x7d || mode == 4 && b == 0x5d { + if b == 0x7d { v.append(Json.obj(open.last.members)) } else { v.append(Json.arr(open.last.items)) } + open.remove(open.last) + i = i + 1 + } else if mode == 2 || mode == 3 { + ok = b == 0x22 + mode = 5 # the key, then ':' + } else if b == 0x5b || b == 0x7b { + open.append(Open { object: b == 0x7b }) + mode = if b == 0x5b { 4 } else { 3 } + i = i + 1 + } else if b == 0x22 || mode == 5 { + i = i + 1 # a string: its bytes, escapes decoded + let s = d[i .. i] + while ok && i < n && d[i] != 0x22 { + let start = i + while i < n && d[i] != 0x22 && d[i] != 0x5c && d[i] >= 0x20 { i = i + 1 } + s = s + d[start .. i] + if i < n && d[i] < 0x20 { ok = false } + else if i + 1 < n && d[i] == 0x5c { + let e = d[i + 1] + if e == 0x75 { + ok = i + 5 < n && hex(d[i + 2]) && hex(d[i + 3]) && hex(d[i + 4]) && hex(d[i + 5]) + let u = if ok { nibble(d[i + 2]) << 12 | nibble(d[i + 3]) << 8 | nibble(d[i + 4]) << 4 | nibble(d[i + 5]) } else { 0 } + i = i + 6 + if (u >= 0xd800 && u < 0xdc00 && i + 5 < n && d[i] == 0x5c && d[i + 1] == 0x75 && hex(d[i + 2]) && hex(d[i + 3]) && + hex(d[i + 4]) && hex(d[i + 5])) { + let low = nibble(d[i + 2]) << 12 | nibble(d[i + 3]) << 8 | nibble(d[i + 4]) << 4 | nibble(d[i + 5]) + if low >= 0xdc00 && low < 0xe000 { + u = 0x10000 + ((u - 0xd800) << 10 | low - 0xdc00) + i = i + 6 + } + } + if u < 0x80 { s = s + byte(u) } + else if u < 0x800 { s = s + byte(0xc0 | u >> 6) + byte(0x80 | u & 0x3f) } + else if u < 0x10000 { s = s + byte(0xe0 | u >> 12) + byte(0x80 | u >> 6 & 0x3f) + byte(0x80 | u & 0x3f) } + else { s = s + byte(0xf0 | u >> 18) + byte(0x80 | u >> 12 & 0x3f) + byte(0x80 | u >> 6 & 0x3f) + byte(0x80 | u & 0x3f) } + } else { + let to = (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 }) + ok = e == 0x22 || e == 0x5c || e == 0x2f || e == 0x62 || e == 0x66 || e == 0x6e || e == 0x72 || e == 0x74 + s = s + byte(to) + i = i + 2 + } + } else if i < n && d[i] == 0x5c { ok = false } + } + ok = ok && i < n + i = i + 1 + if mode == 5 { + open[open.count - 1].name = text(s) + while i < n && space(d[i]) { i = i + 1 } + ok = ok && i < n && d[i] == 0x3a + i = i + 1 + mode = 0 + } else { v.append(Json.str(text(s))) } + } else if b == 0x2d || digit(b) { # -?(0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?: its text read as a number + let start = i + if b == 0x2d { i = i + 1 } + while i < n && (digit(d[i]) || d[i] == 0x2e || d[i] == 0x65 || d[i] == 0x45 || d[i] == 0x2b || d[i] == 0x2d) { i = i + 1 } + v.append(Json.num(number(text(d[start .. i])))) + } else { + let word = if b == 0x74 { "true" } else if b == 0x66 { "false" } else { "null" } + ok = i + word.bytes <= n && text(d[i .. i + word.bytes]) == word + i = i + word.bytes + if ok && b == 0x74 { v.append(Json.bool(true)) } + else if ok && b == 0x66 { v.append(Json.bool(false)) } + else if ok { v.append(Json.null) } + } + if ok && v.count > 0 { # a value read: placed in what is open, or the root + if open.count == 0 { + ok = root.count == 0 + root.append(v.last) + } else if open.last.object { open.last.members.append(Member { name: open.last.name, value: v.last }) } + else { open.last.items.append(v.last) } + v.remove(v.last) + mode = 1 + } + } + valid = ok + if ok { + q.append(root.first) + let at = 0 + while at < q.count { + match q[at] { + null => nulls = nulls + 1 + bool(t) => if t { trues = trues + 1 } else { falses = falses + 1 } + num(x) => { + numbers = numbers + 1 + sum = sum + x + } + str(t) => { + strings = strings + 1 + text_bytes = text_bytes + t.bytes + } + arr(items) => { + arrays = arrays + 1 + for y in items { q.append(y) } + } + obj(members) => { + objects = objects + 1 + for m in members { + key_bytes = key_bytes + m.name.bytes + q.append(m.value) + } + } + } + at = at + 1 + } + } +} |
