diff options
| author | eunhhu <[email protected]> | 2026-10-18 20:54:10 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-10-18 20:54:10 +0100 |
| commit | da0d7514b058f1baa99a8bcc8f9e23b43cac12de (patch) | |
| tree | 688106ea656946bd74f36259c2dd2316b9a429d8 /core | |
| parent | 936c08e3424c606855051ee86f25a7df0f162c62 (diff) | |
| download | pure-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.c | 8 | ||||
| -rw-r--r-- | core/module-run.h | 2 | ||||
| -rw-r--r-- | core/module.c | 137 | ||||
| -rw-r--r-- | core/module.h | 59 | ||||
| -rw-r--r-- | core/test-module.c | 652 |
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}; |
