feat: abstract I/O and strings for freestanding compilation

Co-authored-by: aider (openrouter/moonshotai/kimi-k2.6) <aider@aider.chat>
This commit is contained in:
2026-05-04 10:17:43 +03:00
parent 19c8608c76
commit dbf4eb5d0e
8 changed files with 216 additions and 42 deletions
+13 -10
View File
@@ -1,19 +1,22 @@
CC = gcc CC = clang
CFLAGS = -Wall -Wextra -g -std=c11 -D_POSIX_C_SOURCE=200809L CFLAGS = -Wall -Wextra -g -std=c11
LDFLAGS = COMMON_SRCS = forth_core.c forth_dict.c forth_words.c forth_interp.c main.c
SRCS = forth_core.c forth_dict.c forth_words.c forth_interp.c main.c
OBJS = $(SRCS:.c=.o) HOSTED_SRCS = $(COMMON_SRCS) platform_hosted.c
FREESTANDING_SRCS = $(COMMON_SRCS) platform_freestanding.c
TARGET = forth TARGET = forth
FREESTANDING_TARGET = forth_freestanding
all: $(TARGET) all: $(TARGET)
$(TARGET): $(OBJS) $(TARGET): $(HOSTED_SRCS) forth.h
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(CC) $(CFLAGS) -D_POSIX_C_SOURCE=200809L -o $@ $(HOSTED_SRCS)
%.o: %.c forth.h $(FREESTANDING_TARGET): $(FREESTANDING_SRCS) forth.h
$(CC) $(CFLAGS) -c $< -o $@ $(CC) $(CFLAGS) -ffreestanding -nostdlib -fno-stack-protector -o $@ $(FREESTANDING_SRCS)
clean: clean:
rm -f $(OBJS) $(TARGET) rm -f $(TARGET) $(FREESTANDING_TARGET)
.PHONY: all clean .PHONY: all clean
+37 -17
View File
@@ -1,29 +1,46 @@
#ifndef FORTH_H #ifndef FORTH_H
#define FORTH_H #define FORTH_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdint.h> #include <stdint.h>
#include <inttypes.h> #include <stddef.h>
#include <stdarg.h>
// Configuration (all hard limits removed) // Configuration (all hard limits removed)
#define MAX_NAME_LEN 31 #define MAX_NAME_LEN 31
#define FORTH_EOF (-1)
/* Portable panic hook. Override for bare-metal reset/handling. */ /* Platform interface -- must be provided by the target platform layer. */
#ifndef forth_panic void forth_putchar(char c);
#define forth_panic() do { for (;;) ; } while (0) int forth_getchar(void);
#endif void forth_printf(const char* fmt, ...);
void forth_fflush(void);
void forth_panic(void);
/* Portable I/O hooks. Define FORTH_CUSTOM_IO before including this header /* Platform string-to-number (base 10 is required). */
* to provide bare-metal UART (or other) implementations. */ int64_t forth_strtoll(const char* str, char** endptr, int base);
#ifndef FORTH_CUSTOM_IO
#define forth_putchar(c) putchar(c) /* Portable character classification. */
#define forth_getchar() getchar() static inline int forth_isspace(int c) {
#define forth_printf(...) printf(__VA_ARGS__) return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v';
#define forth_fflush() fflush(stdout) }
#endif
/* Portable string operations. */
static inline size_t forth_strlen(const char* s) {
size_t n = 0;
while (s[n]) n++;
return n;
}
static inline void forth_memcpy(void* dst, const void* src, size_t n) {
char* d = dst;
const char* s = src;
while (n--) *d++ = *s++;
}
static inline int forth_strcmp(const char* a, const char* b) {
while (*a && *a == *b) { a++; b++; }
return (unsigned char)*a - (unsigned char)*b;
}
/* Static memory sizes (override before including forth.h) */ /* Static memory sizes (override before including forth.h) */
#ifndef FORTH_DATA_STACK_SIZE #ifndef FORTH_DATA_STACK_SIZE
@@ -105,6 +122,9 @@ extern Word* w_branch;
extern Word* w_zbranch; extern Word* w_zbranch;
extern Word* w_dot_quote_inner; extern Word* w_dot_quote_inner;
// Entry point
void forth_run(void);
// Core function prototypes // Core function prototypes
void data_push(int64_t val); void data_push(int64_t val);
int64_t data_pop(void); int64_t data_pop(void);
+4 -4
View File
@@ -5,10 +5,10 @@ Word* add_primitive(const char* name, void (*code)(Word*), uint8_t flags) {
w->prev = dict_head; w->prev = dict_head;
dict_head = w; dict_head = w;
size_t len = strlen(name); size_t len = forth_strlen(name);
if (len > MAX_NAME_LEN) len = MAX_NAME_LEN; if (len > MAX_NAME_LEN) len = MAX_NAME_LEN;
w->flags = flags | (uint8_t)len; w->flags = flags | (uint8_t)len;
memcpy(w->name, name, len); forth_memcpy(w->name, name, len);
w->name[len] = '\0'; w->name[len] = '\0';
w->code = code; w->code = code;
w->body = NULL; w->body = NULL;
@@ -18,14 +18,14 @@ Word* add_primitive(const char* name, void (*code)(Word*), uint8_t flags) {
Word* lookup_word(const char* name) { Word* lookup_word(const char* name) {
for (Word* w = dict_head; w != NULL; w = w->prev) { for (Word* w = dict_head; w != NULL; w = w->prev) {
if (w->flags & (1 << 6)) continue; if (w->flags & (1 << 6)) continue;
if (strcmp(w->name, name) == 0) return w; if (forth_strcmp(w->name, name) == 0) return w;
} }
return NULL; return NULL;
} }
Word* lookup_word_internal(const char* name) { Word* lookup_word_internal(const char* name) {
for (Word* w = dict_head; w != NULL; w = w->prev) { for (Word* w = dict_head; w != NULL; w = w->prev) {
if (strcmp(w->name, name) == 0) return w; if (forth_strcmp(w->name, name) == 0) return w;
} }
return NULL; return NULL;
} }
+5 -5
View File
@@ -3,7 +3,7 @@
// Input tokenizer (unchanged) // Input tokenizer (unchanged)
char* next_token(void) { char* next_token(void) {
if (input_ptr == NULL) return NULL; if (input_ptr == NULL) return NULL;
while (*input_ptr != '\0' && isspace((unsigned char)*input_ptr)) { while (*input_ptr != '\0' && forth_isspace((unsigned char)*input_ptr)) {
input_ptr++; input_ptr++;
} }
if (*input_ptr == '\0') return NULL; if (*input_ptr == '\0') return NULL;
@@ -63,10 +63,10 @@ void process_token(const char* token) {
} }
} else { // Try to parse as number } else { // Try to parse as number
char* end; char* end;
long long v = strtoll(token, &end, 10); int64_t v = forth_strtoll(token, &end, 10);
if (end != token && *end == '\0') { if (end != token && *end == '\0') {
if (state == 0) { if (state == 0) {
data_push((int64_t)v); data_push(v);
} else { // Compile lit + number } else { // Compile lit + number
if (!w_lit) { if (!w_lit) {
forth_printf("Fatal: lit word not found\n"); forth_printf("Fatal: lit word not found\n");
@@ -91,13 +91,13 @@ void outer_interpreter(void) {
int c; int c;
while (i < sizeof(line_buf) - 1) { while (i < sizeof(line_buf) - 1) {
c = forth_getchar(); c = forth_getchar();
if (c == EOF || c == '\n' || c == '\r') { if (c == FORTH_EOF || c == '\n' || c == '\r') {
break; break;
} }
line_buf[i++] = (char)c; line_buf[i++] = (char)c;
} }
line_buf[i] = '\0'; line_buf[i] = '\0';
if (i == 0 && c == EOF) { if (i == 0 && c == FORTH_EOF) {
break; break;
} }
input_buf = line_buf; input_buf = line_buf;
+4 -4
View File
@@ -309,7 +309,7 @@ void do_zero_gt(Word* w) {
void do_dot(Word* w) { void do_dot(Word* w) {
(void)w; (void)w;
if (data_sp < 0) return; if (data_sp < 0) return;
forth_printf("%" PRId64 " ", data_pop()); forth_printf("%lld ", (long long)data_pop());
forth_fflush(); forth_fflush();
} }
@@ -329,7 +329,7 @@ void do_emit(Word* w) {
void do_key(Word* w) { void do_key(Word* w) {
(void)w; (void)w;
int c = forth_getchar(); int c = forth_getchar();
data_push(c == EOF ? -1 : c); data_push(c == FORTH_EOF ? -1 : c);
} }
void do_dot_quote(Word* w) { void do_dot_quote(Word* w) {
@@ -337,7 +337,7 @@ void do_dot_quote(Word* w) {
if (state == 0) { if (state == 0) {
// Interpret mode: print immediately // Interpret mode: print immediately
if (input_ptr == NULL) { forth_printf("Missing string\n"); return; } if (input_ptr == NULL) { forth_printf("Missing string\n"); return; }
while (*input_ptr && isspace((unsigned char)*input_ptr)) input_ptr++; while (*input_ptr && forth_isspace((unsigned char)*input_ptr)) input_ptr++;
if (*input_ptr != '"') { forth_printf("Expected \" to start string\n"); return; } if (*input_ptr != '"') { forth_printf("Expected \" to start string\n"); return; }
input_ptr++; input_ptr++;
char* start = input_ptr; char* start = input_ptr;
@@ -349,7 +349,7 @@ void do_dot_quote(Word* w) {
} else { } else {
// Compile mode: compile string for runtime // Compile mode: compile string for runtime
if (input_ptr == NULL) { forth_printf("Missing string\n"); return; } if (input_ptr == NULL) { forth_printf("Missing string\n"); return; }
while (*input_ptr && isspace((unsigned char)*input_ptr)) input_ptr++; while (*input_ptr && forth_isspace((unsigned char)*input_ptr)) input_ptr++;
if (*input_ptr != '"') { forth_printf("Expected \" to start string\n"); return; } if (*input_ptr != '"') { forth_printf("Expected \" to start string\n"); return; }
input_ptr++; input_ptr++;
char* start = input_ptr; char* start = input_ptr;
+1 -2
View File
@@ -1,6 +1,6 @@
#include "forth.h" #include "forth.h"
int main(void) { void forth_run(void) {
here = user_mem; here = user_mem;
// Hidden words first // Hidden words first
@@ -104,5 +104,4 @@ int main(void) {
// Start outer interpreter // Start outer interpreter
outer_interpreter(); outer_interpreter();
return 0;
} }
+114
View File
@@ -0,0 +1,114 @@
#include "forth.h"
#include <stdarg.h>
static inline long syscall3(long n, long a1, long a2, long a3) {
long ret;
__asm__ volatile ("syscall"
: "=a"(ret)
: "a"(n), "D"(a1), "S"(a2), "d"(a3)
: "rcx", "r11", "memory");
return ret;
}
static inline long syscall1(long n, long a1) {
long ret;
__asm__ volatile ("syscall"
: "=a"(ret)
: "a"(n), "D"(a1)
: "rcx", "r11", "memory");
return ret;
}
void forth_putchar(char c) {
char ch = c;
syscall3(1, 1, (long)&ch, 1);
}
void forth_fflush(void) {
}
int forth_getchar(void) {
unsigned char c;
long n = syscall3(0, 0, (long)&c, 1);
if (n <= 0) return FORTH_EOF;
return (int)c;
}
void forth_panic(void) {
syscall1(60, 1);
__builtin_unreachable();
}
int64_t forth_strtoll(const char* str, char** endptr, int base) {
const char* s = str;
int neg = 0;
int64_t val = 0;
while (forth_isspace(*s)) s++;
if (*s == '-') { neg = 1; s++; }
else if (*s == '+') { s++; }
while (*s >= '0' && *s <= '9') {
val = val * base + (*s - '0');
s++;
}
if (endptr) *endptr = (char*)s;
return neg ? -val : val;
}
static void print_str(const char* s) {
while (*s) forth_putchar(*s++);
}
static void print_int(long long v) {
char buf[32];
int i = 30;
int neg = 0;
if (v < 0) { neg = 1; v = -v; }
buf[31] = '\0';
if (v == 0) {
buf[i--] = '0';
} else {
while (v > 0 && i >= 0) {
buf[i--] = '0' + (v % 10);
v /= 10;
}
}
if (neg) buf[i--] = '-';
print_str(&buf[i + 1]);
}
void forth_printf(const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
while (*fmt) {
if (*fmt == '%' && *(fmt + 1)) {
fmt++;
if (*fmt == 's') {
print_str(va_arg(ap, const char*));
} else if (*fmt == 'c') {
forth_putchar((char)va_arg(ap, int));
} else if (*fmt == '%') {
forth_putchar('%');
} else if (*fmt == 'l') {
if (*(fmt + 1) == 'l' && (*(fmt + 2) == 'd' || *(fmt + 2) == 'i' || *(fmt + 2) == 'u')) {
fmt += 2;
print_int(va_arg(ap, long long));
}
} else if (*fmt == 'd' || *fmt == 'i' || *fmt == 'u') {
print_int(va_arg(ap, int));
} else {
forth_putchar('%');
forth_putchar(*fmt);
}
} else {
forth_putchar(*fmt);
}
fmt++;
}
va_end(ap);
}
void _start(void) {
forth_run();
syscall1(60, 0);
__builtin_unreachable();
}
+38
View File
@@ -0,0 +1,38 @@
#include "forth.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
void forth_putchar(char c) {
putchar((unsigned char)c);
}
int forth_getchar(void) {
return getchar();
}
void forth_fflush(void) {
fflush(stdout);
}
void forth_panic(void) {
for (;;);
}
int64_t forth_strtoll(const char* str, char** endptr, int base) {
return (int64_t)strtoll(str, endptr, base);
}
void forth_printf(const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}
int main(void) {
forth_run();
return 0;
}