#include #include #include #include #include #include #include "string_view.c" #include "read_line.c" #include "errors.c" #include "lexer.c" #include "objects.c" #include "parser.c" #include "environment.c" #include "primitives.c" // // Utility macros. // #define MAKE_SYM(STR) make_symbol((StringView){(STR), sizeof(STR) - 1}) #define MAKE_ENV_VAR(ENV,STR,VAR) \ (env_add_symbol((ENV), MAKE_SYM(STR), (VAR))) #define MAKE_ENV_PROC(ENV,STR,FUN) \ (env_add_symbol((ENV), MAKE_SYM(STR), make_procedure(FUN))) void init(void) { // Initialize singletons. obj_nil = alloc_object(OBJ_TYPE_NIL); obj_true = alloc_object(OBJ_TYPE_BOOL); obj_false = alloc_object(OBJ_TYPE_BOOL); obj_err = alloc_object(OBJ_TYPE_ERR); // Global environment. global_env = env_create(NULL); // Primitive symbols. MAKE_ENV_VAR(global_env, "else", obj_true); MAKE_ENV_VAR(global_env, "nil", obj_nil); // Primitive procedures. MAKE_ENV_PROC(global_env, "eval", proc_eval); MAKE_ENV_PROC(global_env, "quote", proc_quote); MAKE_ENV_PROC(global_env, "car", proc_car); MAKE_ENV_PROC(global_env, "cdr", proc_cdr); MAKE_ENV_PROC(global_env, "cons", proc_cons); MAKE_ENV_PROC(global_env, "list", proc_list); MAKE_ENV_PROC(global_env, "+", proc_sum); MAKE_ENV_PROC(global_env, "-", proc_sub); MAKE_ENV_PROC(global_env, "*", proc_mul); MAKE_ENV_PROC(global_env, "/", proc_div); MAKE_ENV_PROC(global_env, "%", proc_mod); MAKE_ENV_PROC(global_env, "print", proc_print); MAKE_ENV_PROC(global_env, "display", proc_display); MAKE_ENV_PROC(global_env, "newline", proc_newline); MAKE_ENV_PROC(global_env, "boolean?", proc_is_boolean); MAKE_ENV_PROC(global_env, "nil?", proc_is_nil); MAKE_ENV_PROC(global_env, "symbol?", proc_is_symbol); MAKE_ENV_PROC(global_env, "string?", proc_is_string); MAKE_ENV_PROC(global_env, "fixnum?", proc_is_fixnum); MAKE_ENV_PROC(global_env, "pair?", proc_is_pair); MAKE_ENV_PROC(global_env, "procedure?", proc_is_procedure); MAKE_ENV_PROC(global_env, "error?", proc_is_error); MAKE_ENV_PROC(global_env, "not", proc_not); MAKE_ENV_PROC(global_env, "and", proc_and); MAKE_ENV_PROC(global_env, "or", proc_or); MAKE_ENV_PROC(global_env, "if", proc_if); MAKE_ENV_PROC(global_env, "cond", proc_cond); MAKE_ENV_PROC(global_env, "<", proc_num_less_than); MAKE_ENV_PROC(global_env, "<=", proc_num_lesseq_than); MAKE_ENV_PROC(global_env, ">", proc_num_greater_than); MAKE_ENV_PROC(global_env, ">=", proc_num_greatereq_than); MAKE_ENV_PROC(global_env, "=", proc_num_equal); MAKE_ENV_PROC(global_env, "eq?", proc_equal); MAKE_ENV_PROC(global_env, "def", proc_define); MAKE_ENV_PROC(global_env, "set!", proc_set); MAKE_ENV_PROC(global_env, "lambda", proc_lambda); // Runtime procedures. MAKE_ENV_PROC(global_env, "supress-errors", proc_supress_errors); } void process_source(const StringView *source) { Tokens tokens = tokenize(source); if (errors_n != 0) { if (tokens.buf != NULL) { free(tokens.buf); } return; } Visitor visitor = (Visitor){ .tokens = tokens, .current = 0, }; while (has_next_token(&visitor) && peek_token(&visitor).type != TOKEN_EOF) { Object *root = parse_tree(&visitor); if (root == obj_err || errors_n != 0) { free_objects(root); break; } // FIXME: Not freeing result or intermediate objects, can leak memory. Object *result = eval(global_env, root); if (result != obj_nil) { display(result); printf("\n"); } free_objects(root); } if (tokens.buf != NULL) { free(tokens.buf); } } #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) { 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); } fprintf(stderr, ": %s\n", error_msgs[err.value]); } errors_n = 0; } free(source); fclose(file); } #define STDIN_BUF_CAP 16 void run_stdin(void) { size_t buf_size = 0; size_t buf_cap = STDIN_BUF_CAP; char *source = malloc(buf_cap * sizeof(char)); char c; while ((c = getchar()) != EOF) { if (buf_size == buf_cap) { buf_cap *= 2; source = realloc(source, buf_cap * sizeof(char)); } source[buf_size] = 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) { for (size_t i = 0; i < errors_n; i++) { Error err = errors[i]; fprintf(stderr, "stdin"); if (err.line != 0) { fprintf(stderr, ":%ld:%ld", err.line, err.col); } fprintf(stderr, ": %s\n", error_msgs[err.value]); } errors_n = 0; } 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(); return EXIT_SUCCESS; } break; default: { print_usage(); return EXIT_FAILURE; } break; } } // Run from stdin. if (optind == argc) { run_stdin(); return EXIT_SUCCESS; } // Run from file. while (optind < argc) { char *file_name = argv[optind]; run_file(file_name); optind++; } return EXIT_SUCCESS; }