aboutsummaryrefslogtreecommitdiffstats
path: root/examples/json_tree.aup
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-10-13 07:40:30 +0100
committereunhhu <[email protected]>2026-10-13 07:40:30 +0100
commit7e9fb3b2368634b9cfe4758e7e6b060a5a6fd523 (patch)
tree56a605805787638ea71e8221310b57cb4bd6c17f /examples/json_tree.aup
parent6209ddb42b953902828972b2e09f0296a51fe565 (diff)
downloadaup-7e9fb3b2368634b9cfe4758e7e6b060a5a6fd523.tar.gz
aup-7e9fb3b2368634b9cfe4758e7e6b060a5a6fd523.zip
Records in records, variants, lists in records and match (Pure 027's stage 2b): a field may hold a record or a variant (flattened into paths: at.x; a variant's case a field of its own, its value at name.case) or a list of anything (list of Json, a variant holding itself through a list); variant V { case, case(type) } and V.case(value) where items are made; match item { case(name) => ... _ => ... } over an item of a variant or a variant in a field (the binding a field, a list read through a register, or fields under it; missing cases refused); x.items.append(...), xs.last.items.append, xs[i].items.append; for, folds, count, first, last and [i] over a list in a field; xs[i] an item of an unkeyed list; number(t) of a text; examples/json_tree.aup; 16 diagnostics, an accepted program, a nested values test
Diffstat (limited to 'examples/json_tree.aup')
-rw-r--r--examples/json_tree.aup171
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
+ }
+ }
+}