diff options
author | Bad Diode <bd@badd10de.dev> | 2021-10-27 13:59:25 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2021-10-27 13:59:25 +0200 |
commit | ad659dca44892a5f2ba4e088603a819af58a5819 (patch) | |
tree | 9743e49b7be6a93bf718a6dbe4a3e7faf4c54d09 /src/bytecode/compiler.h | |
parent | f0cd7a3cab56a6f8d7b4520aaa168a271a94d6d4 (diff) | |
download | bdl-ad659dca44892a5f2ba4e088603a819af58a5819.tar.gz bdl-ad659dca44892a5f2ba4e088603a819af58a5819.zip |
Add support for lexically scoped local variables
Diffstat (limited to 'src/bytecode/compiler.h')
-rwxr-xr-x | src/bytecode/compiler.h | 109 |
1 files changed, 85 insertions, 24 deletions
diff --git a/src/bytecode/compiler.h b/src/bytecode/compiler.h index 7497ea7..f9e1da1 100755 --- a/src/bytecode/compiler.h +++ b/src/bytecode/compiler.h | |||
@@ -4,16 +4,49 @@ | |||
4 | #include "chunk.h" | 4 | #include "chunk.h" |
5 | #include "lexer.h" | 5 | #include "lexer.h" |
6 | 6 | ||
7 | #define MAX_DEPTH 1024 | ||
8 | |||
9 | typedef struct Scope { | ||
10 | StringView *params; | ||
11 | StringView *locals; | ||
12 | } Scope; | ||
13 | |||
7 | typedef struct Compiler { | 14 | typedef struct Compiler { |
8 | Token *tokens; | 15 | Token *tokens; |
9 | size_t current; | 16 | size_t current; |
17 | size_t scope_depth; | ||
18 | Scope scopes[MAX_DEPTH]; | ||
10 | } Compiler; | 19 | } Compiler; |
11 | 20 | ||
21 | |||
12 | // Mimics the functionality in the Scanner functions, but for entire tokens. | 22 | // Mimics the functionality in the Scanner functions, but for entire tokens. |
13 | Token next_token(Compiler *compiler); | 23 | Token next_token(Compiler *compiler); |
14 | Token peek_token(const Compiler *compiler); | 24 | Token peek_token(const Compiler *compiler); |
15 | bool has_next_token(const Compiler *compiler); | 25 | bool has_next_token(const Compiler *compiler); |
16 | 26 | ||
27 | // Scope initialization/exit. | ||
28 | void enter_scope(Compiler *compiler); | ||
29 | void exit_scope(Compiler *compiler); | ||
30 | |||
31 | void | ||
32 | enter_scope(Compiler *compiler) { | ||
33 | Scope *scope = &compiler->scopes[compiler->scope_depth++]; | ||
34 | array_init(scope->params, 0); | ||
35 | array_init(scope->locals, 0); | ||
36 | } | ||
37 | |||
38 | void | ||
39 | exit_scope(Compiler *compiler) { | ||
40 | Scope *scope = &compiler->scopes[--compiler->scope_depth]; | ||
41 | array_free(scope->params); | ||
42 | array_free(scope->locals); | ||
43 | } | ||
44 | |||
45 | Scope * | ||
46 | get_current_scope(Compiler *compiler) { | ||
47 | return &compiler->scopes[compiler->scope_depth - 1]; | ||
48 | } | ||
49 | |||
17 | Chunk * compile(Token *tokens); | 50 | Chunk * compile(Token *tokens); |
18 | 51 | ||
19 | Token | 52 | Token |
@@ -32,10 +65,9 @@ has_next_token(const Compiler *compiler) { | |||
32 | } | 65 | } |
33 | 66 | ||
34 | ssize_t | 67 | ssize_t |
35 | find_local_index(Chunk *chunk, Token tok) { | 68 | find_local_index(Scope *scope, Token tok) { |
36 | // NOTE: This is dumb and potentially slow. | 69 | for (size_t i = 0; i < array_size(scope->locals); i++) { |
37 | for (size_t i = 0; i < array_size(chunk->locals); i++) { | 70 | if (sv_equal(&tok.value, &scope->locals[i])) { |
38 | if (sv_equal(&tok.value, &chunk->locals[i])) { | ||
39 | return i; | 71 | return i; |
40 | } | 72 | } |
41 | } | 73 | } |
@@ -217,29 +249,49 @@ compile_declare_op(Chunk *chunk, Compiler *compiler, Token start, Ops op) { | |||
217 | }); | 249 | }); |
218 | return; | 250 | return; |
219 | } | 251 | } |
220 | // TODO: If we are inside a function and we are using OP_DEF, we just | 252 | |
221 | // declare the local variable directly in the stack. No need for symbols! | 253 | if (compiler->scope_depth <= 1) { |
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); | 254 | Object obj = make_symbol(name.value); |
227 | emit_constant(chunk, name, obj); | 255 | emit_constant(chunk, name, obj); |
228 | } else { | 256 | } 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) { | 257 | if (op == OP_DEF) { |
237 | op = OP_DEF_LOCAL; | 258 | op = OP_DEF_LOCAL; |
259 | // Check if the local is already registered. | ||
260 | Scope *scope = get_current_scope(compiler); | ||
261 | ssize_t idx = find_local_index(scope, name); | ||
262 | if (idx < 0) { | ||
263 | array_push(scope->locals, name.value); | ||
264 | idx = chunk->n_locals++; | ||
265 | } | ||
266 | emit_constant(chunk, name, FIXNUM_VAL(idx)); | ||
238 | } else if (op == OP_SET) { | 267 | } else if (op == OP_SET) { |
239 | op = OP_SET_LOCAL; | 268 | size_t depth = compiler->scope_depth - 1; |
269 | ssize_t idx = -1; | ||
270 | // Check if name is local in this or any previous scope. | ||
271 | do { | ||
272 | Scope *scope = &compiler->scopes[depth]; | ||
273 | idx = find_local_index(scope, name); | ||
274 | if (idx >= 0) { | ||
275 | break; | ||
276 | } | ||
277 | depth--; | ||
278 | } while (depth > 0); | ||
279 | |||
280 | if (idx >= 0) { | ||
281 | // If the value is found emit OP_SET_LOCAL with tree parameters: | ||
282 | // The new value, the scope depth, and the scope index. | ||
283 | op = OP_SET_LOCAL; | ||
284 | emit_constant(chunk, name, FIXNUM_VAL(depth)); | ||
285 | emit_constant(chunk, name, FIXNUM_VAL(idx)); | ||
286 | } else { | ||
287 | // If not found at all, emit set for the global scope. | ||
288 | Object obj = make_symbol(name.value); | ||
289 | emit_constant(chunk, name, obj); | ||
290 | } | ||
240 | } | 291 | } |
241 | emit_constant(chunk, name, FIXNUM_VAL(idx)); | ||
242 | } | 292 | } |
293 | // NOTE: We can have compiler support for preemptively finding if globals | ||
294 | // exist or not. | ||
243 | 295 | ||
244 | Token tok = peek_token(compiler); | 296 | Token tok = peek_token(compiler); |
245 | if (name.type == TOKEN_EOF || tok.type == TOKEN_EOF) { | 297 | if (name.type == TOKEN_EOF || tok.type == TOKEN_EOF) { |
@@ -276,6 +328,7 @@ compile_declare_op(Chunk *chunk, Compiler *compiler, Token start, Ops op) { | |||
276 | 328 | ||
277 | void | 329 | void |
278 | compile_lambda(Chunk *chunk, Compiler *compiler, Token start, StringView name) { | 330 | compile_lambda(Chunk *chunk, Compiler *compiler, Token start, StringView name) { |
331 | enter_scope(compiler); | ||
279 | Object fun = make_lambda(name); | 332 | Object fun = make_lambda(name); |
280 | 333 | ||
281 | // Prepeare parameters. | 334 | // Prepeare parameters. |
@@ -305,7 +358,8 @@ compile_lambda(Chunk *chunk, Compiler *compiler, Token start, StringView name) { | |||
305 | return; | 358 | return; |
306 | } | 359 | } |
307 | // Check if parameters name already exists. | 360 | // Check if parameters name already exists. |
308 | ssize_t idx = find_local_index(fun.chunk, tok); | 361 | Scope *scope = get_current_scope(compiler); |
362 | ssize_t idx = find_local_index(scope, tok); | ||
309 | if (idx >= 0) { | 363 | if (idx >= 0) { |
310 | error_push((Error){ | 364 | error_push((Error){ |
311 | .type = ERR_TYPE_COMPILER, | 365 | .type = ERR_TYPE_COMPILER, |
@@ -315,8 +369,10 @@ compile_lambda(Chunk *chunk, Compiler *compiler, Token start, StringView name) { | |||
315 | }); | 369 | }); |
316 | return; | 370 | return; |
317 | } | 371 | } |
318 | array_push(fun.chunk->params, tok.value); | 372 | array_push(scope->params, tok.value); |
319 | array_push(fun.chunk->locals, tok.value); | 373 | array_push(scope->locals, tok.value); |
374 | fun.chunk->n_params++; | ||
375 | fun.chunk->n_locals++; | ||
320 | } | 376 | } |
321 | } else if (tok.type != TOKEN_NIL) { | 377 | } else if (tok.type != TOKEN_NIL) { |
322 | error_push((Error){ | 378 | error_push((Error){ |
@@ -348,6 +404,7 @@ compile_lambda(Chunk *chunk, Compiler *compiler, Token start, StringView name) { | |||
348 | } | 404 | } |
349 | add_code(fun.chunk, OP_RETURN, start.line, start.column); | 405 | add_code(fun.chunk, OP_RETURN, start.line, start.column); |
350 | emit_constant(chunk, start, fun); | 406 | emit_constant(chunk, start, fun); |
407 | exit_scope(compiler); | ||
351 | } | 408 | } |
352 | 409 | ||
353 | void | 410 | void |
@@ -551,7 +608,8 @@ parse_tree(Chunk *chunk, Compiler *compiler) { | |||
551 | return; | 608 | return; |
552 | } break; | 609 | } break; |
553 | case TOKEN_SYMBOL: { | 610 | case TOKEN_SYMBOL: { |
554 | ssize_t idx = find_local_index(chunk, tok); | 611 | Scope *scope = get_current_scope(compiler); |
612 | ssize_t idx = find_local_index(scope, tok); | ||
555 | if (idx < 0) { | 613 | if (idx < 0) { |
556 | Object obj = make_symbol(tok.value); | 614 | Object obj = make_symbol(tok.value); |
557 | emit_constant(chunk, tok, obj); | 615 | emit_constant(chunk, tok, obj); |
@@ -583,11 +641,13 @@ parse_tree(Chunk *chunk, Compiler *compiler) { | |||
583 | Chunk * | 641 | Chunk * |
584 | compile(Token *tokens) { | 642 | compile(Token *tokens) { |
585 | Chunk *chunk = NULL; | 643 | Chunk *chunk = NULL; |
586 | chunk = NEW_CHUNK(""); | 644 | chunk = NEW_CHUNK("main"); |
587 | Compiler compiler = (Compiler){ | 645 | Compiler compiler = (Compiler){ |
588 | .tokens = tokens, | 646 | .tokens = tokens, |
589 | .current = 0, | 647 | .current = 0, |
648 | .scope_depth = 0, | ||
590 | }; | 649 | }; |
650 | enter_scope(&compiler); | ||
591 | Token main_start = peek_token(&compiler); | 651 | Token main_start = peek_token(&compiler); |
592 | while (has_next_token(&compiler)) { | 652 | while (has_next_token(&compiler)) { |
593 | Token start = peek_token(&compiler); | 653 | Token start = peek_token(&compiler); |
@@ -598,6 +658,7 @@ compile(Token *tokens) { | |||
598 | add_code(chunk, OP_DROP, start.line, start.column); | 658 | add_code(chunk, OP_DROP, start.line, start.column); |
599 | } | 659 | } |
600 | add_code(chunk, OP_RETURN, main_start.line, main_start.column); | 660 | add_code(chunk, OP_RETURN, main_start.line, main_start.column); |
661 | exit_scope(&compiler); | ||
601 | return chunk; | 662 | return chunk; |
602 | } | 663 | } |
603 | 664 | ||