nomen-lang 0.5.0 → 0.6.1

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.
@@ -26,11 +26,11 @@
26
26
  /**
27
27
  * A non-blocking TCP socket whose waits park the calling fiber
28
28
  **/
29
- pub class Tcp : Sendable {
29
+ pub class Tcp: Sendable {
30
30
  // The underlying file descriptor, -1 when closed/failed.
31
31
  pub var int fd = -1
32
32
  // 0 = ok; 1 = DNS, 2 = socket, 3 = connect, 4 = listen, 5 = closed.
33
- pub var int error = 0
33
+ pub var error = 0
34
34
 
35
35
  pub func #init = (self, int handle) {
36
36
  self.fd = handle
@@ -66,7 +66,7 @@ pub class Tcp : Sendable {
66
66
  // Create a listening socket bound to `port` on all interfaces.
67
67
  pub func listen = (int port, int backlog, move out Tcp) {
68
68
  var int handle = Tcp.listen_fd(port, backlog)
69
- var Tcp t = Tcp(handle)
69
+ var t = Tcp(handle)
70
70
  if handle < 0 {
71
71
  t.error = 4
72
72
  }
@@ -78,7 +78,7 @@ pub class Tcp : Sendable {
78
78
  // failed.
79
79
  pub func accept = (ref self, move out Tcp) {
80
80
  var int handle = Tcp.accept_fd(self.fd)
81
- var Tcp t = Tcp(handle)
81
+ var t = Tcp(handle)
82
82
  if handle < 0 {
83
83
  t.error = 5
84
84
  }
@@ -91,14 +91,14 @@ pub class Tcp : Sendable {
91
91
  pub func connect = (string host, int port, move out Tcp) {
92
92
  var int handle = Tcp.socket_fd()
93
93
  if handle < 0 {
94
- var Tcp bad = Tcp(-1)
94
+ var bad = Tcp(-1)
95
95
  bad.error = 2
96
96
  return bad
97
97
  }
98
98
  var int err = Tcp.connect_to(handle, host, port)
99
99
  if err != 0 {
100
100
  Tcp.close_fd(handle)
101
- var Tcp bad = Tcp(-1)
101
+ var bad = Tcp(-1)
102
102
  bad.error = err
103
103
  return bad
104
104
  }
@@ -248,7 +248,7 @@ pub struct string: Stringable, Hashable, Equatable {
248
248
  // are only ever read.
249
249
  pub func replace_first = (self, view string needle, view string replacement, out string) {
250
250
  var sb = StringBuilder()
251
- var int i = 0
251
+ var i = 0
252
252
  var int n = self.length
253
253
  var int nlen = needle.length
254
254
  var bool replaced = nlen == 0
@@ -269,7 +269,7 @@ pub struct string: Stringable, Hashable, Equatable {
269
269
  // left to right. Same edge behavior as replace_first.
270
270
  pub func replace_all = (self, view string needle, view string replacement, out string) {
271
271
  var sb = StringBuilder()
272
- var int i = 0
272
+ var i = 0
273
273
  var int n = self.length
274
274
  var int nlen = needle.length
275
275
  while i < n {
@@ -391,7 +391,7 @@ pub struct string: Stringable, Hashable, Equatable {
391
391
  // Owned copy of self with leading and trailing ASCII whitespace
392
392
  // (tab, LF, VT, FF, CR, space) removed. All-whitespace yields "".
393
393
  pub func trim = (self, out string) {
394
- var int start = 0
394
+ var start = 0
395
395
  while start < self.length; start += 1 {
396
396
  if !self.at(start).is_ascii_space() {
397
397
  break
@@ -421,7 +421,7 @@ pub struct string: Stringable, Hashable, Equatable {
421
421
 
422
422
  // Owned copy of self with leading ASCII whitespace removed.
423
423
  pub func trim_start = (self, out string) {
424
- var int start = 0
424
+ var start = 0
425
425
  while start < self.length; start += 1 {
426
426
  if !self.at(start).is_ascii_space() {
427
427
  break
@@ -435,7 +435,7 @@ pub struct string: Stringable, Hashable, Equatable {
435
435
  // bytes — including UTF-8 sequences — pass through unchanged).
436
436
  pub func to_lowercase = (self, out string) {
437
437
  var sb = StringBuilder()
438
- var int i = 0
438
+ var i = 0
439
439
  while i < self.length; i += 1 {
440
440
  var int code = self.at(i) as int
441
441
  if code > 64 && code < 91 {
@@ -450,7 +450,7 @@ pub struct string: Stringable, Hashable, Equatable {
450
450
  // bytes — including UTF-8 sequences — pass through unchanged).
451
451
  pub func to_uppercase = (self, out string) {
452
452
  var sb = StringBuilder()
453
- var int i = 0
453
+ var i = 0
454
454
  while i < self.length; i += 1 {
455
455
  var int code = self.at(i) as int
456
456
  if code > 96 && code < 123 {
@@ -465,7 +465,7 @@ pub struct string: Stringable, Hashable, Equatable {
465
465
  // needle matches text at byte offset pos (caller guarantees pos + len fits;
466
466
  // the trailing guard makes the bound explicit so `.at` verifies).
467
467
  func match_at = (view string text, view string needle, int pos, out bool) {
468
- var int i = 0
468
+ var i = 0
469
469
  var int nlen = needle.length
470
470
  var int n = text.length
471
471
  while i < nlen && pos + i < n {
@@ -24,7 +24,7 @@ pub struct StringBuilder {
24
24
  if new_cap == 0 {
25
25
  new_cap = 16
26
26
  }
27
- while new_cap < needed; new_cap *= 2 {}
27
+ while new_cap < needed; new_cap *= 2 { }
28
28
  self.data = realloc(self.data, new_cap)
29
29
  self.cap = new_cap
30
30
  }
@@ -120,6 +120,121 @@ pub struct StringBuilder {
120
120
  }
121
121
  }
122
122
 
123
+ // Read-back: how many bytes the buffer currently holds. (The `len` field
124
+ // is private; this is the O(1) peek the incremental-builder flow needs —
125
+ // e.g. recording a mark before appending a section.)
126
+ pub func length = (self, out int) {
127
+ return self.len
128
+ }
129
+
130
+ // Whether the buffer's current bytes end with `suffix` (an empty suffix
131
+ // is always true). Reads the buffer tail in place — no copy of either
132
+ // side (`view string` avoids materializing the checked suffix).
133
+ pub func ends_with = (ref self, view string suffix, out bool) {
134
+ unsafe {
135
+ var int sl = suffix.length
136
+ if sl > self.len {
137
+ return false
138
+ }
139
+ var int start = self.len - sl
140
+ var int i = 0
141
+ while i < sl; i += 1 {
142
+ if (self.data as ptr char)[start + i] != (suffix as ptr char)[i] {
143
+ return false
144
+ }
145
+ }
146
+ }
147
+ return true
148
+ }
149
+
150
+ // Truncate the buffer to `new_len` bytes (clamped to [0, len]). Capacity
151
+ // is kept, so a subsequent append reuses the storage without a realloc.
152
+ pub func truncate = (ref self, int new_len) {
153
+ var int n = new_len
154
+ if n < 0 {
155
+ n = 0
156
+ }
157
+ if n > self.len {
158
+ n = self.len
159
+ }
160
+ self.len = n
161
+ }
162
+
163
+ // Drop the last byte (a no-op when the buffer is empty). The common
164
+ // chomp-the-trailing-separator shape.
165
+ pub func chomp = (ref self) {
166
+ if self.len > 0 {
167
+ self.len = self.len - 1
168
+ }
169
+ }
170
+
171
+ // Extract the bytes from `mark` to the end as a fresh owned string and
172
+ // rewind the buffer to `mark` bytes (capacity kept). The mid-render
173
+ // measure-and-restart shape: record `length()`, append a section, then
174
+ // `take_since(mark)` to measure it and re-emit decorated. `mark` clamps
175
+ // to [0, len]; the returned string is always an independent allocation.
176
+ // (`move out string` — the raw body mallocs the result, so the caller
177
+ // owns it by signature, like `Console.read_line`.)
178
+ func take_since = (ref self, int mark, move out string) {
179
+ ```
180
+ #arch: c
181
+ int start = mark;
182
+ if (start < 0) start = 0;
183
+ if (start > self->len) start = self->len;
184
+ int n = self->len - start;
185
+ char* buf = (char*)malloc((size_t)n + 1);
186
+ if (n > 0) memcpy(buf, (void*)(unsigned long long)(self->data + start), n);
187
+ buf[n] = 0;
188
+ self->len = start;
189
+ // Fat-string ABI: return the (ptr, len) pair — no strlen.
190
+ return (nomen_string){ buf, n };
191
+ ```
192
+ ```
193
+ #arch: aarch64
194
+ // x19 = self, x1 = mark. Clamp start to [0, len]; malloc(n+1),
195
+ // copy the tail, null-terminate, rewind len. Live values live in a
196
+ // 32-byte (16-aligned) frame across the calls (x0-x18 are
197
+ // caller-saved): [sp]=start, [sp+8]=n, [sp+16]=buf.
198
+ ldr x2, [x19, #16] // len
199
+ mov x3, x1 // start = mark
200
+ cmp x3, #0
201
+ b.ge .Lsb_tk_hi
202
+ mov x3, #0
203
+ .Lsb_tk_hi:
204
+ cmp x3, x2
205
+ b.le .Lsb_tk_ok
206
+ mov x3, x2 // start = min(start, len)
207
+ .Lsb_tk_ok:
208
+ sub x4, x2, x3 // n = len - start
209
+ sub sp, sp, #32
210
+ str x3, [sp, #0] // start
211
+ str x4, [sp, #8] // n
212
+ add x0, x4, #1
213
+ // buf = malloc(n + 1) — bare call line: the audit pass rewrites
214
+ // `bl _malloc` (line-exact) to the counter-wrapped allocator.
215
+ bl _malloc
216
+ str x0, [sp, #16]
217
+ ldr x1, [sp, #8] // n
218
+ cbz x1, .Lsb_tk_copied
219
+ ldr x0, [sp, #16] // dst = buf
220
+ ldr x3, [x19, #8] // data
221
+ ldr x4, [sp, #0] // start
222
+ add x1, x3, x4 // src = data + start
223
+ ldr x2, [sp, #8] // n
224
+ bl _memcpy
225
+ .Lsb_tk_copied:
226
+ ldr x0, [sp, #16] // buf
227
+ ldr x1, [sp, #8] // n
228
+ add x2, x0, x1
229
+ strb wzr, [x2] // buf[n] = 0
230
+ ldr x3, [sp, #0] // start
231
+ str x3, [x19, #16] // len = start
232
+ ldr x0, [sp, #16] // return pair: (buf, n)
233
+ ldr x1, [sp, #8]
234
+ add sp, sp, #32
235
+ ```
236
+ }
237
+
123
238
  // Finalize: null-terminate, return the byte buffer as a string, and
124
239
  // release ownership (sets `data` to 0 so the destructor won't free it).
125
240
  func to_string = (ref self, out string) {
@@ -24,6 +24,14 @@ pub class Task<T> : Sendable, Awaitable {
24
24
  pub var uint64 cancel_flag = 0
25
25
  pub var uint64 future = 0
26
26
 
27
+ // INTERNAL (library-only): constructs a zeroed handle. `Thread.start` /
28
+ // `Fiber.start` build the Task they return through this — a user can
29
+ // wait/result/cancel a handle, but never constructs a bare one (it would
30
+ // reference no task at all). The auto-free pass reclaims the returned
31
+ // handle via #destroy, exactly like a spawn-yielded Task.
32
+ internal #init = () {
33
+ }
34
+
27
35
  // Block until the task finishes. Idempotent — calling twice is safe, and
28
36
  // a Task captured from a nursery spawn can be waited on here AND joined
29
37
  // by the nursery at block exit (both wait on the same shared future).
@@ -160,6 +168,24 @@ pub class Task<T> : Sendable, Awaitable {
160
168
  ```
161
169
  }
162
170
 
171
+ // Has the task finished? A poll — never blocks. True once the task's
172
+ // future has completed; false while it may still be running (and for a
173
+ // handle whose machinery was already consumed). The missing peek
174
+ // between wait (blocking) and cancel (cooperative): event loops use it
175
+ // to notice a finished task without parking the UI thread.
176
+ pub func is_done = (ref self, out bool) {
177
+ ```
178
+ #arch: c
179
+ return __nomen_future_is_done((struct nomen_future *)self->future);
180
+ ```
181
+ ```
182
+ #arch: aarch64
183
+ // x0 = self->future; result in x0.
184
+ ldr x0, [x19, #40]
185
+ bl ___nomen_future_is_done
186
+ ```
187
+ }
188
+
163
189
  // Request that the task stop. Cooperative — the task observes this via
164
190
  // `Task.current_cancelled()` at its own checkpoints. No preemption.
165
191
  pub func cancel = (ref self) {
@@ -354,14 +380,18 @@ pub class Task<T> : Sendable, Awaitable {
354
380
  ```
355
381
  #arch: c
356
382
  if (self->future) {
357
- #if T_NEEDS_STRDUP
358
- // T is the fat nomen_string here (the guard is 0 for every other
359
- // T, and the preprocessor removes this region before parsing).
360
- if (self->result_slot) {
361
- T _s = *(T *)self->result_slot;
362
- if (_s.ptr) free(_s.ptr);
363
- }
364
- #endif
383
+ #if T_NEEDS_STRDUP
384
+ // T is the fat nomen_string here (the guard is 0 for every other
385
+ // T, and the preprocessor removes this region before parsing).
386
+ if (self->result_slot) {
387
+ T _s = *(T *)self->result_slot;
388
+ if (_s.ptr) free(_s.ptr);
389
+ // Zero the cell: the future's last release also frees an
390
+ // unconsumed fat result, keyed on a non-NULL ptr (see
391
+ // __nomen_future_release) — this free must be the only one.
392
+ memset((void *)self->result_slot, 0, sizeof(T));
393
+ }
394
+ #endif
365
395
  __nomen_future_release((struct nomen_future *)self->future);
366
396
  self->future = 0;
367
397
  self->result_slot = 0;
@@ -383,6 +413,10 @@ pub class Task<T> : Sendable, Awaitable {
383
413
  cbz x8, .Ldestroy_nofree
384
414
  ldr x0, [x8]
385
415
  cbz x0, .Ldestroy_nofree
416
+ // Mark consumed BEFORE the free (x8 is caller-saved and dead after
417
+ // the call): the future's last release also frees an unconsumed
418
+ // fat result, keyed on a non-NULL cell (see __nomen_future_release).
419
+ stp xzr, xzr, [x8]
386
420
  bl _free
387
421
  .Ldestroy_nofree:
388
422
  ldr x0, [x19, #40]
@@ -48,17 +48,17 @@
48
48
  **/
49
49
  pub class Tester {
50
50
  // Has the *current* test recorded a failure? Reset by `begin_test`.
51
- pub var bool has_failed = false
51
+ pub var has_failed = false
52
52
 
53
53
  // Assertion tallies for the current test.
54
- var int passed = 0
55
- var int failed = 0
54
+ var passed = 0
55
+ var failed = 0
56
56
 
57
57
  // Tallies for the whole file, so `done` records stay per-test while the
58
58
  // process still knows whether anything failed overall.
59
- var int total_failed = 0
59
+ var total_failed = 0
60
60
 
61
- var string current = ""
61
+ var current = ""
62
62
  var uint64 started = 0
63
63
 
64
64
  // Benchmark capture state. `bench`/`bench_n` record the label and sample
@@ -66,9 +66,9 @@ pub class Tester {
66
66
  // function and drives the actual timing loop (Nomen can't store or call a
67
67
  // function pointer, so the harness measures `bench_fn` itself). `bench_fn`
68
68
  // is reconstructed by the harness from the call site, not stored here.
69
- var bool bench_pending = false
70
- var string bench_label = ""
71
- var int bench_samples = 1000
69
+ var bench_pending = false
70
+ var bench_label = ""
71
+ var bench_samples = 1000
72
72
 
73
73
  // (harness) Start timing `name` and clear the per-test state. Called by
74
74
  // the `main` that `nomen test` generates — not by test code.
@@ -188,7 +188,7 @@ pub class Tester {
188
188
  * (default 1000, override with `bench_n`) and computes min / median /
189
189
  * mean / max / stddev over the per-iteration durations.
190
190
  **/
191
- pub func bench = (ref self, string label, func () body) {
191
+ pub func bench = (ref self, string label, func() body) {
192
192
  if self.has_failed {
193
193
  return
194
194
  }
@@ -202,7 +202,7 @@ pub class Tester {
202
202
  * for slow functions (where the default 1000 would dominate the suite) or
203
203
  * for reproducible sample counts.
204
204
  **/
205
- pub func bench_n = (ref self, string label, func () body, int samples) {
205
+ pub func bench_n = (ref self, string label, func() body, int samples) {
206
206
  if self.has_failed {
207
207
  return
208
208
  }
@@ -219,7 +219,16 @@ pub class Tester {
219
219
  * If a test has already recorded a failure, benchmarks are skipped and
220
220
  * this is a no-op.
221
221
  **/
222
- pub func record_bench = (ref self, string label, int n, int min, int median, int max, float mean, float stddev) {
222
+ pub func record_bench = (
223
+ ref self,
224
+ string label,
225
+ int n,
226
+ int min,
227
+ int median,
228
+ int max,
229
+ float mean,
230
+ float stddev
231
+ ) {
223
232
  if self.has_failed {
224
233
  return
225
234
  }
@@ -22,18 +22,18 @@ pub struct CharRun {
22
22
  **/
23
23
  pub struct CharIndex {
24
24
  var view string source
25
- var List<CharRun> runs = List<CharRun>()
26
- var int char_count = 0
25
+ var runs = List<CharRun>()
26
+ var char_count = 0
27
27
 
28
28
  pub func #init = (self, view string source) {
29
29
  self.source = source
30
- var int pos = 0
30
+ var pos = 0
31
31
  var int len = source.length
32
- var int chars = 0
32
+ var chars = 0
33
33
  var int open_at = -1
34
- var int open_width = 0
35
- var int open_start = 0
36
- var int open_count = 0
34
+ var open_width = 0
35
+ var open_start = 0
36
+ var open_count = 0
37
37
  while pos < len {
38
38
  var int w = Utf8.width_at(source, pos)
39
39
  switch {
@@ -122,7 +122,7 @@ pub struct CharIndex {
122
122
  // Rightmost run whose key (char_start when by_char, else byte_index) is
123
123
  // <= target, or -1 when all runs start after it.
124
124
  func rightmost_run = (self, int target, bool by_char, out int) {
125
- var int lo = 0
125
+ var lo = 0
126
126
  var int hi = self.runs.length - 1
127
127
  var int found = -1
128
128
  while lo <= hi {
@@ -9,7 +9,7 @@
9
9
  **/
10
10
  pub struct Chars {
11
11
  var view string source
12
- var int byte_pos = 0
12
+ var byte_pos = 0
13
13
 
14
14
  pub func #init = (self, view string source) {
15
15
  self.source = source
@@ -23,7 +23,7 @@ struct JsonNode {
23
23
  * A flat, buffer-backed pool of JSON tree nodes — the parsed representation `Json.parse` produces
24
24
  **/
25
25
  pub struct JsonTree {
26
- var Buffer<JsonNode> nodes = Buffer<JsonNode>()
26
+ var nodes = Buffer<JsonNode>()
27
27
  var count = 0
28
28
 
29
29
  func alloc_node = (ref self, out int) {