aboutsummaryrefslogtreecommitdiffstats
path: root/prototypes/connections
diff options
context:
space:
mode:
Diffstat (limited to 'prototypes/connections')
-rw-r--r--prototypes/connections/module.c19
-rw-r--r--prototypes/connections/module.h1
-rw-r--r--prototypes/connections/test-module.c33
3 files changed, 50 insertions, 3 deletions
diff --git a/prototypes/connections/module.c b/prototypes/connections/module.c
index 4b5d15d..3d3ee3e 100644
--- a/prototypes/connections/module.c
+++ b/prototypes/connections/module.c
@@ -82,6 +82,9 @@ static const struct { unsigned char pops, in[3], out, where; } signature[PURE_OP
[PURE_OP_INDEX_LAST] = {0, {0}, T_INDEX, READ},
[PURE_OP_INDEX_NUM] = {1, {T_INDEX}, T_NUM, READ},
[PURE_OP_ROW] = {0, {0}, T_INDEX, IN_ROW},
+ /* Stack copies: their types come from the values they move. */
+ [PURE_OP_PICK] = {0, {0}, 0, READ},
+ [PURE_OP_SLIDE] = {0, {0}, 0, READ},
};
static uint32_t read16(const unsigned char *p) { return (uint32_t)p[0] | (uint32_t)p[1] << 8; }
@@ -152,7 +155,7 @@ static bool uses_arg(uint8_t op) {
case PURE_OP_TEXT_LINE: case PURE_OP_TEXT_SET: case PURE_OP_CUR_COUNT: case PURE_OP_LIST_COUNT:
case PURE_OP_LIST_APPEND: case PURE_OP_LIST_INSERT: case PURE_OP_LIST_REMOVE: case PURE_OP_ITEM_GET:
case PURE_OP_ITEM_SET: case PURE_OP_INDEX_LOAD: case PURE_OP_INDEX_STORE: case PURE_OP_INDEX_FIRST:
- case PURE_OP_INDEX_LAST: case PURE_OP_ROW:
+ case PURE_OP_INDEX_LAST: case PURE_OP_ROW: case PURE_OP_PICK: case PURE_OP_SLIDE:
return true;
default: return false;
}
@@ -260,6 +263,18 @@ static bool verify_block(const PureModule *m, PureBlock block, int view, int row
default: break;
}
if (!known) return reject(error, size, "operand names no such state, field, constant or unit", pc);
+ /* PICK copies the value arg below the top; SLIDE drops arg values under the top. */
+ if (op->op == PURE_OP_PICK || op->op == PURE_OP_SLIDE) {
+ bool pick = op->op == PURE_OP_PICK;
+ if (pick ? arg >= (uint32_t)height : !arg || arg >= (uint32_t)height)
+ return reject(error, size, "stack underflow", pc);
+ if (pick && height == PURE_MODULE_STACK) return reject(error, size, "stack overflow", pc);
+ int from = height - 1 - (int)arg, to = pick ? height++ : from;
+ types[to] = pick ? types[from] : types[height - 1];
+ owners[to] = pick ? owners[from] : owners[height - 1];
+ if (!pick) height = from + 1;
+ continue;
+ }
/* Operand types, bottom to top; record operations take their fields. */
unsigned char need[PURE_RECORD_FIELDS + 2];
unsigned pops = signature[op->op].pops;
@@ -992,6 +1007,8 @@ static PureResult run(const PureModule *m, double *values, PureBlock block, Even
stack[sp++].bytes.owner = (uint16_t)op->arg;
++pc;
continue;
+ case PURE_OP_PICK: stack[sp] = stack[sp - 1 - op->arg]; ++sp; ++pc; continue;
+ case PURE_OP_SLIDE: stack[sp - 1 - op->arg] = stack[sp - 1]; sp -= op->arg; ++pc; continue;
default: break;
}
b = stack[--sp].num;
diff --git a/prototypes/connections/module.h b/prototypes/connections/module.h
index 749f9d0..dd20d4a 100644
--- a/prototypes/connections/module.h
+++ b/prototypes/connections/module.h
@@ -37,6 +37,7 @@ typedef enum {
PURE_OP_LIST_COUNT, PURE_OP_LIST_APPEND, PURE_OP_LIST_INSERT, PURE_OP_LIST_REMOVE,
PURE_OP_ITEM_GET, PURE_OP_ITEM_SET, PURE_OP_INDEX_LOAD, PURE_OP_INDEX_STORE,
PURE_OP_INDEX_MOVE, PURE_OP_INDEX_FIRST, PURE_OP_INDEX_LAST, PURE_OP_INDEX_NUM, PURE_OP_ROW,
+ PURE_OP_PICK, PURE_OP_SLIDE,
PURE_OP_COUNT
} PureOpcode;
diff --git a/prototypes/connections/test-module.c b/prototypes/connections/test-module.c
index 3c12d49..a4e746c 100644
--- a/prototypes/connections/test-module.c
+++ b/prototypes/connections/test-module.c
@@ -1099,7 +1099,7 @@ static const char *cell_text(PureApp *app, uint32_t column, size_t row) {
return t.text;
}
-enum { ROW = PURE_OP_ROW, EQ = PURE_OP_EQ, LT = PURE_OP_LT };
+enum { ROW = PURE_OP_ROW, EQ = PURE_OP_EQ, LT = PURE_OP_LT, PICK = PURE_OP_PICK, SLIDE = PURE_OP_SLIDE };
/* A table view has a row per item; its columns read the row and may branch.
* A value crosses a join when every path brings the same stack. */
@@ -1193,6 +1193,35 @@ static void tables(void) {
CHECK(load(bad, &module_b) == (open == 8 ? PURE_OK : PURE_INVALID));
}
CHECK(strstr(error, "more than 8 joins ahead"));
+
+ /* PICK copies a value from below, SLIDE drops values under the top: a
+ * value used twice is computed once. (3 + 4) * (3 + 4) with a text between. */
+ static const O picked[] = {{C, 0, 3}, {C, 0, 4}, {ADD, 0, 0}, {TK, 1, 0}, {PICK, 1, 0}, {PICK, 2, 0}, {MUL, 0, 0},
+ {SLIDE, 2, 0}};
+ static const V squared[] = {{"sq", 0, 8}};
+ bad = (Spec){nums, 1, inputs, 0, squared, 1, NULL, 0, picked, 8, NULL, 0, NULL, 0, constants, 2, NULL, 0, NULL, 0, NULL, 0};
+ CHECK(load(bad, &module_b) == PURE_OK);
+ CHECK(pure_app_start(&app, &module_b, (PureData){0}) == PURE_OK && number_view(&app, 0) == 49);
+ pure_app_stop(&app);
+ static const O pick_text[] = {{TK, 1, 0}, {C, 0, 1}, {PICK, 1, 0}, {SLIDE, 2, 0}};
+ static const V text_left[] = {{"t", 0, 4}};
+ bad.v = NULL; bad.nv = 0; bad.tv = text_left; bad.ntv = 1; bad.o = pick_text; bad.no = 4;
+ CHECK(load(bad, &module_b) == PURE_OK);
+ CHECK(pure_app_start(&app, &module_b, (PureData){0}) == PURE_OK && !strcmp(view_text(&app, 0), "x"));
+ pure_app_stop(&app);
+ static const O pick_type[] = {{TK, 1, 0}, {PICK, 0, 0}, {C, 0, 1}, {ADD, 0, 0}, {SLIDE, 1, 0}};
+ static const V five[] = {{"sq", 0, 5}}, three[] = {{"sq", 0, 3}};
+ bad.o = pick_type; bad.no = 5; bad.tv = NULL; bad.ntv = 0; bad.v = five; bad.nv = 1;
+ CHECK(load(bad, &module_b) == PURE_INVALID && strstr(error, "operand type")); /* a copy keeps its type */
+ static const O pick_far[] = {{C, 0, 1}, {PICK, 1, 0}, {SLIDE, 1, 0}};
+ bad.o = pick_far; bad.no = 3; bad.v = three;
+ CHECK(load(bad, &module_b) == PURE_INVALID && strstr(error, "stack underflow"));
+ static const O slide_all[] = {{C, 0, 1}, {C, 0, 2}, {SLIDE, 2, 0}};
+ bad.o = slide_all;
+ CHECK(load(bad, &module_b) == PURE_INVALID && strstr(error, "stack underflow"));
+ static const O slide_none[] = {{C, 0, 1}, {C, 0, 2}, {SLIDE, 0, 0}};
+ bad.o = slide_none;
+ CHECK(load(bad, &module_b) == PURE_INVALID && strstr(error, "stack underflow"));
}
/* Damaged table modules either fail to load or evaluate every cell safely. */
@@ -1247,6 +1276,6 @@ int main(void) {
tables();
table_mutations();
printf("{\"sizeof_module\":%zu,\"sizeof_app\":%zu}\n", sizeof(PureModule), sizeof(PureApp));
- puts("{\"module_tests\":\"verified-load-atomic-events-faults-signed-zero-if-else-replace-by-name-rejections-mutations-messages-documents-typed-verification-text-cursors-utf8-views-rollback-replace-lists-indices-undo-migration-joins-tables-columns\"}");
+ puts("{\"module_tests\":\"verified-load-atomic-events-faults-signed-zero-if-else-replace-by-name-rejections-mutations-messages-documents-typed-verification-text-cursors-utf8-views-rollback-replace-lists-indices-undo-migration-joins-tables-columns-pick-slide\"}");
return 0;
}