aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-10-18 20:54:10 +0100
committereunhhu <[email protected]>2026-10-18 20:54:10 +0100
commitda0d7514b058f1baa99a8bcc8f9e23b43cac12de (patch)
tree688106ea656946bd74f36259c2dd2316b9a429d8 /core
parent936c08e3424c606855051ee86f25a7df0f162c62 (diff)
downloadpure-da0d7514b058f1baa99a8bcc8f9e23b43cac12de.tar.gz
pure-da0d7514b058f1baa99a8bcc8f9e23b43cac12de.zip
Module format v7, 027's 4d: every input a message: a module's inputs are messages, outputs and arguments (kinds 1 to 3), a message bound to what makes it (PureBinding: a key's press or its release, a text input, a timer, a frame, a pointing at a table), each binding's fields fixed (none; one text; one number, dt; one u32, the item's position, the table its view), one frame input at most; a handler's event its input's number; no DT or TEXT_INPUT (97 opcodes now 95, FIELD reads dt and the text), pure_app_input, frame, tick, text and point, pure_module_position, PureEdge or an app's ticks: every event is pure_app_message, saved after it unless deferred; pure-run sends each command's message to the input bound so (a release or a frame no input takes still an event, clearing the fault, so the workbench parity holds) and defers a stored app's saves past frames as before; pure-serve posts a press with its key's release, refuses timers, frames and pointings (no_input) and ticks timers as messages; holes closed: the tier found a handler's input by the old numbering, reading a message's number field as another input's (aup's numbers caught it), and serve's replies_borrow did too; module-probe presses as messages; unit tests: the test modules' builder (expand) writes the old shapes as bound messages, pointing over a pointing input and its table, stored frames deferred as a host defers them, a press's handler refused a FIELD; counts: b35 against b34 with batches BFS +0.03%, the rest within 0.001%, without batches BFS +0.12%, join +0.01%; the bank's queries (s34 on the 4c aup's module, s35 on 4d's, six each in turns) field -0.23%, found -0.14%, large -0.12%, in_credit +0.11%, the rest within 0.06%; 008 and 027
Diffstat (limited to 'core')
-rw-r--r--core/module-jit.c8
-rw-r--r--core/module-run.h2
-rw-r--r--core/module.c137
-rw-r--r--core/module.h59
-rw-r--r--core/test-module.c652
5 files changed, 461 insertions, 397 deletions
diff --git a/core/module-jit.c b/core/module-jit.c
index 3a3a354..5ba9f03 100644
--- a/core/module-jit.c
+++ b/core/module-jit.c
@@ -2386,7 +2386,7 @@ static uint32_t changes(const PureModule *m, const PureOp *op) {
switch (op->op) {
case PURE_OP_LIST_APPEND: case PURE_OP_LIST_INSERT: case PURE_OP_LIST_REMOVE: return UINT32_C(1) << (op->arg & 31);
case PURE_OP_ITEM_SET: return UINT32_C(1) << (list_of(op->arg) & 31);
- case PURE_OP_CONST: case PURE_OP_LOAD: case PURE_OP_DT: case PURE_OP_ADD: case PURE_OP_SUB: case PURE_OP_MUL:
+ case PURE_OP_CONST: case PURE_OP_LOAD: case PURE_OP_ADD: case PURE_OP_SUB: case PURE_OP_MUL:
case PURE_OP_DIV: case PURE_OP_NEG: case PURE_OP_LT: case PURE_OP_LE: case PURE_OP_GT: case PURE_OP_GE: case PURE_OP_EQ:
case PURE_OP_NE: case PURE_OP_AND: case PURE_OP_OR: case PURE_OP_NOT: case PURE_OP_MIN: case PURE_OP_MAX: case PURE_OP_CLAMP:
case PURE_OP_STORE: case PURE_OP_JZ: case PURE_OP_JMP: case PURE_OP_FIELD: case PURE_OP_LIST_COUNT: case PURE_OP_ITEM_GET:
@@ -5556,10 +5556,8 @@ static void tier_free(Tier *t) {
/* The handler's message input the block is, or -1. */
static int tier_input(const PureModule *m, PureBlock block) {
for (uint32_t i = 0; i < m->handlers; ++i)
- if (m->handler[i].code.start == block.start && m->handler[i].code.length == block.length && m->handler[i].event) {
- int input = (int)((m->handler[i].event - 1) / 2);
- return m->input[input].kind == PURE_KIND_MESSAGE ? input : -1;
- }
+ if (m->handler[i].code.start == block.start && m->handler[i].code.length == block.length)
+ return (int)m->handler[i].event; /* a message input's (027's 4d) */
return -1;
}
diff --git a/core/module-run.h b/core/module-run.h
index ceaa902..cff3f71 100644
--- a/core/module-run.h
+++ b/core/module-run.h
@@ -107,7 +107,7 @@ typedef struct {
const char *key; /* a keyed message's key */
int64_t base; /* the revision a message's edit was made against, or -1 */
PureArena *arena;
- double dt;
+
size_t row; /* the row a column is evaluated for (in a fold, the fold's item) */
size_t outer; /* in a fold, the row of the column it runs in */
Walk *walk; /* the walk the code runs in, or NULL */
diff --git a/core/module.c b/core/module.c
index 55923b2..2fbd37c 100644
--- a/core/module.c
+++ b/core/module.c
@@ -25,7 +25,7 @@ enum { FROM_PAYLOAD, FROM_COPY, FROM_CONSTANT, FROM_TEXT, FROM_ARENA, FROM_ARGUM
/* IN_LOCALS: a handler's locals, read in its walks and the folds in them. */
/* IN_COMPUTE: what a view's body may do too (018): loops, locals, and changes to an event's lists alone. */
/* IN_FUNCTION: a function's code (027's 2c), a handler's but for a message. */
-enum { IN_VIEW = 1, IN_HANDLER = 2, IN_MESSAGE = 4, IN_TEXT = 16, IN_ROW = 32, IN_OUTER = 64, IN_ARGUMENTS = 128,
+enum { IN_VIEW = 1, IN_HANDLER = 2, IN_MESSAGE = 4, IN_ROW = 32, IN_OUTER = 64, IN_ARGUMENTS = 128,
IN_LOCALS = 256, IN_COMPUTE = 512, IN_FUNCTION = 1024 };
enum { READ = IN_VIEW | IN_HANDLER };
/* What an argument names: nothing (it is 0), a state of one type (A_STATE +
@@ -72,7 +72,6 @@ static const struct { unsigned char pops, in[3], out; unsigned short where; unsi
[PURE_OP_NE] = COMPARE, [PURE_OP_AND] = LOGIC(2), [PURE_OP_OR] = LOGIC(2), [PURE_OP_NOT] = LOGIC(1),
[PURE_OP_MIN] = NUMS(2), [PURE_OP_MAX] = NUMS(2), [PURE_OP_CLAMP] = NUMS(3),
[PURE_OP_LOAD] = {0, {0}, T_STATE, READ, A_VALUE, O_ANY},
- [PURE_OP_DT] = {0, {0}, T_NUM, IN_HANDLER, A_NONE, O_ANY},
[PURE_OP_STORE] = {1, {T_STATE}, 0, IN_HANDLER, A_VALUE, O_ANY},
[PURE_OP_JZ] = {1, {T_BOOL}, 0, READ, A_TARGET, O_ANY},
[PURE_OP_JMP] = {0, {0}, 0, READ, A_TARGET, O_ANY},
@@ -82,7 +81,6 @@ static const struct { unsigned char pops, in[3], out; unsigned short where; unsi
[PURE_OP_REQUIRE] = {1, {T_BOOL}, 0, IN_HANDLER, A_NONE, O_ANY},
[PURE_OP_TEXT_CONST] = {0, {0}, T_TEXT, READ, A_CONSTANT, O_ANY},
[PURE_OP_TEXT_LOAD] = {0, {0}, T_TEXT, READ, STATE(TEXT), O_ANY},
- [PURE_OP_TEXT_INPUT] = {0, {0}, T_TEXT, IN_TEXT, A_NONE, O_ANY},
[PURE_OP_NUM_TEXT] = {1, {T_NUM}, T_TEXT, READ, A_FORMAT, O_ANY},
[PURE_OP_CONCAT] = {2, {T_TEXT, T_TEXT}, T_TEXT, READ, A_JOINED, O_ANY},
[PURE_OP_CHARS] = {1, {T_TEXT}, T_NUM, READ, A_NONE, O_ANY},
@@ -328,8 +326,9 @@ PURE_COLD static bool name_at(const unsigned char *strings, uint32_t size, uint3
return true;
}
-/* The input whose handler this is, or -1 for frame. */
-static int event_input(uint32_t event) { return event ? (int)((event - 1) / 2) : -1; }
+/* The verifier's arrival: the message input a handler handles or a view's arguments come in, as its number + 1 (0:
+ * none, a function's or a view's without arguments). */
+static int event_input(uint32_t event) { return event ? (int)event - 1 : -1; }
/* The state an argument names must have this type. */
static bool state_of(const PureModule *m, uint32_t state, uint8_t type) {
@@ -506,7 +505,6 @@ PURE_COLD static const char *verify_code(const PureModule *m, PureBlock block, i
: !handler ? IN_VIEW | (row >= 0 ? IN_ROW : 0) | (folding && outer >= 0 ? IN_OUTER : 0) | (body ? IN_COMPUTE : 0)
: IN_HANDLER | (view == WALK ? IN_ROW : 0);
if (shape && handler && !steps && shape->kind == PURE_KIND_MESSAGE) where |= IN_MESSAGE;
- if (shape && handler && !steps && shape->kind == PURE_KIND_TEXT) where |= IN_TEXT;
if (shape && (!handler || steps) && (shape->kind == PURE_KIND_MESSAGE || shape->kind == PURE_KIND_ARGUMENTS))
where |= IN_ARGUMENTS;
if (locals) where |= IN_LOCALS;
@@ -554,7 +552,6 @@ PURE_COLD static const char *verify_code(const PureModule *m, PureBlock block, i
if (view == FILTER) REFUSE("filter with an effect");
if (folding) REFUSE("fold with an effect");
if (!handler || steps) REFUSE("view with an effect");
- if (allowed & IN_TEXT) REFUSE("text input outside a text handler");
REFUSE("message operation outside a message handler");
}
uint32_t arg = op->arg;
@@ -1716,36 +1713,38 @@ PURE_COLD PureResult pure_module_load(const void *bytes, size_t size, PureModule
for (uint32_t i = 0; i < out->inputs; ++i, at += INPUT_BYTES) {
PureInputShape *shape = &out->input[i];
shape->kind = at[4];
+ shape->binding = at[5];
shape->field_start = (uint16_t)read16(at + 12);
shape->field_count = (uint16_t)read16(at + 14);
- /* Arguments bear their view's name (view.args); other inputs are identifiers. */
- if (!name_at(text, strings, read32(at), shape->kind == PURE_KIND_ARGUMENTS ? NAME_PATH : NAME_IDENTIFIER,
- out->input_names[i])) REFUSE("input name", i);
- if (read16(at + 6) || at[5]) REFUSE("input flags", i);
- if (shape->kind == PURE_KIND_KEY) {
- if (shape->field_start || shape->field_count ||
- !name_at(text, strings, read32(at + 8), NAME_PRINTABLE, out->input_keys[i]) ||
- !key_name(out->input_keys[i])) REFUSE("input key", i);
+ /* Arguments bear their view's name (view.args), and a message may bear a path (an aup's hidden one: up.up,
+ * list.point); an output is an identifier. */
+ if (!name_at(text, strings, read32(at), shape->kind == PURE_KIND_OUTPUT ? NAME_IDENTIFIER : NAME_PATH, out->input_names[i]))
+ REFUSE("input name", i);
+ bool arguments = shape->kind == PURE_KIND_ARGUMENTS, keyed = shape->binding == PURE_BIND_PRESS || shape->binding == PURE_BIND_RELEASE;
+ if (read16(at + 6) || shape->binding > PURE_BIND_POINT || (shape->binding && shape->kind != PURE_KIND_MESSAGE))
+ REFUSE("input flags", i);
+ /* What a binding names (027's 4d): a key, a timer's period, a pointing's table (checked with the views). */
+ if (keyed) {
+ if (!name_at(text, strings, read32(at + 8), NAME_PRINTABLE, out->input_keys[i]) || !key_name(out->input_keys[i]))
+ REFUSE("input key", i);
for (uint32_t k = 0; k < i; ++k)
- if (out->input[k].kind == PURE_KIND_KEY && !strcmp(out->input_keys[k], out->input_keys[i]))
+ if (out->input[k].binding == shape->binding && !strcmp(out->input_keys[k], out->input_keys[i]))
REFUSE("duplicate key", i);
- continue;
- }
- if (shape->kind == PURE_KIND_TEXT) {
- if (shape->field_start || shape->field_count || read32(at + 8)) REFUSE("text input shape", i);
- continue;
- }
- if (shape->kind == PURE_KIND_TIMER) {
- shape->period = read32(at + 8);
- if (shape->field_start || shape->field_count || !shape->period ||
- shape->period > PURE_TIMER_LONGEST) REFUSE("timer input", i);
- continue;
}
- bool arguments = shape->kind == PURE_KIND_ARGUMENTS;
- if ((shape->kind != PURE_KIND_MESSAGE && shape->kind != PURE_KIND_OUTPUT && !arguments) || (!arguments && read32(at + 8)) ||
- shape->field_start != next_field ||
+ if (shape->binding == PURE_BIND_TIMER && (!(shape->period = read32(at + 8)) || shape->period > PURE_TIMER_LONGEST))
+ REFUSE("timer input", i);
+ shape->view = arguments || shape->binding == PURE_BIND_POINT ? read32(at + 8) : 0;
+ if ((shape->kind != PURE_KIND_MESSAGE && shape->kind != PURE_KIND_OUTPUT && !arguments) ||
+ (read32(at + 8) && !arguments && !shape->binding) || shape->field_start != next_field ||
shape->field_count > out->fields - next_field) REFUSE("message shape", i);
- shape->view = arguments ? read32(at + 8) : 0;
+ /* A binding's fields: a key's and a timer's none, a text's one text, a frame's one number, a pointing's one u32. */
+ static const uint8_t bound_field[] = {[PURE_BIND_TEXT] = PURE_FIELD_TEXT, [PURE_BIND_FRAME] = PURE_FIELD_NUMBER,
+ [PURE_BIND_POINT] = PURE_FIELD_U32};
+ if (shape->binding && shape->field_count != (bound_field[shape->binding] != 0)) REFUSE("binding's fields", i);
+ if (shape->binding && shape->field_count &&
+ read32(fields + (size_t)next_field * FIELD_BYTES + 4) != bound_field[shape->binding]) REFUSE("binding's fields", i);
+ for (uint32_t k = 0; shape->binding == PURE_BIND_FRAME && k < i; ++k)
+ if (out->input[k].binding == PURE_BIND_FRAME) REFUSE("second frame input", i);
for (uint32_t f = 0; f < shape->field_count; ++f) {
const unsigned char *entry = fields + (size_t)(next_field + f) * FIELD_BYTES;
PureField *field = &out->field[next_field + f];
@@ -1866,6 +1865,9 @@ PURE_COLD PureResult pure_module_load(const void *bytes, size_t size, PureModule
* read the table's), one set a view, bearing its name. */
for (uint32_t i = 0; i < out->inputs; ++i) {
const PureInputShape *shape = &out->input[i];
+ /* A pointing points at a table's rows (027's 4d). */
+ if (shape->binding == PURE_BIND_POINT && (shape->view >= out->views || out->view_type[shape->view] != PURE_VIEW_TABLE))
+ REFUSE("pointing's table", i);
if (shape->kind != PURE_KIND_ARGUMENTS) continue;
char path[PURE_MODULE_NAME + 8];
if (shape->view >= out->views || out->view_table[shape->view] || out->view_arguments[shape->view]) REFUSE("arguments", i);
@@ -1878,10 +1880,7 @@ PURE_COLD PureResult pure_module_load(const void *bytes, size_t size, PureModule
for (uint32_t i = 0; i < out->handlers; ++i, at += HANDLER_BYTES) {
uint32_t event = read32(at);
out->handler[i] = (PureHandler){event, {read32(at + 4), read32(at + 8)}};
- int input = event_input(event);
- if (event > 2u * out->inputs || (input >= 0 && out->input[input].kind != PURE_KIND_KEY && !(event & 1)) ||
- (input >= 0 && (out->input[input].kind == PURE_KIND_OUTPUT || out->input[input].kind == PURE_KIND_ARGUMENTS)))
- REFUSE("handler event", i);
+ if (event >= out->inputs || out->input[event].kind != PURE_KIND_MESSAGE) REFUSE("handler event", i);
}
/* Functions (027's 2c): a block and the counts of its parameters and results, whose types follow every function's. */
for (uint32_t i = 0, item = 0; i < out->functions; ++i, at += FUNCTION_BYTES) {
@@ -1921,7 +1920,7 @@ PURE_COLD PureResult pure_module_load(const void *bytes, size_t size, PureModule
for (uint32_t i = 0; i < out->views; ++i) {
bad = i;
bool table = out->view_type[i] == PURE_VIEW_TABLE;
- uint32_t event = out->view_arguments[i] ? 2u * out->view_arguments[i] - 1u : 0; /* the arguments' input, as an arrival */
+ uint32_t event = out->view_arguments[i]; /* the arguments' input, as an arrival (its number + 1) */
bool computes = !table && !out->view_table[i]; /* a view outside a table may have a body */
if ((!table || out->view_code[i].length) &&
(why = verify_block(out, out->view_code[i], table ? FILTER : out->view_type[i],
@@ -1960,7 +1959,8 @@ PURE_COLD PureResult pure_module_load(const void *bytes, size_t size, PureModule
}
for (uint32_t i = 0; i < out->handlers; ++i) {
bad = i;
- if ((why = verify_block(out, out->handler[i].code, -1, -1, -1, out->handler[i].event, true, 0, 0, -1, &bad))) goto refused;
+ if ((why = verify_block(out, out->handler[i].code, -1, -1, -1, out->handler[i].event + 1, true, 0, 0, -1, &bad)))
+ goto refused;
if (!locals_agree(out, out->handler[i].code)) { why = "local used as two things"; goto refused; }
}
/* An instruction in no handler's, view's or function's block never runs and was never verified: it becomes CONST 0,
@@ -1994,8 +1994,7 @@ PURE_COLD PureResult pure_module_load(const void *bytes, size_t size, PureModule
}
}
for (uint32_t i = 0; i < out->handlers; ++i) {
- int input = event_input(out->handler[i].event);
- input = input >= 0 && out->input[input].kind == PURE_KIND_MESSAGE ? input : -1;
+ int input = (int)out->handler[i].event;
plan_folds(out, out->handler[i].code, input);
range_code(out, out->handler[i].code, input);
}
@@ -2194,13 +2193,6 @@ int pure_module_view(const PureModule *m, const char *name) {
return -1;
}
-int pure_module_position(const PureModule *m, uint32_t view) {
- if (view >= m->views || m->view_type[view] != PURE_VIEW_NUM || m->view_table[view]) return -1;
- const PureBlock *block = &m->view_code[view];
- const PureOp *code = &m->code[block->start];
- return block->length == 2 && code[0].op == PURE_OP_INDEX_LOAD && code[1].op == PURE_OP_INDEX_NUM ? (int)code[0].arg : -1;
-}
-
int pure_module_text_state(const PureModule *m, uint32_t view) {
if (view >= m->views || m->view_type[view] != PURE_VIEW_TEXT || m->view_table[view]) return -1;
const PureBlock *block = &m->view_code[view];
@@ -3729,7 +3721,6 @@ static inline __attribute__((always_inline)) int step(Frame *frame, const PureOp
case PURE_OP_BREAK: *next = leave_loop(frame); return STEP_NEXT;
case PURE_OP_CONST: stack[sp++].i64 = op->integer; NEXT; /* the bits: a number's, a bool's or an i64's */
case PURE_OP_LOAD: stack[sp++].i64 = values[arg].i64; NEXT;
- case PURE_OP_DT: stack[sp++].num = ev->dt; NEXT;
case PURE_OP_NOW: stack[sp++].num = app->now; NEXT;
case PURE_OP_FLOOR: top->num = floor(top->num); NEXT;
case PURE_OP_STORE: case PURE_OP_CUR_STORE: values[arg].i64 = stack[--sp].i64; NEXT;
@@ -3770,7 +3761,6 @@ static inline __attribute__((always_inline)) int step(Frame *frame, const PureOp
stack[sp++].bytes = (PureBytes){FROM_CONSTANT, 0, {0, 0}, m->constant_offset[arg], m->constant_length[arg]};
NEXT;
case PURE_OP_TEXT_LOAD: stack[sp++].bytes = text_of(app, arg); NEXT;
- case PURE_OP_TEXT_INPUT: stack[sp++].bytes = (PureBytes){FROM_PAYLOAD, 0, {0, 0}, 0, app->payload.size}; NEXT;
case PURE_OP_NUM_TEXT: {
char digits[32];
if (arg > 2) { /* in a format */
@@ -4804,7 +4794,6 @@ static void open_burst(PureApp *app) {
if (app->open) return;
const PureModule *m = app->module;
memcpy(app->durable_values, app->values, sizeof(app->values));
- app->durable_ticks = app->ticks;
app->durable_events = app->events;
for (uint32_t i = 0; i < m->states; ++i) {
if (app->texts[i]) {
@@ -4836,7 +4825,6 @@ static void close_burst(PureApp *app, bool keep) {
else {
pure_undo_rollback(&app->undo, 0);
memcpy(app->values, app->durable_values, sizeof(app->values));
- app->ticks = app->durable_ticks;
app->events = app->durable_events;
++app->changes;
}
@@ -5140,57 +5128,10 @@ static PureResult dispatch(PureApp *app, uint32_t event, Event *ev) {
memcpy(app->values, pending, sizeof(pending));
++app->events;
++app->changes;
- if (!event) ++app->ticks;
app->delivering.pending = false;
- return event && !app->deferred ? pure_app_save(app) : PURE_OK;
-}
-
-PureResult pure_app_frame(PureApp *app) {
- pure_app_finish(app);
- Event ev = {.app = app, .arena = &app->arena, .dt = 1.0 / 60.0};
- return dispatch(app, 0, &ev);
-}
-
-PureResult pure_app_tick(PureApp *app, uint32_t input) {
- if (input >= app->module->inputs || app->module->input[input].kind != PURE_KIND_TIMER) return PURE_INVALID;
- pure_app_finish(app);
- Event ev = {.app = app, .arena = &app->arena};
- return dispatch(app, 1 + 2 * input, &ev);
-}
-
-PureResult pure_app_input(PureApp *app, uint32_t input, PureEdge edge) {
- if (input >= app->module->inputs || app->module->input[input].kind != PURE_KIND_KEY ||
- (edge != PURE_EDGE_DOWN && edge != PURE_EDGE_UP)) return PURE_INVALID;
- pure_app_finish(app);
- Event ev = {.app = app, .arena = &app->arena};
- return dispatch(app, 1 + 2 * input + (edge == PURE_EDGE_UP), &ev);
-}
-
-PureResult pure_app_point(PureApp *app, uint32_t state, size_t position) {
- const PureModule *m = app->module;
- pure_app_finish(app);
- if (state >= m->states || m->state_type[state] != PURE_STATE_INDEX) return PURE_INVALID;
- if (position >= pure_list_count(app->lists[m->state_ref[state]])) return PURE_NOT_FOUND;
- open_burst(app); /* what a failed save returns to */
- app->fault = PURE_FAULT_NONE;
- app->fault_pc = UINT32_MAX;
- app->values[state].num = (double)position;
- ++app->events;
- ++app->changes;
return app->deferred ? PURE_OK : pure_app_save(app);
}
-PureResult pure_app_text(PureApp *app, uint32_t input, PureSpan text) {
- pure_app_finish(app);
- if (input >= app->module->inputs || app->module->input[input].kind != PURE_KIND_TEXT ||
- (text.size && !text.data) || !pure_utf8_valid(text)) return PURE_INVALID;
- app->payload = text;
- Event ev = {.app = app, .arena = &app->arena};
- PureResult result = dispatch(app, 1 + 2 * input, &ev);
- app->payload = (PureSpan){0};
- return result;
-}
-
static PureResult keep_reply(PureApp *app) {
size_t length = (size_t)app->reply.length, start = app->arena.used;
unsigned char *copy = malloc(length ? length : 1);
@@ -5287,7 +5228,7 @@ static PureResult message(PureApp *app, uint32_t input, PureSpan payload, const
}
app->payload = payload;
Event ev = {.app = app, .shape = shape, .arena = &app->arena, .nested = nested, .spans = spans};
- PureResult result = dispatch(app, 1 + 2 * input, &ev);
+ PureResult result = dispatch(app, input, &ev);
if (result != PURE_OK) pure_app_finish(app);
return result;
}
diff --git a/core/module.h b/core/module.h
index 16c2a70..7cf90b9 100644
--- a/core/module.h
+++ b/core/module.h
@@ -23,13 +23,13 @@ enum {
};
typedef enum {
- PURE_OP_CONST, PURE_OP_LOAD, PURE_OP_DT, PURE_OP_ADD, PURE_OP_SUB, PURE_OP_MUL,
+ PURE_OP_CONST, PURE_OP_LOAD, PURE_OP_ADD, PURE_OP_SUB, PURE_OP_MUL,
PURE_OP_DIV, PURE_OP_NEG, PURE_OP_LT, PURE_OP_LE, PURE_OP_GT, PURE_OP_GE,
PURE_OP_EQ, PURE_OP_NE, PURE_OP_AND, PURE_OP_OR, PURE_OP_NOT, PURE_OP_MIN,
PURE_OP_MAX, PURE_OP_CLAMP, PURE_OP_STORE, PURE_OP_JZ, PURE_OP_JMP,
PURE_OP_FIELD, PURE_OP_LEN,
PURE_OP_REPLY, PURE_OP_REQUIRE,
- PURE_OP_TEXT_CONST, PURE_OP_TEXT_LOAD, PURE_OP_TEXT_INPUT, PURE_OP_NUM_TEXT,
+ PURE_OP_TEXT_CONST, PURE_OP_TEXT_LOAD, PURE_OP_NUM_TEXT,
PURE_OP_CONCAT, PURE_OP_CHARS, PURE_OP_LINES, PURE_OP_TEXT_BYTES,
PURE_OP_ANCHOR_LOAD, PURE_OP_ANCHOR_START, PURE_OP_ANCHOR_END, PURE_OP_ANCHOR_MOVE,
PURE_OP_ANCHOR_STORE, PURE_OP_TEXT_INSERT, PURE_OP_TEXT_REMOVE, PURE_OP_TEXT_LINE,
@@ -206,15 +206,23 @@ enum { PURE_LOOP_INTEGER = 256, PURE_MODULE_NESTING = 8 };
* comparisons (LT LE GT GE EQ NE); texts order by their bytes. */
typedef enum { PURE_LESS, PURE_LESS_EQUAL, PURE_GREATER, PURE_GREATER_EQUAL, PURE_EQUAL, PURE_NOT_EQUAL } PureRelation;
-/* A timer ticks every `period` milliseconds while a host runs the app. An
- * output is a message the app sends (SEND, in a handler; it has fields as a
- * message has and no handler): what receives it is the host's wiring.
- * Arguments are the fields a view takes (a view with arguments, its
- * columns and its filter read them as a message handler reads its message);
- * such an input has no handler and names its view, whose name it bears
- * with .arguments after it. */
-typedef enum { PURE_KIND_KEY = 1, PURE_KIND_MESSAGE = 2, PURE_KIND_TEXT = 3, PURE_KIND_TIMER = 4,
- PURE_KIND_OUTPUT = 5, PURE_KIND_ARGUMENTS = 6 } PureInputKind;
+/* An input is a message (027's 4d: the only thing an app takes), an output
+ * or a view's arguments. An output is a message the app sends (SEND, in a
+ * handler; it has fields as a message has and no handler): what receives it
+ * is the host's wiring. Arguments are the fields a view takes (a view with
+ * arguments, its columns and its filter read them as a message handler reads
+ * its message); such an input has no handler and names its view, whose name
+ * it bears with .args after it. */
+typedef enum { PURE_KIND_MESSAGE = 1, PURE_KIND_OUTPUT = 2, PURE_KIND_ARGUMENTS = 3 } PureInputKind;
+/* What makes a message, for its hosts and surfaces (027's 4d): nothing said
+ * (a sender's), a key pressed or released (the key's name: no fields), text
+ * that came in (one text field), a timer's tick every period milliseconds
+ * (no fields: the time is the event's now), a frame of the screen (one
+ * number field, the seconds since the last) or a pointing at a row of a
+ * table view (one u32 field, the row). The runtime reads none of it but the
+ * fields it says a message has. */
+typedef enum { PURE_BIND_NONE, PURE_BIND_PRESS, PURE_BIND_RELEASE, PURE_BIND_TEXT, PURE_BIND_TIMER, PURE_BIND_FRAME,
+ PURE_BIND_POINT } PureBinding;
enum { PURE_TIMER_LONGEST = 86400000, PURE_SOURCE = 64 };
/* The largest number a delivery may have: it is kept as a binary64. */
#define PURE_SEQ_MOST (UINT64_C(1) << 53)
@@ -283,7 +291,6 @@ enum { PURE_STATE_TRANSIENT = 256 };
* empty (a reader's own: readers of one snapshot compute apart). */
typedef enum { PURE_VIEW_NUM = 0, PURE_VIEW_TEXT = 1, PURE_VIEW_TABLE = 2, PURE_VIEW_BOOL = 3, PURE_VIEW_I64 = 4 } PureViewType;
typedef enum { PURE_UNIT_CHAR = 0, PURE_UNIT_LINE = 1 } PureUnit;
-typedef enum { PURE_EDGE_DOWN, PURE_EDGE_UP } PureEdge;
/* EXISTS: an item with that key is already in the keyed list (the event
* reports PURE_CONFLICT). MEMORY: the event would leave the states holding
* more than the app's budget (pure_app_memory; it reports PURE_BUDGET).
@@ -307,7 +314,7 @@ const char *pure_fault_name(PureFault);
* PureRange + 1). */
typedef struct { union { double value; int64_t integer; }; uint32_t arg; uint8_t op, plan, shape, range; } PureOp;
typedef struct { uint32_t start, length; } PureBlock;
-typedef struct { uint32_t event; PureBlock code; } PureHandler;
+typedef struct { uint32_t event; PureBlock code; } PureHandler; /* event: the message input it handles (027's 4d) */
/* A function's block and its parameters' and results' types, from first among the module's signature items: a record
* field's type (PureRecordType: a list's with its record, bytes as PURE_RECORD_BYTES). Set at load: the event's lists,
* registers and held values each call has its own of (a bit each), the lists not an event's it changes (or a function
@@ -321,11 +328,11 @@ typedef struct {
} PureFunction;
typedef struct { char name[PURE_MODULE_NAME]; uint8_t type; uint16_t rows; uint32_t offset; } PureField;
typedef struct {
- uint8_t kind;
+ uint8_t kind, binding; /* PureInputKind, PureBinding (a key's name in input_keys) */
uint16_t field_start, field_count;
uint32_t prefix; /* fixed payload bytes before the variable fields */
uint32_t period; /* a timer's, in milliseconds */
- uint32_t view; /* arguments: the view they are for */
+ uint32_t view; /* arguments: the view they are for; a pointing's: its table */
} PureInputShape;
/* A record type: fields numbered from first, each a number or text. */
@@ -454,9 +461,6 @@ bool pure_module_takes(const PureModule *, uint32_t input, const unsigned char *
* when payload is not laid out with them. */
PureResult pure_module_recast(const PureModule *, uint32_t input, const unsigned char *fields, size_t size,
PureSpan payload, unsigned char **out, size_t *out_size);
-/* The index state a number view shows as its position (a view that is just
- * `at.position`), or -1: what a host moves with pure_app_point. */
-int pure_module_position(const PureModule *, uint32_t view);
/* The text state a text view shows as it is (a view that is just the
* state), or -1: a host may lend its stored extents instead of copying. */
int pure_module_text_state(const PureModule *, uint32_t view);
@@ -522,7 +526,7 @@ typedef struct {
PureValue durable_values[PURE_MODULE_STATES];
PureVersion durable_texts[PURE_MODULE_STATES];
size_t durable_lists[PURE_MODULE_STATES]; /* each stored list's journal length */
- uint64_t durable_ticks, durable_events;
+ uint64_t durable_events;
PureFault fault;
uint32_t fault_pc;
size_t memory; /* the budget of what the states hold (0: none): pure_app_memory */
@@ -533,7 +537,7 @@ typedef struct {
PureFault view_fault; /* why the last view evaluated had no value (pure_app_view_fault) */
char why[128]; /* why the last start or replace was refused, when it says (else empty; a failed start keeps it) */
uint32_t counted; /* the states it counts, a bit each: lists (not a message's rows) and texts */
- uint64_t ticks, events;
+ uint64_t events;
/* The time events see as now, in milliseconds since 1970 (UTC). The host
* sets it, so a script can replay any time; views never see it. */
double now;
@@ -600,19 +604,10 @@ PureResult pure_app_save(PureApp *);
/* Defer saving to pure_app_save (false: save after each event). Needs a data
* host that saves together (PureData.save). */
bool pure_app_defer(PureApp *, bool deferred);
-/* Leaves unsaved frame changes unsaved: call pure_app_save first to keep them. */
+/* Leaves unsaved changes unsaved: call pure_app_save first to keep them. */
void pure_app_stop(PureApp *);
-PureResult pure_app_frame(PureApp *);
-/* A timer input's tick: an event saved like a key's. */
-PureResult pure_app_tick(PureApp *, uint32_t input);
-PureResult pure_app_input(PureApp *, uint32_t input, PureEdge);
-/* Point an index state at the item at position (0 first), as a user pointing
- * at a row: an event with no handler, saved like a key event. NOT_FOUND past
- * the last item, INVALID for a state that is not an index. */
-PureResult pure_app_point(PureApp *, uint32_t state, size_t position);
-/* Deliver committed text (for example from an input method). Not UTF-8: invalid. */
-PureResult pure_app_text(PureApp *, uint32_t input, PureSpan text);
-/* Deliver a message. payload stays alive until pure_app_finish. */
+/* Deliver a message (a key's, a text's, a tick's or a frame's too, as its binding says it is laid out). payload stays
+ * alive until pure_app_finish. */
PureResult pure_app_message(PureApp *, uint32_t input, PureSpan payload);
/* The same, with the variable fields where the host found them: field f
* starts at placed[2f] in payload and has placed[2f + 1] bytes (entries of
diff --git a/core/test-module.c b/core/test-module.c
index 3c221e4..a27c0aa 100644
--- a/core/test-module.c
+++ b/core/test-module.c
@@ -35,12 +35,15 @@ int pure_store_test_fault(PureStoreStage stage) {
typedef struct { const char *name; double initial; } S;
typedef struct { const char *name; uint32_t type; } F;
-/* kind 0 is a key input; otherwise PURE_KIND_MESSAGE with fields. Arguments
- * name their view by its number in key. */
+/* kind 0 is a key input, TEXT_INPUT a text input, TIMER_INPUT a timer; otherwise PURE_KIND_MESSAGE with fields.
+ * Arguments name their view by its number in key. A module has messages bound so (027's 4d): expand writes them. */
typedef struct { const char *name, *key; int kind; const F *fields; size_t nfields; } I;
+/* POINT_INPUT: a pointing message (one u32 field), its table the view numbered key. DT and TI (250, 251): dt and a
+ * text input's text, which a module reads as fields. */
+enum { TEXT_INPUT = 100, TIMER_INPUT = 101, POINT_INPUT = 102, TEST_DT = 250, TEST_TEXT_IN = 251 };
#define KEY(name, key) {name, key, 0, NULL, 0}
/* A timer's period (milliseconds) rides in nfields. */
-#define TIMER(name, period) {name, NULL, PURE_KIND_TIMER, NULL, period}
+#define TIMER(name, period) {name, NULL, TIMER_INPUT, NULL, period}
/* A number view, or a bool (i64) view when its length carries BOOL_VIEW (I64_VIEW). */
typedef struct { const char *name; uint32_t start, length; } V;
#define BOOL_VIEW (1u << 31)
@@ -113,7 +116,74 @@ static uint8_t module_op(uint8_t op) {
}
}
+/* A test's inputs, handlers and code as a module has them (027's 4d): a key input a message its press makes, one more
+ * its release makes for a key up's handler (event 2 + 2i), a frame's message (one number field) for a frame's handler
+ * (event 0), a text input a message of one text field, a timer a message its ticks make; a handler's event its
+ * message's number; dt (DT, 250) the frame's field in its handler and 0 elsewhere, a text input's text (TI, 251) its
+ * field. */
+static I expanded_in[64];
+static struct { uint8_t binding; uint32_t period; } expanded_bound[64];
+static H expanded_h[64];
+static O expanded_o[8192];
+static Spec expand(Spec spec) {
+ static const F text_field[] = {{"text", PURE_FIELD_TEXT}}, dt_field[] = {{"dt", PURE_FIELD_NUMBER}};
+ static char release_names[64][PURE_MODULE_NAME];
+ static const F position_field[] = {{"position", PURE_FIELD_U32}};
+ size_t ni = 0;
+ for (size_t i = 0; i < spec.ni; ++i) {
+ I in = spec.in[i];
+ uint8_t binding = 0;
+ uint32_t period = 0;
+ if (!in.kind) in = (I){in.name, in.key, PURE_KIND_MESSAGE, NULL, 0}, binding = PURE_BIND_PRESS;
+ else if (in.kind == TEXT_INPUT) in = (I){in.name, NULL, PURE_KIND_MESSAGE, text_field, 1}, binding = PURE_BIND_TEXT;
+ else if (in.kind == TIMER_INPUT)
+ period = (uint32_t)in.nfields, in = (I){in.name, NULL, PURE_KIND_MESSAGE, NULL, 0}, binding = PURE_BIND_TIMER;
+ else if (in.kind == POINT_INPUT) in = (I){in.name, in.key, PURE_KIND_MESSAGE, position_field, 1}, binding = PURE_BIND_POINT;
+ expanded_bound[ni].binding = binding, expanded_bound[ni].period = period;
+ expanded_in[ni++] = in;
+ }
+ int release[64], frame = -1;
+ for (size_t i = 0; i < spec.ni; ++i) release[i] = -1;
+ for (size_t h = 0; h < spec.nh; ++h) {
+ uint32_t e = spec.h[h].event;
+ if (!e && frame < 0) {
+ frame = (int)ni;
+ expanded_bound[ni].binding = PURE_BIND_FRAME, expanded_bound[ni].period = 0;
+ expanded_in[ni++] = (I){"frame", NULL, PURE_KIND_MESSAGE, dt_field, 1};
+ } else if (e && !(e & 1) && (e - 2) / 2 < spec.ni && release[(e - 2) / 2] < 0 &&
+ expanded_bound[(e - 2) / 2].binding == PURE_BIND_PRESS) {
+ uint32_t of = (e - 2) / 2;
+ snprintf(release_names[of], PURE_MODULE_NAME, "%s.up", spec.in[of].name);
+ release[of] = (int)ni;
+ expanded_bound[ni].binding = PURE_BIND_RELEASE, expanded_bound[ni].period = 0;
+ expanded_in[ni++] = (I){release_names[of], spec.in[of].key, PURE_KIND_MESSAGE, NULL, 0};
+ }
+ }
+ CHECK(ni <= 64 && spec.nh <= 64 && spec.no <= 8192);
+ for (size_t h = 0; h < spec.nh; ++h) {
+ uint32_t e = spec.h[h].event;
+ expanded_h[h] = spec.h[h];
+ /* an event past the inputs, or a release of what is not a key, past them all (a test of a refusal) */
+ int up = !(e & 1) && e && (e - 2) / 2 < spec.ni ? release[(e - 2) / 2] : -1;
+ expanded_h[h].event = !e ? (uint32_t)frame : e & 1 ? (e - 1) / 2 : up >= 0 ? (uint32_t)up : 1000 + e;
+ }
+ for (size_t k = 0; k < spec.no; ++k) {
+ expanded_o[k] = spec.o[k];
+ if (spec.o[k].op == TEST_TEXT_IN) expanded_o[k] = (O){PURE_OP_FIELD, 0, 0};
+ if (spec.o[k].op != TEST_DT) continue;
+ bool in_frame = false;
+ for (size_t h = 0; h < spec.nh; ++h)
+ in_frame = in_frame || (!spec.h[h].event && k >= spec.h[h].start && k < spec.h[h].start + spec.h[h].length);
+ expanded_o[k] = in_frame ? (O){PURE_OP_FIELD, 0, 0} : (O){PURE_OP_CONST, 0, 0};
+ }
+ spec.in = expanded_in, spec.ni = ni;
+ spec.h = expanded_h;
+ spec.o = expanded_o;
+ return spec;
+}
+
static size_t build(unsigned char *out, Spec spec) {
+ spec = expand(spec);
unsigned char strings[4096], blob[4096];
size_t used = 0, blob_used = 0;
const char *text[512];
@@ -123,9 +193,8 @@ static size_t build(unsigned char *out, Spec spec) {
for (size_t i = 0; i < spec.nt; ++i) text[names++] = spec.t[i].name;
for (size_t i = 0; i < spec.ni; ++i) {
text[names++] = spec.in[i].name;
- if (!spec.in[i].kind) text[names++] = spec.in[i].key;
- for (size_t f = 0; spec.in[i].kind != PURE_KIND_TIMER && f < spec.in[i].nfields; ++f)
- text[names++] = spec.in[i].fields[f].name;
+ if (expanded_bound[i].binding == PURE_BIND_PRESS || expanded_bound[i].binding == PURE_BIND_RELEASE) text[names++] = spec.in[i].key;
+ for (size_t f = 0; f < spec.in[i].nfields; ++f) text[names++] = spec.in[i].fields[f].name;
}
for (size_t i = 0; i < spec.nv; ++i) text[names++] = spec.v[i].name;
for (size_t i = 0; i < spec.ntv; ++i) text[names++] = spec.tv[i].name;
@@ -144,7 +213,7 @@ static size_t build(unsigned char *out, Spec spec) {
used += length;
}
size_t fields = 0;
- for (size_t i = 0; i < spec.ni; ++i) fields += spec.in[i].kind == PURE_KIND_TIMER ? 0 : spec.in[i].nfields;
+ for (size_t i = 0; i < spec.ni; ++i) fields += spec.in[i].nfields;
for (size_t i = 0; i < spec.nk; ++i) { memcpy(blob + blob_used, spec.k[i], strlen(spec.k[i])); blob_used += strlen(spec.k[i]); }
unsigned char *p = out;
CHECK(!spec.nc); /* (collections are gone: 027's 4b) */
@@ -178,21 +247,21 @@ static size_t build(unsigned char *out, Spec spec) {
size_t nf = 0;
for (size_t i = 0; i < spec.ni; ++i) {
const I *in = &spec.in[i];
+ uint8_t binding = expanded_bound[i].binding;
+ bool keyed = binding == PURE_BIND_PRESS || binding == PURE_BIND_RELEASE;
p = put32(p, offset[n++]);
- *p++ = (unsigned char)(in->kind ? in->kind : 1); *p++ = 0; *p++ = 0; *p++ = 0;
- bool timer = in->kind == PURE_KIND_TIMER;
- p = put32(p, timer ? (uint32_t)in->nfields : in->kind == PURE_KIND_ARGUMENTS ? (uint32_t)strtoul(in->key, NULL, 10) :
- in->kind ? 0 : offset[n++]);
- uint32_t first = in->kind == PURE_KIND_MESSAGE || in->kind == PURE_KIND_OUTPUT || in->kind == PURE_KIND_ARGUMENTS
- ? field_start : 0; /* only messages, outputs and arguments have fields */
- *p++ = (unsigned char)first; *p++ = (unsigned char)(first >> 8);
- *p++ = (unsigned char)(timer ? 0 : in->nfields); *p++ = 0;
- for (size_t f = 0; !timer && f < in->nfields; ++f) field_offsets[nf++] = offset[n++];
- field_start += timer ? 0 : (uint32_t)in->nfields;
+ *p++ = (unsigned char)in->kind; *p++ = binding; *p++ = 0; *p++ = 0;
+ p = put32(p, binding == PURE_BIND_TIMER ? expanded_bound[i].period :
+ in->kind == PURE_KIND_ARGUMENTS || binding == PURE_BIND_POINT ? (uint32_t)strtoul(in->key, NULL, 10) :
+ keyed ? offset[n++] : 0);
+ *p++ = (unsigned char)field_start; *p++ = (unsigned char)(field_start >> 8);
+ *p++ = (unsigned char)in->nfields; *p++ = 0;
+ for (size_t f = 0; f < in->nfields; ++f) field_offsets[nf++] = offset[n++];
+ field_start += (uint32_t)in->nfields;
}
nf = 0;
for (size_t i = 0; i < spec.ni; ++i)
- for (size_t f = 0; spec.in[i].kind != PURE_KIND_TIMER && f < spec.in[i].nfields; ++f) {
+ for (size_t f = 0; f < spec.in[i].nfields; ++f) {
p = put32(p, field_offsets[nf++]);
p = put32(p, spec.in[i].fields[f].type);
}
@@ -254,6 +323,45 @@ static size_t build(unsigned char *out, Spec spec) {
}
static PureModule module_a, module_b;
+
+/* A device's events as a module takes them (027's 4d): a key's press and release, a frame, a tick, text, each the
+ * message bound so (a release or a frame no message takes is no event); one for an input not bound so is INVALID. */
+static PureResult press(PureApp *app, uint32_t input) {
+ if (input >= app->module->inputs || app->module->input[input].binding != PURE_BIND_PRESS) return PURE_INVALID;
+ return pure_app_message(app, input, (PureSpan){0});
+}
+static PureResult release(PureApp *app, uint32_t input) {
+ const PureModule *m = app->module;
+ if (input >= m->inputs || m->input[input].binding != PURE_BIND_PRESS) return PURE_INVALID;
+ for (uint32_t i = 0; i < m->inputs; ++i)
+ if (m->input[i].binding == PURE_BIND_RELEASE && !strcmp(m->input_keys[i], m->input_keys[input]))
+ return pure_app_message(app, i, (PureSpan){0});
+ return PURE_OK;
+}
+static PureResult frame(PureApp *app) {
+ static const double dt = 1.0 / 60.0;
+ for (uint32_t i = 0; i < app->module->inputs; ++i)
+ if (app->module->input[i].binding == PURE_BIND_FRAME) return pure_app_message(app, i, (PureSpan){(const unsigned char *)&dt, 8});
+ return PURE_OK;
+}
+static PureResult tick(PureApp *app, uint32_t input) {
+ if (input >= app->module->inputs || app->module->input[input].binding != PURE_BIND_TIMER) return PURE_INVALID;
+ return pure_app_message(app, input, (PureSpan){0});
+}
+/* A test's index put on an item, as a host's pointing did before 027's 4d (now a message an app takes: pointing), and
+ * saved unless deferred. */
+static PureResult place(PureApp *app, uint32_t state, size_t position) {
+ const PureModule *m = app->module;
+ if (state >= m->states || m->state_type[state] != PURE_STATE_INDEX) return PURE_INVALID;
+ if (position >= pure_list_count(app->lists[m->state_ref[state]])) return PURE_NOT_FOUND;
+ app->values[state].num = (double)position;
+ ++app->changes;
+ return app->deferred ? PURE_OK : pure_app_save(app);
+}
+static PureResult type_in(PureApp *app, uint32_t input, PureSpan text) {
+ if (input >= app->module->inputs || app->module->input[input].binding != PURE_BIND_TEXT) return PURE_INVALID;
+ return pure_app_message(app, input, text);
+}
static char error[128];
static PureResult load(Spec spec, PureModule *out) {
@@ -272,7 +380,7 @@ static PureResult load_functions(Spec spec, const FN *fns, size_t count, PureMod
#define LEN(a) (sizeof(a) / sizeof(*(a)))
#define SPEC(s, in, v, h, o) ((Spec){s, LEN(s), in, LEN(in), v, LEN(v), h, LEN(h), o, LEN(o), NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0})
-enum { C = PURE_OP_CONST, L = PURE_OP_LOAD, DT = PURE_OP_DT, ADD = PURE_OP_ADD,
+enum { C = PURE_OP_CONST, L = PURE_OP_LOAD, DT = TEST_DT, ADD = PURE_OP_ADD,
MUL = PURE_OP_MUL, DIV = PURE_OP_DIV, GT = PURE_OP_GT, MIN = PURE_OP_MIN,
MAX = PURE_OP_MAX, CL = PURE_OP_CLAMP, ST = PURE_OP_STORE, JZ = PURE_OP_JZ,
JMP = PURE_OP_JMP, NEG = PURE_OP_NEG };
@@ -306,16 +414,16 @@ static void behaviour(void) {
CHECK(pure_app_start(&app, &module_a, (PureData){0}) == PURE_OK);
double value;
CHECK(pure_app_view(&app, 1, &value) && value == 40);
- for (int i = 0; i < 25; ++i) CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK);
+ for (int i = 0; i < 25; ++i) CHECK(press(&app, 0) == PURE_OK);
CHECK(app.values[0].num == 25 && pure_app_view(&app, 1, &value) && value == 200);
- CHECK(pure_app_input(&app, 0, PURE_EDGE_UP) == PURE_OK && app.events == 26);
+ CHECK(release(&app, 0) == PURE_OK && app.events == 25);
/* if / else with forward JZ and JMP */
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[1].num == 4);
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[1].num == 5);
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[1].num == 6);
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[1].num == 0);
- CHECK(pure_app_frame(&app) == PURE_OK && app.values[1].num == 1.0 / 60.0 && app.ticks == 1);
- CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_INVALID);
+ CHECK(press(&app, 1) == PURE_OK && app.values[1].num == 4);
+ CHECK(press(&app, 1) == PURE_OK && app.values[1].num == 5);
+ CHECK(press(&app, 1) == PURE_OK && app.values[1].num == 6);
+ CHECK(press(&app, 1) == PURE_OK && app.values[1].num == 0);
+ CHECK(frame(&app) == PURE_OK && app.values[1].num == 1.0 / 60.0);
+ CHECK(press(&app, 2) == PURE_INVALID);
CHECK(pure_module_input(&module_a, "move") == 1 && pure_module_view(&module_a, "width") == 1 &&
pure_module_state(&module_a, "missing") == -1);
@@ -352,7 +460,7 @@ static void faults(void) {
CHECK(pure_app_start(&app, &module_a, (PureData){0}) == PURE_OK);
const PureFault expected[] = {PURE_FAULT_DIVIDE, PURE_FAULT_NON_FINITE, PURE_FAULT_CLAMP};
for (uint32_t i = 0; i < 3; ++i) {
- CHECK(pure_app_input(&app, i, PURE_EDGE_DOWN) == PURE_INVALID && app.fault == expected[i]);
+ CHECK(press(&app, i) == PURE_INVALID && app.fault == expected[i]);
CHECK(app.values[0].num == 1 && app.values[1].num == 2 && app.events == 0);
}
/* A later handler of the same event does not run after a fault. */
@@ -376,11 +484,11 @@ static void rejections(void) {
static const I dup_key[] = {KEY("k", "Space"), KEY("j", "Space")};
static const V clash[] = {{"a", 0, 1}};
static const V v_store[] = {{"v", 0, 2}};
- static const V v_dt[] = {{"v", 2, 1}};
+ static const V v_field[] = {{"v", 2, 1}};
static const V v_empty[] = {{"v", 0, 0}};
static const H none[1];
static const O load_a[] = {{L, 0, 0}};
- static const O store[] = {{C, 0, 1}, {ST, 0, 0}, {DT, 0, 0}};
+ static const O store[] = {{C, 0, 1}, {ST, 0, 0}, {PURE_OP_FIELD, 0, 0}};
static const H h0[] = {{0, 0, 2}};
static const H h_bad_event[] = {{3, 0, 2}};
static const O back[] = {{C, 1, 1}, {JZ, 0, 0}};
@@ -409,7 +517,7 @@ static void rejections(void) {
{"duplicate key", {s1, 1, dup_key, 2, clash, 0, none, 0, load_a, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0}, PURE_INVALID},
{"duplicate name", {s1, 1, i1, 0, clash, 1, none, 0, load_a, 1, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0}, PURE_INVALID},
{"view with an effect", {s1, 1, i1, 0, v_store, 1, none, 0, store, 3, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0}, PURE_INVALID},
- {"view with an effect", {s1, 1, i1, 0, v_dt, 1, none, 0, store, 3, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0}, PURE_INVALID},
+ {"view with an effect", {s1, 1, i1, 0, v_field, 1, none, 0, store, 3, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0}, PURE_INVALID},
{"empty view", {s1, 1, i1, 0, v_empty, 1, none, 0, store, 3, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0}, PURE_INVALID},
{"handler event", {s1, 1, i1, 1, clash, 0, h_bad_event, 1, store, 3, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0}, PURE_INVALID},
{"branch not forward", {s1, 1, i1, 0, clash, 0, h_two, 1, back, 2, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0}, PURE_INVALID},
@@ -467,8 +575,8 @@ static void mutations(void) {
PureApp app;
CHECK(pure_app_start(&app, &module_a, (PureData){0}) == PURE_OK);
for (unsigned e = 0; e < 16; ++e) {
- if (e % 3 == 0) pure_app_frame(&app);
- else pure_app_input(&app, e % 4, e & 1 ? PURE_EDGE_UP : PURE_EDGE_DOWN);
+ if (e % 3 == 0) frame(&app);
+ else (e & 1 ? release(&app, e % 4) : press(&app, e % 4));
for (uint32_t v = 0; v < module_a.views; ++v) { double value; pure_app_view(&app, v, &value); }
for (uint32_t s = 0; s < module_a.states; ++s) CHECK(isfinite(app.values[s].num));
}
@@ -512,7 +620,7 @@ enum { FIELD = PURE_OP_FIELD, LEN_ = PURE_OP_LEN, REPLY = PURE_OP_REPLY, REQ = P
/* Messages: echo replies its insert; edit adds start + remove to sum, counts itself and replies its insert; guarded
* counts itself if its insert holds 8 bytes at most. A payload that does not hold the fields fails as limit, a failed
- * require changes nothing, and a key input takes no message. */
+ * require changes nothing, and a key's press, a message with no fields, is one no handler takes here. */
static void messages(void) {
static const F edit_fields[] = {{"start", 1}, {"remove", 1}, {"insert", 2}};
static const I inputs[] = {
@@ -545,7 +653,7 @@ static void messages(void) {
CHECK(pure_app_message(&app, 2, edit_payload(buffer, 0, 0, "123456789")) == PURE_LIMIT);
CHECK(app.values[0].num == 1 && app.values[1].num == 5);
CHECK(pure_app_message(&app, 2, edit_payload(buffer, 0, 0, "12345678")) == PURE_OK && app.values[0].num == 2);
- CHECK(pure_app_message(&app, 3, (PureSpan){0}) == PURE_INVALID && app.values[0].num == 2 && app.events == 3);
+ CHECK(pure_app_message(&app, 3, (PureSpan){0}) == PURE_OK && app.values[0].num == 2 && app.events == 4);
pure_app_stop(&app);
/* Verifier rules for typed operations. */
@@ -553,7 +661,7 @@ static void messages(void) {
static const H key_handler[] = {{7, 0, 2}};
Spec bad = spec;
bad.o = field_in_key; bad.no = 2; bad.h = key_handler; bad.nh = 1;
- CHECK(load(bad, &module_b) == PURE_INVALID && strstr(error, "message operation outside"));
+ CHECK(load(bad, &module_b) == PURE_INVALID && strstr(error, "no such state, field")); /* a press holds no fields */
static const O bytes_state[] = {{FIELD, 2, 0}, {ST, 0, 0}};
static const H edit_handler[] = {{3, 0, 2}};
bad.o = bytes_state; bad.no = 2; bad.h = edit_handler;
@@ -610,8 +718,8 @@ static void message_mutations(void) {
size_t reply = 0;
CHECK(pure_app_reply(&app, count_bytes, &reply) == PURE_OK && reply <= 256);
}
- } else pure_app_input(&app, input, e & 1 ? PURE_EDGE_UP : PURE_EDGE_DOWN);
- pure_app_frame(&app);
+ } else (e & 1 ? release(&app, input) : press(&app, input));
+ frame(&app);
for (uint32_t s = 0; s < module_b.states; ++s) CHECK(isfinite(app.values[s].num));
}
pure_app_stop(&app);
@@ -663,7 +771,7 @@ static double number_view(PureApp *app, uint32_t view) {
return value;
}
-enum { CUL = PURE_OP_CUR_LOAD, CS = PURE_OP_CUR_STORE, CM = PURE_OP_CUR_MOVE, TI = PURE_OP_TEXT_INPUT,
+enum { CUL = PURE_OP_CUR_LOAD, CS = PURE_OP_CUR_STORE, CM = PURE_OP_CUR_MOVE, TI = TEST_TEXT_IN,
TINS = PURE_OP_TEXT_INSERT, TREM = PURE_OP_TEXT_REMOVE, TL = PURE_OP_TEXT_LOAD, TK = PURE_OP_TEXT_CONST,
CHR = PURE_OP_CHARS, NT = PURE_OP_NUM_TEXT, CAT = PURE_OP_CONCAT, TLN = PURE_OP_TEXT_LINE,
CC = PURE_OP_CUR_COUNT, CST = PURE_OP_CUR_START };
@@ -678,7 +786,7 @@ static void texts(void) {
"\xeb\xa9\x94\xeb\xaa\xa8", "XYZ",
" \xc2\xb7 ", "\xec\x9e\x90"};
static const I inputs[] = {
- {"typed", NULL, PURE_KIND_TEXT, NULL, 0}, KEY("left", "ArrowLeft"), KEY("right", "ArrowRight"),
+ {"typed", NULL, TEXT_INPUT, NULL, 0}, KEY("left", "ArrowLeft"), KEY("right", "ArrowRight"),
KEY("up", "ArrowUp"), KEY("down", "ArrowDown"), KEY("back", "Backspace"), KEY("home", "Home"), KEY("bad", "KeyB"),
};
static const O code[] = {
@@ -709,38 +817,38 @@ static void texts(void) {
PureApp app;
CHECK(pure_app_start(&app, &module_a, (PureData){0}) == PURE_OK);
CHECK(!strcmp(state_text(&app, 1), "\xea\xb0\x80" "a\nbc") && app.values[2].num == 0);
- for (int i = 0; i < 6; ++i) CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_OK);
+ for (int i = 0; i < 6; ++i) CHECK(press(&app, 2) == PURE_OK);
CHECK(app.values[2].num == 7); /* stops at the end */
CHECK(number_view(&app, 0) == 5 && number_view(&app, 2) == 1 && !strcmp(view_text(&app, 3), "bc"));
- CHECK(pure_app_input(&app, 3, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 4); /* up keeps column 2 */
+ CHECK(press(&app, 3) == PURE_OK && app.values[2].num == 4); /* up keeps column 2 */
const char *han = "\xed\x95\x9c";
- CHECK(pure_app_text(&app, 0, (PureSpan){(const unsigned char *)han, 3}) == PURE_OK);
+ CHECK(type_in(&app, 0, (PureSpan){(const unsigned char *)han, 3}) == PURE_OK);
CHECK(!strcmp(state_text(&app, 1), "\xea\xb0\x80" "a\xed\x95\x9c\nbc") && app.values[2].num == 7 && app.values[0].num == 1);
- CHECK(pure_app_input(&app, 5, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 4); /* back over 3 bytes */
- CHECK(pure_app_input(&app, 5, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 3);
+ CHECK(press(&app, 5) == PURE_OK && app.values[2].num == 4); /* back over 3 bytes */
+ CHECK(press(&app, 5) == PURE_OK && app.values[2].num == 3);
CHECK(!strcmp(state_text(&app, 1), "\xea\xb0\x80\nbc"));
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 0);
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 0); /* stops at the start */
- CHECK(pure_app_input(&app, 5, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 0); /* nothing before */
- CHECK(pure_app_input(&app, 4, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 4); /* down to column 0 */
+ CHECK(press(&app, 1) == PURE_OK && app.values[2].num == 0);
+ CHECK(press(&app, 1) == PURE_OK && app.values[2].num == 0); /* stops at the start */
+ CHECK(press(&app, 5) == PURE_OK && app.values[2].num == 0); /* nothing before */
+ CHECK(press(&app, 4) == PURE_OK && app.values[2].num == 4); /* down to column 0 */
/* A failing event restores the text, the cursor and the states. */
- CHECK(pure_app_input(&app, 7, PURE_EDGE_DOWN) == PURE_INVALID && app.fault == PURE_FAULT_DIVIDE);
+ CHECK(press(&app, 7) == PURE_INVALID && app.fault == PURE_FAULT_DIVIDE);
CHECK(!strcmp(state_text(&app, 1), "\xea\xb0\x80\nbc") && app.values[2].num == 4 && app.values[0].num == 1);
CHECK(!strcmp(view_text(&app, 4), "\xeb\xa9\x94\xeb\xaa\xa8 \xc2\xb7 4\xec\x9e\x90"));
CHECK(!strcmp(view_text(&app, 5), "0.3333333333333333"));
- CHECK(pure_app_text(&app, 0, (PureSpan){(const unsigned char *)"\xff", 1}) == PURE_INVALID);
- CHECK(pure_app_text(&app, 0, (PureSpan){(const unsigned char *)"\xed\xa0\x80", 3}) == PURE_INVALID); /* surrogate */
- CHECK(pure_app_text(&app, 0, (PureSpan){(const unsigned char *)"\xf0\x9f\x98\x80", 4}) == PURE_OK);
+ CHECK(type_in(&app, 0, (PureSpan){(const unsigned char *)"\xff", 1}) == PURE_INVALID);
+ CHECK(type_in(&app, 0, (PureSpan){(const unsigned char *)"\xed\xa0\x80", 3}) == PURE_INVALID); /* surrogate */
+ CHECK(type_in(&app, 0, (PureSpan){(const unsigned char *)"\xf0\x9f\x98\x80", 4}) == PURE_OK);
CHECK(app.values[2].num == 8 && number_view(&app, 1) == 3); /* offset in characters */
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 4); /* back over the emoji */
- CHECK(pure_app_input(&app, 6, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 0);
+ CHECK(press(&app, 1) == PURE_OK && app.values[2].num == 4); /* back over the emoji */
+ CHECK(press(&app, 6) == PURE_OK && app.values[2].num == 0);
/* Replacement keeps the text and its cursor by name. */
static const S fresh[] = {{"fresh", 7}};
static const T kept[] = {{"note", 1, 0}, {"at", 2, 1}};
static const char *const other[] = {"ignored"};
Spec next = {fresh, 1, inputs, 0, number_views, 0, handlers, 0, code, 0, NULL, 0, kept, 2, other, 1, text_views, 0, NULL, 0, NULL, 0};
CHECK(load(next, &module_b) == PURE_OK);
- CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 3);
+ CHECK(press(&app, 2) == PURE_OK && app.values[2].num == 3);
CHECK(pure_app_replace(&app, &module_b) == PURE_OK);
CHECK(app.values[0].num == 7 && app.values[2].num == 3 && !strcmp(state_text(&app, 1), "\xea\xb0\x80\n\xf0\x9f\x98\x80" "bc"));
pure_app_stop(&app);
@@ -753,7 +861,7 @@ static void texts(void) {
CHECK(load(bad, &module_b) == PURE_INVALID && strstr(error, "cursor of another text"));
static const O input_in_frame[] = {{CUL, 3, 0}, {TI, 0, 0}, {TINS, 1, 0}};
bad.o = input_in_frame;
- CHECK(load(bad, &module_b) == PURE_INVALID && strstr(error, "text input outside"));
+ CHECK(load(bad, &module_b) == PURE_INVALID && strstr(error, "operand type")); /* a frame's field is its dt */
static const O store_text[] = {{TK, 0, 0}, {ST, 0, 0}};
static const H frame2[] = {{0, 0, 2}};
bad.o = store_text; bad.no = 2; bad.h = frame2;
@@ -798,11 +906,11 @@ static void text_mutations(void) {
if (pure_app_start(&app, &module_b, (PureData){0}) != PURE_OK) continue;
for (unsigned e = 0; e < 16; ++e) {
uint32_t input = next_random(&state) % 8;
- if (module_b.input[input].kind == PURE_KIND_TEXT) {
+ if (module_b.input[input].binding == PURE_BIND_TEXT) {
const char *word = words[next_random(&state) % LEN(words)];
- pure_app_text(&app, input, (PureSpan){(const unsigned char *)word, strlen(word)});
- } else if (module_b.input[input].kind == PURE_KIND_KEY) pure_app_input(&app, input, PURE_EDGE_DOWN);
- else pure_app_frame(&app);
+ type_in(&app, input, (PureSpan){(const unsigned char *)word, strlen(word)});
+ } else if (module_b.input[input].binding == PURE_BIND_PRESS) press(&app, input);
+ else frame(&app);
for (uint32_t v = 0; v < module_b.views; ++v) {
double value;
if (module_b.view_type[v] == PURE_VIEW_TEXT) pure_app_view_text(&app, v, ignore_span, NULL);
@@ -1990,7 +2098,7 @@ static void lists(void) {
static const R records[] = {{"task", task_fields, 2}};
static const char *const constants[] = {"", "new", "x"};
static const I inputs[] = {
- {"typed", NULL, PURE_KIND_TEXT, NULL, 0}, KEY("ins", "KeyI"), KEY("del", "Delete"),
+ {"typed", NULL, TEXT_INPUT, NULL, 0}, KEY("ins", "KeyI"), KEY("del", "Delete"),
KEY("up", "ArrowUp"), KEY("down", "ArrowDown"), KEY("toggle", "Space"), KEY("rename", "KeyR"), KEY("bad", "KeyB"),
};
static const O code[] = {
@@ -2022,34 +2130,34 @@ static void lists(void) {
CHECK(number_view(&app, 0) == 0 && number_view(&app, 1) == 0 && !pure_app_view(&app, 2, &value));
CHECK(pure_app_view_text(&app, 3, text_span, &(Text){{0}, 0}) == PURE_NOT_FOUND); /* no item yet */
CHECK(app.fault == PURE_FAULT_NONE); /* views leave faults alone */
- CHECK(pure_app_text(&app, 0, (PureSpan){(const unsigned char *)"a", 1}) == PURE_OK);
- CHECK(pure_app_text(&app, 0, (PureSpan){(const unsigned char *)"b", 1}) == PURE_OK);
+ CHECK(type_in(&app, 0, (PureSpan){(const unsigned char *)"a", 1}) == PURE_OK);
+ CHECK(type_in(&app, 0, (PureSpan){(const unsigned char *)"b", 1}) == PURE_OK);
CHECK(pure_list_count(app.lists[1]) == 2 && app.values[2].num == 1 && app.values[0].num == 2);
- CHECK(pure_app_input(&app, 3, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 0);
- CHECK(pure_app_input(&app, 3, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 0); /* stops at the first */
+ CHECK(press(&app, 3) == PURE_OK && app.values[2].num == 0);
+ CHECK(press(&app, 3) == PURE_OK && app.values[2].num == 0); /* stops at the first */
/* Inserting before the current item: the index follows it. */
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 1);
+ CHECK(press(&app, 1) == PURE_OK && app.values[2].num == 1);
CHECK(!strcmp(item_title(&app, 1, 0), "new") && !strcmp(view_text(&app, 3), "a"));
- CHECK(pure_app_input(&app, 5, PURE_EDGE_DOWN) == PURE_OK && number_view(&app, 2) == 1);
- CHECK(pure_app_input(&app, 6, PURE_EDGE_DOWN) == PURE_OK && !strcmp(view_text(&app, 3), "ax"));
- CHECK(pure_app_input(&app, 4, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 2);
- CHECK(pure_app_input(&app, 4, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 2); /* stops at the last */
+ CHECK(press(&app, 5) == PURE_OK && number_view(&app, 2) == 1);
+ CHECK(press(&app, 6) == PURE_OK && !strcmp(view_text(&app, 3), "ax"));
+ CHECK(press(&app, 4) == PURE_OK && app.values[2].num == 2);
+ CHECK(press(&app, 4) == PURE_OK && app.values[2].num == 2); /* stops at the last */
/* Removing the last item: the index moves to the new last. */
- CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 1 && pure_list_count(app.lists[1]) == 2);
- CHECK(pure_app_input(&app, 3, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 0);
+ CHECK(press(&app, 2) == PURE_OK && app.values[2].num == 1 && pure_list_count(app.lists[1]) == 2);
+ CHECK(press(&app, 3) == PURE_OK && app.values[2].num == 0);
/* Removing the current item: the index names the next. */
- CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 0 && !strcmp(view_text(&app, 3), "ax"));
+ CHECK(press(&app, 2) == PURE_OK && app.values[2].num == 0 && !strcmp(view_text(&app, 3), "ax"));
/* A failing event undoes a set, a remove and an append. */
- CHECK(pure_app_input(&app, 7, PURE_EDGE_DOWN) == PURE_INVALID && app.fault == PURE_FAULT_DIVIDE);
+ CHECK(press(&app, 7) == PURE_INVALID && app.fault == PURE_FAULT_DIVIDE);
CHECK(pure_list_count(app.lists[1]) == 1 && !strcmp(item_title(&app, 1, 0), "ax") &&
pure_list_get(app.lists[1], 0, 1).num == 1 && app.values[2].num == 0 && app.values[0].num == 2);
- CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_OK && pure_list_count(app.lists[1]) == 0 && app.values[2].num == 0);
- CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_NOT_FOUND && app.fault == PURE_FAULT_NOT_FOUND);
- CHECK(pure_app_input(&app, 5, PURE_EDGE_DOWN) == PURE_NOT_FOUND);
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && pure_list_count(app.lists[1]) == 1 && app.values[2].num == 0);
- for (int i = 0; i < 3; ++i) CHECK(pure_app_text(&app, 0, (PureSpan){(const unsigned char *)"\xea\xb0\x80", 3}) == PURE_OK);
- CHECK(pure_app_input(&app, 3, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 2);
- CHECK(pure_app_input(&app, 5, PURE_EDGE_DOWN) == PURE_OK);
+ CHECK(press(&app, 2) == PURE_OK && pure_list_count(app.lists[1]) == 0 && app.values[2].num == 0);
+ CHECK(press(&app, 2) == PURE_NOT_FOUND && app.fault == PURE_FAULT_NOT_FOUND);
+ CHECK(press(&app, 5) == PURE_NOT_FOUND);
+ CHECK(press(&app, 1) == PURE_OK && pure_list_count(app.lists[1]) == 1 && app.values[2].num == 0);
+ for (int i = 0; i < 3; ++i) CHECK(type_in(&app, 0, (PureSpan){(const unsigned char *)"\xea\xb0\x80", 3}) == PURE_OK);
+ CHECK(press(&app, 3) == PURE_OK && app.values[2].num == 2);
+ CHECK(press(&app, 5) == PURE_OK);
/* Replacement keeps the list by name. The same record keeps the list
* itself; a changed record keeps each field by name and kind and gives new
@@ -2146,11 +2254,11 @@ static void list_mutations(void) {
if (pure_app_start(&app, &module_b, (PureData){0}) != PURE_OK) continue;
for (unsigned e = 0; e < 24; ++e) {
uint32_t input = next_random(&state) % 8;
- if (module_b.input[input].kind == PURE_KIND_TEXT) {
+ if (module_b.input[input].binding == PURE_BIND_TEXT) {
const char *word = words[next_random(&state) % LEN(words)];
- pure_app_text(&app, input, (PureSpan){(const unsigned char *)word, strlen(word)});
- } else if (module_b.input[input].kind == PURE_KIND_KEY) pure_app_input(&app, input, PURE_EDGE_DOWN);
- else pure_app_frame(&app);
+ type_in(&app, input, (PureSpan){(const unsigned char *)word, strlen(word)});
+ } else if (module_b.input[input].binding == PURE_BIND_PRESS) press(&app, input);
+ else frame(&app);
for (uint32_t v = 0; v < module_b.views; ++v) {
double value;
if (module_b.view_type[v] == PURE_VIEW_TEXT) pure_app_view_text(&app, v, ignore_span, NULL);
@@ -2198,7 +2306,7 @@ static void tables(void) {
static const RF task_fields[] = {{"title", 1, 0}, {"done", 2, 0}};
static const R records[] = {{"task", task_fields, 2}};
static const char *const constants[] = {"", "x", " ", "empty"};
- static const I inputs[] = {{"typed", NULL, PURE_KIND_TEXT, NULL, 0}, KEY("toggle", "Space")};
+ static const I inputs[] = {{"typed", NULL, TEXT_INPUT, NULL, 0}, KEY("toggle", "Space")};
static const O code[] = {
{TI, 0, 0}, {C, 1, 0}, {LA, 1, 0}, {XE, 1, 0}, {XS, 2, 0}, /* 0 typed */
{XL, 2, 0}, {XL, 2, 0}, {IG, DONE, 0}, {NOT, 0, 0}, {IS, DONE, 0}, /* 5 toggle */
@@ -2224,9 +2332,9 @@ static void tables(void) {
double value;
CHECK(pure_app_rows(&app, 1) == 0 && !strcmp(view_text(&app, 0), "empty")); /* the else branch */
CHECK(pure_app_cell_text(&app, 2, 0, text_span, &(Text){{0}, 0}) == PURE_NOT_FOUND);
- CHECK(pure_app_text(&app, 0, (PureSpan){(const unsigned char *)"a", 1}) == PURE_OK);
- CHECK(pure_app_text(&app, 0, (PureSpan){(const unsigned char *)"\xeb\xb9\xb5", 3}) == PURE_OK);
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK);
+ CHECK(type_in(&app, 0, (PureSpan){(const unsigned char *)"a", 1}) == PURE_OK);
+ CHECK(type_in(&app, 0, (PureSpan){(const unsigned char *)"\xeb\xb9\xb5", 3}) == PURE_OK);
+ CHECK(press(&app, 1) == PURE_OK);
CHECK(pure_app_rows(&app, 1) == 2 && !strcmp(view_text(&app, 0), "\xeb\xb9\xb5"));
CHECK(!strcmp(cell_text(&app, 2, 0), "a") && !strcmp(cell_text(&app, 2, 1), "\xeb\xb9\xb5"));
CHECK(!strcmp(cell_text(&app, 3, 0), " ") && !strcmp(cell_text(&app, 3, 1), "x"));
@@ -2334,8 +2442,8 @@ static void table_mutations(void) {
if (pure_app_start(&app, &module_b, (PureData){0}) != PURE_OK) continue;
for (unsigned e = 0; e < 12; ++e) {
uint32_t input = next_random(&state) % 2;
- if (module_b.input[input].kind == PURE_KIND_TEXT) pure_app_text(&app, input, (PureSpan){(const unsigned char *)"t", 1});
- else if (module_b.input[input].kind == PURE_KIND_KEY) pure_app_input(&app, input, PURE_EDGE_DOWN);
+ if (module_b.input[input].binding == PURE_BIND_TEXT) type_in(&app, input, (PureSpan){(const unsigned char *)"t", 1});
+ else if (module_b.input[input].binding == PURE_BIND_PRESS) press(&app, input);
for (uint32_t v = 0; v < module_b.views; ++v) {
double value;
if (!module_b.view_table[v]) {
@@ -2419,14 +2527,14 @@ static void stored_equivalence(Spec spec, const char *const *words, size_t word_
for (unsigned step = 0; step < 400; ++step) {
uint32_t input = next_random(&seed) % module_a.inputs;
PureResult a, b;
- if (module_a.input[input].kind == PURE_KIND_TEXT) {
+ if (module_a.input[input].binding == PURE_BIND_TEXT) {
const char *word = words[next_random(&seed) % word_count];
PureSpan text = {(const unsigned char *)word, strlen(word)};
- a = pure_app_text(&memory, input, text);
- b = pure_app_text(&stored, input, text);
+ a = type_in(&memory, input, text);
+ b = type_in(&stored, input, text);
} else {
- a = pure_app_input(&memory, input, PURE_EDGE_DOWN);
- b = pure_app_input(&stored, input, PURE_EDGE_DOWN);
+ a = press(&memory, input);
+ b = press(&stored, input);
}
CHECK(a == b);
same_states(&memory, &stored);
@@ -2475,7 +2583,7 @@ static void stored_crashes(void) {
CHECK(load(spec, &module_a) == PURE_OK);
PureApp app;
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK);
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK && consistent(&app) == 1);
+ CHECK(press(&app, 0) == PURE_OK && consistent(&app) == 1);
pure_app_stop(&app);
unsigned expected = 1;
for (int stage = PURE_AFTER_HEADER; stage <= PURE_BEFORE_DIR_SYNC; ++stage) {
@@ -2486,7 +2594,7 @@ static void stored_crashes(void) {
if (pure_app_start(&doomed, &module_a, pure_tree_data(path)) != PURE_OK) _exit(1);
crash = 1;
fail_stage = stage;
- pure_app_input(&doomed, 0, PURE_EDGE_DOWN);
+ press(&doomed, 0);
_exit(0);
}
int status;
@@ -2506,7 +2614,7 @@ static void stored_crashes(void) {
if (pure_app_start(&doomed, &module_a, pure_tree_data(path)) != PURE_OK) _exit(1);
crash = 1;
fail_stage = stage;
- pure_app_input(&doomed, 0, PURE_EDGE_DOWN);
+ press(&doomed, 0);
_exit(0);
}
int status;
@@ -2519,28 +2627,28 @@ static void stored_crashes(void) {
* until it starts again, from what the store has. */
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK);
for (fail_stage = PURE_AFTER_HEADER; fail_stage <= PURE_BEFORE_RENAME; ++fail_stage) {
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_STORE_ERROR && consistent(&app) == expected);
+ CHECK(press(&app, 0) == PURE_STORE_ERROR && consistent(&app) == expected);
}
fail_stage = PURE_BEFORE_DIR_SYNC;
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_UNCERTAIN && consistent(&app) == expected);
+ CHECK(press(&app, 0) == PURE_UNCERTAIN && consistent(&app) == expected);
fail_stage = -1;
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_CONFLICT && consistent(&app) == expected);
+ CHECK(press(&app, 0) == PURE_CONFLICT && consistent(&app) == expected);
pure_app_stop(&app);
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && consistent(&app) == expected + 1);
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK && consistent(&app) == expected + 2);
+ CHECK(press(&app, 0) == PURE_OK && consistent(&app) == expected + 2);
/* The log refusing its entry fails the save and changes nothing; the log
* failing its sync leaves it uncertain: no save follows until the app
* starts again, and then the entry does not count (the tree has not its
* number). */
fail_stage = PURE_LOG_BEFORE_WRITE;
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_STORE_ERROR && consistent(&app) == expected + 2);
+ CHECK(press(&app, 0) == PURE_STORE_ERROR && consistent(&app) == expected + 2);
fail_stage = PURE_LOG_BEFORE_SYNC;
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_UNCERTAIN && consistent(&app) == expected + 2);
+ CHECK(press(&app, 0) == PURE_UNCERTAIN && consistent(&app) == expected + 2);
fail_stage = -1;
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_CONFLICT);
+ CHECK(press(&app, 0) == PURE_CONFLICT);
pure_app_stop(&app);
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && consistent(&app) == expected + 2);
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK && consistent(&app) == expected + 3);
+ CHECK(press(&app, 0) == PURE_OK && consistent(&app) == expected + 3);
pure_app_stop(&app);
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && consistent(&app) == expected + 3);
pure_app_stop(&app);
@@ -2569,13 +2677,13 @@ static void uncertain_numbers(void) {
PureApp app;
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK);
fail_stage = PURE_BEFORE_DIR_SYNC;
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_UNCERTAIN && app.uncertain && app.values[0].num == 0);
+ CHECK(press(&app, 0) == PURE_UNCERTAIN && app.uncertain && app.values[0].num == 0);
fail_stage = -1;
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_CONFLICT && !pure_list_count(app.lists[1]));
+ CHECK(press(&app, 1) == PURE_CONFLICT && !pure_list_count(app.lists[1]));
pure_app_stop(&app);
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[0].num == 1 &&
!pure_list_count(app.lists[1]));
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && pure_list_get(app.lists[1], 0, 0).num == 1);
+ CHECK(press(&app, 1) == PURE_OK && pure_list_get(app.lists[1], 0, 0).num == 1);
pure_app_stop(&app);
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[0].num == 1 &&
pure_list_count(app.lists[1]) == 1 && pure_list_get(app.lists[1], 0, 0).num == 1);
@@ -2583,8 +2691,8 @@ static void uncertain_numbers(void) {
remove_store(path);
}
-/* Frame changes are stored by the next event or by a save; a stop alone
- * leaves them out. */
+/* Frames a host defers (027's 4d: a frame is a message, saved as each is unless deferred) are stored by a save or by
+ * the next event it does not defer; a stop alone leaves them out. */
static void stored_frames(void) {
char *path = new_store();
static const O code[] = {{L, 0, 0}, {C, 0, 1}, {ADD, 0, 0}, {ST, 0, 0}};
@@ -2593,16 +2701,18 @@ static void stored_frames(void) {
NULL, 0, NULL, 0};
CHECK(load(spec, &module_a) == PURE_OK);
PureApp app;
- CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK);
- for (int i = 0; i < 5; ++i) CHECK(pure_app_frame(&app) == PURE_OK);
+ CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && pure_app_defer(&app, true));
+ for (int i = 0; i < 5; ++i) CHECK(frame(&app) == PURE_OK);
pure_app_stop(&app);
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[0].num == 0);
- for (int i = 0; i < 3; ++i) CHECK(pure_app_frame(&app) == PURE_OK);
+ CHECK(pure_app_defer(&app, true));
+ for (int i = 0; i < 3; ++i) CHECK(frame(&app) == PURE_OK);
CHECK(pure_app_save(&app) == PURE_OK);
pure_app_stop(&app);
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[0].num == 3);
- for (int i = 0; i < 2; ++i) CHECK(pure_app_frame(&app) == PURE_OK);
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK);
+ CHECK(pure_app_defer(&app, true));
+ for (int i = 0; i < 2; ++i) CHECK(frame(&app) == PURE_OK);
+ CHECK(pure_app_defer(&app, false) && press(&app, 0) == PURE_OK);
pure_app_stop(&app);
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[0].num == 5);
pure_app_stop(&app);
@@ -2641,7 +2751,7 @@ static void stored_migration(void) {
CHECK(load(before, &module_a) == PURE_OK && load(after, &module_b) == PURE_OK);
PureApp app;
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK);
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK && pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK);
+ CHECK(press(&app, 0) == PURE_OK && press(&app, 0) == PURE_OK);
pure_app_stop(&app);
for (int round = 0; round < 2; ++round) { /* migrated, then stored as migrated */
CHECK(pure_app_start(&app, &module_b, pure_tree_data(path)) == PURE_OK && pure_list_count(app.lists[TASKS]) == 2);
@@ -2653,7 +2763,7 @@ static void stored_migration(void) {
}
/* Back to the first record by replacing the module; the next save stores it. */
CHECK(pure_app_start(&app, &module_b, pure_tree_data(path)) == PURE_OK);
- CHECK(pure_app_replace(&app, &module_a) == PURE_OK && pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK);
+ CHECK(pure_app_replace(&app, &module_a) == PURE_OK && press(&app, 0) == PURE_OK);
pure_app_stop(&app);
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && consistent(&app) == 3);
pure_app_stop(&app);
@@ -2749,7 +2859,7 @@ static void transient_states(void) {
char *path = new_store();
PureApp app;
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK);
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK && pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK);
+ CHECK(press(&app, 0) == PURE_OK && press(&app, 0) == PURE_OK);
CHECK(app.values[N].num == 2 && app.values[T_].num == 2 && pure_list_count(app.lists[LOG]) == 2 &&
pure_list_count(app.lists[KEEP]) == 2 && app.values[I_].num == 1 && app.values[PICK].num == 1 &&
pure_draft_size(app.texts[NOTE_]) == 3);
@@ -2762,11 +2872,11 @@ static void transient_states(void) {
pure_app_stop(&reader);
pure_snapshot_release(snapshot);
/* A failed event undoes transient states too. */
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) != PURE_OK && app.values[T_].num == 2 &&
+ CHECK(press(&app, 1) != PURE_OK && app.values[T_].num == 2 &&
pure_list_count(app.lists[LOG]) == 2 && pure_draft_size(app.texts[NOTE_]) == 3);
/* Changing only transient states stores nothing. */
uint64_t seq = app.seq;
- for (int k = 0; k < 3; ++k) CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_OK);
+ for (int k = 0; k < 3; ++k) CHECK(press(&app, 2) == PURE_OK);
CHECK(app.seq == seq && app.values[T_].num == 5 && pure_list_count(app.lists[LOG]) == 5);
pure_app_stop(&app);
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK);
@@ -2774,10 +2884,10 @@ static void transient_states(void) {
!pure_list_count(app.lists[LOG]) && app.values[I_].num == 0 && app.values[PICK].num == 0 &&
pure_draft_size(app.texts[NOTE_]) == 1);
/* Into b: t, note and log are stored from now on, keep is not. */
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK && pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK);
+ CHECK(press(&app, 0) == PURE_OK && press(&app, 0) == PURE_OK);
CHECK(pure_app_replace(&app, &module_b) == PURE_OK && app.values[T_].num == 2 && pure_list_count(app.lists[LOG]) == 2 &&
pure_list_count(app.lists[KEEP]) == 4 && pure_draft_size(app.texts[NOTE_]) == 3);
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK);
+ CHECK(press(&app, 0) == PURE_OK);
pure_app_stop(&app);
CHECK(pure_app_start(&app, &module_b, pure_tree_data(path)) == PURE_OK);
CHECK(app.values[N].num == 5 && app.values[T_].num == 3 && pure_list_count(app.lists[LOG]) == 3 &&
@@ -2812,8 +2922,8 @@ static void stored_states(void) {
typedef struct { uint32_t input; const char *word; } Step;
static PureResult apply_step(PureApp *app, Step step) {
- if (step.word) return pure_app_text(app, step.input, (PureSpan){(const unsigned char *)step.word, strlen(step.word)});
- return pure_app_input(app, step.input, PURE_EDGE_DOWN);
+ if (step.word) return type_in(app, step.input, (PureSpan){(const unsigned char *)step.word, strlen(step.word)});
+ return press(app, step.input);
}
/* Deferred events apply at once and last only through a save: an app that
@@ -2831,7 +2941,7 @@ static void deferred_equivalence(Spec spec, const char *const *words, size_t wor
unsigned lost = 0, saves = 0;
for (unsigned step = 0; step < 600; ++step) {
Step next = {next_random(&seed) % module_a.inputs, NULL};
- if (module_a.input[next.input].kind == PURE_KIND_TEXT) next.word = words[next_random(&seed) % word_count];
+ if (module_a.input[next.input].binding == PURE_BIND_TEXT) next.word = words[next_random(&seed) % word_count];
PureResult a = apply_step(&memory, next), b = apply_step(&stored, next);
CHECK(a == b);
if (a == PURE_OK) history[applied++] = next;
@@ -2869,11 +2979,11 @@ static void deferred_bursts(void) {
CHECK(load(spec, &module_a) == PURE_OK);
PureApp app;
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && pure_app_defer(&app, true));
- for (int i = 0; i < 5; ++i) CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK);
+ for (int i = 0; i < 5; ++i) CHECK(press(&app, 0) == PURE_OK);
CHECK(consistent(&app) == 5 && app.events == 5);
for (fail_stage = PURE_AFTER_HEADER; fail_stage <= PURE_BEFORE_RENAME; fail_stage += 2) {
CHECK(pure_app_save(&app) == PURE_STORE_ERROR && consistent(&app) == 0 && app.events == 0);
- for (int i = 0; i < 5; ++i) CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK);
+ for (int i = 0; i < 5; ++i) CHECK(press(&app, 0) == PURE_OK);
}
fail_stage = -1;
CHECK(pure_app_save(&app) == PURE_OK && consistent(&app) == 5);
@@ -2887,7 +2997,7 @@ static void deferred_bursts(void) {
if (!child) {
PureApp doomed;
if (pure_app_start(&doomed, &module_a, pure_tree_data(path)) != PURE_OK || !pure_app_defer(&doomed, true)) _exit(1);
- for (int i = 0; i < 7; ++i) if (pure_app_input(&doomed, 0, PURE_EDGE_DOWN) != PURE_OK) _exit(2);
+ for (int i = 0; i < 7; ++i) if (press(&doomed, 0) != PURE_OK) _exit(2);
crash = 1;
fail_stage = stage;
pure_app_save(&doomed);
@@ -2916,7 +3026,7 @@ static void marks(void) {
enum { MARK = 2 | PURE_CURSOR_STAYS << 8 };
static const T textual[] = {{"note", 1, 0}, {"at", 2, 0}, {"mark", MARK, 0}};
static const char *const constants[] = {"ab"};
- static const I inputs[] = {{"typed", NULL, PURE_KIND_TEXT, NULL, 0}, KEY("keep", "KeyK"), KEY("back", "Backspace")};
+ static const I inputs[] = {{"typed", NULL, TEXT_INPUT, NULL, 0}, KEY("keep", "KeyK"), KEY("back", "Backspace")};
static const O code[] = {
{CUL, 1, 0}, {TI, 0, 0}, {TINS, 0, 0}, /* 0 typed: at the cursor */
{CUL, 1, 0}, {CS, 2, 0}, /* 3 keep: mark = at */
@@ -2929,13 +3039,13 @@ static void marks(void) {
CHECK(module_a.state_flags[2] == PURE_CURSOR_STAYS && !module_a.state_flags[1]);
PureApp app;
CHECK(pure_app_start(&app, &module_a, (PureData){0}) == PURE_OK);
- CHECK(pure_app_text(&app, 0, (PureSpan){(const unsigned char *)"x", 1}) == PURE_OK);
+ CHECK(type_in(&app, 0, (PureSpan){(const unsigned char *)"x", 1}) == PURE_OK);
CHECK(!strcmp(state_text(&app, 0), "xab") && app.values[1].num == 1 && app.values[2].num == 0); /* the mark stayed */
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[2].num == 1);
- CHECK(pure_app_text(&app, 0, (PureSpan){(const unsigned char *)"yz", 2}) == PURE_OK);
+ CHECK(press(&app, 1) == PURE_OK && app.values[2].num == 1);
+ CHECK(type_in(&app, 0, (PureSpan){(const unsigned char *)"yz", 2}) == PURE_OK);
CHECK(!strcmp(state_text(&app, 0), "xyzab") && app.values[1].num == 3 && app.values[2].num == 1);
- CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_OK && app.values[1].num == 2 && app.values[2].num == 1);
- CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_OK && app.values[1].num == 1 && app.values[2].num == 1); /* gathered */
+ CHECK(press(&app, 2) == PURE_OK && app.values[1].num == 2 && app.values[2].num == 1);
+ CHECK(press(&app, 2) == PURE_OK && app.values[1].num == 1 && app.values[2].num == 1); /* gathered */
pure_app_stop(&app);
/* A flag only on a cursor, and only this one. */
static const T wrong_flag[] = {{"note", 1, 0}, {"at", 2 | 2 << 8, 0}, {"mark", MARK, 0}};
@@ -2955,7 +3065,7 @@ static const RF fold_fields[] = {{"title", 1, 0}, {"done", 2, 0}, {"points", 0,
static const R fold_records[] = {{"task", fold_fields, 3}};
static const T fold_states[] = {{"tasks", 3, 0}, {"at", 4, 0}};
static const char *const fold_constants[] = {""};
-static const I fold_inputs[] = {{"typed", NULL, PURE_KIND_TEXT, NULL, 0}, KEY("toggle", "KeyT")};
+static const I fold_inputs[] = {{"typed", NULL, TEXT_INPUT, NULL, 0}, KEY("toggle", "KeyT")};
enum { F_TITLE = 0, F_DONE = 1u << 16, F_POINTS = 2u << 16 };
static const O fold_code[] = {
{TI, 0, 0}, {C, 1, 0}, {TI, 0, 0}, {CHR, 0, 0}, {LA, 0, 0}, {XE, 0, 0}, {XS, 1, 0}, /* 0 typed */
@@ -2990,15 +3100,15 @@ static void folds(void) {
CHECK(!pure_app_view(&app, 6, &value)); /* no first open task yet */
const char *const words[] = {"a", "bbb", "\xea\xb0\x80\xea\xb0\x80"}; /* one, three and two characters */
for (size_t i = 0; i < 3; ++i)
- CHECK(pure_app_text(&app, 0, (PureSpan){(const unsigned char *)words[i], strlen(words[i])}) == PURE_OK);
+ CHECK(type_in(&app, 0, (PureSpan){(const unsigned char *)words[i], strlen(words[i])}) == PURE_OK);
CHECK(number_view(&app, 0) == 3 && number_view(&app, 1) == 6 && number_view(&app, 2) == 3);
CHECK(number_view(&app, 3) == 0 && number_view(&app, 4) == 1 && number_view(&app, 5) == 13);
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK); /* the last task is done */
+ CHECK(press(&app, 1) == PURE_OK); /* the last task is done */
CHECK(number_view(&app, 0) == 2 && number_view(&app, 3) == 2 && number_view(&app, 4) == 1);
CHECK(number_view(&app, 6) == 0); /* the first open one: an index */
- CHECK(pure_app_point(&app, 1, 0) == PURE_OK && pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK);
+ CHECK(place(&app, 1, 0) == PURE_OK && press(&app, 1) == PURE_OK);
CHECK(number_view(&app, 6) == 1);
- CHECK(pure_app_point(&app, 1, 1) == PURE_OK && pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK);
+ CHECK(place(&app, 1, 1) == PURE_OK && press(&app, 1) == PURE_OK);
CHECK(!pure_app_view(&app, 6, &value));
CHECK(number_view(&app, 0) == 0 && number_view(&app, 3) == 6 && !pure_app_view(&app, 4, &value)); /* none open */
pure_app_stop(&app);
@@ -3073,16 +3183,16 @@ static void filters(void) {
size_t at;
CHECK(pure_app_start(&app, &module_a, (PureData){0}) == PURE_OK && pure_app_rows(&app, 0) == 0);
for (const char *word = "abc"; *word; ++word)
- CHECK(pure_app_text(&app, 0, (PureSpan){(const unsigned char *)word, 1}) == PURE_OK);
+ CHECK(type_in(&app, 0, (PureSpan){(const unsigned char *)word, 1}) == PURE_OK);
CHECK(pure_app_rows(&app, 0) == 3 && !strcmp(cell_title(&app, 1, 2), "c") && pure_app_cell(&app, 2, 2, &value) && value == 2);
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK); /* c is done: it leaves */
+ CHECK(press(&app, 1) == PURE_OK); /* c is done: it leaves */
CHECK(pure_app_rows(&app, 0) == 2 && !pure_app_cell(&app, 2, 2, &value) && !pure_app_item_row(&app, 0, 2, &at));
- CHECK(pure_app_point(&app, 1, 0) == PURE_OK && pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK); /* a too */
+ CHECK(place(&app, 1, 0) == PURE_OK && press(&app, 1) == PURE_OK); /* a too */
CHECK(pure_app_rows(&app, 0) == 1 && !strcmp(cell_title(&app, 1, 0), "b"));
CHECK(pure_app_cell(&app, 2, 0, &value) && value == 1); /* the item's position, not the row's */
CHECK(pure_app_row_item(&app, 0, 0, &at) && at == 1 && !pure_app_row_item(&app, 0, 1, &at));
CHECK(pure_app_item_row(&app, 0, 1, &at) && at == 0 && !pure_app_item_row(&app, 0, 0, &at));
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK); /* a comes back, before b */
+ CHECK(press(&app, 1) == PURE_OK); /* a comes back, before b */
CHECK(pure_app_rows(&app, 0) == 2 && !strcmp(cell_title(&app, 1, 0), "a") && pure_app_item_row(&app, 0, 1, &at) && at == 1);
CHECK(pure_app_replace(&app, &module_a) == PURE_OK && pure_app_rows(&app, 0) == 2);
pure_app_stop(&app);
@@ -3402,7 +3512,7 @@ static void lane_folds(void) {
}
}
passes += pure_app_passes(&a);
- PureResult went_a = pure_app_input(&a, 0, PURE_EDGE_DOWN), went_b = pure_app_input(&b, 0, PURE_EDGE_DOWN);
+ PureResult went_a = press(&a, 0), went_b = press(&b, 0);
if (went_a != went_b || a.fault != b.fault || a.fault_pc != b.fault_pc || a.work != b.work ||
memcmp(a.values, b.values, 4 * sizeof(double))) {
fprintf(stderr, "lane round %u handler: %d %d %u / %d %d %u\n", round, went_a, a.fault, a.fault_pc, went_b, b.fault,
@@ -3546,7 +3656,7 @@ static void walks(void) {
++orders;
}
} else { /* clean */
- result = pure_app_input(&app, 1, PURE_EDGE_DOWN);
+ result = press(&app, 1);
size_t at = 0;
while (at < shop.stocked) {
bool gone = false;
@@ -3566,7 +3676,7 @@ static void walks(void) {
if (!gone) ++at;
}
++cleaned;
- pure_app_input(&app, 1, PURE_EDGE_UP);
+ release(&app, 1);
}
if (!ok) {
shop = before;
@@ -3655,8 +3765,8 @@ static void walk_mutations(void) {
put_row(payload + k, (double)(next_random(&state) % 8), (double)(next_random(&state) % 3));
if (module_b.input[input].kind == PURE_KIND_MESSAGE)
pure_app_message(&app, input, (PureSpan){payload, length});
- else if (module_b.input[input].kind == PURE_KIND_KEY)
- pure_app_input(&app, input, PURE_EDGE_DOWN);
+ else if (module_b.input[input].binding == PURE_BIND_PRESS)
+ press(&app, input);
for (uint32_t s_ = 0; s_ < module_b.states; ++s_)
if (module_b.state_type[s_] == PURE_STATE_LIST && module_b.state_flags[s_] & PURE_LIST_ROWS >> 8)
CHECK(!pure_list_count(app.lists[s_])); /* rows go with their event */
@@ -3840,7 +3950,7 @@ static void locals(void) {
items[count] = (double)(next_random(&seed) % 20) - 5;
put_double(payload, items[count++]);
CHECK(pure_app_message(&app, 1, (PureSpan){payload, 8}) == PURE_OK);
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK);
+ CHECK(press(&app, 0) == 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;
@@ -4003,7 +4113,7 @@ static void ranges(void) {
CHECK(pure_app_cell(&a, 7, row, &x) && pure_app_cell(&b, 7, row, &y) && x == y);
}
unsigned input = 1 + next_random(&seed) % 3;
- PureResult went_a = pure_app_input(&a, input, PURE_EDGE_DOWN), went_b = pure_app_input(&b, input, PURE_EDGE_DOWN);
+ PureResult went_a = press(&a, input), went_b = press(&b, input);
CHECK(went_a == went_b && same_lists(&a, &b) && !memcmp(a.values, b.values, 3 * sizeof(double)));
walked += input == 1;
}
@@ -4031,8 +4141,8 @@ static void fold_mutations(void) {
if (pure_app_start(&app, &module_b, (PureData){0}) != PURE_OK) continue;
for (unsigned e = 0; e < 16; ++e) {
uint32_t input = next_random(&state) % 2;
- if (module_b.input[input].kind == PURE_KIND_TEXT) pure_app_text(&app, input, (PureSpan){(const unsigned char *)"ab", 2});
- else if (module_b.input[input].kind == PURE_KIND_KEY) pure_app_input(&app, input, PURE_EDGE_DOWN);
+ if (module_b.input[input].binding == PURE_BIND_TEXT) type_in(&app, input, (PureSpan){(const unsigned char *)"ab", 2});
+ else if (module_b.input[input].binding == PURE_BIND_PRESS) press(&app, input);
for (uint32_t v = 0; v < module_b.views; ++v) {
double value;
size_t item;
@@ -4053,33 +4163,53 @@ static void fold_mutations(void) {
}
}
-/* A host points at an item: the index a view shows as its position moves
- * there, as an event with no handler, saved like a key event; deferred, it
- * joins the burst and a failed save undoes it with the rest. */
+/* A host points at a row (027's 4d): a message bound to the table (one u32 field, the row's item), whose handler moves
+ * the index there; saved like any other event, and deferred, it joins the burst. A pointing names a table. */
static void pointing(void) {
- CHECK(load(list_spec, &module_a) == PURE_OK);
- CHECK(pure_module_position(&module_a, 1) == 2); /* position = at.position */
- CHECK(pure_module_position(&module_a, 0) == -1 && pure_module_position(&module_a, 2) == -1 &&
- pure_module_position(&module_a, 3) == -1 && pure_module_position(&module_a, 99) == -1);
+ enum { XAT_ = PURE_OP_INDEX_AT };
+ static const S nums[] = {{"made", 0}};
+ static const T states[] = {{"tasks", 3, 0}, {"at", 4, 1}};
+ static const RF task_fields[] = {{"title", 1, 0}};
+ static const R records[] = {{"task", task_fields, 1}};
+ static const char *const constants[] = {""};
+ static const I inputs[] = {{"typed", NULL, TEXT_INPUT, NULL, 0}, {"rows.point", "1", POINT_INPUT, NULL, 0}};
+ static const O code[] = {
+ {TI, 0, 0}, {LA, 1, 0}, {XE, 1, 0}, {XS, 2, 0}, /* 0 typed: a task, at it */
+ {FIELD, 0, 0}, {XAT_, 1, 0}, {XS, 2, 0}, /* 4 rows.point: at the row's item */
+ {XL, 2, 0}, {XN, 1, 0}, /* 7 view position */
+ {ROW, 1, 0}, {IG, 1, 0}, /* 9 column title */
+ };
+ static const H handlers[] = {{1, 0, 4}, {3, 4, 3}};
+ static const V views[] = {{"position", 7, 2}};
+ static const W tables[] = {{"rows", 2, 0, 1, 0}, {"title", 1, 2, 9, 2}};
+ Spec spec = {nums, 1, inputs, LEN(inputs), views, 1, handlers, LEN(handlers), code, LEN(code), NULL, 0, states, 2,
+ constants, 1, NULL, 0, records, 1, tables, 2};
+ if (load(spec, &module_a) != PURE_OK) { fprintf(stderr, "pointing: %s\n", error); abort(); }
char path[] = "/tmp/pure-point-XXXXXX";
CHECK(mkdtemp(path) && pure_store_init(path) == PURE_OK);
PureApp app;
+ unsigned char row[4];
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK);
- CHECK(pure_app_point(&app, 2, 0) == PURE_NOT_FOUND); /* nothing to point at */
- for (int i = 0; i < 3; ++i) CHECK(pure_app_text(&app, 0, (PureSpan){(const unsigned char *)"a", 1}) == PURE_OK);
+ CHECK(pure_app_message(&app, 1, (PureSpan){put32(row, 0) - 4, 4}) == PURE_NOT_FOUND); /* nothing to point at */
+ for (int i = 0; i < 3; ++i) CHECK(type_in(&app, 0, (PureSpan){(const unsigned char *)"a", 1}) == PURE_OK);
uint64_t events = app.events;
- CHECK(app.values[2].num == 2 && pure_app_point(&app, 2, 0) == PURE_OK && app.values[2].num == 0 && app.events == events + 1);
- CHECK(pure_app_point(&app, 2, 3) == PURE_NOT_FOUND && app.values[2].num == 0);
- CHECK(pure_app_point(&app, 0, 1) == PURE_INVALID && pure_app_point(&app, 1, 1) == PURE_INVALID); /* a number, a list */
- CHECK(pure_app_point(&app, 2, 1) == PURE_OK);
+ CHECK(app.values[2].num == 2 && pure_app_message(&app, 1, (PureSpan){put32(row, 0) - 4, 4}) == PURE_OK);
+ CHECK(app.values[2].num == 0 && app.events == events + 1);
+ CHECK(pure_app_message(&app, 1, (PureSpan){put32(row, 3) - 4, 4}) == PURE_NOT_FOUND && app.values[2].num == 0);
+ CHECK(pure_app_message(&app, 1, (PureSpan){put32(row, 1) - 4, 4}) == PURE_OK);
pure_app_stop(&app);
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[2].num == 1);
- CHECK(pure_app_defer(&app, true) && pure_app_point(&app, 2, 2) == PURE_OK && app.open && app.values[2].num == 2);
+ CHECK(pure_app_defer(&app, true) && pure_app_message(&app, 1, (PureSpan){put32(row, 2) - 4, 4}) == PURE_OK && app.open &&
+ app.values[2].num == 2);
CHECK(pure_app_save(&app) == PURE_OK);
pure_app_stop(&app);
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && app.values[2].num == 2);
pure_app_stop(&app);
remove_store(path);
+ static const W columns_only[] = {{"rows", 1, 0, 9, 2}}; /* no table: a pointing has none to point at */
+ Spec bad = spec;
+ bad.w = columns_only, bad.nw = 1;
+ CHECK(load(bad, &module_b) != PURE_OK && strstr(error, "pointing's table"));
}
/* Keyed lists: items in key order (texts by bytes, numbers by value), each
@@ -4095,9 +4225,9 @@ static const RF score_fields[] = {{"score", 0, 0}}, flag_fields[] = {{"on", 2, 0
static const R keyed_records[] = {{"person", person_fields, 2}, {"score", score_fields, 1}, {"flag", flag_fields, 1}};
static const char *const keyed_constants[] = {"", "bob", "m"};
static const I keyed_inputs[] = {
- {"typed", NULL, PURE_KIND_TEXT, NULL, 0}, {"seek", NULL, PURE_KIND_TEXT, NULL, 0},
- {"has", NULL, PURE_KIND_TEXT, NULL, 0}, KEY("drop", "Delete"), KEY("bump", "KeyB"),
- KEY("two", "KeyN"), KEY("zero", "KeyO"), KEY("minus_zero", "KeyM"), {"twice", NULL, PURE_KIND_TEXT, NULL, 0},
+ {"typed", NULL, TEXT_INPUT, NULL, 0}, {"seek", NULL, TEXT_INPUT, NULL, 0},
+ {"has", NULL, TEXT_INPUT, NULL, 0}, KEY("drop", "Delete"), KEY("bump", "KeyB"),
+ KEY("two", "KeyN"), KEY("zero", "KeyO"), KEY("minus_zero", "KeyM"), {"twice", NULL, TEXT_INPUT, NULL, 0},
};
static const O keyed_code[] = {
{TI, 0, 0}, {C, 0, 0}, {LA, PEOPLE, 0}, {TI, 0, 0}, {FIND, PEOPLE, 0}, {XS, K_AT, 0}, /* 0 typed: add, point at it */
@@ -4132,7 +4262,7 @@ static Spec keyed_spec(const T *states, const O *code, size_t count, const H *ha
}
static PureResult type_text(PureApp *app, uint32_t input, const char *text) {
- return pure_app_text(app, input, (PureSpan){(const unsigned char *)text, strlen(text)});
+ return type_in(app, input, (PureSpan){(const unsigned char *)text, strlen(text)});
}
/* A list's first field (text) of every item, comma separated. */
@@ -4164,8 +4294,8 @@ static void keyed(void) {
CHECK(type_text(&app, 1, "q") == PURE_NOT_FOUND && app.fault == PURE_FAULT_NOT_FOUND && app.values[K_AT].num == 3);
CHECK(type_text(&app, 2, "m") == PURE_OK && app.values[K_FOUND].num == 1);
CHECK(type_text(&app, 2, "c") == PURE_OK && app.values[K_FOUND].num == 0);
- CHECK(pure_app_input(&app, 4, PURE_EDGE_DOWN) == PURE_OK && pure_list_get(app.lists[PEOPLE], 3, 1).num == 1);
- CHECK(pure_app_input(&app, 3, PURE_EDGE_DOWN) == PURE_OK && !strcmp(names_of(&app, PEOPLE), "a,b,m"));
+ CHECK(press(&app, 4) == PURE_OK && pure_list_get(app.lists[PEOPLE], 3, 1).num == 1);
+ CHECK(press(&app, 3) == PURE_OK && !strcmp(names_of(&app, PEOPLE), "a,b,m"));
CHECK(app.values[K_AT].num == 2 && number_view(&app, 2) == 0 && number_view(&app, 3) == 0); /* "m": not bob, not before m */
CHECK(type_text(&app, 1, "b") == PURE_OK && number_view(&app, 2) == 0 && number_view(&app, 3) == 1);
CHECK(type_text(&app, 0, "bob") == PURE_OK && number_view(&app, 2) == 1 && number_view(&app, 3) == 1);
@@ -4174,8 +4304,8 @@ static void keyed(void) {
CHECK(type_text(&app, 0, "") == PURE_OK && type_text(&app, 0, "") == PURE_CONFLICT);
CHECK(!strcmp(names_of(&app, PEOPLE), ",a,ab,b,bob,m,\xea\xb0\x80") && app.values[K_AT].num == 0);
/* Numbers order by value, and -0 is the key 0. */
- CHECK(pure_app_input(&app, 5, PURE_EDGE_DOWN) == PURE_OK && pure_app_input(&app, 6, PURE_EDGE_DOWN) == PURE_OK);
- CHECK(pure_app_input(&app, 7, PURE_EDGE_DOWN) == PURE_CONFLICT && number_view(&app, 4) == 2);
+ CHECK(press(&app, 5) == PURE_OK && press(&app, 6) == PURE_OK);
+ CHECK(press(&app, 7) == PURE_CONFLICT && number_view(&app, 4) == 2);
CHECK(pure_list_get(app.lists[SCORES], 0, 0).num == 0 && !signbit(pure_list_get(app.lists[SCORES], 0, 0).num) &&
pure_list_get(app.lists[SCORES], 1, 0).num == 2);
@@ -4183,7 +4313,7 @@ static void keyed(void) {
CHECK(load(keyed_spec(unkeyed_states, unkeyed_code, LEN(unkeyed_code), unkeyed_handlers, 1), &module_b) == PURE_OK);
CHECK(pure_app_replace(&app, &module_b) == PURE_OK && type_text(&app, 0, "c") == PURE_OK);
CHECK(!strcmp(names_of(&app, PEOPLE), ",a,ab,b,bob,m,\xea\xb0\x80,c") && app.values[K_AT].num == 7);
- CHECK(pure_app_point(&app, K_AT, 6) == PURE_OK); /* at the Korean name */
+ CHECK(place(&app, K_AT, 6) == PURE_OK); /* at the Korean name */
/* Keyed again: put in order, the index following its item. */
CHECK(pure_app_replace(&app, &module_a) == PURE_OK && !strcmp(names_of(&app, PEOPLE), ",a,ab,b,bob,c,m,\xea\xb0\x80"));
CHECK(app.values[K_AT].num == 7 && !strcmp(view_text(&app, 5), "\xea\xb0\x80"));
@@ -4197,7 +4327,7 @@ static void keyed(void) {
CHECK(pure_app_start(&app, &module_b, pure_tree_data(path)) == PURE_OK);
for (const char *const *word = (const char *const[]){"m", "b", "z", NULL}; *word; ++word)
CHECK(type_text(&app, 0, *word) == PURE_OK);
- CHECK(pure_app_point(&app, K_AT, 1) == PURE_OK);
+ CHECK(place(&app, K_AT, 1) == PURE_OK);
pure_app_stop(&app);
for (int round = 0; round < 2; ++round) {
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK);
@@ -4266,8 +4396,8 @@ static void keyed_mutations(void) {
if (pure_app_start(&app, &module_b, (PureData){0}) != PURE_OK) continue;
for (unsigned e = 0; e < 24; ++e) {
uint32_t input = next_random(&state) % module_b.inputs;
- if (module_b.input[input].kind == PURE_KIND_TEXT) type_text(&app, input, words[next_random(&state) % LEN(words)]);
- else if (module_b.input[input].kind == PURE_KIND_KEY) pure_app_input(&app, input, PURE_EDGE_DOWN);
+ if (module_b.input[input].binding == PURE_BIND_TEXT) type_text(&app, input, words[next_random(&state) % LEN(words)]);
+ else if (module_b.input[input].binding == PURE_BIND_PRESS) press(&app, input);
for (uint32_t v = 0; v < module_b.views; ++v) {
double value;
if (module_b.view_type[v] == PURE_VIEW_TEXT) pure_app_view_text(&app, v, ignore_span, NULL);
@@ -4348,8 +4478,8 @@ static void describe(PureApp *app, Shown *s) {
static void keyed_event(PureApp *app, uint32_t *seed) {
static const char *const names[] = {"a", "b", "ab", "", "\xea\xb0\x80", "zz", "bob", "m", "q", "c"};
uint32_t input = next_random(seed) % app->module->inputs;
- if (app->module->input[input].kind == PURE_KIND_TEXT) type_text(app, input, names[next_random(seed) % LEN(names)]);
- else pure_app_input(app, input, PURE_EDGE_DOWN);
+ if (app->module->input[input].binding == PURE_BIND_TEXT) type_text(app, input, names[next_random(seed) % LEN(names)]);
+ else press(app, input);
}
/* A snapshot shows the states of the save before it, as they were, however
@@ -4547,7 +4677,7 @@ static void snapshot_held(Spec spec, void (*event)(PureApp *, uint32_t *)) {
static void crash_event(PureApp *app, uint32_t *seed) {
(void)seed;
- CHECK(pure_app_input(app, 0, PURE_EDGE_DOWN) == PURE_OK);
+ CHECK(press(app, 0) == PURE_OK);
}
static void snapshots(void) {
@@ -5055,10 +5185,10 @@ static void work_budget(void) {
CHECK(pure_app_message(&app, 2, (PureSpan){payload, 8}) == PURE_OK && app.work == 3);
}
pure_app_work(&app, 23 + 22 * 5 - 1);
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_LIMIT && app.fault == PURE_FAULT_WORK);
+ CHECK(press(&app, 1) == PURE_LIMIT && app.fault == PURE_FAULT_WORK);
for (size_t i = 0; i < 5; ++i) CHECK(pure_list_get(app.lists[W_STOCK], i, 1).num == 2); /* nothing changed */
pure_app_work(&app, 23 + 22 * 5);
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.work == 23 + 22 * 5);
+ CHECK(press(&app, 1) == PURE_OK && app.work == 23 + 22 * 5);
for (size_t i = 0; i < 5; ++i) CHECK(pure_list_get(app.lists[W_STOCK], i, 1).num == 3);
pure_app_work(&app, 2);
put32(put32(payload, 9), 1);
@@ -5074,7 +5204,7 @@ static void work_budget(void) {
CHECK(pure_app_start(&app, &module_a, (PureData){0}) == PURE_OK);
pure_app_work(&app, UINT64_MAX);
for (const char *word = "abc"; *word; ++word)
- CHECK(pure_app_text(&app, 0, (PureSpan){(const unsigned char *)word, 1}) == PURE_OK && app.work == 7);
+ CHECK(type_in(&app, 0, (PureSpan){(const unsigned char *)word, 1}) == PURE_OK && app.work == 7);
double value;
CHECK(number_view(&app, 0) == 3 && app.work == 4 + 3 * 3);
pure_app_work(&app, 4 + 3 * 3 - 1);
@@ -5083,9 +5213,9 @@ static void work_budget(void) {
pure_app_work(&app, 4 + 3 * 3);
CHECK(number_view(&app, 0) == 3);
pure_app_work(&app, 0);
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && pure_app_point(&app, 1, 0) == PURE_OK &&
- pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && pure_app_point(&app, 1, 1) == PURE_OK &&
- pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK); /* all done */
+ CHECK(press(&app, 1) == PURE_OK && place(&app, 1, 0) == PURE_OK &&
+ press(&app, 1) == PURE_OK && place(&app, 1, 1) == PURE_OK &&
+ press(&app, 1) == PURE_OK); /* all done */
CHECK(!pure_app_view(&app, 4, &value) && pure_app_view_fault(&app) == PURE_FAULT_NOT_FOUND); /* fewest open: none */
pure_app_stop(&app);
pure_module_release(&module_a);
@@ -5094,7 +5224,7 @@ static void work_budget(void) {
CHECK(load(filter_spec(filter_code, LEN(filter_code), filter_views, 3), &module_a) == PURE_OK);
CHECK(pure_app_start(&app, &module_a, (PureData){0}) == PURE_OK);
for (const char *word = "abcd"; *word; ++word)
- CHECK(pure_app_text(&app, 0, (PureSpan){(const unsigned char *)word, 1}) == PURE_OK);
+ CHECK(type_in(&app, 0, (PureSpan){(const unsigned char *)word, 1}) == PURE_OK);
pure_app_work(&app, 3 * 4 - 1);
size_t at;
CHECK(pure_app_rows(&app, 0) == 0 && pure_app_view_fault(&app) == PURE_FAULT_WORK && !pure_app_row_item(&app, 0, 0, &at));
@@ -5134,31 +5264,31 @@ static void memory_budget(void) {
char *path = new_store();
PureApp app;
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK);
- for (int k = 0; k < 3; ++k) CHECK(pure_app_input(&app, ADD, PURE_EDGE_DOWN) == PURE_OK);
+ for (int k = 0; k < 3; ++k) CHECK(press(&app, ADD) == PURE_OK);
size_t three = pure_app_state_bytes(&app);
CHECK(three == 3 * 8); /* a row's number as it is stored */
/* Room for one row more: the next is refused, and nothing of it stays. */
pure_app_memory(&app, three + 8);
- CHECK(pure_app_input(&app, ADD, PURE_EDGE_DOWN) == PURE_OK && pure_list_count(app.lists[1]) == 4);
- CHECK(pure_app_input(&app, ADD, PURE_EDGE_DOWN) == PURE_BUDGET && app.fault == PURE_FAULT_MEMORY &&
+ CHECK(press(&app, ADD) == PURE_OK && pure_list_count(app.lists[1]) == 4);
+ CHECK(press(&app, ADD) == PURE_BUDGET && app.fault == PURE_FAULT_MEMORY &&
pure_list_count(app.lists[1]) == 4 && pure_app_state_bytes(&app) == three + 8);
/* Over the budget (made smaller), what frees goes on and what grows does not. */
pure_app_memory(&app, 8);
- CHECK(pure_app_input(&app, ADD, PURE_EDGE_DOWN) == PURE_BUDGET);
- CHECK(pure_app_input(&app, DROP, PURE_EDGE_DOWN) == PURE_OK && pure_list_count(app.lists[1]) == 3);
+ CHECK(press(&app, ADD) == PURE_BUDGET);
+ CHECK(press(&app, DROP) == PURE_OK && pure_list_count(app.lists[1]) == 3);
/* Sends count while they wait: a full outbox refuses the next until some are trimmed. */
size_t before = pure_app_state_bytes(&app);
pure_app_memory(&app, before);
- CHECK(pure_app_input(&app, SEND_KEY, PURE_EDGE_DOWN) == PURE_BUDGET && !pure_list_count(app.outbox));
+ CHECK(press(&app, SEND_KEY) == PURE_BUDGET && !pure_list_count(app.outbox));
pure_app_memory(&app, 0);
- CHECK(pure_app_input(&app, SEND_KEY, PURE_EDGE_DOWN) == PURE_OK && pure_list_count(app.outbox) == 1);
+ CHECK(press(&app, SEND_KEY) == PURE_OK && pure_list_count(app.outbox) == 1);
size_t one = pure_app_state_bytes(&app) - before;
pure_app_memory(&app, before + 2 * one);
- CHECK(pure_app_input(&app, SEND_KEY, PURE_EDGE_DOWN) == PURE_OK);
- CHECK(pure_app_input(&app, SEND_KEY, PURE_EDGE_DOWN) == PURE_BUDGET && pure_list_count(app.outbox) == 2);
+ CHECK(press(&app, SEND_KEY) == PURE_OK);
+ CHECK(press(&app, SEND_KEY) == PURE_BUDGET && pure_list_count(app.outbox) == 2);
pure_app_trim(&app, OUT, 1);
CHECK(pure_app_save(&app) == PURE_OK && pure_list_count(app.outbox) == 1);
- CHECK(pure_app_input(&app, SEND_KEY, PURE_EDGE_DOWN) == PURE_OK && outbox_of(&app).items[1].seq == 3);
+ CHECK(press(&app, SEND_KEY) == PURE_OK && outbox_of(&app).items[1].seq == 3);
/* A delivery refused for memory is not heard: given again with room, it is applied once. */
double taken = 7;
pure_app_memory(&app, pure_app_state_bytes(&app));
@@ -5286,19 +5416,19 @@ static void output_meaning(void) {
char *path = new_store();
PureApp app;
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK);
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK && pay_reads(&app, 0, 10, 20));
+ CHECK(press(&app, 0) == PURE_OK && pay_reads(&app, 0, 10, 20));
CHECK(pure_app_replace(&app, &module_b) == PURE_OK && app.module == &module_b && pay_reads(&app, 0, 20, 10));
pure_app_stop(&app); /* no save since: the links name a's fields, the outbox's image b's */
CHECK(pure_app_start(&app, &module_b, pure_tree_data(path)) == PURE_OK && pay_reads(&app, 0, 20, 10));
pure_app_stop(&app);
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && pay_reads(&app, 0, 10, 20));
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK && pay_reads(&app, 1, 10, 20));
+ CHECK(press(&app, 0) == PURE_OK && pay_reads(&app, 1, 10, 20));
CHECK(outbox_of(&app).items[1].seq == 2);
pure_app_stop(&app);
CHECK(pure_app_start(&app, &module_b, pure_tree_data(path)) == PURE_OK && pay_reads(&app, 0, 20, 10) &&
pay_reads(&app, 1, 20, 10) && outbox_of(&app).items[1].seq == 2);
/* Sent with b's fields, logged after the image named them. */
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK && pay_reads(&app, 2, 20, 10));
+ CHECK(press(&app, 0) == PURE_OK && pay_reads(&app, 2, 20, 10));
pure_app_stop(&app);
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && pay_reads(&app, 2, 10, 20));
pure_app_stop(&app);
@@ -5357,11 +5487,11 @@ static void bools(void) {
CHECK(pure_app_start(&app, &module_a, (PureData){0}) == PURE_OK && app.values[B_ON].num == 1 && app.values[B_OFF].num == 0);
CHECK(number_view(&app, 0) == 1 && number_view(&app, 1) == 0 && number_view(&app, 2) == 0 && number_view(&app, 3) == 0);
CHECK(!strcmp(view_text(&app, 4), "true1"));
- for (int k = 0; k < 3; ++k) CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK);
+ for (int k = 0; k < 3; ++k) CHECK(press(&app, 1) == PURE_OK);
CHECK(app.values[B_N].num == 3 && number_view(&app, 1) == 1 && number_view(&app, 3) == 1);
- CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_LIMIT && app.values[B_OFF].num == 0); /* require failed: undone */
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK && app.values[B_ON].num == 0 && !strcmp(view_text(&app, 4), "false1"));
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[B_N].num == 3 && number_view(&app, 2) == 1);
+ CHECK(press(&app, 2) == PURE_LIMIT && app.values[B_OFF].num == 0); /* require failed: undone */
+ CHECK(press(&app, 0) == PURE_OK && app.values[B_ON].num == 0 && !strcmp(view_text(&app, 4), "false1"));
+ CHECK(press(&app, 1) == PURE_OK && app.values[B_N].num == 3 && number_view(&app, 2) == 1);
/* A message's bool is a byte, 0 or 1; any other byte fails the message. */
unsigned char payload[9] = {1};
double five = 5;
@@ -5374,7 +5504,7 @@ static void bools(void) {
CHECK(pure_app_message(&app, 4, (PureSpan){payload, 9}) == PURE_LIMIT && app.values[B_N].num == 5);
pure_app_finish(&app);
/* A send holds its bools as bytes. */
- CHECK(pure_app_input(&app, 3, PURE_EDGE_DOWN) == PURE_OK);
+ CHECK(press(&app, 3) == PURE_OK);
Outbox o = outbox_of(&app);
double n;
memcpy(&n, o.items[0].payload + 2, 8);
@@ -5382,8 +5512,8 @@ static void bools(void) {
/* Stored, a waiting send keeps its bools. */
char *sends = new_store();
PureApp kept;
- CHECK(pure_app_start(&kept, &module_a, pure_tree_data(sends)) == PURE_OK && pure_app_input(&kept, 1, PURE_EDGE_DOWN) == PURE_OK);
- CHECK(pure_app_input(&kept, 3, PURE_EDGE_DOWN) == PURE_OK && pure_app_save(&kept) == PURE_OK);
+ CHECK(pure_app_start(&kept, &module_a, pure_tree_data(sends)) == PURE_OK && press(&kept, 1) == PURE_OK);
+ CHECK(press(&kept, 3) == PURE_OK && pure_app_save(&kept) == PURE_OK);
pure_app_stop(&kept);
CHECK(pure_app_start(&kept, &module_a, pure_tree_data(sends)) == PURE_OK);
Outbox stored_sends = outbox_of(&kept);
@@ -5522,7 +5652,7 @@ static void bools(void) {
NULL, 0, flagged_records, 1, NULL, 0};
CHECK(load(numbers, &module_a) == PURE_OK && load(flagged, &module_b) == PURE_OK);
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK);
- for (int k = 0; k < 3; ++k) CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK);
+ for (int k = 0; k < 3; ++k) CHECK(press(&app, 0) == PURE_OK);
pure_app_stop(&app);
for (int round = 0; round < 2; ++round) { /* migrated, then stored as migrated */
CHECK(pure_app_start(&app, &module_b, pure_tree_data(path)) == PURE_OK && app.values[0].num == 0);
@@ -5568,7 +5698,7 @@ static Ran run_code(const O *code, uint32_t count) {
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);
- Ran ran = {pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK ? PURE_FAULT_NONE : app.fault, {{0}}};
+ Ran ran = {press(&app, 0) == PURE_OK ? PURE_FAULT_NONE : app.fault, {{0}}};
memcpy(ran.values, app.values, sizeof(ran.values));
pure_app_stop(&app);
return ran;
@@ -5691,20 +5821,20 @@ static void integers(void) {
CHECK(i64_view(&app, 0) == 0 && !pure_app_view_i64(&app, 1, &value) && !pure_app_view_i64(&app, 4, &value));
double number;
CHECK(!pure_app_view(&app, 0, &number) && pure_app_view(&app, 3, &number) && number == 0);
- for (int k = 0; k < 3; ++k) CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK);
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK); /* -3 steps */
+ for (int k = 0; k < 3; ++k) CHECK(press(&app, 0) == PURE_OK);
+ CHECK(press(&app, 1) == PURE_OK); /* -3 steps */
CHECK(pure_list_count(app.lists[KI_ITEMS]) == 4 && pure_list_get(app.lists[KI_ITEMS], 0, 0).i64 == -3 * STEP &&
pure_list_get(app.lists[KI_ITEMS], 1, 0).i64 == 0 && pure_list_get(app.lists[KI_ITEMS], 3, 0).i64 == 2 * STEP);
CHECK(i64_view(&app, 0) == 0 && i64_view(&app, 1) == 2 * STEP && i64_view(&app, 2) == -3 * STEP &&
pure_app_view(&app, 3, &number) && number == 4 && i64_view(&app, 4) == -3 * STEP);
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_CONFLICT && app.fault == PURE_FAULT_EXISTS); /* -3 steps again */
- for (int k = 0; k < 4; ++k) CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK);
+ CHECK(press(&app, 1) == PURE_CONFLICT && app.fault == PURE_FAULT_EXISTS); /* -3 steps again */
+ for (int k = 0; k < 4; ++k) CHECK(press(&app, 0) == PURE_OK);
CHECK(app.values[KI_NEXT].i64 == INT64_MAX && !pure_app_view_i64(&app, 0, &value)); /* the sum overflows */
CHECK(!strcmp(view_text(&app, 5), "9223372036854775807"));
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_INVALID && app.fault == PURE_FAULT_OVERFLOW);
+ CHECK(press(&app, 0) == PURE_INVALID && app.fault == PURE_FAULT_OVERFLOW);
CHECK(pure_list_count(app.lists[KI_ITEMS]) == 8 && app.values[KI_NEXT].i64 == INT64_MAX && app.values[KI_N].num == 7);
- CHECK(pure_app_input(&app, 3, PURE_EDGE_DOWN) == PURE_OK && app.values[KI_FOUND].num == 1);
- CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_OK && i64_view(&app, 4) == 0);
+ CHECK(press(&app, 3) == PURE_OK && app.values[KI_FOUND].num == 1);
+ CHECK(press(&app, 2) == PURE_OK && i64_view(&app, 4) == 0);
/* A message's i64 is 8 bytes, a send's too. */
unsigned char payload[16];
int64_t least = INT64_MIN;
@@ -5846,7 +5976,7 @@ static Byted run_bytes(const O *code, uint32_t count) {
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);
- Byted ran = {pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK ? PURE_FAULT_NONE : app.fault, app.values[0].num,
+ Byted ran = {press(&app, 0) == PURE_OK ? PURE_FAULT_NONE : app.fault, app.values[0].num,
app.values[3].i64, {0}, pure_list_count(app.lists[2])};
snprintf(ran.said, sizeof(ran.said), "%s", state_text(&app, 4));
pure_app_stop(&app);
@@ -6029,9 +6159,9 @@ static void formats_and_held(void) {
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);
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK && pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK);
+ CHECK(press(&app, 0) == PURE_OK && press(&app, 0) == PURE_OK);
CHECK(!strcmp(state_text(&app, 4), "Az\xc3\xa9")); /* held5 began empty again */
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_BUDGET && app.fault == PURE_FAULT_MEMORY);
+ CHECK(press(&app, 1) == PURE_BUDGET && app.fault == PURE_FAULT_MEMORY);
CHECK(!strcmp(state_text(&app, 4), "Az\xc3\xa9")); /* nothing changed */
CHECK(pure_app_message(&app, 2, (PureSpan){NULL, 0}) == PURE_OK);
Text replied = {{0}, 0};
@@ -6094,7 +6224,7 @@ static void lists_in_records(void) {
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);
+ CHECK(press(&app, 0) == 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();
@@ -6202,21 +6332,21 @@ static void functions_and_characters(void) {
PureApp app;
CHECK(pure_app_start(&app, &module_b, (PureData){0}) == PURE_OK);
for (int round = 0; round < 3; ++round) { /* the frames' room used again, each event alike */
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK && app.values[IV].i64 == 6765);
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[IV].i64 == 18);
- CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_OK && app.values[IV].i64 == 1111 && app.values[0].num == 1);
- CHECK(pure_app_input(&app, 3, PURE_EDGE_DOWN) == PURE_OK && !strcmp(state_text(&app, SAID), "210212"));
- CHECK(pure_app_input(&app, 4, PURE_EDGE_DOWN) == PURE_OK && app.values[IV].i64 == 0x61 + 0xE9 + 0x20AC + 0x1F600 &&
+ CHECK(press(&app, 0) == PURE_OK && app.values[IV].i64 == 6765);
+ CHECK(press(&app, 1) == PURE_OK && app.values[IV].i64 == 18);
+ CHECK(press(&app, 2) == PURE_OK && app.values[IV].i64 == 1111 && app.values[0].num == 1);
+ CHECK(press(&app, 3) == PURE_OK && !strcmp(state_text(&app, SAID), "210212"));
+ CHECK(press(&app, 4) == PURE_OK && app.values[IV].i64 == 0x61 + 0xE9 + 0x20AC + 0x1F600 &&
!strcmp(state_text(&app, SAID), constants[1]));
}
/* A call without end: the room runs out (MEMORY) where the work does not; with a smaller work budget, WORK. Either
* way nothing changed, and the lists are the app's own again. */
uint64_t before = (uint64_t)app.values[IV].i64;
- CHECK(pure_app_input(&app, 5, PURE_EDGE_DOWN) == PURE_BUDGET && app.fault == PURE_FAULT_MEMORY);
+ CHECK(press(&app, 5) == PURE_BUDGET && app.fault == PURE_FAULT_MEMORY);
CHECK((uint64_t)app.values[IV].i64 == before && pure_list_count(app.lists[OWN]) == 0 && app.calls.at == 0 && !app.calls.used);
pure_app_work(&app, 100000);
- CHECK(pure_app_input(&app, 5, PURE_EDGE_DOWN) == PURE_LIMIT && app.fault == PURE_FAULT_WORK);
- CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_OK && app.values[IV].i64 == 1111 && app.values[0].num == 1);
+ CHECK(press(&app, 5) == PURE_LIMIT && app.fault == PURE_FAULT_WORK);
+ CHECK(press(&app, 2) == PURE_OK && app.values[IV].i64 == 1111 && app.values[0].num == 1);
pure_app_stop(&app);
/* Characters that fail: within one (é at 2), past the end, a surrogate, past U+10FFFF, below 0. */
const O chars_bad[][3] = {{{TK, 1, 0}, IC(2), {CHAT, 0, 0}}, {{TK, 1, 0}, IC(10), {CHAT, 0, 0}},
@@ -6230,7 +6360,7 @@ static void functions_and_characters(void) {
NULL, 0, records, 2, NULL, 0};
if (load(bad, &module_a) != PURE_OK) { fprintf(stderr, "chars %zu: %s\n", k, error); abort(); }
CHECK(pure_app_start(&app, &module_a, (PureData){0}) == PURE_OK);
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) != PURE_OK && app.fault == chars_fault[k]);
+ CHECK(press(&app, 0) != PURE_OK && app.fault == chars_fault[k]);
pure_app_stop(&app);
}
/* Refused: no return; a result of another type; code after a return; RETURN outside a function; a call with an
@@ -6325,7 +6455,7 @@ static void nested_crashes(void) {
char *path = new_store();
PureApp app;
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK);
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK && pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK);
+ CHECK(press(&app, 0) == PURE_OK && press(&app, 0) == PURE_OK);
CHECK(nested_consistent(&app) == 2);
pure_app_stop(&app);
unsigned expected = 2;
@@ -6338,7 +6468,7 @@ static void nested_crashes(void) {
if (pure_app_start(&doomed, &module_a, pure_tree_data(path)) != PURE_OK) _exit(1);
crash = 1;
fail_stage = at;
- pure_app_input(&doomed, 0, PURE_EDGE_DOWN);
+ press(&doomed, 0);
_exit(0);
}
int status;
@@ -6350,11 +6480,11 @@ static void nested_crashes(void) {
crash = 0;
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK);
for (fail_stage = PURE_AFTER_HEADER; fail_stage <= PURE_BEFORE_RENAME; ++fail_stage) /* a failed commit: nothing */
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_STORE_ERROR && nested_consistent(&app) == expected);
+ CHECK(press(&app, 0) == PURE_STORE_ERROR && nested_consistent(&app) == expected);
fail_stage = PURE_LOG_BEFORE_WRITE;
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_STORE_ERROR && nested_consistent(&app) == expected);
+ CHECK(press(&app, 0) == PURE_STORE_ERROR && nested_consistent(&app) == expected);
fail_stage = -1;
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK && nested_consistent(&app) == expected + 1);
+ CHECK(press(&app, 0) == PURE_OK && nested_consistent(&app) == expected + 1);
pure_app_stop(&app);
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK && nested_consistent(&app) == expected + 1);
pure_app_stop(&app);
@@ -6385,7 +6515,7 @@ static void nested_replace(void) {
if (load(spec, &module_a) != PURE_OK) { fprintf(stderr, "nested replace: %s\n", error); abort(); }
PureApp app;
CHECK(pure_app_start(&app, &module_a, (PureData){0}) == PURE_OK);
- for (int k = 0; k < 3; ++k) CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK);
+ for (int k = 0; k < 3; ++k) CHECK(press(&app, 0) == PURE_OK);
/* The leaves gain w (7 by default) and the boards' fields swap places. */
static const RF leaf_wider[] = {{"v", 3, 0}, {"w", 0, 7}};
static const RF board_swapped[] = {{"items", 4, 0}, {"id", 3, 0}};
@@ -6558,7 +6688,7 @@ static void anchors(void) {
CHECK(!pure_app_view(&app, 3, &value) && pure_app_view_fault(&app) == PURE_FAULT_NOT_FOUND);
CHECK(!pure_app_view(&app, 4, &value) && pure_app_view_fault(&app) == PURE_FAULT_LIMIT);
CHECK(!pure_app_view(&app, 5, &value) && pure_app_view_fault(&app) == PURE_FAULT_NOT_FOUND);
- for (int k = 0; k < 2; ++k) CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK);
+ for (int k = 0; k < 2; ++k) CHECK(press(&app, 0) == PURE_OK);
CHECK(number_view(&app, 5) == 1);
pure_app_stop(&app);
const struct { O code[4]; uint32_t count; const char *why; } refused[] = {
@@ -6601,14 +6731,14 @@ static void timers(void) {
Spec spec = SPEC(nums, inputs, one_view, handlers, code);
spec.nv = 0;
if (load(spec, &module_a) != PURE_OK) { fprintf(stderr, "%s\n", error); abort(); }
- CHECK(module_a.input[0].kind == PURE_KIND_TIMER && module_a.input[0].period == 1000);
+ CHECK(module_a.input[0].binding == PURE_BIND_TIMER && module_a.input[0].period == 1000);
PureApp app;
CHECK(pure_app_start(&app, &module_a, (PureData){0}) == PURE_OK);
app.now = 1700000123456.0;
- CHECK(pure_app_tick(&app, 0) == PURE_OK && app.values[0].num == 1700000123456.0 &&
+ CHECK(tick(&app, 0) == PURE_OK && app.values[0].num == 1700000123456.0 &&
app.values[1].num == (double)(1700000123 % 60) && app.values[2].num == 1);
- CHECK(pure_app_tick(&app, 1) == PURE_INVALID && pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_INVALID);
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[3].num == 2 - 20);
+ CHECK(tick(&app, 1) == PURE_INVALID && press(&app, 0) == PURE_INVALID);
+ CHECK(press(&app, 1) == PURE_OK && app.values[3].num == 2 - 20);
/* A zero takes the divisor's sign too. */
static const O zeros[] = {{C, 0, -6}, {C, 0, 3}, {MOD, 0, 0}, {ST, 0, 0}, {C, 0, 6}, {C, 0, -3}, {MOD, 0, 0}, {ST, 1, 0}};
static const H zero_handler[] = {{3, 0, 8}};
@@ -6616,10 +6746,10 @@ static void timers(void) {
signs.o = zeros; signs.no = 8; signs.h = zero_handler; signs.nh = 1;
CHECK(load(signs, &module_b) == PURE_OK);
PureApp zero;
- CHECK(pure_app_start(&zero, &module_b, (PureData){0}) == PURE_OK && pure_app_input(&zero, 1, PURE_EDGE_DOWN) == PURE_OK);
+ CHECK(pure_app_start(&zero, &module_b, (PureData){0}) == PURE_OK && press(&zero, 1) == PURE_OK);
CHECK(zero.values[0].num == 0 && !signbit(zero.values[0].num) && zero.values[1].num == 0 && signbit(zero.values[1].num));
pure_app_stop(&zero);
- CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_INVALID && app.fault == PURE_FAULT_DIVIDE);
+ CHECK(press(&app, 2) == PURE_INVALID && app.fault == PURE_FAULT_DIVIDE);
pure_app_stop(&app);
/* A timer's period is 1 ms to a day, and it has nothing else; now is for handlers. */
static const I no_period[] = {TIMER("tick", 0)}, too_long[] = {TIMER("tick", PURE_TIMER_LONGEST + 1)};
@@ -6731,30 +6861,30 @@ static void loops(void) {
CHECK(module_a.state_key[SEEN] == 1 && module_a.state_flags[SEEN] == PURE_LIST_ROWS >> 8);
PureApp app;
CHECK(pure_app_start(&app, &module_a, (PureData){0}) == PURE_OK);
- for (int k = 0; k < 10; ++k) CHECK(pure_app_input(&app, 9, PURE_EDGE_DOWN) == PURE_OK);
+ for (int k = 0; k < 10; ++k) CHECK(press(&app, 9) == PURE_OK);
CHECK(app.values[N].num == 10 && pure_list_count(app.lists[XS]) == 10);
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK && app.values[S_].num == 45);
+ CHECK(press(&app, 0) == PURE_OK && app.values[S_].num == 45);
CHECK(app.work == 9 + 10 * (4 + 1)); /* the LOOP counts its block and itself each time round */
- CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && app.values[S_].num == 5);
- CHECK(pure_app_input(&app, 2, PURE_EDGE_DOWN) == PURE_OK && app.values[S_].num == 100 + 3 * 2 * 100);
- CHECK(pure_app_input(&app, 3, PURE_EDGE_DOWN) == PURE_OK && app.values[S_].num == 3 && !pure_list_count(app.lists[SEEN]));
- CHECK(pure_app_input(&app, 3, PURE_EDGE_DOWN) == PURE_OK && app.values[S_].num == 3); /* each event's own */
- CHECK(pure_app_input(&app, 6, PURE_EDGE_DOWN) == PURE_OK && app.values[S_].num == 3);
+ CHECK(press(&app, 1) == PURE_OK && app.values[S_].num == 5);
+ CHECK(press(&app, 2) == PURE_OK && app.values[S_].num == 100 + 3 * 2 * 100);
+ CHECK(press(&app, 3) == PURE_OK && app.values[S_].num == 3 && !pure_list_count(app.lists[SEEN]));
+ CHECK(press(&app, 3) == PURE_OK && app.values[S_].num == 3); /* each event's own */
+ CHECK(press(&app, 6) == PURE_OK && app.values[S_].num == 3);
/* A loop that would not end runs out of work, by default too, and changes nothing. */
- CHECK(pure_app_input(&app, 4, PURE_EDGE_DOWN) == PURE_LIMIT && app.fault == PURE_FAULT_WORK && app.values[S_].num == 3);
+ CHECK(press(&app, 4) == PURE_LIMIT && app.fault == PURE_FAULT_WORK && app.values[S_].num == 3);
CHECK(app.work <= PURE_WORK_DEFAULT && app.work > PURE_WORK_DEFAULT - 5);
- CHECK(pure_app_input(&app, 8, PURE_EDGE_DOWN) == PURE_LIMIT && app.fault == PURE_FAULT_WORK);
- CHECK(pure_app_input(&app, 7, PURE_EDGE_DOWN) == PURE_LIMIT && app.fault == PURE_FAULT_LIMIT); /* not a whole number */
+ CHECK(press(&app, 8) == PURE_LIMIT && app.fault == PURE_FAULT_WORK);
+ CHECK(press(&app, 7) == PURE_LIMIT && app.fault == PURE_FAULT_LIMIT); /* not a whole number */
/* A list that would fill the memory fails as it passes the default room, or the budget. */
- CHECK(pure_app_input(&app, 5, PURE_EDGE_DOWN) == PURE_BUDGET && app.fault == PURE_FAULT_MEMORY &&
+ CHECK(press(&app, 5) == PURE_BUDGET && app.fault == PURE_FAULT_MEMORY &&
app.values[S_].num == 3 && !pure_list_count(app.lists[PAGES]));
CHECK(app.grown > (int64_t)PURE_GROWTH_DEFAULT && app.grown < (int64_t)PURE_GROWTH_DEFAULT + 8192);
pure_app_memory(&app, 1 << 20);
- CHECK(pure_app_input(&app, 5, PURE_EDGE_DOWN) == PURE_BUDGET && app.fault == PURE_FAULT_MEMORY &&
+ CHECK(press(&app, 5) == PURE_BUDGET && app.fault == PURE_FAULT_MEMORY &&
app.grown < (1 << 20) + 8192 && !pure_list_count(app.lists[PAGES]));
pure_app_memory(&app, 0);
pure_app_work(&app, 10); /* a budget of its own: the sum's 9, and not one time round */
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_LIMIT && app.values[S_].num == 3);
+ CHECK(press(&app, 0) == PURE_LIMIT && app.values[S_].num == 3);
pure_app_stop(&app);
/* What the verifier refuses. */
@@ -6868,7 +6998,7 @@ static void view_bodies(void) {
CHECK(pure_app_start(&app, &module_a, pure_tree_data(path)) == PURE_OK);
double total = 0, value;
for (int k = 0; k < 20; ++k) {
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK);
+ CHECK(press(&app, 0) == PURE_OK);
total += k % 7;
}
for (int round = 0; round < 2; ++round) /* each evaluation begins anew */
@@ -6878,7 +7008,7 @@ static void view_bodies(void) {
pure_app_memory(&app, 4096);
CHECK(!pure_app_view(&app, 3, &value) && pure_app_view_fault(&app) == PURE_FAULT_MEMORY && !pure_list_count(app.lists[SEEN]));
pure_app_memory(&app, 0);
- CHECK(pure_app_input(&app, 0, PURE_EDGE_DOWN) == PURE_OK); /* the app goes on */
+ CHECK(press(&app, 0) == PURE_OK); /* the app goes on */
total += 20 % 7;
/* Readers of one snapshot compute at once, each in its own lists. */
BodyReads reads = {pure_app_snapshot(&app), total, 7, 2000};