refactor pxl8 repl
This commit is contained in:
parent
04d3af11a9
commit
c0ed855b50
7 changed files with 288 additions and 279 deletions
1
pxl8.sh
1
pxl8.sh
|
|
@ -343,6 +343,7 @@ case "$COMMAND" in
|
||||||
src/pxl8_io.c
|
src/pxl8_io.c
|
||||||
src/pxl8_math.c
|
src/pxl8_math.c
|
||||||
src/pxl8_rec.c
|
src/pxl8_rec.c
|
||||||
|
src/pxl8_repl.c
|
||||||
src/pxl8_save.c
|
src/pxl8_save.c
|
||||||
src/pxl8_script.c
|
src/pxl8_script.c
|
||||||
src/pxl8_sdl3.c
|
src/pxl8_sdl3.c
|
||||||
|
|
|
||||||
73
src/pxl8.c
73
src/pxl8.c
|
|
@ -13,6 +13,7 @@
|
||||||
#include "pxl8_game.h"
|
#include "pxl8_game.h"
|
||||||
#include "pxl8_hal.h"
|
#include "pxl8_hal.h"
|
||||||
#include "pxl8_macros.h"
|
#include "pxl8_macros.h"
|
||||||
|
#include "pxl8_repl.h"
|
||||||
#include "pxl8_script.h"
|
#include "pxl8_script.h"
|
||||||
#include "pxl8_sys.h"
|
#include "pxl8_sys.h"
|
||||||
#include "pxl8_types.h"
|
#include "pxl8_types.h"
|
||||||
|
|
@ -51,31 +52,19 @@ pxl8* pxl8_create(const pxl8_hal* hal) {
|
||||||
void pxl8_destroy(pxl8* sys) {
|
void pxl8_destroy(pxl8* sys) {
|
||||||
if (!sys) return;
|
if (!sys) return;
|
||||||
|
|
||||||
if (sys->game) {
|
if (sys->game) free(sys->game);
|
||||||
free(sys->game);
|
if (sys->cart) pxl8_cart_destroy(sys->cart);
|
||||||
}
|
if (sys->hal && sys->platform_data) sys->hal->destroy(sys->platform_data);
|
||||||
|
|
||||||
if (sys->cart) {
|
|
||||||
pxl8_cart_destroy(sys->cart);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sys->hal && sys->platform_data) {
|
|
||||||
sys->hal->destroy(sys->platform_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(sys);
|
free(sys);
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_result pxl8_init(pxl8* sys, i32 argc, char* argv[]) {
|
pxl8_result pxl8_init(pxl8* sys, i32 argc, char* argv[]) {
|
||||||
if (!sys || !sys->game) {
|
if (!sys || !sys->game) return PXL8_ERROR_INVALID_ARGUMENT;
|
||||||
return PXL8_ERROR_INVALID_ARGUMENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
pxl8_game* game = sys->game;
|
pxl8_game* game = sys->game;
|
||||||
|
|
||||||
pxl8_pixel_mode pixel_mode = PXL8_PIXEL_INDEXED;
|
pxl8_pixel_mode pixel_mode = PXL8_PIXEL_INDEXED;
|
||||||
pxl8_resolution resolution = PXL8_RESOLUTION_640x360;
|
pxl8_resolution resolution = PXL8_RESOLUTION_640x360;
|
||||||
|
|
||||||
const char* script_arg = NULL;
|
const char* script_arg = NULL;
|
||||||
bool bundle_mode = false;
|
bool bundle_mode = false;
|
||||||
bool pack_mode = false;
|
bool pack_mode = false;
|
||||||
|
|
@ -126,10 +115,10 @@ pxl8_result pxl8_init(pxl8* sys, i32 argc, char* argv[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (game->repl_mode) {
|
if (game->repl_mode) {
|
||||||
fprintf(stderr, "\033[38;2;184;187;38m[pxl8]\033[0m Starting in REPL mode with script: %s\n", game->script_path);
|
pxl8_info("starting in REPL mode with script: %s", game->script_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_info("Starting up");
|
pxl8_info("starting up");
|
||||||
|
|
||||||
sys->platform_data = sys->hal->create(pixel_mode, resolution, "pxl8", 1280, 720);
|
sys->platform_data = sys->hal->create(pixel_mode, resolution, "pxl8", 1280, 720);
|
||||||
if (!sys->platform_data) {
|
if (!sys->platform_data) {
|
||||||
|
|
@ -167,7 +156,7 @@ pxl8_result pxl8_init(pxl8* sys, i32 argc, char* argv[]) {
|
||||||
pxl8_cart_mount(sys->cart);
|
pxl8_cart_mount(sys->cart);
|
||||||
strncpy(game->script_path, "main.fnl", sizeof(game->script_path) - 1);
|
strncpy(game->script_path, "main.fnl", sizeof(game->script_path) - 1);
|
||||||
game->script_path[sizeof(game->script_path) - 1] = '\0';
|
game->script_path[sizeof(game->script_path) - 1] = '\0';
|
||||||
pxl8_info("Running embedded cart");
|
pxl8_info("running embedded cart");
|
||||||
} else {
|
} else {
|
||||||
pxl8_error("Failed to load embedded cart");
|
pxl8_error("Failed to load embedded cart");
|
||||||
return PXL8_ERROR_INITIALIZATION_FAILED;
|
return PXL8_ERROR_INITIALIZATION_FAILED;
|
||||||
|
|
@ -227,19 +216,22 @@ pxl8_result pxl8_update(pxl8* sys) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_game* game = sys->game;
|
pxl8_game* game = sys->game;
|
||||||
|
|
||||||
u64 current_time = sys->hal->get_ticks();
|
u64 current_time = sys->hal->get_ticks();
|
||||||
f32 dt = (f32)(current_time - game->last_time) / 1000000000.0f;
|
f32 dt = (f32)(current_time - game->last_time) / 1000000000.0f;
|
||||||
|
|
||||||
game->last_time = current_time;
|
game->last_time = current_time;
|
||||||
game->time += dt;
|
game->time += dt;
|
||||||
|
|
||||||
game->fps_accumulator += dt;
|
game->fps_accumulator += dt;
|
||||||
game->fps_frame_count++;
|
game->fps_frame_count++;
|
||||||
|
|
||||||
if (game->fps_accumulator >= 1.0f) {
|
if (game->fps_accumulator >= 1.0f) {
|
||||||
game->fps = (f32)game->fps_frame_count / game->fps_accumulator;
|
game->fps = (f32)game->fps_frame_count / game->fps_accumulator;
|
||||||
if (!game->repl_mode) {
|
if (!game->repl_mode) {
|
||||||
pxl8_debug("FPS: %.1f (%.2fms)", game->fps, (game->fps_accumulator / game->fps_frame_count) * 1000.0f);
|
pxl8_debug(
|
||||||
|
"FPS: %.1f (%.2fms)",
|
||||||
|
game->fps,
|
||||||
|
(game->fps_accumulator / game->fps_frame_count) * 1000.0f
|
||||||
|
);
|
||||||
}
|
}
|
||||||
game->fps_accumulator = 0.0f;
|
game->fps_accumulator = 0.0f;
|
||||||
game->fps_frame_count = 0;
|
game->fps_frame_count = 0;
|
||||||
|
|
@ -252,37 +244,36 @@ pxl8_result pxl8_update(pxl8* sys) {
|
||||||
pxl8_script_call_function(game->script, "init");
|
pxl8_script_call_function(game->script, "init");
|
||||||
}
|
}
|
||||||
|
|
||||||
game->repl = pxl8_script_repl_create();
|
game->repl = pxl8_repl_create();
|
||||||
if (game->repl) {
|
if (game->repl) {
|
||||||
fprintf(stderr, "\033[38;2;184;187;38m[pxl8 REPL]\033[0m Fennel %s - Tab for completions, Ctrl-D to exit\n", "1.5.1");
|
pxl8_info("[pxl8 REPL] Fennel %s - Tab for completion, Ctrl-D to exit", "1.6.0");
|
||||||
|
|
||||||
if (pxl8_script_load_module(game->script, "pxl8") != PXL8_OK) {
|
if (pxl8_script_load_module(game->script, "pxl8") != PXL8_OK) {
|
||||||
fprintf(stderr, "Warning: Failed to setup pxl8 global: %s\n", pxl8_script_get_last_error(game->script));
|
const char* err_msg = pxl8_script_get_last_error(game->script);
|
||||||
|
pxl8_error("failed to setup pxl8 global: %s", err_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_script_repl_init(game->repl);
|
pxl8_repl_init(game->repl);
|
||||||
game->repl_started = true;
|
game->repl_started = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (game->repl_mode && game->repl) {
|
if (game->repl_mode && game->repl) {
|
||||||
if (pxl8_script_repl_should_quit(game->repl)) {
|
if (pxl8_repl_should_quit(game->repl)) game->running = false;
|
||||||
game->running = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pxl8_script_repl_command* cmd = pxl8_script_repl_pop_command(game->repl);
|
pxl8_repl_command* cmd = pxl8_repl_pop_command(game->repl);
|
||||||
if (cmd) {
|
if (cmd) {
|
||||||
pxl8_result result = pxl8_script_eval_repl(game->script, pxl8_script_repl_command_buffer(cmd));
|
pxl8_result result = pxl8_script_eval_repl(game->script, pxl8_repl_command_buffer(cmd));
|
||||||
if (result != PXL8_OK) {
|
if (result != PXL8_OK) {
|
||||||
if (pxl8_script_is_incomplete_input(game->script)) {
|
if (pxl8_script_is_incomplete_input(game->script)) {
|
||||||
} else {
|
} else {
|
||||||
pxl8_error("%s", pxl8_script_get_last_error(game->script));
|
pxl8_error("%s", pxl8_script_get_last_error(game->script));
|
||||||
pxl8_script_repl_clear_accumulator(game->repl);
|
pxl8_repl_clear_accumulator(game->repl);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pxl8_script_repl_clear_accumulator(game->repl);
|
pxl8_repl_clear_accumulator(game->repl);
|
||||||
}
|
}
|
||||||
pxl8_script_repl_eval_complete(game->repl);
|
pxl8_repl_eval_complete(game->repl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -296,9 +287,7 @@ pxl8_result pxl8_update(pxl8* sys) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_result pxl8_frame(pxl8* sys) {
|
pxl8_result pxl8_frame(pxl8* sys) {
|
||||||
if (!sys || !sys->game) {
|
if (!sys || !sys->game) return PXL8_ERROR_INVALID_ARGUMENT;
|
||||||
return PXL8_ERROR_INVALID_ARGUMENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
pxl8_game* game = sys->game;
|
pxl8_game* game = sys->game;
|
||||||
pxl8_bounds bounds = pxl8_gfx_get_bounds(game->gfx);
|
pxl8_bounds bounds = pxl8_gfx_get_bounds(game->gfx);
|
||||||
|
|
@ -331,13 +320,13 @@ pxl8_result pxl8_frame(pxl8* sys) {
|
||||||
memset(game->input.keys_released, 0, sizeof(game->input.keys_released));
|
memset(game->input.keys_released, 0, sizeof(game->input.keys_released));
|
||||||
memset(game->input.mouse_buttons_pressed, 0, sizeof(game->input.mouse_buttons_pressed));
|
memset(game->input.mouse_buttons_pressed, 0, sizeof(game->input.mouse_buttons_pressed));
|
||||||
memset(game->input.mouse_buttons_released, 0, sizeof(game->input.mouse_buttons_released));
|
memset(game->input.mouse_buttons_released, 0, sizeof(game->input.mouse_buttons_released));
|
||||||
|
|
||||||
|
game->frame_count++;
|
||||||
game->input.mouse_dx = 0;
|
game->input.mouse_dx = 0;
|
||||||
game->input.mouse_dy = 0;
|
game->input.mouse_dy = 0;
|
||||||
game->input.mouse_wheel_x = 0;
|
game->input.mouse_wheel_x = 0;
|
||||||
game->input.mouse_wheel_y = 0;
|
game->input.mouse_wheel_y = 0;
|
||||||
|
|
||||||
game->frame_count++;
|
|
||||||
|
|
||||||
return PXL8_OK;
|
return PXL8_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -349,11 +338,11 @@ void pxl8_quit(pxl8* sys) {
|
||||||
if (game->repl_mode && game->repl) {
|
if (game->repl_mode && game->repl) {
|
||||||
fprintf(stderr, "\r\033[K");
|
fprintf(stderr, "\r\033[K");
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
pxl8_script_repl_shutdown(game->repl);
|
pxl8_repl_shutdown(game->repl);
|
||||||
pxl8_script_repl_destroy(game->repl);
|
pxl8_repl_destroy(game->repl);
|
||||||
}
|
}
|
||||||
|
|
||||||
pxl8_info("Shutting down");
|
pxl8_info("shutting down");
|
||||||
|
|
||||||
if (sys->cart) {
|
if (sys->cart) {
|
||||||
pxl8_cart_unmount(sys->cart);
|
pxl8_cart_unmount(sys->cart);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "pxl8_gfx.h"
|
#include "pxl8_gfx.h"
|
||||||
|
#include "pxl8_repl.h"
|
||||||
#include "pxl8_script.h"
|
#include "pxl8_script.h"
|
||||||
#include "pxl8_types.h"
|
#include "pxl8_types.h"
|
||||||
|
|
||||||
|
|
@ -17,7 +18,7 @@ typedef struct pxl8_game {
|
||||||
f32 fps;
|
f32 fps;
|
||||||
|
|
||||||
pxl8_input_state input;
|
pxl8_input_state input;
|
||||||
pxl8_script_repl* repl;
|
pxl8_repl* repl;
|
||||||
|
|
||||||
bool repl_mode;
|
bool repl_mode;
|
||||||
bool repl_started;
|
bool repl_started;
|
||||||
|
|
|
||||||
228
src/pxl8_repl.c
Normal file
228
src/pxl8_repl.c
Normal file
|
|
@ -0,0 +1,228 @@
|
||||||
|
#include "pxl8_repl.h"
|
||||||
|
#include "pxl8_macros.h"
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <linenoise.h>
|
||||||
|
|
||||||
|
#define PXL8_REPL_RING_BUFFER_SIZE 64
|
||||||
|
#define PXL8_MAX_REPL_COMMAND_SIZE 4096
|
||||||
|
|
||||||
|
struct pxl8_repl_command {
|
||||||
|
char buffer[PXL8_MAX_REPL_COMMAND_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pxl8_repl {
|
||||||
|
pthread_t thread;
|
||||||
|
pthread_mutex_t mutex;
|
||||||
|
pthread_cond_t cond;
|
||||||
|
pxl8_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 void pxl8_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_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_repl_stdin_thread(void* user_data) {
|
||||||
|
pxl8_repl* repl = (pxl8_repl*)user_data;
|
||||||
|
char* line;
|
||||||
|
const char* history_file = ".pxl8_history";
|
||||||
|
|
||||||
|
linenoiseHistorySetMaxLen(100);
|
||||||
|
linenoiseSetMultiLine(1);
|
||||||
|
linenoiseSetCompletionCallback(pxl8_repl_completion);
|
||||||
|
linenoiseSetHintsCallback(pxl8_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_repl* pxl8_repl_create(void) {
|
||||||
|
pxl8_repl* repl = (pxl8_repl*)calloc(1, sizeof(pxl8_repl));
|
||||||
|
return repl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_repl_destroy(pxl8_repl* repl) {
|
||||||
|
if (!repl) return;
|
||||||
|
free(repl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_repl_init(pxl8_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_repl_stdin_thread, repl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pxl8_repl_shutdown(pxl8_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_repl_command* pxl8_repl_pop_command(pxl8_repl* repl) {
|
||||||
|
if (!repl) return NULL;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&repl->mutex);
|
||||||
|
pxl8_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_repl_command_buffer(pxl8_repl_command* cmd) {
|
||||||
|
return cmd ? cmd->buffer : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pxl8_repl_should_quit(pxl8_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_repl_eval_complete(pxl8_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_repl_clear_accumulator(pxl8_repl* repl) {
|
||||||
|
if (!repl) return;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&repl->mutex);
|
||||||
|
repl->accumulator[0] = '\0';
|
||||||
|
pthread_mutex_unlock(&repl->mutex);
|
||||||
|
}
|
||||||
26
src/pxl8_repl.h
Normal file
26
src/pxl8_repl.h
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "pxl8_types.h"
|
||||||
|
|
||||||
|
typedef struct pxl8_repl pxl8_repl;
|
||||||
|
typedef struct pxl8_repl_command pxl8_repl_command;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
pxl8_repl* pxl8_repl_create(void);
|
||||||
|
void pxl8_repl_destroy(pxl8_repl* repl);
|
||||||
|
|
||||||
|
void pxl8_repl_init(pxl8_repl* repl);
|
||||||
|
void pxl8_repl_shutdown(pxl8_repl* repl);
|
||||||
|
|
||||||
|
pxl8_repl_command* pxl8_repl_pop_command(pxl8_repl* repl);
|
||||||
|
const char* pxl8_repl_command_buffer(pxl8_repl_command* cmd);
|
||||||
|
void pxl8_repl_eval_complete(pxl8_repl* repl);
|
||||||
|
void pxl8_repl_clear_accumulator(pxl8_repl* repl);
|
||||||
|
bool pxl8_repl_should_quit(pxl8_repl* repl);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
@ -991,29 +991,6 @@ bool pxl8_script_check_reload(pxl8_script* script) {
|
||||||
return false;
|
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) {
|
static bool pxl8_script_is_incomplete_error(const char* error) {
|
||||||
if (!error) return false;
|
if (!error) return false;
|
||||||
return strstr(error, "expected closing delimiter") != NULL ||
|
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;
|
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) {
|
bool pxl8_script_is_incomplete_input(pxl8_script* script) {
|
||||||
if (!script) return false;
|
if (!script) return false;
|
||||||
return pxl8_script_is_incomplete_error(script->last_error);
|
return pxl8_script_is_incomplete_error(script->last_error);
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,6 @@
|
||||||
#include "pxl8_types.h"
|
#include "pxl8_types.h"
|
||||||
|
|
||||||
typedef struct pxl8_script pxl8_script;
|
typedef struct pxl8_script pxl8_script;
|
||||||
typedef struct pxl8_script_repl pxl8_script_repl;
|
|
||||||
typedef struct pxl8_script_repl_command pxl8_script_repl_command;
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
@ -32,17 +30,6 @@ pxl8_result pxl8_script_load_module(pxl8_script* script, const char* module_name
|
||||||
pxl8_result pxl8_script_run_fennel_file(pxl8_script* script, const char* filename);
|
pxl8_result pxl8_script_run_fennel_file(pxl8_script* script, const char* filename);
|
||||||
pxl8_result pxl8_script_run_file(pxl8_script* script, const char* filename);
|
pxl8_result pxl8_script_run_file(pxl8_script* script, const char* filename);
|
||||||
|
|
||||||
pxl8_script_repl* pxl8_script_repl_create(void);
|
|
||||||
void pxl8_script_repl_destroy(pxl8_script_repl* repl);
|
|
||||||
|
|
||||||
void pxl8_script_repl_clear_accumulator(pxl8_script_repl* repl);
|
|
||||||
const char* pxl8_script_repl_command_buffer(pxl8_script_repl_command* cmd);
|
|
||||||
void pxl8_script_repl_eval_complete(pxl8_script_repl* repl);
|
|
||||||
void pxl8_script_repl_init(pxl8_script_repl* repl);
|
|
||||||
pxl8_script_repl_command* pxl8_script_repl_pop_command(pxl8_script_repl* repl);
|
|
||||||
bool pxl8_script_repl_should_quit(pxl8_script_repl* repl);
|
|
||||||
void pxl8_script_repl_shutdown(pxl8_script_repl* repl);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue