aboutsummaryrefslogtreecommitdiffstats
path: root/src/bytecode
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2022-02-01 18:36:52 +0100
committerBad Diode <bd@badd10de.dev>2022-02-01 18:36:52 +0100
commitee1a5de91c875fb66724dc21c02333bfebe2a812 (patch)
treed3eaa226816d295bb9dc48a2aed27044832ec413 /src/bytecode
parent3156265c7b2da8cc43fee996c0518ea274d39c8a (diff)
downloadbdl-ee1a5de91c875fb66724dc21c02333bfebe2a812.tar.gz
bdl-ee1a5de91c875fb66724dc21c02333bfebe2a812.zip
Add new syntax to lexer and prepare refactor
Diffstat (limited to 'src/bytecode')
-rw-r--r--src/bytecode/chunk.c47
-rwxr-xr-xsrc/bytecode/chunk.h36
-rwxr-xr-xsrc/bytecode/compiler.h748
-rwxr-xr-xsrc/bytecode/darray.h81
-rwxr-xr-xsrc/bytecode/debug.h105
-rwxr-xr-xsrc/bytecode/errors.c52
-rwxr-xr-xsrc/bytecode/errors.h46
-rw-r--r--src/bytecode/hashtable.h208
-rwxr-xr-xsrc/bytecode/lexer.c294
-rwxr-xr-xsrc/bytecode/lexer.h92
-rwxr-xr-xsrc/bytecode/main.c214
-rw-r--r--src/bytecode/objects.c151
-rwxr-xr-xsrc/bytecode/objects.h80
-rwxr-xr-xsrc/bytecode/ops.h46
-rwxr-xr-xsrc/bytecode/read_line.c32
-rwxr-xr-xsrc/bytecode/read_line.h10
-rwxr-xr-xsrc/bytecode/string_view.c40
-rwxr-xr-xsrc/bytecode/string_view.h23
-rwxr-xr-xsrc/bytecode/types.h30
-rwxr-xr-xsrc/bytecode/vm.h396
20 files changed, 0 insertions, 2731 deletions
diff --git a/src/bytecode/chunk.c b/src/bytecode/chunk.c
deleted file mode 100644
index af4a3a2..0000000
--- a/src/bytecode/chunk.c
+++ /dev/null
@@ -1,47 +0,0 @@
1#include "chunk.h"
2#include "objects.h"
3
4Chunk *
5chunk_init(StringView name) {
6 Chunk *chunk = malloc(sizeof(Chunk));
7 array_init(chunk->code, 0);
8 array_init(chunk->constants, 0);
9 array_init(chunk->lines, 0);
10 array_init(chunk->name, name.n);
11 array_insert(chunk->name, name.start, name.n);
12 chunk->n_params = 0;
13 chunk->n_locals = 0;
14 return chunk;
15}
16
17void
18chunk_free(Chunk *chunk) {
19 array_free(chunk->code);
20 for (size_t i = 0; i < array_size(chunk->constants); i++) {
21 Object obj = chunk->constants[i];
22 object_free(&obj);
23 }
24 array_free(chunk->constants);
25 array_free(chunk->lines);
26 array_free(chunk->name);
27 free(chunk);
28}
29
30void
31add_code(Chunk *chunk, u8 byte, size_t line, size_t col) {
32 array_push(chunk->code, byte);
33 LineInfo info = (LineInfo){line, col};
34 array_push(chunk->lines, info);
35}
36
37size_t
38add_constant(Chunk *chunk, Object obj) {
39 size_t pos = array_size(chunk->constants);
40 for (size_t i = 0; i < pos; i++) {
41 if (object_equal(obj, chunk->constants[i])) {
42 return i;
43 }
44 }
45 array_push(chunk->constants, obj);
46 return pos;
47}
diff --git a/src/bytecode/chunk.h b/src/bytecode/chunk.h
deleted file mode 100755
index 9457fa9..0000000
--- a/src/bytecode/chunk.h
+++ /dev/null
@@ -1,36 +0,0 @@
1#ifndef BDL_CHUNK_H
2#define BDL_CHUNK_H
3
4#include "darray.h"
5#include "string_view.h"
6
7typedef struct Object Object;
8
9typedef struct LineInfo {
10 size_t line;
11 size_t col;
12} LineInfo;
13
14typedef struct Chunk {
15 // Program code.
16 u8 *code;
17 // Compile time constants.
18 Object *constants;
19 // Contains debugging information for every code operation.
20 LineInfo *lines;
21 // Chunk name.
22 char *name;
23
24 // Number of locals and parameters.
25 size_t n_params;
26 size_t n_locals;
27} Chunk;
28
29#define NEW_CHUNK(NAME) chunk_init((StringView){(NAME), sizeof(NAME) - 1})
30
31Chunk * chunk_init(StringView name);
32void add_code(Chunk *chunk, u8 byte, size_t line, size_t col);
33size_t add_constant(Chunk *chunk, Object obj);
34void chunk_free(Chunk *chunk);
35
36#endif // BDL_CHUNK_H
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
9typedef struct Scope {
10 StringView *params;
11 StringView *locals;
12 Token *captured;
13} Scope;
14
15typedef 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.
24Token next_token(Compiler *compiler);
25Token peek_token(const Compiler *compiler);
26bool has_next_token(const Compiler *compiler);
27
28// Scope initialization/exit.
29void enter_scope(Compiler *compiler);
30void exit_scope(Compiler *compiler);
31
32void
33enter_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
40void
41exit_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
48Scope *
49get_current_scope(Compiler *compiler) {
50 return &compiler->scopes[compiler->scope_depth - 1];
51}
52
53Chunk * compile(Token *tokens);
54
55Token
56peek_token(const Compiler *compiler) {
57 return compiler->tokens[compiler->current];
58}
59
60Token
61next_token(Compiler *compiler) {
62 return compiler->tokens[compiler->current++];
63}
64
65bool
66has_next_token(const Compiler *compiler) {
67 return compiler->current < array_size(compiler->tokens);
68}
69
70ssize_t
71find_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
80ssize_t
81find_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
90void
91emit_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
104size_t
105emit_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
112void
113patch_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
120void
121parse_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
135void
136parse_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
181void parse_tree(Chunk *chunk, Compiler *compiler);
182
183void
184compile_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
217void
218compile_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
263void
264compile_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
299void
300compile_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
398void
399compile_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
501void
502compile_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
521void
522compile_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
551void
552compile_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
605void
606parse_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
654void
655parse_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
724Chunk *
725compile(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
diff --git a/src/bytecode/darray.h b/src/bytecode/darray.h
deleted file mode 100755
index fa4e293..0000000
--- a/src/bytecode/darray.h
+++ /dev/null
@@ -1,81 +0,0 @@
1#ifndef BDL_DARRAY_H
2#define BDL_DARRAY_H
3
4#include <string.h>
5
6typedef struct ArrayHeader {
7 size_t size;
8 size_t cap;
9} ArrayHeader;
10
11// Header/Size/capacity accessors.
12#define array_head(ARR) ((ArrayHeader *)((char *)(ARR) - sizeof(ArrayHeader)))
13#define array_size(ARR) ((ARR) ? array_head(ARR)->size : 0)
14#define array_cap(ARR) ((ARR) ? array_head(ARR)->cap : 0)
15
16// Initialize a dynamic array ARR with N elements. The initialization doesn't
17// zero out the data, so thread carefully..
18#define array_init(ARR,N) ((ARR) = _array_reserve(N, sizeof(*(ARR))))
19
20// Push a given element T to the dynamic array ARR.
21#define array_push(ARR, T) \
22 ((ARR) = _array_maybe_grow(ARR, sizeof(T)), \
23 (ARR)[array_head(ARR)->size++] = (T))
24
25// Return the last element of the array. Can be used to build stacks.
26#define array_pop(ARR) (ARR)[--array_head(ARR)->size]
27
28// Return the value stored at the OFFSET position from the tail of the array.
29#define array_peek(ARR, OFFSET) (ARR)[array_head(ARR)->size - 1 - (OFFSET)]
30
31// Insert N bytes from the SRC array into the ARR dynamic array.
32#define array_insert(ARR, SRC, N) \
33 ((ARR) = _array_insert(ARR, SRC, N, sizeof(*(ARR))))
34
35// Free the memory from the original allocated position.
36#define array_free(ARR) ((ARR) ? free(array_head(ARR)), (ARR) = NULL : 0)
37
38static inline void *
39_array_reserve(size_t num_elem, size_t type_size) {
40 char *p = malloc(num_elem * type_size + sizeof(ArrayHeader));
41 p += sizeof(ArrayHeader);
42 array_head(p)->size = 0;
43 array_head(p)->cap = num_elem;
44 return p;
45}
46
47static inline void *
48_array_maybe_grow(void *arr, size_t type_size) {
49 ArrayHeader *head = array_head(arr);
50 if (head->cap == head->size) {
51 if (head->cap == 0) {
52 head->cap++;
53 } else {
54 head->cap *= 2;
55 }
56 head = realloc(head, head->cap * type_size + sizeof(ArrayHeader));
57 }
58 arr = (char *)head + sizeof(ArrayHeader);
59 return arr;
60}
61
62static inline
63char * _array_insert(char *arr, const char *src, size_t n_bytes, size_t type_size) {
64 ArrayHeader *head = array_head(arr);
65 size_t new_size = n_bytes + head->size;
66 if (new_size > head->cap * type_size) {
67 if (head->cap == 0) {
68 head->cap = 1;
69 }
70 while (new_size >= head->cap * type_size) {
71 head->cap *= 2;
72 }
73 head = realloc(head, head->cap * type_size + sizeof(ArrayHeader));
74 }
75 arr = (char *)head + sizeof(ArrayHeader);
76 memcpy((arr + head->size), src, n_bytes);
77 head->size = new_size;
78 return arr;
79}
80
81#endif // BDL_DARRAY_H
diff --git a/src/bytecode/debug.h b/src/bytecode/debug.h
deleted file mode 100755
index b21d8e6..0000000
--- a/src/bytecode/debug.h
+++ /dev/null
@@ -1,105 +0,0 @@
1#ifndef BDL_DEBUG_H
2#define BDL_DEBUG_H
3
4#include "chunk.h"
5#include "objects.h"
6
7void disassemble_chunk(Chunk *chunk);
8size_t disassemble_instruction(Chunk *chunk, size_t offset);
9
10static const char* ops_str[] = {
11 // Load/store ops.
12 [OP_CONSTANT] = "OP_CONSTANT",
13 [OP_LOCAL] = "OP_LOCAL",
14 [OP_CAPTURED] = "OP_CAPTURED",
15 [OP_CAPTURE_LOCAL] = "OP_CAPTURE_LOCAL",
16 [OP_SET_CAPTURED] = "OP_SET_CAPTURED",
17 [OP_DEF_LOCAL] = "OP_DEF_LOCAL",
18 [OP_SET_LOCAL] = "OP_SET_LOCAL",
19 [OP_DEF] = "OP_DEF",
20 [OP_SET] = "OP_SET",
21 [OP_GET] = "OP_GET",
22 // Arithmetic ops.
23 [OP_SUM] = "OP_SUM",
24 [OP_SUB] = "OP_SUB",
25 [OP_MUL] = "OP_MUL",
26 [OP_DIV] = "OP_DIV",
27 [OP_MOD] = "OP_MOD",
28 // Logic ops.
29 [OP_NOT] = "OP_NOT",
30 [OP_AND] = "OP_AND",
31 [OP_OR] = "OP_OR",
32 // Numerical comparison ops.
33 [OP_EQUAL] = "OP_EQUAL",
34 [OP_LESS] = "OP_LESS",
35 [OP_GREATER] = "OP_GREATER",
36 [OP_LESS_EQUAL] = "OP_LESS_EQUAL",
37 [OP_GREATER_EQUAL] = "OP_GREATER_EQUAL",
38 // Jump/conditional ops.
39 [OP_JUMP] = "OP_JUMP",
40 [OP_JUMP_IF_FALSE] = "OP_JUMP_IF_FALSE",
41 // Display ops.
42 [OP_DISPLAY] = "OP_DISPLAY",
43 [OP_PRINT] = "OP_PRINT",
44 [OP_NEWLINE] = "OP_NEWLINE",
45 // Procedures.
46 [OP_CALL] = "OP_CALL",
47 [OP_RETURN] = "OP_RETURN",
48 // Clear stack after each statement.
49 [OP_DROP] = "OP_DROP",
50};
51
52void
53disassemble_chunk(Chunk *chunk) {
54 printf("===== %.*s =====\n", (int)array_size(chunk->name), chunk->name);
55 printf("code:\n");
56 size_t offset = 0;
57 while (offset < array_size(chunk->code)) {
58 offset = disassemble_instruction(chunk, offset);
59 }
60 printf("\nconstants:\n");
61 offset = 0;
62 while (offset < array_size(chunk->constants)) {
63 printf("\t%03ld -> ", offset);
64 object_display(chunk->constants[offset]);
65 printf("\n");
66 offset++;
67 }
68 printf("\n");
69}
70
71size_t
72disassemble_instruction(Chunk *chunk, size_t offset) {
73 printf("\t%04ld ", offset);
74 if (offset > 0
75 && chunk->lines[offset].line == chunk->lines[offset - 1].line
76 && chunk->lines[offset].col == chunk->lines[offset - 1].col) {
77 printf("%4s|%-4s ", " ", " ");
78 } else {
79 printf("%4ld:%-4ld ", chunk->lines[offset].line, chunk->lines[offset].col);
80 }
81 u8 instruction = chunk->code[offset];
82 switch (instruction) {
83 case OP_CONSTANT: {
84 u8 constant = chunk->code[offset + 1];
85 printf("%-16s %4d -> ", ops_str[instruction], constant);
86 object_display(chunk->constants[constant]);
87 printf("\n");
88 return offset + 2;
89 } break;
90 case OP_JUMP:
91 case OP_JUMP_IF_FALSE: {
92 u16 a = chunk->code[offset + 1];
93 u16 b = chunk->code[offset + 2];
94 s16 jmp = (a << 8) | b;
95 printf("%-16s %4d\n", ops_str[instruction], jmp);
96 return offset + 3;
97 } break;
98 default: {
99 printf("%s\n", ops_str[instruction]);
100 return offset + 1;
101 } break;
102 }
103}
104
105#endif // BDL_DEBUG_H
diff --git a/src/bytecode/errors.c b/src/bytecode/errors.c
deleted file mode 100755
index b2aab93..0000000
--- a/src/bytecode/errors.c
+++ /dev/null
@@ -1,52 +0,0 @@
1#include "errors.h"
2
3static const char* error_msgs[] = {
4 [ERR_UNKNOWN] = "error: something unexpected happened",
5 [ERR_UNMATCHED_STRING] = "error: unmatched string delimiter",
6 [ERR_UNBALANCED_PAREN] = "error: unbalanced parentheses",
7 [ERR_NOT_IMPLEMENTED] = "error: not implemented",
8 [ERR_EOF_REACHED] = "error: EOF reached",
9 [ERR_UNKNOWN_TOKEN] = "error: unknown token",
10 [ERR_UNKNOWN_OBJ_TYPE] = "error: can't eval unknown object type",
11 [ERR_NOT_A_SYMBOL] = "error: object is not a symbol",
12 [ERR_SYMBOL_NOT_FOUND] = "error: symbol not found",
13 [ERR_OBJ_NOT_CALLABLE] = "error: object is not callable",
14 [ERR_NOT_ENOUGH_ARGS] = "error: not enough arguments",
15 [ERR_TOO_MANY_ARGS] = "error: too many arguments",
16 [ERR_WRONG_ARG_TYPE] = "error: wrong argument type",
17 [ERR_DIVISION_BY_ZERO] = "error: division by zero",
18 [ERR_PC_OOB] = "error: pc out of bounds",
19 [ERR_EMPTY_CHUNK] = "error: empty chunk",
20 [ERR_AMBIGUOUS_PARAMS] = "error: ambiguous parameter names",
21};
22
23static Error errors[ERR_MAX_NUMBER];
24static size_t errors_n = 0;
25static bool supress_errors = false;
26
27void
28error_push(Error error) {
29 if (errors_n < ERR_MAX_NUMBER) {
30 errors[errors_n++] = error;
31 }
32}
33
34void
35report_errors(char *file_name) {
36 for (size_t i = 0; i < errors_n; i++) {
37 Error err = errors[i];
38 fprintf(stderr, "%s", file_name);
39 if (err.line != 0) {
40 fprintf(stderr, ":%ld:%ld", err.line, err.col);
41 }
42 switch (err.type) {
43 case ERR_TYPE_LEXER: { fprintf(stderr, ": [lexer]"); } break;
44 case ERR_TYPE_COMPILER: { fprintf(stderr, ": [compiler]"); } break;
45 case ERR_TYPE_RUNTIME: { fprintf(stderr, ": [runtime]"); } break;
46 case ERR_TYPE_PARSER: { fprintf(stderr, ": [parser]"); } break;
47 default: break;
48 }
49 fprintf(stderr, " %s\n", error_msgs[err.value]);
50 }
51 errors_n = 0;
52}
diff --git a/src/bytecode/errors.h b/src/bytecode/errors.h
deleted file mode 100755
index 8f4386e..0000000
--- a/src/bytecode/errors.h
+++ /dev/null
@@ -1,46 +0,0 @@
1#ifndef BDL_ERRORS_H
2#define BDL_ERRORS_H
3
4typedef enum ErrorType {
5 ERR_TYPE_OK,
6 ERR_TYPE_LEXER,
7 ERR_TYPE_PARSER,
8 ERR_TYPE_COMPILER,
9 ERR_TYPE_RUNTIME,
10} ErrorType;
11
12typedef enum ErrorValue {
13 ERR_UNKNOWN = 0,
14 ERR_UNMATCHED_STRING,
15 ERR_UNBALANCED_PAREN,
16 ERR_NOT_IMPLEMENTED,
17 ERR_EOF_REACHED,
18 ERR_UNKNOWN_TOKEN,
19 ERR_UNKNOWN_OBJ_TYPE,
20 ERR_NOT_A_SYMBOL,
21 ERR_SYMBOL_NOT_FOUND,
22 ERR_OBJ_NOT_CALLABLE,
23 ERR_NOT_ENOUGH_ARGS,
24 ERR_TOO_MANY_ARGS,
25 ERR_WRONG_ARG_TYPE,
26 ERR_DIVISION_BY_ZERO,
27 ERR_AMBIGUOUS_PARAMS,
28
29 // Bytecode interpreter.
30 ERR_PC_OOB,
31 ERR_EMPTY_CHUNK,
32} ErrorValue;
33
34typedef struct Error {
35 ErrorType type;
36 ErrorValue value;
37 size_t line;
38 size_t col;
39} Error;
40
41void error_push(Error error);
42void report_errors(char *file_name);
43
44#define ERR_MAX_NUMBER 16
45
46#endif // BDL_ERRORS_H
diff --git a/src/bytecode/hashtable.h b/src/bytecode/hashtable.h
deleted file mode 100644
index 7c0c380..0000000
--- a/src/bytecode/hashtable.h
+++ /dev/null
@@ -1,208 +0,0 @@
1#ifndef BDL_HASHTABLE_H
2#define BDL_HASHTABLE_H
3
4#include "darray.h"
5#include "objects.h"
6
7// Minimum table capacity.
8#define HT_MIN_CAP 4
9#define HT_MIN_SHIFT 2
10
11// Adjust the load factor threshold at which the table will grow on insertion.
12#define HT_LOAD_THRESHOLD 0.8
13
14typedef struct HashTablePair {
15 Object *key;
16 Object *value;
17} HashTablePair;
18
19struct HashTable;
20typedef uint64_t (HashFunction)(const struct HashTable *table, void *bytes);
21
22typedef struct HashTable {
23 // All available key-value pairs as a dynamic array.
24 HashTablePair *pairs;
25
26 // Internal storage.
27 Object *keys;
28 Object *values;
29
30 // Hash function.
31 HashFunction *hash_func;
32
33 // This table expects the number of buckets to grow in powers of two. To
34 // speedup the default hashing, we memoize the number of bits equivalent to
35 // that power of 2:
36 //
37 // cap := 1024 = 2 ^ 10, shift_amount := 10
38 //
39 uint8_t shift_amount;
40} HashTable;
41
42// Hash a byte stream using a circular shift + XOR hash function.
43static inline uint64_t
44_xor_shift_hash(const char *key, size_t n) {
45 uint64_t hash = 0x65d9d65f6a19574f;
46 char *last = (char *)key + n;
47 while (key != last) {
48 hash ^= (uint64_t)*key++;
49 hash = (hash << 8) | (hash >> (64 - 8));
50 }
51 return hash;
52}
53
54// Use Fibonacci hashing to map a hash to a value in range of the table.
55static inline uint64_t
56_fibonacci_hash(uint64_t hash, size_t shift_amount) {
57 return (hash * UINT64_C(11400714819323198485)) >> (64 - shift_amount);
58}
59
60uint64_t
61_symbol_hash(const HashTable *table, void *key) {
62 Object *obj = key;
63 uint64_t hash = _xor_shift_hash(obj->text, array_size(obj->text));
64 hash = _fibonacci_hash(hash, table->shift_amount);
65 return hash;
66}
67
68static inline float
69ht_load_factor(const HashTable *table) {
70 return (float)array_size(table->pairs) / (float)array_cap(table->pairs);
71}
72
73HashTable *
74ht_init(void) {
75 HashTable *table = malloc(sizeof(HashTable));
76 *table = (HashTable){0};
77 array_init(table->pairs, HT_MIN_CAP);
78 array_init(table->keys, HT_MIN_CAP);
79 array_init(table->values, HT_MIN_CAP);
80 for (size_t i = 0; i < array_cap(table->pairs); i++) {
81 table->pairs[i] = (HashTablePair){NULL, NULL};
82 }
83 table->shift_amount = HT_MIN_SHIFT;
84 table->hash_func = _symbol_hash;
85 return table;
86}
87
88void
89_ht_insert(HashTable *table, Object key, Object value) {
90 size_t position = table->hash_func(table, &key);
91 size_t probe_position = position;
92
93 // Verify the key in that position is free. If not, use linear probing to
94 // find the next free slot.
95 HashTablePair *pairs = table->pairs;
96 bool update = false;
97 while (true) {
98 if (pairs[probe_position].key == NULL) {
99 array_head(pairs)->size++;
100 break;
101 }
102 if (object_equal(*pairs[probe_position].key, key)) {
103 update = true;
104 break;
105 }
106 if (probe_position == array_cap(pairs) - 1) {
107 probe_position = 0;
108 } else {
109 probe_position++;
110 }
111 }
112
113 if (!update) {
114 array_push(table->keys, object_copy(key));
115 array_push(table->values, value);
116 pairs[probe_position].key = &table->keys[array_size(table->keys) - 1];
117 pairs[probe_position].value = &table->values[array_size(table->values) - 1];
118 } else {
119 *pairs[probe_position].value = value;
120 }
121}
122
123void
124_ht_maybe_grow(HashTable *table) {
125 if (ht_load_factor(table) < HT_LOAD_THRESHOLD) {
126 return;
127 }
128
129 // Create a new array with 2x capacity.
130 HashTablePair *old_pairs = table->pairs;
131 Object *old_keys = table->keys;
132 Object *old_values = table->values;
133 table->pairs = NULL;
134 table->keys = NULL;
135 table->values = NULL;
136 array_init(table->pairs, array_cap(old_pairs) * 2);
137 array_init(table->keys, array_cap(old_pairs) * 2);
138 array_init(table->values, array_cap(old_pairs) * 2);
139 for (size_t i = 0; i < array_cap(table->pairs); i++) {
140 table->pairs[i] = (HashTablePair){NULL, NULL};
141 }
142 table->shift_amount++;
143
144 // Hash everything in the table for the new array capacity.
145 for (size_t i = 0; i < array_cap(old_pairs); i++) {
146 if (old_pairs[i].key != NULL) {
147 _ht_insert(table, *old_pairs[i].key, *old_pairs[i].value);
148 }
149 }
150
151 // Free old arrays.
152 array_free(old_pairs);
153 for (size_t i = 0; i < array_size(old_keys); i++) {
154 object_free(&old_keys[i]);
155 }
156 array_free(old_keys);
157 array_free(old_values);
158}
159
160void
161ht_insert(HashTable *table, Object key, Object value) {
162 _ht_maybe_grow(table);
163 _ht_insert(table, key, value);
164 return;
165}
166
167Object *
168ht_lookup(const HashTable *table, Object key) {
169 size_t position = table->hash_func(table, &key);
170 size_t probe_position = position;
171
172 // Verify the key in that position is the same. If not perform linear
173 // probing to find it.
174 HashTablePair *pairs = table->pairs;
175 while (true) {
176 if (pairs[probe_position].key == NULL) {
177 return NULL;
178 }
179 if (object_equal(*pairs[probe_position].key, key)) {
180 break;
181 }
182 if (probe_position == array_cap(pairs) - 1) {
183 probe_position = 0;
184 } else {
185 probe_position++;
186 }
187 if (probe_position == position) {
188 return NULL;
189 }
190 }
191 return pairs[probe_position].value;
192}
193
194void
195ht_free(HashTable *table) {
196 if (table == NULL) {
197 return;
198 }
199 array_free(table->pairs);
200 for (size_t i = 0; i < array_size(table->keys); i++) {
201 object_free(&table->keys[i]);
202 }
203 array_free(table->keys);
204 array_free(table->values);
205 free(table);
206}
207
208#endif // BDL_HASHTABLE_H
diff --git a/src/bytecode/lexer.c b/src/bytecode/lexer.c
deleted file mode 100755
index a80c845..0000000
--- a/src/bytecode/lexer.c
+++ /dev/null
@@ -1,294 +0,0 @@
1#include "lexer.h"
2
3static const char* token_str[] = {
4 [TOKEN_UNKNOWN] = "TOKEN_UNKNOWN",
5 [TOKEN_LPAREN] = "TOKEN_LPAREN",
6 [TOKEN_RPAREN] = "TOKEN_RPAREN",
7 [TOKEN_FIXNUM] = "TOKEN_FIXNUM",
8 [TOKEN_SYMBOL] = "TOKEN_SYMBOL",
9 [TOKEN_STRING] = "TOKEN_STRING",
10 [TOKEN_NIL] = "TOKEN_NIL",
11 [TOKEN_QUOTE] = "TOKEN_QUOTE",
12 [TOKEN_TRUE] = "TOKEN_TRUE",
13 [TOKEN_FALSE] = "TOKEN_FALSE",
14 [TOKEN_IF] = "TOKEN_IF",
15 [TOKEN_ELSE] = "TOKEN_ELSE",
16 [TOKEN_DEF] = "TOKEN_DEF",
17 [TOKEN_SET] = "TOKEN_SET",
18 [TOKEN_FUN] = "TOKEN_FUN",
19 [TOKEN_LAMBDA] = "TOKEN_LAMBDA",
20 [TOKEN_DISPLAY] = "TOKEN_DISPLAY",
21 [TOKEN_PRINT] = "TOKEN_PRINT",
22 [TOKEN_NEWLINE] = "TOKEN_NEWLINE",
23 [TOKEN_ADD] = "TOKEN_ADD",
24 [TOKEN_SUB] = "TOKEN_SUB",
25 [TOKEN_MUL] = "TOKEN_MUL",
26 [TOKEN_DIV] = "TOKEN_DIV",
27 [TOKEN_MOD] = "TOKEN_MOD",
28 [TOKEN_NOT] = "TOKEN_NOT",
29 [TOKEN_AND] = "TOKEN_AND",
30 [TOKEN_OR] = "TOKEN_OR",
31 [TOKEN_EQUAL] = "TOKEN_EQUAL",
32 [TOKEN_LESS] = "TOKEN_LESS",
33 [TOKEN_GREATER] = "TOKEN_GREATER",
34 [TOKEN_LESS_EQUAL] = "TOKEN_LESS_EQUAL",
35 [TOKEN_GREATER_EQUAL] = "TOKEN_GREATER_EQUAL",
36 [TOKEN_EOF] = "TOKEN_EOF",
37};
38
39void
40print_token(Token tok) {
41 printf("LINE: %3ld COL: %3ld ", tok.line, tok.column);
42 printf("%s", token_str[tok.type]);
43 switch (tok.type) {
44 case TOKEN_FIXNUM: {
45 printf(" -> ");
46 sv_write(&tok.value);
47 } break;
48 case TOKEN_SYMBOL: {
49 printf(" -> ");
50 sv_write(&tok.value);
51 } break;
52 case TOKEN_STRING: {
53 printf(" -> ");
54 sv_write(&tok.value);
55 } break;
56 default: {
57 } break;
58 }
59 printf("\n");
60}
61
62char
63scan_next(Scanner *scanner) {
64 char c = sv_next(&scanner->current);
65 if (c == '\n') {
66 scanner->line_number++;
67 scanner->col_number = 1;
68 } else {
69 scanner->col_number++;
70 }
71 scanner->offset++;
72 return c;
73}
74
75char
76scan_peek(const Scanner *scanner) {
77 return sv_peek(&scanner->current);
78}
79
80bool
81scan_has_next(const Scanner *scanner) {
82 return scanner->current.n != 0;
83}
84
85void
86skip_whitespace(Scanner *scanner) {
87 while (scan_has_next(scanner)) {
88 char c = scan_peek(scanner);
89 switch (c) {
90 case ' ':
91 case '\f':
92 case '\n':
93 case '\r':
94 case '\t':
95 case '\v': {
96 scan_next(scanner);
97 } break;
98 default: {
99 return;
100 } break;
101 }
102 }
103}
104
105bool
106is_delimiter(char c) {
107 switch (c) {
108 case EOF:
109 case '\0':
110 case ';':
111 case '"':
112 case '\'':
113 case '(':
114 case ')':
115 case ' ':
116 case '\f':
117 case '\n':
118 case '\r':
119 case '\t':
120 case '\v': {
121 return true;
122 } break;
123 }
124 return false;
125}
126
127#define TOKEN_IS_KEYWORD(VAL, KEYWORD) \
128 sv_equal(&(VAL), &(StringView){(KEYWORD), sizeof(KEYWORD) - 1})
129
130TokenType
131find_primitive_type(const StringView value) {
132 bool is_fixnum = true;
133 for (size_t i = 0; i < value.n; i++) {
134 char c = value.start[i];
135 if (i == 0 && c == '-' && value.n > 1) {
136 continue;
137 }
138 if (!(c >= '0' && c <= '9')) {
139 is_fixnum = false;
140 break;
141 }
142 }
143 if (is_fixnum) {
144 return TOKEN_FIXNUM;
145 }
146 if (TOKEN_IS_KEYWORD(value, "true")) { return TOKEN_TRUE; }
147 if (TOKEN_IS_KEYWORD(value, "false")) { return TOKEN_FALSE; }
148 if (TOKEN_IS_KEYWORD(value, "if")) { return TOKEN_IF; }
149 if (TOKEN_IS_KEYWORD(value, "else")) { return TOKEN_ELSE; }
150 if (TOKEN_IS_KEYWORD(value, "def")) { return TOKEN_DEF; }
151 if (TOKEN_IS_KEYWORD(value, "set!")) { return TOKEN_SET; }
152 if (TOKEN_IS_KEYWORD(value, "fun")) { return TOKEN_FUN; }
153 if (TOKEN_IS_KEYWORD(value, "lambda")) { return TOKEN_LAMBDA; }
154 if (TOKEN_IS_KEYWORD(value, "display")) { return TOKEN_DISPLAY; }
155 if (TOKEN_IS_KEYWORD(value, "print")) { return TOKEN_PRINT; }
156 if (TOKEN_IS_KEYWORD(value, "newline")) { return TOKEN_NEWLINE; }
157 if (TOKEN_IS_KEYWORD(value, "+")) { return TOKEN_ADD; }
158 if (TOKEN_IS_KEYWORD(value, "-")) { return TOKEN_SUB; }
159 if (TOKEN_IS_KEYWORD(value, "*")) { return TOKEN_MUL; }
160 if (TOKEN_IS_KEYWORD(value, "/")) { return TOKEN_DIV; }
161 if (TOKEN_IS_KEYWORD(value, "%")) { return TOKEN_MOD; }
162 if (TOKEN_IS_KEYWORD(value, "not")) { return TOKEN_NOT; }
163 if (TOKEN_IS_KEYWORD(value, "and")) { return TOKEN_AND; }
164 if (TOKEN_IS_KEYWORD(value, "or")) { return TOKEN_OR; }
165 if (TOKEN_IS_KEYWORD(value, "=")) { return TOKEN_EQUAL; }
166 if (TOKEN_IS_KEYWORD(value, "<")) { return TOKEN_LESS; }
167 if (TOKEN_IS_KEYWORD(value, ">")) { return TOKEN_GREATER; }
168 if (TOKEN_IS_KEYWORD(value, "<=")) { return TOKEN_LESS_EQUAL; }
169 if (TOKEN_IS_KEYWORD(value, ">=")) { return TOKEN_GREATER_EQUAL; }
170
171 return TOKEN_SYMBOL;
172}
173
174Token *
175tokenize(const StringView *sv) {
176 Token *tokens = NULL;
177 array_init(tokens, 1);
178 Scanner scanner = (Scanner){
179 .current = *sv,
180 .line_number = 1,
181 .col_number = 1,
182 };
183
184 while (scan_has_next(&scanner)) {
185 skip_whitespace(&scanner);
186 size_t line = scanner.line_number;
187 size_t col = scanner.col_number;
188 size_t offset = scanner.offset;
189 char c = scan_next(&scanner);
190 switch (c) {
191 case ';': {
192 while ((c = scan_next(&scanner)) != '\n' && c != '\0') {}
193 } break;
194 case '"': {
195 char prev = c;
196 bool found = false;
197 size_t n = 0;
198 while (scan_has_next(&scanner)) {
199 c = scan_next(&scanner);
200 if (c == '"' && prev != '\\') {
201 found = true;
202 break;
203 }
204 prev = c;
205 n++;
206 }
207 if (!found) {
208 error_push((Error){
209 .type = ERR_TYPE_LEXER,
210 .value = ERR_UNMATCHED_STRING,
211 .line = line,
212 .col = col,
213 });
214 return tokens;
215 }
216 Token token = (Token){
217 .value = (StringView){
218 .start = &sv->start[offset + 1],
219 .n = n,
220 },
221 .type = TOKEN_STRING,
222 .line = line,
223 .column = col,
224 };
225 array_push(tokens, token);
226 } break;
227 case '\'': {
228 Token token = (Token){
229 .type = TOKEN_QUOTE,
230 .line = line,
231 .column = col,
232 };
233 array_push(tokens, token);
234 } break;
235 case '(': {
236 if (scan_peek(&scanner) == ')') {
237 scan_next(&scanner);
238 Token token = (Token){
239 .type = TOKEN_NIL,
240 .line = line,
241 .column = col,
242 };
243 array_push(tokens, token);
244 } else {
245 Token token = (Token){
246 .type = TOKEN_LPAREN,
247 .line = line,
248 .column = col,
249 };
250 array_push(tokens, token);
251 }
252 } break;
253 case ')': {
254 Token token = (Token){
255 .type = TOKEN_RPAREN,
256 .line = line,
257 .column = col,
258 };
259 array_push(tokens, token);
260 } break;
261 default: {
262 size_t n = 1;
263 while (!is_delimiter(scan_peek(&scanner))) {
264 scan_next(&scanner);
265 n++;
266 }
267 if (c == EOF || c == '\0') {
268 break;
269 }
270 Token token = (Token){
271 .value = (StringView){
272 .start = &sv->start[offset],
273 .n = n,
274 },
275 .type = TOKEN_SYMBOL,
276 .line = line,
277 .column = col,
278 };
279 token.type = find_primitive_type(token.value);
280 array_push(tokens, token);
281 } break;
282 }
283 }
284
285 // Push EOF token.
286 Token token = (Token){
287 .type = TOKEN_EOF,
288 .line = scanner.line_number,
289 .column = 1,
290 };
291 array_push(tokens, token);
292
293 return tokens;
294}
diff --git a/src/bytecode/lexer.h b/src/bytecode/lexer.h
deleted file mode 100755
index 3cadf30..0000000
--- a/src/bytecode/lexer.h
+++ /dev/null
@@ -1,92 +0,0 @@
1#ifndef BDL_LEXER_H
2#define BDL_LEXER_H
3
4#include "string_view.h"
5
6typedef enum TokenType {
7 TOKEN_UNKNOWN = 0,
8
9 // Parentheses.
10 TOKEN_LPAREN,
11 TOKEN_RPAREN,
12
13 // Primitive types.
14 TOKEN_FIXNUM,
15 TOKEN_SYMBOL,
16 TOKEN_STRING,
17
18 // Keywords.
19 TOKEN_NIL,
20 TOKEN_QUOTE,
21 TOKEN_TRUE,
22 TOKEN_FALSE,
23 TOKEN_IF,
24 TOKEN_ELSE,
25 TOKEN_DEF,
26 TOKEN_SET,
27 TOKEN_FUN,
28 TOKEN_LAMBDA,
29 TOKEN_DISPLAY,
30 TOKEN_PRINT,
31 TOKEN_NEWLINE,
32
33 // Arithmetic.
34 TOKEN_ADD,
35 TOKEN_SUB,
36 TOKEN_MUL,
37 TOKEN_DIV,
38 TOKEN_MOD,
39
40 // Boolean comparisons.
41 TOKEN_NOT,
42 TOKEN_AND,
43 TOKEN_OR,
44 TOKEN_EQUAL,
45 TOKEN_LESS,
46 TOKEN_GREATER,
47 TOKEN_LESS_EQUAL,
48 TOKEN_GREATER_EQUAL,
49
50 TOKEN_EOF,
51} TokenType;
52
53typedef struct Token {
54 TokenType type;
55 StringView value;
56 size_t line;
57 size_t column;
58} Token;
59
60typedef struct Scanner {
61 StringView current;
62 size_t line_number;
63 size_t col_number;
64 size_t offset;
65} Scanner;
66
67// Print a token to standard output for debugging purposes.
68void print_token(Token tok);
69
70// Same functionality as the ScanView pairs, but keeping track of line and
71// column numbers.
72char scan_next(Scanner *scanner);
73char scan_peek(const Scanner *scanner);
74
75// Check if the current scanner still have characters left.
76bool scan_has_next(const Scanner *scanner);
77
78// Advance the scanner until we ran out of whitespace.
79void skip_whitespace(Scanner *scanner);
80
81// Check if a given character is a delimiter.
82bool is_delimiter(char c);
83
84// Extract the token type from the current string.
85TokenType find_primitive_type(const StringView value);
86
87// Generate a list of tokens from the given string.
88Token * tokenize(const StringView *sv);
89
90#define TOK_BUF_CAP 256
91
92#endif // BDL_LEXER_H
diff --git a/src/bytecode/main.c b/src/bytecode/main.c
deleted file mode 100755
index 4b80f71..0000000
--- a/src/bytecode/main.c
+++ /dev/null
@@ -1,214 +0,0 @@
1#include <assert.h>
2#include <getopt.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6
7//
8// Config.
9//
10
11#ifdef DEBUG
12#define DEBUG_TRACE_EXECUTION
13#endif
14
15#include "vm.h"
16#include "errors.c"
17#include "chunk.c"
18#include "objects.c"
19#include "compiler.h"
20#include "ops.h"
21#include "debug.h"
22#include "lexer.c"
23#include "read_line.c"
24#include "string_view.c"
25
26static VM vm;
27
28void
29init(void) {
30 vm_init(&vm);
31}
32
33void
34halt(void) {
35 vm_free(&vm);
36}
37
38void
39process_source(const StringView *source) {
40 // Read tokens.
41 Token *tokens = tokenize(source);
42 if (errors_n != 0) {
43 array_free(tokens);
44 return;
45 }
46
47 // Compile chunk.
48 Chunk *main = compile(tokens);
49 if (errors_n != 0) {
50 chunk_free(main);
51 array_free(tokens);
52 return;
53 }
54
55#ifdef DEBUG
56 disassemble_chunk(main);
57#endif
58
59 // Interpret chunk.
60 Object main_proc = make_lambda(main);
61 CallFrame frame = (CallFrame){
62 .closure = main_proc.closure,
63 };
64 array_push(vm.frames, frame);
65 vm_interpret(&vm);
66
67 // Free resources.
68 chunk_free(main);
69 array_free(tokens);
70}
71
72#define REPL_PROMPT "bdl> "
73
74void
75run_repl(void) {
76 printf("BDL REPL (Press Ctrl-D or Ctrl-C to exit)\n");
77 while (true) {
78 printf(REPL_PROMPT);
79 StringView sv = read_line();
80 if (sv.start == NULL) {
81 return;
82 }
83 process_source(&sv);
84
85 // Check if there were any errors.
86 if (errors_n != 0 && !supress_errors) {
87 for (size_t i = 0; i < errors_n; i++) {
88 Error err = errors[i];
89 for (size_t j = 0; j < err.col + sizeof(REPL_PROMPT) - 2; j++) {
90 putchar(' ');
91 }
92 printf("|\n");
93 for (size_t j = 0; j < err.col + sizeof(REPL_PROMPT) - 2; j++) {
94 putchar(' ');
95 }
96 printf("%s\n", error_msgs[err.value]);
97 }
98 errors_n = 0;
99 continue;
100 }
101 }
102}
103
104void
105run_file(char *file_name) {
106 FILE *file = fopen(file_name, "r");
107 if (!file) {
108 fprintf(stderr, "error: couldn't open input file: %s\n", file_name);
109 exit(EXIT_FAILURE);
110 }
111
112 // Read entire file into memory.
113 fseek(file, 0, SEEK_END);
114 size_t file_size = ftell(file);
115 fseek(file, 0, SEEK_SET);
116
117 char *source = malloc(file_size + 1);
118 fread(source, 1, file_size, file);
119 source[file_size] = 0;
120
121 StringView sv = (StringView){
122 .start = source,
123 .n = file_size,
124 };
125
126 process_source(&sv);
127
128 // Check if there were any errors.
129 if (errors_n != 0 && !supress_errors) {
130 report_errors(file_name);
131 }
132
133 free(source);
134 fclose(file);
135}
136
137#define STDIN_BUF_CAP 16
138
139void
140run_stdin(void) {
141 size_t buf_size = 0;
142 char *source = NULL;
143 array_init(source, STDIN_BUF_CAP);
144
145 char c;
146 while ((c = getchar()) != EOF) {
147 array_push(source, c);
148 buf_size++;
149 }
150
151 StringView sv = (StringView){
152 .start = source,
153 .n = buf_size,
154 };
155
156 process_source(&sv);
157
158 // Check if there were any errors.
159 if (errors_n != 0 && !supress_errors) {
160 report_errors("stdin");
161 }
162
163 array_free(source);
164}
165
166#ifndef BIN_NAME
167#define BIN_NAME "bdl"
168#endif
169
170void
171print_usage(void) {
172 printf("Usage: %s [options] <filename filename ...>\n", BIN_NAME);
173 printf("\n");
174 printf("\t-i\tInteractive mode (REPL).\n");
175 printf("\n");
176}
177
178int
179main(int argc, char *argv[]) {
180 init();
181
182 int option;
183 while ((option = getopt(argc, argv, "i")) != -1) {
184 switch (option) {
185 case 'i': {
186 // Interactive mode.
187 run_repl();
188 halt();
189 return EXIT_SUCCESS;
190 } break;
191 default: {
192 print_usage();
193 return EXIT_FAILURE;
194 } break;
195 }
196 }
197
198 // Run from stdin.
199 if (optind == argc) {
200 run_stdin();
201 halt();
202 return EXIT_SUCCESS;
203 }
204
205 // Run from file.
206 while (optind < argc) {
207 char *file_name = argv[optind];
208 run_file(file_name);
209 optind++;
210 }
211
212 halt();
213 return EXIT_SUCCESS;
214}
diff --git a/src/bytecode/objects.c b/src/bytecode/objects.c
deleted file mode 100644
index c2fb989..0000000
--- a/src/bytecode/objects.c
+++ /dev/null
@@ -1,151 +0,0 @@
1#include "objects.h"
2
3Object
4make_string(StringView sv) {
5 Object obj = {
6 .type = OBJ_TYPE_STRING,
7 .text = NULL,
8 };
9 array_init(obj.text, sv.n);
10 array_insert(obj.text, sv.start, sv.n);
11 return obj;
12}
13
14Object
15make_symbol(StringView sv) {
16 Object obj = {
17 .type = OBJ_TYPE_SYMBOL,
18 .text = NULL,
19 };
20 array_init(obj.text, sv.n);
21 array_insert(obj.text, sv.start, sv.n);
22 return obj;
23}
24
25Object
26make_lambda(Chunk *chunk) {
27 Object obj = {
28 .type = OBJ_TYPE_LAMBDA,
29 };
30 obj.closure = malloc(sizeof(Closure));
31 obj.closure->chunk = chunk;
32 array_init(obj.closure->values, 0);
33 return obj;
34}
35
36void
37object_display(Object obj) {
38 switch (obj.type) {
39 case OBJ_TYPE_FIXNUM: {
40 printf("%zd", obj.fixnum);
41 } break;
42 case OBJ_TYPE_TRUE: {
43 printf("true");
44 } break;
45 case OBJ_TYPE_FALSE: {
46 printf("false");
47 } break;
48 case OBJ_TYPE_NIL: {
49 printf("()");
50 } break;
51 case OBJ_TYPE_STRING: {
52 printf("\"%.*s\"", (int)array_size(obj.text), obj.text);
53 } break;
54 case OBJ_TYPE_SYMBOL: {
55 printf(":%.*s", (int)array_size(obj.text), obj.text);
56 } break;
57 case OBJ_TYPE_PAIR: {
58 // printf("(");
59 // display_pair(obj);
60 // printf(")");
61 } break;
62 case OBJ_TYPE_LAMBDA: {
63 Chunk *chunk = obj.closure->chunk;
64 printf("#{procedure:%.*s}",
65 (int)array_size(chunk->name), chunk->name);
66 } break;
67 case OBJ_TYPE_ERR: {
68 printf("#{error}");
69 } break;
70 }
71 return;
72}
73
74void
75object_free(Object *obj) {
76 if (IS_STRING(*obj) || IS_SYMBOL(*obj)) {
77 array_free(obj->text);
78 return;
79 }
80 if (IS_LAMBDA(*obj)) {
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);
90 }
91}
92
93bool
94object_equal(Object a, Object b) {
95 if (a.type != b.type) {
96 return false;
97 }
98 switch (a.type) {
99 case OBJ_TYPE_TRUE:
100 case OBJ_TYPE_FALSE: {
101 return true;
102 } break;
103 case OBJ_TYPE_FIXNUM: {
104 return a.fixnum == b.fixnum;
105 } break;
106 case OBJ_TYPE_SYMBOL:
107 case OBJ_TYPE_STRING: {
108 if (array_size(a.text) != array_size(b.text)) {
109 return false;
110 }
111 for (size_t i = 0; i < array_size(a.text); i++) {
112 if (a.text[i] != b.text[i]) {
113 return false;
114 }
115 }
116 } break;
117 case OBJ_TYPE_LAMBDA: {
118 return a.closure == b.closure;
119 } break;
120 default: {
121 return false;
122 } break;
123 }
124 return true;
125}
126
127Object
128object_copy(Object src) {
129 switch (src.type) {
130 case OBJ_TYPE_SYMBOL:
131 case OBJ_TYPE_STRING: {
132 Object copy = src;
133 copy.text = NULL;
134 array_init(copy.text, array_size(src.text));
135 array_insert(copy.text, src.text, array_size(src.text));
136 return copy;
137 } break;
138 case OBJ_TYPE_LAMBDA: {
139 // Object copy = src;
140 // StringView name = (StringView){
141 // .start = src.chunk->name,
142 // .n = array_size(src.chunk->name),
143 // };
144 // // TODO: copy full chunk?
145 // // copy.chunk = chunk_init(name);
146 // return copy;
147 } break;
148 default: { break; } break;
149 }
150 return src;
151}
diff --git a/src/bytecode/objects.h b/src/bytecode/objects.h
deleted file mode 100755
index a9a7d0f..0000000
--- a/src/bytecode/objects.h
+++ /dev/null
@@ -1,80 +0,0 @@
1#ifndef BDL_OBJECTS_H
2#define BDL_OBJECTS_H
3
4#include "string_view.h"
5#include "darray.h"
6#include "chunk.h"
7
8typedef enum ObjectType {
9 OBJ_TYPE_NIL,
10 OBJ_TYPE_TRUE,
11 OBJ_TYPE_FALSE,
12 OBJ_TYPE_FIXNUM,
13 OBJ_TYPE_SYMBOL,
14 OBJ_TYPE_STRING,
15 OBJ_TYPE_PAIR,
16 OBJ_TYPE_LAMBDA,
17 OBJ_TYPE_ERR,
18} ObjectType;
19
20typedef struct Object Object;
21
22typedef 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
34typedef struct Object {
35 ObjectType type;
36 bool marked;
37 union {
38 // OBJ_TYPE_FIXNUM
39 ssize_t fixnum;
40
41 // OBJ_TYPE_STRING
42 // OBJ_TYPE_SYMBOL
43 char *text;
44
45 // OBJ_TYPE_PAIR
46 // ConsCell *cons_cell;
47
48 // OBJ_TYPE_LAMBDA
49 Closure *closure;
50 };
51} Object;
52
53Object make_string(StringView sv);
54Object make_symbol(StringView sv);
55Object make_lambda(Chunk *chunk);
56void object_display(Object obj);
57void object_free(Object *obj);
58bool object_equal(Object a, Object b);
59Object object_copy(Object src);
60
61// Value initialization.
62#define NIL_VAL ((Object){.type = OBJ_TYPE_NIL})
63#define TRUE_VAL ((Object){.type = OBJ_TYPE_TRUE})
64#define FALSE_VAL ((Object){.type = OBJ_TYPE_FALSE})
65#define FIXNUM_VAL(VAL) ((Object){.type = OBJ_TYPE_FIXNUM, .fixnum = VAL})
66#define BOOL_VAL(VAL) ((VAL) ? TRUE_VAL : FALSE_VAL)
67
68// Value extraction.
69#define AS_FIXNUM(VAL) ((VAL).fixnum)
70
71// Type checking.
72#define IS_NIL(VAL) ((VAL).type == OBJ_TYPE_NIL)
73#define IS_TRUE(VAL) ((VAL).type != OBJ_TYPE_FALSE)
74#define IS_FALSE(VAL) ((VAL).type == OBJ_TYPE_FALSE)
75#define IS_FIXNUM(VAL) ((VAL).type == OBJ_TYPE_FIXNUM)
76#define IS_STRING(VAL) ((VAL).type == OBJ_TYPE_STRING)
77#define IS_SYMBOL(VAL) ((VAL).type == OBJ_TYPE_SYMBOL)
78#define IS_LAMBDA(VAL) ((VAL).type == OBJ_TYPE_LAMBDA)
79
80#endif // BDL_OBJECTS_H
diff --git a/src/bytecode/ops.h b/src/bytecode/ops.h
deleted file mode 100755
index a43aed6..0000000
--- a/src/bytecode/ops.h
+++ /dev/null
@@ -1,46 +0,0 @@
1#ifndef BDL_OPS_H
2#define BDL_OPS_H
3
4typedef enum Ops {
5 // Load/store ops.
6 OP_CONSTANT,
7 OP_LOCAL,
8 OP_CAPTURED,
9 OP_CAPTURE_LOCAL,
10 OP_SET_CAPTURED,
11 OP_DEF_LOCAL,
12 OP_SET_LOCAL,
13 OP_DEF,
14 OP_SET,
15 OP_GET,
16 // Arithmetic ops.
17 OP_SUM,
18 OP_SUB,
19 OP_MUL,
20 OP_DIV,
21 OP_MOD,
22 // Logic ops.
23 OP_NOT,
24 OP_AND,
25 OP_OR,
26 // Numerical comparison ops.
27 OP_EQUAL,
28 OP_LESS,
29 OP_GREATER,
30 OP_LESS_EQUAL,
31 OP_GREATER_EQUAL,
32 // Jump/conditional ops.
33 OP_JUMP,
34 OP_JUMP_IF_FALSE,
35 // Display ops.
36 OP_DISPLAY,
37 OP_PRINT,
38 OP_NEWLINE,
39 // Procedures.
40 OP_CALL,
41 OP_RETURN,
42 // Clear stack after each statement.
43 OP_DROP,
44} Ops;
45
46#endif // BDL_OPS_H
diff --git a/src/bytecode/read_line.c b/src/bytecode/read_line.c
deleted file mode 100755
index 03146ad..0000000
--- a/src/bytecode/read_line.c
+++ /dev/null
@@ -1,32 +0,0 @@
1#include "read_line.h"
2
3static char readline_buf[RL_BUF_SIZE];
4
5StringView
6read_line(void) {
7 // Clear buffer.
8 for (size_t i = 0; i < RL_BUF_SIZE; i++) {
9 readline_buf[i] = 0;
10 }
11
12 // Barebones readline implementation.
13 size_t n = 0;
14 char c;
15 while ((c = getchar()) != '\n') {
16 if (c == '\b') {
17 readline_buf[n] = '\0';
18 n--;
19 } else if (c == EOF || c == '\0') {
20 return (StringView){ .start = NULL, .n = 0 };
21 } else if ((c >= ' ' && c <= '~') && n < RL_BUF_SIZE) {
22 readline_buf[n] = c;
23 n++;
24 }
25 }
26
27 StringView sv = (StringView){
28 .start = (char *)&readline_buf,
29 .n = n,
30 };
31 return sv;
32}
diff --git a/src/bytecode/read_line.h b/src/bytecode/read_line.h
deleted file mode 100755
index 160bce0..0000000
--- a/src/bytecode/read_line.h
+++ /dev/null
@@ -1,10 +0,0 @@
1#ifndef BDL_READ_LINE_H
2#define BDL_READ_LINE_H
3
4#include "string_view.h"
5
6StringView read_line(void);
7
8#define RL_BUF_SIZE 1024
9
10#endif // BDL_READ_LINE_H
diff --git a/src/bytecode/string_view.c b/src/bytecode/string_view.c
deleted file mode 100755
index 8247bd4..0000000
--- a/src/bytecode/string_view.c
+++ /dev/null
@@ -1,40 +0,0 @@
1#include "string_view.h"
2
3char
4sv_next(StringView *sv) {
5 if (sv->n == 0) {
6 return '\0';
7 }
8 char c = sv->start[0];
9 sv->start++;
10 sv->n--;
11 return c;
12}
13
14char
15sv_peek(const StringView *sv) {
16 if (sv->n == 0) {
17 return '\0';
18 }
19 return sv->start[0];
20}
21
22bool
23sv_equal(const StringView *a, const StringView *b) {
24 if (a->n != b->n) {
25 return false;
26 }
27 for (size_t i = 0; i < a->n; i++) {
28 if (a->start[i] != b->start[i]) {
29 return false;
30 }
31 }
32 return true;
33}
34
35void
36sv_write(const StringView *sv) {
37 for (size_t i = 0; i < sv->n; i++) {
38 putchar(sv->start[i]);
39 }
40}
diff --git a/src/bytecode/string_view.h b/src/bytecode/string_view.h
deleted file mode 100755
index 5977ea9..0000000
--- a/src/bytecode/string_view.h
+++ /dev/null
@@ -1,23 +0,0 @@
1#ifndef BDL_STRINGVIEW_H
2#define BDL_STRINGVIEW_H
3
4typedef struct StringView {
5 char *start;
6 size_t n;
7} StringView;
8
9// Consume a character in the stream.
10char sv_next(StringView *sv);
11
12// Check what is the current character in the stream.
13char sv_peek(const StringView *sv);
14
15// Compare if the arguments are the same.
16bool sv_equal(const StringView *a, const StringView *b);
17
18// Write a character to the given output stream.
19void sv_write(const StringView *sv);
20
21#define STRING(STR) (StringView){(STR), sizeof(STR) - 1}
22
23#endif // BDL_STRINGVIEW_H
diff --git a/src/bytecode/types.h b/src/bytecode/types.h
deleted file mode 100755
index dc21756..0000000
--- a/src/bytecode/types.h
+++ /dev/null
@@ -1,30 +0,0 @@
1#ifndef BDL_TYPES_H
2#define BDL_TYPES_H
3
4#include <stdbool.h>
5#include <stddef.h>
6#include <stdint.h>
7
8typedef uint8_t u8;
9typedef uint16_t u16;
10typedef uint32_t u32;
11typedef uint64_t u64;
12typedef int8_t s8;
13typedef int16_t s16;
14typedef int32_t s32;
15typedef int64_t s64;
16typedef volatile u8 vu8;
17typedef volatile u16 vu16;
18typedef volatile u32 vu32;
19typedef volatile u64 vu64;
20typedef volatile s8 vs8;
21typedef volatile s16 vs16;
22typedef volatile s32 vs32;
23typedef volatile s64 vs64;
24
25#define KB(N) ((u64)(N) * 1024)
26#define MB(N) ((u64)KB(N) * 1024)
27#define GB(N) ((u64)MB(N) * 1024)
28#define TB(N) ((u64)GB(N) * 1024)
29
30#endif // BDL_TYPES_H
diff --git a/src/bytecode/vm.h b/src/bytecode/vm.h
deleted file mode 100755
index d363958..0000000
--- a/src/bytecode/vm.h
+++ /dev/null
@@ -1,396 +0,0 @@
1#ifndef BDL_VM_H
2#define BDL_VM_H
3
4#include "types.h"
5#include "errors.h"
6#include "chunk.h"
7#include "ops.h"
8#include "debug.h"
9#include "hashtable.h"
10
11#define VM_FRAMES_CAP 64
12#define VM_STACK_CAP 1024
13
14typedef struct CallFrame {
15 // Current code being run.
16 Closure *closure;
17 // Return counter.
18 u8 *rp;
19 // Starting point of the locals for this procedure.
20 size_t stack_offset;
21} CallFrame;
22
23typedef struct VM {
24 CallFrame *frames;
25 // Stack.
26 Object *stack;
27 // Program counter.
28 u8 *pc;
29 // Global variables.
30 HashTable *globals;
31} VM;
32
33void vm_init(VM *vm);
34void vm_free(VM *vm);
35void vm_reset(VM *vm);
36void vm_interpret(VM *vm);
37
38void
39vm_init(VM *vm) {
40 *vm = (VM){0};
41 array_init(vm->frames, VM_FRAMES_CAP);
42 array_init(vm->stack, VM_STACK_CAP);
43 vm->globals = ht_init();
44}
45
46void
47vm_free(VM *vm) {
48 array_free(vm->frames);
49 array_free(vm->stack);
50 ht_free(vm->globals);
51}
52
53void
54vm_reset(VM *vm) {
55 vm_free(vm);
56 vm_init(vm);
57}
58
59// Helper macros for a more clear VM switch.
60#ifdef DEBUG
61#define STACK_TRACE() \
62 fprintf(stderr, "stack trace:\n"); \
63 for (ssize_t i = array_size(vm->frames) - 1; i >= 0 ; i--) { \
64 CallFrame frame = vm->frames[i]; \
65 Chunk *chunk = frame.closure->chunk; \
66 size_t instruction = vm->pc - chunk->code - 1; \
67 fprintf(stderr, "\t%-4ld -> ", i); \
68 fprintf(stderr, "%.*s",(int)array_size(chunk->name), chunk->name); \
69 fprintf(stderr, ":%ld:%ld\n", chunk->lines[instruction].line, chunk->lines[instruction].col); \
70 vm->pc = frame.rp; \
71 }
72#else
73#define STACK_TRACE()
74#endif
75
76#define RUNTIME_ERROR(ERR) \
77 error_push((Error){ \
78 .type = ERR_TYPE_RUNTIME, \
79 .value = (ERR), \
80 .line = chunk->lines[vm->pc - chunk->code - 1].line, \
81 .col = chunk->lines[vm->pc - chunk->code - 1].col, \
82 }); \
83 STACK_TRACE() \
84 return
85
86#define FIXNUM_ARITHMETIC_OP(OP) \
87 do { \
88 ssize_t n = AS_FIXNUM(array_pop(vm->stack)); \
89 size_t stack_size = array_size(vm->stack) - n; \
90 Object obj = array_peek(vm->stack, n - 1); \
91 if (!IS_FIXNUM(obj)) { \
92 RUNTIME_ERROR(ERR_WRONG_ARG_TYPE); \
93 return; \
94 } \
95 ssize_t acc = AS_FIXNUM(obj); \
96 while (n > 1) { \
97 obj = array_peek(vm->stack, n - 2); \
98 if (!IS_FIXNUM(obj)) { \
99 RUNTIME_ERROR(ERR_WRONG_ARG_TYPE); \
100 return; \
101 } \
102 ssize_t current = AS_FIXNUM(obj); \
103 acc = acc OP current; \
104 n--; \
105 } \
106 array_head(vm->stack)->size = stack_size; \
107 array_push(vm->stack, FIXNUM_VAL(acc)); \
108 } while (false)
109
110#define FIXNUM_COMPARE_OP(OP) \
111 do { \
112 ssize_t n = AS_FIXNUM(array_pop(vm->stack)); \
113 size_t stack_size = array_size(vm->stack) - n; \
114 Object obj = array_peek(vm->stack, n - 1); \
115 if (!IS_FIXNUM(obj)) { \
116 RUNTIME_ERROR(ERR_WRONG_ARG_TYPE); \
117 return; \
118 } \
119 ssize_t prev = AS_FIXNUM(obj); \
120 bool ret = true; \
121 while (n > 1) { \
122 obj = array_peek(vm->stack, n - 2); \
123 if (!IS_FIXNUM(obj)) { \
124 RUNTIME_ERROR(ERR_WRONG_ARG_TYPE); \
125 return; \
126 } \
127 ssize_t current = AS_FIXNUM(obj); \
128 ret = ret && (prev OP current); \
129 prev = current; \
130 n--; \
131 } \
132 array_head(vm->stack)->size = stack_size; \
133 array_push(vm->stack, BOOL_VAL(ret)); \
134 } while (false)
135
136#define LOGIC_OP(OP) \
137 do { \
138 ssize_t n = AS_FIXNUM(array_pop(vm->stack)); \
139 size_t stack_size = array_size(vm->stack) - n; \
140 Object obj = array_peek(vm->stack, n - 1); \
141 bool ret = IS_TRUE(obj); \
142 while (n > 1) { \
143 obj = array_peek(vm->stack, n - 2); \
144 bool current = IS_TRUE(obj); \
145 ret = ret OP current; \
146 n--; \
147 } \
148 array_head(vm->stack)->size = stack_size; \
149 array_push(vm->stack, BOOL_VAL(ret)); \
150 } while (false)
151
152void
153vm_interpret(VM *vm) {
154 CallFrame *frame = &vm->frames[0];
155 Chunk *chunk = frame->closure->chunk;
156 vm->pc = chunk->code;
157 frame->rp = NULL;
158
159 if (chunk->code == NULL || array_size(chunk->code) == 0) {
160 error_push((Error){
161 .type = ERR_TYPE_RUNTIME,
162 .value = ERR_EMPTY_CHUNK,
163 });
164 return;
165 }
166
167 while (true) {
168#ifdef DEBUG_TRACE_EXECUTION
169 printf("stack: [ ");
170 for (size_t i = 0; i < array_size(vm->stack); i++) {
171 object_display(vm->stack[i]);
172 if (i < array_size(vm->stack) - 1) {
173 printf(" | ");
174 }
175 }
176 printf(" ]\nop: ");
177 disassemble_instruction(chunk, (vm->pc - chunk->code));
178#endif
179 u8 instruction = *vm->pc++;
180 switch (instruction) {
181 case OP_LOCAL: {
182 ssize_t idx = AS_FIXNUM(array_pop(vm->stack));
183 array_push(vm->stack, vm->stack[frame->stack_offset + idx]);
184 } break;
185 case OP_CONSTANT: {
186 u8 constant = *vm->pc++;
187 Object obj = chunk->constants[constant];
188 array_push(vm->stack, obj);
189 } break;
190 case OP_CAPTURED: {
191 ssize_t idx = AS_FIXNUM(array_pop(vm->stack));
192 array_push(vm->stack, frame->closure->values[idx]);
193 } break;
194 case OP_CAPTURE_LOCAL: {
195 // This is a closure with captured variables. We need a copy
196 // of it that lives on the heap.
197 Object proc = array_pop(vm->stack);
198 proc = make_lambda(proc.closure->chunk);
199
200 ssize_t n_captured = AS_FIXNUM(array_pop(vm->stack));
201 for (ssize_t i = 0; i < n_captured; i++) {
202 Object value = array_pop(vm->stack);
203 array_push(proc.closure->values, value);
204 }
205 array_push(vm->stack, proc);
206 } break;
207 case OP_SET_CAPTURED: {
208 Object value = array_pop(vm->stack);
209 ssize_t idx = AS_FIXNUM(array_pop(vm->stack));
210 frame->closure->values[idx] = value;
211 } break;
212 case OP_DEF_LOCAL: {
213 Object value = array_pop(vm->stack);
214 ssize_t idx = AS_FIXNUM(array_pop(vm->stack));
215 vm->stack[frame->stack_offset + idx] = value;
216 } break;
217 case OP_SET_LOCAL: {
218 Object value = array_pop(vm->stack);
219 ssize_t idx = AS_FIXNUM(array_pop(vm->stack));
220 CallFrame frame = vm->frames[array_size(vm->frames) - 1];
221 vm->stack[frame.stack_offset + idx] = value;
222 } break;
223 case OP_DEF: {
224 Object value = array_pop(vm->stack);
225 Object name = array_pop(vm->stack);
226 ht_insert(vm->globals, name, value);
227 } break;
228 case OP_SET: {
229 Object value = array_pop(vm->stack);
230 Object name = array_pop(vm->stack);
231 Object *prev = ht_lookup(vm->globals, name);
232 if (prev == NULL) {
233 RUNTIME_ERROR(ERR_SYMBOL_NOT_FOUND);
234 return;
235 }
236 ht_insert(vm->globals, name, value);
237 } break;
238 case OP_GET: {
239 Object name = array_pop(vm->stack);
240 Object *value = ht_lookup(vm->globals, name);
241 if (value == NULL) {
242 RUNTIME_ERROR(ERR_SYMBOL_NOT_FOUND);
243 return;
244 }
245 array_push(vm->stack, *value);
246 } break;
247 case OP_SUM: { FIXNUM_ARITHMETIC_OP(+); } break;
248 case OP_SUB: { FIXNUM_ARITHMETIC_OP(-); } break;
249 case OP_MUL: { FIXNUM_ARITHMETIC_OP(*); } break;
250 case OP_DIV: { FIXNUM_ARITHMETIC_OP(/); } break;
251 case OP_MOD: { FIXNUM_ARITHMETIC_OP(%); } break;
252 case OP_NOT: {
253 Object prev = array_pop(vm->stack);
254 Object new = IS_TRUE(prev) ? FALSE_VAL : TRUE_VAL;
255 array_push(vm->stack, new);
256 } break;
257 case OP_AND: { LOGIC_OP(&&); } break;
258 case OP_OR: { LOGIC_OP(||); } break;
259 case OP_EQUAL: { FIXNUM_COMPARE_OP(==); } break;
260 case OP_LESS: { FIXNUM_COMPARE_OP(<); } break;
261 case OP_GREATER: { FIXNUM_COMPARE_OP(>); } break;
262 case OP_LESS_EQUAL: { FIXNUM_COMPARE_OP(<=); } break;
263 case OP_GREATER_EQUAL: { FIXNUM_COMPARE_OP(>=); } break;
264 case OP_JUMP: {
265 u16 a = *vm->pc++;
266 u16 b = *vm->pc++;
267 s16 offset = (a << 8) | b;
268 vm->pc += offset;
269 } break;
270 case OP_JUMP_IF_FALSE: {
271 u16 a = *vm->pc++;
272 u16 b = *vm->pc++;
273 s16 offset = (a << 8) | b;
274 if (IS_FALSE(array_pop(vm->stack))) {
275 vm->pc += offset;
276 }
277 } break;
278 case OP_DISPLAY: {
279 object_display(array_pop(vm->stack));
280 } break;
281 case OP_PRINT: {
282 Object obj = array_pop(vm->stack);
283 if (!IS_STRING(obj)) {
284 RUNTIME_ERROR(ERR_WRONG_ARG_TYPE);
285 }
286 StringView scanner = (StringView) {
287 .start = obj.text,
288 .n = array_size(obj.text),
289 };
290 while (scanner.n != 0) {
291 char c = sv_next(&scanner);
292 if (c == '\\' && sv_peek(&scanner) == 'n') {
293 putchar('\n');
294 sv_next(&scanner);
295 continue;
296 }
297 if (c == '\\' && sv_peek(&scanner) == '"') {
298 putchar('"');
299 sv_next(&scanner);
300 continue;
301 }
302 putchar(c);
303 }
304 } break;
305 case OP_NEWLINE: {
306 printf("\n");
307 } break;
308 case OP_CALL: {
309 ssize_t n_args = AS_FIXNUM(array_pop(vm->stack));
310 Object proc = vm->stack[array_size(vm->stack) - 1 - n_args];
311
312 // Check the number of arguments is correct.
313 // NOTE: This is probably better handled at compilation, but for
314 // now this is simpler to implement.
315 ssize_t n_params = proc.closure->chunk->n_params;
316 ssize_t n_locals = proc.closure->chunk->n_locals - n_params;
317 if (n_args < n_params) {
318 RUNTIME_ERROR(ERR_NOT_ENOUGH_ARGS);
319 } else if (n_args > n_params) {
320 RUNTIME_ERROR(ERR_TOO_MANY_ARGS);
321 }
322
323#ifdef DEBUG
324 disassemble_chunk(proc.closure->chunk);
325 printf("n_locals: %ld\n", n_locals);
326 printf("n_params: %ld\n", n_params);
327#endif
328 // Tail-call optimization.
329 if (proc.closure->chunk != chunk || *vm->pc != OP_RETURN) {
330 // Prepare new call frame.
331 CallFrame new_frame = (CallFrame){
332 .closure = proc.closure,
333 .rp = vm->pc,
334 .stack_offset = array_size(vm->stack) - n_params,
335 };
336 array_push(vm->frames, new_frame);
337 frame = &vm->frames[array_size(vm->frames) - 1];
338 chunk = frame->closure->chunk;
339
340 // Prepare local slots.
341 for (ssize_t i = 0; i < n_locals; i++) {
342 array_push(vm->stack, NIL_VAL);
343 }
344 } else {
345 // Bind tail-call parameters.
346 for (ssize_t i = 0; i < n_params; i++) {
347 size_t offset = n_locals + n_params - 1 - i;
348 Object obj = array_peek(vm->stack, offset);
349 vm->stack[frame->stack_offset + i] = obj;
350 }
351
352 // Reset stack size.
353 size_t offset = frame->stack_offset + n_params + n_locals;
354 array_head(vm->stack)->size = offset;
355 }
356 vm->pc = chunk->code;
357 } break;
358 case OP_RETURN: {
359 if (frame->rp == NULL) {
360 Object ret = array_pop(vm->stack);
361 if (!IS_NIL(ret)) {
362 object_display(ret);
363 printf("\n");
364 }
365 return;
366 }
367 vm->pc = frame->rp;
368 array_head(vm->frames)->size--;
369 Object ret = array_pop(vm->stack);
370 array_head(vm->stack)->size = frame->stack_offset - 1;
371 array_push(vm->stack, ret);
372 frame = &vm->frames[array_size(vm->frames) - 1];
373 chunk = frame->closure->chunk;
374 } break;
375 case OP_DROP: {
376 array_head(vm->stack)->size = 0;
377 } break;
378 default: {
379 RUNTIME_ERROR(ERR_NOT_IMPLEMENTED);
380 } break;
381 }
382 }
383
384 error_push((Error){
385 .type = ERR_TYPE_RUNTIME,
386 .value = ERR_PC_OOB,
387 .line = chunk->lines[0].line,
388 .col = chunk->lines[0].col,
389 });
390}
391
392#undef FIXNUM_ARITHMETIC_OP
393#undef FIXNUM_COMPARE_OP
394#undef LOGIC_OP
395
396#endif // BDL_VM_H