From 15f0114d6a7da0a4f63028db12cac62c5f9cc922 Mon Sep 17 00:00:00 2001 From: eunhhu Date: Mon, 28 Sep 2026 05:01:23 +0100 Subject: Give an event locals LOCAL_LOAD and LOCAL_STORE name one of an event's 64 slots, what it holds (a number, or an index of a list) and that list: slots start at 0 when an event begins, are shared by its handlers, the walks in them and the folds in those, and are never saved nor in the states' namespace. A handler uses a slot for one thing, so its type is verified as a state's is; views have no locals (IN_LOCALS: a handler's block, a walk in it, a fold in either; stores only in a handler and its walks). A fold reading a local still runs a leaf at a time (one value for every item) and a shaped fold takes one only on its key's side. The signature table's where widens to 16 bits. locals checks a handler that sums items into a local in a walk, counts in a fold the items below it, keeps an index in a local and reads it back, over 80 events against a model, and five refusals. server_rows' shop now takes stock through an index local and counts in one. --- core/test-module.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) (limited to 'core/test-module.c') diff --git a/core/test-module.c b/core/test-module.c index 3b697b3..3c9dfa1 100644 --- a/core/test-module.c +++ b/core/test-module.c @@ -2799,6 +2799,83 @@ static void view_arguments(void) { printf("{\"view_arguments\":{\"accounts\":30,\"branches\":3}}\n"); } +/* ---- Locals ---------------------------------------------------------- + * A handler's locals start at 0 each event, are shared by its walks and the + * folds in them, and are never saved: total sums the items in a walk, a fold + * counts the items below it, an index local finds the last item, and every + * event begins again. */ +enum { LOCAL_L = PURE_OP_LOCAL_LOAD, LOCAL_S = PURE_OP_LOCAL_STORE, L_N = 0, L_M = 1, L_XS = 2 }; +#define LOCAL(slot, list) ((uint32_t)(slot) | ((list) >= 0 ? 1u << 8 | (uint32_t)(list) << 16 : 0)) +static const S local_numbers[] = {{"n", 0}, {"m", 0}}; +static const T local_lists[] = {{"xs", 3, 0}}; +static const RF local_fields[] = {{"a", 0, 0}}; +static const R local_records[] = {{"X", local_fields, 1}}; +static const F local_add_fields[] = {{"a", 3}}; +static const I local_inputs[] = {KEY("sum", "KeyS"), {"add", NULL, PURE_KIND_MESSAGE, false, local_add_fields, 1}}; +static const O local_code[] = { + /* 0 on sum: total = total + x.a for each x (total starts at 0); n = count(x in xs where x.a < total); + * last = xs.last; m = total + last.a (every event: total is 0 again) */ + {LOCAL_L, LOCAL(0, -1), 0}, {C, 0, 1}, {ADD, 0, 0}, {LOCAL_S, LOCAL(0, -1), 0}, /* starts at 0 each event: 1 */ + {EACH, WALKING(L_XS, 5), 0}, {LOCAL_L, LOCAL(0, -1), 0}, {ROW, L_XS, 0}, {IG, ITEM(L_XS, 0), 0}, {ADD, 0, 0}, + {LOCAL_S, LOCAL(0, -1), 0}, + {FOLD, FOLDING(PURE_FOLD_COUNT, L_XS, 4), 0}, {ROW, L_XS, 0}, {IG, ITEM(L_XS, 0), 0}, {LOCAL_L, LOCAL(0, -1), 0}, + {PURE_OP_LT, 0, 0}, {ST, L_N, 0}, + {XE, L_XS, 0}, {LOCAL_S, LOCAL(1, L_XS), 0}, + {LOCAL_L, LOCAL(0, -1), 0}, {LOCAL_L, LOCAL(1, L_XS), 0}, {IG, ITEM(L_XS, 0), 0}, {ADD, 0, 0}, {ST, L_M, 0}, + /* 23 on add: xs.append(X { a }) */ + {FIELD, 0, 0}, {LA, L_XS, 0}, +}; +static const H local_handlers[] = {{1, 0, 23}, {3, 23, 2}}; + +static Spec local_spec(const O *code, size_t count, const H *handlers, size_t nhandlers, const V *views, size_t nviews) { + return (Spec){local_numbers, 2, local_inputs, 2, views, nviews, handlers, nhandlers, code, count, NULL, 0, local_lists, 1, + NULL, 0, NULL, 0, local_records, 1, NULL, 0}; +} + +static void locals(void) { + CHECK(load(local_spec(local_code, LEN(local_code), local_handlers, 2, NULL, 0), &module_a) == PURE_OK && module_a.code[10].plan); + PureApp app; + CHECK(pure_app_start(&app, &module_a, (PureData){0}) == PURE_OK); + double items[64]; + size_t count = 0; + uint32_t seed = 0x10ca15u; + for (unsigned round = 0; round < 40; ++round) { + unsigned char payload[8]; + items[count] = (double)(next_random(&seed) % 20) - 5; + put_double(payload, items[count++]); + CHECK(pure_app_message(&app, 1, NULL, -1, (PureSpan){payload, 8}) == PURE_OK); + CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK); + double total = 1, below = 0; + for (size_t i = 0; i < count; ++i) total += items[i]; + for (size_t i = 0; i < count; ++i) below += items[i] < total; + CHECK(app.values[L_N] == below && app.values[L_M] == total + items[count - 1]); + } + pure_app_stop(&app); + + /* What a local may not be. */ + static const O view_local[] = {{LOCAL_L, LOCAL(0, -1), 0}}; + static const V local_view[] = {{"v", 0, 1}}; + static const O fold_store[] = {{FOLD, FOLDING(PURE_FOLD_COUNT, L_XS, 3), 0}, {C, 0, 1}, {LOCAL_S, LOCAL(0, -1), 0}, {C, 0, 1}, + {ST, L_N, 0}}; + static const O two_things[] = {{C, 0, 1}, {LOCAL_S, LOCAL(0, -1), 0}, {XF, L_XS, 0}, {LOCAL_S, LOCAL(0, L_XS), 0}}; + static const O other_list[] = {{XF, L_XS, 0}, {LOCAL_S, LOCAL(0, 3), 0}}; + static const O far_slot[] = {{C, 0, 1}, {LOCAL_S, LOCAL(64, -1), 0}}; + static const H first2[] = {{1, 0, 2}}, first4[] = {{1, 0, 4}}, first5[] = {{1, 0, 5}}; + const struct { Spec spec; const char *reason; } refused[] = { + {local_spec(view_local, 1, NULL, 0, local_view, 1), "local outside a handler"}, + {local_spec(fold_store, LEN(fold_store), first5, 1, NULL, 0), "fold with an effect"}, + {local_spec(two_things, LEN(two_things), first4, 1, NULL, 0), "local used as two things"}, + {local_spec(other_list, LEN(other_list), first2, 1, NULL, 0), "no such state"}, + {local_spec(far_slot, LEN(far_slot), first2, 1, NULL, 0), "no such state"}, + }; + for (size_t i = 0; i < LEN(refused); ++i) + if (load(refused[i].spec, &module_b) != PURE_INVALID || !strstr(error, refused[i].reason)) { + fprintf(stderr, "local case %zu: expected '%s', got '%s'\n", i, refused[i].reason, error); + abort(); + } + printf("{\"locals\":{\"events\":80}}\n"); +} + /* Damaged fold and filter modules: whatever loads runs safely. */ static void fold_mutations(void) { static unsigned char valid[65536], bytes[65536]; @@ -3723,6 +3800,7 @@ int main(void) { walks(); walk_mutations(); view_arguments(); + locals(); keyed(); keyed_mutations(); snapshots(); -- cgit v1.2.3