From: cassowarii Date: Tue, 30 Jun 2026 14:28:53 +0000 (-0700) Subject: we can have negative numbers now X-Git-Url: https://www.git.cassowary.me/gitweb.cgi?a=commitdiff_plain;h=f94032c7f312067a9d389c7a42b4101587533c82;p=sarabande.git we can have negative numbers now --- diff --git a/src/compile/emit.c b/src/compile/emit.c index 7e906cf..8b07271 100644 --- a/src/compile/emit.c +++ b/src/compile/emit.c @@ -32,19 +32,26 @@ void sbEmit_compile_program(sbVmProgram *vp, sbIrProgram *ir) { /* --- */ -void emit_arg(sbVmCompiler *cm, usize number) { - printf(" arg %zu\n", number); +void emit_arg(sbVmCompiler *cm, i64 actual_number) { + printf(" arg %lld\n", actual_number); u8 buf[9]; - if (number < 253) { + u64 number = actual_number; + if (0 <= actual_number && actual_number < 253) { buf[0] = number; sbVmCompiler_write_code(cm, buf, 1); - } else if (number < 65536) { + } else if (-32768 < actual_number && actual_number < 32768) { buf[0] = BC_LONG_NUM; + if (actual_number < 0) { + number = actual_number + 65536; + } buf[1] = number >> 8; buf[2] = number & 0xFF; sbVmCompiler_write_code(cm, buf, 3); - } else if (number < (1LL << 32)) { + } else if (-(1L << 31) < actual_number && actual_number < (1L << 31)) { buf[0] = BC_VLONG_NUM; + if (actual_number < 0) { + number = actual_number + (1LL << 32); + } buf[1] = (number >> 24) & 0xFF;; buf[2] = (number >> 16) & 0xFF; buf[3] = (number >> 8) & 0xFF; @@ -52,6 +59,7 @@ void emit_arg(sbVmCompiler *cm, usize number) { sbVmCompiler_write_code(cm, buf, 5); } else { buf[0] = BC_VVLONG_NUM; + number = (u64)actual_number; buf[1] = (number >> 56) & 0xFF;; buf[2] = (number >> 48) & 0xFF;; buf[3] = (number >> 40) & 0xFF;; @@ -80,8 +88,10 @@ void compile_chunk(sbVmCompiler *cm, sbIrChunk *chunk) { EARG(chunk->num_args); } - EMIT(BC_ALLOC_VARS); - EARG(chunk->variable_count); + if (chunk->variable_count > 0) { + EMIT(BC_ALLOC_VARS); + EARG(chunk->variable_count); + } for (int i = 0; i < nstmts; i++) { sbIrStmt *stmt = ((sbIrStmt**)chunk->stmts.data)[i]; @@ -209,11 +219,17 @@ void compile_expr(sbVmCompiler *cm, sbIrExpr *expr) { case IR_E_VALUE: if (expr->value.type == IT_NIL) { EMIT(BC_LD_NIL); - } else if (expr->value.type == IT_INTEGER) { + } else if (expr->value.type == IT_BOOLEAN && expr->value.boolean) { + EMIT(BC_LD_TRUE); + } else if (expr->value.type == IT_BOOLEAN) { + EMIT(BC_LD_FALSE); + } else if (expr->value.type == IT_INTEGER && expr->value.integer < (2 << 16)) { EMIT(BC_LD_IMM); EARG(expr->value.integer); } else { - printf("(some value)\n"); + u32 constant_index = sbVmCompiler_add_constant(cm, &expr->value); + EMIT(BC_LD_CONST); + EARG(constant_index); } break; default: diff --git a/src/data/integer.c b/src/data/integer.c index 68dd5ee..337c853 100644 --- a/src/data/integer.c +++ b/src/data/integer.c @@ -80,7 +80,10 @@ hInteger sbInteger_diff(hInteger a, hInteger b) { hInteger sbInteger_mul(hInteger a, hInteger b) { if (!is_bigint(a) && !is_bigint(b)) { - if (b < SARABANDE_INT_MAX / a && b > SARABANDE_INT_MIN / a) { + flag zero_ok = (a == 0); + flag pos_ok = (a > 0 && b < SARABANDE_INT_MAX / a && b > SARABANDE_INT_MIN / a); + flag neg_ok = (a < 0 && b < SARABANDE_INT_MIN / a && b > SARABANDE_INT_MAX / a); + if (zero_ok || pos_ok || neg_ok) { return sbInteger_new(a * b); } } @@ -97,7 +100,10 @@ hInteger sbInteger_floordiv(hInteger a, hInteger b) { /* --- */ flag is_bigint(hInteger n) { - return (n & FLAG_BIGINT); + /* bigint flag is the second bit after the sign bit, normally for smaller + * numbers this bit is equal to the sign bit, so we see if it's NOT equal + * as a flag that a number is a bigint */ + return (n > 0 && (n & FLAG_BIGINT)) || (n < 0 && !(n & FLAG_BIGINT)); } static intblk *alloc_new_block() { diff --git a/src/parse/scanner.c b/src/parse/scanner.c index 344a8be..fadff08 100644 --- a/src/parse/scanner.c +++ b/src/parse/scanner.c @@ -199,6 +199,59 @@ static usize read_symbol(hScanner sc) { return token_size; } +static void read_integer(hScanner sc, sbLexToken *new_token, flag negative) { + new_token->type = T_INTEGER; + + i64 intval = 0; + int base = 10; + int num_done = FALSE; + char ch = PEEK; + + if (ch == '0') { + /* skip a leading zero, and see if it has some base indicator */ + ch = NEXT; + if (ch == 'b') { + ch = NEXT; + base = 2; + } else if (ch == 'o') { + ch = NEXT; + base = 8; + } else if (ch == 'x') { + ch = NEXT; + base = 16; + } else if (is_space(ch) || ch == '\n') { + /* it was literally just the number 0 (probably) */ + num_done = TRUE; + } else { + new_token->type = T_BADNUMBER; + num_done = TRUE; + } + } + + if (!num_done) { + do { + /* TODO: promote to bigint, don't allow overflow */ + intval *= base; + intval += base_digit_value(ch); + do { + /* skip over underscores in numeric literals */ + ch = NEXT; + } while (ch == '_'); + } while (is_base_digit(ch, base)); + + /* if we are now looking at still a character that's a letter or digit, throw error */ + if (is_digit(ch) || is_alpha(ch)) { + new_token->type = T_BADNUMBER; + NEXT; + } else { + new_token->i = intval; + if (negative) { + new_token->i *= -1; + } + } + } +} + static sbLexToken compute_next_token(hScanner sc) { int ch_int = PEEK; unsigned char ch = (unsigned char)ch_int; @@ -359,6 +412,9 @@ static sbLexToken compute_next_token(hScanner sc) { } else if (ch == '=') { new_token.type = T_MINUSEQUALS; NEXT; + } else if (is_digit(ch)) { + /* negative number literal */ + read_integer(sc, &new_token, TRUE); } else { new_token.type = T_MINUS; } @@ -448,52 +504,7 @@ static sbLexToken compute_next_token(hScanner sc) { new_token.symb = sbSymbol_from_bytes(sc->dynamic_buffer.data, sc->dynamic_buffer.size - 1); new_token.size = token_size; } else if (is_digit(ch)) { - new_token.type = T_INTEGER; - - i64 intval = 0; - int base = 10; - int num_done = FALSE; - - if (ch == '0') { - /* skip a leading zero, and see if it has some base indicator */ - ch = NEXT; - if (ch == 'b') { - ch = NEXT; - base = 2; - } else if (ch == 'o') { - ch = NEXT; - base = 8; - } else if (ch == 'x') { - ch = NEXT; - base = 16; - } else if (is_space(ch) || ch == '\n') { - /* it was literally just the number 0 (probably) */ - num_done = TRUE; - } else { - new_token.type = T_BADNUMBER; - num_done = TRUE; - } - } - - if (!num_done) { - do { - /* TODO: promote to bigint, don't allow overflow */ - intval *= base; - intval += base_digit_value(ch); - do { - /* skip over underscores in numeric literals */ - ch = NEXT; - } while (ch == '_'); - } while (is_base_digit(ch, base)); - - /* if we are now looking at still a character that's a letter or digit, throw error */ - if (is_digit(ch) || is_alpha(ch)) { - new_token.type = T_BADNUMBER; - NEXT; - } else { - new_token.i = intval; - } - } + read_integer(sc, &new_token, FALSE); } else { new_token.type = T_ERROR; NEXT; diff --git a/src/vm/block.c b/src/vm/block.c index 202b09f..fb6c834 100644 --- a/src/vm/block.c +++ b/src/vm/block.c @@ -34,10 +34,10 @@ usize sbVmCompiler_get_position(sbVmCompiler *cm) { return cm->bytecode.size; } -void sbVmCompiler_add_constant(sbVmCompiler *cm, hV *constant) { +u32 sbVmCompiler_add_constant(sbVmCompiler *cm, hV *constant) { sbV_retain(constant); - sbBuffer_append(&cm->constants, constant, sizeof(hV)); + return cm->constants.size / sizeof(hV) - 1; } void sbVmProgram_initialize(sbVmProgram *pm, usize initial_arena_size) { @@ -71,7 +71,7 @@ sbBlockId sbVmProgram_add_block(sbVmProgram *pm, sbVmCompiler *cm) { sbVmBlock bk = { .bytecode = bytecode, - .bytecode_length = bytecode_length, + .bytecode_end = bytecode + bytecode_length, .constants = (hV*)constants, .constants_count = constants_length / sizeof(hV), }; diff --git a/src/vm/block.h b/src/vm/block.h index c3e3745..c80286b 100644 --- a/src/vm/block.h +++ b/src/vm/block.h @@ -21,7 +21,7 @@ typedef struct sbVmCompiler { /* fixed size / frozen block of bytecode */ typedef struct sbVmBlock { const u8 *bytecode; - usize bytecode_length; + const u8 *bytecode_end; const hV *constants; usize constants_count; } sbVmBlock; @@ -47,7 +47,7 @@ void sbVmCompiler_overwrite_code_at(sbVmCompiler *cm, usize offset, const u8 *da usize sbVmCompiler_get_position(sbVmCompiler *cm); -void sbVmCompiler_add_constant(sbVmCompiler *pb, hV *constant); +u32 sbVmCompiler_add_constant(sbVmCompiler *cm, hV *constant); void sbVmProgram_initialize(sbVmProgram *pm, usize initial_arena_size); diff --git a/src/vm/bytecode.h b/src/vm/bytecode.h index ae42fe0..ee024ae 100644 --- a/src/vm/bytecode.h +++ b/src/vm/bytecode.h @@ -12,6 +12,8 @@ typedef enum sbOpcode { BC_LD_UPVAL, // push value onto stack from closure BC_LD_BLK, // push reference to function onto stack BC_LD_NIL, // push nil onto stack + BC_LD_TRUE, // push true onto stack + BC_LD_FALSE, // push false onto stack BC_ST_VAR, // pop value from stack into variable BC_ST_UPVAL, // pop value from stack into closure BC_ST_ARG, // decrement TOS and store NOS in variable diff --git a/src/vm/exec.c b/src/vm/exec.c index b51e653..3b70370 100644 --- a/src/vm/exec.c +++ b/src/vm/exec.c @@ -117,93 +117,108 @@ hV *peek_stack(hVm vm, usize offset) { } sbOpcode get_opcode(hVm vm) { + if (vm->ip >= vm->fp->block->bytecode_end) PANIC("execution outside defined code area"); return *(vm->ip++); } +void next_byte(hVm vm, u64 *result) { + u8 byte = *(vm->ip++); +#ifdef DEBUG + if (vm->debugmode) printf("%02X ", byte); +#endif + *result <<= 8; + *result |= byte; +} + /* small numbers that are parameters to opcodes can * just be encoded directly as 1 byte. numbers of 16-bit * scale can be encoded as FC + bytebyte, numbers of * 32 bit scale can be FD + bytebytebytebyte, * 64 bit can be FE + bytebytebytebytebytebytebytebyte, * in big-endian format */ -u64 get_param(hVm vm) { +i64 get_param(hVm vm) { u64 result = *((u8*)vm->ip++); +#ifdef DEBUG + if (vm->debugmode) printf("%02llX ", result); +#endif + i64 signed_result = result; if (result == BC_LONG_NUM) { - result = *(vm->ip++); - result <<= 8; - result |= *(vm->ip++); + result = 0; + next_byte(vm, &result); + next_byte(vm, &result); + signed_result = result; + if (result > (1 << 15)) signed_result = result - (1 << 16); } else if (result == BC_VLONG_NUM) { - result = *(vm->ip++); - result <<= 8; - result |= *(vm->ip++); - result <<= 8; - result |= *(vm->ip++); - result <<= 8; - result |= *(vm->ip++); + printf("here now\n"); + result = 0; + next_byte(vm, &result); // 0 + next_byte(vm, &result); // 1 + next_byte(vm, &result); // 2 + next_byte(vm, &result); // 3 + signed_result = result; + if (result > (1L << 31)) signed_result = result - (1L << 32); } else if (result == BC_VVLONG_NUM) { - result = *(vm->ip++); - result <<= 8; - result |= *(vm->ip++); - result <<= 8; - result |= *(vm->ip++); - result <<= 8; - result |= *(vm->ip++); - result <<= 8; - result |= *(vm->ip++); - result <<= 8; - result |= *(vm->ip++); - result <<= 8; - result |= *(vm->ip++); - result <<= 8; - result |= *(vm->ip++); + result = 0; + next_byte(vm, &result); // 0 + next_byte(vm, &result); // 1 + next_byte(vm, &result); // 2 + next_byte(vm, &result); // 3 + next_byte(vm, &result); // 4 + next_byte(vm, &result); // 5 + next_byte(vm, &result); // 6 + next_byte(vm, &result); // 7 + signed_result = (i64)result; } - return result; + return signed_result; } /* execute one instruction! wow! */ void execute_instruction(hVm vm) { sbOpcode op = get_opcode(vm); +#ifdef DEBUG + if (vm->debugmode) printf("op %02X ", op); +#endif u64 param; hV *v, *w, res; switch (op) { case BC_NOP: - return; + break; case BC_HALT: vm->running = FALSE; - return; + break; case BC_LD_IMM: param = get_param(vm); push_stack(vm, &HVINT(param)); - return; + break; case BC_LD_CONST: param = get_param(vm); push_stack(vm, &vm->fp->block->constants[param]); - return; + break; case BC_LD_CTX: PANIC("todo"); case BC_LD_VAR: param = get_param(vm); push_stack(vm, &vm->fp->locals[param]); - return; + break; case BC_LD_UPVAL: /* Hey, was there upval in there? */ PANIC("todo"); case BC_LD_BLK: param = get_param(vm); push_stack(vm, &HVFUNC(param)); - return; + break; case BC_LD_NIL: push_stack(vm, &HVNIL); - return; + break; case BC_ST_VAR: param = get_param(vm); v = peek_stack(vm, 0); store_local(vm, param, v); pop_stack(vm); - return; + break; case BC_ST_UPVAL: PANIC("todo"); case BC_ST_ARG: @@ -221,14 +236,14 @@ void execute_instruction(hVm vm) { /* if last integer, don't put the 0 count back on the stack */ push_stack(vm, v); } - return; + break; case BC_POP: v = pop_stack(vm); - return; + break; case BC_NPOP: param = get_param(vm); npop_stack(vm, param); - return; + break; case BC_CALL: v = pop_stack(vm); if (v->type != IT_FUNCTION) { @@ -237,7 +252,7 @@ void execute_instruction(hVm vm) { PANIC("attempt to call a non-function value"); } call_block(vm, v->data); - return; + break; case BC_NUMARG: param = get_param(vm); v = pop_stack(vm); @@ -250,28 +265,28 @@ void execute_instruction(hVm vm) { PANIC("wrong number of arguments passed to function."); } push_stack(vm, v); - return; + break; case BC_JMP: param = get_param(vm); vm->ip = &vm->fp->block->bytecode[param]; - return; + break; case BC_JT: param = get_param(vm); v = pop_stack(vm); if (!sbV_c_falsy(v)) { vm->ip = &vm->fp->block->bytecode[param]; } - return; + break; case BC_JF: param = get_param(vm); v = pop_stack(vm); if (sbV_c_falsy(v)) { vm->ip = &vm->fp->block->bytecode[param]; } - return; + break; case BC_RET: return_from_block(vm); - return; + break; case BC_SEND: PANIC("todo"); case BC_OP_EQ: @@ -280,7 +295,7 @@ void execute_instruction(hVm vm) { res = sbV_eq(v, w); npop_stack(vm, 2); push_stack(vm, &res); - return; + break; case BC_OP_NOT: v = pop_stack(vm); if (sbV_c_falsy(v)) { @@ -288,42 +303,42 @@ void execute_instruction(hVm vm) { } else { push_stack(vm, &HVBOOL(FALSE)); } - return; + break; case BC_OP_LT: v = peek_stack(vm, 1); w = peek_stack(vm, 0); res = sbV_lt(v, w); npop_stack(vm, 2); push_stack(vm, &res); - return; + break; case BC_OP_LE: v = peek_stack(vm, 1); w = peek_stack(vm, 0); res = sbV_le(v, w); npop_stack(vm, 2); push_stack(vm, &res); - return; + break; case BC_OP_ADD: v = peek_stack(vm, 1); w = peek_stack(vm, 0); res = sbV_add(v, w); npop_stack(vm, 2); push_stack(vm, &res); - return; + break; case BC_OP_SUB: v = peek_stack(vm, 1); w = peek_stack(vm, 0); res = sbV_sub(v, w); npop_stack(vm, 2); push_stack(vm, &res); - return; + break; case BC_OP_MUL: v = peek_stack(vm, 1); w = peek_stack(vm, 0); res = sbV_mul(v, w); npop_stack(vm, 2); push_stack(vm, &res); - return; + break; case BC_OP_DIV: PANIC("todo"); case BC_OP_FLDIV: @@ -332,7 +347,7 @@ void execute_instruction(hVm vm) { res = sbV_floordiv(v, w); npop_stack(vm, 2); push_stack(vm, &res); - return; + break; case BC_OP_NEG: case BC_OP_MOD: case BC_OP_POW: @@ -342,16 +357,17 @@ void execute_instruction(hVm vm) { res = sbV_incr(v); pop_stack(vm); push_stack(vm, &res); - return; + break; case BC_OP_DECR: v = peek_stack(vm, 0); res = sbV_decr(v); pop_stack(vm); push_stack(vm, &res); - return; + break; case BC_OP_DEREF: PANIC("todo"); case BC_ALLOC_VARS: + /* TODO check for overflow */ param = get_param(vm); /* have to set new rstack space to 0 so we don't accidentally decrement * the ref count of variables from a previous stack frame (or of just garbage) */ @@ -359,7 +375,7 @@ void execute_instruction(hVm vm) { /* we allocate one more variable at 0 for internal use */ vm->rp += (param + 1) * sizeof(hV); vm->fp->num_locals += param; - return; + break; case BC_LIST_GATHER: case BC_HASH_GATHER: PANIC("todo"); @@ -369,4 +385,7 @@ void execute_instruction(hVm vm) { default: PANIC("unrecognized opcode $%02X at position $%016zX", op, (usize)vm->ip); } +#ifdef DEBUG + if (vm->debugmode) printf("\n"); +#endif } diff --git a/src/vm/exec.h b/src/vm/exec.h index 295d756..1c851d2 100644 --- a/src/vm/exec.h +++ b/src/vm/exec.h @@ -52,13 +52,13 @@ typedef struct sbVmStackFrame { } sbVmStackFrame; typedef struct sbVm { - u8 *stack; /* for calculations */ - u8 *rstack; /* for locals and return addresses, like FORTH */ + u8 *stack; /* for calculations */ + u8 *rstack; /* for locals and return addresses, like FORTH */ - const u8 *ip; /* instruction pointer */ + const u8 *ip; /* instruction pointer */ sbVmStackFrame *fp; /* frame pointer */ - u8 *sp; /* stack pointer */ - u8 *rp; /* rstack pointer */ + u8 *sp; /* stack pointer */ + u8 *rp; /* rstack pointer */ usize stacksize; /* to detect overflow, save these */ usize rstacksize; /* ^^ */