Files
bux-lang/rt/runtime_freestanding.c
T
dimgigov ec5984762b
ci / build (ubuntu) (push) Has been cancelled
ci / unit + fmt (push) Has been cancelled
ci / examples (push) Has been cancelled
ci / goldens + tools (push) Has been cancelled
ci / apps (push) Has been cancelled
ci / selfhost smoke (push) Has been cancelled
ci / macos smoke (push) Has been cancelled
ci / windows smoke (push) Has been cancelled
ci / CI gate (push) Has been cancelled
selfhost-loop / bootstrap determinism (push) Has been cancelled
feat: try/unwrap payload types, LSP format, macro paste, freestanding runtime
- Type `?`/`!` as Result/Option Ok payload (not always int); fix unwrap C types
- LSP 0.18 document formatting (bux fmt) + VS Code format-on-save
- Macro `:type` generics (Array_New<$t>) and operators-only tt paste
- Ship runtime_freestanding.c + BUX_RUNTIME=freestanding + smokes/examples
2026-07-28 16:56:35 +03:00

485 lines
16 KiB
C

/* Bux Runtime — freestanding / bare-metal research spike (post-v1.0)
*
* Goal: link with `-ffreestanding -nostdlib` without pulling in a hosted libc.
* This is **not** a full embedded platform kit (no Cortex-M startup, no
* linker scripts, no UART drivers). It is a portable base for:
* - compile smoke under -ffreestanding
* - no-libc static link experiments (custom `_start`)
* - future board BSPs that replace the weak I/O hooks
*
* Select: BUX_RUNTIME=freestanding (bootstrap + selfhost)
*
* Memory: fixed bump allocator over a static arena (default 256 KiB).
* Override at compile time: -DBUX_FS_HEAP_BYTES=N
*
* I/O: weak stubs — default print is a no-op; panic spins. Host may override:
* void bux_fs_write(const char* s, unsigned n);
* void bux_fs_halt(int code);
*/
#if defined(__STDC_HOSTED__) && __STDC_HOSTED__ == 1 && !defined(BUX_FS_FORCE)
/* When accidentally compiled as hosted without -ffreestanding, still avoid
* libc — we implement everything we need. */
#endif
/* ── Fixed-width types without stdint.h ────────────────────────────────── */
typedef signed char bux_i8;
typedef unsigned char bux_u8;
typedef short bux_i16;
typedef unsigned short bux_u16;
typedef int bux_i32;
typedef unsigned int bux_u32;
#if defined(__LP64__) || defined(_WIN64) || defined(__x86_64__) || defined(__aarch64__)
typedef long bux_i64;
typedef unsigned long bux_u64;
typedef unsigned long bux_size;
#else
typedef long long bux_i64;
typedef unsigned long long bux_u64;
typedef unsigned int bux_size;
#endif
typedef bux_u8 bux_bool;
#ifndef NULL
# define NULL ((void*)0)
#endif
#ifndef true
# define true 1
# define false 0
#endif
/* ── Heap ─────────────────────────────────────────────────────────────── */
#ifndef BUX_FS_HEAP_BYTES
# define BUX_FS_HEAP_BYTES (256u * 1024u)
#endif
static unsigned char bux_fs_heap[BUX_FS_HEAP_BYTES];
static bux_size bux_fs_heap_off = 0;
/* Weak hooks — board / host may override */
__attribute__((weak)) void bux_fs_write(const char* s, unsigned n) {
(void)s; (void)n;
}
__attribute__((weak)) void bux_fs_halt(int code) {
(void)code;
for (;;) { /* spin */ }
}
/* ── CLI args (empty under freestanding) ──────────────────────────────── */
int g_argc = 0;
char** g_argv = NULL;
int bux_argc(void) { return g_argc; }
char* bux_argv(int index) {
(void)index;
return (char*)"";
}
/* ── Memory ───────────────────────────────────────────────────────────── */
static void bux_fs_zero(void* p, bux_size n) {
unsigned char* b = (unsigned char*)p;
bux_size i;
for (i = 0; i < n; i++) b[i] = 0;
}
void* bux_alloc(bux_size size) {
/* 8-byte align */
bux_size off = (bux_fs_heap_off + 7u) & ~(bux_size)7u;
if (size == 0) size = 1;
if (off + size > (bux_size)BUX_FS_HEAP_BYTES) {
bux_fs_write("OOM\n", 4);
bux_fs_halt(1);
return NULL;
}
void* p = &bux_fs_heap[off];
bux_fs_heap_off = off + size;
bux_fs_zero(p, size);
return p;
}
void* bux_realloc(void* ptr, bux_size size) {
/* Bump allocator cannot free — allocate fresh + copy if needed */
void* n = bux_alloc(size);
if (ptr != NULL && n != NULL && size > 0) {
unsigned char* d = (unsigned char*)n;
unsigned char* s = (unsigned char*)ptr;
bux_size i;
/* unknown old size — best effort copy of `size` bytes */
for (i = 0; i < size; i++) d[i] = s[i];
}
return n;
}
void bux_free(void* ptr) { (void)ptr; /* bump: no-op */ }
/* ── Basic I/O / panic ────────────────────────────────────────────────── */
void bux_print(const char* s) {
if (!s) return;
unsigned n = 0;
while (s[n]) n++;
bux_fs_write(s, n);
}
void bux_println(const char* s) {
bux_print(s ? s : "");
bux_fs_write("\n", 1);
}
void bux_print_int(bux_i64 n) {
char buf[32];
int i = 0;
int neg = 0;
if (n < 0) { neg = 1; n = -n; }
if (n == 0) { buf[i++] = '0'; }
else {
while (n > 0 && i < 30) {
buf[i++] = (char)('0' + (n % 10));
n /= 10;
}
}
if (neg) buf[i++] = '-';
/* reverse */
int a = 0, b = i - 1;
while (a < b) {
char t = buf[a]; buf[a] = buf[b]; buf[b] = t;
a++; b--;
}
bux_fs_write(buf, (unsigned)i);
}
void bux_print_float(double f) {
/* minimal: cast to int for freestanding */
bux_print_int((bux_i64)f);
}
void bux_print_bool(bux_bool b) { bux_print(b ? "true" : "false"); }
void bux_print_char(char c) { bux_fs_write(&c, 1); }
void bux_panic(const char* msg) {
bux_fs_write("PANIC: ", 7);
if (msg) {
unsigned n = 0;
while (msg[n]) n++;
bux_fs_write(msg, n);
}
bux_fs_write("\n", 1);
bux_fs_halt(1);
}
void bux_exit(int code) { bux_fs_halt(code); }
void bux_assert(int cond, const char* file, int line, const char* expr) {
(void)file; (void)line; (void)expr;
if (!cond) bux_panic("assert failed");
}
/* ── Checked arithmetic ───────────────────────────────────────────────── */
bux_i64 bux_div_i64(bux_i64 a, bux_i64 b) {
if (b == 0) bux_panic("division by zero");
return a / b;
}
bux_i64 bux_mod_i64(bux_i64 a, bux_i64 b) {
if (b == 0) bux_panic("modulo by zero");
return a % b;
}
bux_i64 bux_add_i64_checked(bux_i64 a, bux_i64 b) { return a + b; }
bux_i64 bux_sub_i64_checked(bux_i64 a, bux_i64 b) { return a - b; }
bux_i64 bux_mul_i64_checked(bux_i64 a, bux_i64 b) { return a * b; }
bux_i64 bux_neg_i64_checked(bux_i64 a) { return -a; }
/* ── Strings (no libc) ────────────────────────────────────────────────── */
unsigned int bux_strlen(const char* s) {
unsigned int n = 0;
if (!s) return 0;
while (s[n]) n++;
return n;
}
int bux_strlen_c(const char* s) { return (int)bux_strlen(s); }
int bux_strcmp(const char* a, const char* b) {
if (!a) a = "";
if (!b) b = "";
while (*a && *a == *b) { a++; b++; }
return (unsigned char)*a - (unsigned char)*b;
}
int bux_strncmp(const char* a, const char* b, unsigned int n) {
if (!a) a = "";
if (!b) b = "";
unsigned int i;
for (i = 0; i < n; i++) {
if (a[i] != b[i] || a[i] == 0) return (unsigned char)a[i] - (unsigned char)b[i];
}
return 0;
}
char* bux_strcpy(char* dest, const char* src) {
if (!dest) return NULL;
if (!src) { dest[0] = 0; return dest; }
char* d = dest;
while ((*d++ = *src++)) {}
return dest;
}
char* bux_strcat(char* dest, const char* src) {
if (!dest) return NULL;
if (!src) return dest;
char* d = dest;
while (*d) d++;
while ((*d++ = *src++)) {}
return dest;
}
char* bux_strncpy(char* dest, const char* src, unsigned int n) {
if (!dest) return NULL;
unsigned int i = 0;
if (src) {
for (; i < n && src[i]; i++) dest[i] = src[i];
}
for (; i < n; i++) dest[i] = 0;
return dest;
}
double bux_str_to_float(const char* s) {
(void)s;
return 0.0;
}
bux_i64 bux_str_to_int(const char* s) {
if (!s) return 0;
bux_i64 v = 0;
int neg = 0;
if (*s == '-') { neg = 1; s++; }
while (*s >= '0' && *s <= '9') {
v = v * 10 + (*s - '0');
s++;
}
return neg ? -v : v;
}
const char* bux_strstr(const char* haystack, const char* needle) {
if (!haystack || !needle) return NULL;
if (!*needle) return haystack;
for (const char* h = haystack; *h; h++) {
const char* a = h;
const char* b = needle;
while (*a && *b && *a == *b) { a++; b++; }
if (!*b) return h;
}
return NULL;
}
unsigned int bux_str_offset(const char* pos, const char* base) {
if (!pos || !base) return 0;
return (unsigned int)(pos - base);
}
int bux_str_contains(const char* haystack, const char* needle) {
return bux_strstr(haystack, needle) != NULL;
}
int bux_str_is_null(const char* s) { return s == NULL; }
char* bux_str_slice(const char* s, unsigned int start, unsigned int len) {
if (!s) s = "";
unsigned int sl = bux_strlen(s);
if (start > sl) start = sl;
if (start + len > sl) len = sl - start;
char* out = (char*)bux_alloc(len + 1);
unsigned int i;
for (i = 0; i < len; i++) out[i] = s[start + i];
out[len] = 0;
return out;
}
static int is_ws(char c) {
return c == ' ' || c == '\t' || c == '\n' || c == '\r';
}
char* bux_str_trim_left(const char* s) {
if (!s) s = "";
while (*s && is_ws(*s)) s++;
return bux_str_slice(s, 0, bux_strlen(s));
}
char* bux_str_trim_right(const char* s) {
if (!s) s = "";
unsigned int n = bux_strlen(s);
while (n > 0 && is_ws(s[n - 1])) n--;
return bux_str_slice(s, 0, n);
}
char* bux_str_trim(const char* s) {
char* a = bux_str_trim_left(s);
return bux_str_trim_right(a);
}
char* bux_int_to_str(bux_i64 n) {
char buf[32];
int i = 0;
int neg = 0;
if (n < 0) { neg = 1; n = -n; }
if (n == 0) buf[i++] = '0';
else while (n > 0 && i < 30) { buf[i++] = (char)('0' + (n % 10)); n /= 10; }
if (neg) buf[i++] = '-';
int a = 0, b = i - 1;
while (a < b) { char t = buf[a]; buf[a] = buf[b]; buf[b] = t; a++; b--; }
buf[i] = 0;
return bux_str_slice(buf, 0, (unsigned)i);
}
char* bux_float_to_string(double f) { return bux_int_to_str((bux_i64)f); }
unsigned int bux_str_split_count(const char* s, const char* delim) {
(void)s; (void)delim;
return 0;
}
char* bux_str_split_part(const char* s, const char* delim, unsigned int index) {
(void)s; (void)delim; (void)index;
return (char*)"";
}
char* bux_str_join2(const char* a, const char* b, const char* sep) {
unsigned int na = bux_strlen(a), nb = bux_strlen(b), ns = bux_strlen(sep);
char* out = (char*)bux_alloc(na + ns + nb + 1);
unsigned int i = 0, j;
for (j = 0; j < na; j++) out[i++] = a[j];
for (j = 0; j < ns; j++) out[i++] = sep[j];
for (j = 0; j < nb; j++) out[i++] = b[j];
out[i] = 0;
return out;
}
char* bux_str_format(const char* fmt, const char* a0, const char* a1, const char* a2, const char* a3) {
(void)a1; (void)a2; (void)a3;
/* minimal: return fmt or a0 */
if (a0 && a0[0]) return bux_str_slice(a0, 0, bux_strlen(a0));
if (fmt) return bux_str_slice(fmt, 0, bux_strlen(fmt));
return (char*)"";
}
char* bux_escape_c_string(const char* s, int len) {
(void)len;
return s ? bux_str_slice(s, 0, bux_strlen(s)) : (char*)"";
}
/* StringBuilder stubs (shape-compatible enough for unused mono) */
typedef struct {
char* data;
unsigned int len;
unsigned int cap;
} BuxStringBuilder;
void bux_sb_append(BuxStringBuilder* sb, const char* s) {
(void)sb; (void)s;
}
void bux_sb_append_int(BuxStringBuilder* sb, bux_i64 n) { (void)sb; (void)n; }
void bux_sb_append_float(BuxStringBuilder* sb, double f) { (void)sb; (void)f; }
void bux_sb_append_char(BuxStringBuilder* sb, char c) { (void)sb; (void)c; }
const char* bux_sb_build(BuxStringBuilder* sb) { return sb && sb->data ? sb->data : ""; }
void bux_sb_free(BuxStringBuilder* sb) { (void)sb; }
/* ── FS / path / OS — unavailable ─────────────────────────────────────── */
char* bux_read_file(const char* path) { (void)path; return NULL; }
int bux_write_file(const char* path, const char* content) {
(void)path; (void)content;
return -1;
}
int bux_file_exists(const char* path) { (void)path; return 0; }
char* bux_path_join(const char* a, const char* b) {
return bux_str_join2(a ? a : "", b ? b : "", "/");
}
char* bux_path_parent(const char* path) {
(void)path;
return (char*)".";
}
char* bux_path_ext(const char* path) {
(void)path;
return (char*)"";
}
int bux_mkdir_if_needed(const char* path) { (void)path; return -1; }
int bux_dir_exists(const char* path) { (void)path; return 0; }
char** bux_list_dir(const char* dir, const char* ext, int* out_count) {
(void)dir; (void)ext;
if (out_count) *out_count = 0;
return NULL;
}
/* ── Math (integer only; float ops soft-stub) ─────────────────────────── */
double bux_sqrt(double x) { return x; }
double bux_pow(double x, double y) { (void)y; return x; }
bux_i64 bux_abs_i64(bux_i64 x) { return x < 0 ? -x : x; }
double bux_abs_f64(double x) { return x < 0 ? -x : x; }
bux_i64 bux_min_i64(bux_i64 a, bux_i64 b) { return a < b ? a : b; }
bux_i64 bux_max_i64(bux_i64 a, bux_i64 b) { return a > b ? a : b; }
double bux_min_f64(double a, double b) { return a < b ? a : b; }
double bux_max_f64(double a, double b) { return a > b ? a : b; }
unsigned int bux_hash_bytes(const void* ptr, bux_size size) {
const unsigned char* p = (const unsigned char*)ptr;
unsigned int h = 2166136261u;
bux_size i;
if (!p) return 0;
for (i = 0; i < size; i++) {
h ^= p[i];
h *= 16777619u;
}
return h;
}
int bux_mem_eq(const void* a, const void* b, bux_size size) {
const unsigned char* x = (const unsigned char*)a;
const unsigned char* y = (const unsigned char*)b;
bux_size i;
if (!x || !y) return x == y;
for (i = 0; i < size; i++) if (x[i] != y[i]) return 0;
return 1;
}
unsigned int bux_hash_string(const char* s) {
return bux_hash_bytes(s, bux_strlen(s));
}
const char* bux_getenv(const char* name) { (void)name; return ""; }
const char* bux_cc_ld_stable(void) { return ""; }
int bux_setenv(const char* name, const char* value) {
(void)name; (void)value;
return -1;
}
const char* bux_getcwd(void) { return "."; }
int bux_chdir(const char* path) { (void)path; return -1; }
void bux_install_stop_handlers(void) {}
int bux_should_stop(void) { return 0; }
void bux_set_stop_listen_fd(int fd) { (void)fd; }
bux_i64 bux_time_ms(void) { return 0; }
bux_i64 bux_time_us(void) { return 0; }
/* ── Task / net / crypto — hard stubs ─────────────────────────────────── */
int bux_task_spawn(void* fn, void* arg) { (void)fn; (void)arg; return -1; }
void bux_task_yield(void) {}
void bux_task_sleep_ms(int ms) { (void)ms; }
int bux_chan_new(int cap) { (void)cap; return -1; }
int bux_chan_send(int id, void* msg) { (void)id; (void)msg; return -1; }
void* bux_chan_recv(int id) { (void)id; return NULL; }
void bux_chan_close(int id) { (void)id; }
int bux_tcp_listen(int port) { (void)port; return -1; }
int bux_tcp_accept(int fd) { (void)fd; return -1; }
int bux_tcp_connect(const char* host, int port) {
(void)host; (void)port;
return -1;
}
int bux_tcp_send(int fd, const void* buf, int n) {
(void)fd; (void)buf; (void)n;
return -1;
}
int bux_tcp_recv(int fd, void* buf, int n) {
(void)fd; (void)buf; (void)n;
return -1;
}
void bux_tcp_close(int fd) { (void)fd; }
void* bux_tls_server_ctx(const char* cert, const char* key) {
(void)cert; (void)key;
return NULL;
}
void* bux_tls_server_ctx_ex(const char* cert, const char* key, const char* ca) {
(void)cert; (void)key; (void)ca;
return NULL;
}
int bux_tls_accept(void* ctx, int fd) { (void)ctx; (void)fd; return -1; }
int bux_tls_send(int h, const void* buf, int n) {
(void)h; (void)buf; (void)n;
return -1;
}
int bux_tls_recv(int h, void* buf, int n) {
(void)h; (void)buf; (void)n;
return -1;
}
void bux_tls_close(int h) { (void)h; }
const char* bux_tls_error(void) { return "tls unavailable (freestanding)"; }
/* ── Optional bare `_start` for -nostdlib link experiments ───────────── */
#ifdef BUX_FS_PROVIDE_START
extern int main(int argc, char** argv);
void _start(void) {
g_argc = 0;
g_argv = NULL;
int code = main(0, NULL);
bux_fs_halt(code);
}
#endif