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/module.c | |
| 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/module.c')
| -rw-r--r-- | core/module.c | 137 |
1 files changed, 39 insertions, 98 deletions
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; } |
