#define _GNU_SOURCE #include "pebblisp.h" #include #include #include #include #include struct Settings { int runTests; int ignoreConfig; int ignoreLib; int moreToDo; const char* configFile; char historyFile[128]; } settings; Object getPrompt(struct Environment* env) { Object prompt = fetchFromEnvironment("prompt", env); prompt = cloneObject(prompt); if (prompt.type == TYPE_STRING) { return prompt; } Object param = stringFromSlice("", 1); Object e = funcyEval(&prompt, ¶m, 1, env); cleanObject(&prompt); cleanObject(¶m); return e; } char* prompt(struct Environment* env) { Object p = getPrompt(env); char* ret = readline(p.string); cleanObject(&p); return ret; } char* preprocess(char* buf, struct Environment* env) { Object lambda = fetchFromEnvironment("preprocess", env); Object buffer = nullTerminated(buf); Object s = funcyEval(&lambda, &buffer, 1, env); size_t length; return stringObj(&s, &length); } void sigintHandler(unused int signalNumber) { Object p = getPrompt(global()); write(1, "\n", 1); write(1, p.string, strlen(p.string)); cleanObject(&p); } char* completionGenerator(const char* text, int state) { static size_t i; static size_t completionLength; if (state == 0) { i = 0; completionLength = strlen(text); } struct ObjectTable* table = &global()->table; for (; i < table->capacity; i++) { if (table->elements[i].symbol && strncmp(table->elements[i].symbol, text, completionLength) == 0) { char* symbol = malloc(sizeof(char) * (strlen(table->elements[i].symbol) + 1)); strcpy(symbol, table->elements[i].symbol); i++; return symbol; } } return NULL; } char** completer(const char* text, int start, int end) { return rl_completion_matches(text, completionGenerator); } void repl(struct Environment* env) { rl_attempted_completion_function = completer; signal(SIGINT, sigintHandler); char* buf; using_history(); read_history(settings.historyFile); while ((buf = prompt(env)) != NULL) { if (strcmp("q", buf) == 0) { free(buf); break; } buf = preprocess(buf, env); if (buf[0] == '\0') { free(buf); continue; } add_history(buf); if ((buf[0] == 'c' && buf[1] == 'd')) { char* oldBuf = buf; buf = malloc(sizeof(char) * strlen(buf + 6)); sprintf(buf, "(cd \"%s\")", oldBuf + 3); free(oldBuf); } if ((buf[0] == '?' && (buf[1] == ' ' || buf[1] == '\0'))) { char* oldBuf = buf; buf = malloc(sizeof(char) * strlen(buf + 3)); sprintf(buf, "(%s)", oldBuf); free(oldBuf); } Object o = parseEval(buf, env); if (isFuncy(o) || isError(o, DID_NOT_FIND_SYMBOL)) { cleanObject(&o); system(buf); free(buf); write_history(settings.historyFile); continue; } free(buf); size_t length; char* output = stringObj(&o, &length); cleanObject(&o); printColored(output); free(output); printf("\n"); write_history(settings.historyFile); } } void loadArgsIntoEnv(int argc, const char* argv[], struct Environment* env) { Object args = listObject(); for (int i = 0; i < argc; i++) { nf_addToList(&args, nullTerminated(argv[i])); } addToEnv(env, "args", args); } #ifdef __x86_64__ #include int nestedSegfault = 0; void segfaultHandler(unused int nSignum, unused siginfo_t* si, void* vcontext) { if (nestedSegfault) { printf("Nested segfault!!!\n"); exit(139); } nestedSegfault = 1; printf("Segfaulted!\n"); struct Slice* lastOpen = getLastOpen(); if (lastOpen) { printf("line: %d\n%s\n", lastOpen->lineNumber, lastOpen->text); } else { printf("Happened before token processing.\n"); } ucontext_t* context = vcontext; context->uc_mcontext.gregs[REG_RIP]++; exit(139); } void setupSegfaultHandler() { struct sigaction action; memset(&action, 0, sizeof(struct sigaction)); action.sa_flags = SA_SIGINFO; action.sa_sigaction = segfaultHandler; sigaction(SIGSEGV, &action, NULL); } #else void setupSegfaultHandler() { } #endif #define RUN_TESTS_ARG "--run-tests" #define RUN_DETAILED_TESTS "=detailed" #define IGNORE_CONFIG_ARG "--ignore-config" #define IGNORE_LIB_ARG "--ignore-lib" #define CONFIG_FILE_ARG "--config=" void getSettings(int argc, const char* argv[]) { const char* const home = getenv("HOME"); settings.runTests = 0; settings.ignoreConfig = 0; settings.ignoreLib = 0; settings.moreToDo = 0; settings.configFile = NULL; sprintf(settings.historyFile, "%s/.plhist", home); size_t runTestsLen = strlen(RUN_TESTS_ARG); size_t configFileLen = strlen(CONFIG_FILE_ARG); for (int i = 1; i < argc; i++) { if (strncmp(argv[i], RUN_TESTS_ARG, runTestsLen) == 0) { int isDetailed = strcmp(argv[i] + runTestsLen, RUN_DETAILED_TESTS) == 0; settings.runTests = isDetailed ? 2 : 1; } else if (strncmp(argv[i], CONFIG_FILE_ARG, configFileLen) == 0) { settings.configFile = argv[i] + configFileLen; } else if (strcmp(argv[i], IGNORE_CONFIG_ARG) == 0) { settings.ignoreConfig = 1; } else if (strcmp(argv[i], IGNORE_LIB_ARG) == 0) { settings.ignoreLib = 1; } else if (argv[i][0] == '-') { fprintf(stderr, "Unrecognized argument: '%s'\n", argv[i]); } else if (i == (argc - 1)) { settings.moreToDo = 1; } } } int main(int argc, const char* argv[]) { setupSegfaultHandler(); getSettings(argc, argv); struct Environment env = defaultEnv(); setGlobal(&env); if (settings.runTests) { int ret = runTests(settings.runTests == 2); shredDictionary(); deleteEnv(global()); return ret; } if (!settings.ignoreLib) { readFile(SCRIPTDIR "/lib.pbl", &env); } Object o = parseEval("(def prompt \"pebblisp::> \")", &env); cleanObject(&o); o = parseEval("(def preprocess (fn (text) (text)))", &env); cleanObject(&o); if (!settings.ignoreConfig) { char config[128]; if (settings.configFile) { sprintf(config, "%s", settings.configFile); } else { const char* const home = getenv("HOME"); sprintf(config, "%s/.pebblisp.pbl", home); } if (readFile(config, &env) == 1 && settings.configFile) { fprintf(stderr, "Config file not found at %s\n", config); } } if (settings.moreToDo) { FILE* file = fopen(argv[argc - 1], "r"); if (file) { // Execute a file loadArgsIntoEnv(argc, argv, &env); _readFile(file, &env); } else { // Run arguments directly as pl code Object r = parseEval(argv[argc - 1], &env); printAndClean(&r); } } else { // Run a repl loadArgsIntoEnv(argc, argv, &env); repl(&env); } deleteEnv(&env); shredDictionary(); // eprintf("totalSearchDepth: %d of %d searches\n", getTotalSearchDepth(), getTotalSearches()); // eprintf("\nHEAP-ALLOCATED OBJECTS: %d\n", getAllocations()); // eprintf("TOTAL OBJECT.C ALLOC: %zu\n", getBytes()); }