aboutsummaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-10-18 16:43:57 +0100
committereunhhu <[email protected]>2026-10-18 16:43:57 +0100
commit82925e53aedc3c63e9f7dbf53b88eacfbd86c06e (patch)
tree29849b90693da32cb105b8f55567ad69756d226b /examples
parent7e9fb3b2368634b9cfe4758e7e6b060a5a6fd523 (diff)
downloadaup-82925e53aedc3c63e9f7dbf53b88eacfbd86c06e.tar.gz
aup-82925e53aedc3c63e9f7dbf53b88eacfbd86c06e.zip
Fns that call themselves, blocks, several values, records' values and characters (Pure 027's stage 2c): a fn with a block (statements, return a, b; return alone gives nothing) or one a cycle of calls goes through runs as Pure's function (CALL), once for each kinds of arguments it is called with, in code of its own after the handlers' and views'; its parameters held by name as it begins, its let's lists, held values and registers each call's own (Pure's); -> types where it calls itself before a return says what it gives, a return's values wanted as those types; f(x) as a statement where it gives nothing, let a, b = f(x) and a, b = f(x) (lets and states) where it gives several; a view, a count, sum, max or min calls none; records' and variants' values: R { ... }, V.case(x), V.case and calls giving one onto the stack field by field, held by a name (name.field lets), passed and given back, matched (a case's value named, read only), appended (a call's value held first), an item and a record under an item's field passed as values (a function has its own lists), a list a value holds walked through a register, a list in a local (Pure's local type 4), R { ... } written where a let, an assignment, a return or an argument takes a value; never-run lines after a return or a break refused, no jump past an else whose then stops, the last arm of a match that names every case taken without asking; for ch in t: a text's characters as code points (a let's, a parameter's, a state's, a record's field, a message's text field), char(n); examples/json_tree.aup by recursive descent and a depth-first walk; 13 diagnostics, 2 accepted forms, a functions test against Python (40 random trees built and summed recursively, deepest and leaves as two values, 60 random texts' characters, a call without end failing as memory with nothing changed); the 2b compiler's modules for 49 sources byte for byte the same (json_tree.aup's match aside); three diagnostics' wording follows records being values
Diffstat (limited to 'examples')
-rw-r--r--examples/json_tree.aup247
1 files changed, 114 insertions, 133 deletions
diff --git a/examples/json_tree.aup b/examples/json_tree.aup
index 4451c39..bc97a73 100644
--- a/examples/json_tree.aup
+++ b/examples/json_tree.aup
@@ -1,5 +1,5 @@
-# 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.
+# 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)
@@ -12,12 +12,6 @@ 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
@@ -35,137 +29,124 @@ 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])
-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 }
+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
}
- 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)
- }
+ 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)
}
- at = at + 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
}