nomen-lang 0.5.0 → 0.6.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/core/System/Arena.nm +6 -6
- package/core/System/Buffer.nm +15 -6
- package/core/System/Channel.nm +2 -0
- package/core/System/ClassBuffer.nm +14 -5
- package/core/System/Controls/Button.nm +2 -2
- package/core/System/Controls/CheckBox.nm +2 -2
- package/core/System/Controls/Container.nm +3 -3
- package/core/System/Controls/TextBox.nm +2 -2
- package/core/System/Fiber.nm +85 -5
- package/core/System/Graph.nm +1 -1
- package/core/System/LinkedList.nm +1 -1
- package/core/System/List.nm +3 -3
- package/core/System/Map.nm +2 -2
- package/core/System/Mutex.nm +8 -8
- package/core/System/Option.nm +2 -2
- package/core/System/Random.nm +67 -0
- package/core/System/Result.nm +2 -2
- package/core/System/Set.nm +1 -1
- package/core/System/Spawnable.nm +18 -0
- package/core/System/Stream/File.nm +3 -3
- package/core/System/Stream/Http.nm +5 -5
- package/core/System/Stream/Tcp.nm +6 -6
- package/core/System/String.nm +7 -7
- package/core/System/StringBuilder.nm +1 -1
- package/core/System/Task.nm +42 -8
- package/core/System/Test.nm +20 -11
- package/core/System/Text/CharIndex.nm +8 -8
- package/core/System/Text/Chars.nm +1 -1
- package/core/System/Text/JsonTree.nm +1 -1
- package/core/System/Text/Regex.nm +122 -32
- package/core/System/Text/Utf8.nm +6 -6
- package/core/System/Thread.nm +118 -2
- package/core/System/Tree.nm +1 -1
- package/dist/index.mjs +1166 -1097
- package/package.json +1 -1
package/core/System/Arena.nm
CHANGED
|
@@ -36,17 +36,17 @@ pub struct Arena<T> {
|
|
|
36
36
|
// Slot payloads. T = value struct/primitive routes to Buffer<T>; T =
|
|
37
37
|
// class/trait routes to ClassBuffer<T> (monomorphize rewrites the field),
|
|
38
38
|
// so slot ownership follows the usual element rules.
|
|
39
|
-
var
|
|
39
|
+
var values = Buffer<T>()
|
|
40
40
|
// Per-slot generation, bumped on every free. Slots are calloc'd, so a
|
|
41
41
|
// fresh slot's generation is 0.
|
|
42
|
-
var
|
|
42
|
+
var generations = Buffer<int>()
|
|
43
43
|
// Freed slot indices available for reuse (LIFO free list). A raw Buffer
|
|
44
44
|
// plus an explicit count: a `List<int>` field would need its own
|
|
45
45
|
// monomorphization from inside a generic struct body.
|
|
46
|
-
var
|
|
47
|
-
var
|
|
46
|
+
var free_indices = Buffer<int>()
|
|
47
|
+
var free_count = 0
|
|
48
48
|
// High-water mark: every index < count has a slot (live or free).
|
|
49
|
-
var
|
|
49
|
+
var count = 0
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* A fresh arena with no slots.
|
|
@@ -78,7 +78,7 @@ pub struct Arena<T> {
|
|
|
78
78
|
}
|
|
79
79
|
self.values.grow(index + 1)
|
|
80
80
|
self.values.store(index, value)
|
|
81
|
-
var
|
|
81
|
+
var handle = ArenaRef<T>()
|
|
82
82
|
handle.index = index
|
|
83
83
|
handle.generation = self.generations.load_int(index)
|
|
84
84
|
return handle
|
package/core/System/Buffer.nm
CHANGED
|
@@ -37,7 +37,7 @@ pub struct Buffer<T> {
|
|
|
37
37
|
if new_cap == 0 {
|
|
38
38
|
new_cap = 8
|
|
39
39
|
}
|
|
40
|
-
while new_cap < needed; new_cap *= 2 {}
|
|
40
|
+
while new_cap < needed; new_cap *= 2 { }
|
|
41
41
|
self.data = realloc(self.data, new_cap * 4)
|
|
42
42
|
memset(self.data + self.cap * 4, 0, (new_cap - self.cap) * 4)
|
|
43
43
|
self.cap = new_cap
|
|
@@ -107,7 +107,7 @@ pub struct Buffer<T> {
|
|
|
107
107
|
if new_cap == 0 {
|
|
108
108
|
new_cap = 4
|
|
109
109
|
}
|
|
110
|
-
while new_cap < needed; new_cap *= 2 {}
|
|
110
|
+
while new_cap < needed; new_cap *= 2 { }
|
|
111
111
|
self.data = realloc(self.data, new_cap * 8)
|
|
112
112
|
memset(self.data + self.cap * 8, 0, (new_cap - self.cap) * 8)
|
|
113
113
|
self.cap = new_cap
|
|
@@ -204,7 +204,7 @@ pub struct Buffer<T> {
|
|
|
204
204
|
if new_cap == 0 {
|
|
205
205
|
new_cap = 4
|
|
206
206
|
}
|
|
207
|
-
while new_cap < needed; new_cap *= 2 {}
|
|
207
|
+
while new_cap < needed; new_cap *= 2 { }
|
|
208
208
|
self.data = realloc(self.data, new_cap * T_SIZE)
|
|
209
209
|
memset(self.data + self.cap * T_SIZE, 0, (new_cap - self.cap) * T_SIZE)
|
|
210
210
|
self.cap = new_cap
|
|
@@ -382,7 +382,7 @@ pub struct Buffer<T> {
|
|
|
382
382
|
// this raw form is correct for trivially-copyable elements only. NOT
|
|
383
383
|
// `inline`: the body calls through the fn pointer, so splicing the raw
|
|
384
384
|
// block into a caller would bypass the per-element specializations.
|
|
385
|
-
func modify = (ref self, int i: i >= 0 && i < self.cap, func
|
|
385
|
+
func modify = (ref self, int i: i >= 0 && i < self.cap, func(T, out T) f) {
|
|
386
386
|
```
|
|
387
387
|
#arch: c
|
|
388
388
|
((T*)(unsigned long long)self->data)[i] = ((T (*)(void *, T))f->code)(f->env, ((T*)(unsigned long long)self->data)[i]);
|
|
@@ -520,7 +520,11 @@ pub struct Buffer<T> {
|
|
|
520
520
|
// version that also reclaims dst's previous value first (see
|
|
521
521
|
// owning_buffer_specialize.ts); this raw form is correct for
|
|
522
522
|
// trivially-destructible elements only.
|
|
523
|
-
inline func shift = (
|
|
523
|
+
inline func shift = (
|
|
524
|
+
ref self,
|
|
525
|
+
int dst: dst >= 0 && dst < self.cap,
|
|
526
|
+
int src: src >= 0 && src < self.cap
|
|
527
|
+
) {
|
|
524
528
|
```
|
|
525
529
|
#arch: c
|
|
526
530
|
T* _slots = (T*)(unsigned long long)self->data;
|
|
@@ -587,7 +591,12 @@ pub struct Buffer<T> {
|
|
|
587
591
|
// self's scope and is invalidated if self is reassigned. This is the single
|
|
588
592
|
// primitive every other slice (Array<T>, List<T>, user containers) builds
|
|
589
593
|
// on — higher layers just `return self.items.slice(start, end)`.
|
|
590
|
-
func slice = (
|
|
594
|
+
func slice = (
|
|
595
|
+
self,
|
|
596
|
+
int start: start >= 0,
|
|
597
|
+
int end: end >= start && end <= self.cap,
|
|
598
|
+
out view T
|
|
599
|
+
) {
|
|
591
600
|
```
|
|
592
601
|
#arch: c
|
|
593
602
|
nomen_view _r;
|
package/core/System/Channel.nm
CHANGED
|
@@ -180,6 +180,8 @@ pub class Channel: Sendable {
|
|
|
180
180
|
str x2, [x19, #32] // tail = node
|
|
181
181
|
ldr x0, [x19, #16] // not_empty_cv
|
|
182
182
|
bl _pthread_cond_signal
|
|
183
|
+
add x0, x19, #40 // &self->waiters — wake parked fibers
|
|
184
|
+
bl ___nomen_fiber_waitq_wake
|
|
183
185
|
ldr x0, [x19, #8] // self->mu
|
|
184
186
|
bl _pthread_mutex_unlock
|
|
185
187
|
add sp, sp, #32
|
|
@@ -37,7 +37,7 @@ pub struct ClassBuffer<T> {
|
|
|
37
37
|
if new_cap == 0 {
|
|
38
38
|
new_cap = 4
|
|
39
39
|
}
|
|
40
|
-
while new_cap < needed; new_cap *= 2 {}
|
|
40
|
+
while new_cap < needed; new_cap *= 2 { }
|
|
41
41
|
self.data = realloc(self.data, new_cap * 8)
|
|
42
42
|
memset(self.data + self.cap * 8, 0, (new_cap - self.cap) * 8)
|
|
43
43
|
self.cap = new_cap
|
|
@@ -73,7 +73,7 @@ pub struct ClassBuffer<T> {
|
|
|
73
73
|
if new_cap == 0 {
|
|
74
74
|
new_cap = 4
|
|
75
75
|
}
|
|
76
|
-
while new_cap < needed; new_cap *= 2 {}
|
|
76
|
+
while new_cap < needed; new_cap *= 2 { }
|
|
77
77
|
self.data = realloc(self.data, new_cap * 8)
|
|
78
78
|
memset(self.data + self.cap * 8, 0, (new_cap - self.cap) * 8)
|
|
79
79
|
self.cap = new_cap
|
|
@@ -150,7 +150,7 @@ pub struct ClassBuffer<T> {
|
|
|
150
150
|
// unless `f` returned it. Storing the returned instance raw is sound:
|
|
151
151
|
// the slot takes sole ownership and `f`'s return is not retained
|
|
152
152
|
// elsewhere. NOT `inline`: the body calls through the fn pointer.
|
|
153
|
-
func modify = (ref self, int i: i >= 0 && i < self.cap, func
|
|
153
|
+
func modify = (ref self, int i: i >= 0 && i < self.cap, func(T, out T) f) {
|
|
154
154
|
```
|
|
155
155
|
#arch: c
|
|
156
156
|
long *slots = (long*)(unsigned long long)self->data;
|
|
@@ -299,7 +299,11 @@ pub struct ClassBuffer<T> {
|
|
|
299
299
|
// instance afterwards. The class-buffer counterpart of Buffer.shift,
|
|
300
300
|
// used by Map/Set.remove's backward-shift so shifting entries neither
|
|
301
301
|
// duplicates nor orphans owned instances.
|
|
302
|
-
func shift = (
|
|
302
|
+
func shift = (
|
|
303
|
+
ref self,
|
|
304
|
+
int dst: dst >= 0 && dst < self.cap,
|
|
305
|
+
int src: src >= 0 && src < self.cap
|
|
306
|
+
) {
|
|
303
307
|
```
|
|
304
308
|
#arch: c
|
|
305
309
|
long *slots = (long*)(unsigned long long)self->data;
|
|
@@ -343,7 +347,12 @@ pub struct ClassBuffer<T> {
|
|
|
343
347
|
// pointers, returned as a `view T` (T is a class, so each element is an
|
|
344
348
|
// 8-byte pointer). Borrows from self — invalidated on reassignment. Mirrors
|
|
345
349
|
// Buffer.slice; ClassBuffer stores 8-byte slots, so the element width is 8.
|
|
346
|
-
func slice = (
|
|
350
|
+
func slice = (
|
|
351
|
+
self,
|
|
352
|
+
int start: start >= 0,
|
|
353
|
+
int end: end >= start && end <= self.cap,
|
|
354
|
+
out view T
|
|
355
|
+
) {
|
|
347
356
|
```
|
|
348
357
|
#arch: c
|
|
349
358
|
nomen_view _r;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A push-button control that triggers an action when clicked
|
|
3
3
|
**/
|
|
4
|
-
pub class Button
|
|
4
|
+
pub class Button: Control {
|
|
5
5
|
var uint64 handle = 0
|
|
6
6
|
|
|
7
7
|
pub func #init = (self, Window window, string title) {
|
|
@@ -162,7 +162,7 @@ pub class Button : Control {
|
|
|
162
162
|
|
|
163
163
|
pub func intrinsic_size = (self, out Size) {
|
|
164
164
|
var [w, h] = native_intrinsic(self.handle)
|
|
165
|
-
var
|
|
165
|
+
var s = Size()
|
|
166
166
|
s.width = w
|
|
167
167
|
s.height = h
|
|
168
168
|
return s
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A toggle control with a title and checked/hidden state
|
|
3
3
|
**/
|
|
4
|
-
pub class CheckBox
|
|
4
|
+
pub class CheckBox: Control {
|
|
5
5
|
var uint64 handle = 0
|
|
6
6
|
|
|
7
7
|
pub func #init = (self, Window window) {
|
|
@@ -199,7 +199,7 @@ pub class CheckBox : Control {
|
|
|
199
199
|
// ── Control trait conformance ──
|
|
200
200
|
pub func intrinsic_size = (self, out Size) {
|
|
201
201
|
var [w, h] = native_intrinsic(self.handle)
|
|
202
|
-
var
|
|
202
|
+
var s = Size()
|
|
203
203
|
s.width = w
|
|
204
204
|
s.height = h
|
|
205
205
|
return s
|
|
@@ -248,7 +248,7 @@ pub func native_intrinsic = (uint64 handle, out [int, int]) {
|
|
|
248
248
|
* so a `Container` can also be driven polymorphically as a `Control` (passed to
|
|
249
249
|
* a `Control`-taking function, stored in a `ClassBuffer<Control>`, etc.).
|
|
250
250
|
**/
|
|
251
|
-
pub class Container
|
|
251
|
+
pub class Container: Control {
|
|
252
252
|
var kinds = Buffer<int>()
|
|
253
253
|
// Per-leaf requested size, stored as a (kind, value) pair per axis. The
|
|
254
254
|
// kind is one of LEN_* (auto/fixed/percent/fill); the value is the pixel
|
|
@@ -1358,7 +1358,7 @@ pub class Container : Control {
|
|
|
1358
1358
|
constraints.max_height,
|
|
1359
1359
|
)
|
|
1360
1360
|
var int h = self.rh.load_int(self.root)
|
|
1361
|
-
var
|
|
1361
|
+
var s = Size()
|
|
1362
1362
|
s.width = w
|
|
1363
1363
|
s.height = h
|
|
1364
1364
|
return s
|
|
@@ -1371,7 +1371,7 @@ pub class Container : Control {
|
|
|
1371
1371
|
pub func intrinsic_size = (self, out Size) {
|
|
1372
1372
|
var int w = self.measure_node(self.root, 0, INF, 0, INF)
|
|
1373
1373
|
var int h = self.rh.load_int(self.root)
|
|
1374
|
-
var
|
|
1374
|
+
var s = Size()
|
|
1375
1375
|
s.width = w
|
|
1376
1376
|
s.height = h
|
|
1377
1377
|
return s
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* An editable single-line text input control
|
|
3
3
|
**/
|
|
4
|
-
pub class TextBox
|
|
4
|
+
pub class TextBox: Control {
|
|
5
5
|
var uint64 handle = 0
|
|
6
6
|
|
|
7
7
|
pub func #init = (self, Window window) {
|
|
@@ -203,7 +203,7 @@ pub class TextBox : Control {
|
|
|
203
203
|
// ── Control trait conformance ──
|
|
204
204
|
pub func intrinsic_size = (self, out Size) {
|
|
205
205
|
var [w, h] = native_intrinsic(self.handle)
|
|
206
|
-
var
|
|
206
|
+
var s = Size()
|
|
207
207
|
s.width = w
|
|
208
208
|
s.height = h
|
|
209
209
|
return s
|
package/core/System/Fiber.nm
CHANGED
|
@@ -23,15 +23,25 @@
|
|
|
23
23
|
// calling thread and never starts worker threads — a cooperative,
|
|
24
24
|
// single-threaded runtime for bare-metal targets.
|
|
25
25
|
//
|
|
26
|
-
// The constructor
|
|
27
|
-
//
|
|
28
|
-
// regular
|
|
29
|
-
//
|
|
26
|
+
// The constructor and `.start_on(buf)` are compiler-special-cased (they
|
|
27
|
+
// need the per-site spawn/trampoline machinery and the buffer's compile-time
|
|
28
|
+
// byte size); `.start()` is a regular method over the runtime seam — it
|
|
29
|
+
// reads the packed handles from the instance's fields and drives the
|
|
30
|
+
// scheduler. The runtime (scheduler, context switch) lives in the build's
|
|
31
|
+
// emitted companion C — see build_fiber_spawn.
|
|
32
|
+
|
|
33
|
+
// The launch surface's return type (ASYNC.md: the dependency is
|
|
34
|
+
// declared by the library file, not inferred from user-source tokens).
|
|
35
|
+
import Task
|
|
30
36
|
|
|
31
37
|
/**
|
|
32
38
|
* A stackful coroutine: lightweight, cooperatively scheduled, parks instead of blocking
|
|
33
39
|
**/
|
|
34
|
-
pub class Fiber<T> : Sendable {
|
|
40
|
+
pub class Fiber<T> : Sendable, Spawnable<T> {
|
|
41
|
+
// The construction hook (see Thread.nm): the deferred-call special form.
|
|
42
|
+
#spawn = () {
|
|
43
|
+
}
|
|
44
|
+
|
|
35
45
|
// The packed task closure (a `struct nomen_closure *`); owned by this
|
|
36
46
|
// value until start/start_on/nursery.start transfers it to the runtime.
|
|
37
47
|
pub var uint64 task = 0
|
|
@@ -41,6 +51,76 @@ pub class Fiber<T> : Sendable {
|
|
|
41
51
|
pub var uint64 future = 0
|
|
42
52
|
// Must-start flag: set by .start() / .start_on(buf) / nursery.start.
|
|
43
53
|
pub var started = false
|
|
54
|
+
// LEXICAL NURSERY CAPTURE (ASYNC.md): the tracking slots of the
|
|
55
|
+
// `async` block that lexically encloses the CONSTRUCTION, or 0 when the
|
|
56
|
+
// construction is outside any block. `.start()` registers the future
|
|
57
|
+
// through these stored pointers — registration follows the scope that
|
|
58
|
+
// created the deferred call, not the one that happens to start it. See
|
|
59
|
+
// Thread.nm's matching fields.
|
|
60
|
+
pub var uint64 nursery_futures = 0
|
|
61
|
+
pub var uint64 nursery_count = 0
|
|
62
|
+
pub var uint64 nursery_cap = 0
|
|
63
|
+
|
|
64
|
+
// Start the deferred call on the fiber scheduler and yield the Task<T>
|
|
65
|
+
// handle. Same shape as Thread.start: consume the machinery, register
|
|
66
|
+
// with the construction-time nursery (if captured), transfer the handles
|
|
67
|
+
// out of the instance (started; #destroy no-ops), return the handle.
|
|
68
|
+
// While the fiber waits, it parks and frees its worker instead of
|
|
69
|
+
// blocking it.
|
|
70
|
+
pub func start = (ref self, move out Task<T>) {
|
|
71
|
+
```
|
|
72
|
+
#arch: c
|
|
73
|
+
struct nomen_future *_f = (struct nomen_future *)self->future;
|
|
74
|
+
_f->refs = self->nursery_futures ? 3 : 2;
|
|
75
|
+
__nomen_fiber_spawn((struct nomen_closure *)self->task, _f);
|
|
76
|
+
if (self->nursery_futures) {
|
|
77
|
+
__nomen_nursery_track((void **)self->nursery_futures, (int *)self->nursery_count, (int *)self->nursery_cap, _f);
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
```
|
|
81
|
+
#arch: aarch64
|
|
82
|
+
// x19 = self. f = self->future (#32); nursery_futures at #48.
|
|
83
|
+
ldr x0, [x19, #32]
|
|
84
|
+
ldr x1, [x19, #48]
|
|
85
|
+
mov w2, #2
|
|
86
|
+
cbz x1, .Lnomen_fstart_refs
|
|
87
|
+
mov w2, #3
|
|
88
|
+
.Lnomen_fstart_refs:
|
|
89
|
+
str w2, [x0, #116]
|
|
90
|
+
// __nomen_fiber_spawn(self->task, f)
|
|
91
|
+
ldr x0, [x19, #8]
|
|
92
|
+
ldr x1, [x19, #32]
|
|
93
|
+
bl ___nomen_fiber_spawn
|
|
94
|
+
// if (self->nursery_futures) __nomen_nursery_track(futures, count, cap, f)
|
|
95
|
+
ldr x0, [x19, #48]
|
|
96
|
+
cbz x0, .Lnomen_fstart_notrack
|
|
97
|
+
ldr x1, [x19, #56]
|
|
98
|
+
ldr x2, [x19, #64]
|
|
99
|
+
ldr x3, [x19, #32]
|
|
100
|
+
bl ___nomen_nursery_track
|
|
101
|
+
.Lnomen_fstart_notrack:
|
|
102
|
+
```
|
|
103
|
+
var t = Task<T>()
|
|
104
|
+
t.result_slot = self.result_slot
|
|
105
|
+
t.cancel_flag = self.cancel_flag
|
|
106
|
+
t.future = self.future
|
|
107
|
+
// Transfer the handles out of the instance: the runtime owns
|
|
108
|
+
// everything now, and #destroy must not touch (or must-start-abort).
|
|
109
|
+
self.task = 0
|
|
110
|
+
self.future = 0
|
|
111
|
+
self.result_slot = 0
|
|
112
|
+
self.cancel_flag = 0
|
|
113
|
+
self.started = true
|
|
114
|
+
return t
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Run the deferred call fire-and-forget (the Spawnable daemon role).
|
|
118
|
+
// The fiber is launched exactly like start, but the handle is dropped
|
|
119
|
+
// immediately: the future's refs are released as the discarded Task is
|
|
120
|
+
// reclaimed, and the fiber completes into the void — nothing joins it.
|
|
121
|
+
pub func detach = (ref self) {
|
|
122
|
+
self.start()
|
|
123
|
+
}
|
|
44
124
|
|
|
45
125
|
// Destroying an unstarted Fiber is a programming error (the wrapped
|
|
46
126
|
// call would silently never run) — report it and abort. A started
|
package/core/System/Graph.nm
CHANGED
package/core/System/List.nm
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
**/
|
|
4
4
|
pub struct List<T>: Viewable {
|
|
5
5
|
var length = 0
|
|
6
|
-
var
|
|
6
|
+
var items = Buffer<T>()
|
|
7
7
|
|
|
8
8
|
pub func push = (ref self, move T value) {
|
|
9
9
|
if self.length >= self.items.cap {
|
|
@@ -70,8 +70,8 @@ pub struct List<T>: Viewable {
|
|
|
70
70
|
// never re-checked, so it can't rely on check-time call-arg hoisting for
|
|
71
71
|
// struct element types.
|
|
72
72
|
pub func copy = (self, out List<T>) {
|
|
73
|
-
var
|
|
74
|
-
var
|
|
73
|
+
var dst = List<T>()
|
|
74
|
+
var i = 0
|
|
75
75
|
while i < self.length {
|
|
76
76
|
var T v = self.at(i)
|
|
77
77
|
dst.push(v)
|
package/core/System/Map.nm
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
**/
|
|
11
11
|
pub struct Map<TK: Hashable + Equatable, TV> {
|
|
12
12
|
var length = 0
|
|
13
|
-
var
|
|
14
|
-
var
|
|
13
|
+
var keys = Buffer<TK>()
|
|
14
|
+
var values = Buffer<TV>()
|
|
15
15
|
var used = Buffer<int>()
|
|
16
16
|
|
|
17
17
|
// Variadic-tuple constructor: Map<string, int>(["a", 1], ["b", 2]) or
|
package/core/System/Mutex.nm
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
pub class Mutex: Sendable {
|
|
17
17
|
pub var uint64 handle = 0
|
|
18
18
|
|
|
19
|
-
pub func #init = (self) {
|
|
19
|
+
pub func #init = (self) {
|
|
20
20
|
```
|
|
21
21
|
#arch: c
|
|
22
22
|
self->handle = (unsigned long long)__nomen_mutex_create();
|
|
@@ -26,9 +26,9 @@ pub func #init = (self) {
|
|
|
26
26
|
bl ___nomen_mutex_create
|
|
27
27
|
str x0, [x19, #8]
|
|
28
28
|
```
|
|
29
|
-
}
|
|
29
|
+
}
|
|
30
30
|
|
|
31
|
-
pub func lock = (self) {
|
|
31
|
+
pub func lock = (self) {
|
|
32
32
|
```
|
|
33
33
|
#arch: c
|
|
34
34
|
// Fibers park instead of blocking the worker (both models); other
|
|
@@ -40,9 +40,9 @@ pub func lock = (self) {
|
|
|
40
40
|
ldr x0, [x19, #8]
|
|
41
41
|
bl ___nomen_mutex_lock
|
|
42
42
|
```
|
|
43
|
-
}
|
|
43
|
+
}
|
|
44
44
|
|
|
45
|
-
pub func unlock = (self) {
|
|
45
|
+
pub func unlock = (self) {
|
|
46
46
|
```
|
|
47
47
|
#arch: c
|
|
48
48
|
__nomen_mutex_unlock_wake((void *)self->handle);
|
|
@@ -52,9 +52,9 @@ pub func unlock = (self) {
|
|
|
52
52
|
ldr x0, [x19, #8]
|
|
53
53
|
bl ___nomen_mutex_unlock_wake
|
|
54
54
|
```
|
|
55
|
-
}
|
|
55
|
+
}
|
|
56
56
|
|
|
57
|
-
pub func #destroy = () {
|
|
57
|
+
pub func #destroy = () {
|
|
58
58
|
```
|
|
59
59
|
#arch: c
|
|
60
60
|
if (self->handle) {
|
|
@@ -70,5 +70,5 @@ pub func #destroy = () {
|
|
|
70
70
|
str xzr, [x19, #8]
|
|
71
71
|
.Lmutex_destroy_end:
|
|
72
72
|
```
|
|
73
|
-
}
|
|
73
|
+
}
|
|
74
74
|
}
|
package/core/System/Option.nm
CHANGED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Random — a small, fast pseudo-random generator (splitmix64).
|
|
2
|
+
//
|
|
3
|
+
// Splitmix64 steps a 64-bit state by the golden ratio, then runs two
|
|
4
|
+
// multiply-xor rounds. Streams from nearby seeds are fully decorrelated
|
|
5
|
+
// (each seed is its own stream), so `Random()` — seeded from the wall
|
|
6
|
+
// clock — is safe to construct repeatedly in quick succession (e.g. one
|
|
7
|
+
// per spawned task, all starting within the same microsecond).
|
|
8
|
+
//
|
|
9
|
+
// Not cryptographically secure; `below`/`range` have slight modulo bias
|
|
10
|
+
// for very large bounds.
|
|
11
|
+
//
|
|
12
|
+
// var Random rng = Random()
|
|
13
|
+
// var uint64 roll = rng.below(6) // 0..5
|
|
14
|
+
// var uint64 ms = rng.range(1000, 10000) // 1000..10000, inclusive
|
|
15
|
+
//
|
|
16
|
+
import Time
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A fast splitmix64 pseudo-random number generator
|
|
20
|
+
**/
|
|
21
|
+
pub class Random {
|
|
22
|
+
// The raw 64-bit generator state (seeded from the wall clock by the
|
|
23
|
+
// no-arg constructor, or an explicit seed for reproducible sequences).
|
|
24
|
+
pub var uint64 state = 0
|
|
25
|
+
|
|
26
|
+
// Seed from the wall clock (nanoseconds since the epoch).
|
|
27
|
+
pub func #init = (self) {
|
|
28
|
+
self.state = Time.now_ns()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Seed from an explicit value — reproducible sequences for tests.
|
|
32
|
+
pub func #init = (self, uint64 seed) {
|
|
33
|
+
self.state = seed
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// The next 64-bit value (splitmix64: golden-ratio step, then two
|
|
37
|
+
// multiply-xor rounds). Pure Nomen — the 64-bit arithmetic, bitwise
|
|
38
|
+
// operators, and hex literals all lower directly. The aarch64 codegen is
|
|
39
|
+
// a little looser than a hand-written `#arch` body would be (constant
|
|
40
|
+
// materialization, field reloads, non-immediate shift counts); see
|
|
41
|
+
// FOLLOWUP.md if this ever needs to be hot.
|
|
42
|
+
pub func next = (ref self, out uint64) {
|
|
43
|
+
self.state = self.state + 0x9E3779B97F4A7C15
|
|
44
|
+
var uint64 z = self.state
|
|
45
|
+
z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9
|
|
46
|
+
z = (z ^ (z >> 27)) * 0x94D049BB133111EB
|
|
47
|
+
return z ^ (z >> 31)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// The next value in [0, bound). A bound of 0 returns 0. Slight modulo
|
|
51
|
+
// bias for very large bounds — irrelevant at demo scale.
|
|
52
|
+
pub func below = (ref self, uint64 bound, out uint64) {
|
|
53
|
+
if bound == 0 {
|
|
54
|
+
return 0
|
|
55
|
+
}
|
|
56
|
+
return self.next() % bound
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// The next value in [lo, hi], both ends inclusive. A hi <= lo
|
|
60
|
+
// returns lo.
|
|
61
|
+
pub func range = (ref self, uint64 lo, uint64 hi, out uint64) {
|
|
62
|
+
if hi <= lo {
|
|
63
|
+
return lo
|
|
64
|
+
}
|
|
65
|
+
return lo + self.below(hi - lo + 1)
|
|
66
|
+
}
|
|
67
|
+
}
|
package/core/System/Result.nm
CHANGED
package/core/System/Set.nm
CHANGED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Spawnable — the trait role of a deferred-work LAUNCHER (the C# shape;
|
|
2
|
+
// see docs/ASYNC.md, "Design decisions"). A Spawnable class packs a deferred unit of work
|
|
3
|
+
// (the construction special form) and its `start` submits it, yielding the
|
|
4
|
+
// library-owned `Task<T>` handle — so the await half is guaranteed by the
|
|
5
|
+
// return type and each type carries exactly one role: `Thread<T>`/`Fiber<T>`
|
|
6
|
+
// are spawnable, `Task<T>` is awaitable. `Awaitable` stays non-generic (its
|
|
7
|
+
// `wait` does not mention T); `Spawnable` is generic because `start`'s
|
|
8
|
+
// return type does.
|
|
9
|
+
//
|
|
10
|
+
// `detach` is the daemon role: run the work unjoinable, statement only.
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A class that can launch a deferred call and hand back its Task<T> handle
|
|
14
|
+
**/
|
|
15
|
+
pub trait Spawnable<T> {
|
|
16
|
+
func start = (ref self, move out Task<T>)
|
|
17
|
+
func detach = (ref self)
|
|
18
|
+
}
|
|
@@ -39,7 +39,7 @@ pub struct File {
|
|
|
39
39
|
pub var eof = false
|
|
40
40
|
// errno of the last operation; 0 = success. Raw #arch bodies set it,
|
|
41
41
|
// the Result-returning wrappers map it to FileError.
|
|
42
|
-
var
|
|
42
|
+
var error = 0
|
|
43
43
|
|
|
44
44
|
// Maps an errno to its FileError case.
|
|
45
45
|
func error_of = (int err, out FileError) {
|
|
@@ -569,7 +569,7 @@ pub struct File {
|
|
|
569
569
|
// (static) Read the entire contents of `path` in one open/read/close.
|
|
570
570
|
pub func read_all = (string path, out Result<string, FileError>) {
|
|
571
571
|
var Result<string, FileError> result = .error(FileError.other)
|
|
572
|
-
var
|
|
572
|
+
var f = File()
|
|
573
573
|
match f.open(path, "r") {
|
|
574
574
|
case .ok(did) {
|
|
575
575
|
match f.readAll() {
|
|
@@ -594,7 +594,7 @@ pub struct File {
|
|
|
594
594
|
// be deliberate).
|
|
595
595
|
pub func write_all = (string path, string data, out Result<bool, FileError>) {
|
|
596
596
|
var Result<bool, FileError> result = .error(FileError.other)
|
|
597
|
-
var
|
|
597
|
+
var f = File()
|
|
598
598
|
match f.open(path, "w") {
|
|
599
599
|
case .ok(did) {
|
|
600
600
|
match f.writeAll(data) {
|
|
@@ -28,13 +28,13 @@
|
|
|
28
28
|
pub struct Http {
|
|
29
29
|
// HTTP status code from the most recent request (e.g. 200). 0 when the
|
|
30
30
|
// request failed (the `error` side of the get/post Result says why).
|
|
31
|
-
pub var
|
|
31
|
+
pub var status = 0
|
|
32
32
|
|
|
33
33
|
// Internal failure code from the most recent request: 0 = success,
|
|
34
34
|
// 1 = DNS resolution, 2 = socket creation, 3 = connect, 6 = send failed.
|
|
35
35
|
// Mirrors Tcp's codes on the connect path; the Result-returning wrappers
|
|
36
36
|
// map it to HttpError.
|
|
37
|
-
var
|
|
37
|
+
var error = 0
|
|
38
38
|
|
|
39
39
|
// Maps the raw failure code to its HttpError case.
|
|
40
40
|
func error_of = (int err, out HttpError) {
|
|
@@ -61,7 +61,7 @@ pub struct Http {
|
|
|
61
61
|
// on failure).
|
|
62
62
|
func raw_exchange = (ref self, string method, string url, string body, move out string) {
|
|
63
63
|
// Parse the URL: [http://]host[:port][/path]
|
|
64
|
-
var
|
|
64
|
+
var host_start = 0
|
|
65
65
|
if url.starts_with("http://") {
|
|
66
66
|
host_start = 7
|
|
67
67
|
}
|
|
@@ -77,13 +77,13 @@ pub struct Http {
|
|
|
77
77
|
name_end = colon
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
-
var
|
|
80
|
+
var port = 80
|
|
81
81
|
if name_end != host_end {
|
|
82
82
|
var string port_text = url.substring(name_end + 1, host_end)
|
|
83
83
|
port = int.parse(port_text)
|
|
84
84
|
}
|
|
85
85
|
var string host = url.substring(host_start, name_end)
|
|
86
|
-
var
|
|
86
|
+
var path = "/"
|
|
87
87
|
if slash >= 0 {
|
|
88
88
|
path = url.substring(slash, url.length)
|
|
89
89
|
}
|