diff options
author | Bad Diode <bd@badd10de.dev> | 2022-02-01 18:36:52 +0100 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2022-02-01 18:36:52 +0100 |
commit | ee1a5de91c875fb66724dc21c02333bfebe2a812 (patch) | |
tree | d3eaa226816d295bb9dc48a2aed27044832ec413 /src/bytecode | |
parent | 3156265c7b2da8cc43fee996c0518ea274d39c8a (diff) | |
download | bdl-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.c | 47 | ||||
-rwxr-xr-x | src/bytecode/chunk.h | 36 | ||||
-rwxr-xr-x | src/bytecode/compiler.h | 748 | ||||
-rwxr-xr-x | src/bytecode/darray.h | 81 | ||||
-rwxr-xr-x | src/bytecode/debug.h | 105 | ||||
-rwxr-xr-x | src/bytecode/errors.c | 52 | ||||
-rwxr-xr-x | src/bytecode/errors.h | 46 | ||||
-rw-r--r-- | src/bytecode/hashtable.h | 208 | ||||
-rwxr-xr-x | src/bytecode/lexer.c | 294 | ||||
-rwxr-xr-x | src/bytecode/lexer.h | 92 | ||||
-rwxr-xr-x | src/bytecode/main.c | 214 | ||||
-rw-r--r-- | src/bytecode/objects.c | 151 | ||||
-rwxr-xr-x | src/bytecode/objects.h | 80 | ||||
-rwxr-xr-x | src/bytecode/ops.h | 46 | ||||
-rwxr-xr-x | src/bytecode/read_line.c | 32 | ||||
-rwxr-xr-x | src/bytecode/read_line.h | 10 | ||||
-rwxr-xr-x | src/bytecode/string_view.c | 40 | ||||
-rwxr-xr-x | src/bytecode/string_view.h | 23 | ||||
-rwxr-xr-x | src/bytecode/types.h | 30 | ||||
-rwxr-xr-x | src/bytecode/vm.h | 396 |
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 | |||
4 | Chunk * | ||
5 | chunk_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 | |||
17 | void | ||
18 | chunk_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 | |||
30 | void | ||
31 | add_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 | |||
37 | size_t | ||
38 | add_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 | |||
7 | typedef struct Object Object; | ||
8 | |||
9 | typedef struct LineInfo { | ||
10 | size_t line; | ||
11 | size_t col; | ||
12 | } LineInfo; | ||
13 | |||
14 | typedef 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 | |||
31 | Chunk * chunk_init(StringView name); | ||
32 | void add_code(Chunk *chunk, u8 byte, size_t line, size_t col); | ||
33 | size_t add_constant(Chunk *chunk, Object obj); | ||
34 | void 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 | |||
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 | ||
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 | |||
6 | typedef 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 | |||
38 | static 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 | |||
47 | static 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 | |||
62 | static inline | ||
63 | char * _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 | |||
7 | void disassemble_chunk(Chunk *chunk); | ||
8 | size_t disassemble_instruction(Chunk *chunk, size_t offset); | ||
9 | |||
10 | static 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 | |||
52 | void | ||
53 | disassemble_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 | |||
71 | size_t | ||
72 | disassemble_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 | |||
3 | static 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 | |||
23 | static Error errors[ERR_MAX_NUMBER]; | ||
24 | static size_t errors_n = 0; | ||
25 | static bool supress_errors = false; | ||
26 | |||
27 | void | ||
28 | error_push(Error error) { | ||
29 | if (errors_n < ERR_MAX_NUMBER) { | ||
30 | errors[errors_n++] = error; | ||
31 | } | ||
32 | } | ||
33 | |||
34 | void | ||
35 | report_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 | |||
4 | typedef 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 | |||
12 | typedef 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 | |||
34 | typedef struct Error { | ||
35 | ErrorType type; | ||
36 | ErrorValue value; | ||
37 | size_t line; | ||
38 | size_t col; | ||
39 | } Error; | ||
40 | |||
41 | void error_push(Error error); | ||
42 | void 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 | |||
14 | typedef struct HashTablePair { | ||
15 | Object *key; | ||
16 | Object *value; | ||
17 | } HashTablePair; | ||
18 | |||
19 | struct HashTable; | ||
20 | typedef uint64_t (HashFunction)(const struct HashTable *table, void *bytes); | ||
21 | |||
22 | typedef 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. | ||
43 | static 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. | ||
55 | static inline uint64_t | ||
56 | _fibonacci_hash(uint64_t hash, size_t shift_amount) { | ||
57 | return (hash * UINT64_C(11400714819323198485)) >> (64 - shift_amount); | ||
58 | } | ||
59 | |||
60 | uint64_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 | |||
68 | static inline float | ||
69 | ht_load_factor(const HashTable *table) { | ||
70 | return (float)array_size(table->pairs) / (float)array_cap(table->pairs); | ||
71 | } | ||
72 | |||
73 | HashTable * | ||
74 | ht_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 | |||
88 | void | ||
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 | |||
123 | void | ||
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 | |||
160 | void | ||
161 | ht_insert(HashTable *table, Object key, Object value) { | ||
162 | _ht_maybe_grow(table); | ||
163 | _ht_insert(table, key, value); | ||
164 | return; | ||
165 | } | ||
166 | |||
167 | Object * | ||
168 | ht_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 | |||
194 | void | ||
195 | ht_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 | |||
3 | static 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 | |||
39 | void | ||
40 | print_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 | |||
62 | char | ||
63 | scan_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 | |||
75 | char | ||
76 | scan_peek(const Scanner *scanner) { | ||
77 | return sv_peek(&scanner->current); | ||
78 | } | ||
79 | |||
80 | bool | ||
81 | scan_has_next(const Scanner *scanner) { | ||
82 | return scanner->current.n != 0; | ||
83 | } | ||
84 | |||
85 | void | ||
86 | skip_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 | |||
105 | bool | ||
106 | is_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 | |||
130 | TokenType | ||
131 | find_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 | |||
174 | Token * | ||
175 | tokenize(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 | |||
6 | typedef 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 | |||
53 | typedef struct Token { | ||
54 | TokenType type; | ||
55 | StringView value; | ||
56 | size_t line; | ||
57 | size_t column; | ||
58 | } Token; | ||
59 | |||
60 | typedef 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. | ||
68 | void print_token(Token tok); | ||
69 | |||
70 | // Same functionality as the ScanView pairs, but keeping track of line and | ||
71 | // column numbers. | ||
72 | char scan_next(Scanner *scanner); | ||
73 | char scan_peek(const Scanner *scanner); | ||
74 | |||
75 | // Check if the current scanner still have characters left. | ||
76 | bool scan_has_next(const Scanner *scanner); | ||
77 | |||
78 | // Advance the scanner until we ran out of whitespace. | ||
79 | void skip_whitespace(Scanner *scanner); | ||
80 | |||
81 | // Check if a given character is a delimiter. | ||
82 | bool is_delimiter(char c); | ||
83 | |||
84 | // Extract the token type from the current string. | ||
85 | TokenType find_primitive_type(const StringView value); | ||
86 | |||
87 | // Generate a list of tokens from the given string. | ||
88 | Token * 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 | |||
26 | static VM vm; | ||
27 | |||
28 | void | ||
29 | init(void) { | ||
30 | vm_init(&vm); | ||
31 | } | ||
32 | |||
33 | void | ||
34 | halt(void) { | ||
35 | vm_free(&vm); | ||
36 | } | ||
37 | |||
38 | void | ||
39 | process_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 | |||
74 | void | ||
75 | run_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 | |||
104 | void | ||
105 | run_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 | |||
139 | void | ||
140 | run_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 | |||
170 | void | ||
171 | print_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 | |||
178 | int | ||
179 | main(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 | |||
3 | Object | ||
4 | make_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 | |||
14 | Object | ||
15 | make_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 | |||
25 | Object | ||
26 | make_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 | |||
36 | void | ||
37 | object_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 | |||
74 | void | ||
75 | object_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 | |||
93 | bool | ||
94 | object_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 | |||
127 | Object | ||
128 | object_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 | |||
8 | typedef 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 | |||
20 | typedef struct Object Object; | ||
21 | |||
22 | typedef struct Closure { | ||
23 | // Non-owning reference to a chunk. | ||
24 | Chunk *chunk; | ||
25 | // Captured values for this closure. | ||
26 | Object *values; | ||
27 | } Closure; | ||
28 | |||
29 | // typdef struct ConsCell { | ||
30 | // struct Object *car; | ||
31 | // struct Object *cdr; | ||
32 | // } ConsCell; | ||
33 | |||
34 | typedef 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 | |||
53 | Object make_string(StringView sv); | ||
54 | Object make_symbol(StringView sv); | ||
55 | Object make_lambda(Chunk *chunk); | ||
56 | void object_display(Object obj); | ||
57 | void object_free(Object *obj); | ||
58 | bool object_equal(Object a, Object b); | ||
59 | Object 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 | |||
4 | typedef 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 | |||
3 | static char readline_buf[RL_BUF_SIZE]; | ||
4 | |||
5 | StringView | ||
6 | read_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 | |||
6 | StringView 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 | |||
3 | char | ||
4 | sv_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 | |||
14 | char | ||
15 | sv_peek(const StringView *sv) { | ||
16 | if (sv->n == 0) { | ||
17 | return '\0'; | ||
18 | } | ||
19 | return sv->start[0]; | ||
20 | } | ||
21 | |||
22 | bool | ||
23 | sv_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 | |||
35 | void | ||
36 | sv_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 | |||
4 | typedef struct StringView { | ||
5 | char *start; | ||
6 | size_t n; | ||
7 | } StringView; | ||
8 | |||
9 | // Consume a character in the stream. | ||
10 | char sv_next(StringView *sv); | ||
11 | |||
12 | // Check what is the current character in the stream. | ||
13 | char sv_peek(const StringView *sv); | ||
14 | |||
15 | // Compare if the arguments are the same. | ||
16 | bool sv_equal(const StringView *a, const StringView *b); | ||
17 | |||
18 | // Write a character to the given output stream. | ||
19 | void 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 | |||
8 | typedef uint8_t u8; | ||
9 | typedef uint16_t u16; | ||
10 | typedef uint32_t u32; | ||
11 | typedef uint64_t u64; | ||
12 | typedef int8_t s8; | ||
13 | typedef int16_t s16; | ||
14 | typedef int32_t s32; | ||
15 | typedef int64_t s64; | ||
16 | typedef volatile u8 vu8; | ||
17 | typedef volatile u16 vu16; | ||
18 | typedef volatile u32 vu32; | ||
19 | typedef volatile u64 vu64; | ||
20 | typedef volatile s8 vs8; | ||
21 | typedef volatile s16 vs16; | ||
22 | typedef volatile s32 vs32; | ||
23 | typedef 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 | |||
14 | typedef 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 | |||
23 | typedef 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 | |||
33 | void vm_init(VM *vm); | ||
34 | void vm_free(VM *vm); | ||
35 | void vm_reset(VM *vm); | ||
36 | void vm_interpret(VM *vm); | ||
37 | |||
38 | void | ||
39 | vm_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 | |||
46 | void | ||
47 | vm_free(VM *vm) { | ||
48 | array_free(vm->frames); | ||
49 | array_free(vm->stack); | ||
50 | ht_free(vm->globals); | ||
51 | } | ||
52 | |||
53 | void | ||
54 | vm_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 | |||
152 | void | ||
153 | vm_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 | ||