diff options
| author | eunhhu <[email protected]> | 2026-10-13 07:40:30 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-10-13 07:40:30 +0100 |
| commit | 7e9fb3b2368634b9cfe4758e7e6b060a5a6fd523 (patch) | |
| tree | 56a605805787638ea71e8221310b57cb4bd6c17f /tests | |
| parent | 6209ddb42b953902828972b2e09f0296a51fe565 (diff) | |
| download | aup-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 'tests')
| -rw-r--r-- | tests/check.py | 56 |
1 files changed, 53 insertions, 3 deletions
diff --git a/tests/check.py b/tests/check.py index 965b099..abbdc0a 100644 --- a/tests/check.py +++ b/tests/check.py @@ -64,6 +64,7 @@ CARDS = ('record Card {\n title: text\n lane = 0\n note: text\n}\nstate cards 'output o = message {\n title: text\n lane: u32\n note: text\n}\n') SENDS = ('state n = 0\ninput buy = message {\n item: text\n count: u32\n}\noutput ordered = message {\n' ' item: text\n count: u32\n to: {\n city: text\n }\n}\n') +JSON_M = 'variant Json {\n null\n num(number)\n arr(list of Json)\n}\nrecord M {\n name: text\n value: Json\n}\n' DIAGNOSTICS = [ # Bits and bytes (Pure 027's stage 1): bit operations take i64s; only bytes are read by position; a, b = x, y names # as many values, each a let's or a number's, bool's or i64's state; bytes(xs) of a list of i64. @@ -81,6 +82,32 @@ DIAGNOSTICS = [ ('record R {\n v = 0\n}\ninput m = message { d: bytes }\nstate t: text\non m {\n let xs: list of R\n' ' t = text(bytes(xs))\n}\n', '8:18', 'bytes(...) makes bytes of a list of i64, not of R'), ('state x = 0\nview v = [1, 2]\n', '2:10', "a list's elements begin a let's list"), + # Records in records, variants, lists in records and match (Pure 027's stage 2b). + ('record A {\n b: B\n}\nrecord B {\n a: A\n}\nstate xs: list of A\n', '1:8', "'A' holds itself: hold it in a list"), + ('record A {\n b: Nope\n}\nstate xs: list of A\n', '2:3', "unknown record or variant 'Nope'"), + (JSON_M + 'variant V {\n a\n a\n}\n', '12:3', "duplicate case 'a'"), + (JSON_M + 'state n = 0\non frame {\n let xs: list of Json\n xs.append(Json.num)\n}\n', '13:13', 'Json.num holds a value: give it'), + (JSON_M + 'state n = 0\non frame {\n let xs: list of Json\n xs.append(Json.null(1))\n}\n', '13:18', 'Json.null holds no value'), + (JSON_M + 'state n = 0\non frame {\n let xs: list of Json\n xs.append(Json.nope(1))\n}\n', '13:18', "variant 'Json' has no case 'nope'"), + (JSON_M + 'state n = 0\non frame {\n let xs: list of Json\n for x in xs {\n match x {\n null => n = 1\n }\n }\n}\n', + '14:5', "match misses case 'num': add it, or _ for the rest"), + (JSON_M + 'state n = 0\non frame {\n let xs: list of Json\n for x in xs {\n match x {\n null => n = 1\n null => n = 2\n' + ' _ => n = 3\n }\n }\n}\n', '16:7', "case 'null' is matched twice"), + (JSON_M + 'state n = 0\non frame {\n let ms: list of M\n for m in ms {\n match m {\n _ => n = 3\n }\n }\n}\n', '14:11', + "match takes an item that holds a variant; 'M' holds none"), + (JSON_M + 'state n = 0\non frame {\n let xs: list of Json\n for x in xs {\n match x {\n null(v) => n = 1\n _ => n = 2\n' + ' }\n }\n}\n', '15:12', "case 'null' holds no value to name"), + (JSON_M + 'state n = 0\non frame {\n let xs: list of Json\n for x in xs {\n match x {\n _ => n = 1\n null => n = 2\n' + ' }\n }\n}\n', '16:7', 'an arm after _ is never taken'), + (JSON_M + 'state n = 0\non frame {\n let xs: list of Json\n let ms: list of M\n xs.append(Json.arr(ms))\n}\n', '14:22', + "'arr' holds a list of Json, not of M"), + (JSON_M + 'state xs: list of Json\n', '10:7', "'xs' holds lists in its items: a let's list may, for now"), + (JSON_M + 'state n = 0\non frame {\n let ms: list of M\n for m in ms {\n n = m.value\n }\n}\n', '14:9', + "'value' holds a variant: match it"), + ('record P {\n x = 0\n}\nstate n = 0\non frame {\n let ps: list of P\n match ps.first {\n _ => n = 1\n }\n}\n', '7:9', + "match takes an item that holds a variant; 'P' holds none"), + ('record W {\n' + ''.join(f' f{i} = 0\n' for i in range(12)) + '}\nrecord X {\n a: W\n b: W\n}\nstate xs: list of X\n', '15:8', + "'X' flattens into more than 16 fields"), # Formats, joins, slices, lets of bytes and text, while (Pure 027's stage 2a). ('state t: text\nstate n = 0\non frame {\n t = "{n:08q}"\n}\n', '4:11', 'a format is [0][width][.digits][d x X b o f]'), ('state t: text\nstate n = 0\non frame {\n t = "{n:.2x}"\n}\n', '4:11', 'digits after the point go with f'), @@ -185,7 +212,7 @@ DIAGNOSTICS = [ # records, lists and indices ('record T {\n a = 0\n a = 1\n}\n', '3:3', "duplicate field 'a'"), ('record T {\n}\n', '1:8', 'a record needs at least one field'), - ('record T {\n a: number\n}\n', '2:6', 'a field is a number (field = 0), a bool (field = false), text (field: text) or an i64 (field: i64)'), + ('record T {\n a: number\n}\n', '2:6', 'a field is a number (field = 0), a bool (field = false), text (field: text), an i64 (field: i64), a record'), ('record T {\n a = 0\n}\nstate T = 0\n', '4:7', "'T' is already declared as a record"), ('state xs: list of Missing\n', '1:7', "a list holds a record; 'Missing' is not one"), ('state x = 0\nstate at: index of x\n', '2:7', "an index points into a list state; 'x' is not one"), @@ -193,7 +220,7 @@ DIAGNOSTICS = [ (RECORD + 'state xs: list of T\nstate ys: list of T\nstate at: index of xs\nview v = ys[at].a\n', '7:13', "an index of 'xs' cannot point into 'ys'"), (RECORD + 'state xs: list of T\nstate at: index of xs\nview v = xs[at].b\n', '6:10', "record 'T' has no field 'b'"), - (RECORD + 'state xs: list of T\nstate at: index of xs\nview v = xs[at]\n', '6:10', 'read a field of the item'), + (RECORD + 'state xs: list of T\nstate at: index of xs\nview v = xs[at]\n', '6:10', 'a view is a number, a bool, an i64 or text, not an index'), (RECORD + 'state xs: list of T\nview v = xs\n', '5:10', "'xs' is a list; read xs.count"), (RECORD + 'state xs: list of T\nview v = xs.size\n', '5:10', "a list has count, first and last, not 'size'"), (RECORD + 'state xs: list of T\nstate at: index of xs\nview v = at\n', '6:10', 'a view is a number, a bool, an i64 or text, not an index'), @@ -245,7 +272,7 @@ DIAGNOSTICS = [ ('use missing\n', '1:5', "cannot find library 'missing' (missing.aup)"), ('view v = shapes.area(1, 2)\n', '1:17', "unknown library 'shapes'; write use shapes first"), (LIST + 'view v = count(x in xs where count(y in xs) > 0)\n', '6:30', 'a count, sum, max or min cannot hold another'), - (LIST + 'view v = count(x in at)\n', '6:10', "'at' is not a list"), + (LIST + 'view v = count(x in at)\n', '6:10', "'at' is a list index, not a list"), ('record T {\n t: text\n}\nstate xs: list of T\nview v = sum(x.t for x in xs)\n', '5:14', 'sum, max, min and distinct take numbers or i64s, not text'), (LIST + 'fn f(n) = count(x in xs where x.a == n)\nview v = f(1)\n', '6:38', @@ -409,7 +436,10 @@ DIAGNOSTICS = [ 'an i64 is a whole number; this one is not'), ] +NESTED = 'variant Json {\n null\n bool(bool)\n num(number)\n str(text)\n arr(list of Json)\n obj(list of Member)\n}\nrecord Member {\n name: text\n value: Json\n}\nrecord Point {\n x = 0\n y = 0\n}\nrecord Box {\n at: Point\n tag: text\n}\nstate boxes: list of Box\nstate total = 0\nstate chars = 0\nstate nulls = 0\nstate trues = 0\nstate corner = 0\ninput go = key KeyG\non go.down {\n let docs: list of Json\n docs.append(Json.num(3))\n docs.append(Json.str("hi"))\n docs.append(Json.null)\n let kids: list of Json\n kids.append(Json.num(1))\n kids.append(Json.num(2))\n docs.append(Json.arr(kids))\n docs.last.arr.append(Json.num(10))\n let ms: list of Member\n ms.append(Member { name: "a", value: Json.bool(true) })\n ms.append(Member { name: "b", value: Json.arr([]) })\n docs.append(Json.obj(ms))\n for d in docs {\n match d {\n num(n) => total = total + n\n str(t) => chars = chars + t.chars\n null => nulls = nulls + 1\n arr(items) => {\n for y in items {\n match y {\n num(n) => total = total + n\n _ => nulls = nulls + 100\n }\n }\n }\n obj(members) => {\n for m in members {\n match m.value {\n bool(b) => trues = trues + (if b { 1 } else { 0 })\n arr(xs) => chars = chars + xs.count * 1000\n _ => nulls = nulls + 1000\n }\n }\n }\n bool(b) => nulls = nulls + 10000\n }\n }\n boxes.append(Box { at: Point { x: 1, y: 2 }, tag: "a" })\n corner = boxes.last.at.x * 10 + boxes.last.at.y + docs.count * 100\n}\n' ACCEPTED = [ + # Records in records, variants, lists in records, match, appends into a field's list (Pure 027's 2b). + NESTED, # Formats, joins, slices, lets of bytes and text appended to, while, && and || that decide alone (Pure 027's 2a). 'state t: text\nstate n = 0\nstate y: i64\ninput m = message { d: bytes }\n' 'on m {\n let s = "" + "{n:5}{y:08x}{n:.2f}{y:X}{true:6}"\n let b = m.d[0 .. 0] + byte(0x41)\n b = b + m.d\n' @@ -1358,6 +1388,25 @@ def formats_and_texts(aup, pure_run, work, apps=6, per_app=24): return {'formats': apps * per_app, 'events': checked, 'faults': faults, 'cuts': len(samples)} +def nested_values(aup, pure_run, work): + """Pure 027's 2b: an event builds values of a variant (numbers, a string, null, an array made of a let's list and + appended to through its field, an object of members whose values are variants), walks them with match, and stores + a record holding a record (its fields by path). The same every event (the lists in records go as each ends); a + failing event leaves nothing.""" + path = work / 'nested.aup' + path.write_text(NESTED + 'input bad = key KeyB\non bad.down {\n let xs: list of Json\n xs.append(Json.num(1))\n' + ' total = total + 1\n xs.last.arr.append(Json.num(2))\n require total < 0\n}\n') + compile_module(aup, path, work / 'nested.pmod') + lines = run(pure_run, work / 'nested.pmod', stdin='down go\ndown go\ndown bad\n', check=False).stdout.splitlines() + first, second, bad = (json.loads(line) for line in lines) + assert first['fault'] == 'none' and first['states']['total'] == 16 and first['states']['chars'] == 2, first + assert first['states']['nulls'] == 1 and first['states']['trues'] == 1 and first['states']['corner'] == 512, first + assert first['states']['boxes'] == [{'at.x': 1, 'at.y': 2, 'tag': 'a'}], first['states']['boxes'] + assert second['states']['total'] == 32 and second['states']['corner'] == 512, second + assert bad['fault'] == 'limit' and bad['states'] == second['states'], bad + return {'events': 3} + + def computing(aup, pure_run, work, graphs=20): """Computing in one event (Pure 018): examples/routes.aup's searches over random graphs (breadth first, depth first, nearest by weight) and @@ -2165,6 +2214,7 @@ def main(): report['computing'] = computing(aup, pure_run, work) report['bits_and_bytes'] = bits_and_bytes(aup, pure_run, work) report['formats_and_texts'] = formats_and_texts(aup, pure_run, work) + report['nested_values'] = nested_values(aup, pure_run, work) report['editor_parity_operations'], report['editor_parity_outcomes'] = \ editor_parity(aup, args.pure.resolve(), work, range(args.editor_seeds)) print(json.dumps(report)) |
