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