refactor: make interpreter portable with static pools and I/O hooks

Co-authored-by: aider (openrouter/moonshotai/kimi-k2.6) <aider@aider.chat>
This commit is contained in:
2026-05-03 22:35:21 +03:00
parent b115744991
commit f033187c6c
6 changed files with 145 additions and 153 deletions
+64 -66
View File
@@ -98,7 +98,7 @@ void do_div(Word* w) {
int64_t b = data_pop();
int64_t a = data_pop();
if (b == 0) {
fprintf(stderr, "Division by zero\n");
forth_printf("Division by zero\n");
data_push(a);
data_push(b);
return;
@@ -112,7 +112,7 @@ void do_mod(Word* w) {
int64_t b = data_pop();
int64_t a = data_pop();
if (b == 0) {
fprintf(stderr, "Modulo by zero\n");
forth_printf("Modulo by zero\n");
data_push(a);
data_push(b);
return;
@@ -126,7 +126,7 @@ void do_slash_mod(Word* w) {
int64_t b = data_pop();
int64_t a = data_pop();
if (b == 0) {
fprintf(stderr, "Modulo by zero\n");
forth_printf("Modulo by zero\n");
data_push(a);
data_push(b);
return;
@@ -309,26 +309,26 @@ void do_zero_gt(Word* w) {
void do_dot(Word* w) {
(void)w;
if (data_sp < 0) return;
printf("%" PRId64 " ", data_pop());
fflush(stdout);
forth_printf("%" PRId64 " ", data_pop());
forth_fflush();
}
void do_cr(Word* w) {
(void)w;
printf("\n");
fflush(stdout);
forth_printf("\n");
forth_fflush();
}
void do_emit(Word* w) {
(void)w;
if (data_sp < 0) return;
putchar((char)data_pop());
fflush(stdout);
forth_putchar((char)data_pop());
forth_fflush();
}
void do_key(Word* w) {
(void)w;
int c = getchar();
int c = forth_getchar();
data_push(c == EOF ? -1 : c);
}
@@ -336,28 +336,28 @@ void do_dot_quote(Word* w) {
(void)w;
if (state == 0) {
// Interpret mode: print immediately
if (input_ptr == NULL) { fprintf(stderr, "Missing string\n"); return; }
if (input_ptr == NULL) { forth_printf("Missing string\n"); return; }
while (*input_ptr && isspace((unsigned char)*input_ptr)) input_ptr++;
if (*input_ptr != '"') { fprintf(stderr, "Expected \" to start string\n"); return; }
if (*input_ptr != '"') { forth_printf("Expected \" to start string\n"); return; }
input_ptr++;
char* start = input_ptr;
while (*input_ptr && *input_ptr != '"') input_ptr++;
if (*input_ptr != '"') { fprintf(stderr, "Unterminated string\n"); return; }
while (start < input_ptr) putchar(*start++);
if (*input_ptr != '"') { forth_printf("Unterminated string\n"); return; }
while (start < input_ptr) forth_putchar(*start++);
input_ptr++;
fflush(stdout);
forth_fflush();
} else {
// Compile mode: compile string for runtime
if (input_ptr == NULL) { fprintf(stderr, "Missing string\n"); return; }
if (input_ptr == NULL) { forth_printf("Missing string\n"); return; }
while (*input_ptr && isspace((unsigned char)*input_ptr)) input_ptr++;
if (*input_ptr != '"') { fprintf(stderr, "Expected \" to start string\n"); return; }
if (*input_ptr != '"') { forth_printf("Expected \" to start string\n"); return; }
input_ptr++;
char* start = input_ptr;
while (*input_ptr && *input_ptr != '"') input_ptr++;
if (*input_ptr != '"') { fprintf(stderr, "Unterminated string\n"); return; }
if (*input_ptr != '"') { forth_printf("Unterminated string\n"); return; }
size_t len = input_ptr - start;
if (!w_dot_quote_inner) { fprintf(stderr, "Fatal: do_dot_quote_inner not found\n"); return; }
ensure_compile_cap(2 + (int32_t)len);
if (!w_dot_quote_inner) { forth_printf("Fatal: do_dot_quote_inner not found\n"); return; }
if (!ensure_compile_cap(2 + (int32_t)len)) return;
compile_buf[compile_idx++] = (Cell){.word = w_dot_quote_inner};
compile_buf[compile_idx++] = (Cell){.num = (int64_t)len};
for (size_t i = 0; i < len; i++) {
@@ -372,21 +372,21 @@ void do_dot_quote_inner(Word* w) {
int64_t len = ip->num;
ip++;
for (int64_t i = 0; i < len; i++) {
putchar((char)ip->num);
forth_putchar((char)ip->num);
ip++;
}
fflush(stdout);
forth_fflush();
}
void do_words(Word* w) {
(void)w;
printf("Dictionary words:\n");
forth_printf("Dictionary words:\n");
for (Word* cur = dict_head; cur != NULL; cur = cur->prev) {
if (cur->flags & (1 << 6)) continue;
printf("%s ", cur->name);
forth_printf("%s ", cur->name);
}
printf("\n");
fflush(stdout);
forth_printf("\n");
forth_fflush();
}
// Memory operations
@@ -394,7 +394,7 @@ void do_fetch(Word* w) {
(void)w;
int64_t addr = data_pop();
if (addr < 0 || addr >= user_mem_size) {
fprintf(stderr, "Address out of bounds\n");
forth_printf("Address out of bounds\n");
return;
}
data_push(user_mem[addr].num);
@@ -405,7 +405,7 @@ void do_store(Word* w) {
int64_t addr = data_pop();
int64_t val = data_pop();
if (addr < 0 || addr >= user_mem_size) {
fprintf(stderr, "Address out of bounds\n");
forth_printf("Address out of bounds\n");
return;
}
user_mem[addr].num = val;
@@ -416,7 +416,7 @@ void do_plus_store(Word* w) {
int64_t addr = data_pop();
int64_t val = data_pop();
if (addr < 0 || addr >= user_mem_size) {
fprintf(stderr, "Address out of bounds\n");
forth_printf("Address out of bounds\n");
return;
}
user_mem[addr].num += val;
@@ -427,7 +427,7 @@ void do_cfetch(Word* w) {
int64_t addr = data_pop(); // byte offset
int64_t max_byte = user_mem_size * (int64_t)sizeof(Cell);
if (addr < 0 || addr >= max_byte) {
fprintf(stderr, "Address out of bounds\n");
forth_printf("Address out of bounds\n");
return;
}
uint8_t* base = (uint8_t*)user_mem;
@@ -440,7 +440,7 @@ void do_cstore(Word* w) {
int64_t val = data_pop();
int64_t max_byte = user_mem_size * (int64_t)sizeof(Cell);
if (addr < 0 || addr >= max_byte) {
fprintf(stderr, "Address out of bounds\n");
forth_printf("Address out of bounds\n");
return;
}
uint8_t* base = (uint8_t*)user_mem;
@@ -457,7 +457,7 @@ void do_allot(Word* w) {
(void)w;
int64_t n = data_pop();
if (here + n > user_mem + user_mem_size) {
fprintf(stderr, "User memory overflow\n");
forth_printf("User memory overflow\n");
return;
}
here += n;
@@ -467,11 +467,11 @@ void do_allot(Word* w) {
void do_variable(Word* w) {
(void)w;
char* name = next_token();
if (!name) { fprintf(stderr, "VARIABLE expects a name\n"); return; }
if (!name) { forth_printf("VARIABLE expects a name\n"); return; }
// allocate one cell in user memory for the variable's data
if (here + 1 > user_mem + user_mem_size) {
fprintf(stderr, "User memory overflow\n");
forth_printf("User memory overflow\n");
return;
}
Cell* var_cell = here; // address of the data cell
@@ -479,8 +479,7 @@ void do_variable(Word* w) {
here++;
// create dictionary entry
Word* new_w = malloc(sizeof(Word));
if (!new_w) { printf("Out of memory\n"); exit(1); }
Word* new_w = forth_alloc_word();
new_w->prev = dict_head;
dict_head = new_w;
@@ -497,11 +496,11 @@ void do_constant(Word* w) {
(void)w;
int64_t val = data_pop();
char* name = next_token();
if (!name) { fprintf(stderr, "CONSTANT expects a name\n"); data_push(val); return; }
if (!name) { forth_printf("CONSTANT expects a name\n"); data_push(val); return; }
// allocate a cell in user memory to hold the constant value
if (here + 1 > user_mem + user_mem_size) {
fprintf(stderr, "User memory overflow\n");
forth_printf("User memory overflow\n");
data_push(val); // restore the value (optional)
return;
}
@@ -509,8 +508,7 @@ void do_constant(Word* w) {
val_cell->num = val;
here++;
Word* new_w = malloc(sizeof(Word));
if (!new_w) { printf("Out of memory\n"); exit(1); }
Word* new_w = forth_alloc_word();
new_w->prev = dict_head;
dict_head = new_w;
@@ -547,7 +545,7 @@ void do_r_from(Word* w) {
void do_r_fetch(Word* w) {
(void)w;
if (rp < 0) { fprintf(stderr, "Return stack underflow\n"); return; }
if (rp < 0) { forth_printf("Return stack underflow\n"); return; }
data_push(ret_stack[rp].num);
}
@@ -572,7 +570,7 @@ void do_lit(Word* w) {
void do_colon(Word* w) {
(void)w;
char* name = next_token();
if (!name) { fprintf(stderr, "':' expects a name\n"); return; }
if (!name) { forth_printf("':' expects a name\n"); return; }
size_t len = strlen(name);
if (len > MAX_NAME_LEN) len = MAX_NAME_LEN;
memcpy(compiling_name, name, len);
@@ -584,20 +582,20 @@ void do_colon(Word* w) {
void do_semicolon(Word* w) {
(void)w;
if (state != 1) { fprintf(stderr, "';' only valid in compile mode\n"); return; }
if (!w_exit) { fprintf(stderr, "Fatal: exit word not found\n"); return; }
if (state != 1) { forth_printf("';' only valid in compile mode\n"); return; }
if (!w_exit) { forth_printf("Fatal: exit word not found\n"); return; }
ensure_compile_cap(1);
if (!ensure_compile_cap(1)) return;
compile_buf[compile_idx++] = (Cell){.word = w_exit};
// Create body copy of compiled cells
Cell* body_copy = malloc(compile_idx * sizeof(Cell));
if (!body_copy) { printf("Out of memory\n"); exit(1); }
Cell* body_copy = forth_alloc_body(compile_idx);
if (!body_copy) { forth_printf("Out of memory\n"); return; }
memcpy(body_copy, compile_buf, compile_idx * sizeof(Cell));
// Create new word entry
Word* new_w = malloc(sizeof(Word));
if (!new_w) { printf("Out of memory\n"); free(body_copy); exit(1); }
Word* new_w = forth_alloc_word();
if (!new_w) { forth_printf("Out of memory\n"); return; }
new_w->prev = dict_head;
dict_head = new_w;
@@ -632,9 +630,9 @@ void do_zero_branch(Word* w) {
// Control flow using compile stack (indices)
void do_if(Word* w) {
(void)w;
if (state != 1) { fprintf(stderr, "IF only valid in compile mode\n"); return; }
if (!w_zbranch) { fprintf(stderr, "Fatal: 0branch not found\n"); return; }
ensure_compile_cap(2);
if (state != 1) { forth_printf("IF only valid in compile mode\n"); return; }
if (!w_zbranch) { forth_printf("Fatal: 0branch not found\n"); return; }
if (!ensure_compile_cap(2)) return;
compile_buf[compile_idx++] = (Cell){.word = w_zbranch};
// compile_push current index (where the offset will be placed)
compile_push(compile_idx);
@@ -643,7 +641,7 @@ void do_if(Word* w) {
void do_then(Word* w) {
(void)w;
if (state != 1) { fprintf(stderr, "THEN only valid in compile mode\n"); return; }
if (state != 1) { forth_printf("THEN only valid in compile mode\n"); return; }
int64_t offset_idx = compile_pop();
if (offset_idx < 0) return;
compile_buf[offset_idx].num = compile_idx - offset_idx;
@@ -651,12 +649,12 @@ void do_then(Word* w) {
void do_else(Word* w) {
(void)w;
if (state != 1) { fprintf(stderr, "ELSE only valid in compile mode\n"); return; }
if (state != 1) { forth_printf("ELSE only valid in compile mode\n"); return; }
int64_t if_offset_idx = compile_pop();
if (if_offset_idx < 0) return;
if (!w_branch) { fprintf(stderr, "Fatal: branch not found\n"); return; }
ensure_compile_cap(2);
if (!w_branch) { forth_printf("Fatal: branch not found\n"); return; }
if (!ensure_compile_cap(2)) return;
// resolve IF offset to skip the ELSE branch
compile_buf[if_offset_idx].num = (compile_idx + 2) - if_offset_idx;
@@ -669,30 +667,30 @@ void do_else(Word* w) {
void do_begin(Word* w) {
(void)w;
if (state != 1) { fprintf(stderr, "BEGIN only valid in compile mode\n"); return; }
if (state != 1) { forth_printf("BEGIN only valid in compile mode\n"); return; }
compile_push(compile_idx);
}
void do_until(Word* w) {
(void)w;
if (state != 1) { fprintf(stderr, "UNTIL only valid in compile mode\n"); return; }
if (state != 1) { forth_printf("UNTIL only valid in compile mode\n"); return; }
int64_t begin_idx = compile_pop();
if (begin_idx < 0) return;
if (!w_zbranch) { fprintf(stderr, "Fatal: 0branch not found\n"); return; }
ensure_compile_cap(2);
if (!w_zbranch) { forth_printf("Fatal: 0branch not found\n"); return; }
if (!ensure_compile_cap(2)) return;
compile_buf[compile_idx++] = (Cell){.word = w_zbranch};
compile_buf[compile_idx++] = (Cell){.num = begin_idx - compile_idx};
}
void do_while(Word* w) {
(void)w;
if (state != 1) { fprintf(stderr, "WHILE only valid in compile mode\n"); return; }
if (state != 1) { forth_printf("WHILE only valid in compile mode\n"); return; }
int64_t begin_idx = compile_pop();
if (begin_idx < 0) return;
if (!w_zbranch) { fprintf(stderr, "Fatal: 0branch not found\n"); return; }
ensure_compile_cap(2);
if (!w_zbranch) { forth_printf("Fatal: 0branch not found\n"); return; }
if (!ensure_compile_cap(2)) return;
compile_buf[compile_idx++] = (Cell){.word = w_zbranch};
int64_t while_offset_idx = compile_idx;
compile_idx++; // reserve offset
@@ -703,14 +701,14 @@ void do_while(Word* w) {
void do_repeat(Word* w) {
(void)w;
if (state != 1) { fprintf(stderr, "REPEAT only valid in compile mode\n"); return; }
if (state != 1) { forth_printf("REPEAT only valid in compile mode\n"); return; }
int64_t begin_idx = compile_pop();
if (begin_idx < 0) return;
int64_t while_offset_idx = compile_pop();
if (while_offset_idx < 0) return;
if (!w_branch) { fprintf(stderr, "Fatal: branch not found\n"); return; }
ensure_compile_cap(2);
if (!w_branch) { forth_printf("Fatal: branch not found\n"); return; }
if (!ensure_compile_cap(2)) return;
compile_buf[compile_idx++] = (Cell){.word = w_branch};
compile_buf[compile_idx++] = (Cell){.num = begin_idx - compile_idx};