aboutsummaryrefslogtreecommitdiffstats
path: root/prototypes/connections/pure-run.c
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-09-25 12:09:41 +0100
committereunhhu <[email protected]>2026-09-25 12:09:41 +0100
commita2a430f6c2d973232a4e995133b372ba877558cf (patch)
treef1e22521ba03ff4683737131eec0af05e6e0cbd4 /prototypes/connections/pure-run.c
parent08056bb65bb9d1cfa4ccb1cd0b9cb67554d560a3 (diff)
downloadpure-a2a430f6c2d973232a4e995133b372ba877558cf.tar.gz
pure-a2a430f6c2d973232a4e995133b372ba877558cf.zip
Add text states and cursors to Pure modules
Module format v3 adds text states, cursors, UTF-8 constants, text inputs and text views. A text state is a runtime-owned draft without a store, so a large text edits as an extent tree. Cursors are byte positions the runtime keeps on character boundaries: insertion pushes cursors at or after the point, removal gathers cursors inside the range, and a failed event returns every text draft and cursor to where it was. The verifier types a cursor by the text it belongs to and rejects editing one text with another's cursor. Operations cover constants, concatenation, shortest round-trip number text, character, line and byte counts, character and column-keeping line moves, insert, remove, line views, whole-text replacement and UTF-8 checked conversion from bytes. Replies of edited documents or texts are copied before they are finished. pure-run gains text input and text output. test-module covers typing Korean and emoji, moves that stop at the ends, line moves, views, rollback, invalid UTF-8 and replacement, text-specific verifier rules, and 20,000 damaged text modules whose cursors always stay on character boundaries under ASan/UBSan.
Diffstat (limited to 'prototypes/connections/pure-run.c')
-rw-r--r--prototypes/connections/pure-run.c61
1 files changed, 49 insertions, 12 deletions
diff --git a/prototypes/connections/pure-run.c b/prototypes/connections/pure-run.c
index 71b7d0a..e14e6ad 100644
--- a/prototypes/connections/pure-run.c
+++ b/prototypes/connections/pure-run.c
@@ -6,8 +6,9 @@
/* Test host for a verified module: reads commands from stdin and prints one
* JSON snapshot per command. Not a product interface.
- * down NAME | up NAME | frame [N] | replace PATH | show */
-static const char *const fault_names[] = {"none", "divide", "non_finite", "clamp"};
+ * down NAME | up NAME | frame [N] | type NAME HEX|- | replace PATH | show
+ * Text states and text views print as JSON strings; cursors as byte positions. */
+static const char *const fault_names[] = {"none", "divide", "non_finite", "clamp", "limit", "not_found", "encoding"};
static bool load(const char *path, PureModule *module) {
FILE *file = fopen(path, "rb");
@@ -24,22 +25,48 @@ static bool load(const char *path, PureModule *module) {
return true;
}
-static void show(const PureApp *app, const char *failure) {
+static bool json_span(void *context, PureSpan span) {
+ (void)context;
+ for (size_t i = 0; i < span.size; ++i) {
+ unsigned char c = span.data[i];
+ if (c == '"' || c == '\\') printf("\\%c", c);
+ else if (c < 0x20) printf("\\u%04x", c);
+ else putchar(c);
+ }
+ return true;
+}
+
+static void show(PureApp *app, const char *failure) {
const PureModule *m = app->module;
printf("{\"states\":{");
- for (uint32_t i = 0; i < m->states; ++i)
- printf("%s\"%s\":%.17g", i ? "," : "", m->state_names[i], app->values[i]);
+ for (uint32_t i = 0; i < m->states; ++i) {
+ printf("%s\"%s\":", i ? "," : "", m->state_names[i]);
+ if (m->state_type[i] == PURE_STATE_TEXT) {
+ putchar('"');
+ pure_draft_read(app->texts[i], 0, pure_draft_size(app->texts[i]), json_span, NULL);
+ putchar('"');
+ } else printf("%.17g", app->values[i]);
+ }
printf("},\"views\":{");
for (uint32_t i = 0; i < m->views; ++i) {
+ printf("%s\"%s\":", i ? "," : "", m->view_names[i]);
double value;
- if (pure_app_view(app, i, &value)) printf("%s\"%s\":%.17g", i ? "," : "", m->view_names[i], value);
- else printf("%s\"%s\":null", i ? "," : "", m->view_names[i]);
+ if (m->view_type[i] == PURE_VIEW_TEXT) {
+ putchar('"');
+ if (pure_app_view_text(app, i, json_span, NULL) != PURE_OK) printf("\\u0000failed");
+ putchar('"');
+ } else if (pure_app_view(app, i, &value)) printf("%.17g", value);
+ else printf("null");
}
printf("},\"tick\":%llu,\"successfulEvents\":%llu,\"fault\":\"%s\",\"error\":%s%s%s}\n",
(unsigned long long)app->ticks, (unsigned long long)app->events, fault_names[app->fault],
failure ? "\"" : "", failure ? failure : "null", failure ? "\"" : "");
}
+static int hex(char c) {
+ return c >= '0' && c <= '9' ? c - '0' : c >= 'a' && c <= 'f' ? c - 'a' + 10 : -1;
+}
+
int main(int argc, char **argv) {
static PureModule modules[2];
if (argc != 2 || !load(argv[1], &modules[0])) {
@@ -48,19 +75,27 @@ int main(int argc, char **argv) {
}
unsigned active = 0;
PureApp app;
- pure_app_start(&app, &modules[active], (PureData){0});
- char line[4096];
+ if (pure_app_start(&app, &modules[active], (PureData){0}) != PURE_OK) { fputs("cannot start\n", stderr); return 2; }
+ static char line[1 << 16];
+ static unsigned char text[1 << 15];
int status = 0;
while (fgets(line, sizeof(line), stdin)) {
- char command[16] = "", argument[4000] = "";
+ char command[16] = "", argument[256] = "", data[sizeof(line)] = "";
const char *failure = NULL;
line[strcspn(line, "\r\n")] = 0;
- if (sscanf(line, "%15s %3999s", command, argument) < 1) continue;
+ if (sscanf(line, "%15s %255s %65535s", command, argument, data) < 1) continue;
if (!strcmp(command, "down") || !strcmp(command, "up")) {
int input = pure_module_input(app.module, argument);
if (input < 0) failure = "unknown input";
else if (pure_app_input(&app, (uint32_t)input, command[0] == 'd' ? PURE_EDGE_DOWN : PURE_EDGE_UP) != PURE_OK)
failure = "event failed";
+ } else if (!strcmp(command, "type")) {
+ int input = pure_module_input(app.module, argument);
+ size_t length = strcmp(data, "-") ? strlen(data) / 2 : 0;
+ for (size_t i = 0; i < length && length * 2 == strlen(data); ++i)
+ text[i] = (unsigned char)(hex(data[2 * i]) << 4 | hex(data[2 * i + 1]));
+ if (input < 0) failure = "unknown input";
+ else if (pure_app_text(&app, (uint32_t)input, (PureSpan){text, length}) != PURE_OK) failure = "event failed";
} else if (!strcmp(command, "frame")) {
long count = *argument ? strtol(argument, NULL, 10) : 1;
if (count < 1 || count > 600) failure = "frame count";
@@ -68,10 +103,12 @@ int main(int argc, char **argv) {
if (pure_app_frame(&app) != PURE_OK) failure = "event failed";
} else if (!strcmp(command, "replace")) {
if (!load(argument, &modules[!active])) failure = "invalid module";
- else { active = !active; pure_app_replace(&app, &modules[active]); }
+ else if (pure_app_replace(&app, &modules[!active]) != PURE_OK) failure = "replace failed";
+ else active = !active;
} else if (strcmp(command, "show")) failure = "unknown command";
if (failure) status = 1;
show(&app, failure);
}
+ pure_app_stop(&app);
return status;
}