From 5a44af0308017f4e7b54a8ff6ccdc797b5d94015 Mon Sep 17 00:00:00 2001 From: eunhhu Date: Tue, 13 Oct 2026 05:08:06 +0100 Subject: Module format: bits and bytes (design 027's stage 1): I64_BITS (and, or, xor, a left shift as a times 2^b failing as overflow past the range, a right shift as floor(a / 2^b), a count outside 0-63 failing as limit: integers in two's complement without end), BYTE_AT (the byte of any bytes value at a whole position, a number or an i64, else limit), BYTES_OF (a list of one i64 field as bytes, a value outside 0-255 failing as limit) and INDEX_AT (a list's item at a position, not_found where there is none); the fold-shape and batch analyses count their arity and the lists they read; unit tests of the ends (27 bit cases, 11 reads of a constant's and a text state's bytes, 7 refused) --- core/module.c | 75 +++++++++++++++++++++++++++++++-- core/module.h | 15 ++++++- core/test-module.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 204 insertions(+), 5 deletions(-) (limited to 'core') diff --git a/core/module.c b/core/module.c index 0aa51a4..d95769b 100644 --- a/core/module.c +++ b/core/module.c @@ -31,10 +31,11 @@ enum { READ = IN_VIEW | IN_HANDLER }; * collection, a constant, a unit, the column's row, a branch target, a stack * depth, a fold, a relation, a keyed list, an output, the row of the column * a fold runs in, a number, bool or i64 state (A_VALUE), or the value's type - * (A_TYPE: 0 a number, 1 a bool, 2 an i64). */ + * (A_TYPE: 0 a number, 1 a bool, 2 an i64), how I64_BITS computes (A_BITS) or + * what a position is (A_POSITION: 0 a number, 1 an i64). */ enum { A_NONE, A_STATE, A_ITEM = A_STATE + PURE_STATE_I64 + 1, A_FIELD, A_COLLECTION, A_CONSTANT, A_UNIT, A_ROW, A_TARGET, A_DEPTH, A_FOLD, A_RELATION, A_KEYED, A_OUTPUT, A_OUTER, A_EACH, A_LOCAL, A_VALUE, A_TYPE, - A_LOOP }; + A_LOOP, A_BITS, A_POSITION }; /* A fold's block is verified as a column that leaves one bool (count, first) * or a value and whether to include it (a bool): a number (FOLD_TWO) or an * i64 (FOLD_I64). A table's filter is verified as a column that leaves a @@ -133,6 +134,10 @@ static const struct { unsigned char pops, in[3], out; unsigned short where; unsi [PURE_OP_I64_COMPARE] = {2, {T_I64, T_I64}, T_BOOL, READ, A_RELATION, O_ANY}, [PURE_OP_I64_FROM] = {1, {T_NUM}, T_I64, READ, A_NONE, O_ANY}, [PURE_OP_NUM_FROM] = {1, {T_I64}, T_NUM, READ, A_NONE, O_ANY}, + [PURE_OP_I64_BITS] = {2, {T_I64, T_I64}, T_I64, READ, A_BITS, O_ANY}, + [PURE_OP_BYTE_AT] = {2, {T_BYTES, T_NUM}, T_I64, READ, A_POSITION, O_ANY}, + [PURE_OP_BYTES_OF] = {0, {0}, T_BYTES, READ, STATE(LIST), O_ANY}, + [PURE_OP_INDEX_AT] = {1, {T_NUM}, T_INDEX, READ, STATE(LIST), O_ARG}, }; #undef NUMS #undef COMPARE @@ -382,6 +387,8 @@ PURE_COLD static bool names_arg(const PureModule *m, unsigned kind, uint32_t arg case A_VALUE: return state_of(m, arg, PURE_STATE_NUM) || state_of(m, arg, PURE_STATE_BOOL) || state_of(m, arg, PURE_STATE_I64); case A_LOOP: return (arg & 0xFF) < PURE_MODULE_LOCALS && !(arg & 0xFE00); /* a slot, and whether i64s */ case A_TYPE: return arg <= 2; + case A_BITS: return arg <= PURE_BITS_RIGHT; + case A_POSITION: return arg <= 1; case A_RELATION: return arg <= PURE_NOT_EQUAL; case A_KEYED: return state_of(m, arg, PURE_STATE_LIST) && m->state_key[arg]; case A_OUTPUT: return arg < m->inputs && m->input[arg].kind == PURE_KIND_OUTPUT; @@ -496,6 +503,8 @@ PURE_COLD static const char *verify_code(const PureModule *m, PureBlock block, i if (op->op == PURE_OP_BREAK && height) REFUSE("break with values on the stack"); if (op->op == PURE_OP_ITEM_SET && m->state_key[list_of(arg)] == item_field(arg) + 1) REFUSE("change of a keyed list's key"); + if (op->op == PURE_OP_BYTES_OF && (m->record[m->state_ref[arg]].count != 1 || field_type(m, arg, 0) != T_I64)) + REFUSE("bytes of a list not of one i64"); /* A view's body changes an event's lists alone: its states are a snapshot's. */ if (body && (op->op == PURE_OP_LIST_APPEND || op->op == PURE_OP_LIST_INSERT || op->op == PURE_OP_LIST_REMOVE || op->op == PURE_OP_ITEM_SET) && @@ -562,6 +571,8 @@ PURE_COLD static const char *verify_code(const PureModule *m, PureBlock block, i need[0] = state_type(m, arg); } else if (op->op == PURE_OP_NUM_TEXT && arg) { need[0] = arg == 1 ? T_BOOL : T_I64; + } else if (op->op == PURE_OP_BYTE_AT && arg) { + need[1] = T_I64; /* the position an i64 */ } else if ((op->op == PURE_OP_EQ || op->op == PURE_OP_NE) && height >= 2 && types[height - 1] == T_BOOL && types[height - 2] == T_BOOL) { need[0] = need[1] = T_BOOL; /* two bools compare as two numbers do */ @@ -795,7 +806,7 @@ PURE_COLD static int read_list(const PureOp *op) { switch (op->op) { case PURE_OP_ITEM_GET: case PURE_OP_ITEM_SET: return (int)list_of(op->arg); case PURE_OP_LIST_FIND: case PURE_OP_LIST_HAS: case PURE_OP_LIST_COUNT: case PURE_OP_INDEX_FIRST: case PURE_OP_INDEX_LAST: - case PURE_OP_ROW: case PURE_OP_OUTER_ROW: return (int)(op->arg & 0xFFFF); + case PURE_OP_ROW: case PURE_OP_OUTER_ROW: case PURE_OP_BYTES_OF: case PURE_OP_INDEX_AT: return (int)(op->arg & 0xFFFF); default: return -1; } } @@ -1085,9 +1096,11 @@ PURE_COLD static unsigned arity(uint8_t op) { switch (op) { case PURE_OP_CONST: case PURE_OP_LOAD: case PURE_OP_INDEX_LOAD: case PURE_OP_ROW: case PURE_OP_OUTER_ROW: case PURE_OP_LIST_COUNT: case PURE_OP_INDEX_FIRST: case PURE_OP_INDEX_LAST: case PURE_OP_FIELD: case PURE_OP_LOCAL_LOAD: + case PURE_OP_BYTES_OF: return 0; case PURE_OP_ITEM_GET: case PURE_OP_LIST_FIND: case PURE_OP_LIST_HAS: case PURE_OP_NEG: case PURE_OP_NOT: case PURE_OP_FLOOR: case PURE_OP_INDEX_NUM: case PURE_OP_I64_NEG: case PURE_OP_I64_FROM: case PURE_OP_NUM_FROM: + case PURE_OP_INDEX_AT: return 1; case PURE_OP_CLAMP: return 3; default: return 2; @@ -1099,7 +1112,7 @@ PURE_COLD static int list_read(const PureOp *op) { switch (op->op) { case PURE_OP_ITEM_GET: return (int)list_of(op->arg); case PURE_OP_LIST_COUNT: case PURE_OP_INDEX_FIRST: case PURE_OP_INDEX_LAST: case PURE_OP_LIST_FIND: - case PURE_OP_LIST_HAS: case PURE_OP_ROW: + case PURE_OP_LIST_HAS: case PURE_OP_ROW: case PURE_OP_BYTES_OF: case PURE_OP_INDEX_AT: return (int)op->arg; default: return -1; } @@ -2083,6 +2096,14 @@ static void move_indices(const PureModule *m, PureValue *values, uint32_t list, +/* x / 2^n rounded down, whatever the sign (027's RIGHT). */ +static inline int64_t floor_shift(int64_t x, unsigned n) { return x >= 0 ? x >> n : ~(~x >> n); } + +static bool take_byte(void *context, PureSpan span) { + if (span.size) *(unsigned char *)context = span.data[0]; + return true; +} + /* Append bytes to the arena as a value of it. */ static PureResult keep_bytes(PureArena *arena, const void *bytes, size_t length, PureBytes *out) { if (!reserve(arena, length)) return PURE_ALLOCATION; @@ -3428,6 +3449,52 @@ static inline __attribute__((always_inline)) int step(Frame *frame, const PureOp top->i64 = cell.i64; NEXT; } + case PURE_OP_I64_BITS: { /* on integers in two's complement without end (027) */ + int64_t b = stack[--sp].i64, a = stack[sp - 1].i64, r; + if (arg == PURE_BITS_AND) r = a & b; + else if (arg == PURE_BITS_OR) r = a | b; + else if (arg == PURE_BITS_XOR) r = a ^ b; + else { + if (b < 0 || b > 63) { kind = PURE_FAULT_LIMIT; FAILED; } + if (arg == PURE_BITS_RIGHT) r = floor_shift(a, (unsigned)b); + else if (floor_shift(r = (int64_t)((uint64_t)a << b), (unsigned)b) != a) { kind = PURE_FAULT_OVERFLOW; FAILED; } + } + stack[sp - 1].i64 = r; + NEXT; + } + case PURE_OP_BYTE_AT: { /* the byte at a whole position inside the value */ + Slot at = stack[--sp]; + PureBytes v = stack[sp - 1].bytes; + if (arg ? at.i64 < 0 || (uint64_t)at.i64 >= v.length + : !(at.num >= 0 && at.num < (double)v.length) || at.num != floor(at.num)) { + kind = PURE_FAULT_LIMIT; + FAILED; + } + unsigned char byte = 0; + v.offset += arg ? (uint64_t)at.i64 : (uint64_t)at.num; + v.length = 1; + if ((status = visit_value(app, arena, v, take_byte, &byte)) != PURE_OK) STOPPED(status); + stack[sp - 1].i64 = byte; + NEXT; + } + case PURE_OP_BYTES_OF: { /* a list of one i64, an item a byte */ + const PureList *list = app->lists[arg]; + size_t count = pure_list_count(list); + if (!reserve(arena, count)) STOPPED(PURE_ALLOCATION); + for (size_t i = 0; i < count; ++i) { + int64_t byte = pure_list_get(list, i, 0).i64; + if (byte < 0 || byte > 255) { kind = PURE_FAULT_LIMIT; FAILED; } + arena->bytes[arena->used + i] = (unsigned char)byte; + } + stack[sp++].bytes = (PureBytes){FROM_ARENA, 0, {0, 0}, arena->used, count}; + arena->used += count; + NEXT; + } + case PURE_OP_INDEX_AT: /* the item at a whole position */ + if (top->num != floor(top->num)) { kind = PURE_FAULT_LIMIT; FAILED; } + if (top->num < 0 || top->num >= (double)pure_list_count(app->lists[arg])) { kind = PURE_FAULT_NOT_FOUND; FAILED; } + top->bytes.owner = (uint16_t)arg; + NEXT; default: break; } double b = stack[--sp].num, a = stack[sp - 1].num, value; diff --git a/core/module.h b/core/module.h index e10894d..b230fc3 100644 --- a/core/module.h +++ b/core/module.h @@ -43,6 +43,7 @@ typedef enum { PURE_OP_I64_ADD, PURE_OP_I64_SUB, PURE_OP_I64_MUL, PURE_OP_I64_DIV, PURE_OP_I64_MOD, PURE_OP_I64_NEG, PURE_OP_I64_COMPARE, PURE_OP_I64_MIN, PURE_OP_I64_MAX, PURE_OP_I64_FROM, PURE_OP_NUM_FROM, PURE_OP_LOOP, PURE_OP_BREAK, + PURE_OP_I64_BITS, PURE_OP_BYTE_AT, PURE_OP_BYTES_OF, PURE_OP_INDEX_AT, PURE_OP_COUNT } PureOpcode; /* A bool is its own type (docs/design/016-bool-and-i64.md), held as a @@ -59,7 +60,19 @@ typedef enum { * DIVIDE by zero; COMPARE takes a relation as TEXT_COMPARE does and gives a * bool), I64_FROM makes one of a whole number in range and NUM_FROM a * number of one within 2^53 (else LIMIT). LOAD, STORE, the item and local - * opcodes, FIELD and SEND move them as they move numbers. */ + * opcodes, FIELD and SEND move them as they move numbers. + * I64_BITS computes on two i64s as on integers in two's complement without + * end (docs/design/027): its argument says how (PureBits). AND, OR and XOR + * work bit by bit; LEFT is a * 2^b, failing as OVERFLOW past the range; + * RIGHT is floor(a / 2^b); a shift by b outside 0-63 fails as LIMIT. + * BYTE_AT gives the byte of a bytes value at a position (argument 0: a + * number, 1: an i64) as an i64 0-255, failing as LIMIT where there is none + * (not a whole number, or outside the value). BYTES_OF makes the bytes of a + * list state whose record is one i64 field, an item a byte (a value outside + * 0-255 fails as LIMIT). INDEX_AT makes the index of a list state's item at + * a position (a number), failing as LIMIT if it is not a whole number and + * as NOT_FOUND where there is no item. */ +typedef enum { PURE_BITS_AND, PURE_BITS_OR, PURE_BITS_XOR, PURE_BITS_LEFT, PURE_BITS_RIGHT } PureBits; /* A local: one of an event's PURE_MODULE_LOCALS slots, 0 when the event * begins and never saved. LOCAL_LOAD and LOCAL_STORE name the slot (bits * 0-7), what it holds (bits 8-15: 0 a number, 1 an index, 2 a bool) and an index's diff --git a/core/test-module.c b/core/test-module.c index 42f01af..dca1b67 100644 --- a/core/test-module.c +++ b/core/test-module.c @@ -6013,6 +6013,124 @@ static void integers(void) { pure_save_test_checkpoints - checkpoints); } +/* ---- Bits and bytes (docs/design/027, stage 1) --------------------------- + * I64_BITS works on integers in two's complement without end: a left shift + * past the range fails as overflow, a right shift rounds down, a shift + * outside 0-63 fails as limit. BYTE_AT reads the byte at a whole position + * of any bytes value (here a constant's and a text state's), BYTES_OF makes + * the bytes of a list of one i64, INDEX_AT finds a list's item by position, + * failing where there is none. */ +enum { BITS = PURE_OP_I64_BITS, BAT = PURE_OP_BYTE_AT, BOF = PURE_OP_BYTES_OF, XAT = PURE_OP_INDEX_AT }; + +/* Code run as a key's handler over n (a number), note (text "Az\xc3\xa9"), + * bytes (a list of Byte { v: i64 }), i (an i64) and said (text): the fault, + * and n, i, said and the list's count after it. */ +typedef struct { PureFault fault; double n; int64_t i; char said[64]; size_t count; } Byted; +static Byted run_bytes(const O *code, uint32_t count) { + static const S nums[] = {{"n", 0}}; + static const T others[] = {{"note", 1, 0}, {"bytes", 3, 0}, {"i", PURE_STATE_I64, 0}, {"said", 1, 1}}; + static const RF byte_fields[] = {{"v", 3, 0}}; + static const R records[] = {{"Byte", byte_fields, 1}}; + static const char *const constants[] = {"Az\xc3\xa9", ""}; + static const I inputs[] = {KEY("go", "KeyG")}; + H handler = {1, 0, count}; + Spec spec = {nums, 1, inputs, 1, NULL, 0, &handler, 1, code, count, NULL, 0, others, LEN(others), constants, + LEN(constants), NULL, 0, records, 1, NULL, 0}; + 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, + 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); + return ran; +} + +static void bits_and_bytes(void) { + enum { AND = PURE_BITS_AND, OR = PURE_BITS_OR, XOR = PURE_BITS_XOR, LEFT = PURE_BITS_LEFT, RIGHT = PURE_BITS_RIGHT }; + static const struct { uint32_t how; int64_t a, b; PureFault fault; int64_t result; } cases[] = { + {AND, 0xF0F0, 0x0FF0, PURE_FAULT_NONE, 0x00F0}, {AND, -1, 5, PURE_FAULT_NONE, 5}, + {AND, INT64_MIN, -1, PURE_FAULT_NONE, INT64_MIN}, {AND, -8, 0xFF, PURE_FAULT_NONE, 0xF8}, + {OR, 0xF000, 0x000F, PURE_FAULT_NONE, 0xF00F}, {OR, -8, 3, PURE_FAULT_NONE, -5}, + {XOR, 5, -1, PURE_FAULT_NONE, -6}, {XOR, 0x55, 0xFF, PURE_FAULT_NONE, 0xAA}, {XOR, INT64_MAX, -1, PURE_FAULT_NONE, INT64_MIN}, + {LEFT, 1, 62, PURE_FAULT_NONE, INT64_C(1) << 62}, {LEFT, 1, 63, PURE_FAULT_OVERFLOW, 0}, + {LEFT, -1, 63, PURE_FAULT_NONE, INT64_MIN}, {LEFT, 3, 62, PURE_FAULT_OVERFLOW, 0}, {LEFT, 0, 63, PURE_FAULT_NONE, 0}, + {LEFT, 0x80000000, 32, PURE_FAULT_OVERFLOW, 0}, {LEFT, 0xFFFFFFFF, 7, PURE_FAULT_NONE, INT64_C(0x7FFFFFFF80)}, + {LEFT, -3, 2, PURE_FAULT_NONE, -12}, {LEFT, 5, -1, PURE_FAULT_LIMIT, 0}, {LEFT, 5, 64, PURE_FAULT_LIMIT, 0}, + {RIGHT, 7, 1, PURE_FAULT_NONE, 3}, {RIGHT, -7, 1, PURE_FAULT_NONE, -4}, {RIGHT, INT64_MIN, 63, PURE_FAULT_NONE, -1}, + {RIGHT, -1, 63, PURE_FAULT_NONE, -1}, {RIGHT, 1, 63, PURE_FAULT_NONE, 0}, {RIGHT, INT64_MAX, 0, PURE_FAULT_NONE, INT64_MAX}, + {RIGHT, 5, 64, PURE_FAULT_LIMIT, 0}, {RIGHT, 5, -64, PURE_FAULT_LIMIT, 0}, + }; + for (size_t k = 0; k < LEN(cases); ++k) { + O code[] = {IC(cases[k].a), IC(cases[k].b), {BITS, cases[k].how, 0}, {ST, 1, 0}}; + Ran ran = run_code(code, LEN(code)); + if (ran.fault != cases[k].fault || ran.values[1].i64 != (cases[k].fault ? 0 : cases[k].result)) { + fprintf(stderr, "bits case %zu: fault %d, %" PRId64 "\n", k, (int)ran.fault, ran.values[1].i64); + abort(); + } + } + /* A byte at a position: of a constant's bytes, of a text state's, by a number or an i64. */ + static const struct { uint32_t text_state; uint32_t as_i64; double at; PureFault fault; int64_t byte; } reads[] = { + {0, 0, 0, PURE_FAULT_NONE, 'A'}, {0, 0, 3, PURE_FAULT_NONE, 0xA9}, {0, 1, 2, PURE_FAULT_NONE, 0xC3}, + {0, 0, 4, PURE_FAULT_LIMIT, 0}, {0, 0, -1, PURE_FAULT_LIMIT, 0}, {0, 0, 1.5, PURE_FAULT_LIMIT, 0}, + {0, 1, 4, PURE_FAULT_LIMIT, 0}, {0, 1, -1, PURE_FAULT_LIMIT, 0}, {1, 0, 1, PURE_FAULT_NONE, 'z'}, + {1, 1, 3, PURE_FAULT_NONE, 0xA9}, {1, 0, 4, PURE_FAULT_LIMIT, 0}, + }; + for (size_t k = 0; k < LEN(reads); ++k) { + O position = reads[k].as_i64 ? IC((int64_t)reads[k].at) : (O){C, 0, reads[k].at}; + O code[] = {reads[k].text_state ? (O){TL, 1, 0} : (O){TK, 0, 0}, {PURE_OP_BYTES_FROM, 0, 0}, position, + {BAT, reads[k].as_i64, 0}, {ST, 3, 0}}; + Byted ran = run_bytes(code, LEN(code)); + if (ran.fault != reads[k].fault || ran.i != reads[k].byte) { + fprintf(stderr, "byte read %zu: fault %d, %" PRId64 "\n", k, (int)ran.fault, ran.i); + abort(); + } + } + /* Bytes of a list of one i64: said = text(bytes(list)); a value past a byte fails and leaves nothing. */ + O made[] = {IC('H'), {LA, 2, 0}, IC('i'), {LA, 2, 0}, {BOF, 2, 0}, {PURE_OP_TEXT_FROM, 0, 0}, {PURE_OP_TEXT_SET, 4, 0}}; + Byted ran = run_bytes(made, LEN(made)); + CHECK(ran.fault == PURE_FAULT_NONE && !strcmp(ran.said, "Hi") && ran.count == 2); + O none[] = {{BOF, 2, 0}, {PURE_OP_TEXT_FROM, 0, 0}, {PURE_OP_TEXT_SET, 4, 0}}; + ran = run_bytes(none, LEN(none)); + CHECK(ran.fault == PURE_FAULT_NONE && !strcmp(ran.said, "") && ran.count == 0); + O past[] = {IC('H'), {LA, 2, 0}, IC(256), {LA, 2, 0}, {BOF, 2, 0}, {PURE_OP_TEXT_FROM, 0, 0}, {PURE_OP_TEXT_SET, 4, 0}}; + ran = run_bytes(past, LEN(past)); + CHECK(ran.fault == PURE_FAULT_LIMIT && !strcmp(ran.said, "") && ran.count == 0); + O negative[] = {IC(-1), {LA, 2, 0}, {BOF, 2, 0}, {PURE_OP_TEXT_FROM, 0, 0}, {PURE_OP_TEXT_SET, 4, 0}}; + CHECK(run_bytes(negative, LEN(negative)).fault == PURE_FAULT_LIMIT); + /* An item by position: i = bytes[at].v, where there is one. */ + static const struct { double at; PureFault fault; int64_t v; } items[] = { + {0, PURE_FAULT_NONE, 7}, {1, PURE_FAULT_NONE, 9}, {2, PURE_FAULT_NOT_FOUND, 0}, {-1, PURE_FAULT_NOT_FOUND, 0}, + {0.5, PURE_FAULT_LIMIT, 0}}; + for (size_t k = 0; k < LEN(items); ++k) { + O code[] = {IC(7), {LA, 2, 0}, IC(9), {LA, 2, 0}, {C, 0, items[k].at}, {XAT, 2, 0}, {IG, ITEM(2, 0), 0}, {ST, 3, 0}}; + Byted at = run_bytes(code, LEN(code)); + CHECK(at.fault == items[k].fault && at.i == items[k].v && at.count == (items[k].fault ? 0 : 2)); + } + /* Refused: bits of numbers or of no such kind, a byte of a text, by a position of the other type or of no such kind, + * bytes of a number state, an item by an i64's position. */ + const O refused[][4] = { + {{C, 0, 1}, {C, 0, 2}, {BITS, AND, 0}, {ST, 0, 0}}, {IC(1), IC(2), {BITS, RIGHT + 1, 0}, {ST, 3, 0}}, + {{TK, 0, 0}, {C, 0, 0}, {BAT, 0, 0}, {ST, 3, 0}}, {{TK, 0, 0}, {PURE_OP_BYTES_FROM, 0, 0}, IC(0), {BAT, 0, 0}}, + {{TK, 0, 0}, {PURE_OP_BYTES_FROM, 0, 0}, {C, 0, 0}, {BAT, 2, 0}}, {{BOF, 0, 0}, {PURE_OP_TEXT_FROM, 0, 0}, {PURE_OP_TEXT_SET, 4, 0}}, + {IC(0), {XAT, 2, 0}, {IG, ITEM(2, 0), 0}, {ST, 3, 0}}, + }; + for (size_t k = 0; k < LEN(refused); ++k) { + static const S nums[] = {{"n", 0}}; + static const T others[] = {{"note", 1, 0}, {"bytes", 3, 0}, {"i", PURE_STATE_I64, 0}, {"said", 1, 1}}; + static const RF byte_fields[] = {{"v", 3, 0}}; + static const R records[] = {{"Byte", byte_fields, 1}}; + static const char *const constants[] = {"Az\xc3\xa9", ""}; + static const I inputs[] = {KEY("go", "KeyG")}; + uint32_t length = k == 5 ? 3 : 4; + H handler = {1, 0, length}; + Spec spec = {nums, 1, inputs, 1, NULL, 0, &handler, 1, refused[k], length, NULL, 0, others, LEN(others), constants, + LEN(constants), NULL, 0, records, 1, NULL, 0}; + if (load(spec, &module_b) == PURE_OK) { fprintf(stderr, "bits and bytes: refused %zu loaded\n", k); abort(); } + } + printf("{\"bits_and_bytes\":{\"bits\":%zu,\"reads\":%zu,\"refused\":%zu}}\n", LEN(cases), LEN(reads), LEN(refused)); +} + /* A timer's tick sees the time the host set; floor and a floored modulo * take it apart. now is for handlers only. */ enum { NOW = PURE_OP_NOW, FLOOR = PURE_OP_FLOOR, MOD = PURE_OP_MOD }; @@ -6386,6 +6504,7 @@ int main(void) { output_meaning(); bools(); integers(); + bits_and_bytes(); memory_budget(); work_budget(); fold_mutations(); -- cgit v1.2.3