pebblisp/src/main.c

282 lines
7.5 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#define _GNU_SOURCE
#include "pebblisp.h"
#include <signal.h>
#include <stdlib.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <unistd.h>
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, &param, 1, env);
cleanObject(&prompt);
cleanObject(&param);
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 <ucontext.h>
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());
}