diff options
| author | eunhhu <[email protected]> | 2026-10-18 16:43:57 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-10-18 16:43:57 +0100 |
| commit | 82925e53aedc3c63e9f7dbf53b88eacfbd86c06e (patch) | |
| tree | 29849b90693da32cb105b8f55567ad69756d226b /examples/json_tree.aup | |
| parent | 7e9fb3b2368634b9cfe4758e7e6b060a5a6fd523 (diff) | |
| download | aup-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/json_tree.aup')
| -rw-r--r-- | examples/json_tree.aup | 247 |
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 } |
