aboutsummaryrefslogtreecommitdiffstats
path: root/core/test-module.c
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-09-28 05:01:23 +0100
committereunhhu <[email protected]>2026-09-28 05:01:23 +0100
commit15f0114d6a7da0a4f63028db12cac62c5f9cc922 (patch)
tree0d896ddd1d2925a716c2a08a706f8a0f74f35c40 /core/test-module.c
parentadc4e5724c926cac4ecb8dc3a58a5f9b4d1b9b81 (diff)
downloadpure-15f0114d6a7da0a4f63028db12cac62c5f9cc922.tar.gz
pure-15f0114d6a7da0a4f63028db12cac62c5f9cc922.zip
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.
Diffstat (limited to 'core/test-module.c')
-rw-r--r--core/test-module.c78
1 files changed, 78 insertions, 0 deletions
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();