diff options
-rw-r--r-- | src/bytecode/chunk.c | 2 | ||||
-rwxr-xr-x | src/bytecode/chunk.h | 4 | ||||
-rwxr-xr-x | src/bytecode/compiler.h | 48 | ||||
-rwxr-xr-x | src/bytecode/debug.h | 8 | ||||
-rwxr-xr-x | src/bytecode/errors.c | 1 | ||||
-rwxr-xr-x | src/bytecode/errors.h | 1 | ||||
-rw-r--r-- | src/bytecode/hashtable.h | 1 | ||||
-rwxr-xr-x | src/bytecode/ops.h | 2 | ||||
-rwxr-xr-x | src/bytecode/string_view.h | 2 | ||||
-rwxr-xr-x | src/bytecode/vm.h | 24 |
10 files changed, 77 insertions, 16 deletions
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) { | |||
10 | array_init(chunk->name, name.n); | 10 | array_init(chunk->name, name.n); |
11 | array_insert(chunk->name, name.start, name.n); | 11 | array_insert(chunk->name, name.start, name.n); |
12 | array_init(chunk->params, 0); | 12 | array_init(chunk->params, 0); |
13 | array_init(chunk->locals, 0); | ||
13 | return chunk; | 14 | return chunk; |
14 | } | 15 | } |
15 | 16 | ||
@@ -24,6 +25,7 @@ chunk_free(Chunk *chunk) { | |||
24 | array_free(chunk->lines); | 25 | array_free(chunk->lines); |
25 | array_free(chunk->name); | 26 | array_free(chunk->name); |
26 | array_free(chunk->params); | 27 | array_free(chunk->params); |
28 | array_free(chunk->locals); | ||
27 | free(chunk); | 29 | free(chunk); |
28 | } | 30 | } |
29 | 31 | ||
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 { | |||
22 | char *name; | 22 | char *name; |
23 | // Parameters | 23 | // Parameters |
24 | StringView *params; | 24 | StringView *params; |
25 | // Locals. | ||
26 | StringView *locals; | ||
25 | } Chunk; | 27 | } Chunk; |
26 | 28 | ||
27 | #define NEW_CHUNK(NAME) chunk_init((StringView){(NAME), sizeof(NAME)}) | 29 | #define NEW_CHUNK(NAME) chunk_init((StringView){(NAME), sizeof(NAME) - 1}) |
28 | 30 | ||
29 | Chunk * chunk_init(StringView name); | 31 | Chunk * chunk_init(StringView name); |
30 | void add_code(Chunk *chunk, u8 byte, size_t line, size_t col); | 32 | 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) { | |||
34 | ssize_t | 34 | ssize_t |
35 | find_local_index(Chunk *chunk, Token tok) { | 35 | find_local_index(Chunk *chunk, Token tok) { |
36 | // NOTE: This is dumb and potentially slow. | 36 | // NOTE: This is dumb and potentially slow. |
37 | for (size_t i = 0; i < array_size(chunk->params); i++) { | 37 | for (size_t i = 0; i < array_size(chunk->locals); i++) { |
38 | if (sv_equal(&tok.value, &chunk->params[i])) { | 38 | if (sv_equal(&tok.value, &chunk->locals[i])) { |
39 | return i; | 39 | return i; |
40 | } | 40 | } |
41 | } | 41 | } |
@@ -203,9 +203,11 @@ compile_list_simple_op(Chunk *chunk, Compiler *compiler, Token start, Ops op) { | |||
203 | }); | 203 | }); |
204 | } | 204 | } |
205 | 205 | ||
206 | #define STR_ARRAY(ARR) (StringView){(ARR), array_size(ARR)} | ||
207 | |||
206 | void | 208 | void |
207 | compile_declare_op(Chunk *chunk, Compiler *compiler, Token start, Ops op) { | 209 | compile_declare_op(Chunk *chunk, Compiler *compiler, Token start, Ops op) { |
208 | Token name = peek_token(compiler); | 210 | Token name = next_token(compiler); |
209 | if (name.type != TOKEN_SYMBOL) { | 211 | if (name.type != TOKEN_SYMBOL) { |
210 | error_push((Error){ | 212 | error_push((Error){ |
211 | .type = ERR_TYPE_COMPILER, | 213 | .type = ERR_TYPE_COMPILER, |
@@ -215,9 +217,29 @@ compile_declare_op(Chunk *chunk, Compiler *compiler, Token start, Ops op) { | |||
215 | }); | 217 | }); |
216 | return; | 218 | return; |
217 | } | 219 | } |
218 | Object obj = make_symbol(name.value); | 220 | // TODO: If we are inside a function and we are using OP_DEF, we just |
219 | emit_constant(chunk, name, obj); | 221 | // declare the local variable directly in the stack. No need for symbols! |
220 | next_token(compiler); | 222 | // TODO: If we are inside a function and we are using OP_SET, check if the |
223 | // local variable has been defined before, if not try to read from the | ||
224 | // globals for setting. | ||
225 | if (sv_equal(&STRING(""), &STR_ARRAY(chunk->name))) { | ||
226 | Object obj = make_symbol(name.value); | ||
227 | emit_constant(chunk, name, obj); | ||
228 | } else { | ||
229 | // TODO: only do this if we are defining! not setting. | ||
230 | // Check if we already have the local | ||
231 | ssize_t idx = find_local_index(chunk, name); | ||
232 | if (idx < 0) { | ||
233 | array_push(chunk->locals, name.value); | ||
234 | idx = array_size(chunk->locals) - 1; | ||
235 | } | ||
236 | if (op == OP_DEF) { | ||
237 | op = OP_DEF_LOCAL; | ||
238 | } else if (op == OP_SET) { | ||
239 | op = OP_SET_LOCAL; | ||
240 | } | ||
241 | emit_constant(chunk, name, FIXNUM_VAL(idx)); | ||
242 | } | ||
221 | 243 | ||
222 | Token tok = peek_token(compiler); | 244 | Token tok = peek_token(compiler); |
223 | if (name.type == TOKEN_EOF || tok.type == TOKEN_EOF) { | 245 | if (name.type == TOKEN_EOF || tok.type == TOKEN_EOF) { |
@@ -282,7 +304,19 @@ compile_lambda(Chunk *chunk, Compiler *compiler, Token start, StringView name) { | |||
282 | }); | 304 | }); |
283 | return; | 305 | return; |
284 | } | 306 | } |
307 | // Check if parameters name already exists. | ||
308 | ssize_t idx = find_local_index(fun.chunk, tok); | ||
309 | if (idx >= 0) { | ||
310 | error_push((Error){ | ||
311 | .type = ERR_TYPE_COMPILER, | ||
312 | .value = ERR_AMBIGUOUS_PARAMS, | ||
313 | .line = tok.line, | ||
314 | .col = tok.column, | ||
315 | }); | ||
316 | return; | ||
317 | } | ||
285 | array_push(fun.chunk->params, tok.value); | 318 | array_push(fun.chunk->params, tok.value); |
319 | array_push(fun.chunk->locals, tok.value); | ||
286 | } | 320 | } |
287 | } else if (tok.type != TOKEN_NIL) { | 321 | } else if (tok.type != TOKEN_NIL) { |
288 | error_push((Error){ | 322 | error_push((Error){ |
@@ -549,7 +583,7 @@ parse_tree(Chunk *chunk, Compiler *compiler) { | |||
549 | Chunk * | 583 | Chunk * |
550 | compile(Token *tokens) { | 584 | compile(Token *tokens) { |
551 | Chunk *chunk = NULL; | 585 | Chunk *chunk = NULL; |
552 | chunk = NEW_CHUNK("main"); | 586 | chunk = NEW_CHUNK(""); |
553 | Compiler compiler = (Compiler){ | 587 | Compiler compiler = (Compiler){ |
554 | .tokens = tokens, | 588 | .tokens = tokens, |
555 | .current = 0, | 589 | .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[] = { | |||
11 | // Load/store ops. | 11 | // Load/store ops. |
12 | [OP_CONSTANT] = "OP_CONSTANT", | 12 | [OP_CONSTANT] = "OP_CONSTANT", |
13 | [OP_LOCAL] = "OP_LOCAL", | 13 | [OP_LOCAL] = "OP_LOCAL", |
14 | [OP_DEF_LOCAL] = "OP_DEF_LOCAL", | ||
15 | [OP_SET_LOCAL] = "OP_SET_LOCAL", | ||
14 | [OP_DEF] = "OP_DEF", | 16 | [OP_DEF] = "OP_DEF", |
15 | [OP_SET] = "OP_SET", | 17 | [OP_SET] = "OP_SET", |
16 | [OP_GET] = "OP_GET", | 18 | [OP_GET] = "OP_GET", |
@@ -46,7 +48,11 @@ static const char* ops_str[] = { | |||
46 | 48 | ||
47 | void | 49 | void |
48 | disassemble_chunk(Chunk *chunk) { | 50 | disassemble_chunk(Chunk *chunk) { |
49 | printf("===== %.*s =====\n", (int)array_size(chunk->name), chunk->name); | 51 | if (array_size(chunk->name) < 1) { |
52 | printf("===== main =====\n"); | ||
53 | } else { | ||
54 | printf("===== %.*s =====\n", (int)array_size(chunk->name), chunk->name); | ||
55 | } | ||
50 | printf("code:\n"); | 56 | printf("code:\n"); |
51 | size_t offset = 0; | 57 | size_t offset = 0; |
52 | while (offset < array_size(chunk->code)) { | 58 | 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[] = { | |||
17 | [ERR_DIVISION_BY_ZERO] = "error: division by zero", | 17 | [ERR_DIVISION_BY_ZERO] = "error: division by zero", |
18 | [ERR_PC_OOB] = "error: pc out of bounds", | 18 | [ERR_PC_OOB] = "error: pc out of bounds", |
19 | [ERR_EMPTY_CHUNK] = "error: empty chunk", | 19 | [ERR_EMPTY_CHUNK] = "error: empty chunk", |
20 | [ERR_AMBIGUOUS_PARAMS] = "error: ambiguous parameter names", | ||
20 | }; | 21 | }; |
21 | 22 | ||
22 | static Error errors[ERR_MAX_NUMBER]; | 23 | 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 { | |||
24 | ERR_TOO_MANY_ARGS, | 24 | ERR_TOO_MANY_ARGS, |
25 | ERR_WRONG_ARG_TYPE, | 25 | ERR_WRONG_ARG_TYPE, |
26 | ERR_DIVISION_BY_ZERO, | 26 | ERR_DIVISION_BY_ZERO, |
27 | ERR_AMBIGUOUS_PARAMS, | ||
27 | 28 | ||
28 | // Bytecode interpreter. | 29 | // Bytecode interpreter. |
29 | ERR_PC_OOB, | 30 | 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) { | |||
116 | pairs[probe_position].key = &table->keys[array_size(table->keys) - 1]; | 116 | pairs[probe_position].key = &table->keys[array_size(table->keys) - 1]; |
117 | pairs[probe_position].value = &table->values[array_size(table->values) - 1]; | 117 | pairs[probe_position].value = &table->values[array_size(table->values) - 1]; |
118 | } else { | 118 | } else { |
119 | object_free(*pairs[probe_position].value); | ||
120 | *pairs[probe_position].value = value; | 119 | *pairs[probe_position].value = value; |
121 | } | 120 | } |
122 | } | 121 | } |
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 { | |||
5 | // Load/store ops. | 5 | // Load/store ops. |
6 | OP_CONSTANT, | 6 | OP_CONSTANT, |
7 | OP_LOCAL, | 7 | OP_LOCAL, |
8 | OP_DEF_LOCAL, | ||
9 | OP_SET_LOCAL, | ||
8 | OP_DEF, | 10 | OP_DEF, |
9 | OP_SET, | 11 | OP_SET, |
10 | OP_GET, | 12 | 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); | |||
18 | // Write a character to the given output stream. | 18 | // Write a character to the given output stream. |
19 | void sv_write(const StringView *sv); | 19 | void sv_write(const StringView *sv); |
20 | 20 | ||
21 | #define STRING(STR) (StringView){(STR), sizeof(STR)} | 21 | #define STRING(STR) (StringView){(STR), sizeof(STR) - 1} |
22 | 22 | ||
23 | #endif // BDL_STRINGVIEW_H | 23 | #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) { | |||
183 | Object obj = frame->chunk->constants[constant]; | 183 | Object obj = frame->chunk->constants[constant]; |
184 | array_push(vm->stack, obj); | 184 | array_push(vm->stack, obj); |
185 | } break; | 185 | } break; |
186 | case OP_DEF_LOCAL: { | ||
187 | Object value = array_pop(vm->stack); | ||
188 | ssize_t idx = AS_FIXNUM(array_pop(vm->stack)); | ||
189 | vm->stack[frame->stack_offset + idx] = value; | ||
190 | } break; | ||
186 | case OP_DEF: { | 191 | case OP_DEF: { |
187 | Object value = array_pop(vm->stack); | 192 | Object value = array_pop(vm->stack); |
188 | Object name = array_pop(vm->stack); | 193 | Object name = array_pop(vm->stack); |
@@ -281,12 +286,15 @@ vm_interpret(VM *vm) { | |||
281 | } else if (n_args > n_params) { | 286 | } else if (n_args > n_params) { |
282 | RUNTIME_ERROR(ERR_TOO_MANY_ARGS); | 287 | RUNTIME_ERROR(ERR_TOO_MANY_ARGS); |
283 | } | 288 | } |
289 | ssize_t n_locals = array_size(proc.chunk->locals) - n_params; | ||
284 | 290 | ||
285 | // DEBUG:... | 291 | #ifdef DEBUG |
286 | // disassemble_chunk(proc.chunk); | 292 | disassemble_chunk(proc.chunk); |
293 | #endif | ||
287 | 294 | ||
288 | // Tail-call optimization. | 295 | // Tail-call optimization. |
289 | if (proc.chunk != frame->chunk || *vm->pc != OP_RETURN) { | 296 | if (proc.chunk != frame->chunk || *vm->pc != OP_RETURN) { |
297 | // Prepare new call frame. | ||
290 | CallFrame new_frame = (CallFrame){ | 298 | CallFrame new_frame = (CallFrame){ |
291 | .chunk = proc.chunk, | 299 | .chunk = proc.chunk, |
292 | .rp = vm->pc, | 300 | .rp = vm->pc, |
@@ -294,14 +302,20 @@ vm_interpret(VM *vm) { | |||
294 | }; | 302 | }; |
295 | array_push(vm->frames, new_frame); | 303 | array_push(vm->frames, new_frame); |
296 | frame = &vm->frames[array_size(vm->frames) - 1]; | 304 | frame = &vm->frames[array_size(vm->frames) - 1]; |
305 | |||
306 | // Prepare local slots. | ||
307 | for (ssize_t i = 0; i < n_locals; i++) { | ||
308 | array_push(vm->stack, NIL_VAL); | ||
309 | } | ||
297 | } else { | 310 | } else { |
298 | // Bind tail-call parameters. | 311 | // Bind tail-call parameters. |
299 | for (size_t i = 0; i < (size_t)n_params; i++) { | 312 | for (ssize_t i = 0; i < n_params; i++) { |
300 | Object obj = array_peek(vm->stack, n_params - 1 - i); | 313 | Object obj = array_peek(vm->stack, n_locals + n_params - 1 - i); |
301 | vm->stack[frame->stack_offset + i] = obj; | 314 | vm->stack[frame->stack_offset + i] = obj; |
302 | } | 315 | } |
316 | |||
303 | // Reset stack size. | 317 | // Reset stack size. |
304 | array_head(vm->stack)->size = frame->stack_offset + n_params; | 318 | array_head(vm->stack)->size = frame->stack_offset + n_params + n_locals; |
305 | } | 319 | } |
306 | vm->pc = frame->chunk->code; | 320 | vm->pc = frame->chunk->code; |
307 | } break; | 321 | } break; |