#include #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 "gc.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 garbage collector. init_gc(); // 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); obj_quote = make_symbol((StringView){"quote", 5}); push_root(obj_nil); push_root(obj_true); push_root(obj_false); push_root(obj_err); push_root(obj_quote); // // 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); // MAKE_ENV_PROC(global_env, "fun", proc_fun); // // 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) { // check the stack before parsing size_t root_stack_size = gc.roots.size; Object *root = parse_tree(&visitor); gc.roots.size = root_stack_size; push_root(root); // printf("AFTER: %ld\n", gc.roots.size); // return the stack before parsing to previous state except we now have // the root object as well. // printf("-----------\n"); // printf("ROOTS: \n"); // for (size_t i = 0; i < gc.roots.size; i++) { // display(gc.roots.buf[i]); // printf("\n"); // } // printf("...........\n"); for (size_t i = 0; i < gc.obj_cap; i++) { Object *obj = &gc.obj_list[i]; printf("marked? : %d ", obj->marked); printf("type: %d ", obj->type); display(obj); printf("\n"); } // printf("===========\n"); if (root == obj_err || errors_n != 0) { break; } Object *result = eval(global_env, root); if (result != obj_nil) { display(result); printf("\n"); } pop_root(); // mark_and_sweep(); } 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; }