From bfa54e02881f56b3a969220ecaac706542046149 Mon Sep 17 00:00:00 2001 From: eunhhu Date: Wed, 30 Sep 2026 15:24:56 +0100 Subject: Give bools their own type in the module format (v6, 016 stage 1) A condition was a number, non-zero being true: a flag was a number field, a view showed 1 for true, and nothing kept a number from standing for a bool or a bool from being added to. Now the verifier has a bool among its stack types, and a verified module never mixes the two. No opcode is new: - CONST with argument 1 is a bool (+0 or 1), NUM_TEXT with argument 1 writes one as true or false; LOAD and STORE take a number or a bool state, locals may hold a bool (2). - Comparisons (LT to NE, TEXT_COMPARE) and LIST_HAS give a bool; AND, OR and NOT take and give them; JZ, REQUIRE, filters and folds' tests take one (count and first leave a bool, sum, max, min and distinct a number and a bool); EQ and NE compare two bools too. - Type codes: state 5, record field 2, message field 6 (one byte in the prefix, 0 or 1, or the message fails as limit), view 3. A bool's constant, initial value and default are +0 or 1; a list is not keyed by one. PUREMOD6: v5 modules are built again. At run time a bool is a number's 0 or 1, so leaf-at-a-time folds run as before. A number that becomes a bool (a module replaced, a stored state or item read again, a waiting send recast) keeps 0 (either sign) and 1 and takes the default otherwise; a bool that becomes a number is 0 or 1. The host carries bools on every boundary: JSON true and false only (a row's field too), a query's true and false, true and false in views, table JSON and HTTP deliveries; a link takes a bool only into a bool. A stored outbox's field types were read back with a check that refused bool (at or past rows): a node whose output had a bool field and a send waiting could not start again. test-module now stores such a send and opens it (it fails on the old check), beside 28 bool refusals of the verifier and bool states, fields, views, sends, links and migration. The lane generator makes numbers and bools apart. --- core/module.h | 64 +++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 21 deletions(-) (limited to 'core/module.h') diff --git a/core/module.h b/core/module.h index 2cac3b3..0634ed1 100644 --- a/core/module.h +++ b/core/module.h @@ -8,7 +8,7 @@ #include #include -/* Pure module format v5 (docs/design/008-module-format.md). A toolchain such +/* Pure module format v6 (docs/design/008-module-format.md). A toolchain such * as aup produces it; Pure verifies it completely before anything executes: * operand types (including which text a cursor and which list an index * belongs to), stack heights and the stack at every join, forward-only @@ -42,9 +42,16 @@ typedef enum { PURE_OP_LOCAL_LOAD, PURE_OP_LOCAL_STORE, PURE_OP_COUNT } PureOpcode; +/* A bool is its own type (docs/design/016-bool-and-i64.md), held as a + * number 0 or 1 while it runs: comparisons (LT to NE, TEXT_COMPARE) and + * LIST_HAS push one, AND, OR and NOT work on them, JZ and REQUIRE take one, + * and EQ and NE compare two bools as they compare two numbers. CONST with + * argument 1 pushes a bool (its value 0 or 1); LOAD and STORE work on a + * number or a bool state as its type says; NUM_TEXT with argument 1 makes + * a bool text (true, false). */ /* 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) and an index's + * 0-7), what it holds (bits 8-15: 0 a number, 1 an index, 2 a bool) and an index's * list (bits 16-31); a handler uses a slot for one of them. A handler, the * walks in it and the folds in those read locals; the handler and its walks * store them. */ @@ -56,10 +63,10 @@ enum { PURE_MODULE_LOCALS = 64 }; * (an item it removes, or one before it, moves the walk with it). */ /* FOLD's argument: the kind (bits 0-7), the list state (8-15) and the length * of the block that follows (16-31), run once per item as a column of that - * list. count counts the items whose block leaves a non-zero number; sum, - * max, min and distinct take a value and an include flag from their block - * (distinct counts the different values, -0 as 0); first gives the index of - * the first item whose block leaves a non-zero number. */ + * list. count counts the items whose block leaves true; sum, max, min and + * distinct take a number and whether to include it (a bool) from their + * block (distinct counts the different values, -0 as 0); first gives the + * index of the first item whose block leaves true. */ typedef enum { PURE_FOLD_COUNT, PURE_FOLD_SUM, PURE_FOLD_MAX, PURE_FOLD_MIN, PURE_FOLD_FIRST, PURE_FOLD_DISTINCT } PureFold; /* A slice: a fold whose kind has PURE_FOLD_SLICED, or a walk whose argument * has PURE_EACH_SLICED, first takes two numbers, a low and a high key, and @@ -82,19 +89,31 @@ typedef enum { PURE_KIND_KEY = 1, PURE_KIND_MESSAGE = 2, PURE_KIND_TEXT = 3, PUR enum { PURE_TIMER_LONGEST = 86400000, PURE_SOURCE = 64 }; /* The largest number a delivery may have: it is kept as a binary64. */ #define PURE_SEQ_MOST (UINT64_C(1) << 53) -/* A message field: a u32le or a binary64 (fixed, at its offset in the - * payload's prefix), or bytes, text or rows of any length. The prefix also - * holds each variable field's u32le length but the last one's (its offset is - * PURE_FIELD_REST), which takes what follows the others; the variable - * fields' bytes follow the prefix in declaration order. A text is UTF-8 and - * a number finite, or the message fails. Rows are records of a list of a - * message's rows (rows, a list state), laid out as a list's stored form: a - * message fills that list with them, and fails when they are not whole. */ +/* A message field: a u32le, a binary64 or a bool's one byte (fixed, at its + * offset in the payload's prefix), or bytes, text or rows of any length. The + * prefix also holds each variable field's u32le length but the last one's + * (its offset is PURE_FIELD_REST), which takes what follows the others; the + * variable fields' bytes follow the prefix in declaration order. A text is + * UTF-8, a number finite and a bool 0 or 1, or the message fails. Rows are + * records of a list of a message's rows (rows, a list state), laid out as a + * list's stored form: a message fills that list with them, and fails when + * they are not whole. */ typedef enum { PURE_FIELD_U32 = 1, PURE_FIELD_BYTES = 2, PURE_FIELD_NUMBER = 3, PURE_FIELD_TEXT = 4, - PURE_FIELD_ROWS = 5 } PureFieldType; + PURE_FIELD_ROWS = 5, PURE_FIELD_BOOL = 6 } PureFieldType; enum { PURE_FIELD_REST = UINT32_MAX }; +/* A field held in its payload's prefix (a u32, a number, a bool), and the bytes it takes there. */ +static inline bool pure_field_fixed(uint8_t type) { + return type == PURE_FIELD_U32 || type == PURE_FIELD_NUMBER || type == PURE_FIELD_BOOL; +} +static inline uint32_t pure_field_size(uint8_t type) { + return type == PURE_FIELD_NUMBER ? 8 : type == PURE_FIELD_BOOL ? 1 : 4; +} +/* A number as a bool: 0 (of either sign) is false, 1 true, any other the + * default given. A state or field that becomes a bool keeps its value so. */ +static inline double pure_as_bool(double value, double otherwise) { return value == 0 ? 0 : value == 1 ? 1 : otherwise; } typedef enum { - PURE_STATE_NUM = 0, PURE_STATE_TEXT = 1, PURE_STATE_CURSOR = 2, PURE_STATE_LIST = 3, PURE_STATE_INDEX = 4 + PURE_STATE_NUM = 0, PURE_STATE_TEXT = 1, PURE_STATE_CURSOR = 2, PURE_STATE_LIST = 3, PURE_STATE_INDEX = 4, + PURE_STATE_BOOL = 5 } PureStateType; /* A cursor that stays before text inserted at it (a mark); other cursors go * after it (a caret). A list of a message's rows (its state's flags hold @@ -103,9 +122,9 @@ typedef enum { * every event ends, and it is never saved nor logged for undoing. */ enum { PURE_CURSOR_STAYS = 1, PURE_LIST_ROWS = 256 }; /* A table view has one row per item of a list, or per item its filter - * keeps; its columns are number or text views evaluated for one row at a - * time. */ -typedef enum { PURE_VIEW_NUM = 0, PURE_VIEW_TEXT = 1, PURE_VIEW_TABLE = 2 } PureViewType; + * keeps; its columns are number, bool or text views evaluated for one row at + * a time. */ +typedef enum { PURE_VIEW_NUM = 0, PURE_VIEW_TEXT = 1, PURE_VIEW_TABLE = 2, PURE_VIEW_BOOL = 3 } PureViewType; typedef enum { PURE_UNIT_CHAR = 0, PURE_UNIT_LINE = 1 } PureUnit; typedef enum { PURE_EDGE_DOWN, PURE_EDGE_UP } PureEdge; /* EXISTS: an item with that key is already in the keyed list (the event @@ -198,7 +217,8 @@ typedef struct { PureRecord record[PURE_MODULE_RECORDS]; char record_field_names[PURE_MODULE_RECORD_FIELDS][PURE_MODULE_NAME]; uint8_t record_field_text[PURE_MODULE_RECORD_FIELDS]; - double record_field_default[PURE_MODULE_RECORD_FIELDS]; /* number, or text constant index */ + uint8_t record_field_bool[PURE_MODULE_RECORD_FIELDS]; /* a bool: a number field (0 or 1) as lists go */ + double record_field_default[PURE_MODULE_RECORD_FIELDS]; /* number (a bool's 0 or 1), or text constant index */ } PureModule; /* Decode and verify into out, which must be zeroed (a static one is) or hold @@ -422,7 +442,8 @@ PureResult pure_app_message_fields(PureApp *, uint32_t input, const char *key, i * next event (deferred: copy it before a later save too). */ PureResult pure_app_reply(const PureApp *, bool (*visit)(void *, PureSpan), void *context); void pure_app_finish(PureApp *); -/* A number view; false for a text view or a failed evaluation. */ +/* A number view, or a bool view (0 or 1: its type says which); false for a + * text view or a failed evaluation. */ bool pure_app_view(PureApp *, uint32_t view, double *out); /* A text view, lent in order until the next view evaluation. */ PureResult pure_app_view_text(PureApp *, uint32_t view, bool (*visit)(void *, PureSpan), void *context); @@ -444,6 +465,7 @@ unsigned pure_app_passes(const PureApp *); * its filter leaves the item out). Without a filter both are the identity. */ bool pure_app_row_item(PureApp *, uint32_t table, size_t row, size_t *item); bool pure_app_item_row(PureApp *, uint32_t table, size_t item, size_t *row); +/* A number or bool column's cell (as pure_app_view), or a text column's. */ bool pure_app_cell(PureApp *, uint32_t column, size_t row, double *out); PureResult pure_app_cell_text(PureApp *, uint32_t column, size_t row, bool (*visit)(void *, PureSpan), void *context); /* Switch to another verified module. States keep their value by name (text -- cgit v1.2.3