Files
ai-forth-experiment/forth_words.c
T
haxala1r 58e0de7ae1 fix: add internal lookup for hidden compiler words
Co-authored-by: aider (openrouter/moonshotai/kimi-k2.6) <aider@aider.chat>
2026-05-03 17:45:40 +03:00

861 lines
20 KiB
C

#include "forth.h"
// Stack operations
void do_dup(Word* w) {
(void)w;
if (sp < 0) return;
int32_t v = data_stack[sp];
data_push(v);
}
void do_drop(Word* w) {
(void)w;
data_pop();
}
void do_swap(Word* w) {
(void)w;
if (sp < 1) return;
int32_t a = data_stack[sp-1];
int32_t b = data_stack[sp];
data_stack[sp-1] = b;
data_stack[sp] = a;
}
void do_over(Word* w) {
(void)w;
if (sp < 1) return;
data_push(data_stack[sp-1]);
}
void do_rot(Word* w) {
(void)w;
if (sp < 2) return;
int32_t a = data_stack[sp-2];
int32_t b = data_stack[sp-1];
int32_t c = data_stack[sp];
data_stack[sp-2] = c;
data_stack[sp-1] = a;
data_stack[sp] = b;
}
void do_minus_rot(Word* w) {
(void)w;
if (sp < 2) return;
int32_t a = data_stack[sp-2];
int32_t b = data_stack[sp-1];
int32_t c = data_stack[sp];
data_stack[sp-2] = b;
data_stack[sp-1] = c;
data_stack[sp] = a;
}
void do_nip(Word* w) {
(void)w;
if (sp < 1) return;
data_stack[sp-1] = data_stack[sp];
sp--;
}
void do_tuck(Word* w) {
(void)w;
if (sp < 1) return;
int32_t a = data_stack[sp-1];
int32_t b = data_stack[sp];
data_push(a);
data_stack[sp-2] = b;
}
// Arithmetic operations
void do_add(Word* w) {
(void)w;
if (sp < 1) return;
int32_t b = data_pop();
int32_t a = data_pop();
data_push(a + b);
}
void do_sub(Word* w) {
(void)w;
if (sp < 1) return;
int32_t b = data_pop();
int32_t a = data_pop();
data_push(a - b);
}
void do_mul(Word* w) {
(void)w;
if (sp < 1) return;
int32_t b = data_pop();
int32_t a = data_pop();
data_push(a * b);
}
void do_div(Word* w) {
(void)w;
if (sp < 1) return;
int32_t b = data_pop();
int32_t a = data_pop();
if (b == 0) {
printf("Division by zero\n");
data_push(a);
data_push(b);
return;
}
data_push(a / b);
}
void do_mod(Word* w) {
(void)w;
if (sp < 1) return;
int32_t b = data_pop();
int32_t a = data_pop();
if (b == 0) {
printf("Modulo by zero\n");
data_push(a);
data_push(b);
return;
}
data_push(a % b);
}
void do_slash_mod(Word* w) {
(void)w;
if (sp < 1) return;
int32_t b = data_pop();
int32_t a = data_pop();
if (b == 0) {
printf("Modulo by zero\n");
data_push(a);
data_push(b);
return;
}
data_push(a / b);
data_push(a % b);
}
void do_one_plus(Word* w) {
(void)w;
if (sp < 0) return;
data_stack[sp]++;
}
void do_one_minus(Word* w) {
(void)w;
if (sp < 0) return;
data_stack[sp]--;
}
void do_two_plus(Word* w) {
(void)w;
if (sp < 0) return;
data_stack[sp] += 2;
}
void do_two_minus(Word* w) {
(void)w;
if (sp < 0) return;
data_stack[sp] -= 2;
}
void do_negate(Word* w) {
(void)w;
if (sp < 0) return;
data_stack[sp] = -data_stack[sp];
}
void do_abs(Word* w) {
(void)w;
if (sp < 0) return;
int32_t v = data_pop();
data_push(v < 0 ? -v : v);
}
void do_min(Word* w) {
(void)w;
if (sp < 1) return;
int32_t b = data_pop();
int32_t a = data_pop();
data_push(a < b ? a : b);
}
void do_max(Word* w) {
(void)w;
if (sp < 1) return;
int32_t b = data_pop();
int32_t a = data_pop();
data_push(a > b ? a : b);
}
// Logic operations
void do_and(Word* w) {
(void)w;
if (sp < 1) return;
int32_t b = data_pop();
int32_t a = data_pop();
data_push(a & b);
}
void do_or(Word* w) {
(void)w;
if (sp < 1) return;
int32_t b = data_pop();
int32_t a = data_pop();
data_push(a | b);
}
void do_xor(Word* w) {
(void)w;
if (sp < 1) return;
int32_t b = data_pop();
int32_t a = data_pop();
data_push(a ^ b);
}
void do_invert(Word* w) {
(void)w;
if (sp < 0) return;
data_stack[sp] = ~data_stack[sp];
}
void do_lshift(Word* w) {
(void)w;
if (sp < 1) return;
int32_t b = data_pop();
int32_t a = data_pop();
data_push(a << b);
}
void do_rshift(Word* w) {
(void)w;
if (sp < 1) return;
int32_t b = data_pop();
int32_t a = data_pop();
data_push((int32_t)((uint32_t)a >> b));
}
// Comparison operations
void do_eq(Word* w) {
(void)w;
if (sp < 1) return;
int32_t b = data_pop();
int32_t a = data_pop();
data_push(a == b ? -1 : 0);
}
void do_neq(Word* w) {
(void)w;
if (sp < 1) return;
int32_t b = data_pop();
int32_t a = data_pop();
data_push(a != b ? -1 : 0);
}
void do_lt(Word* w) {
(void)w;
if (sp < 1) return;
int32_t b = data_pop();
int32_t a = data_pop();
data_push(a < b ? -1 : 0);
}
void do_gt(Word* w) {
(void)w;
if (sp < 1) return;
int32_t b = data_pop();
int32_t a = data_pop();
data_push(a > b ? -1 : 0);
}
void do_lte(Word* w) {
(void)w;
if (sp < 1) return;
int32_t b = data_pop();
int32_t a = data_pop();
data_push(a <= b ? -1 : 0);
}
void do_gte(Word* w) {
(void)w;
if (sp < 1) return;
int32_t b = data_pop();
int32_t a = data_pop();
data_push(a >= b ? -1 : 0);
}
void do_zero_eq(Word* w) {
(void)w;
if (sp < 0) return;
int32_t a = data_pop();
data_push(a == 0 ? -1 : 0);
}
void do_zero_lt(Word* w) {
(void)w;
if (sp < 0) return;
int32_t a = data_pop();
data_push(a < 0 ? -1 : 0);
}
void do_zero_gt(Word* w) {
(void)w;
if (sp < 0) return;
int32_t a = data_pop();
data_push(a > 0 ? -1 : 0);
}
// I/O operations
void do_dot(Word* w) {
(void)w;
if (sp < 0) return;
printf("%d ", data_pop());
fflush(stdout);
}
void do_cr(Word* w) {
(void)w;
printf("\n");
fflush(stdout);
}
void do_emit(Word* w) {
(void)w;
if (sp < 0) return;
putchar((char)data_pop());
fflush(stdout);
}
void do_key(Word* w) {
(void)w;
int c = getchar();
data_push(c == EOF ? -1 : c);
}
void do_dot_quote(Word* w) {
(void)w;
// Immediate word: parse string until " and print/compile
if (state == 0) { // Interpret mode: print immediately
if (input_ptr == NULL) {
printf("Missing string\n");
return;
}
// Skip whitespace before opening "
while (*input_ptr != '\0' && isspace((unsigned char)*input_ptr)) {
input_ptr++;
}
if (*input_ptr != '"') {
printf("Expected \" to start string\n");
return;
}
input_ptr++; // Skip opening "
char* start = input_ptr;
// Find closing "
while (*input_ptr != '\0' && *input_ptr != '"') {
input_ptr++;
}
if (*input_ptr != '"') {
printf("Unterminated string\n");
return;
}
// Print the string
while (start < input_ptr) {
putchar(*start++);
}
input_ptr++; // Skip closing "
fflush(stdout);
} else { // Compile mode: compile string for runtime
if (input_ptr == NULL) {
printf("Missing string\n");
return;
}
// Skip whitespace before opening "
while (*input_ptr != '\0' && isspace((unsigned char)*input_ptr)) {
input_ptr++;
}
if (*input_ptr != '"') {
printf("Expected \" to start string\n");
return;
}
input_ptr++; // Skip opening "
char* start = input_ptr;
// Find closing "
while (*input_ptr != '\0' && *input_ptr != '"') {
input_ptr++;
}
if (*input_ptr != '"') {
printf("Unterminated string\n");
return;
}
size_t len = input_ptr - start;
// Compile do_dot_quote_inner
Word* inner_w = lookup_word_internal("do_dot_quote_inner");
if (inner_w == NULL) {
printf("Fatal: do_dot_quote_inner not found\n");
return;
}
if (compile_idx + 2 + (int32_t)len > COMPILE_BUF_SIZE) {
printf("Compile buffer full\n");
return;
}
compile_buf[compile_idx++] = (Cell){.word = inner_w};
compile_buf[compile_idx++] = (Cell){.num = (int32_t)len};
// Store string characters in compile buffer (each as a num cell)
for (size_t i = 0; i < len; i++) {
compile_buf[compile_idx++] = (Cell){.num = (int32_t)start[i]};
}
input_ptr++; // Skip closing "
}
}
void do_dot_quote_inner(Word* w) {
(void)w;
// Runtime: ip points to length cell, followed by string characters
int32_t len = ip->num;
ip++;
for (int32_t i = 0; i < len; i++) {
putchar((char)ip->num);
ip++;
}
fflush(stdout);
}
void do_words(Word* w) {
(void)w;
printf("Dictionary words:\n");
for (Word* cur = dict_head; cur != NULL; cur = cur->prev) {
if (cur->flags & (1 << 6)) continue; // Skip hidden
printf("%s ", cur->name);
}
printf("\n");
fflush(stdout);
}
// Control flow operations
void do_exit(Word* w) {
(void)w;
Cell* ret_addr = ret_pop_ip();
ip = ret_addr;
}
void do_docolon(Word* w) {
// Push current ip (return address) onto return stack
ret_push_ip(ip);
// Set ip to this word's body
ip = w->body;
}
void do_lit(Word* w) {
(void)w;
// ip points to the number cell (inner interpreter already incremented past lit word)
data_push(ip->num);
ip++; // Move past number cell
}
void do_colon(Word* w) {
(void)w;
char* name = next_token();
if (name == NULL) {
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);
compiling_name[len] = '\0';
state = 1; // Enter compile mode
compile_idx = 0; // Reset compile buffer
}
void do_semicolon(Word* w) {
(void)w;
if (state != 1) {
printf("';' is only valid in compile mode\n");
return;
}
Word* exit_w = lookup_word("exit");
if (exit_w == NULL) {
printf("Fatal: exit word not found\n");
return;
}
if (compile_idx >= COMPILE_BUF_SIZE) {
printf("Compile buffer overflow\n");
return;
}
compile_buf[compile_idx++] = (Cell){.word = exit_w};
// Copy compiled body to dictionary body storage
if (body_idx + compile_idx > BODY_SIZE) {
printf("Dictionary body storage full\n");
return;
}
memcpy(&dict_bodies[body_idx], compile_buf, compile_idx * sizeof(Cell));
// Create new word entry
if (dict_idx >= DICT_SIZE) {
printf("Dictionary full\n");
return;
}
Word* new_w = &dict[dict_idx++];
new_w->prev = dict_head;
dict_head = new_w;
size_t len = strlen(compiling_name);
if (len > MAX_NAME_LEN) len = MAX_NAME_LEN;
new_w->flags = (uint8_t)len; // No hidden, no immediate
memcpy(new_w->name, compiling_name, len);
new_w->name[len] = '\0';
new_w->code = do_docolon;
new_w->body = &dict_bodies[body_idx];
body_idx += compile_idx;
state = 0; // Back to interpret mode
}
void do_branch(Word* w) {
(void)w;
// Unconditional branch: ip points to offset cell
int32_t offset = ip->num;
ip += offset; // Jump offset cells (relative to offset cell itself)
}
void do_zero_branch(Word* w) {
(void)w;
// Conditional branch: if top of stack is 0, branch
int32_t cond = data_pop();
if (cond == 0) {
int32_t offset = ip->num;
ip += offset;
} else {
ip++; // Skip offset cell
}
}
// Memory operations
void do_fetch(Word* w) {
(void)w;
int32_t addr = data_pop();
if (addr < 0 || addr >= USER_MEMORY_SIZE) {
printf("Address out of bounds\n");
return;
}
data_push(user_mem[addr]);
}
void do_store(Word* w) {
(void)w;
int32_t val = data_pop();
int32_t addr = data_pop();
if (addr < 0 || addr >= USER_MEMORY_SIZE) {
printf("Address out of bounds\n");
return;
}
user_mem[addr] = val;
}
void do_plus_store(Word* w) {
(void)w;
int32_t val = data_pop();
int32_t addr = data_pop();
if (addr < 0 || addr >= USER_MEMORY_SIZE) {
printf("Address out of bounds\n");
return;
}
user_mem[addr] += val;
}
void do_cfetch(Word* w) {
(void)w;
int32_t addr = data_pop();
int32_t max_addr = USER_MEMORY_SIZE * (int32_t)sizeof(int32_t);
if (addr < 0 || addr >= max_addr) {
printf("Address out of bounds\n");
return;
}
int32_t cell = addr / (int32_t)sizeof(int32_t);
int32_t byte = addr % (int32_t)sizeof(int32_t);
uint8_t* p = (uint8_t*)&user_mem[cell];
data_push((int32_t)p[byte]);
}
void do_cstore(Word* w) {
(void)w;
int32_t val = data_pop();
int32_t addr = data_pop();
int32_t max_addr = USER_MEMORY_SIZE * (int32_t)sizeof(int32_t);
if (addr < 0 || addr >= max_addr) {
printf("Address out of bounds\n");
return;
}
int32_t cell = addr / (int32_t)sizeof(int32_t);
int32_t byte = addr % (int32_t)sizeof(int32_t);
uint8_t* p = (uint8_t*)&user_mem[cell];
p[byte] = (uint8_t)val;
}
void do_here(Word* w) {
(void)w;
data_push((int32_t)(here - user_mem));
}
void do_allot(Word* w) {
(void)w;
int32_t n = data_pop();
here += n;
}
void do_variable(Word* w) {
(void)w;
char* name = next_token();
if (name == NULL) {
printf("VARIABLE expects a name\n");
return;
}
if (dict_idx >= DICT_SIZE) {
printf("Dictionary full\n");
return;
}
if (body_idx >= BODY_SIZE) {
printf("Dictionary body storage full\n");
return;
}
Word* new_w = &dict[dict_idx++];
new_w->prev = dict_head;
dict_head = new_w;
size_t len = strlen(name);
if (len > MAX_NAME_LEN) len = MAX_NAME_LEN;
new_w->flags = (uint8_t)len;
memcpy(new_w->name, name, len);
new_w->name[len] = '\0';
new_w->code = do_do_var;
// Allocate body cell to hold the address (cell index in user_mem)
new_w->body = &dict_bodies[body_idx++];
int32_t addr = (int32_t)(here - user_mem);
new_w->body->num = addr;
// Allocate one cell in user_mem
here++;
}
void do_constant(Word* w) {
(void)w;
int32_t val = data_pop();
char* name = next_token();
if (name == NULL) {
printf("CONSTANT expects a name\n");
data_push(val);
return;
}
if (dict_idx >= DICT_SIZE) {
printf("Dictionary full\n");
return;
}
if (body_idx >= BODY_SIZE) {
printf("Dictionary body storage full\n");
return;
}
Word* new_w = &dict[dict_idx++];
new_w->prev = dict_head;
dict_head = new_w;
size_t len = strlen(name);
if (len > MAX_NAME_LEN) len = MAX_NAME_LEN;
new_w->flags = (uint8_t)len;
memcpy(new_w->name, name, len);
new_w->name[len] = '\0';
new_w->code = do_do_const;
// Allocate body cell to hold the constant value
new_w->body = &dict_bodies[body_idx++];
new_w->body->num = val;
}
void do_do_var(Word* w) {
data_push(w->body->num); // Push the user_mem cell index
}
void do_do_const(Word* w) {
data_push(w->body->num); // Push the constant value
}
// Return stack operations
void do_to_r(Word* w) {
(void)w;
int32_t val = data_pop();
ret_push_num(val);
}
void do_r_from(Word* w) {
(void)w;
int32_t val = ret_pop_num();
data_push(val);
}
void do_r_fetch(Word* w) {
(void)w;
if (rp < 0) {
printf("Return stack underflow\n");
return;
}
data_push(ret_stack[rp].num);
}
// Control flow: IF, ELSE, THEN, BEGIN, UNTIL, WHILE, REPEAT
void do_if(Word* w) {
(void)w;
if (state != 1) {
printf("IF only valid in compile mode\n");
return;
}
Word* zbranch = lookup_word_internal("0branch");
if (zbranch == NULL) {
printf("Fatal: 0branch not found\n");
return;
}
if (compile_idx + 2 > COMPILE_BUF_SIZE) {
printf("Compile buffer full\n");
return;
}
compile_buf[compile_idx++] = (Cell){.word = zbranch};
compile_push((Cell*)&compile_buf[compile_idx]);
compile_idx++; // Reserve offset cell
}
void do_then(Word* w) {
(void)w;
if (state != 1) {
printf("THEN only valid in compile mode\n");
return;
}
Cell* offset_addr = compile_pop();
if (offset_addr == NULL) return;
int32_t offset_idx = (int32_t)(offset_addr - compile_buf);
int32_t target_idx = compile_idx;
offset_addr->num = target_idx - offset_idx;
}
void do_else(Word* w) {
(void)w;
if (state != 1) {
printf("ELSE only valid in compile mode\n");
return;
}
Cell* if_offset = compile_pop();
if (if_offset == NULL) return;
Word* branch_w = lookup_word_internal("branch");
if (branch_w == NULL) {
printf("Fatal: branch not found\n");
return;
}
if (compile_idx + 2 > COMPILE_BUF_SIZE) {
printf("Compile buffer full\n");
return;
}
// Resolve IF offset to jump to after this branch
int32_t if_offset_idx = (int32_t)(if_offset - compile_buf);
int32_t target_idx = compile_idx + 2; // after branch + its offset
if_offset->num = target_idx - if_offset_idx;
// Compile unconditional branch for ELSE part
compile_buf[compile_idx++] = (Cell){.word = branch_w};
compile_push((Cell*)&compile_buf[compile_idx]);
compile_idx++; // Reserve offset cell
}
void do_begin(Word* w) {
(void)w;
if (state != 1) {
printf("BEGIN only valid in compile mode\n");
return;
}
compile_push((Cell*)&compile_buf[compile_idx]);
}
void do_until(Word* w) {
(void)w;
if (state != 1) {
printf("UNTIL only valid in compile mode\n");
return;
}
Cell* begin_addr = compile_pop();
if (begin_addr == NULL) return;
Word* zbranch = lookup_word_internal("0branch");
if (zbranch == NULL) {
printf("Fatal: 0branch not found\n");
return;
}
if (compile_idx + 2 > COMPILE_BUF_SIZE) {
printf("Compile buffer full\n");
return;
}
int32_t begin_idx = (int32_t)(begin_addr - compile_buf);
compile_buf[compile_idx++] = (Cell){.word = zbranch};
int32_t offset_idx = compile_idx;
compile_buf[compile_idx++] = (Cell){.num = begin_idx - offset_idx};
}
void do_while(Word* w) {
(void)w;
if (state != 1) {
printf("WHILE only valid in compile mode\n");
return;
}
Cell* begin_addr = compile_pop();
if (begin_addr == NULL) return;
Word* zbranch = lookup_word_internal("0branch");
if (zbranch == NULL) {
printf("Fatal: 0branch not found\n");
return;
}
if (compile_idx + 2 > COMPILE_BUF_SIZE) {
printf("Compile buffer full\n");
return;
}
compile_buf[compile_idx++] = (Cell){.word = zbranch};
Cell* while_offset = (Cell*)&compile_buf[compile_idx];
compile_idx++; // Reserve offset cell
compile_push(while_offset);
compile_push(begin_addr);
}
void do_repeat(Word* w) {
(void)w;
if (state != 1) {
printf("REPEAT only valid in compile mode\n");
return;
}
Cell* begin_addr = compile_pop();
if (begin_addr == NULL) return;
Cell* while_offset = compile_pop();
if (while_offset == NULL) return;
Word* branch_w = lookup_word_internal("branch");
if (branch_w == NULL) {
printf("Fatal: branch not found\n");
return;
}
if (compile_idx + 2 > COMPILE_BUF_SIZE) {
printf("Compile buffer full\n");
return;
}
// Compile backward branch to begin
int32_t begin_idx = (int32_t)(begin_addr - compile_buf);
compile_buf[compile_idx++] = (Cell){.word = branch_w};
int32_t offset_idx = compile_idx;
compile_buf[compile_idx++] = (Cell){.num = begin_idx - offset_idx};
// Resolve WHILE offset to point after this backward branch
int32_t while_offset_idx = (int32_t)(while_offset - compile_buf);
int32_t target_idx = compile_idx;
while_offset->num = target_idx - while_offset_idx;
}