code wiki / (root) / nx_runtime.nx

nx_runtime.nx source

↩ module page · 185 lines · 4693 B

1// runtime.nx -- core NishiLang runtime. 2// 3// Intended to be used via copy-paste or future module-import. All 4// routines here call only __syscall for their primitives; no libc. 5// 6// Contents: 7// * Arena allocator (bump pointer, never frees) 8// * strlen / strcmp / strneq / memcpy / memset 9// * itoa / atoi (base 10 and hex) 10// * print / println helpers using syscall write to stdout 11// * die() -- print message + exit(1) 12 13// Syscalls live in syscalls.nx now (shared across all modules). 14// nx_safety_envelope: 15// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 16// sil_target: SIL1 17// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 18// verdict: NOT_YET_EVALUATED 19 20import "nx_syscalls.nx" 21 22// ===== arena allocator ============================================== 23// 24// A single mmap-backed bump region. Fast, never frees. Fine for a 25// compiler that runs once, allocates, writes output, exits. 26// 27// Layout: 64-byte header + payload. Caller allocates and manages 28// a pointer to the Arena struct. 29 30struct Arena { 31 base: *u8, // start of payload 32 pos: i64, // next allocation offset (bytes) 33 cap: i64, // total bytes 34} 35 36func arena_new(size: i64) -> *Arena { 37 let raw: *u8 = sys_mmap(size + 64) 38 let a: *Arena = raw as *Arena 39 let base: *u8 = (raw as i64 + 64) as *u8 40 a.base = base 41 a.pos = 0 42 a.cap = size 43 return a 44} 45 46// Allocate `n` bytes, 8-byte aligned. Returns a byte pointer. 47func arena_alloc(a: *Arena, n: i64) -> *u8 { 48 let p: i64 = (a.pos + 7) & -8 // round up to 8 49 if p + n > a.cap { 50 // OOM -- grow would need another mmap; for now just crash 51 // politely by returning 0 (dereferenced access will SEGV). 52 return 0 as *u8 53 } 54 a.pos = p + n 55 let base: *u8 = a.base 56 return (base as i64 + p) as *u8 57} 58 59// ===== string primitives ============================================ 60 61func strlen(s: *u8) -> i64 { 62 var n: i64 = 0 63 while s[n] != 0 { n = n + 1 } 64 return n 65} 66 67// Equal-length byte compare. Returns 1 on match, 0 otherwise. 68func strneq(a: *u8, b: *u8, n: i64) -> i64 { 69 var i: i64 = 0 70 while i < n { 71 if a[i] != b[i] { return 0 } 72 i = i + 1 73 } 74 return 1 75} 76 77// Full equality including terminator. 78func streq(a: *u8, b: *u8) -> i64 { 79 let la: i64 = strlen(a) 80 let lb: i64 = strlen(b) 81 if la != lb { return 0 } 82 return strneq(a, b, la) 83} 84 85func memcpy(dst: *u8, src: *u8, n: i64) -> i64 { 86 var i: i64 = 0 87 while i < n { 88 dst[i] = src[i] 89 i = i + 1 90 } 91 return 0 92} 93 94func memset(dst: *u8, v: i64, n: i64) -> i64 { 95 var i: i64 = 0 96 while i < n { 97 dst[i] = v & 0xFF 98 i = i + 1 99 } 100 return 0 101} 102 103// ===== number parsing / formatting ================================== 104 105// Decode decimal digits at s[0..] until a non-digit byte. Negative 106// sign allowed as the first character. Stops on first non-digit. 107func atoi(s: *u8) -> i64 { 108 var i: i64 = 0 109 var sign: i64 = 1 110 if s[0] == 0x2D { 111 sign = -1 112 i = 1 113 } 114 var v: i64 = 0 115 while s[i] >= 0x30 { 116 if s[i] > 0x39 { break } 117 v = v * 10 + (s[i] - 0x30) 118 i = i + 1 119 } 120 return v * sign 121} 122 123// Write decimal representation of `n` into buf, returns length. 124// buf must have >= 24 bytes for i64 range. Null-terminates. 125func itoa(n: i64, buf: *u8) -> i64 { 126 if n == 0 { 127 buf[0] = 0x30 128 buf[1] = 0 129 return 1 130 } 131 var x: i64 = n 132 var neg: i64 = 0 133 if x < 0 { 134 neg = 1 135 x = 0 - x 136 } 137 // Generate digits in reverse into a scratch. 138 let scratch_raw: *u8 = sys_mmap(64) 139 var sp: i64 = 0 140 while x > 0 { 141 scratch_raw[sp] = 0x30 + (x % 10) 142 x = x / 10 143 sp = sp + 1 144 } 145 var out: i64 = 0 146 if neg { 147 buf[0] = 0x2D 148 out = 1 149 } 150 var i: i64 = sp - 1 151 while i >= 0 { 152 buf[out] = scratch_raw[i] 153 out = out + 1 154 i = i - 1 155 } 156 buf[out] = 0 157 return out 158} 159 160// ===== printing ===================================================== 161 162func print(s: *u8) -> i64 { 163 return sys_write(1, s, strlen(s)) 164} 165 166func println(s: *u8) -> i64 { 167 sys_write(1, s, strlen(s)) 168 let nl_raw: *u8 = sys_mmap(8) 169 nl_raw[0] = 0x0A 170 return sys_write(1, nl_raw, 1) 171} 172 173func print_i64(n: i64) -> i64 { 174 let buf: *u8 = sys_mmap(32) 175 let len: i64 = itoa(n, buf) 176 return sys_write(1, buf, len) 177} 178 179// Abort with a message. Writes to stderr (fd 2), exits 1. 180func die(msg: *u8) -> i64 { 181 sys_write(2, msg, strlen(msg)) 182 return sys_exit(1) 183} 184 185// Library only; self-test lives in runtime_test.nx.