refactor pxl8 repl

This commit is contained in:
asrael 2025-12-02 11:02:23 -06:00
parent 04d3af11a9
commit 3313c800a9
No known key found for this signature in database
GPG key ID: 2786557804DFAE24
25 changed files with 537 additions and 424 deletions

View file

@ -14,7 +14,7 @@
#include "pxl8_cart.h"
#include "pxl8_embed.h"
#include "pxl8_macros.h"
#include "pxl8_log.h"
#include "pxl8_gui.h"
struct pxl8_script {
@ -991,29 +991,6 @@ bool pxl8_script_check_reload(pxl8_script* script) {
return false;
}
#include <pthread.h>
#include <string.h>
#include <linenoise.h>
#define PXL8_REPL_RING_BUFFER_SIZE 64
struct pxl8_script_repl_command {
char buffer[PXL8_MAX_REPL_COMMAND_SIZE];
};
struct pxl8_script_repl {
pthread_t thread;
pthread_mutex_t mutex;
pthread_cond_t cond;
pxl8_script_repl_command ring_buffer[PXL8_REPL_RING_BUFFER_SIZE];
u32 head;
u32 tail;
bool running;
bool should_quit;
bool waiting_for_eval;
char accumulator[PXL8_MAX_REPL_COMMAND_SIZE];
};
static bool pxl8_script_is_incomplete_error(const char* error) {
if (!error) return false;
return strstr(error, "expected closing delimiter") != NULL ||
@ -1022,206 +999,6 @@ static bool pxl8_script_is_incomplete_error(const char* error) {
strstr(error, "unexpected end") != NULL;
}
static void pxl8_script_repl_completion(const char* buf, linenoiseCompletions* lc) {
const char* fennel_keywords[] = {
"fn", "let", "var", "set", "global", "local",
"if", "when", "do", "while", "for", "each",
"lambda", "λ", "partial", "macro", "macros",
"require", "include", "import-macros",
"values", "select", "table", "length",
".", "..", ":", "->", "->>", "-?>", "-?>>",
"doto", "match", "case", "pick-values",
"collect", "icollect", "accumulate"
};
const char* pxl8_functions[] = {
"pxl8.clr", "pxl8.pixel", "pxl8.get_pixel",
"pxl8.line", "pxl8.rect", "pxl8.rect_fill",
"pxl8.circle", "pxl8.circle_fill", "pxl8.text",
"pxl8.get_screen", "pxl8.info", "pxl8.warn",
"pxl8.error", "pxl8.debug", "pxl8.trace"
};
size_t buf_len = strlen(buf);
for (size_t i = 0; i < sizeof(fennel_keywords) / sizeof(fennel_keywords[0]); i++) {
if (strncmp(buf, fennel_keywords[i], buf_len) == 0) {
linenoiseAddCompletion(lc, fennel_keywords[i]);
}
}
for (size_t i = 0; i < sizeof(pxl8_functions) / sizeof(pxl8_functions[0]); i++) {
if (strncmp(buf, pxl8_functions[i], buf_len) == 0) {
linenoiseAddCompletion(lc, pxl8_functions[i]);
}
}
}
static char* pxl8_script_repl_hints(const char* buf, int* color, int* bold) {
if (strncmp(buf, "pxl8.", 5) == 0 && strlen(buf) == 5) {
*color = 35;
*bold = 0;
return "clr|pixel|line|rect|circle|text|get_screen";
}
if (strcmp(buf, "(fn") == 0) {
*color = 36;
*bold = 0;
return " [args] body)";
}
if (strcmp(buf, "(let") == 0) {
*color = 36;
*bold = 0;
return " [bindings] body)";
}
return NULL;
}
static void* pxl8_script_repl_stdin_thread(void* user_data) {
pxl8_script_repl* repl = (pxl8_script_repl*)user_data;
char* line;
const char* history_file = ".pxl8_history";
linenoiseHistorySetMaxLen(100);
linenoiseSetMultiLine(1);
linenoiseSetCompletionCallback(pxl8_script_repl_completion);
linenoiseSetHintsCallback(pxl8_script_repl_hints);
linenoiseHistoryLoad(history_file);
while (repl->running) {
pthread_mutex_lock(&repl->mutex);
bool in_multiline = (repl->accumulator[0] != '\0');
pthread_mutex_unlock(&repl->mutex);
const char* prompt = in_multiline ? ".. " : ">> ";
line = linenoise(prompt);
if (!line) break;
if (strlen(line) > 0 || in_multiline) {
if (!in_multiline) {
linenoiseHistoryAdd(line);
linenoiseHistorySave(history_file);
}
pthread_mutex_lock(&repl->mutex);
if (repl->accumulator[0] != '\0') {
strncat(repl->accumulator, "\n", PXL8_MAX_REPL_COMMAND_SIZE - strlen(repl->accumulator) - 1);
}
strncat(repl->accumulator, line, PXL8_MAX_REPL_COMMAND_SIZE - strlen(repl->accumulator) - 1);
u32 next_tail = (repl->tail + 1) % PXL8_REPL_RING_BUFFER_SIZE;
if (next_tail != repl->head) {
size_t len = strlen(repl->accumulator);
size_t copy_len = len < PXL8_MAX_REPL_COMMAND_SIZE - 1 ? len : PXL8_MAX_REPL_COMMAND_SIZE - 1;
memcpy(repl->ring_buffer[repl->tail].buffer, repl->accumulator, copy_len);
repl->ring_buffer[repl->tail].buffer[copy_len] = '\0';
repl->tail = next_tail;
repl->waiting_for_eval = true;
while (repl->waiting_for_eval && repl->running) {
pthread_cond_wait(&repl->cond, &repl->mutex);
}
}
pthread_mutex_unlock(&repl->mutex);
}
linenoiseFree(line);
}
pthread_mutex_lock(&repl->mutex);
repl->should_quit = true;
pthread_mutex_unlock(&repl->mutex);
return NULL;
}
pxl8_script_repl* pxl8_script_repl_create(void) {
pxl8_script_repl* repl = (pxl8_script_repl*)calloc(1, sizeof(pxl8_script_repl));
return repl;
}
void pxl8_script_repl_destroy(pxl8_script_repl* repl) {
if (!repl) return;
free(repl);
}
void pxl8_script_repl_init(pxl8_script_repl* repl) {
if (!repl) return;
repl->head = 0;
repl->tail = 0;
repl->running = true;
repl->waiting_for_eval = false;
repl->accumulator[0] = '\0';
pthread_mutex_init(&repl->mutex, NULL);
pthread_cond_init(&repl->cond, NULL);
pthread_create(&repl->thread, NULL, pxl8_script_repl_stdin_thread, repl);
}
void pxl8_script_repl_shutdown(pxl8_script_repl* repl) {
if (!repl) return;
pthread_mutex_lock(&repl->mutex);
repl->running = false;
pthread_cond_signal(&repl->cond);
pthread_mutex_unlock(&repl->mutex);
pthread_cancel(repl->thread);
pthread_join(repl->thread, NULL);
system("stty sane 2>/dev/null");
pthread_cond_destroy(&repl->cond);
pthread_mutex_destroy(&repl->mutex);
}
pxl8_script_repl_command* pxl8_script_repl_pop_command(pxl8_script_repl* repl) {
if (!repl) return NULL;
pthread_mutex_lock(&repl->mutex);
pxl8_script_repl_command* cmd = NULL;
if (repl->head != repl->tail) {
cmd = &repl->ring_buffer[repl->head];
repl->head = (repl->head + 1) % PXL8_REPL_RING_BUFFER_SIZE;
}
pthread_mutex_unlock(&repl->mutex);
return cmd;
}
const char* pxl8_script_repl_command_buffer(pxl8_script_repl_command* cmd) {
return cmd ? cmd->buffer : NULL;
}
bool pxl8_script_repl_should_quit(pxl8_script_repl* repl) {
if (!repl) return false;
pthread_mutex_lock(&repl->mutex);
bool should_quit = repl->should_quit;
pthread_mutex_unlock(&repl->mutex);
return should_quit;
}
void pxl8_script_repl_eval_complete(pxl8_script_repl* repl) {
if (!repl) return;
pthread_mutex_lock(&repl->mutex);
repl->waiting_for_eval = false;
pthread_cond_signal(&repl->cond);
pthread_mutex_unlock(&repl->mutex);
}
void pxl8_script_repl_clear_accumulator(pxl8_script_repl* repl) {
if (!repl) return;
pthread_mutex_lock(&repl->mutex);
repl->accumulator[0] = '\0';
pthread_mutex_unlock(&repl->mutex);
}
bool pxl8_script_is_incomplete_input(pxl8_script* script) {
if (!script) return false;
return pxl8_script_is_incomplete_error(script->last_error);