diff options
-rwxr-xr-x | Makefile | 4 | ||||
-rw-r--r-- | src/bytecode/chunk.c | 2 | ||||
-rwxr-xr-x | src/bytecode/compiler.h | 72 | ||||
-rwxr-xr-x | src/bytecode/debug.h | 2 | ||||
-rw-r--r-- | src/bytecode/hashtable.h | 4 | ||||
-rwxr-xr-x | src/bytecode/main.c | 3 | ||||
-rw-r--r-- | src/bytecode/objects.c | 29 | ||||
-rwxr-xr-x | src/bytecode/objects.h | 30 | ||||
-rwxr-xr-x | src/bytecode/ops.h | 3 | ||||
-rwxr-xr-x | src/bytecode/vm.h | 53 |
10 files changed, 139 insertions, 63 deletions
@@ -20,7 +20,7 @@ CFLAGS += $(INC_FLAGS) | |||
20 | LDFLAGS := | 20 | LDFLAGS := |
21 | LDLIBS := | 21 | LDLIBS := |
22 | RELEASE_CFLAGS := -DNDEBUG -O2 -static | 22 | RELEASE_CFLAGS := -DNDEBUG -O2 -static |
23 | DEBUG_CFLAGS := -DDEBUG -O0 -g -fsanitize=address | 23 | DEBUG_CFLAGS := -DDEBUG -O0 -g |
24 | 24 | ||
25 | .PHONY: build tests run clean | 25 | .PHONY: build tests run clean |
26 | 26 | ||
@@ -30,6 +30,8 @@ DEBUG_CFLAGS := -DDEBUG -O0 -g -fsanitize=address | |||
30 | DEBUG ?= 0 | 30 | DEBUG ?= 0 |
31 | ifeq ($(DEBUG), 1) | 31 | ifeq ($(DEBUG), 1) |
32 | CFLAGS += $(DEBUG_CFLAGS) | 32 | CFLAGS += $(DEBUG_CFLAGS) |
33 | else ifeq ($(DEBUG), 2) | ||
34 | CFLAGS += $(DEBUG_CFLAGS) -fsanitize=address | ||
33 | else | 35 | else |
34 | CFLAGS += $(RELEASE_CFLAGS) | 36 | CFLAGS += $(RELEASE_CFLAGS) |
35 | endif | 37 | endif |
diff --git a/src/bytecode/chunk.c b/src/bytecode/chunk.c index 8ff6acf..af4a3a2 100644 --- a/src/bytecode/chunk.c +++ b/src/bytecode/chunk.c | |||
@@ -19,7 +19,7 @@ chunk_free(Chunk *chunk) { | |||
19 | array_free(chunk->code); | 19 | array_free(chunk->code); |
20 | for (size_t i = 0; i < array_size(chunk->constants); i++) { | 20 | for (size_t i = 0; i < array_size(chunk->constants); i++) { |
21 | Object obj = chunk->constants[i]; | 21 | Object obj = chunk->constants[i]; |
22 | object_free(obj); | 22 | object_free(&obj); |
23 | } | 23 | } |
24 | array_free(chunk->constants); | 24 | array_free(chunk->constants); |
25 | array_free(chunk->lines); | 25 | array_free(chunk->lines); |
diff --git a/src/bytecode/compiler.h b/src/bytecode/compiler.h index 124c345..09e49a1 100755 --- a/src/bytecode/compiler.h +++ b/src/bytecode/compiler.h | |||
@@ -9,6 +9,7 @@ | |||
9 | typedef struct Scope { | 9 | typedef struct Scope { |
10 | StringView *params; | 10 | StringView *params; |
11 | StringView *locals; | 11 | StringView *locals; |
12 | StringView *captured; | ||
12 | } Scope; | 13 | } Scope; |
13 | 14 | ||
14 | typedef struct Compiler { | 15 | typedef struct Compiler { |
@@ -33,6 +34,7 @@ enter_scope(Compiler *compiler) { | |||
33 | Scope *scope = &compiler->scopes[compiler->scope_depth++]; | 34 | Scope *scope = &compiler->scopes[compiler->scope_depth++]; |
34 | array_init(scope->params, 0); | 35 | array_init(scope->params, 0); |
35 | array_init(scope->locals, 0); | 36 | array_init(scope->locals, 0); |
37 | array_init(scope->captured, 0); | ||
36 | } | 38 | } |
37 | 39 | ||
38 | void | 40 | void |
@@ -40,6 +42,7 @@ exit_scope(Compiler *compiler) { | |||
40 | Scope *scope = &compiler->scopes[--compiler->scope_depth]; | 42 | Scope *scope = &compiler->scopes[--compiler->scope_depth]; |
41 | array_free(scope->params); | 43 | array_free(scope->params); |
42 | array_free(scope->locals); | 44 | array_free(scope->locals); |
45 | array_free(scope->captured); | ||
43 | } | 46 | } |
44 | 47 | ||
45 | Scope * | 48 | Scope * |
@@ -74,6 +77,16 @@ find_local_index(Scope *scope, Token tok) { | |||
74 | return -1; | 77 | return -1; |
75 | } | 78 | } |
76 | 79 | ||
80 | ssize_t | ||
81 | find_captued_index(Scope *scope, Token tok) { | ||
82 | for (size_t i = 0; i < array_size(scope->captured); i++) { | ||
83 | if (sv_equal(&tok.value, &scope->captured[i])) { | ||
84 | return i; | ||
85 | } | ||
86 | } | ||
87 | return -1; | ||
88 | } | ||
89 | |||
77 | void | 90 | void |
78 | emit_constant(Chunk *chunk, Token tok, Object obj) { | 91 | emit_constant(Chunk *chunk, Token tok, Object obj) { |
79 | size_t prev_size = array_size(chunk->constants); | 92 | size_t prev_size = array_size(chunk->constants); |
@@ -84,7 +97,7 @@ emit_constant(Chunk *chunk, Token tok, Object obj) { | |||
84 | // If the non value constant was already present we need to properly free | 97 | // If the non value constant was already present we need to properly free |
85 | // the memory from the object given to this function. | 98 | // the memory from the object given to this function. |
86 | if (prev_size == array_size(chunk->constants)) { | 99 | if (prev_size == array_size(chunk->constants)) { |
87 | object_free(obj); | 100 | object_free(&obj); |
88 | } | 101 | } |
89 | } | 102 | } |
90 | 103 | ||
@@ -329,7 +342,8 @@ compile_declare_op(Chunk *chunk, Compiler *compiler, Token start, Ops op) { | |||
329 | void | 342 | void |
330 | compile_lambda(Chunk *chunk, Compiler *compiler, Token start, StringView name) { | 343 | compile_lambda(Chunk *chunk, Compiler *compiler, Token start, StringView name) { |
331 | enter_scope(compiler); | 344 | enter_scope(compiler); |
332 | Object fun = make_lambda(name); | 345 | Chunk *proc_chunk = chunk_init(name); |
346 | Object fun = make_lambda(proc_chunk); | ||
333 | 347 | ||
334 | // Prepeare parameters. | 348 | // Prepeare parameters. |
335 | Token tok = next_token(compiler); | 349 | Token tok = next_token(compiler); |
@@ -371,8 +385,8 @@ compile_lambda(Chunk *chunk, Compiler *compiler, Token start, StringView name) { | |||
371 | } | 385 | } |
372 | array_push(scope->params, tok.value); | 386 | array_push(scope->params, tok.value); |
373 | array_push(scope->locals, tok.value); | 387 | array_push(scope->locals, tok.value); |
374 | fun.chunk->n_params++; | 388 | fun.closure->chunk->n_params++; |
375 | fun.chunk->n_locals++; | 389 | fun.closure->chunk->n_locals++; |
376 | } | 390 | } |
377 | } else if (tok.type != TOKEN_NIL) { | 391 | } else if (tok.type != TOKEN_NIL) { |
378 | error_push((Error){ | 392 | error_push((Error){ |
@@ -400,9 +414,9 @@ compile_lambda(Chunk *chunk, Compiler *compiler, Token start, StringView name) { | |||
400 | next_token(compiler); | 414 | next_token(compiler); |
401 | break; | 415 | break; |
402 | } | 416 | } |
403 | parse_tree(fun.chunk, compiler); | 417 | parse_tree(fun.closure->chunk, compiler); |
404 | } | 418 | } |
405 | add_code(fun.chunk, OP_RETURN, start.line, start.column); | 419 | add_code(fun.closure->chunk, OP_RETURN, start.line, start.column); |
406 | emit_constant(chunk, start, fun); | 420 | emit_constant(chunk, start, fun); |
407 | exit_scope(compiler); | 421 | exit_scope(compiler); |
408 | } | 422 | } |
@@ -609,23 +623,49 @@ parse_tree(Chunk *chunk, Compiler *compiler) { | |||
609 | } break; | 623 | } break; |
610 | case TOKEN_SYMBOL: { | 624 | case TOKEN_SYMBOL: { |
611 | if (compiler->scope_depth > 1) { | 625 | if (compiler->scope_depth > 1) { |
612 | size_t depth = compiler->scope_depth - 1; | 626 | Scope *current_scope = get_current_scope(compiler); |
613 | ssize_t idx = -1; | 627 | ssize_t idx = -1; |
614 | do { | 628 | // Check if the variable was already captured. |
615 | Scope *scope = &compiler->scopes[depth]; | 629 | idx = find_captued_index(current_scope, tok); |
616 | idx = find_local_index(scope, tok); | 630 | if (idx >= 0) { |
617 | if (idx >= 0) { | 631 | emit_constant(chunk, tok, FIXNUM_VAL(idx)); |
618 | break; | 632 | add_code(chunk, OP_CAPTURED, tok.line, tok.column); |
619 | } | 633 | return; |
620 | depth--; | 634 | } |
621 | } while (depth > 0); | ||
622 | 635 | ||
636 | // Check current scope locals. If we find it, emit OP_LOCAL. | ||
637 | idx = find_local_index(current_scope, tok); | ||
623 | if (idx >= 0) { | 638 | if (idx >= 0) { |
624 | emit_constant(chunk, tok, FIXNUM_VAL(depth)); | ||
625 | emit_constant(chunk, tok, FIXNUM_VAL(idx)); | 639 | emit_constant(chunk, tok, FIXNUM_VAL(idx)); |
626 | add_code(chunk, OP_LOCAL, tok.line, tok.column); | 640 | add_code(chunk, OP_LOCAL, tok.line, tok.column); |
627 | return; | 641 | return; |
628 | } | 642 | } |
643 | |||
644 | // Descend scopes, if we find the symbol at a different depth, | ||
645 | // we need to capture the variable. | ||
646 | size_t depth = compiler->scope_depth - 2; | ||
647 | while (depth > 0) { | ||
648 | Scope *scope = &compiler->scopes[depth]; | ||
649 | idx = find_local_index(scope, tok); | ||
650 | if (idx >= 0) { | ||
651 | // Capture this local. | ||
652 | emit_constant(chunk, tok, FIXNUM_VAL(idx)); | ||
653 | emit_constant(chunk, tok, FIXNUM_VAL(depth)); | ||
654 | add_code(chunk, OP_CAPTURE_LOCAL, tok.line, tok.column); | ||
655 | |||
656 | // Put captured variable on stack. | ||
657 | ssize_t captured_idx = array_size(current_scope->captured); | ||
658 | emit_constant(chunk, tok, FIXNUM_VAL(captured_idx)); | ||
659 | add_code(chunk, OP_CAPTURED, tok.line, tok.column); | ||
660 | array_push(current_scope->captured, tok.value); | ||
661 | return; | ||
662 | } | ||
663 | depth--; | ||
664 | } | ||
665 | |||
666 | // TODO: Capture globals? | ||
667 | |||
668 | // NOTE: set! must know how to deal also with captured vars. | ||
629 | } | 669 | } |
630 | 670 | ||
631 | Object obj = make_symbol(tok.value); | 671 | Object obj = make_symbol(tok.value); |
diff --git a/src/bytecode/debug.h b/src/bytecode/debug.h index e34b65f..7078744 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_CAPTURED] = "OP_CAPTURED", | ||
15 | [OP_CAPTURE_LOCAL] = "OP_CAPTURE_LOCAL", | ||
14 | [OP_DEF_LOCAL] = "OP_DEF_LOCAL", | 16 | [OP_DEF_LOCAL] = "OP_DEF_LOCAL", |
15 | [OP_SET_LOCAL] = "OP_SET_LOCAL", | 17 | [OP_SET_LOCAL] = "OP_SET_LOCAL", |
16 | [OP_DEF] = "OP_DEF", | 18 | [OP_DEF] = "OP_DEF", |
diff --git a/src/bytecode/hashtable.h b/src/bytecode/hashtable.h index 1f55048..7c0c380 100644 --- a/src/bytecode/hashtable.h +++ b/src/bytecode/hashtable.h | |||
@@ -151,7 +151,7 @@ _ht_maybe_grow(HashTable *table) { | |||
151 | // Free old arrays. | 151 | // Free old arrays. |
152 | array_free(old_pairs); | 152 | array_free(old_pairs); |
153 | for (size_t i = 0; i < array_size(old_keys); i++) { | 153 | for (size_t i = 0; i < array_size(old_keys); i++) { |
154 | object_free(old_keys[i]); | 154 | object_free(&old_keys[i]); |
155 | } | 155 | } |
156 | array_free(old_keys); | 156 | array_free(old_keys); |
157 | array_free(old_values); | 157 | array_free(old_values); |
@@ -198,7 +198,7 @@ ht_free(HashTable *table) { | |||
198 | } | 198 | } |
199 | array_free(table->pairs); | 199 | array_free(table->pairs); |
200 | for (size_t i = 0; i < array_size(table->keys); i++) { | 200 | for (size_t i = 0; i < array_size(table->keys); i++) { |
201 | object_free(table->keys[i]); | 201 | object_free(&table->keys[i]); |
202 | } | 202 | } |
203 | array_free(table->keys); | 203 | array_free(table->keys); |
204 | array_free(table->values); | 204 | array_free(table->values); |
diff --git a/src/bytecode/main.c b/src/bytecode/main.c index 7f2042e..4b80f71 100755 --- a/src/bytecode/main.c +++ b/src/bytecode/main.c | |||
@@ -57,8 +57,9 @@ process_source(const StringView *source) { | |||
57 | #endif | 57 | #endif |
58 | 58 | ||
59 | // Interpret chunk. | 59 | // Interpret chunk. |
60 | Object main_proc = make_lambda(main); | ||
60 | CallFrame frame = (CallFrame){ | 61 | CallFrame frame = (CallFrame){ |
61 | .chunk = main, | 62 | .closure = main_proc.closure, |
62 | }; | 63 | }; |
63 | array_push(vm.frames, frame); | 64 | array_push(vm.frames, frame); |
64 | vm_interpret(&vm); | 65 | vm_interpret(&vm); |
diff --git a/src/bytecode/objects.c b/src/bytecode/objects.c index 3b4a2eb..c2fb989 100644 --- a/src/bytecode/objects.c +++ b/src/bytecode/objects.c | |||
@@ -23,11 +23,13 @@ make_symbol(StringView sv) { | |||
23 | } | 23 | } |
24 | 24 | ||
25 | Object | 25 | Object |
26 | make_lambda(StringView name) { | 26 | make_lambda(Chunk *chunk) { |
27 | Object obj = { | 27 | Object obj = { |
28 | .type = OBJ_TYPE_LAMBDA, | 28 | .type = OBJ_TYPE_LAMBDA, |
29 | .chunk = chunk_init(name), | ||
30 | }; | 29 | }; |
30 | obj.closure = malloc(sizeof(Closure)); | ||
31 | obj.closure->chunk = chunk; | ||
32 | array_init(obj.closure->values, 0); | ||
31 | return obj; | 33 | return obj; |
32 | } | 34 | } |
33 | 35 | ||
@@ -58,8 +60,9 @@ object_display(Object obj) { | |||
58 | // printf(")"); | 60 | // printf(")"); |
59 | } break; | 61 | } break; |
60 | case OBJ_TYPE_LAMBDA: { | 62 | case OBJ_TYPE_LAMBDA: { |
63 | Chunk *chunk = obj.closure->chunk; | ||
61 | printf("#{procedure:%.*s}", | 64 | printf("#{procedure:%.*s}", |
62 | (int)array_size(obj.chunk->name), obj.chunk->name); | 65 | (int)array_size(chunk->name), chunk->name); |
63 | } break; | 66 | } break; |
64 | case OBJ_TYPE_ERR: { | 67 | case OBJ_TYPE_ERR: { |
65 | printf("#{error}"); | 68 | printf("#{error}"); |
@@ -69,13 +72,21 @@ object_display(Object obj) { | |||
69 | } | 72 | } |
70 | 73 | ||
71 | void | 74 | void |
72 | object_free(Object obj) { | 75 | object_free(Object *obj) { |
73 | if (IS_STRING(obj) || IS_SYMBOL(obj)) { | 76 | if (IS_STRING(*obj) || IS_SYMBOL(*obj)) { |
74 | array_free(obj.text); | 77 | array_free(obj->text); |
75 | return; | 78 | return; |
76 | } | 79 | } |
77 | if (IS_LAMBDA(obj)) { | 80 | if (IS_LAMBDA(*obj)) { |
78 | chunk_free(obj.chunk); | 81 | Closure *closure = obj->closure; |
82 | for (size_t i = 0; i < array_size(closure->values); i++) { | ||
83 | object_free(&closure->values[i]); | ||
84 | } | ||
85 | array_free(closure->values); | ||
86 | // NOTE: we are leaking memory without this, we'll need a GC | ||
87 | // soon... | ||
88 | // chunk_free(closure->chunk); | ||
89 | free(closure); | ||
79 | } | 90 | } |
80 | } | 91 | } |
81 | 92 | ||
@@ -104,7 +115,7 @@ object_equal(Object a, Object b) { | |||
104 | } | 115 | } |
105 | } break; | 116 | } break; |
106 | case OBJ_TYPE_LAMBDA: { | 117 | case OBJ_TYPE_LAMBDA: { |
107 | return a.chunk == b.chunk; | 118 | return a.closure == b.closure; |
108 | } break; | 119 | } break; |
109 | default: { | 120 | default: { |
110 | return false; | 121 | return false; |
diff --git a/src/bytecode/objects.h b/src/bytecode/objects.h index 6c286b5..a9a7d0f 100755 --- a/src/bytecode/objects.h +++ b/src/bytecode/objects.h | |||
@@ -17,6 +17,20 @@ typedef enum ObjectType { | |||
17 | OBJ_TYPE_ERR, | 17 | OBJ_TYPE_ERR, |
18 | } ObjectType; | 18 | } ObjectType; |
19 | 19 | ||
20 | typedef struct Object Object; | ||
21 | |||
22 | typedef struct Closure { | ||
23 | // Non-owning reference to a chunk. | ||
24 | Chunk *chunk; | ||
25 | // Captured values for this closure. | ||
26 | Object *values; | ||
27 | } Closure; | ||
28 | |||
29 | // typdef struct ConsCell { | ||
30 | // struct Object *car; | ||
31 | // struct Object *cdr; | ||
32 | // } ConsCell; | ||
33 | |||
20 | typedef struct Object { | 34 | typedef struct Object { |
21 | ObjectType type; | 35 | ObjectType type; |
22 | bool marked; | 36 | bool marked; |
@@ -29,26 +43,18 @@ typedef struct Object { | |||
29 | char *text; | 43 | char *text; |
30 | 44 | ||
31 | // OBJ_TYPE_PAIR | 45 | // OBJ_TYPE_PAIR |
32 | // struct { | 46 | // ConsCell *cons_cell; |
33 | // struct Object *car; | ||
34 | // struct Object *cdr; | ||
35 | // }; | ||
36 | 47 | ||
37 | // OBJ_TYPE_LAMBDA | 48 | // OBJ_TYPE_LAMBDA |
38 | Chunk *chunk; | 49 | Closure *closure; |
39 | // struct { | ||
40 | // struct Object *params; | ||
41 | // struct Object *body; | ||
42 | // struct Environment *env; | ||
43 | // }; | ||
44 | }; | 50 | }; |
45 | } Object; | 51 | } Object; |
46 | 52 | ||
47 | Object make_string(StringView sv); | 53 | Object make_string(StringView sv); |
48 | Object make_symbol(StringView sv); | 54 | Object make_symbol(StringView sv); |
49 | Object make_lambda(StringView name); | 55 | Object make_lambda(Chunk *chunk); |
50 | void object_display(Object obj); | 56 | void object_display(Object obj); |
51 | void object_free(Object obj); | 57 | void object_free(Object *obj); |
52 | bool object_equal(Object a, Object b); | 58 | bool object_equal(Object a, Object b); |
53 | Object object_copy(Object src); | 59 | Object object_copy(Object src); |
54 | 60 | ||
diff --git a/src/bytecode/ops.h b/src/bytecode/ops.h index 52c774a..d45c27e 100755 --- a/src/bytecode/ops.h +++ b/src/bytecode/ops.h | |||
@@ -5,6 +5,9 @@ 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_CAPTURED, | ||
9 | OP_CAPTURE_LOCAL, | ||
10 | // OP_SET_CAPTURED, | ||
8 | OP_DEF_LOCAL, | 11 | OP_DEF_LOCAL, |
9 | OP_SET_LOCAL, | 12 | OP_SET_LOCAL, |
10 | OP_DEF, | 13 | OP_DEF, |
diff --git a/src/bytecode/vm.h b/src/bytecode/vm.h index 3fba3d7..a32d0a1 100755 --- a/src/bytecode/vm.h +++ b/src/bytecode/vm.h | |||
@@ -13,7 +13,7 @@ | |||
13 | 13 | ||
14 | typedef struct CallFrame { | 14 | typedef struct CallFrame { |
15 | // Current code being run. | 15 | // Current code being run. |
16 | Chunk *chunk; | 16 | Closure *closure; |
17 | // Current program counter for this call. | 17 | // Current program counter for this call. |
18 | // Ref to stack. Is this really needed? | 18 | // Ref to stack. Is this really needed? |
19 | // Object *locals; | 19 | // Object *locals; |
@@ -65,7 +65,7 @@ vm_reset(VM *vm) { | |||
65 | fprintf(stderr, "stack trace:\n"); \ | 65 | fprintf(stderr, "stack trace:\n"); \ |
66 | for (ssize_t i = array_size(vm->frames) - 1; i >= 0 ; i--) { \ | 66 | for (ssize_t i = array_size(vm->frames) - 1; i >= 0 ; i--) { \ |
67 | CallFrame frame = vm->frames[i]; \ | 67 | CallFrame frame = vm->frames[i]; \ |
68 | Chunk *chunk = frame.chunk; \ | 68 | Chunk *chunk = frame.closure->chunk; \ |
69 | size_t instruction = vm->pc - chunk->code - 1; \ | 69 | size_t instruction = vm->pc - chunk->code - 1; \ |
70 | fprintf(stderr, "\t%-4ld -> ", i); \ | 70 | fprintf(stderr, "\t%-4ld -> ", i); \ |
71 | fprintf(stderr, "%.*s",(int)array_size(chunk->name), chunk->name); \ | 71 | fprintf(stderr, "%.*s",(int)array_size(chunk->name), chunk->name); \ |
@@ -80,8 +80,8 @@ vm_reset(VM *vm) { | |||
80 | error_push((Error){ \ | 80 | error_push((Error){ \ |
81 | .type = ERR_TYPE_RUNTIME, \ | 81 | .type = ERR_TYPE_RUNTIME, \ |
82 | .value = (ERR), \ | 82 | .value = (ERR), \ |
83 | .line = frame->chunk->lines[vm->pc - frame->chunk->code - 1].line, \ | 83 | .line = chunk->lines[vm->pc - chunk->code - 1].line, \ |
84 | .col = frame->chunk->lines[vm->pc - frame->chunk->code - 1].col, \ | 84 | .col = chunk->lines[vm->pc - chunk->code - 1].col, \ |
85 | }); \ | 85 | }); \ |
86 | STACK_TRACE() \ | 86 | STACK_TRACE() \ |
87 | return | 87 | return |
@@ -149,10 +149,11 @@ vm_reset(VM *vm) { | |||
149 | void | 149 | void |
150 | vm_interpret(VM *vm) { | 150 | vm_interpret(VM *vm) { |
151 | CallFrame *frame = &vm->frames[0]; | 151 | CallFrame *frame = &vm->frames[0]; |
152 | vm->pc = frame->chunk->code; | 152 | Chunk *chunk = frame->closure->chunk; |
153 | vm->pc = chunk->code; | ||
153 | frame->rp = NULL; | 154 | frame->rp = NULL; |
154 | 155 | ||
155 | if (frame->chunk->code == NULL || array_size(frame->chunk->code) == 0) { | 156 | if (chunk->code == NULL || array_size(chunk->code) == 0) { |
156 | error_push((Error){ | 157 | error_push((Error){ |
157 | .type = ERR_TYPE_RUNTIME, | 158 | .type = ERR_TYPE_RUNTIME, |
158 | .value = ERR_EMPTY_CHUNK, | 159 | .value = ERR_EMPTY_CHUNK, |
@@ -170,22 +171,31 @@ vm_interpret(VM *vm) { | |||
170 | } | 171 | } |
171 | } | 172 | } |
172 | printf(" ]\nop: "); | 173 | printf(" ]\nop: "); |
173 | disassemble_instruction(frame->chunk, (vm->pc - frame->chunk->code)); | 174 | disassemble_instruction(chunk, (vm->pc - chunk->code)); |
174 | #endif | 175 | #endif |
175 | u8 instruction = *vm->pc++; | 176 | u8 instruction = *vm->pc++; |
176 | switch (instruction) { | 177 | switch (instruction) { |
177 | case OP_LOCAL: { | 178 | case OP_LOCAL: { |
178 | ssize_t idx = AS_FIXNUM(array_pop(vm->stack)); | 179 | ssize_t idx = AS_FIXNUM(array_pop(vm->stack)); |
179 | ssize_t depth = AS_FIXNUM(array_pop(vm->stack)); | 180 | CallFrame frame = vm->frames[array_size(vm->frames) - 1]; |
180 | depth = array_size(vm->frames) - depth; | ||
181 | CallFrame frame = vm->frames[depth]; | ||
182 | array_push(vm->stack, vm->stack[frame.stack_offset + idx]); | 181 | array_push(vm->stack, vm->stack[frame.stack_offset + idx]); |
183 | } break; | 182 | } break; |
184 | case OP_CONSTANT: { | 183 | case OP_CONSTANT: { |
185 | u8 constant = *vm->pc++; | 184 | u8 constant = *vm->pc++; |
186 | Object obj = frame->chunk->constants[constant]; | 185 | Object obj = chunk->constants[constant]; |
187 | array_push(vm->stack, obj); | 186 | array_push(vm->stack, obj); |
188 | } break; | 187 | } break; |
188 | case OP_CAPTURED: { | ||
189 | assert(false && "not implemented"); | ||
190 | } break; | ||
191 | case OP_CAPTURE_LOCAL: { | ||
192 | assert(false && "not implemented"); | ||
193 | // Object value = array_pop(vm->stack); | ||
194 | // ssize_t idx = AS_FIXNUM(array_pop(vm->stack)); | ||
195 | // ssize_t depth = AS_FIXNUM(array_pop(vm->stack)); | ||
196 | // CallFrame frame = vm->frames[depth]; | ||
197 | // vm->stack[frame.stack_offset + idx] = value; | ||
198 | } break; | ||
189 | case OP_DEF_LOCAL: { | 199 | case OP_DEF_LOCAL: { |
190 | Object value = array_pop(vm->stack); | 200 | Object value = array_pop(vm->stack); |
191 | ssize_t idx = AS_FIXNUM(array_pop(vm->stack)); | 201 | ssize_t idx = AS_FIXNUM(array_pop(vm->stack)); |
@@ -194,8 +204,7 @@ vm_interpret(VM *vm) { | |||
194 | case OP_SET_LOCAL: { | 204 | case OP_SET_LOCAL: { |
195 | Object value = array_pop(vm->stack); | 205 | Object value = array_pop(vm->stack); |
196 | ssize_t idx = AS_FIXNUM(array_pop(vm->stack)); | 206 | ssize_t idx = AS_FIXNUM(array_pop(vm->stack)); |
197 | ssize_t depth = AS_FIXNUM(array_pop(vm->stack)); | 207 | CallFrame frame = vm->frames[array_size(vm->frames) - 1]; |
198 | CallFrame frame = vm->frames[depth]; | ||
199 | vm->stack[frame.stack_offset + idx] = value; | 208 | vm->stack[frame.stack_offset + idx] = value; |
200 | } break; | 209 | } break; |
201 | case OP_DEF: { | 210 | case OP_DEF: { |
@@ -290,8 +299,8 @@ vm_interpret(VM *vm) { | |||
290 | // Check the number of arguments is correct. | 299 | // Check the number of arguments is correct. |
291 | // NOTE: This is probably better handled at compilation, but for | 300 | // NOTE: This is probably better handled at compilation, but for |
292 | // now this is simpler to implement. | 301 | // now this is simpler to implement. |
293 | ssize_t n_params = proc.chunk->n_params; | 302 | ssize_t n_params = proc.closure->chunk->n_params; |
294 | ssize_t n_locals = proc.chunk->n_locals - n_params; | 303 | ssize_t n_locals = proc.closure->chunk->n_locals - n_params; |
295 | if (n_args < n_params) { | 304 | if (n_args < n_params) { |
296 | RUNTIME_ERROR(ERR_NOT_ENOUGH_ARGS); | 305 | RUNTIME_ERROR(ERR_NOT_ENOUGH_ARGS); |
297 | } else if (n_args > n_params) { | 306 | } else if (n_args > n_params) { |
@@ -299,20 +308,21 @@ vm_interpret(VM *vm) { | |||
299 | } | 308 | } |
300 | 309 | ||
301 | #ifdef DEBUG | 310 | #ifdef DEBUG |
302 | disassemble_chunk(proc.chunk); | 311 | disassemble_chunk(proc.closure->chunk); |
303 | printf("n_locals: %ld\n", n_locals); | 312 | printf("n_locals: %ld\n", n_locals); |
304 | printf("n_params: %ld\n", n_params); | 313 | printf("n_params: %ld\n", n_params); |
305 | #endif | 314 | #endif |
306 | // Tail-call optimization. | 315 | // Tail-call optimization. |
307 | if (proc.chunk != frame->chunk || *vm->pc != OP_RETURN) { | 316 | if (proc.closure->chunk != chunk || *vm->pc != OP_RETURN) { |
308 | // Prepare new call frame. | 317 | // Prepare new call frame. |
309 | CallFrame new_frame = (CallFrame){ | 318 | CallFrame new_frame = (CallFrame){ |
310 | .chunk = proc.chunk, | 319 | .closure = proc.closure, |
311 | .rp = vm->pc, | 320 | .rp = vm->pc, |
312 | .stack_offset = array_size(vm->stack) - n_params, | 321 | .stack_offset = array_size(vm->stack) - n_params, |
313 | }; | 322 | }; |
314 | array_push(vm->frames, new_frame); | 323 | array_push(vm->frames, new_frame); |
315 | frame = &vm->frames[array_size(vm->frames) - 1]; | 324 | frame = &vm->frames[array_size(vm->frames) - 1]; |
325 | chunk = frame->closure->chunk; | ||
316 | 326 | ||
317 | // Prepare local slots. | 327 | // Prepare local slots. |
318 | for (ssize_t i = 0; i < n_locals; i++) { | 328 | for (ssize_t i = 0; i < n_locals; i++) { |
@@ -330,7 +340,7 @@ vm_interpret(VM *vm) { | |||
330 | size_t offset = frame->stack_offset + n_params + n_locals; | 340 | size_t offset = frame->stack_offset + n_params + n_locals; |
331 | array_head(vm->stack)->size = offset; | 341 | array_head(vm->stack)->size = offset; |
332 | } | 342 | } |
333 | vm->pc = frame->chunk->code; | 343 | vm->pc = chunk->code; |
334 | } break; | 344 | } break; |
335 | case OP_RETURN: { | 345 | case OP_RETURN: { |
336 | if (frame->rp == NULL) { | 346 | if (frame->rp == NULL) { |
@@ -347,6 +357,7 @@ vm_interpret(VM *vm) { | |||
347 | array_head(vm->stack)->size = frame->stack_offset; | 357 | array_head(vm->stack)->size = frame->stack_offset; |
348 | array_push(vm->stack, ret); | 358 | array_push(vm->stack, ret); |
349 | frame = &vm->frames[array_size(vm->frames) - 1]; | 359 | frame = &vm->frames[array_size(vm->frames) - 1]; |
360 | chunk = frame->closure->chunk; | ||
350 | } break; | 361 | } break; |
351 | case OP_DROP: { | 362 | case OP_DROP: { |
352 | array_head(vm->stack)->size = 0; | 363 | array_head(vm->stack)->size = 0; |
@@ -360,8 +371,8 @@ vm_interpret(VM *vm) { | |||
360 | error_push((Error){ | 371 | error_push((Error){ |
361 | .type = ERR_TYPE_RUNTIME, | 372 | .type = ERR_TYPE_RUNTIME, |
362 | .value = ERR_PC_OOB, | 373 | .value = ERR_PC_OOB, |
363 | .line = frame->chunk->lines[0].line, | 374 | .line = chunk->lines[0].line, |
364 | .col = frame->chunk->lines[0].col, | 375 | .col = chunk->lines[0].col, |
365 | }); | 376 | }); |
366 | } | 377 | } |
367 | 378 | ||