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 | |
parent | 3156265c7b2da8cc43fee996c0518ea274d39c8a (diff) | |
download | bdl-ee1a5de91c875fb66724dc21c02333bfebe2a812.tar.gz bdl-ee1a5de91c875fb66724dc21c02333bfebe2a812.zip |
Add new syntax to lexer and prepare refactor
Diffstat (limited to 'src')
51 files changed, 237 insertions, 5804 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 | ||
diff --git a/src/errors.c b/src/errors.c index 11348fd..efc834f 100644 --- a/src/errors.c +++ b/src/errors.c | |||
@@ -3,44 +3,42 @@ | |||
3 | static const char* error_msgs[] = { | 3 | static const char* error_msgs[] = { |
4 | [ERR_UNKNOWN] = "error: something unexpected happened", | 4 | [ERR_UNKNOWN] = "error: something unexpected happened", |
5 | [ERR_UNMATCHED_STRING] = "error: unmatched string delimiter", | 5 | [ERR_UNMATCHED_STRING] = "error: unmatched string delimiter", |
6 | [ERR_UNBALANCED_PAREN] = "error: unbalanced parentheses", | 6 | [ERR_UNKNOWN_TOK_TYPE] = "error: unknown token type", |
7 | [ERR_NOT_IMPLEMENTED] = "error: not implemented", | 7 | [ERR_MALFORMED_NUMBER] = "error: malformed number token", |
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_NOT_CALLABLE] = "error: 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_AMBIGUOUS_PARAMS] = "error: ambiguous parameter names", | ||
19 | }; | 8 | }; |
20 | 9 | ||
10 | static Error current_error = {.value = ERR_OK}; | ||
11 | |||
21 | void | 12 | void |
22 | error_push(Errors *errors, Error error) { | 13 | push_error(ErrorType type, ErrorValue value, size_t line, size_t col) { |
23 | if (errors->n < ERR_MAX_NUMBER) { | 14 | if (has_errors()) { |
24 | errors->errors[errors->n++] = error; | 15 | return; |
25 | } | 16 | } |
17 | current_error.type = type; | ||
18 | current_error.value = value; | ||
19 | current_error.line = line; | ||
20 | current_error.col = col; | ||
21 | } | ||
22 | |||
23 | bool | ||
24 | has_errors(void) { | ||
25 | return current_error.value != ERR_OK; | ||
26 | } | 26 | } |
27 | 27 | ||
28 | void | 28 | void |
29 | report_errors(Errors *errors, const char *file_name) { | 29 | check_errors(const char *file_name) { |
30 | for (size_t i = 0; i < errors->n; i++) { | 30 | if (!has_errors()) { |
31 | Error err = errors->errors[i]; | 31 | return; |
32 | fprintf(stderr, "%s", file_name); | 32 | } |
33 | if (err.line != 0) { | 33 | fprintf(stderr, "%s", file_name); |
34 | fprintf(stderr, ":%ld:%ld", err.line, err.col); | 34 | if (current_error.line != 0) { |
35 | } | 35 | fprintf(stderr, ":%ld:%ld", current_error.line, current_error.col); |
36 | switch (err.type) { | 36 | } |
37 | case ERR_TYPE_LEXER: { fprintf(stderr, ": [lexer]"); } break; | 37 | switch (current_error.type) { |
38 | case ERR_TYPE_COMPILER: { fprintf(stderr, ": [compiler]"); } break; | 38 | case ERR_TYPE_LEXER: { fprintf(stderr, ": [lexer] "); } break; |
39 | case ERR_TYPE_RUNTIME: { fprintf(stderr, ": [runtime]"); } break; | 39 | case ERR_TYPE_PARSER: { fprintf(stderr, ": [parser] "); } break; |
40 | case ERR_TYPE_PARSER: { fprintf(stderr, ": [parser]"); } break; | 40 | default: break; |
41 | default: break; | ||
42 | } | ||
43 | fprintf(stderr, " %s\n", error_msgs[err.value]); | ||
44 | } | 41 | } |
45 | errors->n = 0; | 42 | fprintf(stderr, "%s\n", error_msgs[current_error.value]); |
43 | exit(EXIT_FAILURE); | ||
46 | } | 44 | } |
diff --git a/src/errors.h b/src/errors.h index 7d8e977..8a378a2 100644 --- a/src/errors.h +++ b/src/errors.h | |||
@@ -6,26 +6,14 @@ | |||
6 | typedef enum ErrorType { | 6 | typedef enum ErrorType { |
7 | ERR_TYPE_LEXER, | 7 | ERR_TYPE_LEXER, |
8 | ERR_TYPE_PARSER, | 8 | ERR_TYPE_PARSER, |
9 | ERR_TYPE_COMPILER, | ||
10 | ERR_TYPE_RUNTIME, | ||
11 | } ErrorType; | 9 | } ErrorType; |
12 | 10 | ||
13 | typedef enum ErrorValue { | 11 | typedef enum ErrorValue { |
14 | ERR_UNKNOWN = 0, | 12 | ERR_UNKNOWN = 0, |
15 | ERR_UNMATCHED_STRING, | 13 | ERR_UNMATCHED_STRING, |
16 | ERR_UNBALANCED_PAREN, | 14 | ERR_UNKNOWN_TOK_TYPE, |
17 | ERR_NOT_IMPLEMENTED, | 15 | ERR_MALFORMED_NUMBER, |
18 | ERR_EOF_REACHED, | 16 | ERR_OK, |
19 | ERR_UNKNOWN_TOKEN, | ||
20 | ERR_UNKNOWN_OBJ_TYPE, | ||
21 | ERR_NOT_A_SYMBOL, | ||
22 | ERR_SYMBOL_NOT_FOUND, | ||
23 | ERR_NOT_CALLABLE, | ||
24 | ERR_NOT_ENOUGH_ARGS, | ||
25 | ERR_TOO_MANY_ARGS, | ||
26 | ERR_WRONG_ARG_TYPE, | ||
27 | ERR_DIVISION_BY_ZERO, | ||
28 | ERR_AMBIGUOUS_PARAMS, | ||
29 | } ErrorValue; | 17 | } ErrorValue; |
30 | 18 | ||
31 | typedef struct Error { | 19 | typedef struct Error { |
@@ -35,14 +23,8 @@ typedef struct Error { | |||
35 | size_t col; | 23 | size_t col; |
36 | } Error; | 24 | } Error; |
37 | 25 | ||
38 | #define ERR_MAX_NUMBER 16 | 26 | void push_error(ErrorType type, ErrorValue value, size_t line, size_t col); |
39 | 27 | void check_errors(const char *file_name); | |
40 | typedef struct Errors { | 28 | bool has_errors(void); |
41 | Error errors[ERR_MAX_NUMBER]; | ||
42 | size_t n; | ||
43 | } Errors; | ||
44 | |||
45 | void error_push(Errors *errors, Error error); | ||
46 | void report_errors(Errors *errors, const char *file_name); | ||
47 | 29 | ||
48 | #endif // BDL_ERRORS_H | 30 | #endif // BDL_ERRORS_H |
diff --git a/src/lexer.c b/src/lexer.c index 09c8f6c..56b670b 100644 --- a/src/lexer.c +++ b/src/lexer.c | |||
@@ -5,7 +5,11 @@ static const char* token_str[] = { | |||
5 | [TOKEN_UNKNOWN] = "TOKEN_UNKNOWN", | 5 | [TOKEN_UNKNOWN] = "TOKEN_UNKNOWN", |
6 | [TOKEN_LPAREN] = "TOKEN_LPAREN", | 6 | [TOKEN_LPAREN] = "TOKEN_LPAREN", |
7 | [TOKEN_RPAREN] = "TOKEN_RPAREN", | 7 | [TOKEN_RPAREN] = "TOKEN_RPAREN", |
8 | [TOKEN_FIXNUM] = "TOKEN_FIXNUM", | 8 | [TOKEN_LSQUARE] = "TOKEN_LSQUARE", |
9 | [TOKEN_RSQUARE] = "TOKEN_RSQUARE", | ||
10 | [TOKEN_LCURLY] = "TOKEN_LCURLY", | ||
11 | [TOKEN_RCURLY] = "TOKEN_RCURLY", | ||
12 | [TOKEN_NUMBER] = "TOKEN_NUMBER", | ||
9 | [TOKEN_SYMBOL] = "TOKEN_SYMBOL", | 13 | [TOKEN_SYMBOL] = "TOKEN_SYMBOL", |
10 | [TOKEN_STRING] = "TOKEN_STRING", | 14 | [TOKEN_STRING] = "TOKEN_STRING", |
11 | [TOKEN_NIL] = "TOKEN_NIL", | 15 | [TOKEN_NIL] = "TOKEN_NIL", |
@@ -16,6 +20,10 @@ static const char* token_str[] = { | |||
16 | [TOKEN_DEF] = "TOKEN_DEF", | 20 | [TOKEN_DEF] = "TOKEN_DEF", |
17 | [TOKEN_SET] = "TOKEN_SET", | 21 | [TOKEN_SET] = "TOKEN_SET", |
18 | [TOKEN_FUN] = "TOKEN_FUN", | 22 | [TOKEN_FUN] = "TOKEN_FUN", |
23 | [TOKEN_STRUCT] = "TOKEN_STRUCT", | ||
24 | [TOKEN_COLON] = "TOKEN_COLON", | ||
25 | [TOKEN_DOT] = "TOKEN_DOT", | ||
26 | [TOKEN_AT] = "TOKEN_AT", | ||
19 | [TOKEN_EOF] = "TOKEN_EOF", | 27 | [TOKEN_EOF] = "TOKEN_EOF", |
20 | }; | 28 | }; |
21 | 29 | ||
@@ -24,14 +32,8 @@ print_token(Token tok) { | |||
24 | printf("[%4ld:%-4ld] ", tok.line, tok.col); | 32 | printf("[%4ld:%-4ld] ", tok.line, tok.col); |
25 | printf("%s", token_str[tok.type]); | 33 | printf("%s", token_str[tok.type]); |
26 | switch (tok.type) { | 34 | switch (tok.type) { |
27 | case TOKEN_FIXNUM: { | 35 | case TOKEN_NUMBER: |
28 | printf(" -> "); | 36 | case TOKEN_SYMBOL: |
29 | sv_write(&tok.value); | ||
30 | } break; | ||
31 | case TOKEN_SYMBOL: { | ||
32 | printf(" -> "); | ||
33 | sv_write(&tok.value); | ||
34 | } break; | ||
35 | case TOKEN_STRING: { | 37 | case TOKEN_STRING: { |
36 | printf(" -> "); | 38 | printf(" -> "); |
37 | sv_write(&tok.value); | 39 | sv_write(&tok.value); |
@@ -55,6 +57,12 @@ scan_next(Scanner *scanner) { | |||
55 | return c; | 57 | return c; |
56 | } | 58 | } |
57 | 59 | ||
60 | void | ||
61 | scan_rewind(Scanner *scanner) { | ||
62 | sv_rewind(&scanner->current); | ||
63 | scanner->offset--; | ||
64 | } | ||
65 | |||
58 | char | 66 | char |
59 | scan_peek(const Scanner *scanner) { | 67 | scan_peek(const Scanner *scanner) { |
60 | return sv_peek(&scanner->current); | 68 | return sv_peek(&scanner->current); |
@@ -95,6 +103,12 @@ is_delimiter(char c) { | |||
95 | case '\'': | 103 | case '\'': |
96 | case '(': | 104 | case '(': |
97 | case ')': | 105 | case ')': |
106 | case '[': | ||
107 | case ']': | ||
108 | case '{': | ||
109 | case '}': | ||
110 | case ':': | ||
111 | case '@': | ||
98 | case ' ': | 112 | case ' ': |
99 | case '\f': | 113 | case '\f': |
100 | case '\n': | 114 | case '\n': |
@@ -110,22 +124,65 @@ is_delimiter(char c) { | |||
110 | #define TOKEN_IS_KEYWORD(VAL, KEYWORD) \ | 124 | #define TOKEN_IS_KEYWORD(VAL, KEYWORD) \ |
111 | sv_equal(&(VAL), &(StringView){(KEYWORD), sizeof(KEYWORD) - 1}) | 125 | sv_equal(&(VAL), &(StringView){(KEYWORD), sizeof(KEYWORD) - 1}) |
112 | 126 | ||
113 | TokenType | 127 | size_t |
114 | find_primitive_type(const StringView value) { | 128 | scan_number_token(Scanner *scanner) { |
115 | bool is_fixnum = true; | 129 | char first = scan_next(scanner); |
116 | for (size_t i = 0; i < value.n; i++) { | 130 | char second = scan_peek(scanner); |
117 | char c = value.start[i]; | 131 | size_t n = 1; |
118 | if (i == 0 && c == '-' && value.n > 1) { | 132 | if (first == '0' && !is_delimiter(second)) { |
119 | continue; | 133 | if (second == 'x') { |
120 | } | 134 | // Hex constant. |
121 | if (!(c >= '0' && c <= '9')) { | 135 | scan_next(scanner); |
122 | is_fixnum = false; | 136 | n++; |
123 | break; | 137 | if (is_delimiter(scan_peek(scanner))) { |
138 | return 0; | ||
139 | } | ||
140 | while (!is_delimiter(scan_peek(scanner))) { | ||
141 | char c = scan_next(scanner); | ||
142 | if (!(c >= '0' && c <= '9') && | ||
143 | !(c >= 'a' && c <= 'f') && | ||
144 | !(c >= 'A' && c <= 'F')) { | ||
145 | return 0; | ||
146 | } | ||
147 | n++; | ||
148 | } | ||
149 | return n; | ||
150 | } else if (second == 'b') { | ||
151 | // Binary constant. | ||
152 | scan_next(scanner); | ||
153 | n++; | ||
154 | if (is_delimiter(scan_peek(scanner))) { | ||
155 | return 0; | ||
156 | } | ||
157 | while (!is_delimiter(scan_peek(scanner))) { | ||
158 | char c = scan_next(scanner); | ||
159 | if (!(c == '0' || c == '1')) { | ||
160 | return 0; | ||
161 | } | ||
162 | n++; | ||
163 | } | ||
124 | } | 164 | } |
125 | } | 165 | } |
126 | if (is_fixnum) { | 166 | |
127 | return TOKEN_FIXNUM; | 167 | // Decimal number or floating point. |
168 | bool has_dot = false; | ||
169 | while (!is_delimiter(scan_peek(scanner))) { | ||
170 | char c = scan_next(scanner); | ||
171 | if (c == '.') { | ||
172 | if (has_dot) { | ||
173 | return 0; | ||
174 | } | ||
175 | has_dot = true; | ||
176 | } else if (!(c >= '0' && c <= '9')) { | ||
177 | return 0; | ||
178 | } | ||
179 | n++; | ||
128 | } | 180 | } |
181 | return n; | ||
182 | } | ||
183 | |||
184 | TokenType | ||
185 | find_token_type(const StringView value) { | ||
129 | if (TOKEN_IS_KEYWORD(value, "nil")) { return TOKEN_NIL; } | 186 | if (TOKEN_IS_KEYWORD(value, "nil")) { return TOKEN_NIL; } |
130 | if (TOKEN_IS_KEYWORD(value, "true")) { return TOKEN_TRUE; } | 187 | if (TOKEN_IS_KEYWORD(value, "true")) { return TOKEN_TRUE; } |
131 | if (TOKEN_IS_KEYWORD(value, "false")) { return TOKEN_FALSE; } | 188 | if (TOKEN_IS_KEYWORD(value, "false")) { return TOKEN_FALSE; } |
@@ -134,12 +191,20 @@ find_primitive_type(const StringView value) { | |||
134 | if (TOKEN_IS_KEYWORD(value, "def")) { return TOKEN_DEF; } | 191 | if (TOKEN_IS_KEYWORD(value, "def")) { return TOKEN_DEF; } |
135 | if (TOKEN_IS_KEYWORD(value, "set!")) { return TOKEN_SET; } | 192 | if (TOKEN_IS_KEYWORD(value, "set!")) { return TOKEN_SET; } |
136 | if (TOKEN_IS_KEYWORD(value, "fun")) { return TOKEN_FUN; } | 193 | if (TOKEN_IS_KEYWORD(value, "fun")) { return TOKEN_FUN; } |
194 | if (TOKEN_IS_KEYWORD(value, "struct")) { return TOKEN_STRUCT; } | ||
137 | 195 | ||
138 | return TOKEN_SYMBOL; | 196 | return TOKEN_SYMBOL; |
139 | } | 197 | } |
140 | 198 | ||
199 | void | ||
200 | print_tokens(Token *tokens) { | ||
201 | for (size_t i = 0; i < array_size(tokens); i++) { | ||
202 | print_token(tokens[i]); | ||
203 | } | ||
204 | } | ||
205 | |||
141 | Token * | 206 | Token * |
142 | tokenize(const StringView *sv, Errors *errors) { | 207 | tokenize(const StringView *sv) { |
143 | Token *tokens = NULL; | 208 | Token *tokens = NULL; |
144 | array_init(tokens, 1); | 209 | array_init(tokens, 1); |
145 | Scanner scanner = (Scanner){ | 210 | Scanner scanner = (Scanner){ |
@@ -153,10 +218,16 @@ tokenize(const StringView *sv, Errors *errors) { | |||
153 | size_t line = scanner.line_number; | 218 | size_t line = scanner.line_number; |
154 | size_t col = scanner.col_number; | 219 | size_t col = scanner.col_number; |
155 | size_t offset = scanner.offset; | 220 | size_t offset = scanner.offset; |
221 | Token token = (Token){ | ||
222 | .type = TOKEN_UNKNOWN, | ||
223 | .line = line, | ||
224 | .col = col, | ||
225 | }; | ||
156 | char c = scan_next(&scanner); | 226 | char c = scan_next(&scanner); |
157 | switch (c) { | 227 | switch (c) { |
158 | case ';': { | 228 | case ';': { |
159 | while ((c = scan_next(&scanner)) != '\n' && c != '\0') {} | 229 | while ((c = scan_next(&scanner)) != '\n' && c != '\0') {} |
230 | continue; | ||
160 | } break; | 231 | } break; |
161 | case '"': { | 232 | case '"': { |
162 | char prev = c; | 233 | char prev = c; |
@@ -172,73 +243,66 @@ tokenize(const StringView *sv, Errors *errors) { | |||
172 | n++; | 243 | n++; |
173 | } | 244 | } |
174 | if (!found) { | 245 | if (!found) { |
175 | error_push(errors, (Error){ | 246 | push_error(ERR_TYPE_LEXER, ERR_UNMATCHED_STRING, line, col); |
176 | .type = ERR_TYPE_LEXER, | ||
177 | .value = ERR_UNMATCHED_STRING, | ||
178 | .line = line, | ||
179 | .col = col, | ||
180 | }); | ||
181 | return tokens; | 247 | return tokens; |
182 | } | 248 | } |
183 | Token token = (Token){ | 249 | token.value = (StringView){ |
184 | .value = (StringView){ | 250 | .start = &sv->start[offset + 1], |
185 | .start = &sv->start[offset + 1], | 251 | .n = n, |
186 | .n = n, | ||
187 | }, | ||
188 | .type = TOKEN_STRING, | ||
189 | .line = line, | ||
190 | .col = col, | ||
191 | }; | ||
192 | array_push(tokens, token); | ||
193 | } break; | ||
194 | case '(': { | ||
195 | if (scan_peek(&scanner) == ')') { | ||
196 | scan_next(&scanner); | ||
197 | Token token = (Token){ | ||
198 | .type = TOKEN_NIL, | ||
199 | .line = line, | ||
200 | .col = col, | ||
201 | }; | ||
202 | array_push(tokens, token); | ||
203 | } else { | ||
204 | Token token = (Token){ | ||
205 | .type = TOKEN_LPAREN, | ||
206 | .line = line, | ||
207 | .col = col, | ||
208 | }; | ||
209 | array_push(tokens, token); | ||
210 | } | ||
211 | } break; | ||
212 | case ')': { | ||
213 | Token token = (Token){ | ||
214 | .type = TOKEN_RPAREN, | ||
215 | .line = line, | ||
216 | .col = col, | ||
217 | }; | 252 | }; |
218 | array_push(tokens, token); | 253 | token.type = TOKEN_STRING; |
219 | } break; | 254 | } break; |
255 | case '(': { token.type = TOKEN_LPAREN; } break; | ||
256 | case ')': { token.type = TOKEN_RPAREN; } break; | ||
257 | case '[': { token.type = TOKEN_LSQUARE; } break; | ||
258 | case ']': { token.type = TOKEN_RSQUARE; } break; | ||
259 | case '{': { token.type = TOKEN_LCURLY; } break; | ||
260 | case '}': { token.type = TOKEN_RCURLY; } break; | ||
261 | case ':': { token.type = TOKEN_COLON; } break; | ||
262 | case '.': { token.type = TOKEN_DOT; } break; | ||
263 | case '@': { token.type = TOKEN_AT; } break; | ||
220 | default: { | 264 | default: { |
221 | size_t n = 1; | 265 | size_t n = 1; |
222 | while (!is_delimiter(scan_peek(&scanner))) { | 266 | if (c == '-' && !is_delimiter(scan_peek(&scanner))) { |
223 | scan_next(&scanner); | 267 | n += scan_number_token(&scanner); |
224 | n++; | 268 | token.value = (StringView){ |
225 | } | ||
226 | if (c == EOF || c == '\0') { | ||
227 | break; | ||
228 | } | ||
229 | Token token = (Token){ | ||
230 | .value = (StringView){ | ||
231 | .start = &sv->start[offset], | 269 | .start = &sv->start[offset], |
232 | .n = n, | 270 | .n = n, |
233 | }, | 271 | }; |
234 | .type = TOKEN_SYMBOL, | 272 | token.type = TOKEN_NUMBER; |
235 | .line = line, | 273 | } else if (c >= '0' && c <= '9') { |
236 | .col = col, | 274 | scan_rewind(&scanner); |
237 | }; | 275 | n = scan_number_token(&scanner); |
238 | token.type = find_primitive_type(token.value); | 276 | if (n == 0) { |
239 | array_push(tokens, token); | 277 | push_error(ERR_TYPE_LEXER, ERR_MALFORMED_NUMBER, line, col); |
278 | return tokens; | ||
279 | } | ||
280 | token.value = (StringView){ | ||
281 | .start = &sv->start[offset], | ||
282 | .n = n, | ||
283 | }; | ||
284 | token.type = TOKEN_NUMBER; | ||
285 | } else { | ||
286 | while (!is_delimiter(scan_peek(&scanner))) { | ||
287 | if (scan_peek(&scanner) == '.') { | ||
288 | break; | ||
289 | } | ||
290 | c = scan_next(&scanner); | ||
291 | n++; | ||
292 | } | ||
293 | token.value = (StringView){ | ||
294 | .start = &sv->start[offset], | ||
295 | .n = n, | ||
296 | }; | ||
297 | token.type = find_token_type(token.value); | ||
298 | } | ||
240 | } break; | 299 | } break; |
241 | } | 300 | } |
301 | if (token.type == TOKEN_UNKNOWN) { | ||
302 | push_error(ERR_TYPE_LEXER, ERR_UNKNOWN_TOK_TYPE, line, col); | ||
303 | return tokens; | ||
304 | } | ||
305 | array_push(tokens, token); | ||
242 | } | 306 | } |
243 | 307 | ||
244 | // Push EOF token. | 308 | // Push EOF token. |
diff --git a/src/lexer.h b/src/lexer.h index c477fbd..d864a1d 100644 --- a/src/lexer.h +++ b/src/lexer.h | |||
@@ -9,9 +9,13 @@ typedef enum TokenType { | |||
9 | // Parentheses. | 9 | // Parentheses. |
10 | TOKEN_LPAREN, | 10 | TOKEN_LPAREN, |
11 | TOKEN_RPAREN, | 11 | TOKEN_RPAREN, |
12 | TOKEN_LSQUARE, | ||
13 | TOKEN_RSQUARE, | ||
14 | TOKEN_LCURLY, | ||
15 | TOKEN_RCURLY, | ||
12 | 16 | ||
13 | // Primitive types. | 17 | // Primitive types. |
14 | TOKEN_FIXNUM, | 18 | TOKEN_NUMBER, |
15 | TOKEN_SYMBOL, | 19 | TOKEN_SYMBOL, |
16 | TOKEN_STRING, | 20 | TOKEN_STRING, |
17 | TOKEN_NIL, | 21 | TOKEN_NIL, |
@@ -24,6 +28,12 @@ typedef enum TokenType { | |||
24 | TOKEN_DEF, | 28 | TOKEN_DEF, |
25 | TOKEN_SET, | 29 | TOKEN_SET, |
26 | TOKEN_FUN, | 30 | TOKEN_FUN, |
31 | TOKEN_STRUCT, | ||
32 | |||
33 | // Special operators. | ||
34 | TOKEN_COLON, | ||
35 | TOKEN_DOT, | ||
36 | TOKEN_AT, | ||
27 | 37 | ||
28 | // End of file. | 38 | // End of file. |
29 | TOKEN_EOF, | 39 | TOKEN_EOF, |
@@ -46,8 +56,8 @@ typedef struct Scanner { | |||
46 | // Print a token to standard output for debugging purposes. | 56 | // Print a token to standard output for debugging purposes. |
47 | void print_token(Token tok); | 57 | void print_token(Token tok); |
48 | 58 | ||
49 | // Same functionality as the ScanView pairs, but keeping track of line and | 59 | // Same functionality as with StringView, but keeping track of line and column |
50 | // column numbers. | 60 | // numbers. |
51 | char scan_next(Scanner *scanner); | 61 | char scan_next(Scanner *scanner); |
52 | char scan_peek(const Scanner *scanner); | 62 | char scan_peek(const Scanner *scanner); |
53 | 63 | ||
@@ -61,9 +71,12 @@ void skip_whitespace(Scanner *scanner); | |||
61 | bool is_delimiter(char c); | 71 | bool is_delimiter(char c); |
62 | 72 | ||
63 | // Extract the token type from the current string. | 73 | // Extract the token type from the current string. |
64 | TokenType find_primitive_type(const StringView value); | 74 | TokenType find_token_type(const StringView value); |
65 | 75 | ||
66 | // Generate a list of tokens from the given string. | 76 | // Generate a list of tokens from the given string. |
67 | Token * tokenize(const StringView *sv, Errors *errors); | 77 | Token * tokenize(const StringView *sv); |
78 | |||
79 | // Display tokens from token list. | ||
80 | void print_tokens(Token *tokens); | ||
68 | 81 | ||
69 | #endif // BDL_LEXER_H | 82 | #endif // BDL_LEXER_H |
@@ -7,8 +7,8 @@ | |||
7 | #include "string_view.c" | 7 | #include "string_view.c" |
8 | #include "errors.c" | 8 | #include "errors.c" |
9 | #include "lexer.c" | 9 | #include "lexer.c" |
10 | #include "parser.c" | 10 | // #include "parser.c" |
11 | #include "ir.h" | 11 | // #include "ir.h" |
12 | // #include "compiler.h" | 12 | // #include "compiler.h" |
13 | 13 | ||
14 | void | 14 | void |
@@ -23,34 +23,32 @@ halt(void) { | |||
23 | 23 | ||
24 | void | 24 | void |
25 | process_source(const StringView *source, const char *file_name) { | 25 | process_source(const StringView *source, const char *file_name) { |
26 | Errors errors = {0}; | ||
27 | |||
28 | // Read tokens. | 26 | // Read tokens. |
29 | Token *tokens = tokenize(source, &errors); | 27 | Token *tokens = tokenize(source); |
30 | if (errors.n != 0) { | 28 | print_tokens(tokens); |
31 | report_errors(&errors, file_name); | 29 | check_errors(file_name); |
32 | array_free(tokens); | 30 | // if (errors.n != 0) { |
33 | exit(EXIT_FAILURE); | 31 | // exit(EXIT_FAILURE); |
34 | } | 32 | // } |
35 | 33 | ||
36 | // Parser. | 34 | // // Parser. |
37 | Program program = parse(tokens, &errors); | 35 | // Program program = parse(tokens, &errors); |
38 | if (errors.n != 0) { | 36 | // if (errors.n != 0) { |
39 | report_errors(&errors, file_name); | 37 | // report_errors(&errors, file_name); |
40 | free_objects(); | 38 | // free_objects(); |
41 | array_free(tokens); | 39 | // array_free(tokens); |
42 | exit(EXIT_FAILURE); | 40 | // exit(EXIT_FAILURE); |
43 | } | 41 | // } |
44 | array_free(tokens); | 42 | // array_free(tokens); |
45 | 43 | ||
46 | // TODO: Optimization. | 44 | // // TODO: Optimization. |
47 | 45 | ||
48 | // Compilation. | 46 | // // Compilation. |
49 | ProgramIr program_ir = compile(program); | 47 | // ProgramIr program_ir = compile(program); |
50 | (void)program_ir; | 48 | // (void)program_ir; |
51 | 49 | ||
52 | // Free resources. | 50 | // // Free resources. |
53 | free_objects(); | 51 | // free_objects(); |
54 | } | 52 | } |
55 | 53 | ||
56 | void | 54 | void |
diff --git a/src/string_view.c b/src/string_view.c index 8247bd4..4e9df5c 100644 --- a/src/string_view.c +++ b/src/string_view.c | |||
@@ -11,6 +11,15 @@ sv_next(StringView *sv) { | |||
11 | return c; | 11 | return c; |
12 | } | 12 | } |
13 | 13 | ||
14 | void | ||
15 | sv_rewind(StringView *sv) { | ||
16 | if (sv->start == 0) { | ||
17 | return; | ||
18 | } | ||
19 | sv->start--; | ||
20 | sv->n++; | ||
21 | } | ||
22 | |||
14 | char | 23 | char |
15 | sv_peek(const StringView *sv) { | 24 | sv_peek(const StringView *sv) { |
16 | if (sv->n == 0) { | 25 | if (sv->n == 0) { |
diff --git a/src/string_view.h b/src/string_view.h index 4dbbaaf..cb0f488 100644 --- a/src/string_view.h +++ b/src/string_view.h | |||
@@ -11,6 +11,9 @@ typedef struct StringView { | |||
11 | // Consume a character in the stream. | 11 | // Consume a character in the stream. |
12 | char sv_next(StringView *sv); | 12 | char sv_next(StringView *sv); |
13 | 13 | ||
14 | // Rewind a character in the stream. | ||
15 | void sv_rewind(StringView *sv); | ||
16 | |||
14 | // Check what is the current character in the stream. | 17 | // Check what is the current character in the stream. |
15 | char sv_peek(const StringView *sv); | 18 | char sv_peek(const StringView *sv); |
16 | 19 | ||
diff --git a/src/treewalk/darray.h b/src/treewalk/darray.h deleted file mode 100644 index db6234d..0000000 --- a/src/treewalk/darray.h +++ /dev/null | |||
@@ -1,78 +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 | // Insert N bytes from the SRC array into the ARR dynamic array. | ||
29 | #define array_insert(ARR, SRC, N) \ | ||
30 | ((ARR) = _array_insert(ARR, SRC, N, sizeof(*(ARR)))) | ||
31 | |||
32 | // Free the memory from the original allocated position. | ||
33 | #define array_free(ARR) ((ARR) ? free(array_head(ARR)), (ARR) = NULL : 0) | ||
34 | |||
35 | static inline void * | ||
36 | _array_reserve(size_t num_elem, size_t type_size) { | ||
37 | char *p = malloc(num_elem * type_size + sizeof(ArrayHeader)); | ||
38 | p += sizeof(ArrayHeader); | ||
39 | array_head(p)->size = 0; | ||
40 | array_head(p)->cap = num_elem; | ||
41 | return p; | ||
42 | } | ||
43 | |||
44 | static inline void * | ||
45 | _array_maybe_grow(void *arr, size_t type_size) { | ||
46 | ArrayHeader *head = array_head(arr); | ||
47 | if (head->cap == head->size) { | ||
48 | if (head->cap == 0) { | ||
49 | head->cap++; | ||
50 | } else { | ||
51 | head->cap *= 2; | ||
52 | } | ||
53 | head = realloc(head, head->cap * type_size + sizeof(ArrayHeader)); | ||
54 | } | ||
55 | arr = (char *)head + sizeof(ArrayHeader); | ||
56 | return arr; | ||
57 | } | ||
58 | |||
59 | static inline | ||
60 | char * _array_insert(char *arr, const char *src, size_t n_bytes, size_t type_size) { | ||
61 | ArrayHeader *head = array_head(arr); | ||
62 | size_t new_size = n_bytes + head->size; | ||
63 | if (new_size >= head->cap * type_size) { | ||
64 | if (head->cap == 0) { | ||
65 | head->cap = 1; | ||
66 | } | ||
67 | while (new_size >= head->cap * type_size) { | ||
68 | head->cap *= 2; | ||
69 | } | ||
70 | head = realloc(head, head->cap * type_size + sizeof(ArrayHeader)); | ||
71 | } | ||
72 | arr = (char *)head + sizeof(ArrayHeader); | ||
73 | memcpy((arr + head->size), src, n_bytes); | ||
74 | head->size = new_size; | ||
75 | return arr; | ||
76 | } | ||
77 | |||
78 | #endif // BDL_DARRAY_H | ||
diff --git a/src/treewalk/environment.c b/src/treewalk/environment.c deleted file mode 100644 index dd4a648..0000000 --- a/src/treewalk/environment.c +++ /dev/null | |||
@@ -1,72 +0,0 @@ | |||
1 | #include "environment.h" | ||
2 | #include "gc.h" | ||
3 | #include "errors.h" | ||
4 | |||
5 | Environment * | ||
6 | env_create(Environment *parent) { | ||
7 | Environment *env = alloc_env(); | ||
8 | env->parent = parent; | ||
9 | env->marked = false; | ||
10 | env->table = ht_init(); | ||
11 | return env; | ||
12 | } | ||
13 | |||
14 | void | ||
15 | env_add_symbol(Environment *env, Object *symbol, Object *value) { | ||
16 | if (symbol->type != OBJ_TYPE_SYMBOL) { | ||
17 | error_push((Error){ | ||
18 | .type = ERR_TYPE_RUNTIME, | ||
19 | .value = ERR_NOT_A_SYMBOL, | ||
20 | .line = 0, | ||
21 | .col = 0, | ||
22 | }); | ||
23 | return; | ||
24 | } | ||
25 | ht_insert(env->table, symbol, value); | ||
26 | } | ||
27 | |||
28 | Object * | ||
29 | env_lookup(Environment *env, Object *symbol) { | ||
30 | while (env != NULL) { | ||
31 | Object *obj = ht_lookup(env->table, symbol); | ||
32 | if (obj != NULL) { | ||
33 | return obj; | ||
34 | } | ||
35 | env = env->parent; | ||
36 | } | ||
37 | return obj_err; | ||
38 | } | ||
39 | |||
40 | Object * | ||
41 | env_update(Environment *env, Object *symbol, Object *value) { | ||
42 | while (env != NULL) { | ||
43 | Object *obj = ht_lookup(env->table, symbol); | ||
44 | if (obj != NULL) { | ||
45 | ht_insert(env->table, symbol, value); | ||
46 | return obj_nil; | ||
47 | } | ||
48 | env = env->parent; | ||
49 | } | ||
50 | error_push((Error){ | ||
51 | .type = ERR_TYPE_RUNTIME, | ||
52 | .value = ERR_SYMBOL_NOT_FOUND, | ||
53 | }); | ||
54 | return obj_err; | ||
55 | } | ||
56 | |||
57 | void | ||
58 | env_add_or_update_current(Environment *env, Object *symbol, Object *value) { | ||
59 | ht_insert(env->table, symbol, value); | ||
60 | } | ||
61 | |||
62 | Environment * | ||
63 | env_extend(Environment *parent, Environment *extra) { | ||
64 | Environment *env = parent; | ||
65 | HashTablePair *pairs = extra->table->pairs; | ||
66 | for (size_t i = 0; i < array_cap(pairs); i++) { | ||
67 | if (pairs[i].key != NULL) { | ||
68 | ht_insert(env->table, pairs[i].key, pairs[i].value); | ||
69 | } | ||
70 | } | ||
71 | return env; | ||
72 | } | ||
diff --git a/src/treewalk/environment.h b/src/treewalk/environment.h deleted file mode 100644 index 5ee21ad..0000000 --- a/src/treewalk/environment.h +++ /dev/null | |||
@@ -1,27 +0,0 @@ | |||
1 | #ifndef BDL_ENVIRONMENT_H | ||
2 | #define BDL_ENVIRONMENT_H | ||
3 | |||
4 | #include "objects.h" | ||
5 | |||
6 | typedef struct Environment { | ||
7 | struct Environment *parent; | ||
8 | HashTable *table; | ||
9 | bool marked; | ||
10 | } Environment; | ||
11 | |||
12 | Environment * env_create(Environment *parent); | ||
13 | void env_add_symbol(Environment *env, Object *symbol, Object *value); | ||
14 | Object * env_lookup(Environment *env, Object *symbol); | ||
15 | Object * env_update(Environment *env, Object *symbol, Object *value); | ||
16 | ssize_t env_index_current(Environment *env, Object *symbol); | ||
17 | void env_add_or_update_current(Environment *env, Object *symbol, Object *value); | ||
18 | Environment * env_extend(Environment *parent, Environment *extra); | ||
19 | |||
20 | #define MAKE_ENV_VAR(ENV,STR,VAR) \ | ||
21 | (env_add_symbol((ENV), MAKE_SYM(STR), (VAR))) | ||
22 | #define MAKE_ENV_PROC(ENV,STR,FUN) \ | ||
23 | (env_add_symbol((ENV), MAKE_SYM(STR), make_procedure(FUN))) | ||
24 | |||
25 | #define ENV_BUF_CAP 8 | ||
26 | |||
27 | #endif // BDL_ENVIRONMENT_H | ||
diff --git a/src/treewalk/errors.c b/src/treewalk/errors.c deleted file mode 100644 index d957cfa..0000000 --- a/src/treewalk/errors.c +++ /dev/null | |||
@@ -1,29 +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 | }; | ||
19 | |||
20 | static Error errors[ERR_MAX_NUMBER]; | ||
21 | static size_t errors_n = 0; | ||
22 | static bool supress_errors = false; | ||
23 | |||
24 | void | ||
25 | error_push(Error error) { | ||
26 | if (errors_n < ERR_MAX_NUMBER) { | ||
27 | errors[errors_n++] = error; | ||
28 | } | ||
29 | } | ||
diff --git a/src/treewalk/errors.h b/src/treewalk/errors.h deleted file mode 100644 index 7916f4a..0000000 --- a/src/treewalk/errors.h +++ /dev/null | |||
@@ -1,38 +0,0 @@ | |||
1 | #ifndef BDL_ERRORS_H | ||
2 | #define BDL_ERRORS_H | ||
3 | |||
4 | typedef enum ErrorType { | ||
5 | ERR_TYPE_LEXER, | ||
6 | ERR_TYPE_PARSER, | ||
7 | ERR_TYPE_RUNTIME, | ||
8 | } ErrorType; | ||
9 | |||
10 | typedef enum ErrorValue { | ||
11 | ERR_UNKNOWN = 0, | ||
12 | ERR_UNMATCHED_STRING, | ||
13 | ERR_UNBALANCED_PAREN, | ||
14 | ERR_NOT_IMPLEMENTED, | ||
15 | ERR_EOF_REACHED, | ||
16 | ERR_UNKNOWN_TOKEN, | ||
17 | ERR_UNKNOWN_OBJ_TYPE, | ||
18 | ERR_NOT_A_SYMBOL, | ||
19 | ERR_SYMBOL_NOT_FOUND, | ||
20 | ERR_OBJ_NOT_CALLABLE, | ||
21 | ERR_NOT_ENOUGH_ARGS, | ||
22 | ERR_TOO_MANY_ARGS, | ||
23 | ERR_WRONG_ARG_TYPE, | ||
24 | ERR_DIVISION_BY_ZERO, | ||
25 | } ErrorValue; | ||
26 | |||
27 | typedef struct Error { | ||
28 | ErrorType type; | ||
29 | ErrorValue value; | ||
30 | size_t line; | ||
31 | size_t col; | ||
32 | } Error; | ||
33 | |||
34 | void error_push(Error error); | ||
35 | |||
36 | #define ERR_MAX_NUMBER 16 | ||
37 | |||
38 | #endif // BDL_ERRORS_H | ||
diff --git a/src/treewalk/gc.c b/src/treewalk/gc.c deleted file mode 100644 index 358a07e..0000000 --- a/src/treewalk/gc.c +++ /dev/null | |||
@@ -1,199 +0,0 @@ | |||
1 | #include "gc.h" | ||
2 | |||
3 | Environment * | ||
4 | alloc_env(void) { | ||
5 | if (array_size(gc.free_envs.offsets) == 0) { | ||
6 | mark_and_sweep(); | ||
7 | if (array_size(gc.free_envs.offsets) == 0) { | ||
8 | fprintf(stderr, "NO MORE ENV MEMORY AVAILABLE WHERE IS YOUR GOD NOW MWAHAHA\n"); | ||
9 | dump_gc(); | ||
10 | exit(EXIT_FAILURE); | ||
11 | // TODO: grow heap tables. | ||
12 | } | ||
13 | } | ||
14 | size_t slot = gc.free_envs.offsets[gc.free_envs.position++]; | ||
15 | array_head(gc.free_envs.offsets)->size--; | ||
16 | return &gc.envs[slot]; | ||
17 | } | ||
18 | |||
19 | void | ||
20 | push_root(Object *obj) { | ||
21 | array_push(gc.roots, obj); | ||
22 | } | ||
23 | |||
24 | Object * | ||
25 | pop_root(void) { | ||
26 | return array_pop(gc.roots); | ||
27 | } | ||
28 | |||
29 | void | ||
30 | push_active_env(Environment *env) { | ||
31 | array_push(gc.active_envs, env); | ||
32 | } | ||
33 | |||
34 | Environment * | ||
35 | pop_active_env(void) { | ||
36 | return array_pop(gc.active_envs); | ||
37 | } | ||
38 | |||
39 | void | ||
40 | gc_init(void) { | ||
41 | gc = (GC){0}; | ||
42 | |||
43 | array_init(gc.objects, GC_OBJS_CAP); | ||
44 | array_init(gc.roots, GC_ROOTS_CAP); | ||
45 | array_init(gc.active_envs, GC_ACTIVE_ENVS_CAP); | ||
46 | array_init(gc.envs, GC_ENVS_CAP); | ||
47 | array_init(gc.free_objects.offsets, GC_OBJS_CAP); | ||
48 | array_init(gc.free_envs.offsets, GC_ENVS_CAP); | ||
49 | |||
50 | // The free list stores the offset from the initial position for all | ||
51 | // available slots. | ||
52 | for (size_t i = 0; i < GC_OBJS_CAP; i++) { | ||
53 | array_push(gc.free_objects.offsets, i); | ||
54 | } | ||
55 | for (size_t i = 0; i < GC_ENVS_CAP; i++) { | ||
56 | array_push(gc.free_envs.offsets, i); | ||
57 | } | ||
58 | } | ||
59 | |||
60 | void | ||
61 | mark_environment(Environment *env) { | ||
62 | if (env == NULL || env->marked) { | ||
63 | return; | ||
64 | } | ||
65 | env->marked = true; | ||
66 | HashTablePair *pairs = env->table->pairs; | ||
67 | for (size_t i = 0; i < array_cap(pairs); i++) { | ||
68 | if (pairs[i].key != NULL) { | ||
69 | mark_obj(pairs[i].key); | ||
70 | mark_obj(pairs[i].value); | ||
71 | } | ||
72 | } | ||
73 | } | ||
74 | |||
75 | void | ||
76 | mark_obj(Object *obj) { | ||
77 | if (obj->marked) { | ||
78 | return; | ||
79 | } | ||
80 | obj->marked = true; | ||
81 | if (obj->type == OBJ_TYPE_PAIR) { | ||
82 | mark_obj(obj->car); | ||
83 | mark_obj(obj->cdr); | ||
84 | } | ||
85 | if (obj->type == OBJ_TYPE_LAMBDA) { | ||
86 | mark_obj(obj->params); | ||
87 | mark_obj(obj->body); | ||
88 | mark_environment(obj->env); | ||
89 | } | ||
90 | } | ||
91 | |||
92 | void | ||
93 | mark_and_sweep(void) { | ||
94 | // Mark. | ||
95 | for (size_t i = 0; i < array_size(gc.active_envs); i++) { | ||
96 | mark_environment(gc.active_envs[i]); | ||
97 | } | ||
98 | for (size_t i = 0; i < array_size(gc.roots); i++) { | ||
99 | mark_obj(gc.roots[i]); | ||
100 | } | ||
101 | |||
102 | // Reset the free list. | ||
103 | gc.free_objects.position = 0; | ||
104 | array_head(gc.free_objects.offsets)->size = 0; | ||
105 | gc.free_envs.position = 0; | ||
106 | array_head(gc.free_envs.offsets)->size = 0; | ||
107 | |||
108 | // Sweep. | ||
109 | for (size_t i = 0; i < array_cap(gc.objects); i++) { | ||
110 | Object *obj = &gc.objects[i]; | ||
111 | if (!obj->marked) { | ||
112 | // Free heap allocated memory for this object if needed. | ||
113 | if (obj->type == OBJ_TYPE_SYMBOL) { | ||
114 | array_free(obj->symbol); | ||
115 | } else if (obj->type == OBJ_TYPE_STRING) { | ||
116 | array_free(obj->string); | ||
117 | } | ||
118 | gc.free_objects.offsets[array_head(gc.free_objects.offsets)->size++] = i; | ||
119 | } | ||
120 | obj->marked = false; | ||
121 | } | ||
122 | for (size_t i = 0; i < array_cap(gc.envs); i++) { | ||
123 | Environment *env = &gc.envs[i]; | ||
124 | if (!env->marked) { | ||
125 | ht_free(env->table); | ||
126 | gc.free_envs.offsets[array_head(gc.free_envs.offsets)->size++] = i; | ||
127 | } | ||
128 | env->marked = false; | ||
129 | } | ||
130 | } | ||
131 | |||
132 | void | ||
133 | dump_gc(void) { | ||
134 | printf("-------------- ROOTS -------------- \n"); | ||
135 | for (size_t i = 0; i < array_size(gc.roots); i++) { | ||
136 | display(gc.roots[i]); | ||
137 | printf("\n"); | ||
138 | } | ||
139 | printf("--------- OBJECTS (TOP 20) -------- \n"); | ||
140 | for (size_t i = 0; i < 20; i++) { | ||
141 | printf("i: %ld -> ", i); | ||
142 | Object *obj = &gc.objects[i]; | ||
143 | display(obj); | ||
144 | bool is_free = false; | ||
145 | for (size_t j = 0; j < array_cap(gc.objects); j++) { | ||
146 | if (gc.free_objects.offsets[j] == i) { | ||
147 | is_free = true; | ||
148 | break; | ||
149 | } | ||
150 | } | ||
151 | if (is_free) { | ||
152 | printf(" [FREE]"); | ||
153 | } | ||
154 | printf("\n"); | ||
155 | } | ||
156 | printf("-------------- MISC --------------- \n"); | ||
157 | printf("gc.roots.size: %ld\n", array_size(gc.roots)); | ||
158 | printf("gc.roots.cap: %ld\n", array_size(gc.roots)); | ||
159 | printf("gc.active_envs.size: %ld\n", array_size(gc.active_envs)); | ||
160 | printf("gc.active_envs.cap: %ld\n", array_cap(gc.active_envs)); | ||
161 | printf("gc.obj_cap: %ld\n", array_cap(gc.objects)); | ||
162 | printf("gc.free_objects.size: %ld\n", array_size(gc.free_objects.offsets)); | ||
163 | printf("gc.free_objects.cap: %ld\n", array_cap(gc.free_objects.offsets)); | ||
164 | printf("gc.free_objects.position: %ld\n", gc.free_objects.position); | ||
165 | printf("array_size(gc.free_envs.offsets): %ld\n", array_size(gc.free_envs.offsets)); | ||
166 | printf("gc.free_envs.cap: %ld\n", array_cap(gc.free_envs.offsets)); | ||
167 | printf("gc.free_envs.position: %ld\n", gc.free_envs.position); | ||
168 | printf("gc.envs.size: %ld\n", array_size(gc.envs)); | ||
169 | printf("gc.envs.cap: %ld\n", array_cap(gc.envs)); | ||
170 | } | ||
171 | |||
172 | Object * | ||
173 | alloc_object(ObjectType type) { | ||
174 | if (array_head(gc.free_objects.offsets)->size == 0) { | ||
175 | mark_and_sweep(); | ||
176 | if (array_head(gc.free_objects.offsets)->size == 0) { | ||
177 | fprintf(stderr, "NO MORE OBJ MEMORY AVAILABLE WHERE IS YOUR GOD NOW MWAHAHA\n"); | ||
178 | dump_gc(); | ||
179 | exit(EXIT_FAILURE); | ||
180 | // TODO: grow heap tables. | ||
181 | // NOTE: When growing the tables, we WILL lose the pointer | ||
182 | // references! Should we work with offsets all the way? That is for | ||
183 | // cdr and car? Should we have a utility function? All in all, we | ||
184 | // need to refactor the codebase first to work with pointer offsets | ||
185 | // rather than objects. This issue is very important, if we are in | ||
186 | // the middle of an operation that tries to allocate memory but we | ||
187 | // had saved pointers to some object, the pointer references may be | ||
188 | // invalidated, crashing or worse, silently returning garbage! Let's | ||
189 | // move on for now implementing the GC and we will revisit this part | ||
190 | // later. | ||
191 | } | ||
192 | } | ||
193 | size_t slot = gc.free_objects.offsets[gc.free_objects.position++]; | ||
194 | array_head(gc.free_objects.offsets)->size--; | ||
195 | Object *obj = &gc.objects[slot]; | ||
196 | obj->type = type; | ||
197 | obj->marked = false; | ||
198 | return obj; | ||
199 | } | ||
diff --git a/src/treewalk/gc.h b/src/treewalk/gc.h deleted file mode 100644 index 9ad1615..0000000 --- a/src/treewalk/gc.h +++ /dev/null | |||
@@ -1,46 +0,0 @@ | |||
1 | #ifndef BDL_GC_H | ||
2 | #define BDL_GC_H | ||
3 | |||
4 | #include "objects.h" | ||
5 | #include "environment.h" | ||
6 | |||
7 | typedef struct FreeList { | ||
8 | size_t *offsets; | ||
9 | size_t position; | ||
10 | } FreeList; | ||
11 | |||
12 | typedef struct GC { | ||
13 | Object **roots; | ||
14 | Environment *envs; | ||
15 | Object *objects; | ||
16 | FreeList free_objects; | ||
17 | FreeList free_envs; | ||
18 | Environment **active_envs; | ||
19 | } GC; | ||
20 | |||
21 | void gc_init(void); | ||
22 | |||
23 | // Allocation functions for objects and environments. | ||
24 | Object * alloc_object(ObjectType type); | ||
25 | Environment * alloc_env(void); | ||
26 | |||
27 | // Root and environment protector functions. | ||
28 | void push_root(Object *obj); | ||
29 | Object * pop_root(void); | ||
30 | void push_active_env(Environment *env); | ||
31 | Environment * pop_active_env(void); | ||
32 | |||
33 | // Mark and sweep algorithm functions. | ||
34 | void mark_environment(Environment *env); | ||
35 | void mark_obj(Object *obj); | ||
36 | void mark_and_sweep(void); | ||
37 | |||
38 | // Debugging function to print out the contentes of some GC fields. | ||
39 | void dump_gc(void); | ||
40 | |||
41 | #define GC_OBJS_CAP 1024 * 1024 | ||
42 | #define GC_ROOTS_CAP 1024 | ||
43 | #define GC_ACTIVE_ENVS_CAP 2 | ||
44 | #define GC_ENVS_CAP 1024 * 4 | ||
45 | |||
46 | #endif // BDL_GC_H | ||
diff --git a/src/treewalk/hashtable.h b/src/treewalk/hashtable.h deleted file mode 100644 index 8f210e3..0000000 --- a/src/treewalk/hashtable.h +++ /dev/null | |||
@@ -1,191 +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 | typedef struct HashTable { | ||
20 | // All available key-value pairs as a dynamic array. | ||
21 | HashTablePair *pairs; | ||
22 | |||
23 | // This table expects the number of buckets to grow in powers of two. To | ||
24 | // speedup the default hashing, we memoize the number of bits equivalent to | ||
25 | // that power of 2: | ||
26 | // | ||
27 | // cap := 1024 = 2 ^ 10, shift_amount := 10 | ||
28 | // | ||
29 | uint8_t shift_amount; | ||
30 | } HashTable; | ||
31 | |||
32 | // Hash a byte stream using a circular shift + XOR hash function. | ||
33 | static inline uint64_t | ||
34 | _xor_shift_hash(const char *key, size_t n) { | ||
35 | uint64_t hash = 0x65d9d65f6a19574f; | ||
36 | char *last = (char *)key + n; | ||
37 | while (key != last) { | ||
38 | hash ^= (uint64_t)*key++; | ||
39 | hash = (hash << 8) | (hash >> (64 - 8)); | ||
40 | } | ||
41 | return hash; | ||
42 | } | ||
43 | |||
44 | // Use Fibonacci hashing to map a hash to a value in range of the table. | ||
45 | static inline uint64_t | ||
46 | _fibonacci_hash(uint64_t hash, size_t shift_amount) { | ||
47 | return (hash * UINT64_C(11400714819323198485)) >> (64 - shift_amount); | ||
48 | } | ||
49 | |||
50 | uint64_t | ||
51 | ht_hash(const HashTable *table, const Object *key) { | ||
52 | uint64_t hash = 0; | ||
53 | switch (key->type) { | ||
54 | case OBJ_TYPE_FIXNUM: { | ||
55 | hash = key->fixnum; | ||
56 | } break; | ||
57 | case OBJ_TYPE_STRING: { | ||
58 | hash = _xor_shift_hash(key->string, array_size(key->string)); | ||
59 | } break; | ||
60 | case OBJ_TYPE_SYMBOL: { | ||
61 | hash = _xor_shift_hash(key->symbol, array_size(key->symbol)); | ||
62 | } break; | ||
63 | case OBJ_TYPE_BOOL: | ||
64 | case OBJ_TYPE_NIL: | ||
65 | case OBJ_TYPE_PAIR: | ||
66 | case OBJ_TYPE_LAMBDA: | ||
67 | case OBJ_TYPE_PROCEDURE: | ||
68 | case OBJ_TYPE_ERR: { | ||
69 | hash = (uintptr_t)key; | ||
70 | } break; | ||
71 | } | ||
72 | hash = _fibonacci_hash(hash, table->shift_amount); | ||
73 | return hash; | ||
74 | } | ||
75 | |||
76 | static inline float | ||
77 | ht_load_factor(const HashTable *table) { | ||
78 | return (float)array_size(table->pairs) / (float)array_cap(table->pairs); | ||
79 | } | ||
80 | |||
81 | HashTable * | ||
82 | ht_init(void) { | ||
83 | HashTable *table = malloc(sizeof(HashTable)); | ||
84 | table->pairs = NULL; | ||
85 | array_init(table->pairs, HT_MIN_CAP); | ||
86 | for (size_t i = 0; i < array_cap(table->pairs); i++) { | ||
87 | table->pairs[i] = (HashTablePair){NULL, NULL}; | ||
88 | } | ||
89 | table->shift_amount = HT_MIN_SHIFT; | ||
90 | return table; | ||
91 | } | ||
92 | |||
93 | void | ||
94 | _ht_insert(HashTable *table, const Object *key, const Object *value) { | ||
95 | size_t position = ht_hash(table, key); | ||
96 | size_t probe_position = position; | ||
97 | |||
98 | // Verify the key in that position is free. If not, use linear probing to | ||
99 | // find the next free slot. | ||
100 | HashTablePair *pairs = table->pairs; | ||
101 | while (true) { | ||
102 | if (pairs[probe_position].key == NULL) { | ||
103 | array_head(pairs)->size++; | ||
104 | break; | ||
105 | } | ||
106 | if (obj_eq(pairs[probe_position].key, key)) { | ||
107 | break; | ||
108 | } | ||
109 | if (probe_position == array_cap(pairs) - 1) { | ||
110 | probe_position = 0; | ||
111 | } else { | ||
112 | probe_position++; | ||
113 | } | ||
114 | } | ||
115 | pairs[probe_position].key = (Object *)key; | ||
116 | pairs[probe_position].value = (Object *)value; | ||
117 | } | ||
118 | |||
119 | void | ||
120 | _ht_maybe_grow(HashTable *table) { | ||
121 | HashTablePair *pairs = table->pairs; | ||
122 | if (ht_load_factor(table) < HT_LOAD_THRESHOLD) { | ||
123 | return; | ||
124 | } | ||
125 | |||
126 | // Create a new array with 2x capacity. | ||
127 | table->pairs = NULL; | ||
128 | array_init(table->pairs, array_cap(pairs) * 2); | ||
129 | for (size_t i = 0; i < array_cap(table->pairs); i++) { | ||
130 | table->pairs[i] = (HashTablePair){NULL, NULL}; | ||
131 | } | ||
132 | table->shift_amount++; | ||
133 | |||
134 | // Hash everything in the table for the new array capacity. | ||
135 | for (size_t i = 0; i < array_cap(pairs); i++) { | ||
136 | if (pairs[i].key != NULL) { | ||
137 | _ht_insert(table, pairs[i].key, pairs[i].value); | ||
138 | } | ||
139 | } | ||
140 | |||
141 | // Free the old array. | ||
142 | array_free(pairs); | ||
143 | } | ||
144 | |||
145 | void | ||
146 | ht_insert(HashTable *table, const Object *key, const Object *value) { | ||
147 | _ht_maybe_grow(table); | ||
148 | _ht_insert(table, key, value); | ||
149 | return; | ||
150 | } | ||
151 | |||
152 | Object * | ||
153 | ht_lookup(const HashTable *table, const Object *key) { | ||
154 | size_t position = ht_hash(table, key); | ||
155 | size_t probe_position = position; | ||
156 | |||
157 | // Verify the key in that position is the same. If not perform linear | ||
158 | // probing to find it. | ||
159 | HashTablePair *pairs = table->pairs; | ||
160 | while (true) { | ||
161 | if (pairs[probe_position].key == NULL) { | ||
162 | return NULL; | ||
163 | } | ||
164 | if (obj_eq(pairs[probe_position].key, key)) { | ||
165 | break; | ||
166 | } | ||
167 | if (probe_position == array_cap(pairs) - 1) { | ||
168 | probe_position = 0; | ||
169 | } else { | ||
170 | probe_position++; | ||
171 | } | ||
172 | if (probe_position == position) { | ||
173 | return NULL; | ||
174 | } | ||
175 | } | ||
176 | return pairs[probe_position].value; | ||
177 | } | ||
178 | |||
179 | void | ||
180 | ht_free(HashTable *table) { | ||
181 | if (table == NULL) { | ||
182 | return; | ||
183 | } | ||
184 | if (table->pairs == NULL) { | ||
185 | return; | ||
186 | } | ||
187 | array_free(table->pairs); | ||
188 | free(table); | ||
189 | } | ||
190 | |||
191 | #endif // BDL_HASHTABLE_H | ||
diff --git a/src/treewalk/lexer.c b/src/treewalk/lexer.c deleted file mode 100644 index 38ca37c..0000000 --- a/src/treewalk/lexer.c +++ /dev/null | |||
@@ -1,257 +0,0 @@ | |||
1 | #include "lexer.h" | ||
2 | |||
3 | void | ||
4 | print_token(Token tok) { | ||
5 | printf("LINE: %3ld COL: %3ld ", tok.line, tok.column); | ||
6 | switch (tok.type) { | ||
7 | case TOKEN_LPAREN: { | ||
8 | printf("TOKEN_LPAREN"); | ||
9 | } break; | ||
10 | case TOKEN_RPAREN: { | ||
11 | printf("TOKEN_RPAREN"); | ||
12 | } break; | ||
13 | case TOKEN_QUOTE: { | ||
14 | printf("TOKEN_QUOTE"); | ||
15 | } break; | ||
16 | case TOKEN_TRUE: { | ||
17 | printf("TOKEN_TRUE"); | ||
18 | } break; | ||
19 | case TOKEN_FALSE: { | ||
20 | printf("TOKEN_FALSE"); | ||
21 | } break; | ||
22 | case TOKEN_NIL: { | ||
23 | printf("TOKEN_NIL"); | ||
24 | } break; | ||
25 | case TOKEN_FIXNUM: { | ||
26 | printf("TOKEN_FIXNUM -> "); | ||
27 | sv_write(&tok.value, stdout); | ||
28 | } break; | ||
29 | case TOKEN_SYMBOL: { | ||
30 | printf("TOKEN_SYMBOL -> "); | ||
31 | sv_write(&tok.value, stdout); | ||
32 | } break; | ||
33 | case TOKEN_STRING: { | ||
34 | printf("TOKEN_STRING -> "); | ||
35 | sv_write(&tok.value, stdout); | ||
36 | } break; | ||
37 | case TOKEN_EOF: { | ||
38 | printf("TOKEN_EOF"); | ||
39 | } break; | ||
40 | case TOKEN_UNKNOWN: { | ||
41 | printf("TOKEN_UNKNOWN"); | ||
42 | } break; | ||
43 | } | ||
44 | printf("\n"); | ||
45 | } | ||
46 | |||
47 | char | ||
48 | scan_next(Scanner *scanner) { | ||
49 | char c = sv_next(&scanner->current); | ||
50 | if (c == '\n') { | ||
51 | scanner->line_number++; | ||
52 | scanner->col_number = 1; | ||
53 | } else { | ||
54 | scanner->col_number++; | ||
55 | } | ||
56 | scanner->offset++; | ||
57 | return c; | ||
58 | } | ||
59 | |||
60 | char | ||
61 | scan_peek(const Scanner *scanner) { | ||
62 | return sv_peek(&scanner->current); | ||
63 | } | ||
64 | |||
65 | bool | ||
66 | scan_has_next(const Scanner *scanner) { | ||
67 | return scanner->current.n != 0; | ||
68 | } | ||
69 | |||
70 | void | ||
71 | skip_whitespace(Scanner *scanner) { | ||
72 | while (scan_has_next(scanner)) { | ||
73 | char c = scan_peek(scanner); | ||
74 | switch (c) { | ||
75 | case ' ': | ||
76 | case '\f': | ||
77 | case '\n': | ||
78 | case '\r': | ||
79 | case '\t': | ||
80 | case '\v': { | ||
81 | scan_next(scanner); | ||
82 | } break; | ||
83 | default: { | ||
84 | return; | ||
85 | } break; | ||
86 | } | ||
87 | } | ||
88 | } | ||
89 | |||
90 | bool | ||
91 | is_delimiter(char c) { | ||
92 | switch (c) { | ||
93 | case EOF: | ||
94 | case '\0': | ||
95 | case ';': | ||
96 | case '"': | ||
97 | case '\'': | ||
98 | case '(': | ||
99 | case ')': | ||
100 | case ' ': | ||
101 | case '\f': | ||
102 | case '\n': | ||
103 | case '\r': | ||
104 | case '\t': | ||
105 | case '\v': { | ||
106 | return true; | ||
107 | } break; | ||
108 | } | ||
109 | return false; | ||
110 | } | ||
111 | |||
112 | TokenType | ||
113 | find_primitive_type(const StringView value) { | ||
114 | bool is_fixnum = true; | ||
115 | for (size_t i = 0; i < value.n; i++) { | ||
116 | char c = value.start[i]; | ||
117 | if (i == 0 && c == '-' && value.n > 1) { | ||
118 | continue; | ||
119 | } | ||
120 | if (!(c >= '0' && c <= '9')) { | ||
121 | is_fixnum = false; | ||
122 | break; | ||
123 | } | ||
124 | } | ||
125 | if (is_fixnum) { | ||
126 | return TOKEN_FIXNUM; | ||
127 | } | ||
128 | if (sv_equal(&value, &(StringView){"true", 4})) { | ||
129 | return TOKEN_TRUE; | ||
130 | } | ||
131 | if (sv_equal(&value, &(StringView){"false", 5})) { | ||
132 | return TOKEN_FALSE; | ||
133 | } | ||
134 | return TOKEN_SYMBOL; | ||
135 | } | ||
136 | |||
137 | Token * | ||
138 | tokenize(const StringView *sv) { | ||
139 | Token *tokens = NULL; | ||
140 | array_init(tokens, 1); | ||
141 | Scanner scanner = (Scanner){ | ||
142 | .current = *sv, | ||
143 | .line_number = 1, | ||
144 | .col_number = 1, | ||
145 | }; | ||
146 | |||
147 | while (scan_has_next(&scanner)) { | ||
148 | skip_whitespace(&scanner); | ||
149 | size_t line = scanner.line_number; | ||
150 | size_t col = scanner.col_number; | ||
151 | size_t offset = scanner.offset; | ||
152 | char c = scan_next(&scanner); | ||
153 | switch (c) { | ||
154 | case ';': { | ||
155 | while ((c = scan_next(&scanner)) != '\n' && c != '\0') {} | ||
156 | } break; | ||
157 | case '"': { | ||
158 | char prev = c; | ||
159 | bool found = false; | ||
160 | size_t n = 0; | ||
161 | while (scan_has_next(&scanner)) { | ||
162 | c = scan_next(&scanner); | ||
163 | if (c == '"' && prev != '\\') { | ||
164 | found = true; | ||
165 | break; | ||
166 | } | ||
167 | prev = c; | ||
168 | n++; | ||
169 | } | ||
170 | if (!found) { | ||
171 | error_push((Error){ | ||
172 | .type = ERR_TYPE_LEXER, | ||
173 | .value = ERR_UNMATCHED_STRING, | ||
174 | .line = line, | ||
175 | .col = col, | ||
176 | }); | ||
177 | return tokens; | ||
178 | } | ||
179 | Token token = (Token){ | ||
180 | .value = (StringView){ | ||
181 | .start = &sv->start[offset + 1], | ||
182 | .n = n, | ||
183 | }, | ||
184 | .type = TOKEN_STRING, | ||
185 | .line = line, | ||
186 | .column = col, | ||
187 | }; | ||
188 | array_push(tokens, token); | ||
189 | } break; | ||
190 | case '\'': { | ||
191 | Token token = (Token){ | ||
192 | .type = TOKEN_QUOTE, | ||
193 | .line = line, | ||
194 | .column = col, | ||
195 | }; | ||
196 | array_push(tokens, token); | ||
197 | } break; | ||
198 | case '(': { | ||
199 | if (scan_peek(&scanner) == ')') { | ||
200 | scan_next(&scanner); | ||
201 | Token token = (Token){ | ||
202 | .type = TOKEN_NIL, | ||
203 | .line = line, | ||
204 | .column = col, | ||
205 | }; | ||
206 | array_push(tokens, token); | ||
207 | } else { | ||
208 | Token token = (Token){ | ||
209 | .type = TOKEN_LPAREN, | ||
210 | .line = line, | ||
211 | .column = col, | ||
212 | }; | ||
213 | array_push(tokens, token); | ||
214 | } | ||
215 | } break; | ||
216 | case ')': { | ||
217 | Token token = (Token){ | ||
218 | .type = TOKEN_RPAREN, | ||
219 | .line = line, | ||
220 | .column = col, | ||
221 | }; | ||
222 | array_push(tokens, token); | ||
223 | } break; | ||
224 | default: { | ||
225 | size_t n = 1; | ||
226 | while (!is_delimiter(scan_peek(&scanner))) { | ||
227 | scan_next(&scanner); | ||
228 | n++; | ||
229 | } | ||
230 | if (c == EOF || c == '\0') { | ||
231 | break; | ||
232 | } | ||
233 | Token token = (Token){ | ||
234 | .value = (StringView){ | ||
235 | .start = &sv->start[offset], | ||
236 | .n = n, | ||
237 | }, | ||
238 | .type = TOKEN_SYMBOL, | ||
239 | .line = line, | ||
240 | .column = col, | ||
241 | }; | ||
242 | token.type = find_primitive_type(token.value); | ||
243 | array_push(tokens, token); | ||
244 | } break; | ||
245 | } | ||
246 | } | ||
247 | |||
248 | // Push EOF token. | ||
249 | Token token = (Token){ | ||
250 | .type = TOKEN_EOF, | ||
251 | .line = scanner.line_number, | ||
252 | .column = 1, | ||
253 | }; | ||
254 | array_push(tokens, token); | ||
255 | |||
256 | return tokens; | ||
257 | } | ||
diff --git a/src/treewalk/lexer.h b/src/treewalk/lexer.h deleted file mode 100644 index 2b2789f..0000000 --- a/src/treewalk/lexer.h +++ /dev/null | |||
@@ -1,57 +0,0 @@ | |||
1 | #ifndef BDL_LEXER_H | ||
2 | #define BDL_LEXER_H | ||
3 | |||
4 | typedef enum TokenType { | ||
5 | TOKEN_UNKNOWN = 0, | ||
6 | TOKEN_LPAREN, | ||
7 | TOKEN_RPAREN, | ||
8 | TOKEN_QUOTE, | ||
9 | TOKEN_TRUE, | ||
10 | TOKEN_FALSE, | ||
11 | TOKEN_NIL, | ||
12 | TOKEN_FIXNUM, | ||
13 | TOKEN_SYMBOL, | ||
14 | TOKEN_STRING, | ||
15 | TOKEN_EOF, | ||
16 | } TokenType; | ||
17 | |||
18 | typedef struct Token { | ||
19 | TokenType type; | ||
20 | StringView value; | ||
21 | size_t line; | ||
22 | size_t column; | ||
23 | } Token; | ||
24 | |||
25 | typedef struct Scanner { | ||
26 | StringView current; | ||
27 | size_t line_number; | ||
28 | size_t col_number; | ||
29 | size_t offset; | ||
30 | } Scanner; | ||
31 | |||
32 | // Print a token to standard output for debugging purposes. | ||
33 | void print_token(Token tok); | ||
34 | |||
35 | // Same functionality as the ScanView pairs, but keeping track of line and | ||
36 | // column numbers. | ||
37 | char scan_next(Scanner *scanner); | ||
38 | char scan_peek(const Scanner *scanner); | ||
39 | |||
40 | // Check if the current scanner still have characters left. | ||
41 | bool scan_has_next(const Scanner *scanner); | ||
42 | |||
43 | // Advance the scanner until we ran out of whitespace. | ||
44 | void skip_whitespace(Scanner *scanner); | ||
45 | |||
46 | // Check if a given character is a delimiter. | ||
47 | bool is_delimiter(char c); | ||
48 | |||
49 | // Extract the token type from the current string. | ||
50 | TokenType find_primitive_type(const StringView value); | ||
51 | |||
52 | // Generate a list of tokens from the given string. | ||
53 | Token * tokenize(const StringView *sv); | ||
54 | |||
55 | #define TOK_BUF_CAP 256 | ||
56 | |||
57 | #endif // BDL_LEXER_H | ||
diff --git a/src/treewalk/main.c b/src/treewalk/main.c deleted file mode 100755 index a5888fd..0000000 --- a/src/treewalk/main.c +++ /dev/null | |||
@@ -1,288 +0,0 @@ | |||
1 | #include <assert.h> | ||
2 | #include <getopt.h> | ||
3 | #include <stdbool.h> | ||
4 | #include <stdint.h> | ||
5 | #include <stdio.h> | ||
6 | #include <stdlib.h> | ||
7 | #include <string.h> | ||
8 | |||
9 | #include "darray.h" | ||
10 | #include "hashtable.h" | ||
11 | |||
12 | #include "singletons.c" | ||
13 | |||
14 | #include "environment.c" | ||
15 | #include "errors.c" | ||
16 | #include "gc.c" | ||
17 | #include "lexer.c" | ||
18 | #include "objects.c" | ||
19 | #include "parser.c" | ||
20 | #include "primitives.c" | ||
21 | #include "read_line.c" | ||
22 | #include "string_view.c" | ||
23 | |||
24 | void | ||
25 | init(void) { | ||
26 | // Initialize garbage collector. | ||
27 | gc_init(); | ||
28 | |||
29 | // Initialize singletons. | ||
30 | obj_nil = alloc_object(OBJ_TYPE_NIL); | ||
31 | obj_true = alloc_object(OBJ_TYPE_BOOL); | ||
32 | obj_false = alloc_object(OBJ_TYPE_BOOL); | ||
33 | obj_err = alloc_object(OBJ_TYPE_ERR); | ||
34 | obj_quote = make_symbol((StringView){"quote", 5}); | ||
35 | proc_if = alloc_object(OBJ_TYPE_ERR); | ||
36 | push_root(obj_nil); | ||
37 | push_root(obj_true); | ||
38 | push_root(obj_false); | ||
39 | push_root(obj_err); | ||
40 | push_root(obj_quote); | ||
41 | push_root(proc_if); | ||
42 | |||
43 | // Global environment. | ||
44 | global_env = env_create(NULL); | ||
45 | // TODO: make sure we create symbols and strings only once (interning | ||
46 | // strings?) | ||
47 | push_active_env(global_env); | ||
48 | |||
49 | // Primitive symbols. | ||
50 | MAKE_ENV_VAR(global_env, "else", obj_true); | ||
51 | MAKE_ENV_VAR(global_env, "nil", obj_nil); | ||
52 | MAKE_ENV_VAR(global_env, "if", proc_if); | ||
53 | |||
54 | // Primitive procedures. | ||
55 | MAKE_ENV_PROC(global_env, "eval", proc_eval); | ||
56 | MAKE_ENV_PROC(global_env, "quote", proc_quote); | ||
57 | MAKE_ENV_PROC(global_env, "car", proc_car); | ||
58 | MAKE_ENV_PROC(global_env, "cdr", proc_cdr); | ||
59 | MAKE_ENV_PROC(global_env, "cons", proc_cons); | ||
60 | MAKE_ENV_PROC(global_env, "list", proc_list); | ||
61 | MAKE_ENV_PROC(global_env, "+", proc_sum); | ||
62 | MAKE_ENV_PROC(global_env, "-", proc_sub); | ||
63 | MAKE_ENV_PROC(global_env, "*", proc_mul); | ||
64 | MAKE_ENV_PROC(global_env, "/", proc_div); | ||
65 | MAKE_ENV_PROC(global_env, "%", proc_mod); | ||
66 | MAKE_ENV_PROC(global_env, "print", proc_print); | ||
67 | MAKE_ENV_PROC(global_env, "display", proc_display); | ||
68 | MAKE_ENV_PROC(global_env, "newline", proc_newline); | ||
69 | MAKE_ENV_PROC(global_env, "boolean?", proc_is_boolean); | ||
70 | MAKE_ENV_PROC(global_env, "nil?", proc_is_nil); | ||
71 | MAKE_ENV_PROC(global_env, "symbol?", proc_is_symbol); | ||
72 | MAKE_ENV_PROC(global_env, "string?", proc_is_string); | ||
73 | MAKE_ENV_PROC(global_env, "fixnum?", proc_is_fixnum); | ||
74 | MAKE_ENV_PROC(global_env, "pair?", proc_is_pair); | ||
75 | MAKE_ENV_PROC(global_env, "procedure?", proc_is_procedure); | ||
76 | MAKE_ENV_PROC(global_env, "error?", proc_is_error); | ||
77 | MAKE_ENV_PROC(global_env, "not", proc_not); | ||
78 | MAKE_ENV_PROC(global_env, "and", proc_and); | ||
79 | MAKE_ENV_PROC(global_env, "or", proc_or); | ||
80 | MAKE_ENV_PROC(global_env, "cond", proc_cond); | ||
81 | MAKE_ENV_PROC(global_env, "<", proc_num_less_than); | ||
82 | MAKE_ENV_PROC(global_env, "<=", proc_num_lesseq_than); | ||
83 | MAKE_ENV_PROC(global_env, ">", proc_num_greater_than); | ||
84 | MAKE_ENV_PROC(global_env, ">=", proc_num_greatereq_than); | ||
85 | MAKE_ENV_PROC(global_env, "=", proc_num_equal); | ||
86 | MAKE_ENV_PROC(global_env, "eq?", proc_equal); | ||
87 | MAKE_ENV_PROC(global_env, "def", proc_define); | ||
88 | MAKE_ENV_PROC(global_env, "set!", proc_set); | ||
89 | MAKE_ENV_PROC(global_env, "lambda", proc_lambda); | ||
90 | MAKE_ENV_PROC(global_env, "fun", proc_fun); | ||
91 | |||
92 | // Runtime procedures. | ||
93 | MAKE_ENV_PROC(global_env, "supress-errors", proc_supress_errors); | ||
94 | } | ||
95 | |||
96 | void | ||
97 | process_source(const StringView *source) { | ||
98 | Token *tokens = tokenize(source); | ||
99 | if (errors_n != 0) { | ||
100 | if (tokens != NULL) { | ||
101 | array_free(tokens); | ||
102 | } | ||
103 | return; | ||
104 | } | ||
105 | |||
106 | Visitor visitor = (Visitor){ | ||
107 | .tokens = tokens, | ||
108 | .current = 0, | ||
109 | }; | ||
110 | while (has_next_token(&visitor) && peek_token(&visitor).type != TOKEN_EOF) { | ||
111 | // Check the root node stack size before parsing | ||
112 | size_t root_stack_size = array_size(gc.roots); | ||
113 | Object *root = parse_tree(&visitor); | ||
114 | array_head(gc.roots)->size = root_stack_size; | ||
115 | if (root == obj_err || errors_n != 0) { | ||
116 | break; | ||
117 | } | ||
118 | push_root(root); | ||
119 | |||
120 | Object *result = eval(global_env, root); | ||
121 | if (result != obj_nil) { | ||
122 | display(result); | ||
123 | printf("\n"); | ||
124 | } | ||
125 | pop_root(); | ||
126 | } | ||
127 | |||
128 | if (tokens != NULL) { | ||
129 | array_free(tokens); | ||
130 | } | ||
131 | } | ||
132 | |||
133 | #define REPL_PROMPT "bdl> " | ||
134 | |||
135 | void | ||
136 | run_repl(void) { | ||
137 | printf("BDL REPL (Press Ctrl-D or Ctrl-C to exit)\n"); | ||
138 | while (true) { | ||
139 | printf(REPL_PROMPT); | ||
140 | StringView sv = read_line(); | ||
141 | if (sv.start == NULL) { | ||
142 | return; | ||
143 | } | ||
144 | process_source(&sv); | ||
145 | |||
146 | // Check if there were any errors. | ||
147 | if (errors_n != 0 && !supress_errors) { | ||
148 | for (size_t i = 0; i < errors_n; i++) { | ||
149 | Error err = errors[i]; | ||
150 | for (size_t j = 0; j < err.col + sizeof(REPL_PROMPT) - 2; j++) { | ||
151 | putchar(' '); | ||
152 | } | ||
153 | printf("|\n"); | ||
154 | for (size_t j = 0; j < err.col + sizeof(REPL_PROMPT) - 2; j++) { | ||
155 | putchar(' '); | ||
156 | } | ||
157 | printf("%s\n", error_msgs[err.value]); | ||
158 | } | ||
159 | errors_n = 0; | ||
160 | continue; | ||
161 | } | ||
162 | } | ||
163 | } | ||
164 | |||
165 | void | ||
166 | run_file(char *file_name) { | ||
167 | FILE *file = fopen(file_name, "r"); | ||
168 | if (!file) { | ||
169 | fprintf(stderr, "error: couldn't open input file: %s\n", file_name); | ||
170 | exit(EXIT_FAILURE); | ||
171 | } | ||
172 | |||
173 | // Read entire file into memory. | ||
174 | fseek(file, 0, SEEK_END); | ||
175 | size_t file_size = ftell(file); | ||
176 | fseek(file, 0, SEEK_SET); | ||
177 | |||
178 | char *source = malloc(file_size + 1); | ||
179 | fread(source, 1, file_size, file); | ||
180 | source[file_size] = 0; | ||
181 | |||
182 | StringView sv = (StringView){ | ||
183 | .start = source, | ||
184 | .n = file_size, | ||
185 | }; | ||
186 | |||
187 | process_source(&sv); | ||
188 | |||
189 | // Check if there were any errors. | ||
190 | if (errors_n != 0 && !supress_errors) { | ||
191 | for (size_t i = 0; i < errors_n; i++) { | ||
192 | Error err = errors[i]; | ||
193 | fprintf(stderr, "%s", file_name); | ||
194 | if (err.line != 0) { | ||
195 | fprintf(stderr, ":%ld:%ld", err.line, err.col); | ||
196 | } | ||
197 | fprintf(stderr, ": %s\n", error_msgs[err.value]); | ||
198 | } | ||
199 | errors_n = 0; | ||
200 | } | ||
201 | |||
202 | free(source); | ||
203 | fclose(file); | ||
204 | } | ||
205 | |||
206 | #define STDIN_BUF_CAP 16 | ||
207 | |||
208 | void | ||
209 | run_stdin(void) { | ||
210 | size_t buf_size = 0; | ||
211 | char *source = NULL; | ||
212 | array_init(source, STDIN_BUF_CAP); | ||
213 | |||
214 | char c; | ||
215 | while ((c = getchar()) != EOF) { | ||
216 | array_push(source, c); | ||
217 | buf_size++; | ||
218 | } | ||
219 | |||
220 | StringView sv = (StringView){ | ||
221 | .start = source, | ||
222 | .n = buf_size, | ||
223 | }; | ||
224 | |||
225 | process_source(&sv); | ||
226 | |||
227 | // Check if there were any errors. | ||
228 | if (errors_n != 0 && !supress_errors) { | ||
229 | for (size_t i = 0; i < errors_n; i++) { | ||
230 | Error err = errors[i]; | ||
231 | fprintf(stderr, "stdin"); | ||
232 | if (err.line != 0) { | ||
233 | fprintf(stderr, ":%ld:%ld", err.line, err.col); | ||
234 | } | ||
235 | fprintf(stderr, ": %s\n", error_msgs[err.value]); | ||
236 | } | ||
237 | errors_n = 0; | ||
238 | } | ||
239 | |||
240 | array_free(source); | ||
241 | } | ||
242 | |||
243 | #ifndef BIN_NAME | ||
244 | #define BIN_NAME "bdl" | ||
245 | #endif | ||
246 | |||
247 | void | ||
248 | print_usage(void) { | ||
249 | printf("Usage: %s [options] <filename filename ...>\n", BIN_NAME); | ||
250 | printf("\n"); | ||
251 | printf("\t-i\tInteractive mode (REPL).\n"); | ||
252 | printf("\n"); | ||
253 | } | ||
254 | |||
255 | int | ||
256 | main(int argc, char *argv[]) { | ||
257 | init(); | ||
258 | |||
259 | int option; | ||
260 | while ((option = getopt(argc, argv, "i")) != -1) { | ||
261 | switch (option) { | ||
262 | case 'i': { | ||
263 | // Interactive mode. | ||
264 | run_repl(); | ||
265 | return EXIT_SUCCESS; | ||
266 | } break; | ||
267 | default: { | ||
268 | print_usage(); | ||
269 | return EXIT_FAILURE; | ||
270 | } break; | ||
271 | } | ||
272 | } | ||
273 | |||
274 | // Run from stdin. | ||
275 | if (optind == argc) { | ||
276 | run_stdin(); | ||
277 | return EXIT_SUCCESS; | ||
278 | } | ||
279 | |||
280 | // Run from file. | ||
281 | while (optind < argc) { | ||
282 | char *file_name = argv[optind]; | ||
283 | run_file(file_name); | ||
284 | optind++; | ||
285 | } | ||
286 | |||
287 | return EXIT_SUCCESS; | ||
288 | } | ||
diff --git a/src/treewalk/objects.c b/src/treewalk/objects.c deleted file mode 100644 index c71bc40..0000000 --- a/src/treewalk/objects.c +++ /dev/null | |||
@@ -1,141 +0,0 @@ | |||
1 | #include "gc.h" | ||
2 | #include "objects.h" | ||
3 | |||
4 | // | ||
5 | // Constructors. | ||
6 | // | ||
7 | |||
8 | Object * | ||
9 | make_fixnum(ssize_t num) { | ||
10 | Object *obj = alloc_object(OBJ_TYPE_FIXNUM); | ||
11 | obj->fixnum = num; | ||
12 | return obj; | ||
13 | } | ||
14 | |||
15 | Object * | ||
16 | make_procedure(Object *(*proc)(struct Environment *, struct Object *args)) { | ||
17 | Object *obj = alloc_object(OBJ_TYPE_PROCEDURE); | ||
18 | obj->proc = proc; | ||
19 | return obj; | ||
20 | } | ||
21 | |||
22 | Object * | ||
23 | make_pair(Object *car, Object *cdr) { | ||
24 | Object *obj = alloc_object(OBJ_TYPE_PAIR); | ||
25 | obj->car = car; | ||
26 | obj->cdr = cdr; | ||
27 | return obj; | ||
28 | } | ||
29 | |||
30 | Object * | ||
31 | make_symbol(StringView sv) { | ||
32 | Object *obj = alloc_object(OBJ_TYPE_SYMBOL); | ||
33 | obj->symbol = NULL; | ||
34 | array_init(obj->symbol, sv.n); | ||
35 | array_insert(obj->symbol, sv.start, sv.n); | ||
36 | return obj; | ||
37 | } | ||
38 | |||
39 | Object * | ||
40 | make_string(void) { | ||
41 | Object *obj = alloc_object(OBJ_TYPE_STRING); | ||
42 | obj->string = NULL; | ||
43 | array_init(obj->string, 0); | ||
44 | return obj; | ||
45 | } | ||
46 | |||
47 | void | ||
48 | append_string(Object *obj, const StringView sv) { | ||
49 | if (sv.n == 0) { | ||
50 | return; | ||
51 | } | ||
52 | array_insert(obj->string, sv.start, sv.n); | ||
53 | } | ||
54 | |||
55 | void | ||
56 | display_pair(Object *root) { | ||
57 | display(root->car); | ||
58 | if (root->cdr->type == OBJ_TYPE_PAIR) { | ||
59 | printf(" "); | ||
60 | display_pair(root->cdr); | ||
61 | } else if (root->cdr == obj_nil) { | ||
62 | return; | ||
63 | } else { | ||
64 | printf(" . "); | ||
65 | display(root->cdr); | ||
66 | } | ||
67 | } | ||
68 | |||
69 | void | ||
70 | display(Object *root) { | ||
71 | switch (root->type) { | ||
72 | case OBJ_TYPE_FIXNUM: { | ||
73 | printf("%zd", root->fixnum); | ||
74 | } break; | ||
75 | case OBJ_TYPE_BOOL: { | ||
76 | if (root == obj_true) { | ||
77 | printf("true"); | ||
78 | } else { | ||
79 | printf("false"); | ||
80 | } | ||
81 | } break; | ||
82 | case OBJ_TYPE_NIL: { | ||
83 | printf("()"); | ||
84 | } break; | ||
85 | case OBJ_TYPE_STRING: { | ||
86 | printf("\"%.*s\"", (int)array_size(root->string), root->string); | ||
87 | } break; | ||
88 | case OBJ_TYPE_SYMBOL: { | ||
89 | printf(":%.*s", (int)array_size(root->symbol), root->symbol); | ||
90 | } break; | ||
91 | case OBJ_TYPE_PAIR: { | ||
92 | printf("("); | ||
93 | display_pair(root); | ||
94 | printf(")"); | ||
95 | } break; | ||
96 | case OBJ_TYPE_LAMBDA: | ||
97 | case OBJ_TYPE_PROCEDURE: { | ||
98 | printf("#{procedure}"); | ||
99 | } break; | ||
100 | case OBJ_TYPE_ERR: { | ||
101 | printf("#{error}"); | ||
102 | } break; | ||
103 | } | ||
104 | return; | ||
105 | } | ||
106 | |||
107 | bool | ||
108 | obj_eq(const Object *a, const Object* b) { | ||
109 | if (a->type != b->type) { | ||
110 | return false; | ||
111 | } | ||
112 | switch (a->type) { | ||
113 | case OBJ_TYPE_FIXNUM: { | ||
114 | return a->fixnum == b->fixnum; | ||
115 | } break; | ||
116 | case OBJ_TYPE_STRING: { | ||
117 | if (array_size(a->string) != array_size(b->string)) { | ||
118 | return false; | ||
119 | } | ||
120 | for (size_t i = 0; i < array_size(a->string); i++) { | ||
121 | if (a->string[i] != b->string[i]) { | ||
122 | return false; | ||
123 | } | ||
124 | } | ||
125 | } break; | ||
126 | case OBJ_TYPE_SYMBOL: { | ||
127 | if (array_size(a->symbol) != array_size(b->symbol)) { | ||
128 | return false; | ||
129 | } | ||
130 | for (size_t i = 0; i < array_size(a->symbol); i++) { | ||
131 | if (a->symbol[i] != b->symbol[i]) { | ||
132 | return false; | ||
133 | } | ||
134 | } | ||
135 | } break; | ||
136 | default: { | ||
137 | return a == b; | ||
138 | } break; | ||
139 | } | ||
140 | return true; | ||
141 | } | ||
diff --git a/src/treewalk/objects.h b/src/treewalk/objects.h deleted file mode 100644 index ed623eb..0000000 --- a/src/treewalk/objects.h +++ /dev/null | |||
@@ -1,75 +0,0 @@ | |||
1 | #ifndef BDL_OBJECTS_H | ||
2 | #define BDL_OBJECTS_H | ||
3 | |||
4 | #include "string_view.h" | ||
5 | |||
6 | typedef enum ObjectType { | ||
7 | OBJ_TYPE_FIXNUM, | ||
8 | OBJ_TYPE_BOOL, | ||
9 | OBJ_TYPE_NIL, | ||
10 | OBJ_TYPE_SYMBOL, | ||
11 | OBJ_TYPE_STRING, | ||
12 | OBJ_TYPE_PAIR, | ||
13 | OBJ_TYPE_PROCEDURE, | ||
14 | OBJ_TYPE_LAMBDA, | ||
15 | OBJ_TYPE_ERR, | ||
16 | } ObjectType; | ||
17 | |||
18 | struct Environment; | ||
19 | |||
20 | typedef struct Object { | ||
21 | ObjectType type; | ||
22 | bool marked; | ||
23 | union { | ||
24 | // OBJ_TYPE_FIXNUM | ||
25 | ssize_t fixnum; | ||
26 | |||
27 | // OBJ_TYPE_STRING | ||
28 | struct { | ||
29 | char *string; | ||
30 | }; | ||
31 | |||
32 | // OBJ_TYPE_PAIR | ||
33 | struct { | ||
34 | struct Object *car; | ||
35 | struct Object *cdr; | ||
36 | }; | ||
37 | |||
38 | // OBJ_TYPE_SYMBOL | ||
39 | struct { | ||
40 | char *symbol; | ||
41 | }; | ||
42 | |||
43 | // OBJ_TYPE_PROCEDURE | ||
44 | struct Object *(*proc)(struct Environment *env, struct Object *args); | ||
45 | |||
46 | // OBJ_TYPE_LAMBDA | ||
47 | struct { | ||
48 | struct Object *params; | ||
49 | struct Object *body; | ||
50 | struct Environment *env; | ||
51 | }; | ||
52 | }; | ||
53 | } Object; | ||
54 | |||
55 | // Object constructors. | ||
56 | Object * make_fixnum(ssize_t num); | ||
57 | Object * make_procedure(Object *(*proc)(struct Environment *, Object *args)); | ||
58 | Object * make_pair(Object *car, Object *cdr); | ||
59 | Object * make_symbol(StringView sv); | ||
60 | Object * make_string(void); | ||
61 | void append_string(Object *obj, const StringView sv); | ||
62 | |||
63 | // Object representation. | ||
64 | void display(Object *root); | ||
65 | void display_pair(Object *root); | ||
66 | |||
67 | // Object comparison. | ||
68 | bool obj_eq(const Object *a, const Object* b); | ||
69 | |||
70 | // Utility macros. | ||
71 | #define DEBUG_OBJ(MSG,OBJ) printf((MSG)); display(OBJ); printf("\n"); | ||
72 | #define PRINT_OBJ(OBJ) display(OBJ); printf("\n"); | ||
73 | #define MAKE_SYM(STR) make_symbol((StringView){(STR), sizeof(STR) - 1}) | ||
74 | |||
75 | #endif // BDL_OBJECTS_H | ||
diff --git a/src/treewalk/parser.c b/src/treewalk/parser.c deleted file mode 100644 index a2f0f71..0000000 --- a/src/treewalk/parser.c +++ /dev/null | |||
@@ -1,139 +0,0 @@ | |||
1 | #include "parser.h" | ||
2 | |||
3 | Token | ||
4 | peek_token(const Visitor *visitor) { | ||
5 | return visitor->tokens[visitor->current]; | ||
6 | } | ||
7 | |||
8 | Token | ||
9 | next_token(Visitor *visitor) { | ||
10 | return visitor->tokens[visitor->current++]; | ||
11 | } | ||
12 | |||
13 | bool | ||
14 | has_next_token(const Visitor *visitor) { | ||
15 | return visitor->current < array_size(visitor->tokens); | ||
16 | } | ||
17 | |||
18 | Object * | ||
19 | parse_fixnum(Token tok) { | ||
20 | ssize_t num = 0; | ||
21 | int sign = 1; | ||
22 | for (size_t i = 0; i < tok.value.n; i++) { | ||
23 | char c = tok.value.start[i]; | ||
24 | if (c == '-') { | ||
25 | sign = -1; | ||
26 | continue; | ||
27 | } | ||
28 | num = num * 10 + (c - '0'); | ||
29 | } | ||
30 | |||
31 | Object *obj = make_fixnum(num * sign); | ||
32 | push_root(obj); | ||
33 | return obj; | ||
34 | } | ||
35 | |||
36 | Object * | ||
37 | parse_list(Visitor *vs) { | ||
38 | Token tok = peek_token(vs); | ||
39 | if (tok.type == TOKEN_EOF) { | ||
40 | return obj_err; | ||
41 | } | ||
42 | Object *root = make_pair(obj_nil, obj_nil); | ||
43 | push_root(root); | ||
44 | Object *next_obj = parse_tree(vs); | ||
45 | if (next_obj == obj_err) { | ||
46 | return obj_err; | ||
47 | } | ||
48 | root->car = next_obj; | ||
49 | Object *list = root; | ||
50 | while (has_next_token(vs)) { | ||
51 | Token tok = peek_token(vs); | ||
52 | if (tok.type == TOKEN_RPAREN) { | ||
53 | next_token(vs); | ||
54 | break; | ||
55 | } | ||
56 | if (tok.type == TOKEN_EOF) { | ||
57 | return obj_err; | ||
58 | } | ||
59 | next_obj = parse_tree(vs); | ||
60 | if (next_obj == obj_err) { | ||
61 | return obj_err; | ||
62 | } | ||
63 | list->cdr = make_pair(next_obj, obj_nil); | ||
64 | list = list->cdr; | ||
65 | } | ||
66 | return root; | ||
67 | } | ||
68 | |||
69 | Object * | ||
70 | parse_tree(Visitor *vs) { | ||
71 | Token tok = next_token(vs); | ||
72 | switch (tok.type) { | ||
73 | case TOKEN_FIXNUM: { | ||
74 | return parse_fixnum(tok); | ||
75 | } break; | ||
76 | case TOKEN_TRUE: { | ||
77 | return obj_true; | ||
78 | } break; | ||
79 | case TOKEN_FALSE: { | ||
80 | return obj_false; | ||
81 | } break; | ||
82 | case TOKEN_RPAREN: { | ||
83 | error_push((Error){ | ||
84 | .type = ERR_TYPE_PARSER, | ||
85 | .value = ERR_UNBALANCED_PAREN, | ||
86 | .line = tok.line, | ||
87 | .col = tok.column, | ||
88 | }); | ||
89 | return obj_err; | ||
90 | } break; | ||
91 | case TOKEN_QUOTE: { | ||
92 | Object *base = make_pair(obj_quote, obj_nil); | ||
93 | base->cdr = make_pair(obj_nil, obj_nil); | ||
94 | push_root(base); | ||
95 | Object *next_obj = parse_tree(vs); | ||
96 | if (next_obj == obj_err) { | ||
97 | return obj_err; | ||
98 | } | ||
99 | base->cdr->car = next_obj; | ||
100 | return base; | ||
101 | } break; | ||
102 | case TOKEN_LPAREN: { | ||
103 | Object *obj = parse_list(vs); | ||
104 | if (obj == obj_err) { | ||
105 | error_push((Error){ | ||
106 | .type = ERR_TYPE_PARSER, | ||
107 | .value = ERR_UNBALANCED_PAREN, | ||
108 | .line = tok.line, | ||
109 | .col = tok.column, | ||
110 | }); | ||
111 | } | ||
112 | return obj; | ||
113 | } break; | ||
114 | case TOKEN_STRING: { | ||
115 | Object *obj = make_string(); | ||
116 | push_root(obj); | ||
117 | append_string(obj, tok.value); | ||
118 | return obj; | ||
119 | } break; | ||
120 | case TOKEN_SYMBOL: { | ||
121 | Object *obj = make_symbol(tok.value); | ||
122 | push_root(obj); | ||
123 | return obj; | ||
124 | } break; | ||
125 | case TOKEN_NIL: { | ||
126 | return obj_nil; | ||
127 | } break; | ||
128 | default: { | ||
129 | break; | ||
130 | } break; | ||
131 | } | ||
132 | error_push((Error){ | ||
133 | .type = ERR_TYPE_PARSER, | ||
134 | .value = ERR_EOF_REACHED, | ||
135 | .line = tok.line, | ||
136 | .col = tok.column, | ||
137 | }); | ||
138 | return obj_err; | ||
139 | } | ||
diff --git a/src/treewalk/parser.h b/src/treewalk/parser.h deleted file mode 100644 index 3834c75..0000000 --- a/src/treewalk/parser.h +++ /dev/null | |||
@@ -1,22 +0,0 @@ | |||
1 | #ifndef BDL_PARSER_H | ||
2 | #define BDL_PARSER_H | ||
3 | |||
4 | typedef struct Visitor { | ||
5 | Token *tokens; | ||
6 | size_t current; | ||
7 | } Visitor; | ||
8 | |||
9 | // Mimics the functionality in the Scanner functions, but for entire tokens. | ||
10 | Token next_token(Visitor *visitor); | ||
11 | Token peek_token(const Visitor *visitor); | ||
12 | bool has_next_token(const Visitor *visitor); | ||
13 | |||
14 | // Parse a token into a fixnum object. | ||
15 | Object * parse_fixnum(Token tok); | ||
16 | |||
17 | // Recursive descent parser. If an object is not a list the parsing is handled | ||
18 | // by the parse_tree function. | ||
19 | Object * parse_list(Visitor *vs); | ||
20 | Object * parse_tree(Visitor *vs); | ||
21 | |||
22 | #endif // BDL_PARSER_H | ||
diff --git a/src/treewalk/primitives.c b/src/treewalk/primitives.c deleted file mode 100644 index 8b0d407..0000000 --- a/src/treewalk/primitives.c +++ /dev/null | |||
@@ -1,918 +0,0 @@ | |||
1 | #include "primitives.h" | ||
2 | |||
3 | Object * | ||
4 | eval(Environment *env, Object *root) { | ||
5 | Object* lambda = NULL; | ||
6 | Object* args = NULL; | ||
7 | Object* ret = NULL; | ||
8 | bool recursion_active = false; | ||
9 | eval_start: | ||
10 | switch (root->type) { | ||
11 | case OBJ_TYPE_ERR: | ||
12 | case OBJ_TYPE_PROCEDURE: | ||
13 | case OBJ_TYPE_LAMBDA: | ||
14 | case OBJ_TYPE_FIXNUM: | ||
15 | case OBJ_TYPE_BOOL: | ||
16 | case OBJ_TYPE_NIL: | ||
17 | case OBJ_TYPE_STRING: { | ||
18 | ret = root; | ||
19 | goto eval_success; | ||
20 | } break; | ||
21 | case OBJ_TYPE_SYMBOL: { | ||
22 | Object *val = env_lookup(env, root); | ||
23 | if (val == obj_err) { | ||
24 | error_push((Error){ | ||
25 | .type = ERR_TYPE_RUNTIME, | ||
26 | .value = ERR_SYMBOL_NOT_FOUND, | ||
27 | }); | ||
28 | return obj_err; | ||
29 | } | ||
30 | ret = val; | ||
31 | goto eval_success; | ||
32 | } break; | ||
33 | case OBJ_TYPE_PAIR: { | ||
34 | if (root->car->type == OBJ_TYPE_SYMBOL) { | ||
35 | Object *val = env_lookup(env, root->car); | ||
36 | if (val == obj_err) { | ||
37 | error_push((Error){ | ||
38 | .type = ERR_TYPE_RUNTIME, | ||
39 | .value = ERR_SYMBOL_NOT_FOUND, | ||
40 | }); | ||
41 | return obj_err; | ||
42 | } | ||
43 | |||
44 | // Primitive `if` procedure with TCO. | ||
45 | if (val == proc_if) { | ||
46 | Object *obj = root->cdr; | ||
47 | if (obj == obj_nil || obj->cdr == obj_nil) { | ||
48 | error_push((Error){ | ||
49 | .type = ERR_TYPE_RUNTIME, | ||
50 | .value = ERR_NOT_ENOUGH_ARGS, | ||
51 | }); | ||
52 | return obj_err; | ||
53 | } | ||
54 | Object *car = obj->car; | ||
55 | Object *cdr = obj->cdr; | ||
56 | Object *condition = eval(env, car); | ||
57 | if (condition == obj_err) { | ||
58 | return obj_err; | ||
59 | } | ||
60 | if (condition == obj_true) { | ||
61 | root = cdr->car; | ||
62 | } else if (cdr->cdr != obj_nil) { | ||
63 | root = cdr->cdr->car; | ||
64 | } else { | ||
65 | return obj_nil; | ||
66 | } | ||
67 | goto eval_start; | ||
68 | } | ||
69 | |||
70 | if (val->type == OBJ_TYPE_PROCEDURE) { | ||
71 | ret = val->proc(env, root->cdr); | ||
72 | goto eval_success; | ||
73 | } | ||
74 | if (val->type == OBJ_TYPE_LAMBDA) { | ||
75 | lambda = val; | ||
76 | goto eval_lambda; | ||
77 | } | ||
78 | error_push((Error){ | ||
79 | .type = ERR_TYPE_RUNTIME, | ||
80 | .value = ERR_OBJ_NOT_CALLABLE, | ||
81 | }); | ||
82 | return obj_err; | ||
83 | } | ||
84 | lambda = eval(env, root->car); | ||
85 | if (lambda == obj_err) { | ||
86 | return obj_err; | ||
87 | } | ||
88 | if (lambda->type != OBJ_TYPE_LAMBDA) { | ||
89 | error_push((Error){ | ||
90 | .type = ERR_TYPE_RUNTIME, | ||
91 | .value = ERR_OBJ_NOT_CALLABLE, | ||
92 | }); | ||
93 | return obj_err; | ||
94 | } | ||
95 | |||
96 | eval_lambda: | ||
97 | args = root->cdr; | ||
98 | Object *params = lambda->params; | ||
99 | if (!recursion_active) { | ||
100 | recursion_active = true; | ||
101 | // Protect current stack. | ||
102 | Environment *tmp = env_create(lambda->env); | ||
103 | push_active_env(tmp); | ||
104 | // Extend environment. | ||
105 | env = env_extend(tmp, env); | ||
106 | } | ||
107 | |||
108 | // Create temporary environment to store bindings. | ||
109 | Environment *tmp = env_create(env); | ||
110 | push_active_env(tmp); | ||
111 | |||
112 | // Evaluate arguments in temporary environment. | ||
113 | while (params != obj_nil) { | ||
114 | if (args == obj_nil) { | ||
115 | error_push((Error){ | ||
116 | .type = ERR_TYPE_RUNTIME, | ||
117 | .value = ERR_NOT_ENOUGH_ARGS, | ||
118 | }); | ||
119 | return obj_err; | ||
120 | } | ||
121 | if (args->car == obj_nil) { | ||
122 | error_push((Error){ | ||
123 | .type = ERR_TYPE_RUNTIME, | ||
124 | .value = ERR_NOT_ENOUGH_ARGS, | ||
125 | }); | ||
126 | return obj_err; | ||
127 | } | ||
128 | Object *symbol = params->car; | ||
129 | Object *value = eval(env, args->car); | ||
130 | if (value == obj_err) { | ||
131 | return obj_err; | ||
132 | } | ||
133 | env_add_or_update_current(tmp, symbol, value); | ||
134 | args = args->cdr; | ||
135 | params = params->cdr; | ||
136 | } | ||
137 | if (args != obj_nil) { | ||
138 | error_push((Error){ | ||
139 | .type = ERR_TYPE_RUNTIME, | ||
140 | .value = ERR_TOO_MANY_ARGS, | ||
141 | }); | ||
142 | return obj_err; | ||
143 | } | ||
144 | |||
145 | // Copy temporary environment values to closure environment. | ||
146 | args = root->cdr; | ||
147 | params = lambda->params; | ||
148 | while (params != obj_nil) { | ||
149 | Object *symbol = params->car; | ||
150 | Object *value = env_lookup(tmp, symbol); | ||
151 | env_add_or_update_current(env, symbol, value); | ||
152 | args = args->cdr; | ||
153 | params = params->cdr; | ||
154 | } | ||
155 | |||
156 | // Release the temporary environment protection. | ||
157 | pop_active_env(); | ||
158 | |||
159 | // Run the body of the function. | ||
160 | root = lambda->body; | ||
161 | while (root->cdr != obj_nil) { | ||
162 | if (eval(env, root->car) == obj_err) { | ||
163 | return obj_err; | ||
164 | }; | ||
165 | root = root->cdr; | ||
166 | } | ||
167 | root = root->car; | ||
168 | goto eval_start; | ||
169 | } break; | ||
170 | } | ||
171 | |||
172 | error_push((Error){ | ||
173 | .type = ERR_TYPE_RUNTIME, | ||
174 | .value = ERR_UNKNOWN_OBJ_TYPE, | ||
175 | }); | ||
176 | return obj_err; | ||
177 | |||
178 | eval_success: | ||
179 | if (recursion_active) { | ||
180 | // Remove stack protector. | ||
181 | pop_active_env(); | ||
182 | } | ||
183 | return ret; | ||
184 | } | ||
185 | |||
186 | Object * | ||
187 | proc_quote(Environment *env, Object *obj) { | ||
188 | (void)env; | ||
189 | return obj->car; | ||
190 | } | ||
191 | |||
192 | static inline Object * | ||
193 | extract_car_with_type(Environment *env, Object *obj, ObjectType expected_type) { | ||
194 | if (obj == obj_nil) { | ||
195 | error_push((Error){ | ||
196 | .type = ERR_TYPE_RUNTIME, | ||
197 | .value = ERR_NOT_ENOUGH_ARGS, | ||
198 | }); | ||
199 | return obj_err; | ||
200 | } | ||
201 | Object *car = eval(env, obj->car); | ||
202 | if (car == obj_err) { | ||
203 | return obj_err; | ||
204 | } | ||
205 | if (car->type != expected_type) { | ||
206 | error_push((Error){ | ||
207 | .type = ERR_TYPE_RUNTIME, | ||
208 | .value = ERR_WRONG_ARG_TYPE, | ||
209 | }); | ||
210 | return obj_err; | ||
211 | } | ||
212 | return car; | ||
213 | } | ||
214 | |||
215 | // | ||
216 | // Arithmetic procedures. | ||
217 | // | ||
218 | |||
219 | Object * | ||
220 | proc_sum(Environment *env, Object *obj) { | ||
221 | Object *car = extract_car_with_type(env, obj, OBJ_TYPE_FIXNUM); | ||
222 | obj = obj->cdr; | ||
223 | ssize_t tot = car->fixnum; | ||
224 | while (obj != obj_nil) { | ||
225 | car = extract_car_with_type(env, obj, OBJ_TYPE_FIXNUM); | ||
226 | tot += car->fixnum; | ||
227 | obj = obj->cdr; | ||
228 | } | ||
229 | return make_fixnum(tot); | ||
230 | } | ||
231 | |||
232 | Object * | ||
233 | proc_sub(Environment *env, Object *obj) { | ||
234 | Object *car = extract_car_with_type(env, obj, OBJ_TYPE_FIXNUM); | ||
235 | obj = obj->cdr; | ||
236 | ssize_t tot = car->fixnum; | ||
237 | while (obj != obj_nil) { | ||
238 | car = extract_car_with_type(env, obj, OBJ_TYPE_FIXNUM); | ||
239 | tot -= car->fixnum; | ||
240 | obj = obj->cdr; | ||
241 | } | ||
242 | return make_fixnum(tot); | ||
243 | } | ||
244 | |||
245 | Object * | ||
246 | proc_mul(Environment *env, Object *obj) { | ||
247 | Object *car = extract_car_with_type(env, obj, OBJ_TYPE_FIXNUM); | ||
248 | obj = obj->cdr; | ||
249 | ssize_t tot = car->fixnum; | ||
250 | while (obj != obj_nil) { | ||
251 | car = extract_car_with_type(env, obj, OBJ_TYPE_FIXNUM); | ||
252 | tot *= car->fixnum; | ||
253 | obj = obj->cdr; | ||
254 | } | ||
255 | return make_fixnum(tot); | ||
256 | } | ||
257 | |||
258 | Object * | ||
259 | proc_div(Environment *env, Object *obj) { | ||
260 | Object *car = extract_car_with_type(env, obj, OBJ_TYPE_FIXNUM); | ||
261 | obj = obj->cdr; | ||
262 | ssize_t tot = car->fixnum; | ||
263 | while (obj != obj_nil) { | ||
264 | car = extract_car_with_type(env, obj, OBJ_TYPE_FIXNUM); | ||
265 | if (car->fixnum == 0) { | ||
266 | error_push((Error){ | ||
267 | .type = ERR_TYPE_RUNTIME, | ||
268 | .value = ERR_DIVISION_BY_ZERO, | ||
269 | }); | ||
270 | return obj_err; | ||
271 | } | ||
272 | tot /= car->fixnum; | ||
273 | obj = obj->cdr; | ||
274 | } | ||
275 | return make_fixnum(tot); | ||
276 | } | ||
277 | |||
278 | Object * | ||
279 | proc_mod(Environment *env, Object *obj) { | ||
280 | Object *car = extract_car_with_type(env, obj, OBJ_TYPE_FIXNUM); | ||
281 | obj = obj->cdr; | ||
282 | ssize_t tot = car->fixnum; | ||
283 | while (obj != obj_nil) { | ||
284 | car = extract_car_with_type(env, obj, OBJ_TYPE_FIXNUM); | ||
285 | if (car->fixnum == 0) { | ||
286 | error_push((Error){ | ||
287 | .type = ERR_TYPE_RUNTIME, | ||
288 | .value = ERR_DIVISION_BY_ZERO, | ||
289 | }); | ||
290 | return obj_err; | ||
291 | } | ||
292 | tot %= car->fixnum; | ||
293 | obj = obj->cdr; | ||
294 | } | ||
295 | return make_fixnum(tot); | ||
296 | } | ||
297 | |||
298 | // | ||
299 | // Display/Evaluation procedues. | ||
300 | // | ||
301 | |||
302 | Object * | ||
303 | proc_display(Environment *env, Object *obj) { | ||
304 | display(eval(env, obj->car)); | ||
305 | return obj_nil; | ||
306 | } | ||
307 | |||
308 | Object * | ||
309 | proc_print(Environment *env, Object *obj) { | ||
310 | Object *car = extract_car_with_type(env, obj, OBJ_TYPE_STRING); | ||
311 | StringView scanner = (StringView) { | ||
312 | .start = car->string, | ||
313 | .n = array_size(car->string), | ||
314 | }; | ||
315 | while (scanner.n != 0) { | ||
316 | char c = sv_next(&scanner); | ||
317 | if (c == '\\' && sv_peek(&scanner) == 'n') { | ||
318 | putchar('\n'); | ||
319 | sv_next(&scanner); | ||
320 | continue; | ||
321 | } | ||
322 | if (c == '\\' && sv_peek(&scanner) == '"') { | ||
323 | putchar('"'); | ||
324 | sv_next(&scanner); | ||
325 | continue; | ||
326 | } | ||
327 | putchar(c); | ||
328 | } | ||
329 | return obj_nil; | ||
330 | } | ||
331 | |||
332 | Object * | ||
333 | proc_newline(Environment *env, Object *obj) { | ||
334 | printf("\n"); | ||
335 | (void)env; | ||
336 | (void)obj; | ||
337 | return obj_nil; | ||
338 | } | ||
339 | |||
340 | // | ||
341 | // Type info procedures. | ||
342 | // | ||
343 | |||
344 | Object * | ||
345 | proc_is_boolean(Environment *env, Object *obj) { | ||
346 | if (obj == obj_nil) { | ||
347 | error_push((Error){ | ||
348 | .type = ERR_TYPE_RUNTIME, | ||
349 | .value = ERR_NOT_ENOUGH_ARGS, | ||
350 | }); | ||
351 | return obj_err; | ||
352 | } | ||
353 | obj = eval(env, obj->car); | ||
354 | if (obj == obj_err) { | ||
355 | return obj_err; | ||
356 | } | ||
357 | return (obj == obj_true || obj == obj_false) ? obj_true : obj_false; | ||
358 | } | ||
359 | |||
360 | Object * | ||
361 | proc_is_nil(Environment *env, Object *obj) { | ||
362 | if (obj == obj_nil) { | ||
363 | error_push((Error){ | ||
364 | .type = ERR_TYPE_RUNTIME, | ||
365 | .value = ERR_NOT_ENOUGH_ARGS, | ||
366 | }); | ||
367 | return obj_err; | ||
368 | } | ||
369 | obj = eval(env, obj->car); | ||
370 | if (obj == obj_err) { | ||
371 | return obj_err; | ||
372 | } | ||
373 | return obj == obj_nil ? obj_true : obj_false; | ||
374 | } | ||
375 | |||
376 | Object * | ||
377 | proc_is_symbol(Environment *env, Object *obj) { | ||
378 | if (obj == obj_nil) { | ||
379 | error_push((Error){ | ||
380 | .type = ERR_TYPE_RUNTIME, | ||
381 | .value = ERR_NOT_ENOUGH_ARGS, | ||
382 | }); | ||
383 | return obj_err; | ||
384 | } | ||
385 | obj = eval(env, obj->car); | ||
386 | if (obj == obj_err) { | ||
387 | return obj_err; | ||
388 | } | ||
389 | return obj->type == OBJ_TYPE_SYMBOL ? obj_true : obj_false; | ||
390 | } | ||
391 | |||
392 | Object * | ||
393 | proc_is_string(Environment *env, Object *obj) { | ||
394 | if (obj == obj_nil) { | ||
395 | error_push((Error){ | ||
396 | .type = ERR_TYPE_RUNTIME, | ||
397 | .value = ERR_NOT_ENOUGH_ARGS, | ||
398 | }); | ||
399 | return obj_err; | ||
400 | } | ||
401 | obj = eval(env, obj->car); | ||
402 | if (obj == obj_err) { | ||
403 | return obj_err; | ||
404 | } | ||
405 | return obj->type == OBJ_TYPE_STRING ? obj_true : obj_false; | ||
406 | } | ||
407 | |||
408 | Object * | ||
409 | proc_is_fixnum(Environment *env, Object *obj) { | ||
410 | if (obj == obj_nil) { | ||
411 | error_push((Error){ | ||
412 | .type = ERR_TYPE_RUNTIME, | ||
413 | .value = ERR_NOT_ENOUGH_ARGS, | ||
414 | }); | ||
415 | return obj_err; | ||
416 | } | ||
417 | obj = eval(env, obj->car); | ||
418 | if (obj == obj_err) { | ||
419 | return obj_err; | ||
420 | } | ||
421 | return obj->type == OBJ_TYPE_FIXNUM ? obj_true : obj_false; | ||
422 | } | ||
423 | |||
424 | Object * | ||
425 | proc_is_pair(Environment *env, Object *obj) { | ||
426 | if (obj == obj_nil) { | ||
427 | error_push((Error){ | ||
428 | .type = ERR_TYPE_RUNTIME, | ||
429 | .value = ERR_NOT_ENOUGH_ARGS, | ||
430 | }); | ||
431 | return obj_err; | ||
432 | } | ||
433 | obj = eval(env, obj->car); | ||
434 | if (obj == obj_err) { | ||
435 | return obj_err; | ||
436 | } | ||
437 | return obj->type == OBJ_TYPE_PAIR ? obj_true : obj_false; | ||
438 | } | ||
439 | |||
440 | Object * | ||
441 | proc_is_procedure(Environment *env, Object *obj) { | ||
442 | if (obj == obj_nil) { | ||
443 | error_push((Error){ | ||
444 | .type = ERR_TYPE_RUNTIME, | ||
445 | .value = ERR_NOT_ENOUGH_ARGS, | ||
446 | }); | ||
447 | return obj_err; | ||
448 | } | ||
449 | obj = eval(env, obj->car); | ||
450 | if (obj == obj_err) { | ||
451 | return obj_err; | ||
452 | } | ||
453 | return obj->type == OBJ_TYPE_PROCEDURE ? obj_true : obj_false; | ||
454 | } | ||
455 | |||
456 | Object * | ||
457 | proc_is_error(Environment *env, Object *obj) { | ||
458 | if (obj == obj_nil) { | ||
459 | error_push((Error){ | ||
460 | .type = ERR_TYPE_RUNTIME, | ||
461 | .value = ERR_NOT_ENOUGH_ARGS, | ||
462 | }); | ||
463 | return obj_err; | ||
464 | } | ||
465 | obj = eval(env, obj->car); | ||
466 | if (obj == obj_err) { | ||
467 | return obj_true; | ||
468 | } | ||
469 | return obj_false; | ||
470 | } | ||
471 | |||
472 | // | ||
473 | // Boolean/conditional procedures. | ||
474 | // | ||
475 | |||
476 | Object * | ||
477 | proc_not(Environment *env, Object *obj) { | ||
478 | if (obj == obj_nil) { | ||
479 | error_push((Error){ | ||
480 | .type = ERR_TYPE_RUNTIME, | ||
481 | .value = ERR_NOT_ENOUGH_ARGS, | ||
482 | }); | ||
483 | return obj_err; | ||
484 | } | ||
485 | obj = eval(env, obj->car); | ||
486 | if (obj == obj_err) { | ||
487 | return obj_err; | ||
488 | } | ||
489 | return obj == obj_false ? obj_true : obj_false; | ||
490 | } | ||
491 | |||
492 | Object * | ||
493 | proc_and(Environment *env, Object *obj) { | ||
494 | while (obj != obj_nil) { | ||
495 | if (proc_not(env, obj) == obj_true) { | ||
496 | return obj_false; | ||
497 | } | ||
498 | obj = obj->cdr; | ||
499 | } | ||
500 | return obj_true; | ||
501 | } | ||
502 | |||
503 | Object * | ||
504 | proc_or(Environment *env, Object *obj) { | ||
505 | while (obj != obj_nil) { | ||
506 | if (proc_not(env, obj) == obj_false) { | ||
507 | return obj_true; | ||
508 | } | ||
509 | obj = obj->cdr; | ||
510 | } | ||
511 | return obj_false; | ||
512 | } | ||
513 | |||
514 | Object * | ||
515 | proc_cond(Environment *env, Object *obj) { | ||
516 | if (obj == obj_nil) { | ||
517 | error_push((Error){ | ||
518 | .type = ERR_TYPE_RUNTIME, | ||
519 | .value = ERR_NOT_ENOUGH_ARGS, | ||
520 | }); | ||
521 | return obj_err; | ||
522 | } | ||
523 | while (obj != obj_nil) { | ||
524 | Object *clause = obj->car; | ||
525 | if (clause->type != OBJ_TYPE_PAIR || clause->cdr == obj_nil) { | ||
526 | error_push((Error){ | ||
527 | .type = ERR_TYPE_RUNTIME, | ||
528 | .value = ERR_WRONG_ARG_TYPE, | ||
529 | }); | ||
530 | return obj_err; | ||
531 | } | ||
532 | Object *test = clause->car; | ||
533 | Object *value = clause->cdr->car; | ||
534 | Object *result = eval(env, test); | ||
535 | if (result == obj_err) { | ||
536 | return obj_err; | ||
537 | } | ||
538 | if (result == obj_true) { | ||
539 | return eval(env, value); | ||
540 | } | ||
541 | obj = obj->cdr; | ||
542 | } | ||
543 | return obj_nil; | ||
544 | } | ||
545 | |||
546 | Object * | ||
547 | proc_num_less_than(Environment *env, Object *obj) { | ||
548 | Object *car = extract_car_with_type(env, obj, OBJ_TYPE_FIXNUM); | ||
549 | obj = obj->cdr; | ||
550 | ssize_t prev = car->fixnum; | ||
551 | while (obj != obj_nil) { | ||
552 | car = extract_car_with_type(env, obj, OBJ_TYPE_FIXNUM); | ||
553 | if (prev >= car->fixnum) { | ||
554 | return obj_false; | ||
555 | } | ||
556 | prev = car->fixnum; | ||
557 | obj = obj->cdr; | ||
558 | } | ||
559 | return obj_true; | ||
560 | } | ||
561 | |||
562 | Object * | ||
563 | proc_num_greater_than(Environment *env, Object *obj) { | ||
564 | Object *car = extract_car_with_type(env, obj, OBJ_TYPE_FIXNUM); | ||
565 | obj = obj->cdr; | ||
566 | ssize_t prev = car->fixnum; | ||
567 | while (obj != obj_nil) { | ||
568 | car = extract_car_with_type(env, obj, OBJ_TYPE_FIXNUM); | ||
569 | if (prev <= car->fixnum) { | ||
570 | return obj_false; | ||
571 | } | ||
572 | prev = car->fixnum; | ||
573 | obj = obj->cdr; | ||
574 | } | ||
575 | return obj_true; | ||
576 | } | ||
577 | |||
578 | Object * | ||
579 | proc_num_lesseq_than(Environment *env, Object *obj) { | ||
580 | Object *car = extract_car_with_type(env, obj, OBJ_TYPE_FIXNUM); | ||
581 | obj = obj->cdr; | ||
582 | ssize_t prev = car->fixnum; | ||
583 | while (obj != obj_nil) { | ||
584 | car = extract_car_with_type(env, obj, OBJ_TYPE_FIXNUM); | ||
585 | if (prev > car->fixnum) { | ||
586 | return obj_false; | ||
587 | } | ||
588 | prev = car->fixnum; | ||
589 | obj = obj->cdr; | ||
590 | } | ||
591 | return obj_true; | ||
592 | } | ||
593 | |||
594 | Object * | ||
595 | proc_num_greatereq_than(Environment *env, Object *obj) { | ||
596 | Object *car = extract_car_with_type(env, obj, OBJ_TYPE_FIXNUM); | ||
597 | obj = obj->cdr; | ||
598 | ssize_t prev = car->fixnum; | ||
599 | while (obj != obj_nil) { | ||
600 | car = extract_car_with_type(env, obj, OBJ_TYPE_FIXNUM); | ||
601 | if (prev < car->fixnum) { | ||
602 | return obj_false; | ||
603 | } | ||
604 | prev = car->fixnum; | ||
605 | obj = obj->cdr; | ||
606 | } | ||
607 | return obj_true; | ||
608 | } | ||
609 | |||
610 | Object * | ||
611 | proc_num_equal(Environment *env, Object *obj) { | ||
612 | Object *car = extract_car_with_type(env, obj, OBJ_TYPE_FIXNUM); | ||
613 | obj = obj->cdr; | ||
614 | ssize_t prev = car->fixnum; | ||
615 | while (obj != obj_nil) { | ||
616 | car = extract_car_with_type(env, obj, OBJ_TYPE_FIXNUM); | ||
617 | if (prev != car->fixnum) { | ||
618 | return obj_false; | ||
619 | } | ||
620 | prev = car->fixnum; | ||
621 | obj = obj->cdr; | ||
622 | } | ||
623 | return obj_true; | ||
624 | } | ||
625 | |||
626 | // | ||
627 | // List operation procedures. | ||
628 | // | ||
629 | |||
630 | Object * | ||
631 | proc_car(Environment *env, Object *obj) { | ||
632 | if (obj == obj_nil) { | ||
633 | error_push((Error){ | ||
634 | .type = ERR_TYPE_RUNTIME, | ||
635 | .value = ERR_NOT_ENOUGH_ARGS, | ||
636 | }); | ||
637 | return obj_err; | ||
638 | } | ||
639 | obj = eval(env, obj->car); | ||
640 | if (obj == obj_err) { | ||
641 | return obj_err; | ||
642 | } | ||
643 | if (obj->type != OBJ_TYPE_PAIR) { | ||
644 | error_push((Error){ | ||
645 | .type = ERR_TYPE_RUNTIME, | ||
646 | .value = ERR_WRONG_ARG_TYPE, | ||
647 | }); | ||
648 | return obj_err; | ||
649 | } | ||
650 | return obj->car; | ||
651 | } | ||
652 | |||
653 | Object * | ||
654 | proc_cdr(Environment *env, Object *obj) { | ||
655 | if (obj == obj_nil) { | ||
656 | error_push((Error){ | ||
657 | .type = ERR_TYPE_RUNTIME, | ||
658 | .value = ERR_NOT_ENOUGH_ARGS, | ||
659 | }); | ||
660 | return obj_err; | ||
661 | } | ||
662 | obj = eval(env, obj->car); | ||
663 | if (obj == obj_err) { | ||
664 | return obj_err; | ||
665 | } | ||
666 | if (obj->type != OBJ_TYPE_PAIR) { | ||
667 | error_push((Error){ | ||
668 | .type = ERR_TYPE_RUNTIME, | ||
669 | .value = ERR_WRONG_ARG_TYPE, | ||
670 | }); | ||
671 | return obj_err; | ||
672 | } | ||
673 | return obj->cdr; | ||
674 | } | ||
675 | |||
676 | Object * | ||
677 | proc_cons(Environment *env, Object *obj) { | ||
678 | if (obj == obj_nil) { | ||
679 | error_push((Error){ | ||
680 | .type = ERR_TYPE_RUNTIME, | ||
681 | .value = ERR_NOT_ENOUGH_ARGS, | ||
682 | }); | ||
683 | return obj_err; | ||
684 | } | ||
685 | Object *head = make_pair(obj_nil, obj_nil); | ||
686 | push_root(head); | ||
687 | head->car = eval(env, obj->car); | ||
688 | if (head->car == obj_err) { | ||
689 | pop_root(); | ||
690 | return obj_err; | ||
691 | } | ||
692 | head->cdr = eval(env, obj->cdr->car); | ||
693 | if (head->cdr == obj_err) { | ||
694 | pop_root(); | ||
695 | return obj_err; | ||
696 | } | ||
697 | pop_root(); | ||
698 | return head; | ||
699 | } | ||
700 | |||
701 | Object * | ||
702 | proc_list(Environment *env, Object *obj) { | ||
703 | if (obj == obj_nil) { | ||
704 | return obj_nil; | ||
705 | } | ||
706 | |||
707 | Object *head = make_pair(obj_nil, obj_nil); | ||
708 | push_root(head); | ||
709 | Object *tmp = eval(env, obj->car); | ||
710 | if (tmp == obj_err) { | ||
711 | pop_root(); | ||
712 | return obj_err; | ||
713 | } | ||
714 | head->car = tmp; | ||
715 | Object *curr = head; | ||
716 | obj = obj->cdr; | ||
717 | while (obj != obj_nil) { | ||
718 | tmp = eval(env, obj->car); | ||
719 | if (tmp == obj_err) { | ||
720 | pop_root(); | ||
721 | return obj_err; | ||
722 | } | ||
723 | curr->cdr = make_pair(tmp, obj_nil); | ||
724 | curr = curr->cdr; | ||
725 | obj = obj->cdr; | ||
726 | } | ||
727 | pop_root(); | ||
728 | return head; | ||
729 | } | ||
730 | |||
731 | // | ||
732 | // Polymorphic procedures. | ||
733 | // | ||
734 | |||
735 | Object * | ||
736 | proc_equal(Environment *env, Object *obj) { | ||
737 | if (obj == obj_nil || obj->cdr == obj_nil) { | ||
738 | error_push((Error){ | ||
739 | .type = ERR_TYPE_RUNTIME, | ||
740 | .value = ERR_NOT_ENOUGH_ARGS, | ||
741 | }); | ||
742 | return obj_err; | ||
743 | } | ||
744 | Object *a = eval(env, obj->car); | ||
745 | if (a == obj_err) { | ||
746 | return obj_err; | ||
747 | } | ||
748 | Object *b = eval(env, obj->cdr->car); | ||
749 | if (b == obj_err) { | ||
750 | return obj_err; | ||
751 | } | ||
752 | return obj_eq(a, b) ? obj_true : obj_false; | ||
753 | } | ||
754 | |||
755 | // | ||
756 | // Variables and declarations. | ||
757 | // | ||
758 | |||
759 | Object * | ||
760 | proc_define(Environment *env, Object *obj) { | ||
761 | if (obj == obj_nil || obj->cdr == obj_nil) { | ||
762 | error_push((Error){ | ||
763 | .type = ERR_TYPE_RUNTIME, | ||
764 | .value = ERR_NOT_ENOUGH_ARGS, | ||
765 | }); | ||
766 | return obj_err; | ||
767 | } | ||
768 | |||
769 | Object *symbol = obj->car; | ||
770 | if (symbol->type != OBJ_TYPE_SYMBOL) { | ||
771 | error_push((Error){ | ||
772 | .type = ERR_TYPE_RUNTIME, | ||
773 | .value = ERR_WRONG_ARG_TYPE, | ||
774 | }); | ||
775 | return obj_err; | ||
776 | } | ||
777 | |||
778 | Object *value = eval(env, obj->cdr->car); | ||
779 | if (value == obj_err) { | ||
780 | return obj_err; | ||
781 | } | ||
782 | |||
783 | env_add_or_update_current(env, symbol, value); | ||
784 | return obj_nil; | ||
785 | } | ||
786 | |||
787 | Object * | ||
788 | proc_set(Environment *env, Object *obj) { | ||
789 | if (obj == obj_nil || obj->cdr == obj_nil) { | ||
790 | error_push((Error){ | ||
791 | .type = ERR_TYPE_RUNTIME, | ||
792 | .value = ERR_NOT_ENOUGH_ARGS, | ||
793 | }); | ||
794 | return obj_err; | ||
795 | } | ||
796 | |||
797 | Object *symbol = obj->car; | ||
798 | if (symbol->type != OBJ_TYPE_SYMBOL) { | ||
799 | error_push((Error){ | ||
800 | .type = ERR_TYPE_RUNTIME, | ||
801 | .value = ERR_WRONG_ARG_TYPE, | ||
802 | }); | ||
803 | return obj_err; | ||
804 | } | ||
805 | |||
806 | Object *value = eval(env, obj->cdr->car); | ||
807 | if (value == obj_err) { | ||
808 | return obj_err; | ||
809 | } | ||
810 | |||
811 | return env_update(env, symbol, value); | ||
812 | } | ||
813 | |||
814 | Object * | ||
815 | proc_lambda(Environment *env, Object *obj) { | ||
816 | if (obj == obj_nil || obj->cdr == obj_nil) { | ||
817 | error_push((Error){ | ||
818 | .type = ERR_TYPE_RUNTIME, | ||
819 | .value = ERR_NOT_ENOUGH_ARGS, | ||
820 | }); | ||
821 | return obj_err; | ||
822 | } | ||
823 | Object *params = obj->car; | ||
824 | if (params != obj_nil && params->type != OBJ_TYPE_PAIR) { | ||
825 | error_push((Error){ | ||
826 | .type = ERR_TYPE_RUNTIME, | ||
827 | .value = ERR_WRONG_ARG_TYPE, | ||
828 | }); | ||
829 | return obj_err; | ||
830 | } | ||
831 | Object *body = obj->cdr; | ||
832 | Object *fun = alloc_object(OBJ_TYPE_LAMBDA); | ||
833 | fun->params = params; | ||
834 | fun->body = body; | ||
835 | fun->env = env; | ||
836 | return fun; | ||
837 | } | ||
838 | |||
839 | Object * | ||
840 | proc_fun(Environment *env, Object *obj) { | ||
841 | if (obj == obj_nil || obj->cdr == obj_nil || obj->cdr->cdr == obj_nil) { | ||
842 | error_push((Error){ | ||
843 | .type = ERR_TYPE_RUNTIME, | ||
844 | .value = ERR_NOT_ENOUGH_ARGS, | ||
845 | }); | ||
846 | return obj_err; | ||
847 | } | ||
848 | |||
849 | Object *name = obj->car; | ||
850 | if (name->type != OBJ_TYPE_SYMBOL) { | ||
851 | error_push((Error){ | ||
852 | .type = ERR_TYPE_RUNTIME, | ||
853 | .value = ERR_WRONG_ARG_TYPE, | ||
854 | }); | ||
855 | return obj_err; | ||
856 | } | ||
857 | |||
858 | Object *params = obj->cdr->car; | ||
859 | if (params != obj_nil && params->type != OBJ_TYPE_PAIR) { | ||
860 | error_push((Error){ | ||
861 | .type = ERR_TYPE_RUNTIME, | ||
862 | .value = ERR_WRONG_ARG_TYPE, | ||
863 | }); | ||
864 | return obj_err; | ||
865 | } | ||
866 | Object *body = obj->cdr->cdr; | ||
867 | Object *fun = alloc_object(OBJ_TYPE_LAMBDA); | ||
868 | fun->params = params; | ||
869 | fun->body = body; | ||
870 | fun->env = env; | ||
871 | env_add_or_update_current(env, name, fun); | ||
872 | return obj_nil; | ||
873 | } | ||
874 | |||
875 | // | ||
876 | // Evaluation. | ||
877 | // | ||
878 | |||
879 | Object * | ||
880 | proc_eval(Environment *env, Object *obj) { | ||
881 | if (obj == obj_nil) { | ||
882 | error_push((Error){ | ||
883 | .type = ERR_TYPE_RUNTIME, | ||
884 | .value = ERR_NOT_ENOUGH_ARGS, | ||
885 | }); | ||
886 | return obj_err; | ||
887 | } | ||
888 | return eval(env, eval(env, obj->car)); | ||
889 | } | ||
890 | |||
891 | // | ||
892 | // Runtime configuration options. | ||
893 | // | ||
894 | |||
895 | Object * | ||
896 | proc_supress_errors(Environment *env, Object *obj) { | ||
897 | Object *car = extract_car_with_type(env, obj, OBJ_TYPE_BOOL); | ||
898 | if (car == obj_err) { | ||
899 | return obj_err; | ||
900 | } | ||
901 | |||
902 | if (car == obj_true) { | ||
903 | supress_errors = true; | ||
904 | } else if (car == obj_false) { | ||
905 | supress_errors = false; | ||
906 | } | ||
907 | return obj_nil; | ||
908 | } | ||
909 | |||
910 | // TODO: map | ||
911 | // TODO: apply | ||
912 | // TODO: filter | ||
913 | |||
914 | // TODO: fixnum left/right shift, mask, invert | ||
915 | // TODO: add primitives for type transforms: string->symbol, symbol->string, etc | ||
916 | // TODO: implement support for semi-quotes | ||
917 | // TODO: LAMBDA | ||
918 | // TODO: let | ||
diff --git a/src/treewalk/primitives.h b/src/treewalk/primitives.h deleted file mode 100644 index f874b17..0000000 --- a/src/treewalk/primitives.h +++ /dev/null | |||
@@ -1,60 +0,0 @@ | |||
1 | #ifndef BDL_PRIMITIVES_H | ||
2 | #define BDL_PRIMITIVES_H | ||
3 | |||
4 | // Function evaluation. | ||
5 | Object * eval(Environment *env, Object *root); | ||
6 | |||
7 | // Evaluation functions. | ||
8 | Object * proc_quote(Environment *env, Object *obj); | ||
9 | Object * proc_eval(Environment *env, Object *obj); | ||
10 | |||
11 | // Arithmetic. | ||
12 | Object * proc_sum(Environment *env, Object *obj); | ||
13 | Object * proc_sub(Environment *env, Object *obj); | ||
14 | Object * proc_mul(Environment *env, Object *obj); | ||
15 | Object * proc_div(Environment *env, Object *obj); | ||
16 | Object * proc_mod(Environment *env, Object *obj); | ||
17 | |||
18 | // Printing. | ||
19 | Object * proc_display(Environment *env, Object *obj); | ||
20 | Object * proc_print(Environment *env, Object *obj); | ||
21 | Object * proc_newline(Environment *env, Object *obj); | ||
22 | |||
23 | // Type checking. | ||
24 | Object * proc_is_boolean(Environment *env, Object *obj); | ||
25 | Object * proc_is_nil(Environment *env, Object *obj); | ||
26 | Object * proc_is_symbol(Environment *env, Object *obj); | ||
27 | Object * proc_is_string(Environment *env, Object *obj); | ||
28 | Object * proc_is_fixnum(Environment *env, Object *obj); | ||
29 | Object * proc_is_pair(Environment *env, Object *obj); | ||
30 | Object * proc_is_procedure(Environment *env, Object *obj); | ||
31 | Object * proc_is_error(Environment *env, Object *obj); | ||
32 | |||
33 | // Logical operations. | ||
34 | Object * proc_not(Environment *env, Object *obj); | ||
35 | Object * proc_and(Environment *env, Object *obj); | ||
36 | Object * proc_or(Environment *env, Object *obj); | ||
37 | Object * proc_cond(Environment *env, Object *obj); | ||
38 | Object * proc_num_less_than(Environment *env, Object *obj); | ||
39 | Object * proc_num_greater_than(Environment *env, Object *obj); | ||
40 | Object * proc_num_lesseq_than(Environment *env, Object *obj); | ||
41 | Object * proc_num_greatereq_than(Environment *env, Object *obj); | ||
42 | Object * proc_num_equal(Environment *env, Object *obj); | ||
43 | Object * proc_equal(Environment *env, Object *obj); | ||
44 | |||
45 | // List operations. | ||
46 | Object * proc_car(Environment *env, Object *obj); | ||
47 | Object * proc_cdr(Environment *env, Object *obj); | ||
48 | Object * proc_cons(Environment *env, Object *obj); | ||
49 | Object * proc_list(Environment *env, Object *obj); | ||
50 | |||
51 | // Environment/variable manipulation. | ||
52 | Object * proc_define(Environment *env, Object *obj); | ||
53 | Object * proc_set(Environment *env, Object *obj); | ||
54 | Object * proc_lambda(Environment *env, Object *obj); | ||
55 | Object * proc_fun(Environment *env, Object *obj); | ||
56 | |||
57 | // Runtinme configuration. | ||
58 | Object * proc_supress_errors(Environment *env, Object *obj); | ||
59 | |||
60 | #endif // BDL_PRIMITIVES_H | ||
diff --git a/src/treewalk/read_line.c b/src/treewalk/read_line.c deleted file mode 100644 index 03146ad..0000000 --- a/src/treewalk/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/treewalk/read_line.h b/src/treewalk/read_line.h deleted file mode 100644 index 160bce0..0000000 --- a/src/treewalk/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/treewalk/singletons.c b/src/treewalk/singletons.c deleted file mode 100644 index eb9c397..0000000 --- a/src/treewalk/singletons.c +++ /dev/null | |||
@@ -1,17 +0,0 @@ | |||
1 | #include "environment.h" | ||
2 | #include "gc.h" | ||
3 | #include "objects.h" | ||
4 | |||
5 | // Global garbage collector singleton. | ||
6 | static GC gc; | ||
7 | |||
8 | // Special singleton Objects. | ||
9 | static Object *obj_nil; | ||
10 | static Object *obj_true; | ||
11 | static Object *obj_false; | ||
12 | static Object *obj_err; | ||
13 | static Object *obj_quote; | ||
14 | static Object *proc_if; | ||
15 | |||
16 | // Global environment. | ||
17 | static Environment *global_env; | ||
diff --git a/src/treewalk/string_view.c b/src/treewalk/string_view.c deleted file mode 100644 index 39fabe9..0000000 --- a/src/treewalk/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, FILE *file) { | ||
37 | for (size_t i = 0; i < sv->n; i++) { | ||
38 | putc(sv->start[i], file); | ||
39 | } | ||
40 | } | ||
diff --git a/src/treewalk/string_view.h b/src/treewalk/string_view.h deleted file mode 100644 index 42273ab..0000000 --- a/src/treewalk/string_view.h +++ /dev/null | |||
@@ -1,21 +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, FILE *file); | ||
20 | |||
21 | #endif // BDL_STRINGVIEW_H | ||
diff --git a/src/x86_64/postlude.asm b/src/x86_64/postlude.asm deleted file mode 100644 index 45be7ee..0000000 --- a/src/x86_64/postlude.asm +++ /dev/null | |||
@@ -1,12 +0,0 @@ | |||
1 | |||
2 | _start_return: | ||
3 | ;; return the last value in the stack | ||
4 | pop rdi | ||
5 | call display | ||
6 | |||
7 | exit: | ||
8 | ;; exit syscall | ||
9 | mov rax, 60 | ||
10 | xor rdi, rdi | ||
11 | syscall | ||
12 | |||
diff --git a/src/x86_64/prelude.asm b/src/x86_64/prelude.asm deleted file mode 100644 index 81a2633..0000000 --- a/src/x86_64/prelude.asm +++ /dev/null | |||
@@ -1,134 +0,0 @@ | |||
1 | section .text | ||
2 | printdln: | ||
3 | sar rdi, FIXNUM_SHIFT | ||
4 | sub rsp, 40 | ||
5 | mov BYTE [rsp+31], 10 | ||
6 | test rdi, rdi | ||
7 | jne .L2 | ||
8 | mov BYTE [rsp+30], 48 | ||
9 | mov eax, 30 | ||
10 | mov r8d, 2 | ||
11 | .L3: | ||
12 | lea rsi, [rsp+rax] | ||
13 | mov rdx, r8 | ||
14 | mov rdi, 1 | ||
15 | mov rax, 1 | ||
16 | syscall | ||
17 | add rsp, 40 | ||
18 | ret | ||
19 | .L2: | ||
20 | mov r10d, 1 | ||
21 | js .L12 | ||
22 | .L4: | ||
23 | mov r8d, 1 | ||
24 | lea r9, [rsp+31] | ||
25 | mov rsi, -3689348814741910323 | ||
26 | .L5: | ||
27 | mov rax, rdi | ||
28 | mov rcx, r9 | ||
29 | mul rsi | ||
30 | sub rcx, r8 | ||
31 | shr rdx, 3 | ||
32 | lea rax, [rdx+rdx*4] | ||
33 | add rax, rax | ||
34 | sub rdi, rax | ||
35 | mov rax, r8 | ||
36 | add r8, 1 | ||
37 | add edi, 48 | ||
38 | mov BYTE [rcx], dil | ||
39 | mov rdi, rdx | ||
40 | test rdx, rdx | ||
41 | jne .L5 | ||
42 | cmp r10d, -1 | ||
43 | jne .L10 | ||
44 | not r8 | ||
45 | mov BYTE [rsp+32+r8], 45 | ||
46 | lea r8, [rax+2] | ||
47 | .L10: | ||
48 | mov eax, 32 | ||
49 | sub rax, r8 | ||
50 | jmp .L3 | ||
51 | .L12: | ||
52 | neg rdi | ||
53 | mov r10d, -1 | ||
54 | jmp .L4 | ||
55 | ret | ||
56 | |||
57 | printbool: | ||
58 | shr rdi, BOOL_SHIFT | ||
59 | cmp rdi, 0 | ||
60 | je print_false | ||
61 | mov rsi, true_str ; addr | ||
62 | mov rdx, 5 ; number of bytes | ||
63 | jmp bool_write | ||
64 | print_false: | ||
65 | mov rsi, false_str ; addr | ||
66 | mov rdx, 6 ; number of bytes | ||
67 | |||
68 | bool_write: | ||
69 | mov rax, 1 | ||
70 | mov rdi, 1 | ||
71 | syscall | ||
72 | ret | ||
73 | |||
74 | printstring: | ||
75 | mov rsi, rdi | ||
76 | mov rax, PTR_MASK | ||
77 | and rsi, rax | ||
78 | mov rdx, [rsi] | ||
79 | add rsi, 8 | ||
80 | mov rax, 1 | ||
81 | mov rdi, 1 | ||
82 | syscall | ||
83 | ret | ||
84 | |||
85 | printlambda: | ||
86 | mov rsi, lambda_str | ||
87 | mov rdx, [rsi] | ||
88 | add rsi, 8 | ||
89 | mov rax, 1 | ||
90 | mov rdi, 1 | ||
91 | syscall | ||
92 | ret | ||
93 | |||
94 | display: | ||
95 | ;; is nil? | ||
96 | mov rax, rdi | ||
97 | cmp rax, NIL_VAL | ||
98 | je display_end | ||
99 | |||
100 | ;; is boolean? | ||
101 | mov rax, rdi | ||
102 | and rax, BOOL_MASK | ||
103 | cmp rax, BOOL_TAG | ||
104 | jne not_bool | ||
105 | call printbool | ||
106 | ret | ||
107 | not_bool: | ||
108 | |||
109 | ;; is string? | ||
110 | mov rax, rdi | ||
111 | and rax, STRING_MASK | ||
112 | cmp rax, STRING_TAG | ||
113 | jne not_string | ||
114 | call printstring | ||
115 | ret | ||
116 | not_string: | ||
117 | |||
118 | ;; is lambda? | ||
119 | mov rax, rdi | ||
120 | and rax, LAMBDA_MASK | ||
121 | cmp rax, LAMBDA_TAG | ||
122 | jne not_lambda | ||
123 | call printlambda | ||
124 | ret | ||
125 | not_lambda: | ||
126 | |||
127 | ;; is fixnum? | ||
128 | mov rax, rdi | ||
129 | and rax, FIXNUM_MASK | ||
130 | cmp rax, FIXNUM_TAG | ||
131 | jne display_end | ||
132 | call printdln | ||
133 | display_end: | ||
134 | ret | ||