nomen-lang 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core/System/List.nm +12 -0
- package/core/System/StringBuilder.nm +126 -3
- package/core/System/Text/Regex.nm +345 -91
- package/dist/index.mjs +492 -62
- package/package.json +1 -1
- package/src/test.ts +45 -5
package/core/System/List.nm
CHANGED
|
@@ -61,6 +61,18 @@ pub struct List<T>: Viewable {
|
|
|
61
61
|
self.items.replace(i, value)
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
// Bounds-checked write that traps when i is outside [0, self.length) —
|
|
65
|
+
// for indices that can't be proven at compile time (cross-module calls
|
|
66
|
+
// can never discharge `set`'s parameter constraint) and out-of-range is
|
|
67
|
+
// a bug, not a case.
|
|
68
|
+
pub func set_or_panic = (ref self, int i, move T value) {
|
|
69
|
+
if i >= 0 && i < self.length {
|
|
70
|
+
self.set(i, value)
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
panic("index out of range")
|
|
74
|
+
}
|
|
75
|
+
|
|
64
76
|
// A deep copy: a fresh List<T> with an independent copy of every element.
|
|
65
77
|
// For owning element types (strings, owning value structs) each slot in the
|
|
66
78
|
// copy owns its own heap data (Buffer deep-copies on store), so the source
|
|
@@ -16,7 +16,9 @@ pub struct StringBuilder {
|
|
|
16
16
|
// Ensure capacity for at least `needed` bytes. Grows geometrically.
|
|
17
17
|
// Plain Nomen + the memory externs — no pointer arithmetic needed, so
|
|
18
18
|
// this doesn't even require an unsafe context.
|
|
19
|
-
|
|
19
|
+
// `internal`: capacity plumbing for the builder's own appenders; not
|
|
20
|
+
// part of the user surface.
|
|
21
|
+
internal func ensure = (ref self, int needed) {
|
|
20
22
|
if self.cap >= needed {
|
|
21
23
|
return
|
|
22
24
|
}
|
|
@@ -84,7 +86,10 @@ pub struct StringBuilder {
|
|
|
84
86
|
// Append a null-terminated string. `unsafe` Nomen single-sourced for
|
|
85
87
|
// both backends: the fat pair's pointer half feeds memcpy directly, and
|
|
86
88
|
// the tracked length replaces the strlen the old C body paid.
|
|
87
|
-
|
|
89
|
+
// `internal`: a library-implementation append — user code appends with
|
|
90
|
+
// `append_char` / `append_string_view`; the library's own structs (Json,
|
|
91
|
+
// Regex) are on the trusted side of the boundary.
|
|
92
|
+
internal func append_string = (ref self, string s) {
|
|
88
93
|
unsafe {
|
|
89
94
|
var int sl = s.length
|
|
90
95
|
self.ensure(self.len + sl + 1)
|
|
@@ -120,9 +125,127 @@ pub struct StringBuilder {
|
|
|
120
125
|
}
|
|
121
126
|
}
|
|
122
127
|
|
|
128
|
+
// Read-back: how many bytes the buffer currently holds. (The `len` field
|
|
129
|
+
// is private; this is the O(1) peek the incremental-builder flow needs —
|
|
130
|
+
// e.g. recording a mark before appending a section.)
|
|
131
|
+
pub func length = (self, out int) {
|
|
132
|
+
return self.len
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Whether the buffer's current bytes end with `suffix` (an empty suffix
|
|
136
|
+
// is always true). Reads the buffer tail in place — no copy of either
|
|
137
|
+
// side (`view string` avoids materializing the checked suffix).
|
|
138
|
+
pub func ends_with = (ref self, view string suffix, out bool) {
|
|
139
|
+
unsafe {
|
|
140
|
+
var int sl = suffix.length
|
|
141
|
+
if sl > self.len {
|
|
142
|
+
return false
|
|
143
|
+
}
|
|
144
|
+
var int start = self.len - sl
|
|
145
|
+
var int i = 0
|
|
146
|
+
while i < sl; i += 1 {
|
|
147
|
+
if (self.data as ptr char)[start + i] != (suffix as ptr char)[i] {
|
|
148
|
+
return false
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return true
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Truncate the buffer to `new_len` bytes (clamped to [0, len]). Capacity
|
|
156
|
+
// is kept, so a subsequent append reuses the storage without a realloc.
|
|
157
|
+
pub func truncate = (ref self, int new_len) {
|
|
158
|
+
var int n = new_len
|
|
159
|
+
if n < 0 {
|
|
160
|
+
n = 0
|
|
161
|
+
}
|
|
162
|
+
if n > self.len {
|
|
163
|
+
n = self.len
|
|
164
|
+
}
|
|
165
|
+
self.len = n
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Drop the last byte (a no-op when the buffer is empty). The common
|
|
169
|
+
// chomp-the-trailing-separator shape.
|
|
170
|
+
pub func chomp = (ref self) {
|
|
171
|
+
if self.len > 0 {
|
|
172
|
+
self.len = self.len - 1
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Extract the bytes from `mark` to the end as a fresh owned string and
|
|
177
|
+
// rewind the buffer to `mark` bytes (capacity kept). The mid-render
|
|
178
|
+
// measure-and-restart shape: record `length()`, append a section, then
|
|
179
|
+
// `take_since(mark)` to measure it and re-emit decorated. `mark` clamps
|
|
180
|
+
// to [0, len]; the returned string is always an independent allocation.
|
|
181
|
+
// (`move out string` — the raw body mallocs the result, so the caller
|
|
182
|
+
// owns it by signature, like `Console.read_line`.)
|
|
183
|
+
pub func take_since = (ref self, int mark, move out string) {
|
|
184
|
+
```
|
|
185
|
+
#arch: c
|
|
186
|
+
int start = mark;
|
|
187
|
+
if (start < 0) start = 0;
|
|
188
|
+
if (start > self->len) start = self->len;
|
|
189
|
+
int n = self->len - start;
|
|
190
|
+
char* buf = (char*)malloc((size_t)n + 1);
|
|
191
|
+
if (n > 0) memcpy(buf, (void*)(unsigned long long)(self->data + start), n);
|
|
192
|
+
buf[n] = 0;
|
|
193
|
+
self->len = start;
|
|
194
|
+
// Fat-string ABI: return the (ptr, len) pair — no strlen.
|
|
195
|
+
return (nomen_string){ buf, n };
|
|
196
|
+
```
|
|
197
|
+
```
|
|
198
|
+
#arch: aarch64
|
|
199
|
+
// x19 = self, x1 = mark. Clamp start to [0, len]; malloc(n+1),
|
|
200
|
+
// copy the tail, null-terminate, rewind len. Live values live in a
|
|
201
|
+
// 32-byte (16-aligned) frame across the calls (x0-x18 are
|
|
202
|
+
// caller-saved): [sp]=start, [sp+8]=n, [sp+16]=buf.
|
|
203
|
+
ldr x2, [x19, #16] // len
|
|
204
|
+
mov x3, x1 // start = mark
|
|
205
|
+
cmp x3, #0
|
|
206
|
+
b.ge .Lsb_tk_hi
|
|
207
|
+
mov x3, #0
|
|
208
|
+
.Lsb_tk_hi:
|
|
209
|
+
cmp x3, x2
|
|
210
|
+
b.le .Lsb_tk_ok
|
|
211
|
+
mov x3, x2 // start = min(start, len)
|
|
212
|
+
.Lsb_tk_ok:
|
|
213
|
+
sub x4, x2, x3 // n = len - start
|
|
214
|
+
sub sp, sp, #32
|
|
215
|
+
str x3, [sp, #0] // start
|
|
216
|
+
str x4, [sp, #8] // n
|
|
217
|
+
add x0, x4, #1
|
|
218
|
+
// buf = malloc(n + 1) — bare call line: the audit pass rewrites
|
|
219
|
+
// `bl _malloc` (line-exact) to the counter-wrapped allocator.
|
|
220
|
+
bl _malloc
|
|
221
|
+
str x0, [sp, #16]
|
|
222
|
+
ldr x1, [sp, #8] // n
|
|
223
|
+
cbz x1, .Lsb_tk_copied
|
|
224
|
+
ldr x0, [sp, #16] // dst = buf
|
|
225
|
+
ldr x3, [x19, #8] // data
|
|
226
|
+
ldr x4, [sp, #0] // start
|
|
227
|
+
add x1, x3, x4 // src = data + start
|
|
228
|
+
ldr x2, [sp, #8] // n
|
|
229
|
+
bl _memcpy
|
|
230
|
+
.Lsb_tk_copied:
|
|
231
|
+
ldr x0, [sp, #16] // buf
|
|
232
|
+
ldr x1, [sp, #8] // n
|
|
233
|
+
add x2, x0, x1
|
|
234
|
+
strb wzr, [x2] // buf[n] = 0
|
|
235
|
+
ldr x3, [sp, #0] // start
|
|
236
|
+
str x3, [x19, #16] // len = start
|
|
237
|
+
ldr x0, [sp, #16] // return pair: (buf, n)
|
|
238
|
+
ldr x1, [sp, #8]
|
|
239
|
+
add sp, sp, #32
|
|
240
|
+
```
|
|
241
|
+
}
|
|
242
|
+
|
|
123
243
|
// Finalize: null-terminate, return the byte buffer as a string, and
|
|
124
244
|
// release ownership (sets `data` to 0 so the destructor won't free it).
|
|
125
|
-
|
|
245
|
+
// Explicitly `pub`: struct members default to pub, but this is the
|
|
246
|
+
// builder's primary read-back — the contract should not depend on the
|
|
247
|
+
// default.
|
|
248
|
+
pub func to_string = (ref self, out string) {
|
|
126
249
|
```
|
|
127
250
|
#arch: c
|
|
128
251
|
int idx = self->len;
|