#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) { 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); } } 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 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 = start.line, .col = 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 = start.line, .col = start.column, }); return; } break; } parse_tree(chunk, vs); if (tok.type == TOKEN_SYMBOL) { add_code(chunk, OP_GET, tok.line, tok.column); } n++; } emit_constant(chunk, start, FIXNUM_VAL(n)); add_code(chunk, op, start.line, start.column); } void compile_list_unary_op(Chunk *chunk, Visitor *vs, Token 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 = start.line, .col = 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 = start.line, .col = start.column, }); } return; } parse_tree(chunk, vs); if (tok.type == TOKEN_SYMBOL) { add_code(chunk, OP_GET, tok.line, tok.column); } 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, Visitor *vs, Token start, Ops op) { if (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 = 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(vs); 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, }); } void compile_declare_op(Chunk *chunk, Visitor *vs, Token start, Ops op) { Token name = peek_token(vs); if (name.type != TOKEN_SYMBOL) { error_push((Error){ .type = ERR_TYPE_COMPILER, .value = ERR_WRONG_ARG_TYPE, .line = start.line, .col = start.column, }); return; } parse_tree(chunk, vs); Token expr = peek_token(vs); if (name.type == TOKEN_EOF || expr.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 || expr.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, vs); if (expr.type == TOKEN_SYMBOL) { add_code(chunk, OP_GET, expr.line, expr.column); } if (peek_token(vs).type != TOKEN_RPAREN) { error_push((Error){ .type = ERR_TYPE_COMPILER, .value = ERR_TOO_MANY_ARGS, .line = start.line, .col = start.column, }); return; } next_token(vs); add_code(chunk, op, start.line, start.column); } void parse_list(Chunk *chunk, Visitor *vs, Token start) { if (!has_next_token(vs)) { error_push((Error){ .type = ERR_TYPE_COMPILER, .value = ERR_UNBALANCED_PAREN, .line = start.line, .col = start.column, }); return; } Token tok = next_token(vs); switch (tok.type) { case TOKEN_ADD: { compile_list_binary_op(chunk, vs, start, OP_SUM); } break; case TOKEN_SUB: { compile_list_binary_op(chunk, vs, start, OP_SUB); } break; case TOKEN_MUL: { compile_list_binary_op(chunk, vs, start, OP_MUL); } break; case TOKEN_DIV: { compile_list_binary_op(chunk, vs, start, OP_DIV); } break; case TOKEN_MOD: { compile_list_binary_op(chunk, vs, start, OP_MOD); } break; case TOKEN_NOT: { compile_list_unary_op(chunk, vs, start, OP_NOT); } break; case TOKEN_AND: { compile_list_binary_op(chunk, vs, start, OP_AND); } break; case TOKEN_OR: { compile_list_binary_op(chunk, vs, start, OP_OR); } break; case TOKEN_EQUAL: { compile_list_binary_op(chunk, vs, start, OP_EQUAL); } break; case TOKEN_LESS: { compile_list_binary_op(chunk, vs, start, OP_LESS); } break; case TOKEN_GREATER: { compile_list_binary_op(chunk, vs, start, OP_GREATER); } break; case TOKEN_LESS_EQUAL: { compile_list_binary_op(chunk, vs, start, OP_LESS_EQUAL); } break; case TOKEN_GREATER_EQUAL: { compile_list_binary_op(chunk, vs, start, OP_GREATER_EQUAL); } break; case TOKEN_PRINT: { compile_list_unary_op(chunk, vs, start, OP_PRINT); } break; case TOKEN_DISPLAY: { compile_list_unary_op(chunk, vs, start, OP_DISPLAY); } break; case TOKEN_NEWLINE: { compile_list_simple_op(chunk, vs, start, OP_NEWLINE); } break; case TOKEN_DEF: { compile_declare_op(chunk, vs, start, OP_DEF); } break; case TOKEN_SET: { compile_declare_op(chunk, vs, start, OP_SET); } 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); 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(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