nomen-lang 0.6.7 → 0.6.9
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/NOMEN_AGENTS.md +12 -3
- package/audit_runtime.c +50 -0
- package/core/System/Buffer.nm +38 -0
- package/core/System/StringBuilder.nm +10 -1
- package/dist/index.mjs +520 -97
- package/package.json +3 -2
- package/src/args.ts +8 -3
- package/src/index.ts +8 -3
- package/src/init.ts +10 -0
- package/src/test.ts +98 -23
package/NOMEN_AGENTS.md
CHANGED
|
@@ -219,9 +219,9 @@ Special function names: `#init` (constructor), `#destroy` (auto-raii destructor)
|
|
|
219
219
|
|
|
220
220
|
```nomen
|
|
221
221
|
if cond { ... }
|
|
222
|
-
switch
|
|
223
|
-
0 { ... }
|
|
224
|
-
1 { ... }
|
|
222
|
+
switch {
|
|
223
|
+
case x == 0 { ... }
|
|
224
|
+
case x == 1 { ... }
|
|
225
225
|
else { ... }
|
|
226
226
|
}
|
|
227
227
|
for i of 0 .. 10 { ... } // exclusive range
|
|
@@ -280,6 +280,15 @@ const typed = Array<int>(1, 2, 3)
|
|
|
280
280
|
for n of nums { Console.write("\{n}\n") }
|
|
281
281
|
```
|
|
282
282
|
|
|
283
|
+
### Compile-time embedding
|
|
284
|
+
|
|
285
|
+
```nomen
|
|
286
|
+
const corpus = #embed_file("bench/full-markdown.md") // string literal of the
|
|
287
|
+
// file's contents; path
|
|
288
|
+
// resolves from the
|
|
289
|
+
// package root
|
|
290
|
+
```
|
|
291
|
+
|
|
283
292
|
## Reference
|
|
284
293
|
|
|
285
294
|
- Full language spec: [SPEC.md](https://github.com/andrewjk/nomen/blob/main/SPEC.md)
|
package/audit_runtime.c
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#include <stdio.h>
|
|
2
|
+
#include <stdlib.h>
|
|
3
|
+
#include <string.h>
|
|
4
|
+
|
|
5
|
+
// Atomic: worker threads from the pool malloc/free concurrently, and a
|
|
6
|
+
// plain `long` would lose updates under that contention (showing up as
|
|
7
|
+
// spurious LEAK/negative-count failures in tests that use spawns).
|
|
8
|
+
static volatile long nomen_malloc_count = 0;
|
|
9
|
+
|
|
10
|
+
void *nomen_malloc_wrap(unsigned long size) {
|
|
11
|
+
void *ptr = malloc(size);
|
|
12
|
+
__atomic_add_fetch(&nomen_malloc_count, 1, __ATOMIC_SEQ_CST);
|
|
13
|
+
return ptr;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
void *nomen_calloc_wrap(unsigned long count, unsigned long size) {
|
|
17
|
+
void *ptr = calloc(count, size);
|
|
18
|
+
__atomic_add_fetch(&nomen_malloc_count, 1, __ATOMIC_SEQ_CST);
|
|
19
|
+
return ptr;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
void *nomen_realloc_wrap(void *old_ptr, unsigned long size) {
|
|
23
|
+
if (!old_ptr) {
|
|
24
|
+
__atomic_add_fetch(&nomen_malloc_count, 1, __ATOMIC_SEQ_CST);
|
|
25
|
+
}
|
|
26
|
+
void *ptr = realloc(old_ptr, size);
|
|
27
|
+
return ptr;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
void nomen_free_wrap(void *ptr) {
|
|
31
|
+
if (ptr) {
|
|
32
|
+
free(ptr);
|
|
33
|
+
__atomic_sub_fetch(&nomen_malloc_count, 1, __ATOMIC_SEQ_CST);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
void *nomen_strdup_wrap(const char *s) {
|
|
38
|
+
unsigned long len = strlen(s) + 1;
|
|
39
|
+
void *ptr = malloc(len);
|
|
40
|
+
if (ptr) memcpy(ptr, s, len);
|
|
41
|
+
__atomic_add_fetch(&nomen_malloc_count, 1, __ATOMIC_SEQ_CST);
|
|
42
|
+
return ptr;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
void nomen_audit_check(void) {
|
|
46
|
+
long count = __atomic_load_n(&nomen_malloc_count, __ATOMIC_SEQ_CST);
|
|
47
|
+
if (count != 0) {
|
|
48
|
+
printf("LEAK: %ld allocation(s)\n", count);
|
|
49
|
+
}
|
|
50
|
+
}
|
package/core/System/Buffer.nm
CHANGED
|
@@ -510,6 +510,17 @@ pub struct Buffer<T> {
|
|
|
510
510
|
```
|
|
511
511
|
}
|
|
512
512
|
|
|
513
|
+
// Move-taking write, mirroring `List.set`: the caller's value is
|
|
514
|
+
// consumed and an independent copy is installed in the slot (replace
|
|
515
|
+
// deep-copies owning elements). The owning entry point a caller uses
|
|
516
|
+
// when it wants to hand over a value — `replace`'s by-value parameter
|
|
517
|
+
// cannot accept the `move` keyword, so a borrow being transferred
|
|
518
|
+
// (e.g. a loaded element) must go through here rather than be bound to
|
|
519
|
+
// a freed local.
|
|
520
|
+
pub func set = (ref self, int i: i >= 0 && i < self.cap, move T value) {
|
|
521
|
+
self.replace(i, value)
|
|
522
|
+
}
|
|
523
|
+
|
|
513
524
|
// Move slot src into slot dst: overwrite dst with src's bytes and zero
|
|
514
525
|
// src, so exactly ONE slot owns the value afterwards. The
|
|
515
526
|
// ownership-transferring primitive behind backward-shift deletion
|
|
@@ -620,6 +631,33 @@ pub struct Buffer<T> {
|
|
|
620
631
|
memset(self.data, 0, len * T_SIZE)
|
|
621
632
|
}
|
|
622
633
|
|
|
634
|
+
// An independent copy: fresh slab, per-element deep copies. Each slot
|
|
635
|
+
// round-trips load→replace through a local, so owning element types get
|
|
636
|
+
// the same treatment a List<T>.copy slot does (the load materializes an
|
|
637
|
+
// owned copy, replace gives the destination its own). Class elements
|
|
638
|
+
// never reach this body (a `Buffer<T>` with T a class monomorphizes to
|
|
639
|
+
// `ClassBuffer<T>`, which has no copy).
|
|
640
|
+
pub func copy = (self, out Buffer<T>: out.cap == self.cap) {
|
|
641
|
+
var dst = Buffer<T>()
|
|
642
|
+
if self.cap > 0 {
|
|
643
|
+
dst.alloc(self.cap)
|
|
644
|
+
var i = 0
|
|
645
|
+
while i < self.cap {
|
|
646
|
+
// Bind the loaded element and hand it to `set` (move): for a
|
|
647
|
+
// string element load returns a BORROW of the source slot, and
|
|
648
|
+
// the move transfers it into the write without a scope-exit
|
|
649
|
+
// reclaim (which would free the source's buffer); for an owning
|
|
650
|
+
// struct element load returns an owned copy that transfers the
|
|
651
|
+
// same way. Mirrors List.copy's `var T v = self.at(i)` +
|
|
652
|
+
// `dst.push(v)`.
|
|
653
|
+
var T v = self.load(i)
|
|
654
|
+
dst.set(i, v)
|
|
655
|
+
i += 1
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
return dst
|
|
659
|
+
}
|
|
660
|
+
|
|
623
661
|
internal inline func store_or_int = (ref self, int i: i >= 0 && i < self.cap, int val) {
|
|
624
662
|
```
|
|
625
663
|
#arch: c
|
|
@@ -241,7 +241,10 @@ pub struct StringBuilder {
|
|
|
241
241
|
}
|
|
242
242
|
|
|
243
243
|
// Finalize: null-terminate, return the byte buffer as a string, and
|
|
244
|
-
// release ownership (sets `data` to 0 so the destructor won't free it
|
|
244
|
+
// release ownership (sets `data` to 0 so the destructor won't free it,
|
|
245
|
+
// and resets `len`/`cap` so the builder is a valid empty builder again —
|
|
246
|
+
// a stale `cap` would let a later append skip the grow and memcpy to a
|
|
247
|
+
// null buffer). Reuse after `to_string` yields a fresh builder.
|
|
245
248
|
// Explicitly `pub`: struct members default to pub, but this is the
|
|
246
249
|
// builder's primary read-back — the contract should not depend on the
|
|
247
250
|
// default.
|
|
@@ -260,6 +263,8 @@ pub struct StringBuilder {
|
|
|
260
263
|
((unsigned char*)(unsigned long long)self->data)[idx] = 0;
|
|
261
264
|
unsigned long long result = self->data;
|
|
262
265
|
self->data = 0;
|
|
266
|
+
self->len = 0;
|
|
267
|
+
self->cap = 0;
|
|
263
268
|
return (nomen_string){ (char*)result, (long)idx };
|
|
264
269
|
```
|
|
265
270
|
```
|
|
@@ -290,6 +295,10 @@ pub struct StringBuilder {
|
|
|
290
295
|
strb wzr, [x0, x1] // data[len] = 0
|
|
291
296
|
// transfer ownership: clear self->data. x0 retains the old ptr.
|
|
292
297
|
str xzr, [x19, #8]
|
|
298
|
+
// reset the stale bookkeeping so reuse yields a valid empty builder
|
|
299
|
+
// (a stale cap would skip the append's grow against the null buffer)
|
|
300
|
+
str xzr, [x19, #16]
|
|
301
|
+
str xzr, [x19, #24]
|
|
293
302
|
// Fat-string ABI: (x0 = data, x1 = len) is already the return pair.
|
|
294
303
|
```
|
|
295
304
|
}
|