From 56289e87a2b196727c50784d4e3a368e1cae1ce6 Mon Sep 17 00:00:00 2001 From: hanez Date: Sun, 14 Sep 2025 22:35:23 +0200 Subject: [PATCH] Some REPL enhancements like a history, better error messages and some Makefile enhancements. --- src/fun.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++---- src/parser.c | 21 +++++++++++++++- src/parser.h | 3 +++ src/vm.c | 35 ++++++++++++++++++++++++--- src/vm.h | 6 +++++ 5 files changed, 124 insertions(+), 8 deletions(-) diff --git a/src/fun.c b/src/fun.c index 234a18e..4b192d9 100644 --- a/src/fun.c +++ b/src/fun.c @@ -63,11 +63,23 @@ int main(int argc, char **argv) { } // REPL mode - printf("Fun REPL. Type code and press Enter. Submit an empty line to run. Type 'exit' or 'quit' to leave.\n"); + printf("Fun REPL. Type code and press Enter. Submit an empty line to run.\n"); + printf("Commands: :help, :reset, :dump, :quit\n"); char *buffer = NULL; size_t bufcap = 0; size_t buflen = 0; +#ifdef FUN_DEBUG + // Open history file for appending + const char *home = getenv("HOME"); + char hist_path[1024]; + FILE *hist = NULL; + if (home) { + snprintf(hist_path, sizeof(hist_path), "%s/.fun_history", home); + hist = fopen(hist_path, "a"); + } +#endif + for (;;) { fputs(buflen == 0 ? "fun> " : "... ", stdout); fflush(stdout); @@ -78,9 +90,24 @@ int main(int argc, char **argv) { break; // EOF } - // Exit commands - if (buflen == 0 && (strcmp(line, "exit\n") == 0 || strcmp(line, "quit\n") == 0)) { - break; + // Single-line commands when buffer is empty + if (buflen == 0 && line[0] == ':') { + if (strncmp(line, ":quit", 5) == 0) break; + if (strncmp(line, ":help", 5) == 0) { + printf("Commands: :help, :reset, :dump, :quit\n"); + continue; + } + if (strncmp(line, ":reset", 6) == 0) { + vm_reset(&vm); + printf("VM state reset.\n"); + continue; + } + if (strncmp(line, ":dump", 5) == 0) { + vm_dump_globals(&vm); + continue; + } + printf("Unknown command. Use :help\n"); + continue; } // Empty line -> compile and run accumulated buffer @@ -99,6 +126,34 @@ int main(int argc, char **argv) { vm_print_output(&vm); vm_clear_output(&vm); bytecode_free(bc); +#ifdef FUN_DEBUG + if (hist) { + fprintf(hist, "%s\n", buffer); + fflush(hist); + } +#endif + } else { + // Better error message with caret + int line_no = 0, col_no = 0; + char emsg[256]; + if (parser_last_error(emsg, sizeof(emsg), &line_no, &col_no)) { + printf("Parse error at %d:%d: %s\n", line_no, col_no, emsg); + // Print the offending line and caret + int cur_line = 1; + const char *p = buffer; + while (*p && cur_line < line_no) { + if (*p == '\n') cur_line++; + p++; + } + const char *line_start = p; + while (*p && *p != '\n') p++; + fwrite(line_start, 1, (size_t)(p - line_start), stdout); + printf("\n"); + for (int i = 1; i < col_no; ++i) putchar(' '); + printf("^\n"); + } else { + printf("Parse error.\n"); + } } // reset buffer buflen = 0; @@ -117,6 +172,10 @@ int main(int argc, char **argv) { buflen += linelen; } +#ifdef FUN_DEBUG + if (hist) fclose(hist); +#endif + free(buffer); return 0; } diff --git a/src/parser.c b/src/parser.c index cc4a8fb..d1b4be0 100644 --- a/src/parser.c +++ b/src/parser.c @@ -11,6 +11,8 @@ static int g_has_error = 0; static size_t g_err_pos = 0; static char g_err_msg[256]; +static int g_err_line = 0; +static int g_err_col = 0; /* ---- compiler-generated temporary counter ---- */ static int g_temp_counter = 0; @@ -1277,12 +1279,16 @@ Bytecode *parse_file_to_bytecode(const char *path) { g_has_error = 0; g_err_pos = 0; g_err_msg[0] = '\0'; + g_err_line = 0; + g_err_col = 0; Bytecode *bc = compile_minimal(src, len); if (g_has_error) { int line = 1, col = 1; calc_line_col(src, len, g_err_pos, &line, &col); + g_err_line = line; + g_err_col = col; fprintf(stderr, "Parse error %s:%d:%d: %s\n", path ? path : "", line, col, g_err_msg); if (bc) bytecode_free(bc); free(src); @@ -1304,15 +1310,28 @@ Bytecode *parse_string_to_bytecode(const char *source) { g_has_error = 0; g_err_pos = 0; g_err_msg[0] = '\0'; + g_err_line = 0; + g_err_col = 0; Bytecode *bc = compile_minimal(source, len); if (g_has_error) { int line = 1, col = 1; calc_line_col(source, len, g_err_pos, &line, &col); - fprintf(stderr, "Parse error :%d:%d: %s\n", line, col, g_err_msg); + g_err_line = line; + g_err_col = col; if (bc) bytecode_free(bc); return NULL; } return bc; +} + +int parser_last_error(char *msgBuf, unsigned long msgCap, int *outLine, int *outCol) { + if (!g_has_error) return 0; + if (msgBuf && msgCap > 0) { + snprintf(msgBuf, msgCap, "%s", g_err_msg); + } + if (outLine) *outLine = g_err_line; + if (outCol) *outCol = g_err_col; + return 1; } \ No newline at end of file diff --git a/src/parser.h b/src/parser.h index 65defff..e588d2b 100644 --- a/src/parser.h +++ b/src/parser.h @@ -19,4 +19,7 @@ Bytecode *parse_file_to_bytecode(const char *path); /* Parse source provided as a single string buffer (for REPL, tests, etc.). */ Bytecode *parse_string_to_bytecode(const char *source); +/* Query the last parser error (1 if present, 0 if none). */ +int parser_last_error(char *msgBuf, unsigned long msgCap, int *outLine, int *outCol); + #endif diff --git a/src/vm.c b/src/vm.c index df53528..32ff18a 100644 --- a/src/vm.c +++ b/src/vm.c @@ -23,9 +23,38 @@ void vm_clear_output(VM *vm) { } void vm_free(VM *vm) { - //if (vm->output) free(vm->output); - //vm->output = NULL; - //vm->output_count = 0; + // currently nothing persistent allocated inside VM itself +} + +/* forward declaration for helper used in vm_reset */ +static void vm_pop_frame(VM *vm); + +void vm_reset(VM *vm) { + // Pop all frames (free locals) + while (vm->fp >= 0) { + vm_pop_frame(vm); + } + // Clear stack + vm->sp = -1; + // Free globals + for (int i = 0; i < VM_MAX_GLOBALS; ++i) { + free_value(vm->globals[i]); + vm->globals[i] = make_nil(); + } + // Clear output buffer + vm_clear_output(vm); +} + +void vm_dump_globals(VM *vm) { + printf("=== globals ===\n"); + for (int i = 0; i < VM_MAX_GLOBALS; ++i) { + if (vm->globals[i].type != VAL_NIL) { + printf("[%d] ", i); + print_value(&vm->globals[i]); + printf("\n"); + } + } + printf("===============\n"); } static void push_value(VM *vm, Value v) { diff --git a/src/vm.h b/src/vm.h index cd002d7..7dbe8dd 100644 --- a/src/vm.h +++ b/src/vm.h @@ -43,6 +43,12 @@ void vm_init(VM *vm); void vm_clear_output(VM *vm); void vm_print_output(VM *vm); void vm_free(VM *vm); + +// reset VM to initial state (free globals/locals/output; keep VM object) +void vm_reset(VM *vm); + +// print non-nil globals (index and value) to stdout +void vm_dump_globals(VM *vm); // run entry Bytecode (pushes first frame) void vm_run(VM *vm, Bytecode *entry);