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.