From 24d95e19b4eec44c5f8c9f551ebf4e8fc465d5f5 Mon Sep 17 00:00:00 2001 From: eunhhu Date: Wed, 30 Sep 2026 16:32:17 +0100 Subject: Give aup an i64: exact integers, and literals typed by what they meet state, record, message, output and argument fields, lets, views, keys and slices can be i64 (Pure 016 stage 2). An integer literal becomes an i64 where the other side of an operation, a comparison or an assignment is one; a literal-only side follows the other, found by compiling it on trial and rolling back. + - * and negation fail the event with overflow, / and % floor, and i64(x) and number(n) are the only way between the two, so a mix is a diagnostic that says so. The check adds 18 i64 diagnostics, i64 accepted forms (with a 30-deep chain that must stay linear), an event-by-event program past 2^53 and at the edges, and i64 values in the generated programs. --- compiler/compile.c | 419 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 320 insertions(+), 99 deletions(-) (limited to 'compiler') diff --git a/compiler/compile.c b/compiler/compile.c index 6a690d5..0c71bc3 100644 --- a/compiler/compile.c +++ b/compiler/compile.c @@ -18,19 +18,20 @@ enum { CONST, LOAD, DT, ADD, SUB, MUL, DIV, NEG, LT, LE, GT, GE, EQ, NE, AND, OR TEXT_SET, TEXT_FROM, BYTES_FROM, CUR_COUNT, LIST_COUNT, LIST_APPEND, LIST_INSERT, LIST_REMOVE, ITEM_GET, ITEM_SET, INDEX_LOAD, INDEX_STORE, INDEX_MOVE, INDEX_FIRST, INDEX_LAST, INDEX_NUM, ROW, PICK, SLIDE, FOLD, TEXT_COMPARE, LIST_FIND, LIST_HAS, NOW, FLOOR, MOD, SEND, OUTER_ROW, EACH, - LOCAL_LOAD, LOCAL_STORE }; + LOCAL_LOAD, LOCAL_STORE, I64_ADD, I64_SUB, I64_MUL, I64_DIV, I64_MOD, I64_NEG, I64_COMPARE, I64_MIN, I64_MAX, + I64_FROM, NUM_FROM }; enum { LOCALS = 64 }; /* an event's locals (Pure's) */ -enum { FOLD_COUNT, FOLD_SUM, FOLD_MAX, FOLD_MIN, FOLD_FIRST, FOLD_DISTINCT }; +enum { FOLD_COUNT, FOLD_SUM, FOLD_MAX, FOLD_MIN, FOLD_FIRST, FOLD_DISTINCT, FOLD_INTEGER = 0x40 }; enum { KIND_KEY = 1, KIND_MESSAGE = 2, KIND_TEXT = 3, KIND_TIMER = 4, KIND_OUTPUT = 5, KIND_ARGUMENTS = 6, FIELD_U32 = 1, FIELD_BYTES = 2, FIELD_NUMBER = 3, FIELD_TEXT = 4, - FIELD_ROWS = 5, FIELD_BOOL = 6 }; + FIELD_ROWS = 5, FIELD_BOOL = 6, FIELD_I64 = 7 }; /* A list of a message's rows: filled as the message arrives, emptied when its event ends (Pure's flag). */ enum { LIST_ROWS = 256 }; -enum { STATE_NUM = 0, STATE_TEXT = 1, STATE_CURSOR = 2, STATE_LIST = 3, STATE_INDEX = 4, STATE_BOOL = 5, +enum { STATE_NUM = 0, STATE_TEXT = 1, STATE_CURSOR = 2, STATE_LIST = 3, STATE_INDEX = 4, STATE_BOOL = 5, STATE_I64 = 6, UNIT_CHAR = 0, UNIT_LINE = 1 }; -/* Pure's view types; a bool record field is type 2 and a bool local holds 2. */ -enum { VIEW_NUM = 0, VIEW_TEXT = 1, VIEW_TABLE = 2, VIEW_BOOL = 3, FIELD_RECORD_BOOL = 2, LOCAL_BOOL = 2 }; -typedef enum { NUM = 1, BYTES, TEXT, CURSOR, UNIT, INDEX, BOOL } Type; +/* Pure's view types, record field types (0 a number, 1 a text, 2 a bool, 3 an i64) and what a local holds. */ +enum { VIEW_NUM = 0, VIEW_TEXT = 1, VIEW_TABLE = 2, VIEW_BOOL = 3, VIEW_I64 = 4, LOCAL_BOOL = 2, LOCAL_I64 = 3 }; +typedef enum { NUM = 1, BYTES, TEXT, CURSOR, UNIT, INDEX, BOOL, I64 } Type; typedef struct { Type type; int owner; } Kind; /* owner: a cursor's text state or an index's list state */ typedef enum { T_EOF, T_NL, T_NUMBER, T_NAME, T_PUNCT, T_STRING } TokenKind; @@ -38,6 +39,8 @@ typedef struct { TokenKind kind; int line, column; double number; + int64_t integer; /* a literal of digits alone (whole): its exact value, when not past an i64's range (wide) */ + bool whole, wide; char text[NAME + 1]; /* name, or punctuation of one or two characters */ size_t start, end; /* string contents in the source */ } Token; @@ -63,6 +66,8 @@ typedef struct Expr { ExprKind kind; int line, column, op; double number; + int64_t integer; /* E_NUMBER: as its token's */ + bool whole, wide; char name[NAME], member[NAME]; struct Expr *args[PARAMS], *next; unsigned count; @@ -97,13 +102,14 @@ typedef struct { unsigned view; /* arguments: the view they are for */ } Input; -/* A record: fields in order, each a number, a bool or text with its default. */ +/* A record: fields in order, each a number, a bool, an i64 or text with its default. */ typedef struct { char name[NAME]; int line, column; char field_names[FIELDS_PER_RECORD][NAME]; - bool field_text[FIELDS_PER_RECORD], field_bool[FIELDS_PER_RECORD]; + Type field_kinds[FIELDS_PER_RECORD]; /* NUM, BOOL, I64 or TEXT */ double field_number[FIELDS_PER_RECORD]; + int64_t field_integer[FIELDS_PER_RECORD]; Expr *field_string[FIELDS_PER_RECORD]; /* a text default, or NULL for "" */ uint32_t field_constant[FIELDS_PER_RECORD]; unsigned fields; @@ -144,6 +150,7 @@ typedef struct { char state_key[STATES][NAME]; int state_key_line[STATES], state_key_column[STATES], list_key[STATES]; double initial[STATES]; + int64_t state_integer[STATES]; /* an i64 state's start */ Expr *state_text[STATES]; unsigned states; char collection_names[COLLECTIONS][NAME]; @@ -191,7 +198,8 @@ typedef struct { unsigned fn_count; Scope *scope; /* the fn being inlined, or NULL */ unsigned joins; /* branches ahead that carry a value */ - struct { uint8_t op; uint32_t arg; double value; } code[CODE]; + Type want; /* what the expression value() compiles next is wanted as: an i64 makes a literal one */ + struct { uint8_t op; uint32_t arg; union { double value; int64_t integer; }; } code[CODE]; unsigned code_count; uint32_t view_code[VIEWS][2], handler_code[HANDLERS][2]; int view_types[VIEWS]; @@ -299,6 +307,15 @@ static Token scan(Compiler *c) { token.kind = T_NUMBER; token.number = strtod(text, NULL); if (!isfinite(token.number)) fail_at(c, token.line, token.column, "number must be finite"); + /* Digits alone: exactly, for an i64 (past its range, wide). */ + token.whole = strspn(text, "0123456789") == n; + uint64_t exact = 0; + for (size_t i = 0; token.whole && i < n && !token.wide; ++i) { + unsigned digit = (unsigned)(text[i] - '0'); + token.wide = exact > (UINT64_C(9223372036854775807) - digit) / 10; + exact = exact * 10 + digit; + } + token.integer = token.wide ? 0 : (int64_t)exact; return token; } if (letter(ch)) { @@ -695,6 +712,9 @@ static Expr *expression(Compiler *c, int minimum, bool multiline) { if (token.kind == T_NUMBER) { left = node(c, E_NUMBER, token.line, token.column); left->number = token.number; + left->integer = token.integer; + left->whole = token.whole; + left->wide = token.wide; } else if (token.kind == T_STRING) { left = string(c, &token); } else if (token.kind == T_PUNCT && (!strcmp(token.text, "-") || !strcmp(token.text, "!"))) { @@ -702,6 +722,13 @@ static Expr *expression(Compiler *c, int minimum, bool multiline) { left->op = token.text[0] == '-' ? NEG : NOT; left->args[0] = expression(c, 7, multiline); left->count = 1; + if (left->op == NEG && left->args[0]->kind == E_NUMBER) { /* -5: one literal, a number or an i64 */ + Expr *literal = left->args[0]; + literal->number = -literal->number; + literal->integer = -literal->integer; + literal->line = left->line, literal->column = left->column; + left = literal; + } } else if (token.kind == T_PUNCT && !strcmp(token.text, "(")) { left = expression(c, 0, true); newlines(c); @@ -712,7 +739,7 @@ static Expr *expression(Compiler *c, int minimum, bool multiline) { } else if (token.kind == T_NAME) { static const struct { const char *name; unsigned count; int op; } calls[] = {{"min", 2, MIN}, {"max", 2, MAX}, {"clamp", 3, CLAMP}, {"text", 1, TEXT_FROM}, {"bytes", 1, BYTES_FROM}, - {"floor", 1, FLOOR}}; + {"floor", 1, FLOOR}, {"i64", 1, I64_FROM}, {"number", 1, NUM_FROM}}; int call = -1, how = -1; for (int i = 0; i < (int)(sizeof(calls) / sizeof(*calls)); ++i) if (!strcmp(token.text, calls[i].name)) call = i; static const char *const folds[] = {"count", "sum", "max", "min", "first", "distinct"}; @@ -1097,7 +1124,7 @@ static int used_library(const Compiler *c, int module, const char *name) { } static const char *declared_kind(const Compiler *c, const char *name) { - static const char *const states[] = {"a state", "a text state", "a cursor", "a list", "a list index", "a state"}; + static const char *const states[] = {"a state", "a text state", "a cursor", "a list", "a list index", "a state", "a state"}; if (fn_index(c, c->module, name) >= 0) return "a fn"; if (used_library(c, c->module, name) >= 0) return "a library"; if (c->module) return NULL; /* a library has only fns and uses */ @@ -1188,9 +1215,10 @@ static void message_fields(Compiler *c, Input *in, const char *group) { continue; } if (c->field_count == FIELDS) fail_at(c, name.line, name.column, "at most %d message fields are allowed", FIELDS); - Token type = expect_name(c, "u32, number, bool, bytes, text or list of a record"); + Token type = expect_name(c, "u32, i64, number, bool, bytes, text or list of a record"); static const struct { const char *name; int type; } types[] = { - {"u32", FIELD_U32}, {"number", FIELD_NUMBER}, {"bool", FIELD_BOOL}, {"bytes", FIELD_BYTES}, {"text", FIELD_TEXT}}; + {"u32", FIELD_U32}, {"i64", FIELD_I64}, {"number", FIELD_NUMBER}, {"bool", FIELD_BOOL}, {"bytes", FIELD_BYTES}, + {"text", FIELD_TEXT}}; int field_type = 0; for (size_t t = 0; t < sizeof(types) / sizeof(*types); ++t) if (!strcmp(type.text, types[t].name)) field_type = types[t].type; @@ -1204,7 +1232,7 @@ static void message_fields(Compiler *c, Input *in, const char *group) { in->field_column[in->fields] = record.column; field_type = FIELD_ROWS; } - if (!field_type) fail_at(c, type.line, type.column, "field types are u32, number, bool, bytes, text and list of a record"); + if (!field_type) fail_at(c, type.line, type.column, "field types are u32, i64, number, bool, bytes, text and list of a record"); memcpy(in->field_names[in->fields], path, NAME); in->field_types[in->fields++] = field_type; ++c->field_count; @@ -1223,6 +1251,17 @@ static double signed_number(Compiler *c, const char *what) { return sign * take(c).number; } +/* A signed integer literal in an i64's range: an i64 state's or field's start. */ +static int64_t signed_integer(Compiler *c, const char *what) { + bool negative = false; + if (is(c, "-") || is(c, "+")) negative = take(c).text[0] == '-'; + if (c->token.kind != T_NUMBER || !c->token.whole) + fail_at(c, c->token.line, c->token.column, "expected a whole number for %s", what); + if (c->token.wide) fail_at(c, c->token.line, c->token.column, "the number is past an i64's range"); + int64_t value = take(c).integer; + return negative ? -value : value; +} + /* A plain string with no {expressions}: a text state or field default. */ static Expr *plain_string(Compiler *c, const char *what) { if (c->token.kind != T_STRING) fail_at(c, c->token.line, c->token.column, "%s starts from a string", what); @@ -1259,18 +1298,21 @@ static void record_declaration(Compiler *c) { ++c->record_fields; if (is(c, ":")) { take(c); - Token type = expect_name(c, "text"); - if (strcmp(type.text, "text")) - fail_at(c, type.line, type.column, "a field is a number (field = 0), a bool (field = false) or text (field: text)"); - r->field_text[index] = true; + Token type = expect_name(c, "text or i64"); + if (strcmp(type.text, "text") && strcmp(type.text, "i64")) + fail_at(c, type.line, type.column, + "a field is a number (field = 0), a bool (field = false), text (field: text) or an i64 (field: i64)"); + r->field_kinds[index] = type.text[0] == 't' ? TEXT : I64; if (is(c, "=")) { take(c); - r->field_string[index] = plain_string(c, "a text field"); + if (r->field_kinds[index] == TEXT) r->field_string[index] = plain_string(c, "a text field"); + else r->field_integer[index] = signed_integer(c, "an i64 field's default"); } } else { - expect(c, "=", "'=' and a number or a bool, or ': text'"); + expect(c, "=", "'=' and a number or a bool, or ': text' or ': i64'"); + r->field_kinds[index] = NUM; if (word(c, "true") || word(c, "false")) { - r->field_bool[index] = true; + r->field_kinds[index] = BOOL; r->field_number[index] = take(c).text[0] == 't'; } else r->field_number[index] = signed_number(c, "field default"); } @@ -1300,8 +1342,14 @@ static void state_declaration(Compiler *c) { c->state_column[index] = name.column; if (is(c, ":")) { take(c); - Token type = expect_name(c, "text, cursor, mark, list or index"); - if (!strcmp(type.text, "text")) { + Token type = expect_name(c, "text, i64, cursor, mark, list or index"); + if (!strcmp(type.text, "i64")) { /* state n: i64 = 0: exact, whole */ + c->state_types[index] = STATE_I64; + if (is(c, "=")) { + take(c); + c->state_integer[index] = signed_integer(c, "an i64 state"); + } + } else if (!strcmp(type.text, "text")) { c->state_types[index] = STATE_TEXT; if (is(c, "=")) { take(c); @@ -1327,8 +1375,8 @@ static void state_declaration(Compiler *c) { c->state_key_column[index] = key.column; } } else fail_at(c, type.line, type.column, - "state types are numbers, text, cursor or mark in a text, list of a record, index of a list, " - "and bytes by key"); + "state types are numbers, bools, i64, text, cursor or mark in a text, list of a record, index of " + "a list, and bytes by key"); ++c->states; return; } @@ -1499,13 +1547,14 @@ static void view_arguments(Compiler *c, unsigned view, const Token *name) { if (!strcmp(in->field_names[f], argument.text)) fail_at(c, argument.line, argument.column, "duplicate argument '%s'", argument.text); expect(c, ":", "':'"); - Token type = expect_name(c, "u32, number, bool, bytes or text"); + Token type = expect_name(c, "u32, i64, number, bool, bytes or text"); static const struct { const char *name; int type; } types[] = { - {"u32", FIELD_U32}, {"number", FIELD_NUMBER}, {"bool", FIELD_BOOL}, {"bytes", FIELD_BYTES}, {"text", FIELD_TEXT}}; + {"u32", FIELD_U32}, {"i64", FIELD_I64}, {"number", FIELD_NUMBER}, {"bool", FIELD_BOOL}, {"bytes", FIELD_BYTES}, + {"text", FIELD_TEXT}}; int field_type = 0; for (size_t t = 0; t < sizeof(types) / sizeof(*types); ++t) if (!strcmp(type.text, types[t].name)) field_type = types[t].type; - if (!field_type) fail_at(c, type.line, type.column, "an argument is a u32, number, bool, bytes or text"); + if (!field_type) fail_at(c, type.line, type.column, "an argument is a u32, i64, number, bool, bytes or text"); if (c->field_count == FIELDS) fail_at(c, argument.line, argument.column, "at most %d message fields are allowed", FIELDS); memcpy(in->field_names[in->fields], argument.text, NAME); in->field_line[in->fields] = argument.line; @@ -1644,8 +1693,14 @@ static void emit(Compiler *c, int line, int column, uint8_t op, uint32_t arg, do c->code[c->code_count++].value = value; } +/* An i64 constant: CONST with argument 2, its value the integer's 8 bytes. */ +static void emit_integer(Compiler *c, int line, int column, int64_t value) { + emit(c, line, column, CONST, 2, 0); + c->code[c->code_count - 1].integer = value; +} + static const char *type_name(Type type) { - static const char *const names[] = {"", "a number", "bytes", "text", "a cursor", "a distance", "an index", "a bool"}; + static const char *const names[] = {"", "a number", "bytes", "text", "a cursor", "a distance", "an index", "a bool", "an i64"}; return names[type]; } @@ -1715,12 +1770,12 @@ static unsigned record_field(Compiler *c, const Record *r, const char *field, in /* A record field's kind: text, a bool or a number. */ static Type field_kind(const Record *r, unsigned field) { - return r->field_text[field] ? TEXT : r->field_bool[field] ? BOOL : NUM; + return r->field_kinds[field]; } /* The kind of a message field's value (a u32 is a number). */ static Type message_kind(int type) { - return type == FIELD_TEXT ? TEXT : type == FIELD_BYTES ? BYTES : type == FIELD_BOOL ? BOOL : NUM; + return type == FIELD_TEXT ? TEXT : type == FIELD_BYTES ? BYTES : type == FIELD_BOOL ? BOOL : type == FIELD_I64 ? I64 : NUM; } static Kind value(Compiler *c, const Expr *e, bool handler, int height); @@ -1743,6 +1798,111 @@ static void truth(Compiler *c, const Expr *e, bool handler, int height, const ch expect_kind(c, e, kind, BOOL); } +/* An integer literal (digits alone, or negated ones): a number, or an i64 + * where one is wanted. */ +static bool loose(const Expr *e) { return e->kind == E_NUMBER && e->whole; } + +/* The literal e, emitted at code[at] as a number, made the i64 wanted beside it. */ +static void made_i64(Compiler *c, const Expr *e, unsigned at) { + if (e->wide) fail_at(c, e->line, e->column, "the number is past an i64's range"); + c->code[at].arg = 2; + c->code[at].integer = e->integer; +} + +/* e where a value of type want goes (0: any): an integer literal is an i64 + * where an i64 is wanted, and so are the literals of an arithmetic or a + * conditional expression that gives one. */ +static Kind wanted(Compiler *c, const Expr *e, bool handler, int height, Type want) { + if (want == I64 && loose(e)) { + if (height + 1 > STACK) fail_at(c, e->line, e->column, "expression needs more than 64 stack values"); + emit(c, e->line, e->column, CONST, 2, 0); + made_i64(c, e, c->code_count - 1); + return (Kind){I64, -1}; + } + c->want = want; + return value(c, e, handler, height); +} + +/* e where an i64 goes: a number is refused, and the message says how to convert it. */ +static void integer(Compiler *c, const Expr *e, bool handler, int height) { + Kind kind = wanted(c, e, handler, height, I64); + if (kind.type == NUM) + fail_at(c, e->line, e->column, e->kind == E_NUMBER ? "an i64 is a whole number; this one is not" : + "expected an i64, not a number: convert it, as in i64(x)"); + expect_kind(c, e, kind, I64); +} + +/* Integer literals alone, with arithmetic, negation, min, max and + * conditional expressions of them: a number, or an i64 where one is wanted. */ +static bool literal_only(const Expr *e) { + if (loose(e)) return true; + if (e->kind == E_IF) return literal_only(e->args[1]) && literal_only(e->args[2]); /* whatever its condition */ + bool arithmetic = (e->kind == E_BINARY && (e->op == ADD || e->op == SUB || e->op == MUL || e->op == DIV || e->op == MOD) && + e->args[1]->kind != E_UNIT) || + (e->kind == E_CALL && (e->op == MIN || e->op == MAX)) || (e->kind == E_UNARY && e->op == NEG); + for (unsigned i = 0; arithmetic && i < e->count; ++i) arithmetic = literal_only(e->args[i]); + return arithmetic; +} + +/* The type e has, compiled and taken back: what literals beside it take. */ +static Type probe(Compiler *c, const Expr *e, bool handler, int height) { + unsigned code = c->code_count, constants = c->constants, joins = c->joins; + size_t used = c->constant_used; + Type type = value(c, e, handler, height).type; + c->code_count = code, c->constants = constants, c->constant_used = used, c->joins = joins; + return type; +} + +/* What an arithmetic operation's or a comparison's operands are wanted as, + * before the left one is emitted: a number or an i64 when that is wanted of + * the operation (want), else when the left one is literals alone the right + * one's type (probed once: the right one is then compiled as wanted, so no + * probe below it runs again). */ +static Type left_hint(Compiler *c, const Expr *e, bool handler, int height, Type want) { + if (want == I64 || want == NUM) return want; + if (!literal_only(e->args[0]) || literal_only(e->args[1])) return 0; + Type right = probe(c, e->args[1], handler, height + 1); + return right == I64 || right == NUM ? right : 0; +} + +/* The right operand of an arithmetic operation or a comparison whose left + * one, of kind left, was emitted: both numbers or both i64s. Their type. */ +static Type second(Compiler *c, const Expr *e, Kind left, bool handler, int height) { + const Expr *a = e->args[0], *b = e->args[1]; + if (left.type == I64) { + integer(c, b, handler, height + 1); + return I64; + } + expect_kind(c, a, left, NUM); + Kind right = wanted(c, b, handler, height + 1, NUM); + if (right.type == I64) + fail_at(c, b->line, b->column, "an i64 and a number do not mix: convert one, as in i64(x) or number(n)"); + expect_kind(c, b, right, NUM); + return NUM; +} + +static uint8_t i64_op(int op) { + switch (op) { + case ADD: return I64_ADD; + case SUB: return I64_SUB; + case MUL: return I64_MUL; + case DIV: return I64_DIV; + case MOD: return I64_MOD; + case MIN: return I64_MIN; + default: return I64_MAX; + } +} + +/* A conditional expression's branch, of the other branches' type (kind, once the first gives it). */ +static void if_branch(Compiler *c, const Expr *e, bool handler, int height, Type hint, Kind *kind) { + Kind got = wanted(c, e, handler, height, hint); + if (kind->type && (got.type != kind->type || got.owner != kind->owner)) + fail_at(c, e->line, e->column, got.type != kind->type ? "the branches of if give %s and %s" : kind->type == CURSOR ? + "the branches of if give cursors of different texts" : "the branches of if give indices of different lists", + type_name(kind->type), type_name(got.type)); + *kind = got; +} + /* A cursor of the named text state. */ static void cursor_of(Compiler *c, const Expr *e, bool handler, int height, int text, const char *name) { Kind kind = value(c, e, handler, height); @@ -1763,11 +1923,16 @@ static void index_of(Compiler *c, const Expr *e, bool handler, int height, int l /* The item name[e] names: e is an index of the list, or for a keyed list a * key, found by LIST_FIND. */ static void item_of(Compiler *c, const Expr *e, bool handler, int height, int list) { + unsigned at = c->code_count; Kind kind = value(c, e, handler, height); int key = c->list_key[list] - 1; if (kind.type != INDEX && key >= 0) { const Record *r = record_of(c, list); Type type = field_kind(r, (unsigned)key); + if (type == I64 && kind.type == NUM && loose(e)) { /* xs[5] by an i64 key */ + made_i64(c, e, at); + kind.type = I64; + } if (kind.type != type) fail_at(c, e->line, e->column, "'%s' is keyed by %s, %s: expected %s or an index, not %s", c->state_names[list], r->field_names[key], type_name(type), type_name(type), type_name(kind.type)); @@ -1878,11 +2043,13 @@ static int local_named(const Compiler *c, const char *name) { static uint32_t local_arg(const Compiler *c, int local) { const Kind *kind = &c->locals[local].kind; return c->locals[local].slot | (kind->type == INDEX ? 1u << 8 | (uint32_t)kind->owner << 16 : - kind->type == BOOL ? (uint32_t)LOCAL_BOOL << 8 : 0); + kind->type == BOOL ? (uint32_t)LOCAL_BOOL << 8 : kind->type == I64 ? (uint32_t)LOCAL_I64 << 8 : 0); } /* Emit an expression; height is the stack height before it. */ static Kind value(Compiler *c, const Expr *e, bool handler, int height) { + Type want = c->want; /* set by wanted() for this expression alone */ + c->want = 0; if (height + 1 > STACK) fail_at(c, e->line, e->column, "expression needs more than 64 stack values"); switch (e->kind) { case E_NUMBER: emit(c, e->line, e->column, CONST, 0, e->number); return (Kind){NUM, -1}; @@ -1901,9 +2068,10 @@ static Kind value(Compiler *c, const Expr *e, bool handler, int height) { int pieces = 0; for (const Expr *part = e->args[0]; part; part = part->next) { Kind kind = value(c, part, handler, height + (pieces > 0)); - if (kind.type == NUM || kind.type == BOOL) emit(c, part->line, part->column, NUM_TEXT, kind.type == BOOL, 0); + if (kind.type == NUM || kind.type == BOOL || kind.type == I64) + emit(c, part->line, part->column, NUM_TEXT, kind.type == BOOL ? 1 : kind.type == I64 ? 2 : 0, 0); else if (kind.type != TEXT) - fail_at(c, part->line, part->column, "a string shows numbers, bools and text, not %s", type_name(kind.type)); + fail_at(c, part->line, part->column, "a string shows numbers, bools, i64s and text, not %s", type_name(kind.type)); if (pieces++) emit(c, part->line, part->column, CONCAT, 0, 0); } return (Kind){TEXT, -1}; @@ -1954,8 +2122,8 @@ static Kind value(Compiler *c, const Expr *e, bool handler, int height) { if (type == STATE_LIST) fail_at(c, e->line, e->column, "'%s' is a list; read %s.count or a field, as in %s[index].field", e->name, e->name, e->name); - static const uint8_t loads[] = {LOAD, TEXT_LOAD, CUR_LOAD, 0, INDEX_LOAD, LOAD}; - static const Type types[] = {NUM, TEXT, CURSOR, 0, INDEX, BOOL}; + static const uint8_t loads[] = {LOAD, TEXT_LOAD, CUR_LOAD, 0, INDEX_LOAD, LOAD, LOAD}; + static const Type types[] = {NUM, TEXT, CURSOR, 0, INDEX, BOOL, I64}; emit(c, e->line, e->column, loads[type], (uint32_t)index, 0); return (Kind){types[type], type == STATE_CURSOR || type == STATE_INDEX ? cursor_owner(c, index) : -1}; } @@ -2058,7 +2226,7 @@ static Kind value(Compiler *c, const Expr *e, bool handler, int height) { e->member, record_of(c, list)->name); if (e->count != 1) fail_at(c, e->line, e->column, "%s.has takes one key", e->member); Type type = field_kind(record_of(c, list), (unsigned)c->list_key[list] - 1); - Kind given = value(c, e->args[0], handler, height); + Kind given = wanted(c, e->args[0], handler, height, type); if (given.type != type) fail_at(c, e->args[0]->line, e->args[0]->column, "'%s' is keyed by %s, %s: expected %s, not %s", e->member, key, type_name(type), type_name(type), type_name(given.type)); @@ -2112,21 +2280,24 @@ static Kind value(Compiler *c, const Expr *e, bool handler, int height) { } case E_IF: { /* Arms of an else-if chain share one end; a value crosses it, so the - * branches ahead that carry one are counted against Pure's limit. */ + * branches ahead that carry one are counted against Pure's limit. An + * integer literal branch takes the other branches' type. */ unsigned ends[DEPTH], arms = 0, saved = c->joins; Kind kind = {0, -1}; + /* Literal branches take the type of the first that is no literal (an i64's, then). */ + Type hint = want == I64 ? I64 : 0; + for (const Expr *at = e; !hint; at = at->args[2]) { + if (!literal_only(at->args[1])) hint = probe(c, at->args[1], handler, height) == I64 ? I64 : NUM; + else if (at->args[2]->kind != E_IF) hint = literal_only(at->args[2]) ? NUM : probe(c, at->args[2], handler, height) == I64 ? I64 : NUM; + else continue; + } const Expr *arm = e; for (;;) { truth(c, arm->args[0], handler, height, "if"); unsigned jump = c->code_count; emit(c, arm->line, arm->column, JZ, 0, 0); if (height && ++c->joins > JOINS) fail_at(c, arm->line, arm->column, "conditional expressions nest too deeply"); - Kind then = value(c, arm->args[1], handler, height); - if (arms && (then.type != kind.type || then.owner != kind.owner)) - fail_at(c, arm->args[1]->line, arm->args[1]->column, then.type != kind.type ? - "the branches of if give %s and %s" : "the branches of if point into different texts or lists", - type_name(kind.type), type_name(then.type)); - kind = then; + if_branch(c, arm->args[1], handler, height, hint, &kind); ends[arms++] = c->code_count; emit(c, arm->line, arm->column, JMP, 0, 0); if (arms == 1 && ++c->joins > JOINS) fail_at(c, arm->line, arm->column, "conditional expressions nest too deeply"); @@ -2135,12 +2306,7 @@ static Kind value(Compiler *c, const Expr *e, bool handler, int height) { if (arm->args[2]->kind != E_IF) break; arm = arm->args[2]; } - Kind otherwise = value(c, arm->args[2], handler, height); - if (otherwise.type != kind.type || otherwise.owner != kind.owner) - fail_at(c, arm->args[2]->line, arm->args[2]->column, otherwise.type != kind.type ? - "the branches of if give %s and %s" : kind.type == CURSOR ? - "the branches of if give cursors of different texts" : "the branches of if give indices of different lists", - type_name(kind.type), type_name(otherwise.type)); + if_branch(c, arm->args[2], handler, height, hint, &kind); for (unsigned i = 0; i < arms; ++i) c->code[ends[i]].arg = c->code_count; c->joins = saved; return kind; @@ -2156,10 +2322,11 @@ static Kind value(Compiler *c, const Expr *e, bool handler, int height) { if (c->filtering) fail_at(c, e->line, e->column, "a table's where cannot hold a count, sum, max or min"); bool sliced = e->args[2] != NULL; if (sliced) { /* the keys from low up to before high, found by key */ - if (!keyed_by(c, list) || field_kind(record_of(c, list), (unsigned)c->list_key[list] - 1) != NUM) - fail_at(c, e->line, e->column, "'%s' is not keyed by a number: a slice takes one", e->member); - number(c, e->args[2], handler, height); - number(c, e->args[3], handler, height + 1); + Type key = keyed_by(c, list) ? field_kind(record_of(c, list), (unsigned)c->list_key[list] - 1) : TEXT; + if (key != NUM && key != I64) + fail_at(c, e->line, e->column, "'%s' is not keyed by a number or an i64: a slice takes one", e->member); + expect_kind(c, e->args[2], wanted(c, e->args[2], handler, height, key), key); + expect_kind(c, e->args[3], wanted(c, e->args[3], handler, height + 1, key), key); } unsigned at = c->code_count, joins = c->joins; emit(c, e->line, e->column, FOLD, 0, 0); @@ -2169,19 +2336,23 @@ static Kind value(Compiler *c, const Expr *e, bool handler, int height) { memcpy(c->fold_name, e->name, NAME); c->joins = 0; bool itemwise = e->op == FOLD_COUNT || e->op == FOLD_FIRST; + Type values = NUM; if (!itemwise) { Kind kind = value(c, e->args[1], false, 0); - if (kind.type != NUM) - fail_at(c, e->args[1]->line, e->args[1]->column, "sum, max, min and distinct take numbers, not %s", + if (kind.type != NUM && kind.type != I64) + fail_at(c, e->args[1]->line, e->args[1]->column, "sum, max, min and distinct take numbers or i64s, not %s", type_name(kind.type)); + values = kind.type; } if (e->args[0]) truth(c, e->args[0], false, !itemwise, "where"); else emit(c, e->line, e->column, CONST, 1, 1); /* every item */ c->folding = false; c->fold_list = -1; c->joins = joins; - c->code[at].arg = (uint32_t)e->op | (sliced ? 0x80u : 0) | (uint32_t)list << 8 | (uint32_t)(c->code_count - at - 1) << 16; - return e->op == FOLD_FIRST ? (Kind){INDEX, list} : (Kind){NUM, -1}; + c->code[at].arg = (uint32_t)e->op | (values == I64 ? FOLD_INTEGER : 0) | (sliced ? 0x80u : 0) | (uint32_t)list << 8 | + (uint32_t)(c->code_count - at - 1) << 16; + return e->op == FOLD_FIRST ? (Kind){INDEX, list} : + (Kind){values == I64 && e->op != FOLD_DISTINCT ? I64 : NUM, -1}; /* an i64's sum, most or least */ } case E_RECORD: case E_FIELD: fail_at(c, e->line, e->column, "a record value goes into a list, as in list.append(%s { ... })", e->name); @@ -2191,20 +2362,23 @@ static Kind value(Compiler *c, const Expr *e, bool handler, int height) { if (e->op == ADD || e->op == SUB) { /* at + 1 char moves a cursor, at + 1 an index; both stop at the ends. */ bool distance = e->args[1]->kind == E_UNIT; - Kind left = value(c, e->args[0], handler, height); + Kind left = wanted(c, e->args[0], handler, height, distance ? 0 : left_hint(c, e, handler, height, want)); if (distance && left.type != CURSOR) fail_at(c, e->line, e->column, "only a cursor moves by a distance, not %s", type_name(left.type)); - if (!distance && left.type != INDEX) expect_kind(c, e->args[0], left, NUM); - number(c, distance ? e->args[1]->args[0] : e->args[1], handler, height + 1); - if (left.type == NUM) { emit(c, e->line, e->column, (uint8_t)e->op, 0, 0); return left; } - if (e->op == SUB) emit(c, e->line, e->column, NEG, 0, 0); - if (distance) emit(c, e->line, e->column, CUR_MOVE, (uint32_t)e->args[1]->op, 0); - else emit(c, e->line, e->column, INDEX_MOVE, 0, 0); - return left; + if (distance || left.type == INDEX) { + number(c, distance ? e->args[1]->args[0] : e->args[1], handler, height + 1); + if (e->op == SUB) emit(c, e->line, e->column, NEG, 0, 0); + if (distance) emit(c, e->line, e->column, CUR_MOVE, (uint32_t)e->args[1]->op, 0); + else emit(c, e->line, e->column, INDEX_MOVE, 0, 0); + return left; + } + Type type = second(c, e, left, handler, height); + emit(c, e->line, e->column, type == I64 ? i64_op(e->op) : (uint8_t)e->op, 0, 0); + return (Kind){type, -1}; } if (e->op >= LT && e->op <= NE) { - /* Texts compare by their bytes, numbers as numbers; bools are equal or not. */ - Kind left = value(c, e->args[0], handler, height); + /* Texts compare by their bytes, numbers and i64s as numbers; bools are equal or not. */ + Kind left = wanted(c, e->args[0], handler, height, left_hint(c, e, handler, height, 0)); if (left.type == TEXT) { expect_kind(c, e->args[1], value(c, e->args[1], handler, height + 1), TEXT); emit(c, e->line, e->column, TEXT_COMPARE, (uint32_t)(e->op - LT), 0); @@ -2216,21 +2390,54 @@ static Kind value(Compiler *c, const Expr *e, bool handler, int height) { emit(c, e->line, e->column, (uint8_t)e->op, 0, 0); return (Kind){BOOL, -1}; } - expect_kind(c, e->args[0], left, NUM); - number(c, e->args[1], handler, height + 1); - emit(c, e->line, e->column, (uint8_t)e->op, 0, 0); + if (second(c, e, left, handler, height) == I64) emit(c, e->line, e->column, I64_COMPARE, (uint32_t)(e->op - LT), 0); + else emit(c, e->line, e->column, (uint8_t)e->op, 0, 0); return (Kind){BOOL, -1}; } /* fall through */ default: { - /* !, && and || take bools (both sides are computed); the rest numbers. */ + /* !, && and || take bools (both sides are computed); *, /, %, min and + * max numbers or i64s; clamp and floor numbers. */ bool logic = e->op == AND || e->op == OR || e->op == NOT; - for (unsigned i = 0; i < e->count; ++i) { - if (logic) truth(c, e->args[i], handler, height + (int)i, e->op == NOT ? "!" : e->op == AND ? "&&" : "||"); - else number(c, e->args[i], handler, height + (int)i); + if (logic) { + for (unsigned i = 0; i < e->count; ++i) + truth(c, e->args[i], handler, height + (int)i, e->op == NOT ? "!" : e->op == AND ? "&&" : "||"); + emit(c, e->line, e->column, (uint8_t)e->op, 0, 0); + return (Kind){BOOL, -1}; + } + if (e->op == I64_FROM) { /* i64(x): a whole number in range; a literal is one exactly */ + if (loose(e->args[0])) integer(c, e->args[0], handler, height); + else { + number(c, e->args[0], handler, height); + emit(c, e->line, e->column, I64_FROM, 0, 0); + } + return (Kind){I64, -1}; + } + if (e->op == NUM_FROM) { /* number(i): exact within 2^53 */ + if (loose(e->args[0])) return value(c, e->args[0], handler, height); + integer(c, e->args[0], handler, height); + emit(c, e->line, e->column, NUM_FROM, 0, 0); + return (Kind){NUM, -1}; + } + if (e->op == NEG) { + Kind operand = wanted(c, e->args[0], handler, height, want); + if (operand.type == I64) { + emit(c, e->line, e->column, I64_NEG, 0, 0); + return operand; + } + expect_kind(c, e->args[0], operand, NUM); + emit(c, e->line, e->column, NEG, 0, 0); + return (Kind){NUM, -1}; } + if (e->count == 2 && (e->op == MUL || e->op == DIV || e->op == MOD || e->op == MIN || e->op == MAX)) { + Kind left = wanted(c, e->args[0], handler, height, left_hint(c, e, handler, height, want)); + Type type = second(c, e, left, handler, height); + emit(c, e->line, e->column, type == I64 ? i64_op(e->op) : (uint8_t)e->op, 0, 0); + return (Kind){type, -1}; + } + for (unsigned i = 0; i < e->count; ++i) number(c, e->args[i], handler, height + (int)i); emit(c, e->line, e->column, (uint8_t)e->op, 0, 0); - return (Kind){logic ? BOOL : NUM, -1}; + return (Kind){NUM, -1}; } } } @@ -2332,10 +2539,11 @@ static void record_fields(Compiler *c, const Expr *e, int list, int height) { record_values(c, e, list, values); for (unsigned k = 0; k < r->fields; ++k) { int at = height + (int)k; - if (values[k]) expect_kind(c, values[k], value(c, values[k], true, at), field_kind(r, k)); + if (values[k]) expect_kind(c, values[k], wanted(c, values[k], true, at, field_kind(r, k)), field_kind(r, k)); else if (at + 1 > STACK) fail_at(c, e->line, e->column, "expression needs more than 64 stack values"); - else if (r->field_text[k]) emit(c, e->line, e->column, TEXT_CONST, r->field_constant[k], 0); - else emit(c, e->line, e->column, CONST, r->field_bool[k], r->field_number[k]); + else if (r->field_kinds[k] == TEXT) emit(c, e->line, e->column, TEXT_CONST, r->field_constant[k], 0); + else if (r->field_kinds[k] == I64) emit_integer(c, e->line, e->column, r->field_integer[k]); + else emit(c, e->line, e->column, CONST, r->field_kinds[k] == BOOL, r->field_number[k]); } } @@ -2380,8 +2588,8 @@ static void statements(Compiler *c, const Stmt *s) { fail_at(c, s->line, s->column, "'%s' is the item this for walks", s->name); if (c->local_slots == LOCALS) fail_at(c, s->line, s->column, "at most %d lets are allowed in a handler", LOCALS); Kind held = value(c, s->value, true, 0); - if (held.type != NUM && held.type != BOOL && held.type != INDEX) - fail_at(c, s->value->line, s->value->column, "a let holds a number, a bool or an index, not %s", + if (held.type != NUM && held.type != BOOL && held.type != I64 && held.type != INDEX) + fail_at(c, s->value->line, s->value->column, "a let holds a number, a bool, an i64 or an index, not %s", type_name(held.type)); unsigned local = c->local_count++; memcpy(c->locals[local].name, s->name, NAME); @@ -2393,7 +2601,7 @@ static void statements(Compiler *c, const Stmt *s) { case S_ASSIGN: { int local = local_named(c, s->name); if (local >= 0) { /* a let takes a new value of its kind */ - Kind kind = value(c, s->value, true, 0); + Kind kind = wanted(c, s->value, true, 0, c->locals[local].kind.type); if (kind.type != c->locals[local].kind.type || (kind.type == INDEX && kind.owner != c->locals[local].kind.owner)) fail_at(c, s->value->line, s->value->column, "'%s' holds %s", s->name, c->locals[local].kind.type == INDEX ? "an index of its list" : type_name(c->locals[local].kind.type)); @@ -2406,7 +2614,10 @@ static void statements(Compiler *c, const Stmt *s) { if (kind) fail_at(c, s->line, s->column, "cannot assign to %s '%s'", kind, s->name); fail_at(c, s->line, s->column, "unknown state '%s'", s->name); } - if (c->state_types[index] == STATE_NUM || c->state_types[index] == STATE_BOOL) { + if (c->state_types[index] == STATE_I64) { + integer(c, s->value, true, 0); + emit(c, s->line, s->column, STORE, (uint32_t)index, 0); + } else if (c->state_types[index] == STATE_NUM || c->state_types[index] == STATE_BOOL) { expect_kind(c, s->value, value(c, s->value, true, 0), c->state_types[index] == STATE_BOOL ? BOOL : NUM); emit(c, s->line, s->column, STORE, (uint32_t)index, 0); } else if (c->state_types[index] == STATE_LIST) { @@ -2493,7 +2704,7 @@ static void statements(Compiler *c, const Stmt *s) { for (unsigned k = 0; k < r->fields; ++k) { if ((int)k == key) continue; item_of(c, s->first, true, 0, list); - expect_kind(c, values[k], value(c, values[k], true, 1), field_kind(r, k)); + expect_kind(c, values[k], wanted(c, values[k], true, 1, field_kind(r, k)), field_kind(r, k)); emit(c, s->line, s->column, ITEM_SET, (uint32_t)list | k << 16, 0); } break; @@ -2508,7 +2719,7 @@ static void statements(Compiler *c, const Stmt *s) { if (c->list_key[list] == (int)field + 1) fail_at(c, s->line, s->column, "the key of '%s' does not change; remove the item and add it again", c->state_names[list]); if (*s->name) item_of(c, s->first, true, 0, list); - expect_kind(c, s->value, value(c, s->value, true, 1), field_kind(r, field)); + expect_kind(c, s->value, wanted(c, s->value, true, 1, field_kind(r, field)), field_kind(r, field)); emit(c, s->line, s->column, ITEM_SET, (uint32_t)list | field << 16, 0); break; } @@ -2538,7 +2749,7 @@ static void statements(Compiler *c, const Stmt *s) { if (!given[f]) fail_at(c, s->line, s->column, "send %s needs '%s'", s->name, out->field_names[f]); int type = out->field_types[f]; if (type == FIELD_BYTES) bytes_value(c, given[f], (int)f, "a bytes field"); - else expect_kind(c, given[f], value(c, given[f], true, (int)f), message_kind(type)); + else expect_kind(c, given[f], wanted(c, given[f], true, (int)f, message_kind(type)), message_kind(type)); } emit(c, s->line, s->column, SEND, (uint32_t)output, 0); break; @@ -2561,10 +2772,12 @@ static void statements(Compiler *c, const Stmt *s) { const char *kind = declared_kind(c, s->name); if (kind) fail_at(c, s->last->line, s->last->column, "'%s' is already declared as %s", s->name, kind); if (s->low) { /* a slice: the keys from low up to before high */ - if (!keyed_by(c, list) || field_kind(record_of(c, list), (unsigned)c->list_key[list] - 1) != NUM) - fail_at(c, s->first->line, s->first->column, "'%s' is not keyed by a number: a slice takes one", s->member); - number(c, s->low, true, 0); - number(c, s->high, true, 1); + Type key = keyed_by(c, list) ? field_kind(record_of(c, list), (unsigned)c->list_key[list] - 1) : TEXT; + if (key != NUM && key != I64) + fail_at(c, s->first->line, s->first->column, "'%s' is not keyed by a number or an i64: a slice takes one", + s->member); + expect_kind(c, s->low, wanted(c, s->low, true, 0, key), key); + expect_kind(c, s->high, wanted(c, s->high, true, 1, key), key); } unsigned at = c->code_count, jump = 0; emit(c, s->line, s->column, EACH, 0, 0); @@ -2621,6 +2834,11 @@ static unsigned char *put32(unsigned char *p, uint32_t v) { return p + 4; } +static unsigned char *put64(unsigned char *p, uint64_t v) { + p = put32(p, (uint32_t)v); + return put32(p, (uint32_t)(v >> 32)); +} + static unsigned char *put_double(unsigned char *p, double value) { uint64_t bits; memcpy(&bits, &value, sizeof(bits)); @@ -2688,7 +2906,7 @@ static void generate(Compiler *c, unsigned char **module, size_t *size) { if (*c->state_key[i]) { const Record *r = &c->records[record]; c->list_key[i] = 1 + (int)record_field(c, r, c->state_key[i], c->state_key_line[i], c->state_key_column[i]); - if (r->field_bool[c->list_key[i] - 1]) + if (r->field_kinds[c->list_key[i] - 1] == BOOL) fail_at(c, c->state_key_line[i], c->state_key_column[i], "a list is keyed by a number or text; '%s' is a bool", c->state_key[i]); } @@ -2698,7 +2916,7 @@ static void generate(Compiler *c, unsigned char **module, size_t *size) { Record *r = &c->records[i]; for (unsigned k = 0; k < r->fields; ++k) { Expr empty = {.kind = E_TEXT, .line = r->line, .column = r->column}; - if (r->field_text[k]) r->field_constant[k] = constant(c, r->field_string[k] ? r->field_string[k] : &empty); + if (r->field_kinds[k] == TEXT) r->field_constant[k] = constant(c, r->field_string[k] ? r->field_string[k] : &empty); } } for (unsigned i = 0; i < c->handler_count; ++i) { @@ -2764,10 +2982,10 @@ static void generate(Compiler *c, unsigned char **module, size_t *size) { c->arguments = c->view_arguments[c->row >= 0 ? (unsigned)c->row : i] - 1; c->view_code[i][0] = c->code_count; Kind kind = value(c, c->view_value[i], false, 0); - if (kind.type != NUM && kind.type != BOOL && kind.type != TEXT) - fail_at(c, c->view_value[i]->line, c->view_value[i]->column, "a %s is a number, a bool or text, not %s", + if (kind.type != NUM && kind.type != BOOL && kind.type != I64 && kind.type != TEXT) + fail_at(c, c->view_value[i]->line, c->view_value[i]->column, "a %s is a number, a bool, an i64 or text, not %s", c->row >= 0 ? "column" : "view", type_name(kind.type)); - c->view_types[i] = kind.type == TEXT ? VIEW_TEXT : kind.type == BOOL ? VIEW_BOOL : VIEW_NUM; + c->view_types[i] = kind.type == TEXT ? VIEW_TEXT : kind.type == BOOL ? VIEW_BOOL : kind.type == I64 ? VIEW_I64 : VIEW_NUM; c->view_code[i][1] = c->code_count - c->view_code[i][0]; } c->row = -1; @@ -2822,6 +3040,7 @@ static void generate(Compiler *c, unsigned char **module, size_t *size) { p = put32(p, state_name[i]); p = put32(p, (uint32_t)c->state_types[i]); if (c->state_types[i] == STATE_NUM || c->state_types[i] == STATE_BOOL) p = put_double(p, c->initial[i]); + else if (c->state_types[i] == STATE_I64) p = put64(p, (uint64_t)c->state_integer[i]); else { /* flags: a mark (1: the cursor stays), or a keyed list's key field + 1 */ p = put32(p, state_ref[i]); p = put32(p, c->state_types[i] == STATE_LIST ? (c->state_rows[i] ? LIST_ROWS : (uint32_t)c->list_key[i]) : @@ -2861,8 +3080,10 @@ static void generate(Compiler *c, unsigned char **module, size_t *size) { for (unsigned k = 0; k < c->records[i].fields; ++k) { const Record *r = &c->records[i]; p = put32(p, record_field_name[i][k]); - p = put32(p, r->field_text[k] ? 1 : r->field_bool[k] ? FIELD_RECORD_BOOL : 0); - if (r->field_text[k]) { p = put32(p, r->field_constant[k]); p = put32(p, 0); } + static const uint32_t types[] = {[NUM] = 0, [TEXT] = 1, [BOOL] = 2, [I64] = 3}; /* Pure's record field types */ + p = put32(p, types[r->field_kinds[k]]); + if (r->field_kinds[k] == TEXT) { p = put32(p, r->field_constant[k]); p = put32(p, 0); } + else if (r->field_kinds[k] == I64) p = put64(p, (uint64_t)r->field_integer[k]); else p = put_double(p, r->field_number[k]); } for (unsigned i = 0; i < c->views; ++i) { @@ -2881,7 +3102,7 @@ static void generate(Compiler *c, unsigned char **module, size_t *size) { for (unsigned i = 0; i < c->code_count; ++i) { *p++ = c->code[i].op; *p++ = 0; *p++ = 0; *p++ = 0; p = put32(p, c->code[i].arg); - p = put_double(p, c->code[i].value); + p = put64(p, (uint64_t)c->code[i].integer); /* a number's bits, or an i64's */ } memcpy(p, strings, used); if (c->constant_used) memcpy(p + used, c->constant_data, c->constant_used); -- cgit v1.2.3