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 | 8481b12fb794690cce0d9c54032ffba3de1fe31d (patch) | |
| tree | 55a7f3c74b8521b84262acb4cf444ddda9ef5070 /core/test-module.c | |
| parent | d83ccb2e3b3d276577c61b30a4a323909a654690 (diff) | |
| download | pure-8481b12fb794690cce0d9c54032ffba3de1fe31d.tar.gz pure-8481b12fb794690cce0d9c54032ffba3de1fe31d.zip | |
Module format and executor: 027's stage 2b, lists in records: a record field of type LIST (its items' record in its default) holds a list the event made, its number an i64 in the cell (0 an empty one), so lists keep their inside, batches and the JIT as they were (the JIT's append fast path leaves such records alone); LIST_VALUE (a copy of an event's list, nothing made for an empty one), LIST_BIND (a register, an event's list flagged PURE_LIST_BOUND, read and walked, never changed by code), APPEND_IN (an item into the list in a field of an event's list's item, copied first when another value shares it or a register is bound to it), sharing counted where cells take them; such records only in event lists (no stored, transient or message list, no key); the event's lists go as each event or view's body begins, registers given back their own; record field names may be paths (at.x: records in records, flattened by aup); NUM_PARSE (a text written as a JSON number, its nearest double, core's pure_parse_number); unit tests (values copied, shared then appended, a bound register seeing what it was bound to, three events alike, 6 refused; under ASan and UBSan too, which found the record kinds' missing LIST)
Diffstat (limited to 'core/test-module.c')
| -rw-r--r-- | core/test-module.c | 76 |
1 files changed, 74 insertions, 2 deletions
diff --git a/core/test-module.c b/core/test-module.c index 02d5015..f45cada 100644 --- a/core/test-module.c +++ b/core/test-module.c @@ -180,7 +180,7 @@ static size_t build(unsigned char *out, Spec spec) { p = put32(p, offset[++n]); p = put32(p, field->text); if (field->text == 1) { p = put32(p, (uint32_t)field->initial); p = put32(p, 0); } - else if (field->text == 3) { + else if (field->text == 3 || field->text == 4) { /* an i64's default, a list's record */ int64_t initial = (int64_t)field->initial; p = put32(p, (uint32_t)(uint64_t)initial); p = put32(p, (uint32_t)((uint64_t)initial >> 32)); @@ -345,7 +345,7 @@ static void rejections(void) { static const O nan_const[] = {{C, 0, NAN}, {ST, 0, 0}}; static const O noncanon[] = {{C, 0, 1}, {NEG, 1, 0}, {ST, 0, 0}}; static const O unreachable[] = {{JMP, 2, 0}, {C, 0, 1}, {C, 0, 1}, {ST, 0, 0}}; - static const O bad_op[] = {{99, 0, 0}, {ST, 0, 0}}; + static const O bad_op[] = {{PURE_OP_COUNT, 0, 0}, {ST, 0, 0}}; /* past the last opcode */ static const H h_all[] = {{0, 0, 1}}; static const H h_two[] = {{0, 0, 2}}; static const H h_five[] = {{0, 0, 5}}; @@ -6247,6 +6247,77 @@ static void formats_and_held(void) { printf("{\"formats_and_held\":{\"formats\":%zu,\"slices\":%zu,\"refused\":%zu}}\n", LEN(shown), LEN(slices), LEN(refused)); } +/* ---- Lists in records (docs/design/027, stage 2b) ------------------------ + * A Node's kids are a list of Leaf. A list made a value (LIST_VALUE) is a + * copy; a value stored in a second cell shares the list, and an append into + * either (APPEND_IN) makes that cell's own first; a register (LIST_BIND) + * reads the list as it was bound, appends after it go to a copy. */ +enum { LV = PURE_OP_LIST_VALUE, BIND = PURE_OP_LIST_BIND, AIN = PURE_OP_APPEND_IN }; +#define NEXT_DIGIT(list) {L, 0, 0}, {C, 0, 10}, {MUL, 0, 0}, {LC, list, 0}, {ADD, 0, 0}, {ST, 0, 0} + +static void lists_in_records(void) { + static const S nums[] = {{"n", 0}}; + static const RF leaf_fields[] = {{"v", 3, 0}}; + static const RF node_fields[] = {{"name", 3, 0}, {"kids", 4, 0}}; /* kids: a list of record 0, Leaf */ + static const R records[] = {{"Leaf", leaf_fields, 1}, {"Node", node_fields, 2}}; + static const T others[] = {{"let.nodes", 3 | (uint32_t)PURE_LIST_ROWS << 8, 1}, {"let.leaves", 3 | (uint32_t)PURE_LIST_ROWS << 8, 0}, + {"let.view", 3 | (uint32_t)(PURE_LIST_ROWS | PURE_LIST_BOUND) << 8, 0}, {"i", PURE_STATE_I64, 0}}; + static const I inputs[] = {KEY("go", "KeyG")}; + const O code[] = { + {C, 0, 0}, {ST, 0, 0}, /* 0 n = 0 */ + IC(7), {LA, 2, 0}, IC(9), {LA, 2, 0}, /* 2 leaves = [7, 9] */ + IC(1), {LV, 2, 0}, {LA, 1, 0}, /* 6 nodes.append(Node { 1, leaves }) */ + IC(11), {LA, 2, 0}, /* 9 leaves.append(11): kids keep 2 */ + {XF, 1, 0}, IC(5), {AIN, ITEM(1, 1), 0}, /* 11 nodes[0].kids += 5 */ + IC(2), {XF, 1, 0}, {IG, ITEM(1, 1), 0}, {LA, 1, 0}, /* 14 nodes.append(Node { 2, nodes[0].kids }) */ + {C, 0, 1}, {XAT, 1, 0}, IC(6), {AIN, ITEM(1, 1), 0}, /* 18 nodes[1].kids += 6: its own first */ + {XF, 1, 0}, {IG, ITEM(1, 1), 0}, {BIND, 3, 0}, /* 22 view = nodes[0].kids (3) */ + NEXT_DIGIT(3), /* 25 */ + {XF, 1, 0}, IC(8), {AIN, ITEM(1, 1), 0}, /* 31 nodes[0].kids += 8: bound, so a copy */ + NEXT_DIGIT(3), /* 34 the view still 3 */ + {XF, 1, 0}, {IG, ITEM(1, 1), 0}, {BIND, 3, 0}, NEXT_DIGIT(3), /* 40 nodes[0].kids now 4 */ + {C, 0, 1}, {XAT, 1, 0}, {IG, ITEM(1, 1), 0}, {BIND, 3, 0}, NEXT_DIGIT(3), /* 49 nodes[1].kids: 7 9 5 6 */ + {FOLD, FOLDING(PURE_FOLD_SUM | PURE_FOLD_INTEGER, 3, 3), 0}, {ROW, 3, 0}, {IG, ITEM(3, 0), 0}, {C, 1, 1}, {ST, 4, 0}, + NEXT_DIGIT(2), /* 64 leaves: 3 */ + }; + H handler = {1, 0, LEN(code)}; + Spec spec = {nums, 1, inputs, 1, NULL, 0, &handler, 1, code, LEN(code), NULL, 0, others, LEN(others), NULL, 0, NULL, 0, + records, 2, NULL, 0}; + if (load(spec, &module_b) != PURE_OK) { fprintf(stderr, "%s\n", error); abort(); } + PureApp app; + CHECK(pure_app_start(&app, &module_b, (PureData){0}) == PURE_OK); + for (int round = 0; round < 3; ++round) { /* each event begins with its lists and theirs empty */ + CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK); + if (app.values[0].num != 33443 || app.values[4].i64 != 27) { + fprintf(stderr, "lists in records: n %g, i %" PRId64 "\n", app.values[0].num, app.values[4].i64); + abort(); + } + CHECK(pure_list_count(app.lists[1]) == 0 && app.lists[3] && pure_list_count(app.lists[3]) == 0); + } + pure_app_stop(&app); + /* Refused: a change of a register, a bind to a list not bound, an append into a field not a list, a list of another + * record; a stored list of records holding lists. */ + const O refused[][4] = { + {IC(1), {LA, 3, 0}, {C, 0, 0}, {ST, 0, 0}}, + {{XF, 1, 0}, {IG, ITEM(1, 1), 0}, {BIND, 2, 0}, {C, 0, 0}}, + {{XF, 1, 0}, IC(1), {AIN, ITEM(1, 0), 0}, {C, 0, 0}}, + {IC(1), {LV, 1, 0}, {LA, 1, 0}, {C, 0, 0}}, + {{LV, 4, 0}, {C, 0, 0}, {C, 0, 0}, {C, 0, 0}}, + }; + for (size_t k = 0; k < LEN(refused); ++k) { + H one = {1, 0, 3}; + Spec bad = {nums, 1, inputs, 1, NULL, 0, &one, 1, refused[k], 3, NULL, 0, others, LEN(others), NULL, 0, NULL, 0, + records, 2, NULL, 0}; + if (load(bad, &module_b) == PURE_OK) { fprintf(stderr, "lists in records: refused %zu loaded\n", k); abort(); } + } + static const T stored[] = {{"nodes", 3, 1}}; + static const O nothing[] = {{C, 0, 0}, {ST, 0, 0}}; + H two = {1, 0, 2}; + Spec kept = {nums, 1, inputs, 1, NULL, 0, &two, 1, nothing, 2, NULL, 0, stored, 1, NULL, 0, NULL, 0, records, 2, NULL, 0}; + CHECK(load(kept, &module_b) != PURE_OK); + printf("{\"lists_in_records\":{\"rounds\":3,\"refused\":%zu}}\n", LEN(refused) + 1); +} + /* A timer's tick sees the time the host set; floor and a floored modulo * take it apart. now is for handlers only. */ enum { NOW = PURE_OP_NOW, FLOOR = PURE_OP_FLOOR, MOD = PURE_OP_MOD }; @@ -6622,6 +6693,7 @@ int main(void) { integers(); bits_and_bytes(); formats_and_held(); + lists_in_records(); memory_budget(); work_budget(); fold_mutations(); |
