diff options
Diffstat (limited to 'src/bytecode/compiler.h')
-rwxr-xr-x | src/bytecode/compiler.h | 748 |
1 files changed, 0 insertions, 748 deletions
diff --git a/src/bytecode/compiler.h b/src/bytecode/compiler.h deleted file mode 100755 index 5f38216..0000000 --- a/src/bytecode/compiler.h +++ /dev/null | |||
@@ -1,748 +0,0 @@ | |||
1 | #ifndef BDL_COMPILER_H | ||
2 | #define BDL_COMPILER_H | ||
3 | |||
4 | #include "chunk.h" | ||
5 | #include "lexer.h" | ||
6 | |||
7 | #define MAX_DEPTH 1024 | ||
8 | |||
9 | typedef struct Scope { | ||
10 | StringView *params; | ||
11 | StringView *locals; | ||
12 | Token *captured; | ||
13 | } Scope; | ||
14 | |||
15 | typedef struct Compiler { | ||
16 | Token *tokens; | ||
17 | size_t current; | ||
18 | size_t scope_depth; | ||
19 | Scope scopes[MAX_DEPTH]; | ||
20 | } Compiler; | ||
21 | |||
22 | |||
23 | // Mimics the functionality in the Scanner functions, but for entire tokens. | ||
24 | Token next_token(Compiler *compiler); | ||
25 | Token peek_token(const Compiler *compiler); | ||
26 | bool has_next_token(const Compiler *compiler); | ||
27 | |||
28 | // Scope initialization/exit. | ||
29 | void enter_scope(Compiler *compiler); | ||
30 | void exit_scope(Compiler *compiler); | ||
31 | |||
32 | void | ||
33 | enter_scope(Compiler *compiler) { | ||
34 | Scope *scope = &compiler->scopes[compiler->scope_depth++]; | ||
35 | array_init(scope->params, 0); | ||
36 | array_init(scope->locals, 0); | ||
37 | array_init(scope->captured, 0); | ||
38 | } | ||
39 | |||
40 | void | ||
41 | exit_scope(Compiler *compiler) { | ||
42 | Scope *scope = &compiler->scopes[--compiler->scope_depth]; | ||
43 | array_free(scope->params); | ||
44 | array_free(scope->locals); | ||
45 | array_free(scope->captured); | ||
46 | } | ||
47 | |||
48 | Scope * | ||
49 | get_current_scope(Compiler *compiler) { | ||
50 | return &compiler->scopes[compiler->scope_depth - 1]; | ||
51 | } | ||
52 | |||
53 | Chunk * compile(Token *tokens); | ||
54 | |||
55 | Token | ||
56 | peek_token(const Compiler *compiler) { | ||
57 | return compiler->tokens[compiler->current]; | ||
58 | } | ||
59 | |||
60 | Token | ||
61 | next_token(Compiler *compiler) { | ||
62 | return compiler->tokens[compiler->current++]; | ||
63 | } | ||
64 | |||
65 | bool | ||
66 | has_next_token(const Compiler *compiler) { | ||
67 | return compiler->current < array_size(compiler->tokens); | ||
68 | } | ||
69 | |||
70 | ssize_t | ||
71 | find_local_index(Scope *scope, Token tok) { | ||
72 | for (size_t i = 0; i < array_size(scope->locals); i++) { | ||
73 | if (sv_equal(&tok.value, &scope->locals[i])) { | ||
74 | return i; | ||
75 | } | ||
76 | } | ||
77 | return -1; | ||
78 | } | ||
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].value)) { | ||
84 | return i; | ||
85 | } | ||
86 | } | ||
87 | return -1; | ||
88 | } | ||
89 | |||
90 | void | ||
91 | emit_constant(Chunk *chunk, Token tok, Object obj) { | ||
92 | size_t prev_size = array_size(chunk->constants); | ||
93 | size_t num_idx = add_constant(chunk, obj); | ||
94 | add_code(chunk, OP_CONSTANT, tok.line, tok.column); | ||
95 | add_code(chunk, num_idx, tok.line, tok.column); | ||
96 | |||
97 | // If the non value constant was already present we need to properly free | ||
98 | // the memory from the object given to this function. | ||
99 | if (prev_size == array_size(chunk->constants)) { | ||
100 | object_free(&obj); | ||
101 | } | ||
102 | } | ||
103 | |||
104 | size_t | ||
105 | emit_jump(Chunk *chunk, Token tok, Ops op, ssize_t offset) { | ||
106 | add_code(chunk, op, tok.line, tok.column); | ||
107 | add_code(chunk, ((u16)offset >> 8) & 0xFF, tok.line, tok.column); | ||
108 | add_code(chunk, ((u16)offset) & 0xFF, tok.line, tok.column); | ||
109 | return array_size(chunk->code) - 2; | ||
110 | } | ||
111 | |||
112 | void | ||
113 | patch_jump(Chunk *chunk, ssize_t offset) { | ||
114 | ssize_t jump = array_size(chunk->code) - offset - 2; | ||
115 | assert((u16)jump <= UINT16_MAX && "error: jump is too long"); | ||
116 | chunk->code[offset] = ((u16)jump >> 8) & 0xFF; | ||
117 | chunk->code[offset + 1] = ((u16)jump) & 0xFF; | ||
118 | } | ||
119 | |||
120 | void | ||
121 | parse_fixnum(Chunk *chunk, Token tok) { | ||
122 | ssize_t num = 0; | ||
123 | int sign = 1; | ||
124 | for (size_t i = 0; i < tok.value.n; i++) { | ||
125 | char c = tok.value.start[i]; | ||
126 | if (c == '-') { | ||
127 | sign = -1; | ||
128 | continue; | ||
129 | } | ||
130 | num = num * 10 + (c - '0'); | ||
131 | } | ||
132 | emit_constant(chunk, tok, FIXNUM_VAL(num * sign)); | ||
133 | } | ||
134 | |||
135 | void | ||
136 | parse_symbol(Chunk *chunk, Compiler *compiler, Token tok) { | ||
137 | if (compiler->scope_depth > 1) { | ||
138 | Scope *current_scope = get_current_scope(compiler); | ||
139 | ssize_t idx = -1; | ||
140 | // Check if the variable was already captured. | ||
141 | idx = find_captued_index(current_scope, tok); | ||
142 | if (idx >= 0) { | ||
143 | emit_constant(chunk, tok, FIXNUM_VAL(idx)); | ||
144 | add_code(chunk, OP_CAPTURED, tok.line, tok.column); | ||
145 | return; | ||
146 | } | ||
147 | |||
148 | // Check current scope locals. If we find it, emit OP_LOCAL. | ||
149 | idx = find_local_index(current_scope, tok); | ||
150 | if (idx >= 0) { | ||
151 | emit_constant(chunk, tok, FIXNUM_VAL(idx)); | ||
152 | add_code(chunk, OP_LOCAL, tok.line, tok.column); | ||
153 | return; | ||
154 | } | ||
155 | |||
156 | // Descend scopes, if we find the symbol at a different depth, | ||
157 | // we need to capture the variable. | ||
158 | size_t depth = compiler->scope_depth - 2; | ||
159 | while (depth > 0) { | ||
160 | Scope *scope = &compiler->scopes[depth]; | ||
161 | idx = find_local_index(scope, tok); | ||
162 | if (idx >= 0) { | ||
163 | // Put captured variable on stack. | ||
164 | ssize_t captured_idx = array_size(current_scope->captured); | ||
165 | emit_constant(chunk, tok, FIXNUM_VAL(captured_idx)); | ||
166 | add_code(chunk, OP_CAPTURED, tok.line, tok.column); | ||
167 | array_push(current_scope->captured, tok); | ||
168 | return; | ||
169 | } | ||
170 | depth--; | ||
171 | } | ||
172 | |||
173 | // TODO: Capture globals? | ||
174 | } | ||
175 | |||
176 | Object obj = make_symbol(tok.value); | ||
177 | emit_constant(chunk, tok, obj); | ||
178 | add_code(chunk, OP_GET, tok.line, tok.column); | ||
179 | } | ||
180 | |||
181 | void parse_tree(Chunk *chunk, Compiler *compiler); | ||
182 | |||
183 | void | ||
184 | compile_list_binary_op(Chunk *chunk, Compiler *compiler, Token start, Ops op) { | ||
185 | size_t n = 0; | ||
186 | while (has_next_token(compiler)) { | ||
187 | Token tok = peek_token(compiler); | ||
188 | if (tok.type == TOKEN_EOF) { | ||
189 | error_push((Error){ | ||
190 | .type = ERR_TYPE_COMPILER, | ||
191 | .value = ERR_UNBALANCED_PAREN, | ||
192 | .line = start.line, | ||
193 | .col = start.column, | ||
194 | }); | ||
195 | return; | ||
196 | } | ||
197 | if (tok.type == TOKEN_RPAREN) { | ||
198 | next_token(compiler); | ||
199 | if (n <= 1) { | ||
200 | error_push((Error){ | ||
201 | .type = ERR_TYPE_COMPILER, | ||
202 | .value = ERR_NOT_ENOUGH_ARGS, | ||
203 | .line = start.line, | ||
204 | .col = start.column, | ||
205 | }); | ||
206 | return; | ||
207 | } | ||
208 | break; | ||
209 | } | ||
210 | parse_tree(chunk, compiler); | ||
211 | n++; | ||
212 | } | ||
213 | emit_constant(chunk, start, FIXNUM_VAL(n)); | ||
214 | add_code(chunk, op, start.line, start.column); | ||
215 | } | ||
216 | |||
217 | void | ||
218 | compile_list_unary_op(Chunk *chunk, Compiler *compiler, Token start, Ops op) { | ||
219 | size_t n = 0; | ||
220 | while (has_next_token(compiler)) { | ||
221 | Token tok = peek_token(compiler); | ||
222 | if (tok.type == TOKEN_EOF) { | ||
223 | error_push((Error){ | ||
224 | .type = ERR_TYPE_COMPILER, | ||
225 | .value = ERR_UNBALANCED_PAREN, | ||
226 | .line = start.line, | ||
227 | .col = start.column, | ||
228 | }); | ||
229 | return; | ||
230 | } | ||
231 | if (tok.type == TOKEN_RPAREN) { | ||
232 | next_token(compiler); | ||
233 | if (n == 0) { | ||
234 | error_push((Error){ | ||
235 | .type = ERR_TYPE_COMPILER, | ||
236 | .value = ERR_NOT_ENOUGH_ARGS, | ||
237 | .line = start.line, | ||
238 | .col = start.column, | ||
239 | }); | ||
240 | } | ||
241 | return; | ||
242 | } | ||
243 | parse_tree(chunk, compiler); | ||
244 | add_code(chunk, op, start.line, start.column); | ||
245 | n++; | ||
246 | if (n > 1) { | ||
247 | error_push((Error){ | ||
248 | .type = ERR_TYPE_COMPILER, | ||
249 | .value = ERR_TOO_MANY_ARGS, | ||
250 | .line = start.line, | ||
251 | .col = start.column, | ||
252 | }); | ||
253 | } | ||
254 | } | ||
255 | error_push((Error){ | ||
256 | .type = ERR_TYPE_COMPILER, | ||
257 | .value = ERR_UNBALANCED_PAREN, | ||
258 | .line = start.line, | ||
259 | .col = start.column, | ||
260 | }); | ||
261 | } | ||
262 | |||
263 | void | ||
264 | compile_list_simple_op(Chunk *chunk, Compiler *compiler, Token start, Ops op) { | ||
265 | if (has_next_token(compiler)) { | ||
266 | Token tok = peek_token(compiler); | ||
267 | if (tok.type == TOKEN_EOF) { | ||
268 | error_push((Error){ | ||
269 | .type = ERR_TYPE_COMPILER, | ||
270 | .value = ERR_UNBALANCED_PAREN, | ||
271 | .line = start.line, | ||
272 | .col = start.column, | ||
273 | }); | ||
274 | return; | ||
275 | } | ||
276 | if (tok.type != TOKEN_RPAREN) { | ||
277 | error_push((Error){ | ||
278 | .type = ERR_TYPE_COMPILER, | ||
279 | .value = ERR_TOO_MANY_ARGS, | ||
280 | .line = start.line, | ||
281 | .col = start.column, | ||
282 | }); | ||
283 | return; | ||
284 | } | ||
285 | next_token(compiler); | ||
286 | add_code(chunk, op, start.line, start.column); | ||
287 | return; | ||
288 | } | ||
289 | error_push((Error){ | ||
290 | .type = ERR_TYPE_COMPILER, | ||
291 | .value = ERR_UNBALANCED_PAREN, | ||
292 | .line = start.line, | ||
293 | .col = start.column, | ||
294 | }); | ||
295 | } | ||
296 | |||
297 | #define STR_ARRAY(ARR) (StringView){(ARR), array_size(ARR)} | ||
298 | |||
299 | void | ||
300 | compile_declare_op(Chunk *chunk, Compiler *compiler, Token start, Ops op) { | ||
301 | Token name = next_token(compiler); | ||
302 | if (name.type != TOKEN_SYMBOL) { | ||
303 | error_push((Error){ | ||
304 | .type = ERR_TYPE_COMPILER, | ||
305 | .value = ERR_WRONG_ARG_TYPE, | ||
306 | .line = start.line, | ||
307 | .col = start.column, | ||
308 | }); | ||
309 | return; | ||
310 | } | ||
311 | |||
312 | if (compiler->scope_depth <= 1) { | ||
313 | Object obj = make_symbol(name.value); | ||
314 | emit_constant(chunk, name, obj); | ||
315 | } else { | ||
316 | if (op == OP_DEF) { | ||
317 | op = OP_DEF_LOCAL; | ||
318 | // Check if the local is already registered. | ||
319 | Scope *scope = get_current_scope(compiler); | ||
320 | ssize_t idx = find_local_index(scope, name); | ||
321 | if (idx < 0) { | ||
322 | array_push(scope->locals, name.value); | ||
323 | idx = chunk->n_locals++; | ||
324 | } | ||
325 | emit_constant(chunk, name, FIXNUM_VAL(idx)); | ||
326 | } else if (op == OP_SET) { | ||
327 | // FIXME: This is fucking ugly. | ||
328 | Scope *current_scope = get_current_scope(compiler); | ||
329 | ssize_t idx = -1; | ||
330 | // Check if the variable was already captured. | ||
331 | idx = find_captued_index(current_scope, name); | ||
332 | if (idx >= 0) { | ||
333 | emit_constant(chunk, name, FIXNUM_VAL(idx)); | ||
334 | op = OP_SET_CAPTURED; | ||
335 | } else { | ||
336 | idx = find_local_index(current_scope, name); | ||
337 | if (idx >= 0) { | ||
338 | emit_constant(chunk, name, FIXNUM_VAL(idx)); | ||
339 | op = OP_SET_LOCAL; | ||
340 | } else { | ||
341 | size_t depth = compiler->scope_depth - 2; | ||
342 | while (depth > 0) { | ||
343 | Scope *scope = &compiler->scopes[depth]; | ||
344 | idx = find_local_index(scope, name); | ||
345 | if (idx >= 0) { | ||
346 | op = OP_SET_CAPTURED; | ||
347 | ssize_t captured_idx = array_size(current_scope->captured); | ||
348 | emit_constant(chunk, name, FIXNUM_VAL(captured_idx)); | ||
349 | array_push(current_scope->captured, name); | ||
350 | break; | ||
351 | } | ||
352 | depth--; | ||
353 | } | ||
354 | if (idx < 0) { | ||
355 | Object obj = make_symbol(name.value); | ||
356 | emit_constant(chunk, name, obj); | ||
357 | } | ||
358 | } | ||
359 | } | ||
360 | } | ||
361 | } | ||
362 | // NOTE: We can have compiler support for preemptively finding if globals | ||
363 | // exist or not. | ||
364 | |||
365 | Token tok = peek_token(compiler); | ||
366 | if (name.type == TOKEN_EOF || tok.type == TOKEN_EOF) { | ||
367 | error_push((Error){ | ||
368 | .type = ERR_TYPE_COMPILER, | ||
369 | .value = ERR_UNBALANCED_PAREN, | ||
370 | .line = start.line, | ||
371 | .col = start.column, | ||
372 | }); | ||
373 | return; | ||
374 | } | ||
375 | if (name.type == TOKEN_RPAREN || tok.type == TOKEN_RPAREN) { | ||
376 | error_push((Error){ | ||
377 | .type = ERR_TYPE_COMPILER, | ||
378 | .value = ERR_NOT_ENOUGH_ARGS, | ||
379 | .line = start.line, | ||
380 | .col = start.column, | ||
381 | }); | ||
382 | return; | ||
383 | } | ||
384 | parse_tree(chunk, compiler); | ||
385 | if (peek_token(compiler).type != TOKEN_RPAREN) { | ||
386 | error_push((Error){ | ||
387 | .type = ERR_TYPE_COMPILER, | ||
388 | .value = ERR_TOO_MANY_ARGS, | ||
389 | .line = start.line, | ||
390 | .col = start.column, | ||
391 | }); | ||
392 | return; | ||
393 | } | ||
394 | next_token(compiler); | ||
395 | add_code(chunk, op, start.line, start.column); | ||
396 | } | ||
397 | |||
398 | void | ||
399 | compile_lambda(Chunk *chunk, Compiler *compiler, Token start, StringView name) { | ||
400 | enter_scope(compiler); | ||
401 | Chunk *proc_chunk = chunk_init(name); | ||
402 | Object fun = make_lambda(proc_chunk); | ||
403 | |||
404 | // Prepeare parameters. | ||
405 | Token tok = next_token(compiler); | ||
406 | if (tok.type == TOKEN_LPAREN){ | ||
407 | while (has_next_token(compiler)) { | ||
408 | Token tok = next_token(compiler); | ||
409 | if (tok.type == TOKEN_EOF) { | ||
410 | error_push((Error){ | ||
411 | .type = ERR_TYPE_COMPILER, | ||
412 | .value = ERR_UNBALANCED_PAREN, | ||
413 | .line = start.line, | ||
414 | .col = start.column, | ||
415 | }); | ||
416 | return; | ||
417 | } | ||
418 | if (tok.type == TOKEN_RPAREN) { | ||
419 | break; | ||
420 | } | ||
421 | if (tok.type != TOKEN_SYMBOL) { | ||
422 | error_push((Error){ | ||
423 | .type = ERR_TYPE_COMPILER, | ||
424 | .value = ERR_WRONG_ARG_TYPE, | ||
425 | .line = tok.line, | ||
426 | .col = tok.column, | ||
427 | }); | ||
428 | return; | ||
429 | } | ||
430 | // Check if parameters name already exists. | ||
431 | Scope *scope = get_current_scope(compiler); | ||
432 | ssize_t idx = find_local_index(scope, tok); | ||
433 | if (idx >= 0) { | ||
434 | error_push((Error){ | ||
435 | .type = ERR_TYPE_COMPILER, | ||
436 | .value = ERR_AMBIGUOUS_PARAMS, | ||
437 | .line = tok.line, | ||
438 | .col = tok.column, | ||
439 | }); | ||
440 | return; | ||
441 | } | ||
442 | array_push(scope->params, tok.value); | ||
443 | array_push(scope->locals, tok.value); | ||
444 | fun.closure->chunk->n_params++; | ||
445 | fun.closure->chunk->n_locals++; | ||
446 | } | ||
447 | } else if (tok.type != TOKEN_NIL) { | ||
448 | error_push((Error){ | ||
449 | .type = ERR_TYPE_COMPILER, | ||
450 | .value = ERR_WRONG_ARG_TYPE, | ||
451 | .line = tok.line, | ||
452 | .col = tok.column, | ||
453 | }); | ||
454 | return; | ||
455 | } | ||
456 | |||
457 | // Compile body. | ||
458 | while (has_next_token(compiler)) { | ||
459 | Token tok = peek_token(compiler); | ||
460 | if (tok.type == TOKEN_EOF) { | ||
461 | error_push((Error){ | ||
462 | .type = ERR_TYPE_COMPILER, | ||
463 | .value = ERR_UNBALANCED_PAREN, | ||
464 | .line = start.line, | ||
465 | .col = start.column, | ||
466 | }); | ||
467 | return; | ||
468 | } | ||
469 | if (tok.type == TOKEN_RPAREN) { | ||
470 | next_token(compiler); | ||
471 | break; | ||
472 | } | ||
473 | parse_tree(fun.closure->chunk, compiler); | ||
474 | } | ||
475 | add_code(fun.closure->chunk, OP_RETURN, start.line, start.column); | ||
476 | |||
477 | // Prepare closure value capture. | ||
478 | Scope *scope = get_current_scope(compiler); | ||
479 | size_t n_captured = array_size(scope->captured); | ||
480 | if (n_captured > 0) { | ||
481 | compiler->scope_depth--; | ||
482 | for (size_t i = 0; i < n_captured; i++) { | ||
483 | Token tok = scope->captured[i]; | ||
484 | parse_symbol(chunk, compiler, tok); | ||
485 | } | ||
486 | // Number of captured values. | ||
487 | emit_constant(chunk, tok, FIXNUM_VAL(n_captured)); | ||
488 | |||
489 | // Push created lambda to stack. | ||
490 | emit_constant(chunk, start, fun); | ||
491 | |||
492 | // Emit capture local instruction. | ||
493 | add_code(chunk, OP_CAPTURE_LOCAL, tok.line, tok.column); | ||
494 | compiler->scope_depth++; | ||
495 | } else { | ||
496 | emit_constant(chunk, start, fun); | ||
497 | } | ||
498 | exit_scope(compiler); | ||
499 | } | ||
500 | |||
501 | void | ||
502 | compile_fun_op(Chunk *chunk, Compiler *compiler, Token start) { | ||
503 | Token name = peek_token(compiler); | ||
504 | if (name.type != TOKEN_SYMBOL) { | ||
505 | error_push((Error){ | ||
506 | .type = ERR_TYPE_COMPILER, | ||
507 | .value = ERR_WRONG_ARG_TYPE, | ||
508 | .line = start.line, | ||
509 | .col = start.column, | ||
510 | }); | ||
511 | return; | ||
512 | } | ||
513 | Object obj = make_symbol(name.value); | ||
514 | emit_constant(chunk, name, obj); | ||
515 | next_token(compiler); | ||
516 | |||
517 | compile_lambda(chunk, compiler, start, name.value); | ||
518 | add_code(chunk, OP_DEF, start.line, start.column); | ||
519 | } | ||
520 | |||
521 | void | ||
522 | compile_call_op(Chunk *chunk, Compiler *compiler, Token start, Token name) { | ||
523 | if (name.type == TOKEN_SYMBOL) { | ||
524 | parse_symbol(chunk, compiler, name); | ||
525 | } | ||
526 | |||
527 | // Compile body. | ||
528 | size_t n = 0; | ||
529 | while (has_next_token(compiler)) { | ||
530 | Token tok = peek_token(compiler); | ||
531 | if (tok.type == TOKEN_EOF) { | ||
532 | error_push((Error){ | ||
533 | .type = ERR_TYPE_COMPILER, | ||
534 | .value = ERR_UNBALANCED_PAREN, | ||
535 | .line = start.line, | ||
536 | .col = start.column, | ||
537 | }); | ||
538 | return; | ||
539 | } | ||
540 | if (tok.type == TOKEN_RPAREN) { | ||
541 | next_token(compiler); | ||
542 | break; | ||
543 | } | ||
544 | parse_tree(chunk, compiler); | ||
545 | n++; | ||
546 | } | ||
547 | emit_constant(chunk, start, FIXNUM_VAL(n)); | ||
548 | add_code(chunk, OP_CALL, start.line, start.column); | ||
549 | } | ||
550 | |||
551 | void | ||
552 | compile_if_op(Chunk *chunk, Compiler *compiler, Token start) { | ||
553 | Token tok = peek_token(compiler); | ||
554 | if (tok.type == TOKEN_EOF) { | ||
555 | error_push((Error){ | ||
556 | .type = ERR_TYPE_COMPILER, | ||
557 | .value = ERR_UNBALANCED_PAREN, | ||
558 | .line = start.line, | ||
559 | .col = start.column, | ||
560 | }); | ||
561 | return; | ||
562 | } | ||
563 | if (tok.type == TOKEN_RPAREN) { | ||
564 | error_push((Error){ | ||
565 | .type = ERR_TYPE_COMPILER, | ||
566 | .value = ERR_NOT_ENOUGH_ARGS, | ||
567 | .line = start.line, | ||
568 | .col = start.column, | ||
569 | }); | ||
570 | return; | ||
571 | } | ||
572 | |||
573 | // Condition. | ||
574 | parse_tree(chunk, compiler); | ||
575 | size_t jmp_false = emit_jump(chunk, start, OP_JUMP_IF_FALSE, 0xFFFF); | ||
576 | |||
577 | // True expression. | ||
578 | parse_tree(chunk, compiler); | ||
579 | |||
580 | // No second expression. | ||
581 | if (peek_token(compiler).type == TOKEN_RPAREN) { | ||
582 | patch_jump(chunk, jmp_false); | ||
583 | next_token(compiler); | ||
584 | return; | ||
585 | } | ||
586 | |||
587 | // False expression. | ||
588 | size_t jmp_end = emit_jump(chunk, start, OP_JUMP, 0xFFFF); | ||
589 | patch_jump(chunk, jmp_false); | ||
590 | parse_tree(chunk, compiler); | ||
591 | patch_jump(chunk, jmp_end); | ||
592 | |||
593 | if (peek_token(compiler).type != TOKEN_RPAREN) { | ||
594 | error_push((Error){ | ||
595 | .type = ERR_TYPE_COMPILER, | ||
596 | .value = ERR_TOO_MANY_ARGS, | ||
597 | .line = start.line, | ||
598 | .col = start.column, | ||
599 | }); | ||
600 | return; | ||
601 | } | ||
602 | next_token(compiler); | ||
603 | } | ||
604 | |||
605 | void | ||
606 | parse_list(Chunk *chunk, Compiler *compiler, Token start) { | ||
607 | if (!has_next_token(compiler)) { | ||
608 | error_push((Error){ | ||
609 | .type = ERR_TYPE_COMPILER, | ||
610 | .value = ERR_UNBALANCED_PAREN, | ||
611 | .line = start.line, | ||
612 | .col = start.column, | ||
613 | }); | ||
614 | return; | ||
615 | } | ||
616 | while (has_next_token(compiler)) { | ||
617 | Token tok = next_token(compiler); | ||
618 | if (tok.type == TOKEN_LPAREN) { | ||
619 | parse_list(chunk, compiler, tok); | ||
620 | } | ||
621 | switch (tok.type) { | ||
622 | case TOKEN_ADD: { compile_list_binary_op(chunk, compiler, start, OP_SUM); } break; | ||
623 | case TOKEN_SUB: { compile_list_binary_op(chunk, compiler, start, OP_SUB); } break; | ||
624 | case TOKEN_MUL: { compile_list_binary_op(chunk, compiler, start, OP_MUL); } break; | ||
625 | case TOKEN_DIV: { compile_list_binary_op(chunk, compiler, start, OP_DIV); } break; | ||
626 | case TOKEN_MOD: { compile_list_binary_op(chunk, compiler, start, OP_MOD); } break; | ||
627 | case TOKEN_NOT: { compile_list_unary_op(chunk, compiler, start, OP_NOT); } break; | ||
628 | case TOKEN_AND: { compile_list_binary_op(chunk, compiler, start, OP_AND); } break; | ||
629 | case TOKEN_OR: { compile_list_binary_op(chunk, compiler, start, OP_OR); } break; | ||
630 | case TOKEN_EQUAL: { compile_list_binary_op(chunk, compiler, start, OP_EQUAL); } break; | ||
631 | case TOKEN_LESS: { compile_list_binary_op(chunk, compiler, start, OP_LESS); } break; | ||
632 | case TOKEN_GREATER: { compile_list_binary_op(chunk, compiler, start, OP_GREATER); } break; | ||
633 | case TOKEN_LESS_EQUAL: { compile_list_binary_op(chunk, compiler, start, OP_LESS_EQUAL); } break; | ||
634 | case TOKEN_GREATER_EQUAL: { compile_list_binary_op(chunk, compiler, start, OP_GREATER_EQUAL); } break; | ||
635 | case TOKEN_PRINT: { compile_list_unary_op(chunk, compiler, start, OP_PRINT); } break; | ||
636 | case TOKEN_DISPLAY: { compile_list_unary_op(chunk, compiler, start, OP_DISPLAY); } break; | ||
637 | case TOKEN_NEWLINE: { | ||
638 | compile_list_simple_op(chunk, compiler, start, OP_NEWLINE); | ||
639 | emit_constant(chunk, start, NIL_VAL); | ||
640 | } break; | ||
641 | case TOKEN_DEF: { compile_declare_op(chunk, compiler, start, OP_DEF); } break; | ||
642 | case TOKEN_SET: { compile_declare_op(chunk, compiler, start, OP_SET); } break; | ||
643 | case TOKEN_FUN: { compile_fun_op(chunk, compiler, start); } break; | ||
644 | case TOKEN_LAMBDA: { compile_lambda(chunk, compiler, start, STRING("lambda")); } break; | ||
645 | case TOKEN_IF: { compile_if_op(chunk, compiler, start); } break; | ||
646 | default: { | ||
647 | compile_call_op(chunk, compiler, start, tok); | ||
648 | } break; | ||
649 | } | ||
650 | return; | ||
651 | } | ||
652 | } | ||
653 | |||
654 | void | ||
655 | parse_tree(Chunk *chunk, Compiler *compiler) { | ||
656 | Token tok = next_token(compiler); | ||
657 | if (errors_n != 0) { | ||
658 | return; | ||
659 | } | ||
660 | switch (tok.type) { | ||
661 | case TOKEN_FIXNUM: { | ||
662 | parse_fixnum(chunk, tok); | ||
663 | return ; | ||
664 | } break; | ||
665 | case TOKEN_TRUE: { | ||
666 | emit_constant(chunk, tok, TRUE_VAL); | ||
667 | return; | ||
668 | } break; | ||
669 | case TOKEN_FALSE: { | ||
670 | emit_constant(chunk, tok, FALSE_VAL); | ||
671 | return; | ||
672 | } break; | ||
673 | case TOKEN_RPAREN: { | ||
674 | error_push((Error){ | ||
675 | .type = ERR_TYPE_COMPILER, | ||
676 | .value = ERR_UNBALANCED_PAREN, | ||
677 | .line = tok.line, | ||
678 | .col = tok.column, | ||
679 | }); | ||
680 | return; | ||
681 | } break; | ||
682 | case TOKEN_QUOTE: { | ||
683 | // Object *base = make_pair(obj_quote, obj_nil); | ||
684 | // base->cdr = make_pair(obj_nil, obj_nil); | ||
685 | // push_root(base); | ||
686 | // Object *next_obj = parse_tree(compiler); | ||
687 | // if (next_obj == obj_err) { | ||
688 | // return obj_err; | ||
689 | // } | ||
690 | // base->cdr->car = next_obj; | ||
691 | // return base; | ||
692 | return; | ||
693 | } break; | ||
694 | case TOKEN_LPAREN: { | ||
695 | parse_list(chunk, compiler, tok); | ||
696 | return; | ||
697 | } break; | ||
698 | case TOKEN_STRING: { | ||
699 | Object obj = make_string(tok.value); | ||
700 | emit_constant(chunk, tok, obj); | ||
701 | return; | ||
702 | } break; | ||
703 | case TOKEN_SYMBOL: { | ||
704 | parse_symbol(chunk, compiler, tok); | ||
705 | return; | ||
706 | } break; | ||
707 | case TOKEN_NIL: { | ||
708 | emit_constant(chunk, tok, NIL_VAL); | ||
709 | return; | ||
710 | } break; | ||
711 | default: { | ||
712 | break; | ||
713 | } break; | ||
714 | } | ||
715 | error_push((Error){ | ||
716 | .type = ERR_TYPE_COMPILER, | ||
717 | .value = ERR_EOF_REACHED, | ||
718 | .line = tok.line, | ||
719 | .col = tok.column, | ||
720 | }); | ||
721 | return; | ||
722 | } | ||
723 | |||
724 | Chunk * | ||
725 | compile(Token *tokens) { | ||
726 | Chunk *chunk = NULL; | ||
727 | chunk = NEW_CHUNK("main"); | ||
728 | Compiler compiler = (Compiler){ | ||
729 | .tokens = tokens, | ||
730 | .current = 0, | ||
731 | .scope_depth = 0, | ||
732 | }; | ||
733 | enter_scope(&compiler); | ||
734 | Token main_start = peek_token(&compiler); | ||
735 | while (has_next_token(&compiler)) { | ||
736 | Token start = peek_token(&compiler); | ||
737 | parse_tree(chunk, &compiler); | ||
738 | if (peek_token(&compiler).type == TOKEN_EOF) { | ||
739 | break; | ||
740 | } | ||
741 | add_code(chunk, OP_DROP, start.line, start.column); | ||
742 | } | ||
743 | add_code(chunk, OP_RETURN, main_start.line, main_start.column); | ||
744 | exit_scope(&compiler); | ||
745 | return chunk; | ||
746 | } | ||
747 | |||
748 | #endif // BDL_COMPILER_H | ||