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:
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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,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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user