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/test-module.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) (limited to 'core/test-module.c') 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