nomen-lang 0.7.1 → 0.9.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 +8 -1
- package/core/System/Buffer.nm +397 -8
- package/core/System/ClassBuffer.nm +156 -0
- package/core/System/Controls/Alert.nm +326 -0
- package/core/System/Controls/App.nm +147 -0
- package/core/System/Controls/Button.nm +119 -1
- package/core/System/Controls/CheckBox.nm +25 -1
- package/core/System/Controls/ColorPicker.nm +307 -0
- package/core/System/Controls/ComboBox.nm +430 -0
- package/core/System/Controls/Container.nm +843 -9
- package/core/System/Controls/Control.nm +443 -0
- package/core/System/Controls/DatePicker.nm +288 -0
- package/core/System/Controls/Form.nm +70 -0
- package/core/System/Controls/GroupBox.nm +267 -0
- package/core/System/Controls/ImageView.nm +311 -0
- package/core/System/Controls/LevelIndicator.nm +305 -0
- package/core/System/Controls/ListView.nm +633 -0
- package/core/System/Controls/Menu.nm +428 -0
- package/core/System/Controls/NumberInput.nm +56 -0
- package/core/System/Controls/OtpInput.nm +50 -0
- package/core/System/Controls/OutlineView.nm +598 -0
- package/core/System/Controls/Pagination.nm +142 -0
- package/core/System/Controls/Popover.nm +296 -0
- package/core/System/Controls/Progress.nm +348 -0
- package/core/System/Controls/Radio.nm +292 -0
- package/core/System/Controls/Scrollable.nm +584 -0
- package/core/System/Controls/SearchField.nm +326 -0
- package/core/System/Controls/SecureField.nm +344 -0
- package/core/System/Controls/Segmented.nm +380 -0
- package/core/System/Controls/Select.nm +340 -0
- package/core/System/Controls/Separator.nm +198 -0
- package/core/System/Controls/Sheet.nm +329 -0
- package/core/System/Controls/Slider.nm +315 -0
- package/core/System/Controls/Spinner.nm +287 -0
- package/core/System/Controls/SplitView.nm +509 -0
- package/core/System/Controls/Stepper.nm +349 -0
- package/core/System/Controls/Switch.nm +259 -0
- package/core/System/Controls/Table.nm +833 -0
- package/core/System/Controls/Tabs.nm +500 -0
- package/core/System/Controls/Text.nm +108 -11
- package/core/System/Controls/TextBox.nm +113 -2
- package/core/System/Controls/TextView.nm +329 -0
- package/core/System/Controls/Toggle.nm +346 -0
- package/core/System/Controls/Toolbar.nm +501 -0
- package/core/System/Controls/Tooltip.nm +76 -0
- package/core/System/Controls/Window.nm +1548 -91
- package/core/System/LinkedList.nm +8 -1
- package/core/System/List.nm +97 -7
- package/core/System/Map.nm +191 -18
- package/core/System/Mutex.nm +4 -4
- package/core/System/Random.nm +3 -4
- package/core/System/Set.nm +101 -13
- package/core/System/StableList.nm +371 -0
- package/core/System/Stream/Directory.nm +2 -2
- package/core/System/Stream/File.nm +2 -2
- package/core/System/Stream/Http.nm +2 -2
- package/core/System/String.nm +10 -5
- package/core/System/StringBuilder.nm +213 -2
- package/core/System/Text/CharIndex.nm +9 -9
- package/core/System/Text/Json.nm +60 -46
- package/core/System/Text/JsonTree.nm +120 -92
- package/core/System/Text/Regex.nm +23 -18
- package/core/System/Text/Utf8.nm +5 -5
- 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 +38332 -30604
- 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
|
|
37
|
+
var nodes = StableList<JsonNode>()
|
|
28
38
|
|
|
39
|
+
// Append a fresh node and return its index.
|
|
29
40
|
func alloc_node = (ref self, out int) {
|
|
30
|
-
|
|
31
|
-
self.nodes.
|
|
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
|
-
}
|
|
54
|
-
|
|
55
|
-
// Free a node's strdup'd text pointer (set_text writes a heap pointer via
|
|
56
|
-
// raw strdup; this mirrors it). idx is x1, following set_text's layout.
|
|
57
|
-
func free_text = (ref self, int idx) {
|
|
58
|
-
unsafe {
|
|
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
|
-
}
|
|
41
|
+
self.nodes.push(JsonNode())
|
|
42
|
+
return self.nodes.length - 1
|
|
68
43
|
}
|
|
69
44
|
|
|
70
|
-
//
|
|
71
|
-
//
|
|
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.
|
|
72
48
|
func reset = (ref self) {
|
|
73
|
-
|
|
74
|
-
while i < self.count; i += 1 {
|
|
75
|
-
self.free_text(i)
|
|
76
|
-
}
|
|
77
|
-
self.count = 0
|
|
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
86
|
func set_kind = (ref self, int idx, int val) {
|
|
109
|
-
|
|
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
94
|
func set_child = (ref self, int idx, int val) {
|
|
115
|
-
|
|
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
102
|
func set_next = (ref self, int idx, int val) {
|
|
121
|
-
|
|
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
110
|
func set_val = (ref self, int idx, int val) {
|
|
127
|
-
|
|
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
|
|
@@ -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
|
|
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/float.nm
CHANGED
|
@@ -5,37 +5,51 @@ pub struct float: Stringable {
|
|
|
5
5
|
pub func to_string = (self, mv out string) {
|
|
6
6
|
```
|
|
7
7
|
#arch: c
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
// Single snprintf into a stack scratch (360 bytes covers "%f" of the
|
|
9
|
+
// largest double: 309 integer digits + '.' + 6 decimals + sign), then
|
|
10
|
+
// one malloc + memcpy — no sizing pass, no second format.
|
|
11
|
+
char tmp[360];
|
|
12
|
+
int length = snprintf(tmp, sizeof(tmp), "%f", self);
|
|
13
|
+
char* str = (char*)malloc((size_t)length + 1);
|
|
14
|
+
memcpy(str, tmp, (size_t)length);
|
|
15
|
+
str[length] = 0;
|
|
12
16
|
return (nomen_string){ str, length };
|
|
13
17
|
```
|
|
14
18
|
```
|
|
15
19
|
#arch: aarch64
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
mov
|
|
23
|
-
mov x1, xzr
|
|
20
|
+
// x19 = self (the double). Render into a 360-byte scratch at
|
|
21
|
+
// [sp+16]; [sp+0] is snprintf's variadic stack slot (Darwin passes
|
|
22
|
+
// variadic args on the stack), reused after the call for the
|
|
23
|
+
// length; [sp+8] holds buf.
|
|
24
|
+
sub sp, sp, #400
|
|
25
|
+
add x0, sp, #16
|
|
26
|
+
mov x1, #360
|
|
24
27
|
adr x2, .Lfmt_float_f
|
|
25
28
|
fmov d0, x19
|
|
29
|
+
str d0, [sp, #0] // variadic double slot
|
|
26
30
|
bl _snprintf
|
|
31
|
+
str x0, [sp, #0] // length
|
|
27
32
|
add x0, x0, #1
|
|
28
|
-
str x0, [sp, #64]
|
|
29
33
|
bl _malloc
|
|
30
|
-
str x0, [sp, #
|
|
31
|
-
ldr
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
add
|
|
34
|
+
str x0, [sp, #8] // buf
|
|
35
|
+
ldr x1, [sp, #0]
|
|
36
|
+
add x2, sp, #16 // src
|
|
37
|
+
mov x3, x1
|
|
38
|
+
.Lft_cl:
|
|
39
|
+
cbz x3, .Lft_done
|
|
40
|
+
ldrb w4, [x2]
|
|
41
|
+
strb w4, [x0]
|
|
42
|
+
add x2, x2, #1
|
|
43
|
+
add x0, x0, #1
|
|
44
|
+
sub x3, x3, #1
|
|
45
|
+
b .Lft_cl
|
|
46
|
+
.Lft_done:
|
|
47
|
+
ldr x0, [sp, #8]
|
|
48
|
+
add x2, x0, x1
|
|
49
|
+
strb wzr, [x2] // buf[length] = 0
|
|
50
|
+
ldr x0, [sp, #8]
|
|
51
|
+
ldr x1, [sp, #0]
|
|
52
|
+
add sp, sp, #400
|
|
39
53
|
b .Lend_float_to_string
|
|
40
54
|
.Lfmt_float_f: .asciz "%f"
|
|
41
55
|
.p2align 2
|
package/core/System/int.nm
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
* A signed integer — the default integer type
|
|
3
3
|
**/
|
|
4
4
|
pub struct int: Stringable, Hashable, Equatable {
|
|
5
|
+
|
|
6
|
+
// The divisor contract (SPEC, Division and Modulo by Zero): scalar
|
|
7
|
+
// arithmetic lowers natively, so these declarations are the contract
|
|
8
|
+
// signatures the checker enforces at every `/` and `%` site.
|
|
9
|
+
func #op_div = (self, int divisor: divisor != 0, out int) {
|
|
10
|
+
return self / divisor
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
func #op_mod = (self, int divisor: divisor != 0, out int) {
|
|
14
|
+
return self % divisor
|
|
15
|
+
}
|
|
5
16
|
pub func hash = (self, out uint) {
|
|
6
17
|
return self as uint
|
|
7
18
|
}
|
|
@@ -9,39 +20,80 @@ pub struct int: Stringable, Hashable, Equatable {
|
|
|
9
20
|
pub func to_string = (self, mv out string) {
|
|
10
21
|
```
|
|
11
22
|
#arch: c
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
snprintf
|
|
15
|
-
|
|
16
|
-
|
|
23
|
+
// Single pass: render the digits into a stack scratch (24 bytes
|
|
24
|
+
// covers INT64_MIN's sign + 19 digits + slack), then one malloc and
|
|
25
|
+
// one memcpy. The old body paid two snprintf passes over the value.
|
|
26
|
+
long v = self;
|
|
27
|
+
char tmp[24];
|
|
28
|
+
int pos = 24;
|
|
29
|
+
// Magnitude via the two's-complement negate (correct for INT64_MIN,
|
|
30
|
+
// whose positive counterpart is not representable).
|
|
31
|
+
unsigned long long u = v < 0 ? (unsigned long long)(-(v + 1)) + 1ULL : (unsigned long long)v;
|
|
32
|
+
do {
|
|
33
|
+
tmp[--pos] = (char)('0' + (int)(u % 10));
|
|
34
|
+
u /= 10;
|
|
35
|
+
} while (u != 0);
|
|
36
|
+
if (v < 0) tmp[--pos] = '-';
|
|
37
|
+
long len = 24 - pos;
|
|
38
|
+
char* str = (char*)malloc((size_t)len + 1);
|
|
39
|
+
memcpy(str, tmp + pos, (size_t)len);
|
|
40
|
+
str[len] = 0;
|
|
41
|
+
return (nomen_string){ str, len };
|
|
17
42
|
```
|
|
18
43
|
```
|
|
19
44
|
#arch: aarch64
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
mov
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
add
|
|
31
|
-
|
|
45
|
+
// x19 = self (the value). Render digits into a 24-byte scratch, then
|
|
46
|
+
// malloc(len + 1) + copy. [sp]=len, [sp+8]=buf, [sp+16]=pos across
|
|
47
|
+
// the malloc call.
|
|
48
|
+
sub sp, sp, #48
|
|
49
|
+
mov x9, x19
|
|
50
|
+
mov x10, x9
|
|
51
|
+
cmp x9, #0
|
|
52
|
+
b.ge .Lit_pos
|
|
53
|
+
neg x10, x10 // magnitude (wraps correctly for INT64_MIN)
|
|
54
|
+
.Lit_pos:
|
|
55
|
+
add x11, sp, #48
|
|
56
|
+
mov x12, x11
|
|
57
|
+
.Lit_digit:
|
|
58
|
+
mov x13, #10
|
|
59
|
+
udiv x14, x10, x13
|
|
60
|
+
msub x15, x14, x13, x10
|
|
61
|
+
add x15, x15, #48
|
|
62
|
+
sub x12, x12, #1
|
|
63
|
+
strb w15, [x12]
|
|
64
|
+
mov x10, x14
|
|
65
|
+
cbnz x10, .Lit_digit
|
|
66
|
+
cmp x9, #0
|
|
67
|
+
b.ge .Lit_store
|
|
68
|
+
sub x12, x12, #1
|
|
69
|
+
mov w15, #45 // '-'
|
|
70
|
+
strb w15, [x12]
|
|
71
|
+
.Lit_store:
|
|
72
|
+
sub x1, x11, x12 // len
|
|
73
|
+
str x12, [sp, #16] // pos
|
|
74
|
+
str x1, [sp, #0] // len
|
|
75
|
+
add x0, x1, #1
|
|
32
76
|
bl _malloc
|
|
33
|
-
str x0, [sp, #
|
|
34
|
-
ldr
|
|
35
|
-
ldr
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
add
|
|
77
|
+
str x0, [sp, #8] // buf
|
|
78
|
+
ldr x1, [sp, #0]
|
|
79
|
+
ldr x12, [sp, #16]
|
|
80
|
+
mov x3, x1
|
|
81
|
+
.Lit_cl:
|
|
82
|
+
cbz x3, .Lit_done
|
|
83
|
+
ldrb w4, [x12]
|
|
84
|
+
strb w4, [x0]
|
|
85
|
+
add x12, x12, #1
|
|
86
|
+
add x0, x0, #1
|
|
87
|
+
sub x3, x3, #1
|
|
88
|
+
b .Lit_cl
|
|
89
|
+
.Lit_done:
|
|
90
|
+
ldr x0, [sp, #8]
|
|
91
|
+
add x2, x0, x1
|
|
92
|
+
strb wzr, [x2] // buf[len] = 0
|
|
93
|
+
ldr x0, [sp, #8]
|
|
94
|
+
ldr x1, [sp, #0]
|
|
95
|
+
add sp, sp, #48
|
|
42
96
|
b .Lend_int_to_string
|
|
43
|
-
.Lfmt_int_ld: .asciz "%ld"
|
|
44
|
-
.p2align 2
|
|
45
97
|
.Lend_int_to_string:
|
|
46
98
|
```
|
|
47
99
|
}
|
package/core/System/int16.nm
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
* A 16-bit signed integer
|
|
3
3
|
**/
|
|
4
4
|
pub struct int16: Stringable, Hashable, Equatable {
|
|
5
|
+
|
|
6
|
+
// The divisor contract (SPEC, Division and Modulo by Zero): scalar
|
|
7
|
+
// arithmetic lowers natively, so these declarations are the contract
|
|
8
|
+
// signatures the checker enforces at every `/` and `%` site.
|
|
9
|
+
func #op_div = (self, int16 divisor: divisor != 0, out int16) {
|
|
10
|
+
return self / divisor
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
func #op_mod = (self, int16 divisor: divisor != 0, out int16) {
|
|
14
|
+
return self % divisor
|
|
15
|
+
}
|
|
5
16
|
pub func hash = (self, out uint) {
|
|
6
17
|
return self as uint
|
|
7
18
|
}
|
package/core/System/int32.nm
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
* A 32-bit signed integer
|
|
3
3
|
**/
|
|
4
4
|
pub struct int32: Stringable, Hashable, Equatable {
|
|
5
|
+
|
|
6
|
+
// The divisor contract (SPEC, Division and Modulo by Zero): scalar
|
|
7
|
+
// arithmetic lowers natively, so these declarations are the contract
|
|
8
|
+
// signatures the checker enforces at every `/` and `%` site.
|
|
9
|
+
func #op_div = (self, int32 divisor: divisor != 0, out int32) {
|
|
10
|
+
return self / divisor
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
func #op_mod = (self, int32 divisor: divisor != 0, out int32) {
|
|
14
|
+
return self % divisor
|
|
15
|
+
}
|
|
5
16
|
pub func hash = (self, out uint) {
|
|
6
17
|
return self as uint
|
|
7
18
|
}
|
package/core/System/int64.nm
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
* A 64-bit signed integer
|
|
3
3
|
**/
|
|
4
4
|
pub struct int64: Stringable, Hashable, Equatable {
|
|
5
|
+
|
|
6
|
+
// The divisor contract (SPEC, Division and Modulo by Zero): scalar
|
|
7
|
+
// arithmetic lowers natively, so these declarations are the contract
|
|
8
|
+
// signatures the checker enforces at every `/` and `%` site.
|
|
9
|
+
func #op_div = (self, int64 divisor: divisor != 0, out int64) {
|
|
10
|
+
return self / divisor
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
func #op_mod = (self, int64 divisor: divisor != 0, out int64) {
|
|
14
|
+
return self % divisor
|
|
15
|
+
}
|
|
5
16
|
pub func hash = (self, out uint) {
|
|
6
17
|
return self as uint
|
|
7
18
|
}
|