nomen-lang 0.0.19 → 0.2.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 +57 -12
- package/core/System/Array.nm +11 -11
- package/core/System/Buffer.nm +2 -2
- package/core/System/Channel.nm +146 -12
- package/core/System/ClassBuffer.nm +2 -2
- package/core/System/Console.nm +28 -17
- package/core/System/Controls/Button.nm +96 -8
- package/core/System/Controls/CheckBox.nm +117 -12
- package/core/System/Controls/Container.nm +114 -8
- package/core/System/Controls/Text.nm +98 -8
- package/core/System/Controls/TextBox.nm +120 -10
- package/core/System/Controls/Window.nm +231 -23
- package/core/System/Graph.nm +1 -1
- package/core/System/Init.nm +4 -8
- package/core/System/LinkedList.nm +1 -1
- package/core/System/List.nm +3 -3
- package/core/System/Map.nm +5 -5
- package/core/System/Math.nm +10 -32
- package/core/System/Result.nm +1 -1
- package/core/System/Set.nm +4 -4
- package/core/System/Stream/Directory.nm +189 -63
- package/core/System/Stream/DirectoryError.nm +21 -0
- package/core/System/Stream/File.nm +339 -141
- package/core/System/Stream/FileError.nm +21 -0
- package/core/System/Stream/Http.nm +109 -44
- package/core/System/Stream/HttpError.nm +20 -0
- package/core/System/String.nm +83 -69
- package/core/System/StringBuilder.nm +11 -13
- package/core/System/Task.nm +46 -12
- package/core/System/Test.nm +1 -1
- package/core/System/Text/Json.nm +5 -3
- package/core/System/Text/JsonTree.nm +23 -14
- package/core/System/Text/Regex.nm +124 -64
- package/core/System/Tree.nm +1 -1
- package/core/System/bool.nm +4 -9
- package/core/System/char.nm +3 -9
- package/core/System/float.nm +2 -1
- package/core/System/float32.nm +44 -0
- package/core/System/float64.nm +44 -0
- package/core/System/int.nm +3 -9
- package/core/System/int16.nm +47 -0
- package/core/System/int32.nm +47 -0
- package/core/System/int64.nm +3 -9
- package/core/System/int8.nm +3 -9
- package/core/System/ufloat.nm +44 -0
- package/core/System/ufloat32.nm +44 -0
- package/core/System/ufloat64.nm +44 -0
- package/core/System/uint.nm +3 -9
- package/core/System/uint16.nm +47 -0
- package/core/System/uint32.nm +47 -0
- package/core/System/uint64.nm +3 -9
- package/core/System/uint8.nm +3 -9
- package/core/docs/System/Channel.md +2 -0
- package/core/docs/System/Result.md +5 -1
- package/core/docs/System/Task.md +1 -1
- package/dist/index.mjs +38794 -21299
- package/package.json +38 -34
- package/src/args.ts +11 -0
- package/src/index.ts +21 -3
- package/src/init.ts +1 -1
- package/src/test.ts +48 -4
- package/src/types/Config.ts +2 -0
package/NOMEN_AGENTS.md
CHANGED
|
@@ -36,22 +36,50 @@ The rules below trip up newcomers and LLMs. Follow them.
|
|
|
36
36
|
|
|
37
37
|
Nomen infers types from initializers and return values. Don't spell them out
|
|
38
38
|
unless the inference would be wrong or the annotation materially aids a public
|
|
39
|
-
API.
|
|
39
|
+
API. This includes generic instantiations — an initializer like
|
|
40
|
+
`split_lines(text)` already says `List<Line>`.
|
|
40
41
|
|
|
41
42
|
```nomen
|
|
42
|
-
const x = 5
|
|
43
|
-
const int x = 5
|
|
44
|
-
const
|
|
45
|
-
|
|
43
|
+
const x = 5 // good — inferred int
|
|
44
|
+
const int x = 5 // avoid
|
|
45
|
+
const lines = split_lines(text) // good — inferred List<Line>
|
|
46
|
+
const List<Line> lines = split_lines(text) // avoid
|
|
47
|
+
const msg = "hi" // good
|
|
48
|
+
var items = [1, 2, 3] // good — int[]
|
|
46
49
|
```
|
|
47
50
|
|
|
48
51
|
Annotate when you need a wider type (`int64` rather than `int`), when there is
|
|
49
52
|
no initializer, or on a `pub` API where the type is the contract.
|
|
50
53
|
|
|
51
|
-
###
|
|
54
|
+
### Loop updates go in the header
|
|
52
55
|
|
|
53
|
-
|
|
54
|
-
|
|
56
|
+
`for` and `while` take a post-statement after the condition, C-`for` style.
|
|
57
|
+
Don't bury the update as the last statement of the body.
|
|
58
|
+
|
|
59
|
+
```nomen
|
|
60
|
+
while i < n; i += 1 { ... } // good — update in the header
|
|
61
|
+
while i < n { ...; i += 1 } // avoid
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Only hoist an update that runs on every iteration — if a `continue` (or early
|
|
65
|
+
`return` that isn't meant to advance) must skip it, keep it in the body.
|
|
66
|
+
|
|
67
|
+
### No `else if` — use `switch`
|
|
68
|
+
|
|
69
|
+
Nomen forbids `else if` and `else { if { … } }` chains. Consecutive sibling
|
|
70
|
+
`if`s on mutually exclusive conditions are the same smell (and a `did_x` flag
|
|
71
|
+
set only to gate the next `if` is a giveaway): fold them into a `switch`,
|
|
72
|
+
which evaluates cases in order and runs the first true one.
|
|
73
|
+
|
|
74
|
+
```nomen
|
|
75
|
+
switch {
|
|
76
|
+
case moved { ... }
|
|
77
|
+
case is_ins { ... }
|
|
78
|
+
else { ... }
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Independent conditions that can all fire stay as separate `if`s.
|
|
55
83
|
|
|
56
84
|
### Constraints are compile-time assertions
|
|
57
85
|
|
|
@@ -72,7 +100,7 @@ func safe = (string[] s, int i: i >= 0 && i < s.length, out string) {
|
|
|
72
100
|
| -------- | ---------------------------------------------- |
|
|
73
101
|
| `ref T` | mutable borrow; caller must also write `ref` |
|
|
74
102
|
| `view T` | read-only borrow |
|
|
75
|
-
| `
|
|
103
|
+
| `move T` | transfer ownership; caller writes `move` |
|
|
76
104
|
| `out T` | output parameter, assigned inside the function |
|
|
77
105
|
|
|
78
106
|
`const` values cannot be passed to `ref` — rebind the caller as `var` first.
|
|
@@ -106,15 +134,27 @@ const y = x + 1 // Error: 'x' is null
|
|
|
106
134
|
|
|
107
135
|
```nomen
|
|
108
136
|
import System // standard library
|
|
109
|
-
import System
|
|
110
|
-
import System
|
|
137
|
+
import System::Test // submodule
|
|
138
|
+
import System::Controls::Geometry // a single module file
|
|
111
139
|
```
|
|
112
140
|
|
|
141
|
+
Import paths must mirror the library's file layout — a typo'd path
|
|
142
|
+
(`import System::Contrls`) is a compile error.
|
|
143
|
+
|
|
144
|
+
Files in the same folder are one module — they already see each other's
|
|
145
|
+
declarations, so never import a sibling. Only cross-namespace references
|
|
146
|
+
(subfolders, libraries) need an import.
|
|
147
|
+
|
|
113
148
|
### No exceptions
|
|
114
149
|
|
|
115
150
|
Failures are signalled via return values — nullable results, status structs,
|
|
116
151
|
or boolean + out-parameter pairs. There is no `throw`/`try`/`catch`.
|
|
117
152
|
|
|
153
|
+
`Result<T, E>` is declared `must_use`: a call returning it must bind or match
|
|
154
|
+
the value (`var _ = f.close()` to discard deliberately; a bare `f.close()`
|
|
155
|
+
statement is a compile error). Any enum or bitset can opt into this with the
|
|
156
|
+
`must_use` modifier.
|
|
157
|
+
|
|
118
158
|
## Tests
|
|
119
159
|
|
|
120
160
|
A test is any `pub func <name> = (ref Tester t)` in a `*.test.nm` file.
|
|
@@ -123,7 +163,7 @@ benchmarks.
|
|
|
123
163
|
|
|
124
164
|
```nomen
|
|
125
165
|
import System
|
|
126
|
-
import System
|
|
166
|
+
import System::Test
|
|
127
167
|
|
|
128
168
|
func add = (int a, int b, out int) => a + b
|
|
129
169
|
|
|
@@ -153,6 +193,10 @@ var int? maybe = null // nullable
|
|
|
153
193
|
ref int ptr // reference to an int
|
|
154
194
|
```
|
|
155
195
|
|
|
196
|
+
Keywords (`out`, `in`, `match`, `if`, `of`, `as`, `swap`, …) are reserved and
|
|
197
|
+
cannot be used as variable, parameter, field, type, or case names. Function
|
|
198
|
+
names are exempt since calls go through `.name(...)` (e.g. `Regex.match`).
|
|
199
|
+
|
|
156
200
|
### Functions
|
|
157
201
|
|
|
158
202
|
```nomen
|
|
@@ -177,6 +221,7 @@ switch x {
|
|
|
177
221
|
for i of 0 .. 10 { ... } // exclusive range
|
|
178
222
|
for item of array { ... }
|
|
179
223
|
while cond { ... }
|
|
224
|
+
while cond; update { ... } // post-statement (e.g. while i < n; i += 1)
|
|
180
225
|
```
|
|
181
226
|
|
|
182
227
|
### Structs and classes
|
package/core/System/Array.nm
CHANGED
|
@@ -114,13 +114,13 @@ pub struct Array<T>: Viewable {
|
|
|
114
114
|
```
|
|
115
115
|
#arch: c
|
|
116
116
|
T* _data = (T*)((char*)self + sizeof(*self));
|
|
117
|
-
// T_NEEDS_STRDUP is 1 when T is string
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
//
|
|
117
|
+
// T_NEEDS_STRDUP is 1 when T is string (a fat value whose backing
|
|
118
|
+
// bytes must be heap-copied per slot — otherwise the slot would share
|
|
119
|
+
// the incoming pointer and auto_free would double-free it). 0 for all
|
|
120
|
+
// other T (plain assignment).
|
|
121
121
|
#if T_NEEDS_STRDUP
|
|
122
|
-
free(_data[index]);
|
|
123
|
-
_data[index] = (
|
|
122
|
+
free(_data[index].ptr);
|
|
123
|
+
_data[index] = nomen_str_dup(value);
|
|
124
124
|
#else
|
|
125
125
|
_data[index] = value;
|
|
126
126
|
#endif
|
|
@@ -258,12 +258,12 @@ pub struct Array<T>: Viewable {
|
|
|
258
258
|
((long*)_result)[1] = count;
|
|
259
259
|
T* _data = (T*)((char*)_result + _header);
|
|
260
260
|
for (long i = 0; i < count; i++) {
|
|
261
|
-
// T_NEEDS_STRDUP is 1 when T is string (a
|
|
262
|
-
//
|
|
263
|
-
//
|
|
264
|
-
//
|
|
261
|
+
// T_NEEDS_STRDUP is 1 when T is string (a fat value whose backing
|
|
262
|
+
// bytes must be heap-copied per slot — otherwise all n slots would
|
|
263
|
+
// share one pointer and auto_free would n-free it). For all other
|
|
264
|
+
// T it is 0 and the plain assignment runs.
|
|
265
265
|
#if T_NEEDS_STRDUP
|
|
266
|
-
_data[i] = (
|
|
266
|
+
_data[i] = nomen_str_dup(value);
|
|
267
267
|
#else
|
|
268
268
|
_data[i] = value;
|
|
269
269
|
#endif
|
package/core/System/Buffer.nm
CHANGED
|
@@ -445,11 +445,11 @@ pub struct Buffer<T> {
|
|
|
445
445
|
}
|
|
446
446
|
|
|
447
447
|
// Move a value out of a T_SIZE-sized slot: return it and zero the slot so the
|
|
448
|
-
// buffer no longer holds a (possibly owning) copy. Declared `
|
|
448
|
+
// buffer no longer holds a (possibly owning) copy. Declared `move out T` so
|
|
449
449
|
// the result is treated as OWNED by the borrow checker — the slot is
|
|
450
450
|
// relinquished, so the caller is the sole remaining owner. The value
|
|
451
451
|
// counterpart of ClassBuffer.move_int — used by List.pop for any value T.
|
|
452
|
-
inline func move_T = (ref self, int i: i >= 0 && i < self.cap,
|
|
452
|
+
inline func move_T = (ref self, int i: i >= 0 && i < self.cap, move out T) {
|
|
453
453
|
```
|
|
454
454
|
#arch: c
|
|
455
455
|
T* _slots = (T*)(unsigned long long)self->data;
|
package/core/System/Channel.nm
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
// Channel — thread-safe single-direction queue for passing values between
|
|
2
2
|
// tasks. Unbounded, FIFO, blocking receive.
|
|
3
3
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
4
|
+
// The queue node's payload is two words wide: every message carries a
|
|
5
|
+
// (value, len) pair. `send`/`receive` move uint64 values (len stays 0);
|
|
6
|
+
// `send_string`/`receive_string` move fat strings without truncating the len
|
|
7
|
+
// half. A typed Channel<T> wrapper can come later — the storage story is
|
|
8
|
+
// identical, only the call-site types change.
|
|
9
|
+
//
|
|
10
|
+
// String payloads are copied in and moved out: `send_string` duplicates the
|
|
11
|
+
// value into the node (the sender keeps its own string), and
|
|
12
|
+
// `receive_string` moves that copy out to the receiver (zeroing the node's
|
|
13
|
+
// payload). A message that is never received is freed by #destroy's drain.
|
|
14
|
+
// This keeps literals (rodata), heap strings freed at the sender's scope
|
|
15
|
+
// exit, and messages crossing task boundaries all sound.
|
|
7
16
|
//
|
|
8
17
|
// Default stance in Nomen is "no shared mutable state" — channels are the
|
|
9
18
|
// preferred way for tasks to communicate. See ASYNC.md.
|
|
@@ -24,6 +33,7 @@ pub class Channel: Sendable {
|
|
|
24
33
|
#include <pthread.h>
|
|
25
34
|
struct nomen_channel_node {
|
|
26
35
|
unsigned long long value;
|
|
36
|
+
unsigned long long len;
|
|
27
37
|
struct nomen_channel_node *next;
|
|
28
38
|
};
|
|
29
39
|
```
|
|
@@ -58,6 +68,7 @@ pub class Channel: Sendable {
|
|
|
58
68
|
#arch: c
|
|
59
69
|
struct nomen_channel_node *n = (struct nomen_channel_node *)malloc(sizeof(struct nomen_channel_node));
|
|
60
70
|
n->value = value;
|
|
71
|
+
n->len = 0;
|
|
61
72
|
n->next = NULL;
|
|
62
73
|
pthread_mutex_lock((pthread_mutex_t *)self->mu);
|
|
63
74
|
if (self->tail) {
|
|
@@ -73,18 +84,19 @@ pub class Channel: Sendable {
|
|
|
73
84
|
#arch: aarch64
|
|
74
85
|
// x19 = self (set by prologue), x1 = value
|
|
75
86
|
str x1, [sp, #-16]! // save value on stack
|
|
76
|
-
mov x0, #
|
|
87
|
+
mov x0, #24 // node = { value, len, next }
|
|
77
88
|
bl _malloc // x0 = node
|
|
78
89
|
ldr x1, [sp], #16 // restore value
|
|
79
90
|
str x1, [x0, #0] // node->value = value
|
|
80
|
-
str xzr, [x0, #8] // node->
|
|
91
|
+
str xzr, [x0, #8] // node->len = 0
|
|
92
|
+
str xzr, [x0, #16] // node->next = NULL
|
|
81
93
|
str x0, [sp, #-16]! // save node on stack
|
|
82
94
|
ldr x0, [x19, #8] // self->mu
|
|
83
95
|
bl _pthread_mutex_lock
|
|
84
96
|
ldr x2, [sp], #16 // restore node
|
|
85
97
|
ldr x1, [x19, #32] // tail
|
|
86
98
|
cbz x1, .Lchannel_send_empty
|
|
87
|
-
str x2, [x1, #
|
|
99
|
+
str x2, [x1, #16] // tail->next = node
|
|
88
100
|
b .Lchannel_send_enqueued
|
|
89
101
|
.Lchannel_send_empty:
|
|
90
102
|
str x2, [x19, #24] // head = node
|
|
@@ -97,6 +109,67 @@ pub class Channel: Sendable {
|
|
|
97
109
|
```
|
|
98
110
|
}
|
|
99
111
|
|
|
112
|
+
// Pass a fat string through the channel. The value is DUPLICATED into the
|
|
113
|
+
// queue node — the sender keeps ownership of what it passed — so the
|
|
114
|
+
// message survives the sender's scope exit intact (true len, embedded
|
|
115
|
+
// NULs and all). Receive it with receive_string.
|
|
116
|
+
pub func send_string = (self, string value) {
|
|
117
|
+
```
|
|
118
|
+
#arch: c
|
|
119
|
+
struct nomen_channel_node *n = (struct nomen_channel_node *)malloc(sizeof(struct nomen_channel_node));
|
|
120
|
+
nomen_string _v = value.ptr ? nomen_str_dup(value) : (nomen_string){0, 0};
|
|
121
|
+
n->value = (unsigned long long)_v.ptr;
|
|
122
|
+
n->len = (unsigned long long)_v.len;
|
|
123
|
+
n->next = NULL;
|
|
124
|
+
pthread_mutex_lock((pthread_mutex_t *)self->mu);
|
|
125
|
+
if (self->tail) {
|
|
126
|
+
((struct nomen_channel_node *)self->tail)->next = n;
|
|
127
|
+
} else {
|
|
128
|
+
self->head = (unsigned long long)n;
|
|
129
|
+
}
|
|
130
|
+
self->tail = (unsigned long long)n;
|
|
131
|
+
pthread_cond_signal((pthread_cond_t *)self->not_empty_cv);
|
|
132
|
+
pthread_mutex_unlock((pthread_mutex_t *)self->mu);
|
|
133
|
+
```
|
|
134
|
+
```
|
|
135
|
+
#arch: aarch64
|
|
136
|
+
// x19 = self (set by prologue), x1 = value.ptr, x2 = value.len
|
|
137
|
+
stp x1, x2, [sp, #-32]! // [sp]=ptr [sp+8]=len [sp+16]=node scratch
|
|
138
|
+
mov x0, #24 // node = { value, len, next }
|
|
139
|
+
bl _malloc // x0 = node
|
|
140
|
+
str x0, [sp, #16] // save node across the strdup call
|
|
141
|
+
ldr x1, [sp, #0] // value.ptr
|
|
142
|
+
cbz x1, .Lchannel_send_str_zero
|
|
143
|
+
mov x0, x1
|
|
144
|
+
bl _strdup // x0 = owned copy (node owns its buffer)
|
|
145
|
+
b .Lchannel_send_str_have
|
|
146
|
+
.Lchannel_send_str_zero:
|
|
147
|
+
mov x0, #0
|
|
148
|
+
.Lchannel_send_str_have:
|
|
149
|
+
ldr x2, [sp, #16] // node
|
|
150
|
+
str x0, [x2, #0] // node->value = copied ptr
|
|
151
|
+
ldr x1, [sp, #8] // value.len
|
|
152
|
+
str x1, [x2, #8] // node->len = len
|
|
153
|
+
str xzr, [x2, #16] // node->next = NULL
|
|
154
|
+
ldr x0, [x19, #8] // self->mu
|
|
155
|
+
bl _pthread_mutex_lock
|
|
156
|
+
ldr x2, [sp, #16] // restore node
|
|
157
|
+
ldr x1, [x19, #32] // tail
|
|
158
|
+
cbz x1, .Lchannel_send_str_empty
|
|
159
|
+
str x2, [x1, #16] // tail->next = node
|
|
160
|
+
b .Lchannel_send_str_enqueued
|
|
161
|
+
.Lchannel_send_str_empty:
|
|
162
|
+
str x2, [x19, #24] // head = node
|
|
163
|
+
.Lchannel_send_str_enqueued:
|
|
164
|
+
str x2, [x19, #32] // tail = node
|
|
165
|
+
ldr x0, [x19, #16] // not_empty_cv
|
|
166
|
+
bl _pthread_cond_signal
|
|
167
|
+
ldr x0, [x19, #8] // self->mu
|
|
168
|
+
bl _pthread_mutex_unlock
|
|
169
|
+
add sp, sp, #32
|
|
170
|
+
```
|
|
171
|
+
}
|
|
172
|
+
|
|
100
173
|
pub func receive = (self, out uint64) {
|
|
101
174
|
```
|
|
102
175
|
#arch: c
|
|
@@ -126,7 +199,7 @@ pub class Channel: Sendable {
|
|
|
126
199
|
b .Lchannel_receive_wait
|
|
127
200
|
.Lchannel_receive_got:
|
|
128
201
|
// x1 = node
|
|
129
|
-
ldr x2, [x1, #
|
|
202
|
+
ldr x2, [x1, #16] // node->next
|
|
130
203
|
str x2, [x19, #24] // head = node->next
|
|
131
204
|
cbnz x2, .Lchannel_receive_not_empty
|
|
132
205
|
str xzr, [x19, #32] // tail = 0
|
|
@@ -141,14 +214,69 @@ pub class Channel: Sendable {
|
|
|
141
214
|
```
|
|
142
215
|
}
|
|
143
216
|
|
|
217
|
+
// Block until a message is available, then return it as an OWNED string
|
|
218
|
+
// (the node's copy is moved out — the payload leaves the channel, and a
|
|
219
|
+
// second receive of the same message is not possible). The fat (ptr, len)
|
|
220
|
+
// pair survives intact. Pair with send_string.
|
|
221
|
+
pub func receive_string = (self, move out string) {
|
|
222
|
+
```
|
|
223
|
+
#arch: c
|
|
224
|
+
pthread_mutex_lock((pthread_mutex_t *)self->mu);
|
|
225
|
+
while (!self->head) {
|
|
226
|
+
pthread_cond_wait((pthread_cond_t *)self->not_empty_cv, (pthread_mutex_t *)self->mu);
|
|
227
|
+
}
|
|
228
|
+
struct nomen_channel_node *n = (struct nomen_channel_node *)self->head;
|
|
229
|
+
self->head = (unsigned long long)n->next;
|
|
230
|
+
if (!self->head) self->tail = 0;
|
|
231
|
+
nomen_string v = { (char *)n->value, (long)n->len };
|
|
232
|
+
n->value = 0;
|
|
233
|
+
n->len = 0;
|
|
234
|
+
free(n);
|
|
235
|
+
pthread_mutex_unlock((pthread_mutex_t *)self->mu);
|
|
236
|
+
return v;
|
|
237
|
+
```
|
|
238
|
+
```
|
|
239
|
+
#arch: aarch64
|
|
240
|
+
// x19 = self (set by prologue); returns the fat pair in x0/x1
|
|
241
|
+
ldr x0, [x19, #8] // self->mu
|
|
242
|
+
bl _pthread_mutex_lock
|
|
243
|
+
.Lchannel_receive_str_wait:
|
|
244
|
+
ldr x1, [x19, #24] // head
|
|
245
|
+
cbnz x1, .Lchannel_receive_str_got
|
|
246
|
+
ldr x0, [x19, #16] // not_empty_cv
|
|
247
|
+
ldr x1, [x19, #8] // mu
|
|
248
|
+
bl _pthread_cond_wait
|
|
249
|
+
b .Lchannel_receive_str_wait
|
|
250
|
+
.Lchannel_receive_str_got:
|
|
251
|
+
// x1 = node
|
|
252
|
+
ldr x2, [x1, #16] // node->next
|
|
253
|
+
str x2, [x19, #24] // head = node->next
|
|
254
|
+
cbnz x2, .Lchannel_receive_str_not_empty
|
|
255
|
+
str xzr, [x19, #32] // tail = 0
|
|
256
|
+
.Lchannel_receive_str_not_empty:
|
|
257
|
+
ldr x0, [x1, #0] // value.ptr
|
|
258
|
+
ldr x2, [x1, #8] // value.len
|
|
259
|
+
stp x0, x2, [sp, #-16]! // push pair
|
|
260
|
+
str xzr, [x1, #0] // payload moves out of the node
|
|
261
|
+
str xzr, [x1, #8]
|
|
262
|
+
mov x0, x1 // x0 = node (for free)
|
|
263
|
+
bl _free
|
|
264
|
+
ldr x0, [x19, #8] // self->mu
|
|
265
|
+
bl _pthread_mutex_unlock
|
|
266
|
+
ldp x0, x1, [sp], #16 // pop pair → x0/x1 (return)
|
|
267
|
+
```
|
|
268
|
+
}
|
|
269
|
+
|
|
144
270
|
pub func #destroy = () {
|
|
145
271
|
```
|
|
146
272
|
#arch: c
|
|
147
273
|
if (self->mu) {
|
|
148
|
-
// Drain any pending nodes.
|
|
274
|
+
// Drain any pending nodes. A string payload that was never
|
|
275
|
+
// received still owns its buffer — free it with the node.
|
|
149
276
|
struct nomen_channel_node *n = (struct nomen_channel_node *)self->head;
|
|
150
277
|
while (n) {
|
|
151
278
|
struct nomen_channel_node *next = n->next;
|
|
279
|
+
if (n->value) free((void *)n->value);
|
|
152
280
|
free(n);
|
|
153
281
|
n = next;
|
|
154
282
|
}
|
|
@@ -167,14 +295,20 @@ pub class Channel: Sendable {
|
|
|
167
295
|
// x19 = self (set by prologue)
|
|
168
296
|
ldr x0, [x19, #8] // self->mu
|
|
169
297
|
cbz x0, .Lchannel_destroy_done
|
|
170
|
-
// Drain nodes
|
|
298
|
+
// Drain nodes (freeing any unconsumed string payload)
|
|
171
299
|
ldr x1, [x19, #24] // head
|
|
172
300
|
.Lchannel_destroy_drain:
|
|
173
301
|
cbz x1, .Lchannel_destroy_drained
|
|
174
|
-
ldr x2, [x1, #
|
|
175
|
-
|
|
302
|
+
ldr x2, [x1, #16] // next (before calls clobber x1)
|
|
303
|
+
stp x1, x2, [sp, #-16]! // save node + next
|
|
304
|
+
ldr x0, [x1, #0] // payload ptr
|
|
305
|
+
cbz x0, .Lchannel_destroy_no_payload
|
|
306
|
+
bl _free
|
|
307
|
+
.Lchannel_destroy_no_payload:
|
|
308
|
+
ldr x0, [sp, #0] // node
|
|
176
309
|
bl _free
|
|
177
|
-
ldr x1, [sp
|
|
310
|
+
ldr x1, [sp, #8] // next
|
|
311
|
+
add sp, sp, #16
|
|
178
312
|
b .Lchannel_destroy_drain
|
|
179
313
|
.Lchannel_destroy_drained:
|
|
180
314
|
// Destroy mutex and condvar
|
|
@@ -175,14 +175,14 @@ pub struct ClassBuffer<T> {
|
|
|
175
175
|
}
|
|
176
176
|
|
|
177
177
|
// Move a class pointer out of a slot: return it and zero the slot so the
|
|
178
|
-
// buffer no longer references it. Declared `
|
|
178
|
+
// buffer no longer references it. Declared `move out T` so the borrow
|
|
179
179
|
// checker treats the result as OWNED — the slot is relinquished, so the
|
|
180
180
|
// caller is the sole remaining owner. This is the owning-extract primitive
|
|
181
181
|
// (parallel to List.pop); use it (not load_T) when transferring ownership
|
|
182
182
|
// out of a ClassBuffer slot, or moving a value into a fresh owning
|
|
183
183
|
// container — load_T returns a BORROW (the slot is unchanged) and storing
|
|
184
184
|
// it elsewhere would create shared ownership (double-free at destroy).
|
|
185
|
-
inline func move_T = (ref self, int i: i >= 0 && i < self.cap,
|
|
185
|
+
inline func move_T = (ref self, int i: i >= 0 && i < self.cap, move out T) {
|
|
186
186
|
```
|
|
187
187
|
#arch: c
|
|
188
188
|
long *slots = (long*)(unsigned long long)self->data;
|
package/core/System/Console.nm
CHANGED
|
@@ -9,12 +9,12 @@ pub struct Console {
|
|
|
9
9
|
```
|
|
10
10
|
```
|
|
11
11
|
#arch: aarch64
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
// Fat-string ABI: line = (x0 ptr, x1 len); printf needs the fmt in
|
|
13
|
+
// x0 and the NUL-terminated ptr half in x1.
|
|
14
|
+
mov x2, x0
|
|
15
15
|
adr x0, .Lfmt_console_s
|
|
16
|
+
mov x1, x2
|
|
16
17
|
bl _printf
|
|
17
|
-
add sp, sp, #16
|
|
18
18
|
b .Lend_Console_write
|
|
19
19
|
.Lfmt_console_s: .asciz "%s"
|
|
20
20
|
.p2align 2
|
|
@@ -29,12 +29,11 @@ pub struct Console {
|
|
|
29
29
|
```
|
|
30
30
|
```
|
|
31
31
|
#arch: aarch64
|
|
32
|
-
|
|
33
|
-
mov
|
|
34
|
-
str x1, [sp]
|
|
32
|
+
// Fat-string ABI: line = (x0 ptr, x1 len).
|
|
33
|
+
mov x2, x0
|
|
35
34
|
adr x0, .Lfmt_console_snl
|
|
35
|
+
mov x1, x2
|
|
36
36
|
bl _printf
|
|
37
|
-
add sp, sp, #16
|
|
38
37
|
b .Lend_Console_write_line
|
|
39
38
|
.Lfmt_console_snl: .asciz "%s\n"
|
|
40
39
|
.p2align 2
|
|
@@ -42,7 +41,9 @@ pub struct Console {
|
|
|
42
41
|
```
|
|
43
42
|
}
|
|
44
43
|
|
|
45
|
-
|
|
44
|
+
// Read one line from stdin (newline stripped). The raw body mallocs the
|
|
45
|
+
// buffer — `move out string` transfers ownership to the caller.
|
|
46
|
+
pub func read_line = (move out string) {
|
|
46
47
|
```
|
|
47
48
|
#arch: c
|
|
48
49
|
int cap = 16;
|
|
@@ -58,7 +59,9 @@ pub struct Console {
|
|
|
58
59
|
c = getchar();
|
|
59
60
|
}
|
|
60
61
|
buf[len] = 0;
|
|
61
|
-
return
|
|
62
|
+
// Fat-string ABI: return the (ptr, len) pair — len is already
|
|
63
|
+
// counted, no strlen.
|
|
64
|
+
return (nomen_string){ buf, len };
|
|
62
65
|
```
|
|
63
66
|
```
|
|
64
67
|
#arch: aarch64
|
|
@@ -121,42 +124,45 @@ pub struct Console {
|
|
|
121
124
|
```
|
|
122
125
|
}
|
|
123
126
|
|
|
124
|
-
|
|
127
|
+
// The platform name ("macos" / "ios" / …). The raw body strdups a static
|
|
128
|
+
// string — `move out string` transfers the owned copy to the caller.
|
|
129
|
+
pub func platform = (move out string) {
|
|
125
130
|
```
|
|
126
131
|
#arch: c
|
|
127
132
|
#platform: macos
|
|
128
|
-
return strdup("macos");
|
|
133
|
+
return (nomen_string){ strdup("macos"), 5 };
|
|
129
134
|
```
|
|
130
135
|
```
|
|
131
136
|
#arch: c
|
|
132
137
|
#platform: ios
|
|
133
|
-
return strdup("ios");
|
|
138
|
+
return (nomen_string){ strdup("ios"), 3 };
|
|
134
139
|
```
|
|
135
140
|
```
|
|
136
141
|
#arch: c
|
|
137
142
|
#platform: linux
|
|
138
|
-
return strdup("linux");
|
|
143
|
+
return (nomen_string){ strdup("linux"), 5 };
|
|
139
144
|
```
|
|
140
145
|
```
|
|
141
146
|
#arch: c
|
|
142
147
|
#platform: android
|
|
143
|
-
return strdup("android");
|
|
148
|
+
return (nomen_string){ strdup("android"), 7 };
|
|
144
149
|
```
|
|
145
150
|
```
|
|
146
151
|
#arch: c
|
|
147
152
|
#platform: windows
|
|
148
|
-
return strdup("windows");
|
|
153
|
+
return (nomen_string){ strdup("windows"), 7 };
|
|
149
154
|
```
|
|
150
155
|
```
|
|
151
156
|
#arch: c
|
|
152
157
|
#platform: web
|
|
153
|
-
return strdup("web");
|
|
158
|
+
return (nomen_string){ strdup("web"), 3 };
|
|
154
159
|
```
|
|
155
160
|
```
|
|
156
161
|
#arch: aarch64
|
|
157
162
|
#platform: macos
|
|
158
163
|
adr x0, .Lconsole_plat_macos
|
|
159
164
|
bl _strdup
|
|
165
|
+
mov x1, #5
|
|
160
166
|
b .return_Console_platform
|
|
161
167
|
.Lconsole_plat_macos: .asciz "macos"
|
|
162
168
|
.p2align 2
|
|
@@ -166,6 +172,7 @@ pub struct Console {
|
|
|
166
172
|
#platform: ios
|
|
167
173
|
adr x0, .Lconsole_plat_ios
|
|
168
174
|
bl _strdup
|
|
175
|
+
mov x1, #3
|
|
169
176
|
b .return_Console_platform
|
|
170
177
|
.Lconsole_plat_ios: .asciz "ios"
|
|
171
178
|
.p2align 2
|
|
@@ -175,6 +182,7 @@ pub struct Console {
|
|
|
175
182
|
#platform: linux
|
|
176
183
|
adr x0, .Lconsole_plat_linux
|
|
177
184
|
bl _strdup
|
|
185
|
+
mov x1, #5
|
|
178
186
|
b .return_Console_platform
|
|
179
187
|
.Lconsole_plat_linux: .asciz "linux"
|
|
180
188
|
.p2align 2
|
|
@@ -184,6 +192,7 @@ pub struct Console {
|
|
|
184
192
|
#platform: android
|
|
185
193
|
adr x0, .Lconsole_plat_android
|
|
186
194
|
bl _strdup
|
|
195
|
+
mov x1, #7
|
|
187
196
|
b .return_Console_platform
|
|
188
197
|
.Lconsole_plat_android: .asciz "android"
|
|
189
198
|
.p2align 2
|
|
@@ -193,6 +202,7 @@ pub struct Console {
|
|
|
193
202
|
#platform: windows
|
|
194
203
|
adr x0, .Lconsole_plat_windows
|
|
195
204
|
bl _strdup
|
|
205
|
+
mov x1, #7
|
|
196
206
|
b .return_Console_platform
|
|
197
207
|
.Lconsole_plat_windows: .asciz "windows"
|
|
198
208
|
.p2align 2
|
|
@@ -202,6 +212,7 @@ pub struct Console {
|
|
|
202
212
|
#platform: web
|
|
203
213
|
adr x0, .Lconsole_plat_web
|
|
204
214
|
bl _strdup
|
|
215
|
+
mov x1, #3
|
|
205
216
|
b .return_Console_platform
|
|
206
217
|
.Lconsole_plat_web: .asciz "web"
|
|
207
218
|
.p2align 2
|