diff options
author | ubq323 <ubq323@ubq323.website> | 2024-06-24 20:20:06 +0100 |
---|---|---|
committer | ubq323 <ubq323@ubq323.website> | 2024-06-24 20:20:06 +0100 |
commit | e9b99a90510309ac4f5d91d4a5138e7a84904057 (patch) | |
tree | 9d633feb897bcbbd43da6419882df600f9de5595 /com.c | |
parent | bc47478d855b08023409dbfc8550958991265c14 (diff) |
add local variables and (let) form
Diffstat (limited to 'com.c')
-rw-r--r-- | com.c | 113 |
1 files changed, 96 insertions, 17 deletions
@@ -32,6 +32,7 @@ static int stack_effect_of(Op opcode) { switch (opcode) { case OP_LOADK: case OP_GETGLOBAL: + case OP_GETLOCAL: case OP_NIL: case OP_TRUE: case OP_FALSE: @@ -55,11 +56,12 @@ static int stack_effect_of(Op opcode) { case OP_MOD: return -1; + // these ones depend on their argument. handle them specifically case OP_CALL: - // it depends. handle that one specifically + case OP_ENDSCOPE: return 0; default: - abort(); + ERROR("unknown stack effect of opcode %d",opcode); } } @@ -102,12 +104,19 @@ static size_t compile_constant(Compiler *C, Val v) { return ix; } +// len is 1 + number of args static void compile_call_instr(Compiler *C, uint8_t len) { compile_opcode(C, OP_CALL); compile_byte(C, len); C->stack_cur -= len; } +static void compile_endscope_instr(Compiler *C, uint8_t nlocals) { + compile_opcode(C, OP_ENDSCOPE); + compile_byte(C, nlocals); + C->stack_cur -= nlocals; +} + static size_t placeholder(Compiler *C) { size_t old_ix = BYTECODE(C).len; compile_byte(C, 0x00); @@ -119,20 +128,11 @@ static void patch(Compiler *C, size_t addr, uint16_t val) { BYTECODE(C).d[addr+1] = (val & 0xff00) >> 8; } - - - +// ---- static void compile_node(Compiler *C, AstNode a); typedef void (*form_compiler)(Compiler *C, AstVec l, Op op); -typedef struct { - char *name; - int min_params; - bool ellipsis; - form_compiler action; - Op op; -} BuiltinForm; void single_form(Compiler *C, AstVec l, Op op) { @@ -211,7 +211,7 @@ void arith_form(Compiler *C, AstVec l, Op op) { compile_opcode(C, op); } -void fn_form(Compiler *C, AstVec l, Op op) { +void fn_form(Compiler *C, AstVec l, Op _) { // (fn (arg arg arg) body ...) CHECK(l.vals[1].ty == AST_LIST, "fn's first argument must be list"); AstVec arglist = l.vals[1].as.list; @@ -232,8 +232,70 @@ void fn_form(Compiler *C, AstVec l, Op op) { compile_byte(C, compile_constant(C, VAL_OBJ(func))); } +static void begin_scope(Compiler *C) { + Scope *sc = malloc(sizeof(Scope)); + CHECK(sc != NULL, "memory fail"); + memset(sc, 0, sizeof(Scope)); + sc->outer = C->scope; + C->scope = sc; +} +static void end_scope(Compiler *C) { + Scope *sc = C->scope; + CHECK(sc != NULL, "attempt to end nonexistent scope"); + C->scope = sc->outer; + // printf("ending scope with %d locals, named: \n", sc->nlocals); + // for (int i = 0; i < sc->nlocals; i++) { + // Local loc = sc->locals[i]; + // printf("\t%3d %s\n",loc.slot, loc.name); + // } + + compile_endscope_instr(C, sc->nlocals); + + free(sc); +} +static void declare_local(Compiler *C, char *name) { + Scope *sc = C->scope; + CHECK(sc != NULL, "can't declare local outside of scope"); + Local *l = &sc->locals[sc->nlocals++]; + l->name = name; + // -1 because local is expected to be already on the stack + // ie sitting just below where stack_cur points + l->slot = C->stack_cur - 1; +} + +void let_form(Compiler *C, AstVec l, Op _) { + CHECK(l.vals[1].ty == AST_LIST, "let's first argument must be list"); + AstVec bindlist = l.vals[1].as.list; + CHECK(bindlist.len % 2 == 0, "unmatched binding in let"); + int nbinds = bindlist.len / 2; + + begin_scope(C); + for (int i = 0; i < nbinds; i++) { + int ix = i * 2; + AstNode name = bindlist.vals[ix]; + AstNode expr = bindlist.vals[ix+1]; + CHECK(name.ty == AST_IDENT, "binding name must be identifier"); + compile_node(C, expr); + declare_local(C, name.as.str); + } + for (int i = 2; i < l.len - 1; i++) { + compile_node(C, l.vals[i]); + compile_opcode(C, OP_DROP); + } + compile_node(C, l.vals[l.len-1]); + end_scope(C); +} + +typedef struct { + char *name; + int min_params; + bool ellipsis; + form_compiler action; + Op op; +} BuiltinForm; + static BuiltinForm builtin_forms[] = { { "puts", 1, false, single_form, OP_PUTS }, { "print", 1, false, single_form, OP_PRINT }, @@ -242,6 +304,7 @@ static BuiltinForm builtin_forms[] = { { "if", 3, false, if_form, 0 }, { "while", 2, true, while_form, 0 }, { "fn", 2, true, fn_form, 0 }, + { "let", 2, true, let_form, 0 }, #define ARITH_OP(str, op) \ { str, 2, false, arith_form, op }, ARITH_OP("+", OP_ADD) @@ -279,10 +342,26 @@ static void compile_node(Compiler *C, AstNode a) { } } if (!found_builtin) { - // read global variable - ObjString *o = objstring_copy_cstr(C->S, a.as.str); - compile_opcode(C, OP_GETGLOBAL); - compile_byte(C, compile_constant(C, VAL_OBJ(o))); + // read local or global variable + bool found_local = false; + if (C->scope != NULL) { + Scope *sc = C->scope; + for (int i = 0; i < sc->nlocals; i++) { + Local loc = sc->locals[i]; + if (0 == strcmp(ident, loc.name)) { + compile_opcode(C, OP_GETLOCAL); + compile_byte(C, loc.slot); + found_local = true; + break; + } + } + } + if (!found_local) { + // read global + ObjString *o = objstring_copy_cstr(C->S, a.as.str); + compile_opcode(C, OP_GETGLOBAL); + compile_byte(C, compile_constant(C, VAL_OBJ(o))); + } } break; case AST_NUM: |