From f0cd7a3cab56a6f8d7b4520aaa168a271a94d6d4 Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Wed, 27 Oct 2021 10:43:30 +0200 Subject: Add initial implementation of locals --- src/bytecode/chunk.c | 2 ++ src/bytecode/chunk.h | 4 +++- src/bytecode/compiler.h | 48 +++++++++++++++++++++++++++++++++++++++------- src/bytecode/debug.h | 8 +++++++- src/bytecode/errors.c | 1 + src/bytecode/errors.h | 1 + src/bytecode/hashtable.h | 1 - src/bytecode/ops.h | 2 ++ src/bytecode/string_view.h | 2 +- src/bytecode/vm.h | 24 ++++++++++++++++++----- 10 files changed, 77 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/bytecode/chunk.c b/src/bytecode/chunk.c index e566e78..71562fa 100644 --- a/src/bytecode/chunk.c +++ b/src/bytecode/chunk.c @@ -10,6 +10,7 @@ chunk_init(StringView name) { array_init(chunk->name, name.n); array_insert(chunk->name, name.start, name.n); array_init(chunk->params, 0); + array_init(chunk->locals, 0); return chunk; } @@ -24,6 +25,7 @@ chunk_free(Chunk *chunk) { array_free(chunk->lines); array_free(chunk->name); array_free(chunk->params); + array_free(chunk->locals); free(chunk); } diff --git a/src/bytecode/chunk.h b/src/bytecode/chunk.h index a3e02d1..c584d4a 100755 --- a/src/bytecode/chunk.h +++ b/src/bytecode/chunk.h @@ -22,9 +22,11 @@ typedef struct Chunk { char *name; // Parameters StringView *params; + // Locals. + StringView *locals; } Chunk; -#define NEW_CHUNK(NAME) chunk_init((StringView){(NAME), sizeof(NAME)}) +#define NEW_CHUNK(NAME) chunk_init((StringView){(NAME), sizeof(NAME) - 1}) Chunk * chunk_init(StringView name); void add_code(Chunk *chunk, u8 byte, size_t line, size_t col); diff --git a/src/bytecode/compiler.h b/src/bytecode/compiler.h index 6a17beb..7497ea7 100755 --- a/src/bytecode/compiler.h +++ b/src/bytecode/compiler.h @@ -34,8 +34,8 @@ has_next_token(const Compiler *compiler) { ssize_t find_local_index(Chunk *chunk, Token tok) { // NOTE: This is dumb and potentially slow. - for (size_t i = 0; i < array_size(chunk->params); i++) { - if (sv_equal(&tok.value, &chunk->params[i])) { + for (size_t i = 0; i < array_size(chunk->locals); i++) { + if (sv_equal(&tok.value, &chunk->locals[i])) { return i; } } @@ -203,9 +203,11 @@ compile_list_simple_op(Chunk *chunk, Compiler *compiler, Token start, Ops op) { }); } +#define STR_ARRAY(ARR) (StringView){(ARR), array_size(ARR)} + void compile_declare_op(Chunk *chunk, Compiler *compiler, Token start, Ops op) { - Token name = peek_token(compiler); + Token name = next_token(compiler); if (name.type != TOKEN_SYMBOL) { error_push((Error){ .type = ERR_TYPE_COMPILER, @@ -215,9 +217,29 @@ compile_declare_op(Chunk *chunk, Compiler *compiler, Token start, Ops op) { }); return; } - Object obj = make_symbol(name.value); - emit_constant(chunk, name, obj); - next_token(compiler); + // TODO: If we are inside a function and we are using OP_DEF, we just + // declare the local variable directly in the stack. No need for symbols! + // TODO: If we are inside a function and we are using OP_SET, check if the + // local variable has been defined before, if not try to read from the + // globals for setting. + if (sv_equal(&STRING(""), &STR_ARRAY(chunk->name))) { + Object obj = make_symbol(name.value); + emit_constant(chunk, name, obj); + } else { + // TODO: only do this if we are defining! not setting. + // Check if we already have the local + ssize_t idx = find_local_index(chunk, name); + if (idx < 0) { + array_push(chunk->locals, name.value); + idx = array_size(chunk->locals) - 1; + } + if (op == OP_DEF) { + op = OP_DEF_LOCAL; + } else if (op == OP_SET) { + op = OP_SET_LOCAL; + } + emit_constant(chunk, name, FIXNUM_VAL(idx)); + } Token tok = peek_token(compiler); if (name.type == TOKEN_EOF || tok.type == TOKEN_EOF) { @@ -282,7 +304,19 @@ compile_lambda(Chunk *chunk, Compiler *compiler, Token start, StringView name) { }); return; } + // Check if parameters name already exists. + ssize_t idx = find_local_index(fun.chunk, tok); + if (idx >= 0) { + error_push((Error){ + .type = ERR_TYPE_COMPILER, + .value = ERR_AMBIGUOUS_PARAMS, + .line = tok.line, + .col = tok.column, + }); + return; + } array_push(fun.chunk->params, tok.value); + array_push(fun.chunk->locals, tok.value); } } else if (tok.type != TOKEN_NIL) { error_push((Error){ @@ -549,7 +583,7 @@ parse_tree(Chunk *chunk, Compiler *compiler) { Chunk * compile(Token *tokens) { Chunk *chunk = NULL; - chunk = NEW_CHUNK("main"); + chunk = NEW_CHUNK(""); Compiler compiler = (Compiler){ .tokens = tokens, .current = 0, diff --git a/src/bytecode/debug.h b/src/bytecode/debug.h index 06d48b0..54d2cdb 100755 --- a/src/bytecode/debug.h +++ b/src/bytecode/debug.h @@ -11,6 +11,8 @@ static const char* ops_str[] = { // Load/store ops. [OP_CONSTANT] = "OP_CONSTANT", [OP_LOCAL] = "OP_LOCAL", + [OP_DEF_LOCAL] = "OP_DEF_LOCAL", + [OP_SET_LOCAL] = "OP_SET_LOCAL", [OP_DEF] = "OP_DEF", [OP_SET] = "OP_SET", [OP_GET] = "OP_GET", @@ -46,7 +48,11 @@ static const char* ops_str[] = { void disassemble_chunk(Chunk *chunk) { - printf("===== %.*s =====\n", (int)array_size(chunk->name), chunk->name); + if (array_size(chunk->name) < 1) { + printf("===== main =====\n"); + } else { + printf("===== %.*s =====\n", (int)array_size(chunk->name), chunk->name); + } printf("code:\n"); size_t offset = 0; while (offset < array_size(chunk->code)) { diff --git a/src/bytecode/errors.c b/src/bytecode/errors.c index b4595a1..b2aab93 100755 --- a/src/bytecode/errors.c +++ b/src/bytecode/errors.c @@ -17,6 +17,7 @@ static const char* error_msgs[] = { [ERR_DIVISION_BY_ZERO] = "error: division by zero", [ERR_PC_OOB] = "error: pc out of bounds", [ERR_EMPTY_CHUNK] = "error: empty chunk", + [ERR_AMBIGUOUS_PARAMS] = "error: ambiguous parameter names", }; static Error errors[ERR_MAX_NUMBER]; diff --git a/src/bytecode/errors.h b/src/bytecode/errors.h index 425c768..8f4386e 100755 --- a/src/bytecode/errors.h +++ b/src/bytecode/errors.h @@ -24,6 +24,7 @@ typedef enum ErrorValue { ERR_TOO_MANY_ARGS, ERR_WRONG_ARG_TYPE, ERR_DIVISION_BY_ZERO, + ERR_AMBIGUOUS_PARAMS, // Bytecode interpreter. ERR_PC_OOB, diff --git a/src/bytecode/hashtable.h b/src/bytecode/hashtable.h index 81c841e..1f55048 100644 --- a/src/bytecode/hashtable.h +++ b/src/bytecode/hashtable.h @@ -116,7 +116,6 @@ _ht_insert(HashTable *table, Object key, Object value) { pairs[probe_position].key = &table->keys[array_size(table->keys) - 1]; pairs[probe_position].value = &table->values[array_size(table->values) - 1]; } else { - object_free(*pairs[probe_position].value); *pairs[probe_position].value = value; } } diff --git a/src/bytecode/ops.h b/src/bytecode/ops.h index 501a37f..52c774a 100755 --- a/src/bytecode/ops.h +++ b/src/bytecode/ops.h @@ -5,6 +5,8 @@ typedef enum Ops { // Load/store ops. OP_CONSTANT, OP_LOCAL, + OP_DEF_LOCAL, + OP_SET_LOCAL, OP_DEF, OP_SET, OP_GET, diff --git a/src/bytecode/string_view.h b/src/bytecode/string_view.h index 79d8305..5977ea9 100755 --- a/src/bytecode/string_view.h +++ b/src/bytecode/string_view.h @@ -18,6 +18,6 @@ bool sv_equal(const StringView *a, const StringView *b); // Write a character to the given output stream. void sv_write(const StringView *sv); -#define STRING(STR) (StringView){(STR), sizeof(STR)} +#define STRING(STR) (StringView){(STR), sizeof(STR) - 1} #endif // BDL_STRINGVIEW_H diff --git a/src/bytecode/vm.h b/src/bytecode/vm.h index 3f59aff..84d2432 100755 --- a/src/bytecode/vm.h +++ b/src/bytecode/vm.h @@ -183,6 +183,11 @@ vm_interpret(VM *vm) { Object obj = frame->chunk->constants[constant]; array_push(vm->stack, obj); } break; + case OP_DEF_LOCAL: { + Object value = array_pop(vm->stack); + ssize_t idx = AS_FIXNUM(array_pop(vm->stack)); + vm->stack[frame->stack_offset + idx] = value; + } break; case OP_DEF: { Object value = array_pop(vm->stack); Object name = array_pop(vm->stack); @@ -281,12 +286,15 @@ vm_interpret(VM *vm) { } else if (n_args > n_params) { RUNTIME_ERROR(ERR_TOO_MANY_ARGS); } + ssize_t n_locals = array_size(proc.chunk->locals) - n_params; - // DEBUG:... - // disassemble_chunk(proc.chunk); +#ifdef DEBUG + disassemble_chunk(proc.chunk); +#endif // Tail-call optimization. if (proc.chunk != frame->chunk || *vm->pc != OP_RETURN) { + // Prepare new call frame. CallFrame new_frame = (CallFrame){ .chunk = proc.chunk, .rp = vm->pc, @@ -294,14 +302,20 @@ vm_interpret(VM *vm) { }; array_push(vm->frames, new_frame); frame = &vm->frames[array_size(vm->frames) - 1]; + + // Prepare local slots. + for (ssize_t i = 0; i < n_locals; i++) { + array_push(vm->stack, NIL_VAL); + } } else { // Bind tail-call parameters. - for (size_t i = 0; i < (size_t)n_params; i++) { - Object obj = array_peek(vm->stack, n_params - 1 - i); + for (ssize_t i = 0; i < n_params; i++) { + Object obj = array_peek(vm->stack, n_locals + n_params - 1 - i); vm->stack[frame->stack_offset + i] = obj; } + // Reset stack size. - array_head(vm->stack)->size = frame->stack_offset + n_params; + array_head(vm->stack)->size = frame->stack_offset + n_params + n_locals; } vm->pc = frame->chunk->code; } break; -- cgit v1.2.1