From: cassowarii Date: Tue, 30 Jun 2026 04:28:15 +0000 (-0700) Subject: we can actually run (very simple) programs! AMAZING X-Git-Url: https://www.git.cassowary.me/gitweb.cgi?a=commitdiff_plain;h=00c6b0e56b59173e14e54ec30d5ec2b5e573b08b;p=sarabande.git we can actually run (very simple) programs! AMAZING --- diff --git a/Makefile b/Makefile index a40b929..a58e1c1 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CC=gcc CFLAGS=-Wall -g -DDEBUG -fsanitize=undefined -#CFLAGS=-Wall -O3 -flto -DGLEW_STATIC -DDEBUG -fsanitize=undefined +#CFLAGS=-Wall -O3 -flto LIBS= SRC := src @@ -14,10 +14,13 @@ build/a.out: $(OBJECTS) @mkdir -p $(dir $@) $(CC) $(CFLAGS) -I$(SRC) $(OBJECTS) $(LIBS) -o $@ -.PHONY: parsedebug +.PHONY: parsedebug optimized parsedebug: CFLAGS += -DPARSEDEBUG parsedebug: build/a.out +optimized: CFLAGS=-Wall -O3 -flto +optimized: build/a.out + $(OBJ)/%.o: $(SRC)/%.c @mkdir -p $(dir $@) $(CC) $(CFLAGS) $(LIBS) -I$(SRC) -c $< -o $@ diff --git a/src/compile/emit.c b/src/compile/emit.c index 66b18cc..209c8c0 100644 --- a/src/compile/emit.c +++ b/src/compile/emit.c @@ -30,32 +30,10 @@ void sbEmit_compile_program(sbVmProgram *vp, sbIrProgram *ir) { sbVmCompiler_deinitialize(&cm); } -void compile_chunk(sbVmCompiler *cm, sbIrChunk *chunk) { - //sbVmCompiler_add_constant(&cm, &HVINT(5)); - //sbVmCompiler_add_constant(&cm, &HVINT(9)); - - printf("\n--block--\n"); - - int nstmts = chunk->stmts.size / sizeof(sbIrStmt*); - for (int i = 0; i < nstmts; i++) { - sbIrStmt *stmt = ((sbIrStmt**)chunk->stmts.data)[i]; - - compile_stmt(cm, stmt); - } -} - -/* when compiling one line at a time, we don't know the position - * of labels we haven't seen yet. we leave a 4-byte slot for a blank - * address, and say "please put the address of this label here later!" */ -void record_labelpos(sbVmCompiler *cm, sbIrLabel *label, u32 offset) { - struct labelpos lp = { - }; - - sbBuffer_append(&cm->label_positions, &lp, sizeof(struct labelpos)); -} +/* --- */ void emit_arg(sbVmCompiler *cm, usize number) { - printf(" arg %zu\n", number); + printf(" arg %zu\n", number); u8 buf[9]; if (number < 256) { buf[0] = number; @@ -86,8 +64,40 @@ void emit_arg(sbVmCompiler *cm, usize number) { } } + +void compile_chunk(sbVmCompiler *cm, sbIrChunk *chunk) { + //sbVmCompiler_add_constant(&cm, &HVINT(5)); + //sbVmCompiler_add_constant(&cm, &HVINT(9)); + + printf("\n--block %d--\n", chunk->id); + + int nstmts = chunk->stmts.size / sizeof(sbIrStmt*); + if (chunk->id > 0) { + /* don't check args for initial block + * (note: when we have modules or whatever, this might + * be an incorrect way to do this) */ + EMIT(BC_NUMARG); + EARG(chunk->num_args); + } + for (int i = 0; i < nstmts; i++) { + sbIrStmt *stmt = ((sbIrStmt**)chunk->stmts.data)[i]; + + compile_stmt(cm, stmt); + } +} + +/* when compiling one line at a time, we don't know the position + * of labels we haven't seen yet. we leave a 4-byte slot for a blank + * address, and say "please put the address of this label here later!" */ +void record_labelpos(sbVmCompiler *cm, sbIrLabel *label, u32 offset) { + struct labelpos lp = { + }; + + sbBuffer_append(&cm->label_positions, &lp, sizeof(struct labelpos)); +} + void compile_stmt(sbVmCompiler *cm, sbIrStmt *stmt) { - printf("%zu ", sbVmCompiler_get_position(cm)); + printf("%3zu ", sbVmCompiler_get_position(cm)); switch (stmt->type) { case IR_S_EXPR: compile_expr(cm, stmt->expr); @@ -115,6 +125,15 @@ void compile_stmt(sbVmCompiler *cm, sbIrStmt *stmt) { stmt->label->block_position = sbVmCompiler_get_position(cm); printf("\n"); break; + case IR_S_ASSIGN: + compile_expr(cm, stmt->assign.expr); + EMIT(BC_ST_VAR); + EARG(stmt->assign.var->slot_id); + break; + case IR_S_ARG: + EMIT(BC_ST_ARG); + EARG(stmt->arg.var->slot_id); + break; case IR_S_RETURN: if (stmt->expr) { compile_expr(cm, stmt->expr); @@ -128,6 +147,8 @@ void compile_stmt(sbVmCompiler *cm, sbIrStmt *stmt) { void compile_op(sbVmCompiler *cm, sbAstOp op); void compile_expr(sbVmCompiler *cm, sbIrExpr *expr) { + sbIrExpr *E1; + int count = 0; switch(expr->type) { case IR_E_OP: compile_expr(cm, expr->op.left); @@ -136,6 +157,36 @@ void compile_expr(sbVmCompiler *cm, sbIrExpr *expr) { } compile_op(cm, expr->op.type); break; + case IR_E_CALL: + E1 = expr->call.param; + while (E1) { + count ++; + compile_expr(cm, E1->param.this); + E1 = E1->param.next; + } + EMIT(BC_LD_IMM); + EARG(count); /* calling convention: store argument count on stack */ + compile_expr(cm, expr->call.func); + EMIT(BC_CALL); + break; + case IR_E_VAR: + EMIT(BC_LD_VAR); + EARG(expr->var->slot_id); + break; + case IR_E_FUNC: + EMIT(BC_LD_BLK); + EARG(expr->func->id); + break; + case IR_E_VALUE: + if (expr->value.type == IT_NIL) { + EMIT(BC_LD_NIL); + } else if (expr->value.type == IT_INTEGER) { + EMIT(BC_LD_IMM); + EARG(expr->value.integer); + } else { + printf("(some value)\n"); + } + break; default: printf("(compile an expr)\n"); } @@ -145,6 +196,14 @@ void compile_op(sbVmCompiler *cm, sbAstOp op) { switch (op) { case AST_OP_ADD: EMIT(BC_OP_ADD); break; case AST_OP_SUB: EMIT(BC_OP_SUB); break; + case AST_OP_MUL: EMIT(BC_OP_MUL); break; + case AST_OP_FLDIV: EMIT(BC_OP_FLDIV); break; + case AST_OP_EQ: EMIT(BC_OP_EQ); break; + case AST_OP_NE: EMIT(BC_OP_EQ, BC_OP_NOT); break; + case AST_OP_LT: EMIT(BC_OP_LT); break; + case AST_OP_GT: EMIT(BC_OP_LE, BC_OP_NOT); break; + case AST_OP_LE: EMIT(BC_OP_LE); break; + case AST_OP_GE: EMIT(BC_OP_LT, BC_OP_NOT); break; default: printf("unknown operation!\n"); } diff --git a/src/compile/ir.c b/src/compile/ir.c index 4a3150c..8c931b9 100644 --- a/src/compile/ir.c +++ b/src/compile/ir.c @@ -348,6 +348,7 @@ static sbIrChunk *compile_ast_function(hIrProgram ir, sbAst paramsAst, sbAst seq sbBuffer_append(&arg_vars, &v1, sizeof(sbIrVariable*)); v1->initialized = TRUE; param_comma = param_comma->seq.right; + ck->num_args ++; } while (arg_vars.size > 0) { diff --git a/src/compile/ir.h b/src/compile/ir.h index 719a34c..5b129e4 100644 --- a/src/compile/ir.h +++ b/src/compile/ir.h @@ -117,6 +117,8 @@ struct sbIrProgram; typedef struct sbIrChunk { struct sbIrProgram *program; i32 id; + i16 num_args; + flag variadic; i32 label_count; i32 variable_count; i32 lowest_var_id; diff --git a/src/data/operations.c b/src/data/operations.c index aca8317..ed117b9 100644 --- a/src/data/operations.c +++ b/src/data/operations.c @@ -6,7 +6,7 @@ hV sbV_add(const hV *a, const hV *b) { if (a->type == IT_INTEGER && b->type == IT_INTEGER) { return sbV_int(sbInteger_sum(a->integer, b->integer)); } else { - PANIC("todo"); + PANIC("todo (add type %llu and type %llu)", a->type, b->type); } } @@ -57,3 +57,27 @@ hV sbV_eq(const hV *a, const hV *b) { return HVBOOL(FALSE); } } + +hV sbV_lt(const hV *a, const hV *b) { + if (a->type == IT_INTEGER && b->type == IT_INTEGER) { + if (a->integer < b->integer) { + return HVBOOL(TRUE); + } else { + return HVBOOL(FALSE); + } + } else { + PANIC("todo"); + } +} + +hV sbV_le(const hV *a, const hV *b) { + if (a->type == IT_INTEGER && b->type == IT_INTEGER) { + if (a->integer <= b->integer) { + return HVBOOL(TRUE); + } else { + return HVBOOL(FALSE); + } + } else { + PANIC("todo"); + } +} diff --git a/src/data/operations.h b/src/data/operations.h index ed97ea2..587411b 100644 --- a/src/data/operations.h +++ b/src/data/operations.h @@ -13,3 +13,7 @@ hV sbV_incr(const hV *a); hV sbV_decr(const hV *a); hV sbV_eq(const hV *a, const hV *b); + +hV sbV_lt(const hV *a, const hV *b); + +hV sbV_le(const hV *a, const hV *b); diff --git a/src/data/value.c b/src/data/value.c index 8fdb686..46ac7b9 100644 --- a/src/data/value.c +++ b/src/data/value.c @@ -51,6 +51,13 @@ hV sbV_int(hInteger i) { }; } +hV sbV_function(u64 id) { + return (hV) { + .type = IT_FUNCTION, + .data = id, + }; +} + flag sbV_c_eq(const hV *a, const hV *b) { if (a->type != b->type) return FALSE; if (a->type == ITX_TOMBSTONE || b->type == ITX_TOMBSTONE) return FALSE; diff --git a/src/data/value.h b/src/data/value.h index 7526178..5cd07e1 100644 --- a/src/data/value.h +++ b/src/data/value.h @@ -6,6 +6,7 @@ #define HVINT(n) ((hV) { .type = IT_INTEGER, .integer = n }) #define HVSTR(s) ((hV) { .type = IT_STRING, .string = OBJSL(s) }) #define HVBOOL(b) ((hV) { .type = IT_BOOLEAN, .boolean = b }) +#define HVFUNC(i) ((hV) { .type = IT_FUNCTION, .data = i }) typedef u64 hHash; typedef u64 hString; @@ -37,6 +38,7 @@ typedef struct hV { hInteger integer; u64 boolean; double float_val; + u64 data; }; } hV; @@ -47,6 +49,7 @@ hV sbV_float(double fl); hV sbV_hash(hHash hash); hV sbV_int(hInteger i); hV sbV_boolean(flag b); +hV sbV_function(u64 id); flag sbV_c_eq(const hV *a, const hV *b); flag sbV_c_falsy(const hV *a); diff --git a/src/main.c b/src/main.c index 7490375..d653061 100644 --- a/src/main.c +++ b/src/main.c @@ -9,7 +9,7 @@ #include "vm/exec.h" int main(int argc, char **argv) { - if (argc == 2) { + if (argc >= 2) { sbString_sys_init(); sbHash_sys_init(); sbSymbol_sys_init(); @@ -17,13 +17,26 @@ int main(int argc, char **argv) { sbParser pr; sbParser_initialize(&pr); - sbAst parse_result = sbParser_parse_file(&pr, argv[1]); + const char *filename; + flag debugmode = FALSE; + if (argc == 3 && sbstrncmp(argv[1], "-D", 2) == 0) { + filename = argv[2]; + debugmode = TRUE; + } else if (argc == 2) { + filename = argv[1]; + } else { + printf("usage: %s [-D] \n", argv[0]); + printf("usage: -D = bytecode debugger\n"); + return 0; + } + + sbAst parse_result = sbParser_parse_file(&pr, filename); if (parse_result == NULL) { - fprintf(stderr, "fatal error: Could not open script '%s'\n", argv[1]); + fprintf(stderr, "fatal error: Could not open script '%s'\n", filename); return -2; } else if (parse_result->type == AST_ERROR) { - fprintf(stderr, "fatal error: Could not run '%s' due to syntax errors.\n", argv[1]); + fprintf(stderr, "fatal error: Could not run '%s' due to syntax errors.\n", filename); return -1; } @@ -39,7 +52,7 @@ int main(int argc, char **argv) { sbParser_deinitialize(&pr); if (ir.error_count > 0) { - fprintf(stderr, "Could not run '%s' due to errors.\n", argv[1]); + fprintf(stderr, "Could not run '%s' due to errors.\n", filename); return -3; } else { // print out program @@ -53,70 +66,23 @@ int main(int argc, char **argv) { sbIrProgram_deinitialize(&ir); + sbVm vm = {0}; + sbVm_initialize(&vm, 1048576, 1048576, debugmode); + + sbVm_execute(&vm, &pm); + + printf("Stack result: "); + for (u8 *p = vm.stack; p < vm.sp; p++) { + printf("%02X ", *p); + } + printf("\n"); + sbVmProgram_deinitialize(&pm); + sbVm_deinitialize(&vm); sbSymbol_sys_deinit(); sbHash_sys_deinit(); sbString_sys_deinit(); } else { fprintf(stderr, "please provide a file as input\n"); } - - sbVm vm = {0}; - sbVm_initialize(&vm, 1048576, 1048576); - - sbVmProgram pm; - sbVmProgram_initialize(&pm, 65536); - - sbVmCompiler pb = sbVmCompiler_create(4096, 4096); - - u8 code1[] = { - BC_ALLOC_VARS, 0x02, /* 0 1 */ - BC_LD_IMM, 0x00, /* var1 = 0 2 3 */ - BC_ST_VAR, 0x01, /* ... 4 5 */ - BC_LD_CONST, 0x01, /* var0 = 9 6 7 */ - BC_ST_VAR, 0x00, /* ... 8 9 */ - BC_LD_CONST, 0x00, /* (LABEL) var1 += 5 ; 10 11 */ - BC_LD_VAR, 0x01, /* ... */ - BC_OP_ADD, /* ... */ - BC_ST_VAR, 0x01, /* ... */ - BC_LD_VAR, 0x00, /* var0 = var0 - 1 */ - BC_OP_DECR, /* ... */ - BC_ST_VAR, 0x00, /* ... */ - BC_LD_VAR, 0x00, /* if var0 != 0 */ - BC_LD_IMM, 0x00, /* ... */ - BC_OP_EQ, /* ... */ - BC_JF, 0x0A, /* ...then goto LABEL */ - BC_LD_VAR, 0x01, /* return var1 */ - BC_LD_IMM, 0x01, /* ... */ - BC_HALT, - }; - - sbVmCompiler_add_constant(&pb, &HVINT(5)); - sbVmCompiler_add_constant(&pb, &HVINT(9)); - - sbVmCompiler_write_code(&pb, code1, sizeof(code1)); - sbVmProgram_add_block(&pm, &pb); - sbVmCompiler_reset(&pb); - - u8 code2[] = { - BC_LD_CONST, 0x00, - BC_OP_ADD, - BC_RET, - }; - - sbVmCompiler_add_constant(&pb, &HVINT(7)); - - sbVmCompiler_write_code(&pb, code2, sizeof(code2)); - sbVmProgram_add_block(&pm, &pb); - sbVmCompiler_reset(&pb); - - sbVmCompiler_deinitialize(&pb); - - sbVm_execute(&vm, &pm); - - printf("Stack result: "); - for (u8 *p = vm.stack; p < vm.sp; p++) { - printf("%02X ", *p); - } - printf("\n"); } diff --git a/src/vm/bytecode.h b/src/vm/bytecode.h index 299434d..ae42fe0 100644 --- a/src/vm/bytecode.h +++ b/src/vm/bytecode.h @@ -10,17 +10,22 @@ typedef enum sbOpcode { BC_LD_CTX, // look up key in context object and push result BC_LD_VAR, // push value onto stack from variable 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_ST_VAR, // pop value from stack into variable BC_ST_UPVAL, // pop value from stack into closure - BC_POP = 0x08, // pop value from stack - BC_NPOP, // pop value from stack + BC_ST_ARG, // decrement TOS and store NOS in variable + BC_POP, // pop value from stack + BC_NPOP, // pop N values from stack given by top number BC_CALL, // push return address and jump to new block BC_RET, // return to calling block + BC_NUMARG, // check number of function arguments = this + BC_MINARG, // check number of function arguments > this BC_JMP, // unconditional local jump inside current block BC_JT, // jump if true BC_JF, // jump if false BC_SEND, // like call, but jump to object method - BC_OP_EQ = 0x10, // == + BC_OP_EQ, // == BC_OP_NOT, // boolean not BC_OP_ADD, // add BC_OP_SUB, // subtract @@ -28,11 +33,13 @@ typedef enum sbOpcode { BC_OP_DIV, // true divide BC_OP_FLDIV, // floor divide BC_OP_NEG, // negative - BC_OP_MOD = 0x18, // mod + BC_OP_MOD, // mod BC_OP_POW, // power BC_OP_INCR, // increment BC_OP_DECR, // decrement BC_OP_DEREF, // dereference pointer + BC_OP_LT, // less than + BC_OP_LE, // less than or equal to BC_ALLOC_VARS, // create space in rstack for local variables BC_LIST_GATHER, // create list from count + list of values on stack BC_HASH_GATHER, // create hash from count + list of pairs of keys/values on stack diff --git a/src/vm/exec.c b/src/vm/exec.c index fef3fd1..ea50f2b 100644 --- a/src/vm/exec.c +++ b/src/vm/exec.c @@ -5,7 +5,7 @@ void call_block(hVm vm, usize block_id); void execute_instruction(hVm vm); -void sbVm_initialize(hVm vm, usize stacksize, usize rstacksize) { +void sbVm_initialize(hVm vm, usize stacksize, usize rstacksize, flag debugmode) { *vm = (sbVm) {0}; vm->stack = malloc(stacksize); vm->stacksize = stacksize; @@ -15,6 +15,8 @@ void sbVm_initialize(hVm vm, usize stacksize, usize rstacksize) { vm->sp = vm->stack; vm->fp = (sbVmStackFrame*)vm->rstack; vm->rp = vm->rstack; + + vm->debugmode = debugmode; } void sbVm_deinitialize(hVm vm) { @@ -33,6 +35,13 @@ sbVmStatus sbVm_execute(hVm vm, sbVmProgram *pm) { while (vm->running) { execute_instruction(vm); + if (vm->debugmode) { + printf("Stack: "); + for (u8 *p = vm->stack; p < vm->sp; p++) { + printf("%02X ", *p); + } + printf("\n"); + } } return VM_STAT_SUCCESS; @@ -62,9 +71,14 @@ void return_from_block(hVm vm) { sbVmStackFrame *frame = (sbVmStackFrame*)vm->fp; - vm->fp = frame->last_fp; - vm->rp = frame->last_rp; - vm->ip = frame->return_addr; + if (frame->return_addr == NULL) { + /* returning to address 0 means quit */ + vm->running = FALSE; + } else { + vm->fp = frame->last_fp; + vm->rp = frame->last_rp; + vm->ip = frame->return_addr; + } } void debug_print_hv(const hV *value) { @@ -177,6 +191,10 @@ void execute_instruction(hVm vm) { 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; case BC_ST_VAR: param = get_param(vm); v = peek_stack(vm, 0); @@ -185,6 +203,22 @@ void execute_instruction(hVm vm) { return; case BC_ST_UPVAL: PANIC("todo"); + case BC_ST_ARG: + param = get_param(vm); + v = pop_stack(vm); /* argument count */ + if (v->type != IT_INTEGER) { + /* internal error! this should be generated correctly */ + PANIC("calling convention violation: number of args should be an integer"); + } + w = pop_stack(vm); /* argument value */ + store_local(vm, param, w); + /* we track the number of arguments remaining, for variadic functions later */ + v->integer --; + if (v->integer > 0) { + /* if last integer, don't put the 0 count back on the stack */ + push_stack(vm, v); + } + return; case BC_POP: v = pop_stack(vm); return; @@ -194,7 +228,25 @@ void execute_instruction(hVm vm) { return; case BC_CALL: v = pop_stack(vm); - call_block(vm, v->integer); + if (v->type != IT_FUNCTION) { + /* We need to figure out exception support or some such. + * User error should not panic. */ + PANIC("attempt to call a non-function value"); + } + call_block(vm, v->data); + return; + case BC_NUMARG: + param = get_param(vm); + v = pop_stack(vm); + if (v->type != IT_INTEGER) { + /* internal error! this should be generated correctly */ + PANIC("calling convention violation: number of args should be an integer"); + } + if (v->integer != param) { + /* This should be an exception. */ + PANIC("wrong number of arguments passed to function."); + } + push_stack(vm, v); return; case BC_JMP: param = get_param(vm); @@ -234,6 +286,20 @@ void execute_instruction(hVm vm) { push_stack(vm, &HVBOOL(FALSE)); } return; + 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; + 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; case BC_OP_ADD: v = peek_stack(vm, 1); w = peek_stack(vm, 0); @@ -295,8 +361,8 @@ void execute_instruction(hVm vm) { PANIC("todo"); case BC_LONG_NUM: case BC_VLONG_NUM: - PANIC("illegal opcode $%02X at position $%08zX\n", op, (usize)vm->ip); + PANIC("illegal opcode $%02X at position $%016zX", op, (usize)vm->ip); default: - PANIC("unrecognized opcode $%02X at position $%08zX\n", op, (usize)vm->ip); + PANIC("unrecognized opcode $%02X at position $%016zX", op, (usize)vm->ip); } } diff --git a/src/vm/exec.h b/src/vm/exec.h index 6fa4406..295d756 100644 --- a/src/vm/exec.h +++ b/src/vm/exec.h @@ -65,11 +65,12 @@ typedef struct sbVm { sbVmProgram *program; flag running; + flag debugmode; } sbVm; typedef sbVm *hVm; -void sbVm_initialize(hVm vm, usize stacksize, usize rstacksize); +void sbVm_initialize(hVm vm, usize stacksize, usize rstacksize, flag debugmode); void sbVm_deinitialize(hVm vm);