nomen-lang 0.8.0 → 0.10.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/audit_runtime.c +12 -2
- package/core/System/Arena.nm +14 -5
- package/core/System/Array.nm +1 -1
- package/core/System/Atomic.nm +3 -3
- package/core/System/BigInt.nm +182 -23
- package/core/System/Buffer.nm +392 -33
- package/core/System/ClassBuffer.nm +145 -13
- package/core/System/Controls/Alert.nm +3 -3
- package/core/System/Controls/App.nm +17 -17
- package/core/System/Controls/Button.nm +2 -2
- package/core/System/Controls/CheckBox.nm +3 -3
- package/core/System/Controls/ColorPicker.nm +2 -2
- package/core/System/Controls/ComboBox.nm +2 -2
- package/core/System/Controls/Container.nm +39 -33
- package/core/System/Controls/DatePicker.nm +1 -1
- package/core/System/Controls/Form.nm +2 -2
- package/core/System/Controls/GroupBox.nm +2 -2
- package/core/System/Controls/ImageView.nm +2 -2
- package/core/System/Controls/Layout.nm +5 -5
- package/core/System/Controls/LevelIndicator.nm +2 -2
- package/core/System/Controls/ListView.nm +2 -2
- package/core/System/Controls/Menu.nm +2 -2
- package/core/System/Controls/NumberInput.nm +3 -3
- package/core/System/Controls/OtpInput.nm +2 -2
- package/core/System/Controls/OutlineView.nm +2 -2
- package/core/System/Controls/Pagination.nm +14 -14
- package/core/System/Controls/Popover.nm +2 -2
- package/core/System/Controls/Progress.nm +2 -2
- package/core/System/Controls/Radio.nm +3 -3
- package/core/System/Controls/Scrollable.nm +1 -1
- package/core/System/Controls/SearchField.nm +2 -2
- package/core/System/Controls/SecureField.nm +2 -2
- package/core/System/Controls/Segmented.nm +2 -2
- package/core/System/Controls/Select.nm +2 -2
- package/core/System/Controls/Separator.nm +2 -2
- package/core/System/Controls/Sheet.nm +2 -2
- package/core/System/Controls/Slider.nm +2 -2
- package/core/System/Controls/Spinner.nm +5 -5
- package/core/System/Controls/SplitView.nm +3 -3
- package/core/System/Controls/Stepper.nm +2 -2
- package/core/System/Controls/Switch.nm +2 -2
- package/core/System/Controls/Table.nm +2 -2
- package/core/System/Controls/Tabs.nm +2 -2
- package/core/System/Controls/Text.nm +2 -2
- package/core/System/Controls/TextBox.nm +3 -3
- package/core/System/Controls/TextView.nm +2 -2
- package/core/System/Controls/Toggle.nm +3 -3
- package/core/System/Controls/Toolbar.nm +2 -2
- package/core/System/Controls/Window.nm +12 -5
- package/core/System/Fiber.nm +16 -3
- package/core/System/Graph.nm +2 -2
- package/core/System/LinkedList.nm +13 -4
- package/core/System/List.nm +76 -17
- package/core/System/Map.nm +179 -21
- package/core/System/Mutex.nm +4 -4
- package/core/System/Random.nm +6 -7
- package/core/System/Set.nm +99 -23
- package/core/System/StableList.nm +371 -0
- package/core/System/Stream/Directory.nm +2 -2
- package/core/System/Stream/File.nm +18 -18
- package/core/System/Stream/Http.nm +5 -5
- package/core/System/Stream/Tcp.nm +5 -5
- package/core/System/String.nm +10 -5
- package/core/System/StringBuilder.nm +223 -12
- package/core/System/Task.nm +56 -64
- package/core/System/Test.nm +18 -18
- package/core/System/Text/CharIndex.nm +9 -9
- package/core/System/Text/Chars.nm +1 -1
- package/core/System/Text/Json.nm +72 -58
- package/core/System/Text/JsonTree.nm +126 -98
- package/core/System/Text/Regex.nm +43 -38
- package/core/System/Text/Utf8.nm +5 -5
- package/core/System/Thread.nm +26 -3
- package/core/System/Tree.nm +19 -3
- package/core/System/float.nm +36 -22
- package/core/System/int.nm +80 -28
- package/core/System/int16.nm +11 -0
- package/core/System/int32.nm +11 -0
- package/core/System/int64.nm +11 -0
- package/core/System/int8.nm +11 -0
- package/core/System/uint.nm +11 -0
- package/core/System/uint16.nm +11 -0
- package/core/System/uint32.nm +11 -0
- package/core/System/uint64.nm +11 -0
- package/core/System/uint8.nm +11 -0
- package/core/docs/System/StringBuilder.md +6 -0
- package/core/docs/System/Text/JsonTree.md +3 -2
- package/dist/index.mjs +37622 -30603
- package/package.json +1 -1
- package/src/bench_loop.nm +9 -9
|
@@ -1,156 +1,184 @@
|
|
|
1
|
-
// JsonTree: a flat pool of JSON tree nodes backed by a
|
|
2
|
-
// Each node is a value struct
|
|
3
|
-
//
|
|
4
|
-
// calls reset() per document
|
|
1
|
+
// JsonTree: a flat pool of JSON tree nodes backed by a StableList<JsonNode>.
|
|
2
|
+
// Each node is a value struct appended to the list; node indices are list
|
|
3
|
+
// indices, and list slots never move. The caller creates a JsonTree once and
|
|
4
|
+
// calls reset() per document. The pool owns its node texts end to end —
|
|
5
|
+
// set_text/take_text's interior field write frees the displaced text and the
|
|
6
|
+
// list's own destroy reclaims every node — so there is no manual text
|
|
7
|
+
// lifecycle. (The pre-StableList version kept a raw Buffer slab with a
|
|
8
|
+
// hand-encoded element layout and a strdup/free dance; see the stable grow-only
|
|
9
|
+
// arrays".)
|
|
5
10
|
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// the slab's text pointer dangling.
|
|
11
|
+
// Accessors read and write through the slot alias (`ref JsonNode n =
|
|
12
|
+
// self.nodes.at(idx)`), the checked interior-mutation form: the alias binds
|
|
13
|
+
// the slot itself, so field writes land in the list and reads copy nothing.
|
|
10
14
|
|
|
11
15
|
// A node in a parsed JSON tree, stored by value in the pool.
|
|
12
16
|
// kind: 0=null, 1=true, 2=number, 3=string, 4=array, 5=object, 6=false
|
|
13
17
|
// child/next/val are node indices, -1 = none.
|
|
18
|
+
//
|
|
19
|
+
// `text` is nullable and seeds as `null` (the in-band zero pair): the auto
|
|
20
|
+
// constructor stores no pointer for it, so a fresh node costs NO allocation
|
|
21
|
+
// — every parsed node used to pay a strdup-of-"" seed plus the free when
|
|
22
|
+
// take_text displaced it (or at destroy for kinds that never carry text).
|
|
23
|
+
// set_text/take_text install the owned value; until then the field holds
|
|
24
|
+
// null and every read (get_text/get_text_view) answers the empty string.
|
|
14
25
|
mv struct JsonNode {
|
|
15
26
|
var kind = 0
|
|
16
|
-
var text =
|
|
27
|
+
var string? text = null
|
|
17
28
|
var int child = -1
|
|
18
29
|
var int next = -1
|
|
19
30
|
var int val = -1
|
|
20
31
|
}
|
|
21
32
|
|
|
22
33
|
/**
|
|
23
|
-
* A flat,
|
|
34
|
+
* A flat, pool-backed set of JSON tree nodes — the parsed representation `Json.parse` produces
|
|
24
35
|
**/
|
|
25
36
|
pub mv struct JsonTree {
|
|
26
|
-
var nodes =
|
|
27
|
-
var count = 0
|
|
28
|
-
|
|
29
|
-
func alloc_node = (ref self, out int) {
|
|
30
|
-
var int idx = self.count
|
|
31
|
-
self.nodes.grow(idx + 1)
|
|
32
|
-
self.count = idx + 1
|
|
33
|
-
var default = JsonNode()
|
|
34
|
-
self.nodes.store(idx, default)
|
|
35
|
-
// store_T deep-copies the mv `text` field (strdup of the shared
|
|
36
|
-
// empty-string literal) into the slot. JsonTree manages node texts
|
|
37
|
-
// manually (set_text strdup's into the slab; free_text/release frees
|
|
38
|
-
// them; NULL means "no text"), so the slot must start NULL — free the
|
|
39
|
-
// strdup'd default and NULL it, or reset() would leak it. The slab
|
|
40
|
-
// slot layout: 56-byte JsonNode elements — 7 words — with the text
|
|
41
|
-
// pair at words 2 and 3 of each element.
|
|
42
|
-
unsafe {
|
|
43
|
-
var ptr uint64 slots = self.nodes.data as ptr uint64
|
|
44
|
-
var int t = idx * 7 + 2
|
|
45
|
-
var uint64 old = slots[t]
|
|
46
|
-
if old != 0 {
|
|
47
|
-
free(old)
|
|
48
|
-
}
|
|
49
|
-
slots[t] = 0
|
|
50
|
-
slots[t + 1] = 0
|
|
51
|
-
}
|
|
52
|
-
return idx
|
|
53
|
-
}
|
|
37
|
+
var nodes = StableList<JsonNode>()
|
|
54
38
|
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
var ptr uint64 slots = self.nodes.data as ptr uint64
|
|
60
|
-
var int t = idx * 7 + 2
|
|
61
|
-
var uint64 p = slots[t]
|
|
62
|
-
if p != 0 {
|
|
63
|
-
free(p)
|
|
64
|
-
slots[t] = 0
|
|
65
|
-
slots[t + 1] = 0
|
|
66
|
-
}
|
|
67
|
-
}
|
|
39
|
+
// Append a fresh node and return its index.
|
|
40
|
+
func alloc_node = (ref self { nodes }, out int) {
|
|
41
|
+
self.nodes.push(JsonNode())
|
|
42
|
+
return self.nodes.length - 1
|
|
68
43
|
}
|
|
69
44
|
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
self.free_text(i)
|
|
76
|
-
}
|
|
77
|
-
self.count = 0
|
|
45
|
+
// Drop the current document: the fresh StableList displaces the old one,
|
|
46
|
+
// whose destroy frees every node (texts included) and its slabs. The
|
|
47
|
+
// next parse starts from a clean pool.
|
|
48
|
+
func reset = (ref self { nodes }) {
|
|
49
|
+
self.nodes = StableList<JsonNode>()
|
|
78
50
|
}
|
|
79
51
|
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
52
|
+
// Install node `idx`'s text, TAKING ownership of the argument (the D3
|
|
53
|
+
// mv convention): `set_text(n, mv s)` transfers the caller's buffer
|
|
54
|
+
// into the pool with no copy anywhere — the field store moves the pair
|
|
55
|
+
// raw and the caller's variable is invalidated. (Pass `.copy()` for the
|
|
56
|
+
// keep-the-original spelling: `set_text(n, s.copy())`.) The pool owns
|
|
57
|
+
// its node texts end to end, so the transferred buffer is freed exactly
|
|
58
|
+
// once, by the list's destroy or the next text store. For strings
|
|
59
|
+
// produced by a StringBuilder, `take_text` installs the builder's
|
|
60
|
+
// buffer with no copy at all.
|
|
61
|
+
func set_text = (ref self {}, int idx, mv string text) {
|
|
62
|
+
if idx < 0 || idx >= self.nodes.length {
|
|
63
|
+
panic("index out of range")
|
|
87
64
|
}
|
|
88
|
-
|
|
65
|
+
ref JsonNode n = self.nodes.at(idx)
|
|
66
|
+
n.text = mv text
|
|
89
67
|
}
|
|
90
68
|
|
|
91
|
-
//
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
slots[t] = buf
|
|
104
|
-
slots[t + 1] = len
|
|
69
|
+
// Install node `idx`'s text by TAKING the builder's buffer — the parse
|
|
70
|
+
// path's zero-copy store. `to_string` is a take (the builder hands its
|
|
71
|
+
// heap buffer over and resets), and a fresh-call RHS stores into the
|
|
72
|
+
// field directly, so per parsed string/key the only allocation in the
|
|
73
|
+
// whole node lifecycle is the builder's own buffer — the node's null
|
|
74
|
+
// seed costs nothing and there is no copy malloc, no seed free, no
|
|
75
|
+
// temp free. (A `mv string` parameter cannot do this: the string-arg
|
|
76
|
+
// ownership convention makes mv string arguments caller-owned
|
|
77
|
+
// borrows, forcing the callee-side copy this method exists to skip.)
|
|
78
|
+
func take_text = (ref self {}, int idx, ref StringBuilder sb {}) {
|
|
79
|
+
if idx < 0 || idx >= self.nodes.length {
|
|
80
|
+
panic("index out of range")
|
|
105
81
|
}
|
|
82
|
+
ref JsonNode n = self.nodes.at(idx)
|
|
83
|
+
n.text = sb.to_string()
|
|
106
84
|
}
|
|
107
85
|
|
|
108
|
-
func set_kind = (ref self, int idx, int val) {
|
|
109
|
-
|
|
86
|
+
func set_kind = (ref self {}, int idx, int val) {
|
|
87
|
+
if idx < 0 || idx >= self.nodes.length {
|
|
88
|
+
panic("index out of range")
|
|
89
|
+
}
|
|
90
|
+
ref JsonNode n = self.nodes.at(idx)
|
|
110
91
|
n.kind = val
|
|
111
|
-
self.nodes.store(idx, n)
|
|
112
92
|
}
|
|
113
93
|
|
|
114
|
-
func set_child = (ref self, int idx, int val) {
|
|
115
|
-
|
|
94
|
+
func set_child = (ref self {}, int idx, int val) {
|
|
95
|
+
if idx < 0 || idx >= self.nodes.length {
|
|
96
|
+
panic("index out of range")
|
|
97
|
+
}
|
|
98
|
+
ref JsonNode n = self.nodes.at(idx)
|
|
116
99
|
n.child = val
|
|
117
|
-
self.nodes.store(idx, n)
|
|
118
100
|
}
|
|
119
101
|
|
|
120
|
-
func set_next = (ref self, int idx, int val) {
|
|
121
|
-
|
|
102
|
+
func set_next = (ref self {}, int idx, int val) {
|
|
103
|
+
if idx < 0 || idx >= self.nodes.length {
|
|
104
|
+
panic("index out of range")
|
|
105
|
+
}
|
|
106
|
+
ref JsonNode n = self.nodes.at(idx)
|
|
122
107
|
n.next = val
|
|
123
|
-
self.nodes.store(idx, n)
|
|
124
108
|
}
|
|
125
109
|
|
|
126
|
-
func set_val = (ref self, int idx, int val) {
|
|
127
|
-
|
|
110
|
+
func set_val = (ref self {}, int idx, int val) {
|
|
111
|
+
if idx < 0 || idx >= self.nodes.length {
|
|
112
|
+
panic("index out of range")
|
|
113
|
+
}
|
|
114
|
+
ref JsonNode n = self.nodes.at(idx)
|
|
128
115
|
n.val = val
|
|
129
|
-
self.nodes.store(idx, n)
|
|
130
116
|
}
|
|
131
117
|
|
|
132
118
|
func get_kind = (self, int idx, out int) {
|
|
133
|
-
|
|
119
|
+
if idx < 0 || idx >= self.nodes.length {
|
|
120
|
+
panic("index out of range")
|
|
121
|
+
}
|
|
122
|
+
ref JsonNode n = self.nodes.at(idx)
|
|
134
123
|
return n.kind
|
|
135
124
|
}
|
|
136
125
|
|
|
137
126
|
func get_text = (self, int idx, out string) {
|
|
138
|
-
|
|
127
|
+
if idx < 0 || idx >= self.nodes.length {
|
|
128
|
+
panic("index out of range")
|
|
129
|
+
}
|
|
130
|
+
ref JsonNode n = self.nodes.at(idx)
|
|
131
|
+
// The aarch64 `??` yields the left pair AS-IS (no dup), so the
|
|
132
|
+
// coalesce spelling would return a borrow of the node's text — the
|
|
133
|
+
// caller's free would take the pool's buffer. Spell the owned copy:
|
|
134
|
+
// a null text answers a fresh "" and a non-null one takes
|
|
135
|
+
// `.copy()` (the return contract — the caller owns the result —
|
|
136
|
+
// is the `.copy()` shape the method always had).
|
|
137
|
+
if n.text == null {
|
|
138
|
+
return ""
|
|
139
|
+
}
|
|
139
140
|
return n.text.copy()
|
|
140
141
|
}
|
|
141
142
|
|
|
143
|
+
// A non-mv (ptr, len) view over node `idx`'s text — the zero-copy read.
|
|
144
|
+
// The borrow roots at `self` and re-roots at the call-site receiver
|
|
145
|
+
// (the `slice` convention), so it is invalidated by `reset()`/re-parse
|
|
146
|
+
// like any pool borrow. Callers that immediately append, hash, or
|
|
147
|
+
// inspect the text take this instead of paying `get_text`'s per-read
|
|
148
|
+
// copy allocation. A node that never had text installed views the
|
|
149
|
+
// empty string (a rodata-rooted empty borrow).
|
|
150
|
+
func get_text_view = (self, int idx, out view string) {
|
|
151
|
+
if idx < 0 || idx >= self.nodes.length {
|
|
152
|
+
panic("index out of range")
|
|
153
|
+
}
|
|
154
|
+
ref JsonNode n = self.nodes.at(idx)
|
|
155
|
+
if n.text == null {
|
|
156
|
+
return "".slice(0, 0)
|
|
157
|
+
}
|
|
158
|
+
return n.text.slice(0, n.text.length)
|
|
159
|
+
}
|
|
160
|
+
|
|
142
161
|
func get_child = (self, int idx, out int) {
|
|
143
|
-
|
|
162
|
+
if idx < 0 || idx >= self.nodes.length {
|
|
163
|
+
panic("index out of range")
|
|
164
|
+
}
|
|
165
|
+
ref JsonNode n = self.nodes.at(idx)
|
|
144
166
|
return n.child
|
|
145
167
|
}
|
|
146
168
|
|
|
147
169
|
func get_next = (self, int idx, out int) {
|
|
148
|
-
|
|
170
|
+
if idx < 0 || idx >= self.nodes.length {
|
|
171
|
+
panic("index out of range")
|
|
172
|
+
}
|
|
173
|
+
ref JsonNode n = self.nodes.at(idx)
|
|
149
174
|
return n.next
|
|
150
175
|
}
|
|
151
176
|
|
|
152
177
|
func get_val = (self, int idx, out int) {
|
|
153
|
-
|
|
178
|
+
if idx < 0 || idx >= self.nodes.length {
|
|
179
|
+
panic("index out of range")
|
|
180
|
+
}
|
|
181
|
+
ref JsonNode n = self.nodes.at(idx)
|
|
154
182
|
return n.val
|
|
155
183
|
}
|
|
156
184
|
}
|
|
@@ -33,10 +33,9 @@ pub struct Regex {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
// Return the first substring of input matching pattern, or "" if none.
|
|
36
|
-
// `mv out string`: the
|
|
37
|
-
//
|
|
36
|
+
// `mv out string`: the matched span (or the empty literal) is handed to
|
|
37
|
+
// the caller as an owned value.
|
|
38
38
|
pub func match = (string pattern, string input, mv out string) {
|
|
39
|
-
var match_result = ""
|
|
40
39
|
var int pat_len = pattern.length
|
|
41
40
|
var int len = input.length
|
|
42
41
|
var string charset = first_byte_set(pattern, pat_len)
|
|
@@ -52,14 +51,14 @@ pub struct Regex {
|
|
|
52
51
|
var caps = List<int>()
|
|
53
52
|
var int end = match_here(pattern, 0, pat_len, input, i, len, ref caps)
|
|
54
53
|
if end >= 0 {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
54
|
+
// One owned substring copy. The old accumulator
|
|
55
|
+
// (`match_result = match_result + "\{input.at(j)}"` per
|
|
56
|
+
// matched byte) paid an interpolation malloc PLUS a
|
|
57
|
+
// whole-accumulator re-copy per byte — O(n²) in the span.
|
|
58
|
+
return input.substring(i, end)
|
|
60
59
|
}
|
|
61
60
|
}
|
|
62
|
-
return
|
|
61
|
+
return ""
|
|
63
62
|
}
|
|
64
63
|
|
|
65
64
|
// Try to match pattern anywhere in input. On a match, append the whole
|
|
@@ -70,7 +69,7 @@ pub struct Regex {
|
|
|
70
69
|
pub func captures = (
|
|
71
70
|
string pattern,
|
|
72
71
|
string input,
|
|
73
|
-
ref List<string> dst,
|
|
72
|
+
ref List<string> dst { items },
|
|
74
73
|
out bool
|
|
75
74
|
) { var int pat_len = pattern.length
|
|
76
75
|
var int len = input.length
|
|
@@ -118,7 +117,7 @@ pub struct Regex {
|
|
|
118
117
|
// first, so a reused RegexMatch carries nothing over. Scans every
|
|
119
118
|
// position: the first-byte prefilter would skip nullable patterns'
|
|
120
119
|
// empty matches at skipped positions, reporting a wrong .start.
|
|
121
|
-
pub func find = (string pattern, string input, ref RegexMatch dst) {
|
|
120
|
+
pub func find = (string pattern, string input, ref RegexMatch dst { text }) {
|
|
122
121
|
dst.found = false
|
|
123
122
|
dst.start = 0
|
|
124
123
|
dst.end = 0
|
|
@@ -173,25 +172,27 @@ pub struct Regex {
|
|
|
173
172
|
}
|
|
174
173
|
|
|
175
174
|
// Replace all non-overlapping occurrences of pattern in input with replacement.
|
|
175
|
+
// Literal bytes between matches are copied in bulk runs (one
|
|
176
|
+
// `append_string_view` per run) — the old loop appended per byte.
|
|
176
177
|
pub func replace_all = (
|
|
177
178
|
string pattern,
|
|
178
179
|
string input,
|
|
179
180
|
string replacement,
|
|
180
181
|
out string
|
|
181
|
-
) {
|
|
182
|
+
) {
|
|
183
|
+
var sb = StringBuilder()
|
|
182
184
|
var int pat_len = pattern.length
|
|
183
185
|
var int len = input.length
|
|
184
186
|
var string charset = first_byte_set(pattern, pat_len)
|
|
185
187
|
var int clen = charset.length
|
|
186
188
|
var pos = 0
|
|
189
|
+
var run_start = 0
|
|
187
190
|
while pos < len {
|
|
188
191
|
if clen > 0 {
|
|
189
|
-
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
pos += 1
|
|
194
|
-
}
|
|
192
|
+
// Prefilter: jump to the next candidate byte. The skipped
|
|
193
|
+
// run stays pending — it is copied in bulk at the next
|
|
194
|
+
// match, or by the tail copy below.
|
|
195
|
+
pos = find_next_byte(input, pos, len, charset)
|
|
195
196
|
if pos >= len {
|
|
196
197
|
break
|
|
197
198
|
}
|
|
@@ -199,18 +200,22 @@ pub struct Regex {
|
|
|
199
200
|
var caps = List<int>()
|
|
200
201
|
var int end = match_here(pattern, 0, pat_len, input, pos, len, ref caps)
|
|
201
202
|
if end >= 0 {
|
|
203
|
+
sb.append_string_view(input.slice(run_start, pos))
|
|
202
204
|
sb.append_string(replacement)
|
|
203
205
|
if end == pos {
|
|
204
|
-
|
|
206
|
+
// Empty match: the byte at pos is not consumed — it
|
|
207
|
+
// starts the next literal run.
|
|
208
|
+
run_start = pos
|
|
205
209
|
pos += 1
|
|
206
210
|
} else {
|
|
207
211
|
pos = end
|
|
212
|
+
run_start = pos
|
|
208
213
|
}
|
|
209
214
|
} else {
|
|
210
|
-
sb.append_char(input.at(pos))
|
|
211
215
|
pos += 1
|
|
212
216
|
}
|
|
213
217
|
}
|
|
218
|
+
sb.append_string_view(input.slice(run_start, len))
|
|
214
219
|
return sb.to_string()
|
|
215
220
|
}
|
|
216
221
|
|
|
@@ -227,13 +232,13 @@ pub struct Regex {
|
|
|
227
232
|
}
|
|
228
233
|
|
|
229
234
|
// find, ignoring ASCII case.
|
|
230
|
-
pub func find_ci = (string pattern, string input, ref RegexMatch dst) {
|
|
235
|
+
pub func find_ci = (string pattern, string input, ref RegexMatch dst { text }) {
|
|
231
236
|
var string folded = fold_pattern(pattern)
|
|
232
237
|
Regex.find(folded, input, ref dst)
|
|
233
238
|
}
|
|
234
239
|
|
|
235
240
|
// captures, ignoring ASCII case.
|
|
236
|
-
pub func captures_ci = (string pattern, string input, ref List<string> dst, out bool) {
|
|
241
|
+
pub func captures_ci = (string pattern, string input, ref List<string> dst { items }, out bool) {
|
|
237
242
|
var string folded = fold_pattern(pattern)
|
|
238
243
|
return Regex.captures(folded, input, ref dst)
|
|
239
244
|
}
|
|
@@ -303,7 +308,7 @@ func fold_pattern = (string pattern, mv out string) {
|
|
|
303
308
|
|
|
304
309
|
// fold a character class [cs..ce): copy its contents, adding the case
|
|
305
310
|
// counterpart of every letter member and mirroring letter ranges.
|
|
306
|
-
func fold_class = (string pattern, int cs, int ce, ref StringBuilder dst) {
|
|
311
|
+
func fold_class = (string pattern, int cs, int ce, ref StringBuilder dst {}) {
|
|
307
312
|
dst.append_char('[')
|
|
308
313
|
var int i = cs + 1
|
|
309
314
|
if i < ce - 1 {
|
|
@@ -430,7 +435,7 @@ func collect_first_bytes = (
|
|
|
430
435
|
string pattern,
|
|
431
436
|
int pat_pos,
|
|
432
437
|
int pat_end,
|
|
433
|
-
ref StringBuilder sb,
|
|
438
|
+
ref StringBuilder sb {},
|
|
434
439
|
out int
|
|
435
440
|
) {
|
|
436
441
|
var depth = 0
|
|
@@ -477,7 +482,7 @@ func collect_first_bytes = (
|
|
|
477
482
|
// atom — literal, escaped, class, or group — quantified with '?'/'*' is
|
|
478
483
|
// nullable: what FOLLOWS it can also be first, so the scan continues past it.
|
|
479
484
|
// A wildcard '.' or an empty alternative makes the set indeterminable.
|
|
480
|
-
func alt_first_bytes = (string pattern, int start, int end, ref StringBuilder sb, out int) {
|
|
485
|
+
func alt_first_bytes = (string pattern, int start, int end, ref StringBuilder sb {}, out int) {
|
|
481
486
|
var int i = start
|
|
482
487
|
while i < end {
|
|
483
488
|
var char c = pattern.at(i)
|
|
@@ -717,7 +722,7 @@ func add_class_chars = (
|
|
|
717
722
|
string pattern,
|
|
718
723
|
int start,
|
|
719
724
|
int end,
|
|
720
|
-
ref StringBuilder sb
|
|
725
|
+
ref StringBuilder sb {}
|
|
721
726
|
) { var int cs = start
|
|
722
727
|
if cs < end {
|
|
723
728
|
if pattern.at(cs) == '^' {
|
|
@@ -772,7 +777,7 @@ func match_here = (
|
|
|
772
777
|
string input,
|
|
773
778
|
int in_pos,
|
|
774
779
|
int in_len,
|
|
775
|
-
ref List<int> caps,
|
|
780
|
+
ref List<int> caps { items },
|
|
776
781
|
out int: out <= in_len
|
|
777
782
|
) {
|
|
778
783
|
// Handle top-level alternation: a '|' at bracket/paren depth 0 splits the
|
|
@@ -1031,7 +1036,7 @@ func match_group_then_rest = (
|
|
|
1031
1036
|
string input,
|
|
1032
1037
|
int in_pos,
|
|
1033
1038
|
int in_len,
|
|
1034
|
-
ref List<int> caps,
|
|
1039
|
+
ref List<int> caps { items },
|
|
1035
1040
|
out int
|
|
1036
1041
|
) {
|
|
1037
1042
|
var int g = group_index_at(pattern, atom_start)
|
|
@@ -1122,7 +1127,7 @@ func match_single_atom = (
|
|
|
1122
1127
|
string input,
|
|
1123
1128
|
int in_pos,
|
|
1124
1129
|
int in_len,
|
|
1125
|
-
ref List<int> caps,
|
|
1130
|
+
ref List<int> caps { items },
|
|
1126
1131
|
out int
|
|
1127
1132
|
) {
|
|
1128
1133
|
var char pc = pattern.at(atom_start)
|
|
@@ -1304,7 +1309,7 @@ func match_counted = (
|
|
|
1304
1309
|
int lazy,
|
|
1305
1310
|
int count,
|
|
1306
1311
|
int in_len,
|
|
1307
|
-
ref List<int> caps,
|
|
1312
|
+
ref List<int> caps { items },
|
|
1308
1313
|
out int
|
|
1309
1314
|
) {
|
|
1310
1315
|
var can_stop = count >= min_count
|
|
@@ -1402,7 +1407,7 @@ func match_star = (
|
|
|
1402
1407
|
int min_count,
|
|
1403
1408
|
int lazy,
|
|
1404
1409
|
int in_len,
|
|
1405
|
-
ref List<int> caps,
|
|
1410
|
+
ref List<int> caps { items },
|
|
1406
1411
|
out int
|
|
1407
1412
|
) {
|
|
1408
1413
|
var char pc = pattern.at(atom_start)
|
|
@@ -1485,7 +1490,7 @@ func match_star_group = (
|
|
|
1485
1490
|
int min_count,
|
|
1486
1491
|
int lazy,
|
|
1487
1492
|
int in_len,
|
|
1488
|
-
ref List<int> caps,
|
|
1493
|
+
ref List<int> caps { items },
|
|
1489
1494
|
out int
|
|
1490
1495
|
) {
|
|
1491
1496
|
if lazy > 0 {
|
|
@@ -1554,7 +1559,7 @@ func match_group_iteration = (
|
|
|
1554
1559
|
int min_count,
|
|
1555
1560
|
int lazy,
|
|
1556
1561
|
int in_len,
|
|
1557
|
-
ref List<int> caps,
|
|
1562
|
+
ref List<int> caps { items },
|
|
1558
1563
|
out int
|
|
1559
1564
|
) {
|
|
1560
1565
|
var int g = group_index_at(pattern, atom_start)
|
|
@@ -1621,7 +1626,7 @@ func match_optional = (
|
|
|
1621
1626
|
int in_pos,
|
|
1622
1627
|
int lazy,
|
|
1623
1628
|
int in_len,
|
|
1624
|
-
ref List<int> caps,
|
|
1629
|
+
ref List<int> caps { items },
|
|
1625
1630
|
out int
|
|
1626
1631
|
) {
|
|
1627
1632
|
if lazy > 0 {
|
|
@@ -1894,7 +1899,7 @@ func group_index_at = (string pattern, int open_pos, out int) {
|
|
|
1894
1899
|
// group_pos. A later span for the same group supersedes the earlier one, so
|
|
1895
1900
|
// a repeated group reports its last iteration; a failed group records
|
|
1896
1901
|
// (-1, -1) so stale spans from an abandoned attempt never surface.
|
|
1897
|
-
func record_capture = (ref List<int> caps, int group, int start, int end) {
|
|
1902
|
+
func record_capture = (ref List<int> caps { items }, int group, int start, int end) {
|
|
1898
1903
|
if group <= 0 {
|
|
1899
1904
|
return
|
|
1900
1905
|
}
|
|
@@ -1953,7 +1958,7 @@ func match_backref = (
|
|
|
1953
1958
|
int group,
|
|
1954
1959
|
int in_pos,
|
|
1955
1960
|
int in_len,
|
|
1956
|
-
ref List<int> caps,
|
|
1961
|
+
ref List<int> caps {},
|
|
1957
1962
|
out int
|
|
1958
1963
|
) {
|
|
1959
1964
|
var int at = last_capture_index(caps, group)
|
|
@@ -1989,7 +1994,7 @@ func match_star_backref = (
|
|
|
1989
1994
|
int min_count,
|
|
1990
1995
|
int lazy,
|
|
1991
1996
|
int in_len,
|
|
1992
|
-
ref List<int> caps,
|
|
1997
|
+
ref List<int> caps { items },
|
|
1993
1998
|
out int
|
|
1994
1999
|
) {
|
|
1995
2000
|
var int group = backref_group(pattern.at(atom_start + 1))
|
|
@@ -2111,7 +2116,7 @@ func matches_shorthand = (char c, char b, out bool) {
|
|
|
2111
2116
|
// first-byte charset. Complement shorthands (S/D/W) match "anything else" —
|
|
2112
2117
|
// not expressible as an inclusion set — so they return -1, which disables
|
|
2113
2118
|
// the caller's prefilter.
|
|
2114
|
-
func shorthand_members = (char c, ref StringBuilder sb, out int) {
|
|
2119
|
+
func shorthand_members = (char c, ref StringBuilder sb {}, out int) {
|
|
2115
2120
|
var int kind = c as int
|
|
2116
2121
|
if kind == 83 || kind == 68 || kind == 87 {
|
|
2117
2122
|
return -1
|
package/core/System/Text/Utf8.nm
CHANGED
|
@@ -126,11 +126,11 @@ pub struct Utf8 {
|
|
|
126
126
|
sb.append_char(((0x80 | (code & 0x3F)) as char))
|
|
127
127
|
}
|
|
128
128
|
} else {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
129
|
+
sb.append_char(((0xF0 | (code >> 18)) as char))
|
|
130
|
+
sb.append_char(((0x80 | ((code >> 12) & 0x3F)) as char))
|
|
131
|
+
sb.append_char(((0x80 | ((code >> 6) & 0x3F)) as char))
|
|
132
|
+
sb.append_char(((0x80 | (code & 0x3F)) as char))
|
|
133
|
+
}
|
|
134
134
|
return sb.to_string()
|
|
135
135
|
}
|
|
136
136
|
|
package/core/System/Thread.nm
CHANGED
|
@@ -82,9 +82,13 @@ pub mv struct Thread<T> : Spawnable<T> {
|
|
|
82
82
|
// fields, so #destroy becomes a no-op afterwards. The future's refs
|
|
83
83
|
// account for the trampoline, the returned handle, and the nursery (when
|
|
84
84
|
// captured).
|
|
85
|
-
pub func start = (ref self, mv out Task<T>) {
|
|
85
|
+
pub func start = (ref self {}, mv out Task<T>) {
|
|
86
86
|
```
|
|
87
87
|
#arch: c
|
|
88
|
+
// Single-launch contract: a consumed construction (started) owns
|
|
89
|
+
// nothing — the handles below are zeroed, and writing the ref count
|
|
90
|
+
// through the null future would fault. Abort with the reason.
|
|
91
|
+
if (self->started) __nomen_spawn_double_start_abort();
|
|
88
92
|
struct nomen_future *_f = (struct nomen_future *)self->future;
|
|
89
93
|
_f->refs = self->nursery_futures ? 3 : 2;
|
|
90
94
|
__nomen_pool_submit((struct nomen_closure *)self->task);
|
|
@@ -94,7 +98,12 @@ pub mv struct Thread<T> : Spawnable<T> {
|
|
|
94
98
|
```
|
|
95
99
|
```
|
|
96
100
|
#arch: aarch64
|
|
97
|
-
// x19 = self. f = self->future (#32); nursery_futures
|
|
101
|
+
// x19 = self. started at #40; f = self->future (#32); nursery_futures
|
|
102
|
+
// at #48. Single-launch contract first: a consumed construction has
|
|
103
|
+
// zeroed handles, and the ref-store below would write through null.
|
|
104
|
+
ldrb w9, [x19, #40]
|
|
105
|
+
cbnz w9, .Lnomen_start_double
|
|
106
|
+
// f = self->future (#32); nursery_futures at #48.
|
|
98
107
|
ldr x0, [x19, #32]
|
|
99
108
|
ldr x1, [x19, #48]
|
|
100
109
|
mov w2, #2
|
|
@@ -113,6 +122,10 @@ pub mv struct Thread<T> : Spawnable<T> {
|
|
|
113
122
|
ldr x3, [x19, #32]
|
|
114
123
|
bl ___nomen_nursery_track
|
|
115
124
|
.Lnomen_start_notrack:
|
|
125
|
+
b .Lnomen_start_done
|
|
126
|
+
.Lnomen_start_double:
|
|
127
|
+
bl ___nomen_spawn_double_start_abort
|
|
128
|
+
.Lnomen_start_done:
|
|
116
129
|
```
|
|
117
130
|
// The handle is a fresh heap Task built through the internal #init;
|
|
118
131
|
// the caller owns it (a discarded statement form is reclaimed at
|
|
@@ -136,9 +149,12 @@ pub mv struct Thread<T> : Spawnable<T> {
|
|
|
136
149
|
// mid-execution BY DESIGN. The unused future (a Thread always carries
|
|
137
150
|
// the full machinery) is released — first detaching the closure from it,
|
|
138
151
|
// so the daemon runner owns the closure now.
|
|
139
|
-
pub func detach = (ref self) {
|
|
152
|
+
pub func detach = (ref self {}) {
|
|
140
153
|
```
|
|
141
154
|
#arch: c
|
|
155
|
+
// Single-launch contract (see .start): a consumed construction has
|
|
156
|
+
// zeroed handles, and detaching the null closure would crash a worker.
|
|
157
|
+
if (self->started) __nomen_spawn_double_start_abort();
|
|
142
158
|
struct nomen_future *_uf = (struct nomen_future *)self->future;
|
|
143
159
|
if (_uf) {
|
|
144
160
|
_uf->owner_args = NULL;
|
|
@@ -148,6 +164,9 @@ pub mv struct Thread<T> : Spawnable<T> {
|
|
|
148
164
|
```
|
|
149
165
|
```
|
|
150
166
|
#arch: aarch64
|
|
167
|
+
// Single-launch contract first (see .start): started at #40.
|
|
168
|
+
ldrb w9, [x19, #40]
|
|
169
|
+
cbnz w9, .Lnomen_detach_double
|
|
151
170
|
// if (self->future) { owner_args = NULL; release it }
|
|
152
171
|
ldr x0, [x19, #32]
|
|
153
172
|
cbz x0, .Lnomen_detach_nofut
|
|
@@ -157,6 +176,10 @@ pub mv struct Thread<T> : Spawnable<T> {
|
|
|
157
176
|
// __nomen_task_detach(self->task)
|
|
158
177
|
ldr x0, [x19, #8]
|
|
159
178
|
bl ___nomen_task_detach
|
|
179
|
+
b .Lnomen_detach_done
|
|
180
|
+
.Lnomen_detach_double:
|
|
181
|
+
bl ___nomen_spawn_double_start_abort
|
|
182
|
+
.Lnomen_detach_done:
|
|
160
183
|
```
|
|
161
184
|
self.task = 0
|
|
162
185
|
self.future = 0
|