nomen-lang 0.6.13 → 0.7.0
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 +27 -13
- package/audit_runtime.c +2 -0
- package/core/System/Arena.nm +4 -4
- package/core/System/Array.nm +108 -78
- package/core/System/Atomic.nm +81 -0
- package/core/System/Awaitable.nm +12 -5
- package/core/System/BigInt.nm +1 -1
- package/core/System/Box.nm +32 -0
- package/core/System/Buffer.nm +18 -18
- package/core/System/Channel.nm +27 -11
- package/core/System/ClassBuffer.nm +24 -28
- package/core/System/Console.nm +4 -4
- package/core/System/Controls/Button.nm +8 -3
- package/core/System/Controls/CheckBox.nm +8 -3
- package/core/System/Controls/Container.nm +18 -11
- package/core/System/Controls/Layout.nm +1 -1
- package/core/System/Controls/Text.nm +9 -4
- package/core/System/Controls/TextBox.nm +8 -3
- package/core/System/Controls/Window.nm +10 -3
- package/core/System/Fiber.nm +6 -2
- package/core/System/Graph.nm +2 -2
- package/core/System/Init.nm +2 -2
- package/core/System/LinkedList.nm +2 -2
- package/core/System/List.nm +7 -7
- package/core/System/Map.nm +10 -7
- package/core/System/Math.nm +1 -1
- package/core/System/Mutex.nm +14 -2
- package/core/System/Random.nm +7 -1
- package/core/System/Result.nm +1 -1
- package/core/System/Sendable.nm +2 -2
- package/core/System/Set.nm +6 -6
- package/core/System/Spawnable.nm +9 -9
- package/core/System/Stream/Directory.nm +1 -2
- package/core/System/Stream/File.nm +8 -9
- package/core/System/Stream/Http.nm +2 -3
- package/core/System/Stream/Tcp.nm +13 -8
- package/core/System/String.nm +22 -12
- package/core/System/StringBuilder.nm +2 -2
- package/core/System/Task.nm +21 -29
- package/core/System/Test.nm +10 -4
- package/core/System/Text/CharIndex.nm +2 -3
- package/core/System/Text/Json.nm +8 -12
- package/core/System/Text/JsonTree.nm +4 -4
- package/core/System/Text/Regex.nm +7 -7
- package/core/System/Text/Utf8.nm +2 -3
- package/core/System/Thread.nm +9 -4
- package/core/System/Tree.nm +2 -2
- package/core/System/Viewable.nm +2 -2
- package/core/System/WeakHandle.nm +56 -0
- package/core/System/bool.nm +1 -1
- package/core/System/char.nm +1 -1
- package/core/System/float.nm +1 -1
- package/core/System/float32.nm +1 -1
- package/core/System/float64.nm +1 -1
- package/core/System/int.nm +1 -1
- package/core/System/int16.nm +1 -1
- package/core/System/int32.nm +1 -1
- package/core/System/int64.nm +1 -1
- package/core/System/int8.nm +1 -1
- package/core/System/ufloat.nm +1 -1
- package/core/System/ufloat32.nm +1 -1
- package/core/System/ufloat64.nm +1 -1
- package/core/System/uint.nm +1 -1
- package/core/System/uint16.nm +1 -1
- package/core/System/uint32.nm +1 -1
- package/core/System/uint64.nm +1 -1
- package/core/System/uint8.nm +1 -1
- package/core/docs/System/Result.md +2 -2
- package/dist/index.mjs +29031 -25546
- package/package.json +1 -1
- package/src/test.ts +26 -11
package/core/System/Math.nm
CHANGED
package/core/System/Mutex.nm
CHANGED
|
@@ -10,16 +10,28 @@
|
|
|
10
10
|
// observes the cancel flag at its next checkpoint after acquiring — so a
|
|
11
11
|
// matching unlock is always sound.
|
|
12
12
|
|
|
13
|
+
// Mutex is an OWNING STRUCT (STRUCT_PLAN_3 Phase R, tranche 3 — the shape
|
|
14
|
+
// menu): the single field is an opaque handle, so the value is movable and
|
|
15
|
+
// its `#destroy` closes what the handle names. Moving a Mutex transfers the
|
|
16
|
+
// handle; there is no aliasing copy to misuse. Sharing one across tasks
|
|
17
|
+
// rides the checked region borrows (`async { }`) — the lock/unlock methods
|
|
18
|
+
// take the receiver bare (struct receivers travel by address), so a locked
|
|
19
|
+
// section mutates the kernel object the handle names, never the binding.
|
|
20
|
+
|
|
13
21
|
/**
|
|
14
22
|
* A pthread-backed mutual-exclusion lock for protecting shared mutable state
|
|
15
23
|
**/
|
|
16
|
-
pub
|
|
24
|
+
pub mv struct Mutex {
|
|
17
25
|
pub var uint64 handle = 0
|
|
18
26
|
|
|
19
27
|
pub func #init = (self) {
|
|
28
|
+
// The constructor's `self` is the INLINE instance (a local value in the
|
|
29
|
+
// C lowering — methods take the pointer form, the ctor does not), so the
|
|
30
|
+
// C body uses the `.` accessor; the aarch64 body writes through x19 =
|
|
31
|
+
// &instance as always.
|
|
20
32
|
```
|
|
21
33
|
#arch: c
|
|
22
|
-
self
|
|
34
|
+
self.handle = (unsigned long long)__nomen_mutex_create();
|
|
23
35
|
```
|
|
24
36
|
```
|
|
25
37
|
#arch: aarch64
|
package/core/System/Random.nm
CHANGED
|
@@ -15,10 +15,16 @@
|
|
|
15
15
|
//
|
|
16
16
|
import Time
|
|
17
17
|
|
|
18
|
+
// Random is an OWNING STRUCT (STRUCT_PLAN_3 Phase R, tranche 3 — the shape
|
|
19
|
+
// menu): bindings mv instead of alias. The state is a bare uint64, so the
|
|
20
|
+
// value is freely movable (and trivially copyable in principle — `mv`
|
|
21
|
+
// is the uniform spelling for the launch/IO primitives; a `ref Random`
|
|
22
|
+
// parameter is the read-only borrow that keeps one stream per binding).
|
|
23
|
+
|
|
18
24
|
/**
|
|
19
25
|
* A fast splitmix64 pseudo-random number generator
|
|
20
26
|
**/
|
|
21
|
-
pub
|
|
27
|
+
pub mv struct Random {
|
|
22
28
|
// The raw 64-bit generator state (seeded from the wall clock by the
|
|
23
29
|
// no-arg constructor, or an explicit seed for reproducible sequences).
|
|
24
30
|
pub var uint64 state = 0
|
package/core/System/Result.nm
CHANGED
package/core/System/Sendable.nm
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
// Marker trait for types whose values are safe to
|
|
1
|
+
// Marker trait for types whose values are safe to mv across task boundaries.
|
|
2
2
|
// See ASYNC.md for the design. Auto-derivable in a follow-up; for now, structs
|
|
3
3
|
// opt in explicitly with `: Sendable`.
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* A marker trait for values that are safe to
|
|
6
|
+
* A marker trait for values that are safe to mv across task boundaries
|
|
7
7
|
**/
|
|
8
8
|
pub trait Sendable {
|
|
9
9
|
}
|
package/core/System/Set.nm
CHANGED
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
* slot indexing is a mask (`hash & (cap - 1)`) — correct for negative hashes
|
|
8
8
|
* too (two's-complement `&` already lands in `[0, cap)`).
|
|
9
9
|
**/
|
|
10
|
-
pub struct Set<T: Hashable + Equatable> {
|
|
10
|
+
pub mv struct Set<T: Hashable + Equatable> {
|
|
11
11
|
var length = 0
|
|
12
12
|
var slots = Buffer<T>()
|
|
13
13
|
var used = Buffer<int>()
|
|
14
14
|
|
|
15
|
-
pub func add = (ref self,
|
|
15
|
+
pub func add = (ref self, mv T value) {
|
|
16
16
|
var int cap = self.slots.cap
|
|
17
17
|
if cap == 0 {
|
|
18
18
|
cap = self.slots.alloc(8)
|
|
@@ -148,8 +148,8 @@ pub struct Set<T: Hashable + Equatable> {
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
func rehash = (ref self, int new_cap) {
|
|
151
|
-
var Buffer<T> old_slots =
|
|
152
|
-
var Buffer<int> old_used =
|
|
151
|
+
var Buffer<T> old_slots = mv self.slots swap Buffer<T>()
|
|
152
|
+
var Buffer<int> old_used = mv self.used swap Buffer<int>()
|
|
153
153
|
var int old_cap = old_slots.cap
|
|
154
154
|
|
|
155
155
|
self.slots.alloc(new_cap)
|
|
@@ -163,12 +163,12 @@ pub struct Set<T: Hashable + Equatable> {
|
|
|
163
163
|
// is owned by exactly one buffer (the new self) afterwards.
|
|
164
164
|
// load_T would leave the old slot referencing the same class
|
|
165
165
|
// pointer — a double-free when old_slots is destroyed. The
|
|
166
|
-
// result is bound to a local so
|
|
166
|
+
// result is bound to a local so a mv-element copy (e.g. a
|
|
167
167
|
// `Buffer<string>` slot, which store_T strdup's) is freed here
|
|
168
168
|
// once the new buffer has made its own copy — passing it inline
|
|
169
169
|
// would orphan the move_T result.
|
|
170
170
|
var T v = old_slots.move(i)
|
|
171
|
-
self.add(
|
|
171
|
+
self.add(mv v)
|
|
172
172
|
}
|
|
173
173
|
}
|
|
174
174
|
}
|
package/core/System/Spawnable.nm
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
// Spawnable — the trait role of a deferred-work LAUNCHER (the C# shape;
|
|
2
|
-
// see docs/ASYNC.md, "Design decisions"). A Spawnable
|
|
3
|
-
// (the construction special form) and its `start` submits it,
|
|
4
|
-
// library-owned `Task<T>` handle — so the await half is
|
|
5
|
-
// return type and each type carries exactly one role:
|
|
6
|
-
// are spawnable, `Task<T>` is awaitable.
|
|
7
|
-
// `wait` does not mention T);
|
|
8
|
-
// return type does.
|
|
2
|
+
// see docs/ASYNC.md, "Design decisions"). A Spawnable type packs a deferred
|
|
3
|
+
// unit of work (the construction special form) and its `start` submits it,
|
|
4
|
+
// yielding the library-owned `Task<T>` handle — so the await half is
|
|
5
|
+
// guaranteed by the return type and each type carries exactly one role:
|
|
6
|
+
// `Thread<T>`/`Fiber<T>` are spawnable, `Task<T>` is awaitable.
|
|
7
|
+
// `Awaitable` stays non-generic (its `wait` does not mention T);
|
|
8
|
+
// `Spawnable` is generic because `start`'s return type does.
|
|
9
9
|
//
|
|
10
10
|
// `detach` is the daemon role: run the work unjoinable, statement only.
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* A
|
|
13
|
+
* A type that can launch a deferred call and hand back its Task<T> handle
|
|
14
14
|
**/
|
|
15
15
|
pub trait Spawnable<T> {
|
|
16
|
-
func start = (ref self,
|
|
16
|
+
func start = (ref self, mv out Task<T>)
|
|
17
17
|
func detach = (ref self)
|
|
18
18
|
}
|
|
@@ -56,10 +56,9 @@ pub struct File {
|
|
|
56
56
|
case err == 17 {
|
|
57
57
|
return FileError.already_exists
|
|
58
58
|
}
|
|
59
|
-
|
|
59
|
+
} else {
|
|
60
60
|
return FileError.other
|
|
61
61
|
}
|
|
62
|
-
}
|
|
63
62
|
}
|
|
64
63
|
|
|
65
64
|
// Open the file at `path` with a C mode string ("r", "w", "a", …).
|
|
@@ -157,9 +156,9 @@ pub struct File {
|
|
|
157
156
|
}
|
|
158
157
|
|
|
159
158
|
// Read every byte from the current position to EOF and return it. Sets
|
|
160
|
-
// `eof` and `error`. Declared `
|
|
159
|
+
// `eof` and `error`. Declared `mv out string`: the raw body mallocs a
|
|
161
160
|
// fresh buffer and hands ownership to the caller.
|
|
162
|
-
func raw_read_all = (ref self,
|
|
161
|
+
func raw_read_all = (ref self, mv out string) {
|
|
163
162
|
```
|
|
164
163
|
#arch: c
|
|
165
164
|
FILE *f = (FILE*)(unsigned long long)self->handle;
|
|
@@ -272,8 +271,8 @@ pub struct File {
|
|
|
272
271
|
|
|
273
272
|
// Read up to the next '\n' (the newline is not included). Sets `eof`
|
|
274
273
|
// when the end of the file is reached and `error` on failure. The raw
|
|
275
|
-
// body mallocs a fresh buffer — `
|
|
276
|
-
func raw_read_line = (ref self,
|
|
274
|
+
// body mallocs a fresh buffer — `mv out string` transfers it.
|
|
275
|
+
func raw_read_line = (ref self, mv out string) {
|
|
277
276
|
```
|
|
278
277
|
#arch: c
|
|
279
278
|
FILE *f = (FILE*)(unsigned long long)self->handle;
|
|
@@ -416,8 +415,8 @@ pub struct File {
|
|
|
416
415
|
|
|
417
416
|
// Read up to `size` bytes from the current position. Sets `eof` if fewer
|
|
418
417
|
// than `size` bytes remain and `error` on failure. The raw body mallocs a
|
|
419
|
-
// fresh buffer — `
|
|
420
|
-
func raw_read_chunk = (ref self, int size,
|
|
418
|
+
// fresh buffer — `mv out string` transfers it.
|
|
419
|
+
func raw_read_chunk = (ref self, int size, mv out string) {
|
|
421
420
|
```
|
|
422
421
|
#arch: c
|
|
423
422
|
FILE *f = (FILE*)(unsigned long long)self->handle;
|
|
@@ -590,7 +589,7 @@ pub struct File {
|
|
|
590
589
|
|
|
591
590
|
// (static) Write `data` to `path` (replacing any existing contents) in
|
|
592
591
|
// one open/write/close. A write failure is reported as `.error`, not
|
|
593
|
-
// silently swallowed (the `
|
|
592
|
+
// silently swallowed (the `checked` Result discard check forces this to
|
|
594
593
|
// be deliberate).
|
|
595
594
|
pub func write_all = (string path, string data, out Result<bool, FileError>) {
|
|
596
595
|
var Result<bool, FileError> result = .error(FileError.other)
|
|
@@ -48,10 +48,9 @@ pub struct Http {
|
|
|
48
48
|
case err == 3 {
|
|
49
49
|
return HttpError.connect
|
|
50
50
|
}
|
|
51
|
-
|
|
51
|
+
} else {
|
|
52
52
|
return HttpError.other
|
|
53
53
|
}
|
|
54
|
-
}
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
// Shared socket round-trip used by get/post. `method` is the request
|
|
@@ -59,7 +58,7 @@ pub struct Http {
|
|
|
59
58
|
// (sends `body` and the Content-* headers). Sets self->status (0 on
|
|
60
59
|
// failure) and self->error, and returns the response body (empty string
|
|
61
60
|
// on failure).
|
|
62
|
-
func raw_exchange = (ref self, string method, string url, string body,
|
|
61
|
+
func raw_exchange = (ref self, string method, string url, string body, mv out string) {
|
|
63
62
|
// Parse the URL: [http://]host[:port][/path]
|
|
64
63
|
var host_start = 0
|
|
65
64
|
if url.starts_with("http://") {
|
|
@@ -23,10 +23,15 @@
|
|
|
23
23
|
// linked companion on aarch64) — OS socket/DNS interop, where hand-written
|
|
24
24
|
// per-target assembly is not practical.
|
|
25
25
|
|
|
26
|
+
// Tcp is an OWNING STRUCT (STRUCT_PLAN_3 Phase R, tranche 3 — the shape
|
|
27
|
+
// menu): the fields are the fd and an error code — the fd is the handle, so
|
|
28
|
+
// the value is movable and `#destroy` closes it. A moved Tcp transfers the
|
|
29
|
+
// socket; there is no aliasing copy to double-close.
|
|
30
|
+
|
|
26
31
|
/**
|
|
27
32
|
* A non-blocking TCP socket whose waits park the calling fiber
|
|
28
33
|
**/
|
|
29
|
-
pub
|
|
34
|
+
pub mv struct Tcp {
|
|
30
35
|
// The underlying file descriptor, -1 when closed/failed.
|
|
31
36
|
pub var int fd = -1
|
|
32
37
|
// 0 = ok; 1 = DNS, 2 = socket, 3 = connect, 4 = listen, 5 = closed.
|
|
@@ -64,7 +69,7 @@ pub class Tcp: Sendable {
|
|
|
64
69
|
// ---- listening ----
|
|
65
70
|
|
|
66
71
|
// Create a listening socket bound to `port` on all interfaces.
|
|
67
|
-
pub func listen = (int port, int backlog,
|
|
72
|
+
pub func listen = (int port, int backlog, mv out Tcp) {
|
|
68
73
|
var int handle = Tcp.listen_fd(port, backlog)
|
|
69
74
|
var t = Tcp(handle)
|
|
70
75
|
if handle < 0 {
|
|
@@ -76,7 +81,7 @@ pub class Tcp: Sendable {
|
|
|
76
81
|
// Accept the next connection, parking the fiber while the backlog is
|
|
77
82
|
// empty. The returned socket is -1 if the listener was cancelled or
|
|
78
83
|
// failed.
|
|
79
|
-
pub func accept = (ref self,
|
|
84
|
+
pub func accept = (ref self, mv out Tcp) {
|
|
80
85
|
var int handle = Tcp.accept_fd(self.fd)
|
|
81
86
|
var t = Tcp(handle)
|
|
82
87
|
if handle < 0 {
|
|
@@ -88,7 +93,7 @@ pub class Tcp: Sendable {
|
|
|
88
93
|
// ---- connecting ----
|
|
89
94
|
|
|
90
95
|
// Connect to `host`:`port`. Check `fd >= 0` (or `error == 0`) for success.
|
|
91
|
-
pub func connect = (string host, int port,
|
|
96
|
+
pub func connect = (string host, int port, mv out Tcp) {
|
|
92
97
|
var int handle = Tcp.socket_fd()
|
|
93
98
|
if handle < 0 {
|
|
94
99
|
var bad = Tcp(-1)
|
|
@@ -115,12 +120,12 @@ pub class Tcp: Sendable {
|
|
|
115
120
|
|
|
116
121
|
// Receive up to `max` bytes. Parks the fiber until data arrives; returns
|
|
117
122
|
// the empty string at end-of-stream, on error, or when cancelled.
|
|
118
|
-
pub func recv = (ref self, int max,
|
|
123
|
+
pub func recv = (ref self, int max, mv out string) {
|
|
119
124
|
return Tcp.recv_some(self.fd, max)
|
|
120
125
|
}
|
|
121
126
|
|
|
122
127
|
// Receive until the peer closes (how Connection: close HTTP works).
|
|
123
|
-
pub func recv_all = (ref self,
|
|
128
|
+
pub func recv_all = (ref self, mv out string) {
|
|
124
129
|
return Tcp.recv_until_close(self.fd)
|
|
125
130
|
}
|
|
126
131
|
|
|
@@ -326,7 +331,7 @@ pub class Tcp: Sendable {
|
|
|
326
331
|
```
|
|
327
332
|
}
|
|
328
333
|
|
|
329
|
-
func recv_some = (int handle, int max,
|
|
334
|
+
func recv_some = (int handle, int max, mv out string) {
|
|
330
335
|
```
|
|
331
336
|
#arch: c
|
|
332
337
|
long cap = max > 0 ? max : 1;
|
|
@@ -375,7 +380,7 @@ pub class Tcp: Sendable {
|
|
|
375
380
|
```
|
|
376
381
|
}
|
|
377
382
|
|
|
378
|
-
func recv_until_close = (int handle,
|
|
383
|
+
func recv_until_close = (int handle, mv out string) {
|
|
379
384
|
```
|
|
380
385
|
#arch: c
|
|
381
386
|
long cap = 4096, len = 0;
|
package/core/System/String.nm
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// FFI: the C library's heap-copy entry point (string.h). The adapter
|
|
2
2
|
// marshals the fat string to the thin char* the C ABI expects; the string
|
|
3
3
|
// result is re-wrapped with its length.
|
|
4
|
-
|
|
4
|
+
foreign func strdup = (string s, mv out string)
|
|
5
5
|
|
|
6
6
|
// Raw memory externs — the allocation primitives every `unsafe` Nomen body
|
|
7
7
|
// across core (Buffer, StringBuilder, Array, ...) is built on. This file is
|
|
@@ -9,17 +9,20 @@ extern func strdup = (string s, move out string)
|
|
|
9
9
|
// every core file regardless of pull order. Free externs emit under an
|
|
10
10
|
// `extern_<name>` label that wraps the identically-named C symbol, so these
|
|
11
11
|
// can never collide with it (docs/CORE_RAW.md roadmap items 1+2).
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
foreign func calloc = (int count, int size, out uint64)
|
|
13
|
+
foreign func malloc = (int bytes, out uint64)
|
|
14
|
+
foreign func realloc = (uint64 address, int bytes, out uint64)
|
|
15
|
+
foreign func free = (uint64 address)
|
|
16
|
+
foreign func memcpy = (uint64 dst, uint64 src, int bytes, out uint64)
|
|
17
|
+
foreign func memset = (uint64 dst, int value, int bytes, out uint64)
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* A UTF-8 text string — a heap-owned, length-tracked sequence of bytes
|
|
20
|
+
* A UTF-8 text string — a heap-owned, length-tracked sequence of bytes.
|
|
21
|
+
* STRUCT_PLAN_3 D3: `string` is OWNING — the fat (ptr, len) pair owns its
|
|
22
|
+
* bytes, so bindings mv unless a copy is spelled (`.copy()`); `view string`
|
|
23
|
+
* is the cheap read-only passing form.
|
|
21
24
|
**/
|
|
22
|
-
pub struct string: Stringable, Hashable, Equatable {
|
|
25
|
+
pub mv struct string: Stringable, Hashable, Equatable {
|
|
23
26
|
pub func hash = (self, out uint) {
|
|
24
27
|
// djb2 over exactly self.length bytes — matches the byte count of
|
|
25
28
|
// owned strings and literals (views hash the viewed bytes only).
|
|
@@ -31,12 +34,19 @@ pub struct string: Stringable, Hashable, Equatable {
|
|
|
31
34
|
return h
|
|
32
35
|
}
|
|
33
36
|
|
|
34
|
-
//
|
|
37
|
+
// The uniform mv-type verb: a fresh, owned byte-exact copy of self.
|
|
38
|
+
// The deep-copy spelling the binding/assignment copy errors name — same
|
|
39
|
+
// allocation contract as to_string (fresh buffer, never an alias).
|
|
40
|
+
pub func copy = (self, mv out string) {
|
|
41
|
+
return self.to_string()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Returns an OWNED byte-exact copy of self (`mv out string`). Fresh
|
|
35
45
|
// allocation, never an alias of self's storage — the result can outlive
|
|
36
46
|
// the receiver (e.g. escape a match branch that reclaims the scrutinee
|
|
37
47
|
// temp). Copies by tracked length, so embedded NULs survive (an extern
|
|
38
48
|
// strdup boundary would re-synthesize length via strlen and truncate).
|
|
39
|
-
pub func to_string = (self,
|
|
49
|
+
pub func to_string = (self, mv out string) {
|
|
40
50
|
```
|
|
41
51
|
#arch: c
|
|
42
52
|
// Fat-string ABI: copy self.len bytes; no strlen anywhere.
|
|
@@ -90,7 +100,7 @@ pub struct string: Stringable, Hashable, Equatable {
|
|
|
90
100
|
panic("index out of range")
|
|
91
101
|
}
|
|
92
102
|
|
|
93
|
-
// A non-
|
|
103
|
+
// A non-mv slice [start, end) into self's buffer. Returns a `view
|
|
94
104
|
// string` (ptr, len) that borrows from self: it may not outlive self's
|
|
95
105
|
// scope and is invalidated if self is reassigned. Use .to_string() to
|
|
96
106
|
// materialize an owned copy.
|
|
@@ -178,9 +178,9 @@ pub struct StringBuilder {
|
|
|
178
178
|
// measure-and-restart shape: record `length()`, append a section, then
|
|
179
179
|
// `take_since(mark)` to measure it and re-emit decorated. `mark` clamps
|
|
180
180
|
// to [0, len]; the returned string is always an independent allocation.
|
|
181
|
-
// (`
|
|
181
|
+
// (`mv out string` — the raw body mallocs the result, so the caller
|
|
182
182
|
// owns it by signature, like `Console.read_line`.)
|
|
183
|
-
pub func take_since = (ref self, int mark,
|
|
183
|
+
pub func take_since = (ref self, int mark, mv out string) {
|
|
184
184
|
```
|
|
185
185
|
#arch: c
|
|
186
186
|
int start = mark;
|
package/core/System/Task.nm
CHANGED
|
@@ -11,13 +11,18 @@
|
|
|
11
11
|
// on the first spawn — not in Task's own methods — so that Task can be
|
|
12
12
|
// generic without duplicating the pool per instantiation.
|
|
13
13
|
//
|
|
14
|
+
// Task is an OWNING STRUCT (STRUCT_PLAN_3 Phase R, tranche 2 — the shape
|
|
15
|
+
// menu): every field is an opaque handle (uint64/bool), so the handle value
|
|
16
|
+
// is movable and its `#destroy` releases the future reference it holds.
|
|
17
|
+
// Moving a Task transfers that reference; there is no aliasing copy.
|
|
18
|
+
//
|
|
14
19
|
// See ASYNC.md for the design.
|
|
15
20
|
|
|
16
21
|
/**
|
|
17
22
|
* A handle to an asynchronously-running computation, backed by a shared thread pool.
|
|
18
23
|
* Conforms to `Awaitable`: `.wait()` may park the current execution context.
|
|
19
24
|
**/
|
|
20
|
-
pub
|
|
25
|
+
pub mv struct Task<T> : Awaitable {
|
|
21
26
|
pub var uint64 handle = 0
|
|
22
27
|
pub var done = false
|
|
23
28
|
pub var uint64 result_slot = 0
|
|
@@ -35,40 +40,27 @@ pub class Task<T> : Sendable, Awaitable {
|
|
|
35
40
|
// Block until the task finishes. Idempotent — calling twice is safe, and
|
|
36
41
|
// a Task captured from a nursery spawn can be waited on here AND joined
|
|
37
42
|
// by the nursery at block exit (both wait on the same shared future).
|
|
38
|
-
|
|
43
|
+
//
|
|
44
|
+
// The receiver is a bare VALUE self (matching the Awaitable trait):
|
|
45
|
+
// waiting never mutates the handle — it parks on the future the handle
|
|
46
|
+
// names. Struct receivers travel by address regardless (the raw body
|
|
47
|
+
// reads `self->future`), so the ABI is unchanged; the bare spelling is
|
|
48
|
+
// what lets the read-only `Awaitable` dispatch form typecheck.
|
|
49
|
+
// The legacy `pthread_join` fallback is gone — `handle` has been
|
|
50
|
+
// permanently zero since the pool migration.
|
|
51
|
+
pub func wait = (self) {
|
|
39
52
|
```
|
|
40
53
|
#arch: c
|
|
41
54
|
if (self->future) {
|
|
42
55
|
__nomen_future_wait((struct nomen_future *)self->future);
|
|
43
|
-
} else if (self->handle && !self->done) {
|
|
44
|
-
void *ret;
|
|
45
|
-
pthread_join((pthread_t)self->handle, &ret);
|
|
46
|
-
self->handle = 0;
|
|
47
|
-
self->done = 1;
|
|
48
56
|
}
|
|
49
57
|
```
|
|
50
58
|
```
|
|
51
59
|
#arch: aarch64
|
|
52
|
-
// if (self->future)
|
|
60
|
+
// if (self->future) __nomen_future_wait(self->future)
|
|
53
61
|
ldr x0, [x19, #40]
|
|
54
|
-
cbz x0, .Lwait_handle
|
|
55
|
-
bl ___nomen_future_wait
|
|
56
|
-
b .Lwait_done
|
|
57
|
-
.Lwait_handle:
|
|
58
|
-
// else if (self->handle && !self->done)
|
|
59
|
-
ldr x0, [x19, #8]
|
|
60
62
|
cbz x0, .Lwait_done
|
|
61
|
-
|
|
62
|
-
cbnz w1, .Lwait_done
|
|
63
|
-
// pthread_join((pthread_t)self->handle, &ret)
|
|
64
|
-
sub sp, sp, #16
|
|
65
|
-
mov x1, sp
|
|
66
|
-
bl _pthread_join
|
|
67
|
-
add sp, sp, #16
|
|
68
|
-
// self->handle = 0; self->done = 1
|
|
69
|
-
str xzr, [x19, #8]
|
|
70
|
-
mov w0, #1
|
|
71
|
-
strb w0, [x19, #16]
|
|
63
|
+
bl ___nomen_future_wait
|
|
72
64
|
.Lwait_done:
|
|
73
65
|
```
|
|
74
66
|
}
|
|
@@ -78,10 +70,10 @@ pub class Task<T> : Sendable, Awaitable {
|
|
|
78
70
|
// the full return value — including the 16-byte fat-string pair — and the
|
|
79
71
|
// trampoline writes the function's return value there before exiting.
|
|
80
72
|
// The result is MOVED OUT: the slot is zeroed as the value leaves, and
|
|
81
|
-
// `
|
|
73
|
+
// `mv out T` marks the caller as the owner. Call it once; a second call
|
|
82
74
|
// returns the zero value. For tasks with no return value (void
|
|
83
75
|
// functions), returns the zero value.
|
|
84
|
-
pub func result = (ref self,
|
|
76
|
+
pub func result = (ref self, mv out T) {
|
|
85
77
|
```
|
|
86
78
|
#arch: c
|
|
87
79
|
if (self->future) {
|
|
@@ -284,7 +276,7 @@ pub class Task<T> : Sendable, Awaitable {
|
|
|
284
276
|
// ---- User-defined async primitive seam (ASYNC.md, "User-defined async
|
|
285
277
|
// primitives") ----
|
|
286
278
|
// The construction sugar stores the packed-task machinery in a user
|
|
287
|
-
//
|
|
279
|
+
// spawnable's plain uint64 fields (task / result_slot / cancel_flag /
|
|
288
280
|
// future); these statics are the library boundary its own launch
|
|
289
281
|
// methods drive the runtime through — the same calls the generated
|
|
290
282
|
// Thread/Fiber launch code makes, so a user primitive parks fibers,
|
|
@@ -294,7 +286,7 @@ pub class Task<T> : Sendable, Awaitable {
|
|
|
294
286
|
|
|
295
287
|
// Submit a packed task closure (the `task` field's value) to the shared
|
|
296
288
|
// worker pool. The closure must come from a construction of an
|
|
297
|
-
// Awaitable-conforming
|
|
289
|
+
// Awaitable-conforming spawnable — it is not callable.
|
|
298
290
|
pub func pool_submit = (uint64 closure) {
|
|
299
291
|
```
|
|
300
292
|
#arch: c
|
package/core/System/Test.nm
CHANGED
|
@@ -32,6 +32,12 @@
|
|
|
32
32
|
//
|
|
33
33
|
// See TESTING.md for the full design.
|
|
34
34
|
|
|
35
|
+
// Tester is an OWNING STRUCT (STRUCT_PLAN_3 Phase R, tranche 3 — the shape
|
|
36
|
+
// menu): the harness builds exactly one per run, so the single-owner rule
|
|
37
|
+
// costs nothing and the record state moves with the value. The string
|
|
38
|
+
// fields (current/bench_label) are normalized heap-owned by the compiler's
|
|
39
|
+
// struct string machinery, so `#destroy` reclaims them cleanly.
|
|
40
|
+
|
|
35
41
|
/**
|
|
36
42
|
* Records assertions and benchmarks for one test file.
|
|
37
43
|
*
|
|
@@ -47,7 +53,7 @@
|
|
|
47
53
|
* skipped, so a single root cause reports as a single failure. Check
|
|
48
54
|
* `t.has_failed` to bail out of expensive follow-up work yourself.
|
|
49
55
|
**/
|
|
50
|
-
pub
|
|
56
|
+
pub mv struct Tester {
|
|
51
57
|
// Has the *current* test recorded a failure? Reset by `begin_test`.
|
|
52
58
|
pub var has_failed = false
|
|
53
59
|
|
|
@@ -94,7 +100,7 @@ pub class Tester {
|
|
|
94
100
|
self.has_failed = false
|
|
95
101
|
self.passed = 0
|
|
96
102
|
self.failed = 0
|
|
97
|
-
self.current = name
|
|
103
|
+
self.current = name.copy()
|
|
98
104
|
Console.write_line("\\nomen|start|" + name)
|
|
99
105
|
self.started = Time.now_ns()
|
|
100
106
|
}
|
|
@@ -211,7 +217,7 @@ pub class Tester {
|
|
|
211
217
|
return
|
|
212
218
|
}
|
|
213
219
|
self.bench_pending = true
|
|
214
|
-
self.bench_label = label
|
|
220
|
+
self.bench_label = label.copy()
|
|
215
221
|
self.bench_samples = 1000
|
|
216
222
|
}
|
|
217
223
|
|
|
@@ -225,7 +231,7 @@ pub class Tester {
|
|
|
225
231
|
return
|
|
226
232
|
}
|
|
227
233
|
self.bench_pending = true
|
|
228
|
-
self.bench_label = label
|
|
234
|
+
self.bench_label = label.copy()
|
|
229
235
|
self.bench_samples = samples
|
|
230
236
|
}
|
|
231
237
|
|
|
@@ -20,7 +20,7 @@ pub struct CharRun {
|
|
|
20
20
|
* A run-length character-width index (construct with `CharIndex(text)`,
|
|
21
21
|
* then `byte_offset_of` / `char_index_of` / `char_at` in O(log runs))
|
|
22
22
|
**/
|
|
23
|
-
pub struct CharIndex {
|
|
23
|
+
pub mv struct CharIndex {
|
|
24
24
|
var view string source
|
|
25
25
|
var runs = List<CharRun>()
|
|
26
26
|
var char_count = 0
|
|
@@ -50,7 +50,7 @@ pub struct CharIndex {
|
|
|
50
50
|
pos = pos + w
|
|
51
51
|
chars = chars + 1
|
|
52
52
|
}
|
|
53
|
-
|
|
53
|
+
} else {
|
|
54
54
|
if open_at >= 0 {
|
|
55
55
|
self.runs.push(CharRun(open_at, open_start, open_width, open_count))
|
|
56
56
|
}
|
|
@@ -61,7 +61,6 @@ pub struct CharIndex {
|
|
|
61
61
|
pos = pos + w
|
|
62
62
|
chars = chars + 1
|
|
63
63
|
}
|
|
64
|
-
}
|
|
65
64
|
}
|
|
66
65
|
if open_at >= 0 {
|
|
67
66
|
self.runs.push(CharRun(open_at, open_start, open_width, open_count))
|
package/core/System/Text/Json.nm
CHANGED
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
**/
|
|
15
15
|
pub struct Json {
|
|
16
16
|
// Serialize a string to a JSON string literal, escaping the special
|
|
17
|
-
// characters ", \, newline, tab and carriage return. `
|
|
17
|
+
// characters ", \, newline, tab and carriage return. `mv out string`:
|
|
18
18
|
// returns the StringBuilder's owned heap copy.
|
|
19
|
-
pub func serialize = (string value,
|
|
19
|
+
pub func serialize = (string value, mv out string) {
|
|
20
20
|
var sb = StringBuilder()
|
|
21
21
|
sb.append_char('"')
|
|
22
22
|
var i = 0
|
|
@@ -40,18 +40,17 @@ pub struct Json {
|
|
|
40
40
|
case code == 13 {
|
|
41
41
|
sb.append_string("\x5cr")
|
|
42
42
|
}
|
|
43
|
-
|
|
43
|
+
} else {
|
|
44
44
|
sb.append_char(c)
|
|
45
45
|
}
|
|
46
|
-
}
|
|
47
46
|
}
|
|
48
47
|
sb.append_char('"')
|
|
49
48
|
return sb.to_string()
|
|
50
49
|
}
|
|
51
50
|
|
|
52
51
|
// Deserialize a JSON string literal to the raw, unescaped value.
|
|
53
|
-
// `
|
|
54
|
-
pub func deserialize = (string json,
|
|
52
|
+
// `mv out string`: returns the StringBuilder's owned heap copy.
|
|
53
|
+
pub func deserialize = (string json, mv out string) {
|
|
55
54
|
var sb = StringBuilder()
|
|
56
55
|
var int len = json.length
|
|
57
56
|
var i = 0
|
|
@@ -90,10 +89,9 @@ pub struct Json {
|
|
|
90
89
|
case ec == 92 {
|
|
91
90
|
sb.append_string("\x5c")
|
|
92
91
|
}
|
|
93
|
-
|
|
92
|
+
} else {
|
|
94
93
|
sb.append_char(e)
|
|
95
94
|
}
|
|
96
|
-
}
|
|
97
95
|
}
|
|
98
96
|
} else {
|
|
99
97
|
sb.append_char(c)
|
|
@@ -165,10 +163,9 @@ func json_parse_string = (string s, ref Buffer<int> pos, int len, ref JsonTree t
|
|
|
165
163
|
case ec == 92 {
|
|
166
164
|
sb.append_string("\x5c")
|
|
167
165
|
}
|
|
168
|
-
|
|
166
|
+
} else {
|
|
169
167
|
sb.append_char(e)
|
|
170
168
|
}
|
|
171
|
-
}
|
|
172
169
|
}
|
|
173
170
|
} else {
|
|
174
171
|
sb.append_char(c)
|
|
@@ -218,11 +215,10 @@ func json_parse_literal = (string s, ref Buffer<int> pos, int len, ref JsonTree
|
|
|
218
215
|
tree.set_kind(n, 6)
|
|
219
216
|
adv = 5
|
|
220
217
|
}
|
|
221
|
-
|
|
218
|
+
} else {
|
|
222
219
|
tree.set_kind(n, 0)
|
|
223
220
|
adv = 4
|
|
224
221
|
}
|
|
225
|
-
}
|
|
226
222
|
pos.store_int(0, i + adv)
|
|
227
223
|
return n
|
|
228
224
|
}
|