#ifndef BDL_COMPILER_H #define BDL_COMPILER_H #include "chunk.h" #include "lexer.h" typedef struct Visitor { Token *tokens; size_t current; } Visitor; // Mimics the functionality in the Scanner functions, but for entire tokens. Token next_token(Visitor *visitor); Token peek_token(const Visitor *visitor); bool has_next_token(const Visitor *visitor); Chunk * compile(Token *tokens); Token peek_token(const Visitor *visitor) { return visitor->tokens[visitor->current]; } Token next_token(Visitor *visitor) { return visitor->tokens[visitor->current++]; } bool has_next_token(const Visitor *visitor) { return visitor->current < array_size(visitor->tokens); } void emit_constant(Chunk *chunk, Token tok, Object obj) { // TODO: Should we deduplicate constants? For example why store a number // more than once instead of reusing the existing index? 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); } 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_tree(Chunk *chunk, Visitor *vs); void compile_list_binary_op(Chunk *chunk, Visitor *vs, Token list_start, Ops op) { size_t n = 0; while (has_next_token(vs)) { Token tok = peek_token(vs); if (tok.type == TOKEN_EOF) { error_push((Error){ .type = ERR_TYPE_COMPILER, .value = ERR_UNBALANCED_PAREN, .line = list_start.line, .col = list_start.column, }); return; } if (tok.type == TOKEN_RPAREN) { next_token(vs); if (n <= 1) { error_push((Error){ .type = ERR_TYPE_COMPILER, .value = ERR_NOT_ENOUGH_ARGS, .line = list_start.line, .col = list_start.column, }); return; } break; } parse_tree(chunk, vs); n++; } emit_constant(chunk, list_start, FIXNUM_VAL(n)); add_code(chunk, op, list_start.line, list_start.column); } void compile_list_unary_op(Chunk *chunk, Visitor *vs, Token list_start, Ops op) { size_t n = 0; while (has_next_token(vs)) { Token tok = peek_token(vs); if (tok.type == TOKEN_EOF) { error_push((Error){ .type = ERR_TYPE_COMPILER, .value = ERR_UNBALANCED_PAREN, .line = list_start.line, .col = list_start.column, }); return; } if (tok.type == TOKEN_RPAREN) { next_token(vs); if (n == 0) { error_push((Error){ .type = ERR_TYPE_COMPILER, .value = ERR_NOT_ENOUGH_ARGS, .line = list_start.line, .col = list_start.column, }); } return; } parse_tree(chunk, vs); add_code(chunk, op, list_start.line, list_start.column); n++; if (n > 1) { error_push((Error){ .type = ERR_TYPE_COMPILER, .value = ERR_TOO_MANY_ARGS, .line = list_start.line, .col = list_start.column, }); } } error_push((Error){ .type = ERR_TYPE_COMPILER, .value = ERR_UNBALANCED_PAREN, .line = list_start.line, .col = list_start.column, }); } void parse_list(Chunk *chunk, Visitor *vs, Token list_start) { if (!has_next_token(vs)) { error_push((Error){ .type = ERR_TYPE_COMPILER, .value = ERR_UNBALANCED_PAREN, .line = list_start.line, .col = list_start.column, }); } Token tok = next_token(vs); switch (tok.type) { case TOKEN_ADD: { compile_list_binary_op(chunk, vs, list_start, OP_SUM); } break; case TOKEN_SUB: { compile_list_binary_op(chunk, vs, list_start, OP_SUB); } break; case TOKEN_MUL: { compile_list_binary_op(chunk, vs, list_start, OP_MUL); } break; case TOKEN_DIV: { compile_list_binary_op(chunk, vs, list_start, OP_DIV); } break; case TOKEN_MOD: { compile_list_binary_op(chunk, vs, list_start, OP_MOD); } break; case TOKEN_NOT: { compile_list_unary_op(chunk, vs, list_start, OP_NOT); } break; case TOKEN_AND: { compile_list_binary_op(chunk, vs, list_start, OP_AND); } break; case TOKEN_OR: { compile_list_binary_op(chunk, vs, list_start, OP_OR); } break; case TOKEN_EQUAL: { compile_list_binary_op(chunk, vs, list_start, OP_EQUAL); } break; case TOKEN_LESS: { compile_list_binary_op(chunk, vs, list_start, OP_LESS); } break; case TOKEN_GREATER: { compile_list_binary_op(chunk, vs, list_start, OP_GREATER); } break; case TOKEN_LESS_EQUAL: { compile_list_binary_op(chunk, vs, list_start, OP_LESS_EQUAL); } break; case TOKEN_GREATER_EQUAL: { compile_list_binary_op(chunk, vs, list_start, OP_GREATER_EQUAL); } break; default: { error_push((Error){ .type = ERR_TYPE_COMPILER, .value = ERR_OBJ_NOT_CALLABLE, .line = tok.line, .col = tok.column, }); } break; } } void parse_tree(Chunk *chunk, Visitor *vs) { Token tok = next_token(vs); 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(vs); // if (next_obj == obj_err) { // return obj_err; // } // base->cdr->car = next_obj; // return base; return; } break; case TOKEN_LPAREN: { parse_list(chunk, vs, tok); return; } break; case TOKEN_STRING: { Object obj = make_string(tok.value); emit_constant(chunk, tok, obj); return; } break; case TOKEN_SYMBOL: { Object obj = make_symbol(tok.value); emit_constant(chunk, tok, obj); 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 = chunk_init(); Visitor visitor = (Visitor){ .tokens = tokens, .current = 0, }; Token start_tok = peek_token(&visitor); while (has_next_token(&visitor) && peek_token(&visitor).type != TOKEN_EOF) { parse_tree(chunk, &visitor); } add_code(chunk, OP_RETURN, start_tok.line, start_tok.column); return chunk; } #endif // BDL_COMPILER_H