Some REPL enhancements like a history, better error messages and some Makefile enhancements.
This commit is contained in:
parent
b69c8305f6
commit
56289e87a2
5 changed files with 124 additions and 8 deletions
67
src/fun.c
67
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;
|
||||
}
|
||||
|
|
|
|||
21
src/parser.c
21
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 : "<input>", 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 <input>:%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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
35
src/vm.c
35
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) {
|
||||
|
|
|
|||
6
src/vm.h
6
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);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue