From ee1a5de91c875fb66724dc21c02333bfebe2a812 Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Tue, 1 Feb 2022 18:36:52 +0100 Subject: Add new syntax to lexer and prepare refactor --- src/bytecode/chunk.c | 47 --- src/bytecode/chunk.h | 36 --- src/bytecode/compiler.h | 748 --------------------------------------------- src/bytecode/darray.h | 81 ----- src/bytecode/debug.h | 105 ------- src/bytecode/errors.c | 52 ---- src/bytecode/errors.h | 46 --- src/bytecode/hashtable.h | 208 ------------- src/bytecode/lexer.c | 294 ------------------ src/bytecode/lexer.h | 92 ------ src/bytecode/main.c | 214 ------------- src/bytecode/objects.c | 151 --------- src/bytecode/objects.h | 80 ----- src/bytecode/ops.h | 46 --- src/bytecode/read_line.c | 32 -- src/bytecode/read_line.h | 10 - src/bytecode/string_view.c | 40 --- src/bytecode/string_view.h | 23 -- src/bytecode/types.h | 30 -- src/bytecode/vm.h | 396 ------------------------ 20 files changed, 2731 deletions(-) delete mode 100644 src/bytecode/chunk.c delete mode 100755 src/bytecode/chunk.h delete mode 100755 src/bytecode/compiler.h delete mode 100755 src/bytecode/darray.h delete mode 100755 src/bytecode/debug.h delete mode 100755 src/bytecode/errors.c delete mode 100755 src/bytecode/errors.h delete mode 100644 src/bytecode/hashtable.h delete mode 100755 src/bytecode/lexer.c delete mode 100755 src/bytecode/lexer.h delete mode 100755 src/bytecode/main.c delete mode 100644 src/bytecode/objects.c delete mode 100755 src/bytecode/objects.h delete mode 100755 src/bytecode/ops.h delete mode 100755 src/bytecode/read_line.c delete mode 100755 src/bytecode/read_line.h delete mode 100755 src/bytecode/string_view.c delete mode 100755 src/bytecode/string_view.h delete mode 100755 src/bytecode/types.h delete mode 100755 src/bytecode/vm.h (limited to 'src/bytecode') 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 @@ -#include "chunk.h" -#include "objects.h" - -Chunk * -chunk_init(StringView name) { - Chunk *chunk = malloc(sizeof(Chunk)); - array_init(chunk->code, 0); - array_init(chunk->constants, 0); - array_init(chunk->lines, 0); - array_init(chunk->name, name.n); - array_insert(chunk->name, name.start, name.n); - chunk->n_params = 0; - chunk->n_locals = 0; - return chunk; -} - -void -chunk_free(Chunk *chunk) { - array_free(chunk->code); - for (size_t i = 0; i < array_size(chunk->constants); i++) { - Object obj = chunk->constants[i]; - object_free(&obj); - } - array_free(chunk->constants); - array_free(chunk->lines); - array_free(chunk->name); - free(chunk); -} - -void -add_code(Chunk *chunk, u8 byte, size_t line, size_t col) { - array_push(chunk->code, byte); - LineInfo info = (LineInfo){line, col}; - array_push(chunk->lines, info); -} - -size_t -add_constant(Chunk *chunk, Object obj) { - size_t pos = array_size(chunk->constants); - for (size_t i = 0; i < pos; i++) { - if (object_equal(obj, chunk->constants[i])) { - return i; - } - } - array_push(chunk->constants, obj); - return pos; -} 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 @@ -#ifndef BDL_CHUNK_H -#define BDL_CHUNK_H - -#include "darray.h" -#include "string_view.h" - -typedef struct Object Object; - -typedef struct LineInfo { - size_t line; - size_t col; -} LineInfo; - -typedef struct Chunk { - // Program code. - u8 *code; - // Compile time constants. - Object *constants; - // Contains debugging information for every code operation. - LineInfo *lines; - // Chunk name. - char *name; - - // Number of locals and parameters. - size_t n_params; - size_t n_locals; -} Chunk; - -#define NEW_CHUNK(NAME) chunk_init((StringView){(NAME), sizeof(NAME) - 1}) - -Chunk * chunk_init(StringView name); -void add_code(Chunk *chunk, u8 byte, size_t line, size_t col); -size_t add_constant(Chunk *chunk, Object obj); -void chunk_free(Chunk *chunk); - -#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 @@ -#ifndef BDL_COMPILER_H -#define BDL_COMPILER_H - -#include "chunk.h" -#include "lexer.h" - -#define MAX_DEPTH 1024 - -typedef struct Scope { - StringView *params; - StringView *locals; - Token *captured; -} Scope; - -typedef struct Compiler { - Token *tokens; - size_t current; - size_t scope_depth; - Scope scopes[MAX_DEPTH]; -} Compiler; - - -// Mimics the functionality in the Scanner functions, but for entire tokens. -Token next_token(Compiler *compiler); -Token peek_token(const Compiler *compiler); -bool has_next_token(const Compiler *compiler); - -// Scope initialization/exit. -void enter_scope(Compiler *compiler); -void exit_scope(Compiler *compiler); - -void -enter_scope(Compiler *compiler) { - Scope *scope = &compiler->scopes[compiler->scope_depth++]; - array_init(scope->params, 0); - array_init(scope->locals, 0); - array_init(scope->captured, 0); -} - -void -exit_scope(Compiler *compiler) { - Scope *scope = &compiler->scopes[--compiler->scope_depth]; - array_free(scope->params); - array_free(scope->locals); - array_free(scope->captured); -} - -Scope * -get_current_scope(Compiler *compiler) { - return &compiler->scopes[compiler->scope_depth - 1]; -} - -Chunk * compile(Token *tokens); - -Token -peek_token(const Compiler *compiler) { - return compiler->tokens[compiler->current]; -} - -Token -next_token(Compiler *compiler) { - return compiler->tokens[compiler->current++]; -} - -bool -has_next_token(const Compiler *compiler) { - return compiler->current < array_size(compiler->tokens); -} - -ssize_t -find_local_index(Scope *scope, Token tok) { - for (size_t i = 0; i < array_size(scope->locals); i++) { - if (sv_equal(&tok.value, &scope->locals[i])) { - return i; - } - } - return -1; -} - -ssize_t -find_captued_index(Scope *scope, Token tok) { - for (size_t i = 0; i < array_size(scope->captured); i++) { - if (sv_equal(&tok.value, &scope->captured[i].value)) { - return i; - } - } - return -1; -} - -void -emit_constant(Chunk *chunk, Token tok, Object obj) { - size_t prev_size = array_size(chunk->constants); - size_t num_idx = add_constant(chunk, obj); - add_code(chunk, OP_CONSTANT, tok.line, tok.column); - add_code(chunk, num_idx, tok.line, tok.column); - - // If the non value constant was already present we need to properly free - // the memory from the object given to this function. - if (prev_size == array_size(chunk->constants)) { - object_free(&obj); - } -} - -size_t -emit_jump(Chunk *chunk, Token tok, Ops op, ssize_t offset) { - add_code(chunk, op, tok.line, tok.column); - add_code(chunk, ((u16)offset >> 8) & 0xFF, tok.line, tok.column); - add_code(chunk, ((u16)offset) & 0xFF, tok.line, tok.column); - return array_size(chunk->code) - 2; -} - -void -patch_jump(Chunk *chunk, ssize_t offset) { - ssize_t jump = array_size(chunk->code) - offset - 2; - assert((u16)jump <= UINT16_MAX && "error: jump is too long"); - chunk->code[offset] = ((u16)jump >> 8) & 0xFF; - chunk->code[offset + 1] = ((u16)jump) & 0xFF; -} - -void -parse_fixnum(Chunk *chunk, Token tok) { - ssize_t num = 0; - int sign = 1; - for (size_t i = 0; i < tok.value.n; i++) { - char c = tok.value.start[i]; - if (c == '-') { - sign = -1; - continue; - } - num = num * 10 + (c - '0'); - } - emit_constant(chunk, tok, FIXNUM_VAL(num * sign)); -} - -void -parse_symbol(Chunk *chunk, Compiler *compiler, Token tok) { - if (compiler->scope_depth > 1) { - Scope *current_scope = get_current_scope(compiler); - ssize_t idx = -1; - // Check if the variable was already captured. - idx = find_captued_index(current_scope, tok); - if (idx >= 0) { - emit_constant(chunk, tok, FIXNUM_VAL(idx)); - add_code(chunk, OP_CAPTURED, tok.line, tok.column); - return; - } - - // Check current scope locals. If we find it, emit OP_LOCAL. - idx = find_local_index(current_scope, tok); - if (idx >= 0) { - emit_constant(chunk, tok, FIXNUM_VAL(idx)); - add_code(chunk, OP_LOCAL, tok.line, tok.column); - return; - } - - // Descend scopes, if we find the symbol at a different depth, - // we need to capture the variable. - size_t depth = compiler->scope_depth - 2; - while (depth > 0) { - Scope *scope = &compiler->scopes[depth]; - idx = find_local_index(scope, tok); - if (idx >= 0) { - // Put captured variable on stack. - ssize_t captured_idx = array_size(current_scope->captured); - emit_constant(chunk, tok, FIXNUM_VAL(captured_idx)); - add_code(chunk, OP_CAPTURED, tok.line, tok.column); - array_push(current_scope->captured, tok); - return; - } - depth--; - } - - // TODO: Capture globals? - } - - Object obj = make_symbol(tok.value); - emit_constant(chunk, tok, obj); - add_code(chunk, OP_GET, tok.line, tok.column); -} - -void parse_tree(Chunk *chunk, Compiler *compiler); - -void -compile_list_binary_op(Chunk *chunk, Compiler *compiler, Token start, Ops op) { - size_t n = 0; - while (has_next_token(compiler)) { - Token tok = peek_token(compiler); - if (tok.type == TOKEN_EOF) { - error_push((Error){ - .type = ERR_TYPE_COMPILER, - .value = ERR_UNBALANCED_PAREN, - .line = start.line, - .col = start.column, - }); - return; - } - if (tok.type == TOKEN_RPAREN) { - next_token(compiler); - if (n <= 1) { - error_push((Error){ - .type = ERR_TYPE_COMPILER, - .value = ERR_NOT_ENOUGH_ARGS, - .line = start.line, - .col = start.column, - }); - return; - } - break; - } - parse_tree(chunk, compiler); - n++; - } - emit_constant(chunk, start, FIXNUM_VAL(n)); - add_code(chunk, op, start.line, start.column); -} - -void -compile_list_unary_op(Chunk *chunk, Compiler *compiler, Token start, Ops op) { - size_t n = 0; - while (has_next_token(compiler)) { - Token tok = peek_token(compiler); - if (tok.type == TOKEN_EOF) { - error_push((Error){ - .type = ERR_TYPE_COMPILER, - .value = ERR_UNBALANCED_PAREN, - .line = start.line, - .col = start.column, - }); - return; - } - if (tok.type == TOKEN_RPAREN) { - next_token(compiler); - if (n == 0) { - error_push((Error){ - .type = ERR_TYPE_COMPILER, - .value = ERR_NOT_ENOUGH_ARGS, - .line = start.line, - .col = start.column, - }); - } - return; - } - parse_tree(chunk, compiler); - add_code(chunk, op, start.line, start.column); - n++; - if (n > 1) { - error_push((Error){ - .type = ERR_TYPE_COMPILER, - .value = ERR_TOO_MANY_ARGS, - .line = start.line, - .col = start.column, - }); - } - } - error_push((Error){ - .type = ERR_TYPE_COMPILER, - .value = ERR_UNBALANCED_PAREN, - .line = start.line, - .col = start.column, - }); -} - -void -compile_list_simple_op(Chunk *chunk, Compiler *compiler, Token start, Ops op) { - if (has_next_token(compiler)) { - Token tok = peek_token(compiler); - if (tok.type == TOKEN_EOF) { - error_push((Error){ - .type = ERR_TYPE_COMPILER, - .value = ERR_UNBALANCED_PAREN, - .line = start.line, - .col = start.column, - }); - return; - } - if (tok.type != TOKEN_RPAREN) { - error_push((Error){ - .type = ERR_TYPE_COMPILER, - .value = ERR_TOO_MANY_ARGS, - .line = start.line, - .col = start.column, - }); - return; - } - next_token(compiler); - add_code(chunk, op, start.line, start.column); - return; - } - error_push((Error){ - .type = ERR_TYPE_COMPILER, - .value = ERR_UNBALANCED_PAREN, - .line = start.line, - .col = start.column, - }); -} - -#define STR_ARRAY(ARR) (StringView){(ARR), array_size(ARR)} - -void -compile_declare_op(Chunk *chunk, Compiler *compiler, Token start, Ops op) { - Token name = next_token(compiler); - if (name.type != TOKEN_SYMBOL) { - error_push((Error){ - .type = ERR_TYPE_COMPILER, - .value = ERR_WRONG_ARG_TYPE, - .line = start.line, - .col = start.column, - }); - return; - } - - if (compiler->scope_depth <= 1) { - Object obj = make_symbol(name.value); - emit_constant(chunk, name, obj); - } else { - if (op == OP_DEF) { - op = OP_DEF_LOCAL; - // Check if the local is already registered. - Scope *scope = get_current_scope(compiler); - ssize_t idx = find_local_index(scope, name); - if (idx < 0) { - array_push(scope->locals, name.value); - idx = chunk->n_locals++; - } - emit_constant(chunk, name, FIXNUM_VAL(idx)); - } else if (op == OP_SET) { - // FIXME: This is fucking ugly. - Scope *current_scope = get_current_scope(compiler); - ssize_t idx = -1; - // Check if the variable was already captured. - idx = find_captued_index(current_scope, name); - if (idx >= 0) { - emit_constant(chunk, name, FIXNUM_VAL(idx)); - op = OP_SET_CAPTURED; - } else { - idx = find_local_index(current_scope, name); - if (idx >= 0) { - emit_constant(chunk, name, FIXNUM_VAL(idx)); - op = OP_SET_LOCAL; - } else { - size_t depth = compiler->scope_depth - 2; - while (depth > 0) { - Scope *scope = &compiler->scopes[depth]; - idx = find_local_index(scope, name); - if (idx >= 0) { - op = OP_SET_CAPTURED; - ssize_t captured_idx = array_size(current_scope->captured); - emit_constant(chunk, name, FIXNUM_VAL(captured_idx)); - array_push(current_scope->captured, name); - break; - } - depth--; - } - if (idx < 0) { - Object obj = make_symbol(name.value); - emit_constant(chunk, name, obj); - } - } - } - } - } - // NOTE: We can have compiler support for preemptively finding if globals - // exist or not. - - Token tok = peek_token(compiler); - if (name.type == TOKEN_EOF || tok.type == TOKEN_EOF) { - error_push((Error){ - .type = ERR_TYPE_COMPILER, - .value = ERR_UNBALANCED_PAREN, - .line = start.line, - .col = start.column, - }); - return; - } - if (name.type == TOKEN_RPAREN || tok.type == TOKEN_RPAREN) { - error_push((Error){ - .type = ERR_TYPE_COMPILER, - .value = ERR_NOT_ENOUGH_ARGS, - .line = start.line, - .col = start.column, - }); - return; - } - parse_tree(chunk, compiler); - if (peek_token(compiler).type != TOKEN_RPAREN) { - error_push((Error){ - .type = ERR_TYPE_COMPILER, - .value = ERR_TOO_MANY_ARGS, - .line = start.line, - .col = start.column, - }); - return; - } - next_token(compiler); - add_code(chunk, op, start.line, start.column); -} - -void -compile_lambda(Chunk *chunk, Compiler *compiler, Token start, StringView name) { - enter_scope(compiler); - Chunk *proc_chunk = chunk_init(name); - Object fun = make_lambda(proc_chunk); - - // Prepeare parameters. - Token tok = next_token(compiler); - if (tok.type == TOKEN_LPAREN){ - while (has_next_token(compiler)) { - Token tok = next_token(compiler); - if (tok.type == TOKEN_EOF) { - error_push((Error){ - .type = ERR_TYPE_COMPILER, - .value = ERR_UNBALANCED_PAREN, - .line = start.line, - .col = start.column, - }); - return; - } - if (tok.type == TOKEN_RPAREN) { - break; - } - if (tok.type != TOKEN_SYMBOL) { - error_push((Error){ - .type = ERR_TYPE_COMPILER, - .value = ERR_WRONG_ARG_TYPE, - .line = tok.line, - .col = tok.column, - }); - return; - } - // Check if parameters name already exists. - Scope *scope = get_current_scope(compiler); - ssize_t idx = find_local_index(scope, tok); - if (idx >= 0) { - error_push((Error){ - .type = ERR_TYPE_COMPILER, - .value = ERR_AMBIGUOUS_PARAMS, - .line = tok.line, - .col = tok.column, - }); - return; - } - array_push(scope->params, tok.value); - array_push(scope->locals, tok.value); - fun.closure->chunk->n_params++; - fun.closure->chunk->n_locals++; - } - } else if (tok.type != TOKEN_NIL) { - error_push((Error){ - .type = ERR_TYPE_COMPILER, - .value = ERR_WRONG_ARG_TYPE, - .line = tok.line, - .col = tok.column, - }); - return; - } - - // Compile body. - while (has_next_token(compiler)) { - Token tok = peek_token(compiler); - if (tok.type == TOKEN_EOF) { - error_push((Error){ - .type = ERR_TYPE_COMPILER, - .value = ERR_UNBALANCED_PAREN, - .line = start.line, - .col = start.column, - }); - return; - } - if (tok.type == TOKEN_RPAREN) { - next_token(compiler); - break; - } - parse_tree(fun.closure->chunk, compiler); - } - add_code(fun.closure->chunk, OP_RETURN, start.line, start.column); - - // Prepare closure value capture. - Scope *scope = get_current_scope(compiler); - size_t n_captured = array_size(scope->captured); - if (n_captured > 0) { - compiler->scope_depth--; - for (size_t i = 0; i < n_captured; i++) { - Token tok = scope->captured[i]; - parse_symbol(chunk, compiler, tok); - } - // Number of captured values. - emit_constant(chunk, tok, FIXNUM_VAL(n_captured)); - - // Push created lambda to stack. - emit_constant(chunk, start, fun); - - // Emit capture local instruction. - add_code(chunk, OP_CAPTURE_LOCAL, tok.line, tok.column); - compiler->scope_depth++; - } else { - emit_constant(chunk, start, fun); - } - exit_scope(compiler); -} - -void -compile_fun_op(Chunk *chunk, Compiler *compiler, Token start) { - Token name = peek_token(compiler); - if (name.type != TOKEN_SYMBOL) { - error_push((Error){ - .type = ERR_TYPE_COMPILER, - .value = ERR_WRONG_ARG_TYPE, - .line = start.line, - .col = start.column, - }); - return; - } - Object obj = make_symbol(name.value); - emit_constant(chunk, name, obj); - next_token(compiler); - - compile_lambda(chunk, compiler, start, name.value); - add_code(chunk, OP_DEF, start.line, start.column); -} - -void -compile_call_op(Chunk *chunk, Compiler *compiler, Token start, Token name) { - if (name.type == TOKEN_SYMBOL) { - parse_symbol(chunk, compiler, name); - } - - // Compile body. - size_t n = 0; - while (has_next_token(compiler)) { - Token tok = peek_token(compiler); - if (tok.type == TOKEN_EOF) { - error_push((Error){ - .type = ERR_TYPE_COMPILER, - .value = ERR_UNBALANCED_PAREN, - .line = start.line, - .col = start.column, - }); - return; - } - if (tok.type == TOKEN_RPAREN) { - next_token(compiler); - break; - } - parse_tree(chunk, compiler); - n++; - } - emit_constant(chunk, start, FIXNUM_VAL(n)); - add_code(chunk, OP_CALL, start.line, start.column); -} - -void -compile_if_op(Chunk *chunk, Compiler *compiler, Token start) { - Token tok = peek_token(compiler); - if (tok.type == TOKEN_EOF) { - error_push((Error){ - .type = ERR_TYPE_COMPILER, - .value = ERR_UNBALANCED_PAREN, - .line = start.line, - .col = start.column, - }); - return; - } - if (tok.type == TOKEN_RPAREN) { - error_push((Error){ - .type = ERR_TYPE_COMPILER, - .value = ERR_NOT_ENOUGH_ARGS, - .line = start.line, - .col = start.column, - }); - return; - } - - // Condition. - parse_tree(chunk, compiler); - size_t jmp_false = emit_jump(chunk, start, OP_JUMP_IF_FALSE, 0xFFFF); - - // True expression. - parse_tree(chunk, compiler); - - // No second expression. - if (peek_token(compiler).type == TOKEN_RPAREN) { - patch_jump(chunk, jmp_false); - next_token(compiler); - return; - } - - // False expression. - size_t jmp_end = emit_jump(chunk, start, OP_JUMP, 0xFFFF); - patch_jump(chunk, jmp_false); - parse_tree(chunk, compiler); - patch_jump(chunk, jmp_end); - - if (peek_token(compiler).type != TOKEN_RPAREN) { - error_push((Error){ - .type = ERR_TYPE_COMPILER, - .value = ERR_TOO_MANY_ARGS, - .line = start.line, - .col = start.column, - }); - return; - } - next_token(compiler); -} - -void -parse_list(Chunk *chunk, Compiler *compiler, Token start) { - if (!has_next_token(compiler)) { - error_push((Error){ - .type = ERR_TYPE_COMPILER, - .value = ERR_UNBALANCED_PAREN, - .line = start.line, - .col = start.column, - }); - return; - } - while (has_next_token(compiler)) { - Token tok = next_token(compiler); - if (tok.type == TOKEN_LPAREN) { - parse_list(chunk, compiler, tok); - } - switch (tok.type) { - case TOKEN_ADD: { compile_list_binary_op(chunk, compiler, start, OP_SUM); } break; - case TOKEN_SUB: { compile_list_binary_op(chunk, compiler, start, OP_SUB); } break; - case TOKEN_MUL: { compile_list_binary_op(chunk, compiler, start, OP_MUL); } break; - case TOKEN_DIV: { compile_list_binary_op(chunk, compiler, start, OP_DIV); } break; - case TOKEN_MOD: { compile_list_binary_op(chunk, compiler, start, OP_MOD); } break; - case TOKEN_NOT: { compile_list_unary_op(chunk, compiler, start, OP_NOT); } break; - case TOKEN_AND: { compile_list_binary_op(chunk, compiler, start, OP_AND); } break; - case TOKEN_OR: { compile_list_binary_op(chunk, compiler, start, OP_OR); } break; - case TOKEN_EQUAL: { compile_list_binary_op(chunk, compiler, start, OP_EQUAL); } break; - case TOKEN_LESS: { compile_list_binary_op(chunk, compiler, start, OP_LESS); } break; - case TOKEN_GREATER: { compile_list_binary_op(chunk, compiler, start, OP_GREATER); } break; - case TOKEN_LESS_EQUAL: { compile_list_binary_op(chunk, compiler, start, OP_LESS_EQUAL); } break; - case TOKEN_GREATER_EQUAL: { compile_list_binary_op(chunk, compiler, start, OP_GREATER_EQUAL); } break; - case TOKEN_PRINT: { compile_list_unary_op(chunk, compiler, start, OP_PRINT); } break; - case TOKEN_DISPLAY: { compile_list_unary_op(chunk, compiler, start, OP_DISPLAY); } break; - case TOKEN_NEWLINE: { - compile_list_simple_op(chunk, compiler, start, OP_NEWLINE); - emit_constant(chunk, start, NIL_VAL); - } break; - case TOKEN_DEF: { compile_declare_op(chunk, compiler, start, OP_DEF); } break; - case TOKEN_SET: { compile_declare_op(chunk, compiler, start, OP_SET); } break; - case TOKEN_FUN: { compile_fun_op(chunk, compiler, start); } break; - case TOKEN_LAMBDA: { compile_lambda(chunk, compiler, start, STRING("lambda")); } break; - case TOKEN_IF: { compile_if_op(chunk, compiler, start); } break; - default: { - compile_call_op(chunk, compiler, start, tok); - } break; - } - return; - } -} - -void -parse_tree(Chunk *chunk, Compiler *compiler) { - Token tok = next_token(compiler); - if (errors_n != 0) { - return; - } - switch (tok.type) { - case TOKEN_FIXNUM: { - parse_fixnum(chunk, tok); - return ; - } break; - case TOKEN_TRUE: { - emit_constant(chunk, tok, TRUE_VAL); - return; - } break; - case TOKEN_FALSE: { - emit_constant(chunk, tok, FALSE_VAL); - return; - } break; - case TOKEN_RPAREN: { - error_push((Error){ - .type = ERR_TYPE_COMPILER, - .value = ERR_UNBALANCED_PAREN, - .line = tok.line, - .col = tok.column, - }); - return; - } break; - case TOKEN_QUOTE: { - // Object *base = make_pair(obj_quote, obj_nil); - // base->cdr = make_pair(obj_nil, obj_nil); - // push_root(base); - // Object *next_obj = parse_tree(compiler); - // if (next_obj == obj_err) { - // return obj_err; - // } - // base->cdr->car = next_obj; - // return base; - return; - } break; - case TOKEN_LPAREN: { - parse_list(chunk, compiler, tok); - return; - } break; - case TOKEN_STRING: { - Object obj = make_string(tok.value); - emit_constant(chunk, tok, obj); - return; - } break; - case TOKEN_SYMBOL: { - parse_symbol(chunk, compiler, tok); - return; - } break; - case TOKEN_NIL: { - emit_constant(chunk, tok, NIL_VAL); - return; - } break; - default: { - break; - } break; - } - error_push((Error){ - .type = ERR_TYPE_COMPILER, - .value = ERR_EOF_REACHED, - .line = tok.line, - .col = tok.column, - }); - return; -} - -Chunk * -compile(Token *tokens) { - Chunk *chunk = NULL; - chunk = NEW_CHUNK("main"); - Compiler compiler = (Compiler){ - .tokens = tokens, - .current = 0, - .scope_depth = 0, - }; - enter_scope(&compiler); - Token main_start = peek_token(&compiler); - while (has_next_token(&compiler)) { - Token start = peek_token(&compiler); - parse_tree(chunk, &compiler); - if (peek_token(&compiler).type == TOKEN_EOF) { - break; - } - add_code(chunk, OP_DROP, start.line, start.column); - } - add_code(chunk, OP_RETURN, main_start.line, main_start.column); - exit_scope(&compiler); - return chunk; -} - -#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 @@ -#ifndef BDL_DARRAY_H -#define BDL_DARRAY_H - -#include - -typedef struct ArrayHeader { - size_t size; - size_t cap; -} ArrayHeader; - -// Header/Size/capacity accessors. -#define array_head(ARR) ((ArrayHeader *)((char *)(ARR) - sizeof(ArrayHeader))) -#define array_size(ARR) ((ARR) ? array_head(ARR)->size : 0) -#define array_cap(ARR) ((ARR) ? array_head(ARR)->cap : 0) - -// Initialize a dynamic array ARR with N elements. The initialization doesn't -// zero out the data, so thread carefully.. -#define array_init(ARR,N) ((ARR) = _array_reserve(N, sizeof(*(ARR)))) - -// Push a given element T to the dynamic array ARR. -#define array_push(ARR, T) \ - ((ARR) = _array_maybe_grow(ARR, sizeof(T)), \ - (ARR)[array_head(ARR)->size++] = (T)) - -// Return the last element of the array. Can be used to build stacks. -#define array_pop(ARR) (ARR)[--array_head(ARR)->size] - -// Return the value stored at the OFFSET position from the tail of the array. -#define array_peek(ARR, OFFSET) (ARR)[array_head(ARR)->size - 1 - (OFFSET)] - -// Insert N bytes from the SRC array into the ARR dynamic array. -#define array_insert(ARR, SRC, N) \ - ((ARR) = _array_insert(ARR, SRC, N, sizeof(*(ARR)))) - -// Free the memory from the original allocated position. -#define array_free(ARR) ((ARR) ? free(array_head(ARR)), (ARR) = NULL : 0) - -static inline void * -_array_reserve(size_t num_elem, size_t type_size) { - char *p = malloc(num_elem * type_size + sizeof(ArrayHeader)); - p += sizeof(ArrayHeader); - array_head(p)->size = 0; - array_head(p)->cap = num_elem; - return p; -} - -static inline void * -_array_maybe_grow(void *arr, size_t type_size) { - ArrayHeader *head = array_head(arr); - if (head->cap == head->size) { - if (head->cap == 0) { - head->cap++; - } else { - head->cap *= 2; - } - head = realloc(head, head->cap * type_size + sizeof(ArrayHeader)); - } - arr = (char *)head + sizeof(ArrayHeader); - return arr; -} - -static inline -char * _array_insert(char *arr, const char *src, size_t n_bytes, size_t type_size) { - ArrayHeader *head = array_head(arr); - size_t new_size = n_bytes + head->size; - if (new_size > head->cap * type_size) { - if (head->cap == 0) { - head->cap = 1; - } - while (new_size >= head->cap * type_size) { - head->cap *= 2; - } - head = realloc(head, head->cap * type_size + sizeof(ArrayHeader)); - } - arr = (char *)head + sizeof(ArrayHeader); - memcpy((arr + head->size), src, n_bytes); - head->size = new_size; - return arr; -} - -#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 @@ -#ifndef BDL_DEBUG_H -#define BDL_DEBUG_H - -#include "chunk.h" -#include "objects.h" - -void disassemble_chunk(Chunk *chunk); -size_t disassemble_instruction(Chunk *chunk, size_t offset); - -static const char* ops_str[] = { - // Load/store ops. - [OP_CONSTANT] = "OP_CONSTANT", - [OP_LOCAL] = "OP_LOCAL", - [OP_CAPTURED] = "OP_CAPTURED", - [OP_CAPTURE_LOCAL] = "OP_CAPTURE_LOCAL", - [OP_SET_CAPTURED] = "OP_SET_CAPTURED", - [OP_DEF_LOCAL] = "OP_DEF_LOCAL", - [OP_SET_LOCAL] = "OP_SET_LOCAL", - [OP_DEF] = "OP_DEF", - [OP_SET] = "OP_SET", - [OP_GET] = "OP_GET", - // Arithmetic ops. - [OP_SUM] = "OP_SUM", - [OP_SUB] = "OP_SUB", - [OP_MUL] = "OP_MUL", - [OP_DIV] = "OP_DIV", - [OP_MOD] = "OP_MOD", - // Logic ops. - [OP_NOT] = "OP_NOT", - [OP_AND] = "OP_AND", - [OP_OR] = "OP_OR", - // Numerical comparison ops. - [OP_EQUAL] = "OP_EQUAL", - [OP_LESS] = "OP_LESS", - [OP_GREATER] = "OP_GREATER", - [OP_LESS_EQUAL] = "OP_LESS_EQUAL", - [OP_GREATER_EQUAL] = "OP_GREATER_EQUAL", - // Jump/conditional ops. - [OP_JUMP] = "OP_JUMP", - [OP_JUMP_IF_FALSE] = "OP_JUMP_IF_FALSE", - // Display ops. - [OP_DISPLAY] = "OP_DISPLAY", - [OP_PRINT] = "OP_PRINT", - [OP_NEWLINE] = "OP_NEWLINE", - // Procedures. - [OP_CALL] = "OP_CALL", - [OP_RETURN] = "OP_RETURN", - // Clear stack after each statement. - [OP_DROP] = "OP_DROP", -}; - -void -disassemble_chunk(Chunk *chunk) { - printf("===== %.*s =====\n", (int)array_size(chunk->name), chunk->name); - printf("code:\n"); - size_t offset = 0; - while (offset < array_size(chunk->code)) { - offset = disassemble_instruction(chunk, offset); - } - printf("\nconstants:\n"); - offset = 0; - while (offset < array_size(chunk->constants)) { - printf("\t%03ld -> ", offset); - object_display(chunk->constants[offset]); - printf("\n"); - offset++; - } - printf("\n"); -} - -size_t -disassemble_instruction(Chunk *chunk, size_t offset) { - printf("\t%04ld ", offset); - if (offset > 0 - && chunk->lines[offset].line == chunk->lines[offset - 1].line - && chunk->lines[offset].col == chunk->lines[offset - 1].col) { - printf("%4s|%-4s ", " ", " "); - } else { - printf("%4ld:%-4ld ", chunk->lines[offset].line, chunk->lines[offset].col); - } - u8 instruction = chunk->code[offset]; - switch (instruction) { - case OP_CONSTANT: { - u8 constant = chunk->code[offset + 1]; - printf("%-16s %4d -> ", ops_str[instruction], constant); - object_display(chunk->constants[constant]); - printf("\n"); - return offset + 2; - } break; - case OP_JUMP: - case OP_JUMP_IF_FALSE: { - u16 a = chunk->code[offset + 1]; - u16 b = chunk->code[offset + 2]; - s16 jmp = (a << 8) | b; - printf("%-16s %4d\n", ops_str[instruction], jmp); - return offset + 3; - } break; - default: { - printf("%s\n", ops_str[instruction]); - return offset + 1; - } break; - } -} - -#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 @@ -#include "errors.h" - -static const char* error_msgs[] = { - [ERR_UNKNOWN] = "error: something unexpected happened", - [ERR_UNMATCHED_STRING] = "error: unmatched string delimiter", - [ERR_UNBALANCED_PAREN] = "error: unbalanced parentheses", - [ERR_NOT_IMPLEMENTED] = "error: not implemented", - [ERR_EOF_REACHED] = "error: EOF reached", - [ERR_UNKNOWN_TOKEN] = "error: unknown token", - [ERR_UNKNOWN_OBJ_TYPE] = "error: can't eval unknown object type", - [ERR_NOT_A_SYMBOL] = "error: object is not a symbol", - [ERR_SYMBOL_NOT_FOUND] = "error: symbol not found", - [ERR_OBJ_NOT_CALLABLE] = "error: object is not callable", - [ERR_NOT_ENOUGH_ARGS] = "error: not enough arguments", - [ERR_TOO_MANY_ARGS] = "error: too many arguments", - [ERR_WRONG_ARG_TYPE] = "error: wrong argument type", - [ERR_DIVISION_BY_ZERO] = "error: division by zero", - [ERR_PC_OOB] = "error: pc out of bounds", - [ERR_EMPTY_CHUNK] = "error: empty chunk", - [ERR_AMBIGUOUS_PARAMS] = "error: ambiguous parameter names", -}; - -static Error errors[ERR_MAX_NUMBER]; -static size_t errors_n = 0; -static bool supress_errors = false; - -void -error_push(Error error) { - if (errors_n < ERR_MAX_NUMBER) { - errors[errors_n++] = error; - } -} - -void -report_errors(char *file_name) { - for (size_t i = 0; i < errors_n; i++) { - Error err = errors[i]; - fprintf(stderr, "%s", file_name); - if (err.line != 0) { - fprintf(stderr, ":%ld:%ld", err.line, err.col); - } - switch (err.type) { - case ERR_TYPE_LEXER: { fprintf(stderr, ": [lexer]"); } break; - case ERR_TYPE_COMPILER: { fprintf(stderr, ": [compiler]"); } break; - case ERR_TYPE_RUNTIME: { fprintf(stderr, ": [runtime]"); } break; - case ERR_TYPE_PARSER: { fprintf(stderr, ": [parser]"); } break; - default: break; - } - fprintf(stderr, " %s\n", error_msgs[err.value]); - } - errors_n = 0; -} 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 @@ -#ifndef BDL_ERRORS_H -#define BDL_ERRORS_H - -typedef enum ErrorType { - ERR_TYPE_OK, - ERR_TYPE_LEXER, - ERR_TYPE_PARSER, - ERR_TYPE_COMPILER, - ERR_TYPE_RUNTIME, -} ErrorType; - -typedef enum ErrorValue { - ERR_UNKNOWN = 0, - ERR_UNMATCHED_STRING, - ERR_UNBALANCED_PAREN, - ERR_NOT_IMPLEMENTED, - ERR_EOF_REACHED, - ERR_UNKNOWN_TOKEN, - ERR_UNKNOWN_OBJ_TYPE, - ERR_NOT_A_SYMBOL, - ERR_SYMBOL_NOT_FOUND, - ERR_OBJ_NOT_CALLABLE, - ERR_NOT_ENOUGH_ARGS, - ERR_TOO_MANY_ARGS, - ERR_WRONG_ARG_TYPE, - ERR_DIVISION_BY_ZERO, - ERR_AMBIGUOUS_PARAMS, - - // Bytecode interpreter. - ERR_PC_OOB, - ERR_EMPTY_CHUNK, -} ErrorValue; - -typedef struct Error { - ErrorType type; - ErrorValue value; - size_t line; - size_t col; -} Error; - -void error_push(Error error); -void report_errors(char *file_name); - -#define ERR_MAX_NUMBER 16 - -#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 @@ -#ifndef BDL_HASHTABLE_H -#define BDL_HASHTABLE_H - -#include "darray.h" -#include "objects.h" - -// Minimum table capacity. -#define HT_MIN_CAP 4 -#define HT_MIN_SHIFT 2 - -// Adjust the load factor threshold at which the table will grow on insertion. -#define HT_LOAD_THRESHOLD 0.8 - -typedef struct HashTablePair { - Object *key; - Object *value; -} HashTablePair; - -struct HashTable; -typedef uint64_t (HashFunction)(const struct HashTable *table, void *bytes); - -typedef struct HashTable { - // All available key-value pairs as a dynamic array. - HashTablePair *pairs; - - // Internal storage. - Object *keys; - Object *values; - - // Hash function. - HashFunction *hash_func; - - // This table expects the number of buckets to grow in powers of two. To - // speedup the default hashing, we memoize the number of bits equivalent to - // that power of 2: - // - // cap := 1024 = 2 ^ 10, shift_amount := 10 - // - uint8_t shift_amount; -} HashTable; - -// Hash a byte stream using a circular shift + XOR hash function. -static inline uint64_t -_xor_shift_hash(const char *key, size_t n) { - uint64_t hash = 0x65d9d65f6a19574f; - char *last = (char *)key + n; - while (key != last) { - hash ^= (uint64_t)*key++; - hash = (hash << 8) | (hash >> (64 - 8)); - } - return hash; -} - -// Use Fibonacci hashing to map a hash to a value in range of the table. -static inline uint64_t -_fibonacci_hash(uint64_t hash, size_t shift_amount) { - return (hash * UINT64_C(11400714819323198485)) >> (64 - shift_amount); -} - -uint64_t -_symbol_hash(const HashTable *table, void *key) { - Object *obj = key; - uint64_t hash = _xor_shift_hash(obj->text, array_size(obj->text)); - hash = _fibonacci_hash(hash, table->shift_amount); - return hash; -} - -static inline float -ht_load_factor(const HashTable *table) { - return (float)array_size(table->pairs) / (float)array_cap(table->pairs); -} - -HashTable * -ht_init(void) { - HashTable *table = malloc(sizeof(HashTable)); - *table = (HashTable){0}; - array_init(table->pairs, HT_MIN_CAP); - array_init(table->keys, HT_MIN_CAP); - array_init(table->values, HT_MIN_CAP); - for (size_t i = 0; i < array_cap(table->pairs); i++) { - table->pairs[i] = (HashTablePair){NULL, NULL}; - } - table->shift_amount = HT_MIN_SHIFT; - table->hash_func = _symbol_hash; - return table; -} - -void -_ht_insert(HashTable *table, Object key, Object value) { - size_t position = table->hash_func(table, &key); - size_t probe_position = position; - - // Verify the key in that position is free. If not, use linear probing to - // find the next free slot. - HashTablePair *pairs = table->pairs; - bool update = false; - while (true) { - if (pairs[probe_position].key == NULL) { - array_head(pairs)->size++; - break; - } - if (object_equal(*pairs[probe_position].key, key)) { - update = true; - break; - } - if (probe_position == array_cap(pairs) - 1) { - probe_position = 0; - } else { - probe_position++; - } - } - - if (!update) { - array_push(table->keys, object_copy(key)); - array_push(table->values, value); - pairs[probe_position].key = &table->keys[array_size(table->keys) - 1]; - pairs[probe_position].value = &table->values[array_size(table->values) - 1]; - } else { - *pairs[probe_position].value = value; - } -} - -void -_ht_maybe_grow(HashTable *table) { - if (ht_load_factor(table) < HT_LOAD_THRESHOLD) { - return; - } - - // Create a new array with 2x capacity. - HashTablePair *old_pairs = table->pairs; - Object *old_keys = table->keys; - Object *old_values = table->values; - table->pairs = NULL; - table->keys = NULL; - table->values = NULL; - array_init(table->pairs, array_cap(old_pairs) * 2); - array_init(table->keys, array_cap(old_pairs) * 2); - array_init(table->values, array_cap(old_pairs) * 2); - for (size_t i = 0; i < array_cap(table->pairs); i++) { - table->pairs[i] = (HashTablePair){NULL, NULL}; - } - table->shift_amount++; - - // Hash everything in the table for the new array capacity. - for (size_t i = 0; i < array_cap(old_pairs); i++) { - if (old_pairs[i].key != NULL) { - _ht_insert(table, *old_pairs[i].key, *old_pairs[i].value); - } - } - - // Free old arrays. - array_free(old_pairs); - for (size_t i = 0; i < array_size(old_keys); i++) { - object_free(&old_keys[i]); - } - array_free(old_keys); - array_free(old_values); -} - -void -ht_insert(HashTable *table, Object key, Object value) { - _ht_maybe_grow(table); - _ht_insert(table, key, value); - return; -} - -Object * -ht_lookup(const HashTable *table, Object key) { - size_t position = table->hash_func(table, &key); - size_t probe_position = position; - - // Verify the key in that position is the same. If not perform linear - // probing to find it. - HashTablePair *pairs = table->pairs; - while (true) { - if (pairs[probe_position].key == NULL) { - return NULL; - } - if (object_equal(*pairs[probe_position].key, key)) { - break; - } - if (probe_position == array_cap(pairs) - 1) { - probe_position = 0; - } else { - probe_position++; - } - if (probe_position == position) { - return NULL; - } - } - return pairs[probe_position].value; -} - -void -ht_free(HashTable *table) { - if (table == NULL) { - return; - } - array_free(table->pairs); - for (size_t i = 0; i < array_size(table->keys); i++) { - object_free(&table->keys[i]); - } - array_free(table->keys); - array_free(table->values); - free(table); -} - -#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 @@ -#include "lexer.h" - -static const char* token_str[] = { - [TOKEN_UNKNOWN] = "TOKEN_UNKNOWN", - [TOKEN_LPAREN] = "TOKEN_LPAREN", - [TOKEN_RPAREN] = "TOKEN_RPAREN", - [TOKEN_FIXNUM] = "TOKEN_FIXNUM", - [TOKEN_SYMBOL] = "TOKEN_SYMBOL", - [TOKEN_STRING] = "TOKEN_STRING", - [TOKEN_NIL] = "TOKEN_NIL", - [TOKEN_QUOTE] = "TOKEN_QUOTE", - [TOKEN_TRUE] = "TOKEN_TRUE", - [TOKEN_FALSE] = "TOKEN_FALSE", - [TOKEN_IF] = "TOKEN_IF", - [TOKEN_ELSE] = "TOKEN_ELSE", - [TOKEN_DEF] = "TOKEN_DEF", - [TOKEN_SET] = "TOKEN_SET", - [TOKEN_FUN] = "TOKEN_FUN", - [TOKEN_LAMBDA] = "TOKEN_LAMBDA", - [TOKEN_DISPLAY] = "TOKEN_DISPLAY", - [TOKEN_PRINT] = "TOKEN_PRINT", - [TOKEN_NEWLINE] = "TOKEN_NEWLINE", - [TOKEN_ADD] = "TOKEN_ADD", - [TOKEN_SUB] = "TOKEN_SUB", - [TOKEN_MUL] = "TOKEN_MUL", - [TOKEN_DIV] = "TOKEN_DIV", - [TOKEN_MOD] = "TOKEN_MOD", - [TOKEN_NOT] = "TOKEN_NOT", - [TOKEN_AND] = "TOKEN_AND", - [TOKEN_OR] = "TOKEN_OR", - [TOKEN_EQUAL] = "TOKEN_EQUAL", - [TOKEN_LESS] = "TOKEN_LESS", - [TOKEN_GREATER] = "TOKEN_GREATER", - [TOKEN_LESS_EQUAL] = "TOKEN_LESS_EQUAL", - [TOKEN_GREATER_EQUAL] = "TOKEN_GREATER_EQUAL", - [TOKEN_EOF] = "TOKEN_EOF", -}; - -void -print_token(Token tok) { - printf("LINE: %3ld COL: %3ld ", tok.line, tok.column); - printf("%s", token_str[tok.type]); - switch (tok.type) { - case TOKEN_FIXNUM: { - printf(" -> "); - sv_write(&tok.value); - } break; - case TOKEN_SYMBOL: { - printf(" -> "); - sv_write(&tok.value); - } break; - case TOKEN_STRING: { - printf(" -> "); - sv_write(&tok.value); - } break; - default: { - } break; - } - printf("\n"); -} - -char -scan_next(Scanner *scanner) { - char c = sv_next(&scanner->current); - if (c == '\n') { - scanner->line_number++; - scanner->col_number = 1; - } else { - scanner->col_number++; - } - scanner->offset++; - return c; -} - -char -scan_peek(const Scanner *scanner) { - return sv_peek(&scanner->current); -} - -bool -scan_has_next(const Scanner *scanner) { - return scanner->current.n != 0; -} - -void -skip_whitespace(Scanner *scanner) { - while (scan_has_next(scanner)) { - char c = scan_peek(scanner); - switch (c) { - case ' ': - case '\f': - case '\n': - case '\r': - case '\t': - case '\v': { - scan_next(scanner); - } break; - default: { - return; - } break; - } - } -} - -bool -is_delimiter(char c) { - switch (c) { - case EOF: - case '\0': - case ';': - case '"': - case '\'': - case '(': - case ')': - case ' ': - case '\f': - case '\n': - case '\r': - case '\t': - case '\v': { - return true; - } break; - } - return false; -} - -#define TOKEN_IS_KEYWORD(VAL, KEYWORD) \ - sv_equal(&(VAL), &(StringView){(KEYWORD), sizeof(KEYWORD) - 1}) - -TokenType -find_primitive_type(const StringView value) { - bool is_fixnum = true; - for (size_t i = 0; i < value.n; i++) { - char c = value.start[i]; - if (i == 0 && c == '-' && value.n > 1) { - continue; - } - if (!(c >= '0' && c <= '9')) { - is_fixnum = false; - break; - } - } - if (is_fixnum) { - return TOKEN_FIXNUM; - } - if (TOKEN_IS_KEYWORD(value, "true")) { return TOKEN_TRUE; } - if (TOKEN_IS_KEYWORD(value, "false")) { return TOKEN_FALSE; } - if (TOKEN_IS_KEYWORD(value, "if")) { return TOKEN_IF; } - if (TOKEN_IS_KEYWORD(value, "else")) { return TOKEN_ELSE; } - if (TOKEN_IS_KEYWORD(value, "def")) { return TOKEN_DEF; } - if (TOKEN_IS_KEYWORD(value, "set!")) { return TOKEN_SET; } - if (TOKEN_IS_KEYWORD(value, "fun")) { return TOKEN_FUN; } - if (TOKEN_IS_KEYWORD(value, "lambda")) { return TOKEN_LAMBDA; } - if (TOKEN_IS_KEYWORD(value, "display")) { return TOKEN_DISPLAY; } - if (TOKEN_IS_KEYWORD(value, "print")) { return TOKEN_PRINT; } - if (TOKEN_IS_KEYWORD(value, "newline")) { return TOKEN_NEWLINE; } - if (TOKEN_IS_KEYWORD(value, "+")) { return TOKEN_ADD; } - if (TOKEN_IS_KEYWORD(value, "-")) { return TOKEN_SUB; } - if (TOKEN_IS_KEYWORD(value, "*")) { return TOKEN_MUL; } - if (TOKEN_IS_KEYWORD(value, "/")) { return TOKEN_DIV; } - if (TOKEN_IS_KEYWORD(value, "%")) { return TOKEN_MOD; } - if (TOKEN_IS_KEYWORD(value, "not")) { return TOKEN_NOT; } - if (TOKEN_IS_KEYWORD(value, "and")) { return TOKEN_AND; } - if (TOKEN_IS_KEYWORD(value, "or")) { return TOKEN_OR; } - if (TOKEN_IS_KEYWORD(value, "=")) { return TOKEN_EQUAL; } - if (TOKEN_IS_KEYWORD(value, "<")) { return TOKEN_LESS; } - if (TOKEN_IS_KEYWORD(value, ">")) { return TOKEN_GREATER; } - if (TOKEN_IS_KEYWORD(value, "<=")) { return TOKEN_LESS_EQUAL; } - if (TOKEN_IS_KEYWORD(value, ">=")) { return TOKEN_GREATER_EQUAL; } - - return TOKEN_SYMBOL; -} - -Token * -tokenize(const StringView *sv) { - Token *tokens = NULL; - array_init(tokens, 1); - Scanner scanner = (Scanner){ - .current = *sv, - .line_number = 1, - .col_number = 1, - }; - - while (scan_has_next(&scanner)) { - skip_whitespace(&scanner); - size_t line = scanner.line_number; - size_t col = scanner.col_number; - size_t offset = scanner.offset; - char c = scan_next(&scanner); - switch (c) { - case ';': { - while ((c = scan_next(&scanner)) != '\n' && c != '\0') {} - } break; - case '"': { - char prev = c; - bool found = false; - size_t n = 0; - while (scan_has_next(&scanner)) { - c = scan_next(&scanner); - if (c == '"' && prev != '\\') { - found = true; - break; - } - prev = c; - n++; - } - if (!found) { - error_push((Error){ - .type = ERR_TYPE_LEXER, - .value = ERR_UNMATCHED_STRING, - .line = line, - .col = col, - }); - return tokens; - } - Token token = (Token){ - .value = (StringView){ - .start = &sv->start[offset + 1], - .n = n, - }, - .type = TOKEN_STRING, - .line = line, - .column = col, - }; - array_push(tokens, token); - } break; - case '\'': { - Token token = (Token){ - .type = TOKEN_QUOTE, - .line = line, - .column = col, - }; - array_push(tokens, token); - } break; - case '(': { - if (scan_peek(&scanner) == ')') { - scan_next(&scanner); - Token token = (Token){ - .type = TOKEN_NIL, - .line = line, - .column = col, - }; - array_push(tokens, token); - } else { - Token token = (Token){ - .type = TOKEN_LPAREN, - .line = line, - .column = col, - }; - array_push(tokens, token); - } - } break; - case ')': { - Token token = (Token){ - .type = TOKEN_RPAREN, - .line = line, - .column = col, - }; - array_push(tokens, token); - } break; - default: { - size_t n = 1; - while (!is_delimiter(scan_peek(&scanner))) { - scan_next(&scanner); - n++; - } - if (c == EOF || c == '\0') { - break; - } - Token token = (Token){ - .value = (StringView){ - .start = &sv->start[offset], - .n = n, - }, - .type = TOKEN_SYMBOL, - .line = line, - .column = col, - }; - token.type = find_primitive_type(token.value); - array_push(tokens, token); - } break; - } - } - - // Push EOF token. - Token token = (Token){ - .type = TOKEN_EOF, - .line = scanner.line_number, - .column = 1, - }; - array_push(tokens, token); - - return tokens; -} 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 @@ -#ifndef BDL_LEXER_H -#define BDL_LEXER_H - -#include "string_view.h" - -typedef enum TokenType { - TOKEN_UNKNOWN = 0, - - // Parentheses. - TOKEN_LPAREN, - TOKEN_RPAREN, - - // Primitive types. - TOKEN_FIXNUM, - TOKEN_SYMBOL, - TOKEN_STRING, - - // Keywords. - TOKEN_NIL, - TOKEN_QUOTE, - TOKEN_TRUE, - TOKEN_FALSE, - TOKEN_IF, - TOKEN_ELSE, - TOKEN_DEF, - TOKEN_SET, - TOKEN_FUN, - TOKEN_LAMBDA, - TOKEN_DISPLAY, - TOKEN_PRINT, - TOKEN_NEWLINE, - - // Arithmetic. - TOKEN_ADD, - TOKEN_SUB, - TOKEN_MUL, - TOKEN_DIV, - TOKEN_MOD, - - // Boolean comparisons. - TOKEN_NOT, - TOKEN_AND, - TOKEN_OR, - TOKEN_EQUAL, - TOKEN_LESS, - TOKEN_GREATER, - TOKEN_LESS_EQUAL, - TOKEN_GREATER_EQUAL, - - TOKEN_EOF, -} TokenType; - -typedef struct Token { - TokenType type; - StringView value; - size_t line; - size_t column; -} Token; - -typedef struct Scanner { - StringView current; - size_t line_number; - size_t col_number; - size_t offset; -} Scanner; - -// Print a token to standard output for debugging purposes. -void print_token(Token tok); - -// Same functionality as the ScanView pairs, but keeping track of line and -// column numbers. -char scan_next(Scanner *scanner); -char scan_peek(const Scanner *scanner); - -// Check if the current scanner still have characters left. -bool scan_has_next(const Scanner *scanner); - -// Advance the scanner until we ran out of whitespace. -void skip_whitespace(Scanner *scanner); - -// Check if a given character is a delimiter. -bool is_delimiter(char c); - -// Extract the token type from the current string. -TokenType find_primitive_type(const StringView value); - -// Generate a list of tokens from the given string. -Token * tokenize(const StringView *sv); - -#define TOK_BUF_CAP 256 - -#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 @@ -#include -#include -#include -#include -#include - -// -// Config. -// - -#ifdef DEBUG -#define DEBUG_TRACE_EXECUTION -#endif - -#include "vm.h" -#include "errors.c" -#include "chunk.c" -#include "objects.c" -#include "compiler.h" -#include "ops.h" -#include "debug.h" -#include "lexer.c" -#include "read_line.c" -#include "string_view.c" - -static VM vm; - -void -init(void) { - vm_init(&vm); -} - -void -halt(void) { - vm_free(&vm); -} - -void -process_source(const StringView *source) { - // Read tokens. - Token *tokens = tokenize(source); - if (errors_n != 0) { - array_free(tokens); - return; - } - - // Compile chunk. - Chunk *main = compile(tokens); - if (errors_n != 0) { - chunk_free(main); - array_free(tokens); - return; - } - -#ifdef DEBUG - disassemble_chunk(main); -#endif - - // Interpret chunk. - Object main_proc = make_lambda(main); - CallFrame frame = (CallFrame){ - .closure = main_proc.closure, - }; - array_push(vm.frames, frame); - vm_interpret(&vm); - - // Free resources. - chunk_free(main); - array_free(tokens); -} - -#define REPL_PROMPT "bdl> " - -void -run_repl(void) { - printf("BDL REPL (Press Ctrl-D or Ctrl-C to exit)\n"); - while (true) { - printf(REPL_PROMPT); - StringView sv = read_line(); - if (sv.start == NULL) { - return; - } - process_source(&sv); - - // Check if there were any errors. - if (errors_n != 0 && !supress_errors) { - for (size_t i = 0; i < errors_n; i++) { - Error err = errors[i]; - for (size_t j = 0; j < err.col + sizeof(REPL_PROMPT) - 2; j++) { - putchar(' '); - } - printf("|\n"); - for (size_t j = 0; j < err.col + sizeof(REPL_PROMPT) - 2; j++) { - putchar(' '); - } - printf("%s\n", error_msgs[err.value]); - } - errors_n = 0; - continue; - } - } -} - -void -run_file(char *file_name) { - FILE *file = fopen(file_name, "r"); - if (!file) { - fprintf(stderr, "error: couldn't open input file: %s\n", file_name); - exit(EXIT_FAILURE); - } - - // Read entire file into memory. - fseek(file, 0, SEEK_END); - size_t file_size = ftell(file); - fseek(file, 0, SEEK_SET); - - char *source = malloc(file_size + 1); - fread(source, 1, file_size, file); - source[file_size] = 0; - - StringView sv = (StringView){ - .start = source, - .n = file_size, - }; - - process_source(&sv); - - // Check if there were any errors. - if (errors_n != 0 && !supress_errors) { - report_errors(file_name); - } - - free(source); - fclose(file); -} - -#define STDIN_BUF_CAP 16 - -void -run_stdin(void) { - size_t buf_size = 0; - char *source = NULL; - array_init(source, STDIN_BUF_CAP); - - char c; - while ((c = getchar()) != EOF) { - array_push(source, c); - buf_size++; - } - - StringView sv = (StringView){ - .start = source, - .n = buf_size, - }; - - process_source(&sv); - - // Check if there were any errors. - if (errors_n != 0 && !supress_errors) { - report_errors("stdin"); - } - - array_free(source); -} - -#ifndef BIN_NAME -#define BIN_NAME "bdl" -#endif - -void -print_usage(void) { - printf("Usage: %s [options] \n", BIN_NAME); - printf("\n"); - printf("\t-i\tInteractive mode (REPL).\n"); - printf("\n"); -} - -int -main(int argc, char *argv[]) { - init(); - - int option; - while ((option = getopt(argc, argv, "i")) != -1) { - switch (option) { - case 'i': { - // Interactive mode. - run_repl(); - halt(); - return EXIT_SUCCESS; - } break; - default: { - print_usage(); - return EXIT_FAILURE; - } break; - } - } - - // Run from stdin. - if (optind == argc) { - run_stdin(); - halt(); - return EXIT_SUCCESS; - } - - // Run from file. - while (optind < argc) { - char *file_name = argv[optind]; - run_file(file_name); - optind++; - } - - halt(); - return EXIT_SUCCESS; -} 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 @@ -#include "objects.h" - -Object -make_string(StringView sv) { - Object obj = { - .type = OBJ_TYPE_STRING, - .text = NULL, - }; - array_init(obj.text, sv.n); - array_insert(obj.text, sv.start, sv.n); - return obj; -} - -Object -make_symbol(StringView sv) { - Object obj = { - .type = OBJ_TYPE_SYMBOL, - .text = NULL, - }; - array_init(obj.text, sv.n); - array_insert(obj.text, sv.start, sv.n); - return obj; -} - -Object -make_lambda(Chunk *chunk) { - Object obj = { - .type = OBJ_TYPE_LAMBDA, - }; - obj.closure = malloc(sizeof(Closure)); - obj.closure->chunk = chunk; - array_init(obj.closure->values, 0); - return obj; -} - -void -object_display(Object obj) { - switch (obj.type) { - case OBJ_TYPE_FIXNUM: { - printf("%zd", obj.fixnum); - } break; - case OBJ_TYPE_TRUE: { - printf("true"); - } break; - case OBJ_TYPE_FALSE: { - printf("false"); - } break; - case OBJ_TYPE_NIL: { - printf("()"); - } break; - case OBJ_TYPE_STRING: { - printf("\"%.*s\"", (int)array_size(obj.text), obj.text); - } break; - case OBJ_TYPE_SYMBOL: { - printf(":%.*s", (int)array_size(obj.text), obj.text); - } break; - case OBJ_TYPE_PAIR: { - // printf("("); - // display_pair(obj); - // printf(")"); - } break; - case OBJ_TYPE_LAMBDA: { - Chunk *chunk = obj.closure->chunk; - printf("#{procedure:%.*s}", - (int)array_size(chunk->name), chunk->name); - } break; - case OBJ_TYPE_ERR: { - printf("#{error}"); - } break; - } - return; -} - -void -object_free(Object *obj) { - if (IS_STRING(*obj) || IS_SYMBOL(*obj)) { - array_free(obj->text); - return; - } - if (IS_LAMBDA(*obj)) { - Closure *closure = obj->closure; - for (size_t i = 0; i < array_size(closure->values); i++) { - object_free(&closure->values[i]); - } - array_free(closure->values); - // NOTE: we are leaking memory without this, we'll need a GC - // soon... - // chunk_free(closure->chunk); - free(closure); - } -} - -bool -object_equal(Object a, Object b) { - if (a.type != b.type) { - return false; - } - switch (a.type) { - case OBJ_TYPE_TRUE: - case OBJ_TYPE_FALSE: { - return true; - } break; - case OBJ_TYPE_FIXNUM: { - return a.fixnum == b.fixnum; - } break; - case OBJ_TYPE_SYMBOL: - case OBJ_TYPE_STRING: { - if (array_size(a.text) != array_size(b.text)) { - return false; - } - for (size_t i = 0; i < array_size(a.text); i++) { - if (a.text[i] != b.text[i]) { - return false; - } - } - } break; - case OBJ_TYPE_LAMBDA: { - return a.closure == b.closure; - } break; - default: { - return false; - } break; - } - return true; -} - -Object -object_copy(Object src) { - switch (src.type) { - case OBJ_TYPE_SYMBOL: - case OBJ_TYPE_STRING: { - Object copy = src; - copy.text = NULL; - array_init(copy.text, array_size(src.text)); - array_insert(copy.text, src.text, array_size(src.text)); - return copy; - } break; - case OBJ_TYPE_LAMBDA: { - // Object copy = src; - // StringView name = (StringView){ - // .start = src.chunk->name, - // .n = array_size(src.chunk->name), - // }; - // // TODO: copy full chunk? - // // copy.chunk = chunk_init(name); - // return copy; - } break; - default: { break; } break; - } - return src; -} 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 @@ -#ifndef BDL_OBJECTS_H -#define BDL_OBJECTS_H - -#include "string_view.h" -#include "darray.h" -#include "chunk.h" - -typedef enum ObjectType { - OBJ_TYPE_NIL, - OBJ_TYPE_TRUE, - OBJ_TYPE_FALSE, - OBJ_TYPE_FIXNUM, - OBJ_TYPE_SYMBOL, - OBJ_TYPE_STRING, - OBJ_TYPE_PAIR, - OBJ_TYPE_LAMBDA, - OBJ_TYPE_ERR, -} ObjectType; - -typedef struct Object Object; - -typedef struct Closure { - // Non-owning reference to a chunk. - Chunk *chunk; - // Captured values for this closure. - Object *values; -} Closure; - -// typdef struct ConsCell { -// struct Object *car; -// struct Object *cdr; -// } ConsCell; - -typedef struct Object { - ObjectType type; - bool marked; - union { - // OBJ_TYPE_FIXNUM - ssize_t fixnum; - - // OBJ_TYPE_STRING - // OBJ_TYPE_SYMBOL - char *text; - - // OBJ_TYPE_PAIR - // ConsCell *cons_cell; - - // OBJ_TYPE_LAMBDA - Closure *closure; - }; -} Object; - -Object make_string(StringView sv); -Object make_symbol(StringView sv); -Object make_lambda(Chunk *chunk); -void object_display(Object obj); -void object_free(Object *obj); -bool object_equal(Object a, Object b); -Object object_copy(Object src); - -// Value initialization. -#define NIL_VAL ((Object){.type = OBJ_TYPE_NIL}) -#define TRUE_VAL ((Object){.type = OBJ_TYPE_TRUE}) -#define FALSE_VAL ((Object){.type = OBJ_TYPE_FALSE}) -#define FIXNUM_VAL(VAL) ((Object){.type = OBJ_TYPE_FIXNUM, .fixnum = VAL}) -#define BOOL_VAL(VAL) ((VAL) ? TRUE_VAL : FALSE_VAL) - -// Value extraction. -#define AS_FIXNUM(VAL) ((VAL).fixnum) - -// Type checking. -#define IS_NIL(VAL) ((VAL).type == OBJ_TYPE_NIL) -#define IS_TRUE(VAL) ((VAL).type != OBJ_TYPE_FALSE) -#define IS_FALSE(VAL) ((VAL).type == OBJ_TYPE_FALSE) -#define IS_FIXNUM(VAL) ((VAL).type == OBJ_TYPE_FIXNUM) -#define IS_STRING(VAL) ((VAL).type == OBJ_TYPE_STRING) -#define IS_SYMBOL(VAL) ((VAL).type == OBJ_TYPE_SYMBOL) -#define IS_LAMBDA(VAL) ((VAL).type == OBJ_TYPE_LAMBDA) - -#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 @@ -#ifndef BDL_OPS_H -#define BDL_OPS_H - -typedef enum Ops { - // Load/store ops. - OP_CONSTANT, - OP_LOCAL, - OP_CAPTURED, - OP_CAPTURE_LOCAL, - OP_SET_CAPTURED, - OP_DEF_LOCAL, - OP_SET_LOCAL, - OP_DEF, - OP_SET, - OP_GET, - // Arithmetic ops. - OP_SUM, - OP_SUB, - OP_MUL, - OP_DIV, - OP_MOD, - // Logic ops. - OP_NOT, - OP_AND, - OP_OR, - // Numerical comparison ops. - OP_EQUAL, - OP_LESS, - OP_GREATER, - OP_LESS_EQUAL, - OP_GREATER_EQUAL, - // Jump/conditional ops. - OP_JUMP, - OP_JUMP_IF_FALSE, - // Display ops. - OP_DISPLAY, - OP_PRINT, - OP_NEWLINE, - // Procedures. - OP_CALL, - OP_RETURN, - // Clear stack after each statement. - OP_DROP, -} Ops; - -#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 @@ -#include "read_line.h" - -static char readline_buf[RL_BUF_SIZE]; - -StringView -read_line(void) { - // Clear buffer. - for (size_t i = 0; i < RL_BUF_SIZE; i++) { - readline_buf[i] = 0; - } - - // Barebones readline implementation. - size_t n = 0; - char c; - while ((c = getchar()) != '\n') { - if (c == '\b') { - readline_buf[n] = '\0'; - n--; - } else if (c == EOF || c == '\0') { - return (StringView){ .start = NULL, .n = 0 }; - } else if ((c >= ' ' && c <= '~') && n < RL_BUF_SIZE) { - readline_buf[n] = c; - n++; - } - } - - StringView sv = (StringView){ - .start = (char *)&readline_buf, - .n = n, - }; - return sv; -} 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 @@ -#ifndef BDL_READ_LINE_H -#define BDL_READ_LINE_H - -#include "string_view.h" - -StringView read_line(void); - -#define RL_BUF_SIZE 1024 - -#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 @@ -#include "string_view.h" - -char -sv_next(StringView *sv) { - if (sv->n == 0) { - return '\0'; - } - char c = sv->start[0]; - sv->start++; - sv->n--; - return c; -} - -char -sv_peek(const StringView *sv) { - if (sv->n == 0) { - return '\0'; - } - return sv->start[0]; -} - -bool -sv_equal(const StringView *a, const StringView *b) { - if (a->n != b->n) { - return false; - } - for (size_t i = 0; i < a->n; i++) { - if (a->start[i] != b->start[i]) { - return false; - } - } - return true; -} - -void -sv_write(const StringView *sv) { - for (size_t i = 0; i < sv->n; i++) { - putchar(sv->start[i]); - } -} 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 @@ -#ifndef BDL_STRINGVIEW_H -#define BDL_STRINGVIEW_H - -typedef struct StringView { - char *start; - size_t n; -} StringView; - -// Consume a character in the stream. -char sv_next(StringView *sv); - -// Check what is the current character in the stream. -char sv_peek(const StringView *sv); - -// Compare if the arguments are the same. -bool sv_equal(const StringView *a, const StringView *b); - -// Write a character to the given output stream. -void sv_write(const StringView *sv); - -#define STRING(STR) (StringView){(STR), sizeof(STR) - 1} - -#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 @@ -#ifndef BDL_TYPES_H -#define BDL_TYPES_H - -#include -#include -#include - -typedef uint8_t u8; -typedef uint16_t u16; -typedef uint32_t u32; -typedef uint64_t u64; -typedef int8_t s8; -typedef int16_t s16; -typedef int32_t s32; -typedef int64_t s64; -typedef volatile u8 vu8; -typedef volatile u16 vu16; -typedef volatile u32 vu32; -typedef volatile u64 vu64; -typedef volatile s8 vs8; -typedef volatile s16 vs16; -typedef volatile s32 vs32; -typedef volatile s64 vs64; - -#define KB(N) ((u64)(N) * 1024) -#define MB(N) ((u64)KB(N) * 1024) -#define GB(N) ((u64)MB(N) * 1024) -#define TB(N) ((u64)GB(N) * 1024) - -#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 @@ -#ifndef BDL_VM_H -#define BDL_VM_H - -#include "types.h" -#include "errors.h" -#include "chunk.h" -#include "ops.h" -#include "debug.h" -#include "hashtable.h" - -#define VM_FRAMES_CAP 64 -#define VM_STACK_CAP 1024 - -typedef struct CallFrame { - // Current code being run. - Closure *closure; - // Return counter. - u8 *rp; - // Starting point of the locals for this procedure. - size_t stack_offset; -} CallFrame; - -typedef struct VM { - CallFrame *frames; - // Stack. - Object *stack; - // Program counter. - u8 *pc; - // Global variables. - HashTable *globals; -} VM; - -void vm_init(VM *vm); -void vm_free(VM *vm); -void vm_reset(VM *vm); -void vm_interpret(VM *vm); - -void -vm_init(VM *vm) { - *vm = (VM){0}; - array_init(vm->frames, VM_FRAMES_CAP); - array_init(vm->stack, VM_STACK_CAP); - vm->globals = ht_init(); -} - -void -vm_free(VM *vm) { - array_free(vm->frames); - array_free(vm->stack); - ht_free(vm->globals); -} - -void -vm_reset(VM *vm) { - vm_free(vm); - vm_init(vm); -} - -// Helper macros for a more clear VM switch. -#ifdef DEBUG -#define STACK_TRACE() \ - fprintf(stderr, "stack trace:\n"); \ - for (ssize_t i = array_size(vm->frames) - 1; i >= 0 ; i--) { \ - CallFrame frame = vm->frames[i]; \ - Chunk *chunk = frame.closure->chunk; \ - size_t instruction = vm->pc - chunk->code - 1; \ - fprintf(stderr, "\t%-4ld -> ", i); \ - fprintf(stderr, "%.*s",(int)array_size(chunk->name), chunk->name); \ - fprintf(stderr, ":%ld:%ld\n", chunk->lines[instruction].line, chunk->lines[instruction].col); \ - vm->pc = frame.rp; \ - } -#else -#define STACK_TRACE() -#endif - -#define RUNTIME_ERROR(ERR) \ - error_push((Error){ \ - .type = ERR_TYPE_RUNTIME, \ - .value = (ERR), \ - .line = chunk->lines[vm->pc - chunk->code - 1].line, \ - .col = chunk->lines[vm->pc - chunk->code - 1].col, \ - }); \ - STACK_TRACE() \ - return - -#define FIXNUM_ARITHMETIC_OP(OP) \ - do { \ - ssize_t n = AS_FIXNUM(array_pop(vm->stack)); \ - size_t stack_size = array_size(vm->stack) - n; \ - Object obj = array_peek(vm->stack, n - 1); \ - if (!IS_FIXNUM(obj)) { \ - RUNTIME_ERROR(ERR_WRONG_ARG_TYPE); \ - return; \ - } \ - ssize_t acc = AS_FIXNUM(obj); \ - while (n > 1) { \ - obj = array_peek(vm->stack, n - 2); \ - if (!IS_FIXNUM(obj)) { \ - RUNTIME_ERROR(ERR_WRONG_ARG_TYPE); \ - return; \ - } \ - ssize_t current = AS_FIXNUM(obj); \ - acc = acc OP current; \ - n--; \ - } \ - array_head(vm->stack)->size = stack_size; \ - array_push(vm->stack, FIXNUM_VAL(acc)); \ - } while (false) - -#define FIXNUM_COMPARE_OP(OP) \ - do { \ - ssize_t n = AS_FIXNUM(array_pop(vm->stack)); \ - size_t stack_size = array_size(vm->stack) - n; \ - Object obj = array_peek(vm->stack, n - 1); \ - if (!IS_FIXNUM(obj)) { \ - RUNTIME_ERROR(ERR_WRONG_ARG_TYPE); \ - return; \ - } \ - ssize_t prev = AS_FIXNUM(obj); \ - bool ret = true; \ - while (n > 1) { \ - obj = array_peek(vm->stack, n - 2); \ - if (!IS_FIXNUM(obj)) { \ - RUNTIME_ERROR(ERR_WRONG_ARG_TYPE); \ - return; \ - } \ - ssize_t current = AS_FIXNUM(obj); \ - ret = ret && (prev OP current); \ - prev = current; \ - n--; \ - } \ - array_head(vm->stack)->size = stack_size; \ - array_push(vm->stack, BOOL_VAL(ret)); \ - } while (false) - -#define LOGIC_OP(OP) \ - do { \ - ssize_t n = AS_FIXNUM(array_pop(vm->stack)); \ - size_t stack_size = array_size(vm->stack) - n; \ - Object obj = array_peek(vm->stack, n - 1); \ - bool ret = IS_TRUE(obj); \ - while (n > 1) { \ - obj = array_peek(vm->stack, n - 2); \ - bool current = IS_TRUE(obj); \ - ret = ret OP current; \ - n--; \ - } \ - array_head(vm->stack)->size = stack_size; \ - array_push(vm->stack, BOOL_VAL(ret)); \ - } while (false) - -void -vm_interpret(VM *vm) { - CallFrame *frame = &vm->frames[0]; - Chunk *chunk = frame->closure->chunk; - vm->pc = chunk->code; - frame->rp = NULL; - - if (chunk->code == NULL || array_size(chunk->code) == 0) { - error_push((Error){ - .type = ERR_TYPE_RUNTIME, - .value = ERR_EMPTY_CHUNK, - }); - return; - } - - while (true) { -#ifdef DEBUG_TRACE_EXECUTION - printf("stack: [ "); - for (size_t i = 0; i < array_size(vm->stack); i++) { - object_display(vm->stack[i]); - if (i < array_size(vm->stack) - 1) { - printf(" | "); - } - } - printf(" ]\nop: "); - disassemble_instruction(chunk, (vm->pc - chunk->code)); -#endif - u8 instruction = *vm->pc++; - switch (instruction) { - case OP_LOCAL: { - ssize_t idx = AS_FIXNUM(array_pop(vm->stack)); - array_push(vm->stack, vm->stack[frame->stack_offset + idx]); - } break; - case OP_CONSTANT: { - u8 constant = *vm->pc++; - Object obj = chunk->constants[constant]; - array_push(vm->stack, obj); - } break; - case OP_CAPTURED: { - ssize_t idx = AS_FIXNUM(array_pop(vm->stack)); - array_push(vm->stack, frame->closure->values[idx]); - } break; - case OP_CAPTURE_LOCAL: { - // This is a closure with captured variables. We need a copy - // of it that lives on the heap. - Object proc = array_pop(vm->stack); - proc = make_lambda(proc.closure->chunk); - - ssize_t n_captured = AS_FIXNUM(array_pop(vm->stack)); - for (ssize_t i = 0; i < n_captured; i++) { - Object value = array_pop(vm->stack); - array_push(proc.closure->values, value); - } - array_push(vm->stack, proc); - } break; - case OP_SET_CAPTURED: { - Object value = array_pop(vm->stack); - ssize_t idx = AS_FIXNUM(array_pop(vm->stack)); - frame->closure->values[idx] = value; - } break; - case OP_DEF_LOCAL: { - Object value = array_pop(vm->stack); - ssize_t idx = AS_FIXNUM(array_pop(vm->stack)); - vm->stack[frame->stack_offset + idx] = value; - } break; - case OP_SET_LOCAL: { - Object value = array_pop(vm->stack); - ssize_t idx = AS_FIXNUM(array_pop(vm->stack)); - CallFrame frame = vm->frames[array_size(vm->frames) - 1]; - vm->stack[frame.stack_offset + idx] = value; - } break; - case OP_DEF: { - Object value = array_pop(vm->stack); - Object name = array_pop(vm->stack); - ht_insert(vm->globals, name, value); - } break; - case OP_SET: { - Object value = array_pop(vm->stack); - Object name = array_pop(vm->stack); - Object *prev = ht_lookup(vm->globals, name); - if (prev == NULL) { - RUNTIME_ERROR(ERR_SYMBOL_NOT_FOUND); - return; - } - ht_insert(vm->globals, name, value); - } break; - case OP_GET: { - Object name = array_pop(vm->stack); - Object *value = ht_lookup(vm->globals, name); - if (value == NULL) { - RUNTIME_ERROR(ERR_SYMBOL_NOT_FOUND); - return; - } - array_push(vm->stack, *value); - } break; - case OP_SUM: { FIXNUM_ARITHMETIC_OP(+); } break; - case OP_SUB: { FIXNUM_ARITHMETIC_OP(-); } break; - case OP_MUL: { FIXNUM_ARITHMETIC_OP(*); } break; - case OP_DIV: { FIXNUM_ARITHMETIC_OP(/); } break; - case OP_MOD: { FIXNUM_ARITHMETIC_OP(%); } break; - case OP_NOT: { - Object prev = array_pop(vm->stack); - Object new = IS_TRUE(prev) ? FALSE_VAL : TRUE_VAL; - array_push(vm->stack, new); - } break; - case OP_AND: { LOGIC_OP(&&); } break; - case OP_OR: { LOGIC_OP(||); } break; - case OP_EQUAL: { FIXNUM_COMPARE_OP(==); } break; - case OP_LESS: { FIXNUM_COMPARE_OP(<); } break; - case OP_GREATER: { FIXNUM_COMPARE_OP(>); } break; - case OP_LESS_EQUAL: { FIXNUM_COMPARE_OP(<=); } break; - case OP_GREATER_EQUAL: { FIXNUM_COMPARE_OP(>=); } break; - case OP_JUMP: { - u16 a = *vm->pc++; - u16 b = *vm->pc++; - s16 offset = (a << 8) | b; - vm->pc += offset; - } break; - case OP_JUMP_IF_FALSE: { - u16 a = *vm->pc++; - u16 b = *vm->pc++; - s16 offset = (a << 8) | b; - if (IS_FALSE(array_pop(vm->stack))) { - vm->pc += offset; - } - } break; - case OP_DISPLAY: { - object_display(array_pop(vm->stack)); - } break; - case OP_PRINT: { - Object obj = array_pop(vm->stack); - if (!IS_STRING(obj)) { - RUNTIME_ERROR(ERR_WRONG_ARG_TYPE); - } - StringView scanner = (StringView) { - .start = obj.text, - .n = array_size(obj.text), - }; - while (scanner.n != 0) { - char c = sv_next(&scanner); - if (c == '\\' && sv_peek(&scanner) == 'n') { - putchar('\n'); - sv_next(&scanner); - continue; - } - if (c == '\\' && sv_peek(&scanner) == '"') { - putchar('"'); - sv_next(&scanner); - continue; - } - putchar(c); - } - } break; - case OP_NEWLINE: { - printf("\n"); - } break; - case OP_CALL: { - ssize_t n_args = AS_FIXNUM(array_pop(vm->stack)); - Object proc = vm->stack[array_size(vm->stack) - 1 - n_args]; - - // Check the number of arguments is correct. - // NOTE: This is probably better handled at compilation, but for - // now this is simpler to implement. - ssize_t n_params = proc.closure->chunk->n_params; - ssize_t n_locals = proc.closure->chunk->n_locals - n_params; - if (n_args < n_params) { - RUNTIME_ERROR(ERR_NOT_ENOUGH_ARGS); - } else if (n_args > n_params) { - RUNTIME_ERROR(ERR_TOO_MANY_ARGS); - } - -#ifdef DEBUG - disassemble_chunk(proc.closure->chunk); - printf("n_locals: %ld\n", n_locals); - printf("n_params: %ld\n", n_params); -#endif - // Tail-call optimization. - if (proc.closure->chunk != chunk || *vm->pc != OP_RETURN) { - // Prepare new call frame. - CallFrame new_frame = (CallFrame){ - .closure = proc.closure, - .rp = vm->pc, - .stack_offset = array_size(vm->stack) - n_params, - }; - array_push(vm->frames, new_frame); - frame = &vm->frames[array_size(vm->frames) - 1]; - chunk = frame->closure->chunk; - - // Prepare local slots. - for (ssize_t i = 0; i < n_locals; i++) { - array_push(vm->stack, NIL_VAL); - } - } else { - // Bind tail-call parameters. - for (ssize_t i = 0; i < n_params; i++) { - size_t offset = n_locals + n_params - 1 - i; - Object obj = array_peek(vm->stack, offset); - vm->stack[frame->stack_offset + i] = obj; - } - - // Reset stack size. - size_t offset = frame->stack_offset + n_params + n_locals; - array_head(vm->stack)->size = offset; - } - vm->pc = chunk->code; - } break; - case OP_RETURN: { - if (frame->rp == NULL) { - Object ret = array_pop(vm->stack); - if (!IS_NIL(ret)) { - object_display(ret); - printf("\n"); - } - return; - } - vm->pc = frame->rp; - array_head(vm->frames)->size--; - Object ret = array_pop(vm->stack); - array_head(vm->stack)->size = frame->stack_offset - 1; - array_push(vm->stack, ret); - frame = &vm->frames[array_size(vm->frames) - 1]; - chunk = frame->closure->chunk; - } break; - case OP_DROP: { - array_head(vm->stack)->size = 0; - } break; - default: { - RUNTIME_ERROR(ERR_NOT_IMPLEMENTED); - } break; - } - } - - error_push((Error){ - .type = ERR_TYPE_RUNTIME, - .value = ERR_PC_OOB, - .line = chunk->lines[0].line, - .col = chunk->lines[0].col, - }); -} - -#undef FIXNUM_ARITHMETIC_OP -#undef FIXNUM_COMPARE_OP -#undef LOGIC_OP - -#endif // BDL_VM_H -- cgit v1.2.1