From 330370e7408898fcdb802d347366945f73f243c6 Mon Sep 17 00:00:00 2001 From: Emin Arslan Date: Sun, 3 May 2026 17:30:46 +0300 Subject: [PATCH] feat: implement missing Forth features and fix compiler warnings Co-authored-by: aider (openrouter/moonshotai/kimi-k2.6) --- forth_dict.c | 2 +- forth_words.c | 414 ++++++++++++++++++++++++++++++++++++++++++++++---- main.c | 29 +++- 3 files changed, 411 insertions(+), 34 deletions(-) diff --git a/forth_dict.c b/forth_dict.c index a5eb0f8..f7d7374 100644 --- a/forth_dict.c +++ b/forth_dict.c @@ -12,7 +12,7 @@ Word* add_primitive(const char* name, void (*code)(Word*), uint8_t flags) { size_t len = strlen(name); if (len > MAX_NAME_LEN) len = MAX_NAME_LEN; w->flags = flags | (uint8_t)len; - strncpy(w->name, name, len); + memcpy(w->name, name, len); w->name[len] = '\0'; w->code = code; w->body = NULL; diff --git a/forth_words.c b/forth_words.c index b35c95c..da11398 100644 --- a/forth_words.c +++ b/forth_words.c @@ -2,16 +2,19 @@ // 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]; @@ -20,11 +23,13 @@ void do_swap(Word* w) { } 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]; @@ -35,6 +40,7 @@ void do_rot(Word* w) { } 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]; @@ -45,12 +51,14 @@ void do_minus_rot(Word* w) { } 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]; @@ -60,6 +68,7 @@ void do_tuck(Word* w) { // Arithmetic operations void do_add(Word* w) { + (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); @@ -67,6 +76,7 @@ void do_add(Word* w) { } void do_sub(Word* w) { + (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); @@ -74,6 +84,7 @@ void do_sub(Word* w) { } void do_mul(Word* w) { + (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); @@ -81,6 +92,7 @@ void do_mul(Word* w) { } void do_div(Word* w) { + (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); @@ -94,6 +106,7 @@ void do_div(Word* w) { } void do_mod(Word* w) { + (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); @@ -107,6 +120,7 @@ void do_mod(Word* w) { } void do_slash_mod(Word* w) { + (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); @@ -121,37 +135,44 @@ void do_slash_mod(Word* w) { } 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(); @@ -159,6 +180,7 @@ void do_min(Word* w) { } void do_max(Word* w) { + (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); @@ -167,6 +189,7 @@ void do_max(Word* w) { // Logic operations void do_and(Word* w) { + (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); @@ -174,6 +197,7 @@ void do_and(Word* w) { } void do_or(Word* w) { + (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); @@ -181,6 +205,7 @@ void do_or(Word* w) { } void do_xor(Word* w) { + (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); @@ -188,11 +213,13 @@ void do_xor(Word* w) { } 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(); @@ -200,6 +227,7 @@ void do_lshift(Word* w) { } void do_rshift(Word* w) { + (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); @@ -208,13 +236,15 @@ void do_rshift(Word* w) { // 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); // Forth uses -1 for true, 0 for false + 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(); @@ -222,6 +252,7 @@ void do_neq(Word* w) { } void do_lt(Word* w) { + (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); @@ -229,6 +260,7 @@ void do_lt(Word* w) { } void do_gt(Word* w) { + (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); @@ -236,6 +268,7 @@ void do_gt(Word* w) { } void do_lte(Word* w) { + (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); @@ -243,6 +276,7 @@ void do_lte(Word* w) { } void do_gte(Word* w) { + (void)w; if (sp < 1) return; int32_t b = data_pop(); int32_t a = data_pop(); @@ -250,18 +284,21 @@ void do_gte(Word* w) { } 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); @@ -269,28 +306,33 @@ void do_zero_gt(Word* w) { // 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) { @@ -351,7 +393,7 @@ void do_dot_quote(Word* w) { printf("Fatal: do_dot_quote_inner not found\n"); return; } - if (compile_idx + 2 + len > COMPILE_BUF_SIZE) { + if (compile_idx + 2 + (int32_t)len > COMPILE_BUF_SIZE) { printf("Compile buffer full\n"); return; } @@ -366,6 +408,7 @@ void do_dot_quote(Word* w) { } 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++; @@ -377,6 +420,7 @@ void do_dot_quote_inner(Word* w) { } 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 @@ -388,6 +432,7 @@ void do_words(Word* w) { // Control flow operations void do_exit(Word* w) { + (void)w; Cell* ret_addr = ret_pop_ip(); ip = ret_addr; } @@ -400,24 +445,29 @@ void do_docolon(Word* w) { } 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; } - strncpy(compiling_name, name, MAX_NAME_LEN); - compiling_name[MAX_NAME_LEN] = '\0'; + 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; @@ -452,7 +502,7 @@ void do_semicolon(Word* 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 - strncpy(new_w->name, compiling_name, len); + memcpy(new_w->name, compiling_name, len); new_w->name[len] = '\0'; new_w->code = do_docolon; new_w->body = &dict_bodies[body_idx]; @@ -462,12 +512,14 @@ void do_semicolon(Word* w) { } void do_branch(Word* w) { + (void)w; // Unconditional branch: ip points to offset cell int32_t offset = ip->num; - ip += offset; // Jump offset cells (offset is relative to after the offset cell) + 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) { @@ -478,29 +530,331 @@ void do_zero_branch(Word* w) { } } -// Memory operations (stubs for now) -void do_fetch(Word* w) { /* TODO */ } -void do_store(Word* w) { /* TODO */ } -void do_plus_store(Word* w) { /* TODO */ } -void do_cfetch(Word* w) { /* TODO */ } -void do_cstore(Word* w) { /* TODO */ } -void do_variable(Word* w) { /* TODO */ } -void do_constant(Word* w) { /* TODO */ } -void do_do_var(Word* w) { /* TODO */ } -void do_do_const(Word* w) { /* TODO */ } -void do_here(Word* w) { /* TODO */ } -void do_allot(Word* w) { /* TODO */ } +// 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]); +} -// Return stack operations (stubs for now) -void do_to_r(Word* w) { /* TODO */ } -void do_r_from(Word* w) { /* TODO */ } -void do_r_fetch(Word* w) { /* TODO */ } +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; +} -// Additional control flow stubs -void do_if(Word* w) { /* TODO */ } -void do_else(Word* w) { /* TODO */ } -void do_then(Word* w) { /* TODO */ } -void do_begin(Word* w) { /* TODO */ } -void do_until(Word* w) { /* TODO */ } -void do_while(Word* w) { /* TODO */ } -void do_repeat(Word* w) { /* TODO */ } +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("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("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("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("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("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; +} diff --git a/main.c b/main.c index 4410946..cc7635e 100644 --- a/main.c +++ b/main.c @@ -7,8 +7,8 @@ int main(void) { add_primitive("docolon", do_docolon, 1 << 6); // Hidden add_primitive("lit", do_lit, 1 << 6); // Hidden add_primitive("do_dot_quote_inner", do_dot_quote_inner, 1 << 6); // Hidden - add_primitive("0branch", do_zero_branch, 1 << 6); // Hidden (for IF) - add_primitive("branch", do_branch, 1 << 6); // Hidden (for ELSE, BEGIN) + add_primitive("0branch", do_zero_branch, 1 << 6); // Hidden (for IF, UNTIL, WHILE) + add_primitive("branch", do_branch, 1 << 6); // Hidden (for ELSE, REPEAT) // Public primitives // Stack ops @@ -64,9 +64,32 @@ int main(void) { add_primitive(".\"", do_dot_quote, 1 << 7); // Immediate add_primitive("words", do_words, 0); - // Compilation words + // Memory + add_primitive("@", do_fetch, 0); + add_primitive("!", do_store, 0); + add_primitive("+!", do_plus_store, 0); + add_primitive("c@", do_cfetch, 0); + add_primitive("c!", do_cstore, 0); + add_primitive("variable", do_variable, 0); + add_primitive("constant", do_constant, 0); + add_primitive("here", do_here, 0); + add_primitive("allot", do_allot, 0); + + // Return stack + add_primitive(">r", do_to_r, 0); + add_primitive("r>", do_r_from, 0); + add_primitive("r@", do_r_fetch, 0); + + // Compilation / control flow add_primitive(":", do_colon, 0); add_primitive(";", do_semicolon, 1 << 7); // Immediate + add_primitive("if", do_if, 1 << 7); // Immediate + add_primitive("else", do_else, 1 << 7); // Immediate + add_primitive("then", do_then, 1 << 7); // Immediate + add_primitive("begin", do_begin, 1 << 7); // Immediate + add_primitive("until", do_until, 1 << 7); // Immediate + add_primitive("while", do_while, 1 << 7); // Immediate + add_primitive("repeat", do_repeat, 1 << 7); // Immediate // Start outer interpreter outer_interpreter();