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
package/core/System/Task.nm
CHANGED
|
@@ -23,8 +23,6 @@
|
|
|
23
23
|
* Conforms to `Awaitable`: `.wait()` may park the current execution context.
|
|
24
24
|
**/
|
|
25
25
|
pub mv struct Task<T> : Awaitable {
|
|
26
|
-
pub var uint64 handle = 0
|
|
27
|
-
pub var done = false
|
|
28
26
|
pub var uint64 result_slot = 0
|
|
29
27
|
pub var uint64 cancel_flag = 0
|
|
30
28
|
pub var uint64 future = 0
|
|
@@ -46,8 +44,6 @@ pub mv struct Task<T> : Awaitable {
|
|
|
46
44
|
// names. Struct receivers travel by address regardless (the raw body
|
|
47
45
|
// reads `self->future`), so the ABI is unchanged; the bare spelling is
|
|
48
46
|
// what lets the read-only `Awaitable` dispatch form typecheck.
|
|
49
|
-
// The legacy `pthread_join` fallback is gone — `handle` has been
|
|
50
|
-
// permanently zero since the pool migration.
|
|
51
47
|
pub func wait = (self) {
|
|
52
48
|
```
|
|
53
49
|
#arch: c
|
|
@@ -58,7 +54,7 @@ pub mv struct Task<T> : Awaitable {
|
|
|
58
54
|
```
|
|
59
55
|
#arch: aarch64
|
|
60
56
|
// if (self->future) __nomen_future_wait(self->future)
|
|
61
|
-
ldr x0, [x19, #
|
|
57
|
+
ldr x0, [x19, #24]
|
|
62
58
|
cbz x0, .Lwait_done
|
|
63
59
|
bl ___nomen_future_wait
|
|
64
60
|
.Lwait_done:
|
|
@@ -71,18 +67,22 @@ pub mv struct Task<T> : Awaitable {
|
|
|
71
67
|
// trampoline writes the function's return value there before exiting.
|
|
72
68
|
// The result is MOVED OUT: the slot is zeroed as the value leaves, and
|
|
73
69
|
// `mv out T` marks the caller as the owner. Call it once; a second call
|
|
74
|
-
// returns the zero value.
|
|
75
|
-
//
|
|
76
|
-
|
|
70
|
+
// returns the zero value. An empty handle (never launched, machinery
|
|
71
|
+
// already destroyed) owns no slot — it also returns the zero value
|
|
72
|
+
// rather than reading through a null pointer. For tasks with no return
|
|
73
|
+
// value (void functions), returns the zero value.
|
|
74
|
+
pub func result = (ref self {}, mv out T) {
|
|
77
75
|
```
|
|
78
76
|
#arch: c
|
|
79
77
|
if (self->future) {
|
|
80
78
|
__nomen_future_wait((struct nomen_future *)self->future);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
79
|
+
}
|
|
80
|
+
// An empty or already-consumed handle owns no result cell — return
|
|
81
|
+
// the zero value instead of reading through a null slot.
|
|
82
|
+
if (!self->result_slot) {
|
|
83
|
+
T _z;
|
|
84
|
+
memset(&_z, 0, sizeof(T));
|
|
85
|
+
return _z;
|
|
86
86
|
}
|
|
87
87
|
T _r = *(T *)self->result_slot;
|
|
88
88
|
memset((void *)self->result_slot, 0, sizeof(T));
|
|
@@ -91,72 +91,64 @@ pub mv struct Task<T> : Awaitable {
|
|
|
91
91
|
```
|
|
92
92
|
#arch: aarch64
|
|
93
93
|
// Wait for completion (same as wait method)
|
|
94
|
-
ldr x0, [x19, #
|
|
95
|
-
cbz x0, .Lresult_handle
|
|
96
|
-
bl ___nomen_future_wait
|
|
97
|
-
b .Lresult_read
|
|
98
|
-
.Lresult_handle:
|
|
99
|
-
ldr x0, [x19, #8]
|
|
94
|
+
ldr x0, [x19, #24]
|
|
100
95
|
cbz x0, .Lresult_read
|
|
101
|
-
|
|
102
|
-
cbnz w1, .Lresult_read
|
|
103
|
-
sub sp, sp, #16
|
|
104
|
-
mov x1, sp
|
|
105
|
-
bl _pthread_join
|
|
106
|
-
add sp, sp, #16
|
|
107
|
-
str xzr, [x19, #8]
|
|
108
|
-
mov w0, #1
|
|
109
|
-
strb w0, [x19, #16]
|
|
96
|
+
bl ___nomen_future_wait
|
|
110
97
|
.Lresult_read:
|
|
98
|
+
// An empty or already-consumed handle owns no result cell: return
|
|
99
|
+
// the zero T instead of reading through a null slot.
|
|
100
|
+
ldr x8, [x19, #8]
|
|
101
|
+
cbz x8, .Lresult_zero
|
|
111
102
|
// The result cell is always 16 bytes (see the spawn build phase):
|
|
112
103
|
// load the full pair — a fat string returns (ptr, len) in x0/x1, a
|
|
113
104
|
// scalar returns its word in x0 — then zero the cell (the value is
|
|
114
105
|
// moved out; a second call observes the zero).
|
|
115
|
-
ldr x8, [x19, #24]
|
|
116
106
|
ldr x0, [x8]
|
|
117
107
|
ldr x1, [x8, #8]
|
|
118
108
|
stp xzr, xzr, [x8]
|
|
109
|
+
b .Lresult_end
|
|
110
|
+
.Lresult_zero:
|
|
111
|
+
mov x0, #0
|
|
112
|
+
mov x1, #0
|
|
113
|
+
.Lresult_end:
|
|
119
114
|
```
|
|
120
115
|
}
|
|
121
116
|
|
|
122
117
|
// Block until the task finishes, then return its result as a uint64.
|
|
123
118
|
// Convenience for the common case; same as result() for uint64 tasks.
|
|
124
|
-
|
|
119
|
+
// Like result(), it zeroes the cell as the value leaves (one end state
|
|
120
|
+
// for a consumed handle), and an empty handle returns zero.
|
|
121
|
+
pub func result_uint64 = (ref self {}, out uint64) {
|
|
125
122
|
```
|
|
126
123
|
#arch: c
|
|
127
124
|
if (self->future) {
|
|
128
125
|
__nomen_future_wait((struct nomen_future *)self->future);
|
|
129
|
-
} else if (self->handle && !self->done) {
|
|
130
|
-
void *ret;
|
|
131
|
-
pthread_join((pthread_t)self->handle, &ret);
|
|
132
|
-
self->handle = 0;
|
|
133
|
-
self->done = 1;
|
|
134
126
|
}
|
|
135
|
-
|
|
127
|
+
if (!self->result_slot) {
|
|
128
|
+
return 0;
|
|
129
|
+
}
|
|
130
|
+
unsigned long long _r = *(unsigned long long *)self->result_slot;
|
|
131
|
+
*(unsigned long long *)self->result_slot = 0;
|
|
132
|
+
return _r;
|
|
136
133
|
```
|
|
137
134
|
```
|
|
138
135
|
#arch: aarch64
|
|
139
136
|
// Wait for completion
|
|
140
|
-
ldr x0, [x19, #
|
|
141
|
-
cbz x0, .Lru64_handle
|
|
142
|
-
bl ___nomen_future_wait
|
|
143
|
-
b .Lru64_read
|
|
144
|
-
.Lru64_handle:
|
|
145
|
-
ldr x0, [x19, #8]
|
|
137
|
+
ldr x0, [x19, #24]
|
|
146
138
|
cbz x0, .Lru64_read
|
|
147
|
-
|
|
148
|
-
cbnz w1, .Lru64_read
|
|
149
|
-
sub sp, sp, #16
|
|
150
|
-
mov x1, sp
|
|
151
|
-
bl _pthread_join
|
|
152
|
-
add sp, sp, #16
|
|
153
|
-
str xzr, [x19, #8]
|
|
154
|
-
mov w0, #1
|
|
155
|
-
strb w0, [x19, #16]
|
|
139
|
+
bl ___nomen_future_wait
|
|
156
140
|
.Lru64_read:
|
|
157
|
-
//
|
|
158
|
-
|
|
159
|
-
|
|
141
|
+
// An empty or already-consumed handle owns no result cell: return
|
|
142
|
+
// zero instead of reading through a null slot. Otherwise zero the
|
|
143
|
+
// cell as the value leaves (matching result()'s end state).
|
|
144
|
+
ldr x8, [x19, #8]
|
|
145
|
+
cbz x8, .Lru64_zero
|
|
146
|
+
ldr x0, [x8]
|
|
147
|
+
str xzr, [x8]
|
|
148
|
+
b .Lru64_end
|
|
149
|
+
.Lru64_zero:
|
|
150
|
+
mov x0, #0
|
|
151
|
+
.Lru64_end:
|
|
160
152
|
```
|
|
161
153
|
}
|
|
162
154
|
|
|
@@ -165,7 +157,7 @@ pub mv struct Task<T> : Awaitable {
|
|
|
165
157
|
// handle whose machinery was already consumed). The missing peek
|
|
166
158
|
// between wait (blocking) and cancel (cooperative): event loops use it
|
|
167
159
|
// to notice a finished task without parking the UI thread.
|
|
168
|
-
pub func is_done = (ref self, out bool) {
|
|
160
|
+
pub func is_done = (ref self {}, out bool) {
|
|
169
161
|
```
|
|
170
162
|
#arch: c
|
|
171
163
|
return __nomen_future_is_done((struct nomen_future *)self->future);
|
|
@@ -173,14 +165,14 @@ pub mv struct Task<T> : Awaitable {
|
|
|
173
165
|
```
|
|
174
166
|
#arch: aarch64
|
|
175
167
|
// x0 = self->future; result in x0.
|
|
176
|
-
ldr x0, [x19, #
|
|
168
|
+
ldr x0, [x19, #24]
|
|
177
169
|
bl ___nomen_future_is_done
|
|
178
170
|
```
|
|
179
171
|
}
|
|
180
172
|
|
|
181
173
|
// Request that the task stop. Cooperative — the task observes this via
|
|
182
174
|
// `Task.current_cancelled()` at its own checkpoints. No preemption.
|
|
183
|
-
pub func cancel = (ref self) {
|
|
175
|
+
pub func cancel = (ref self {}) {
|
|
184
176
|
```
|
|
185
177
|
#arch: c
|
|
186
178
|
// Sets the flag and wakes any fiber parked on this task's future, so
|
|
@@ -195,12 +187,12 @@ pub mv struct Task<T> : Awaitable {
|
|
|
195
187
|
#arch: aarch64
|
|
196
188
|
// if (self->future) __nomen_future_cancel(self->future);
|
|
197
189
|
// else if (self->cancel_flag) *(self->cancel_flag) = 1
|
|
198
|
-
ldr x0, [x19, #
|
|
190
|
+
ldr x0, [x19, #24]
|
|
199
191
|
cbz x0, .Lcancel_flag_path
|
|
200
192
|
bl ___nomen_future_cancel
|
|
201
193
|
b .Lcancel_done
|
|
202
194
|
.Lcancel_flag_path:
|
|
203
|
-
ldr x0, [x19, #
|
|
195
|
+
ldr x0, [x19, #16]
|
|
204
196
|
cbz x0, .Lcancel_done
|
|
205
197
|
mov x1, #1
|
|
206
198
|
str x1, [x0]
|
|
@@ -393,12 +385,12 @@ pub mv struct Task<T> : Awaitable {
|
|
|
393
385
|
```
|
|
394
386
|
#arch: aarch64
|
|
395
387
|
// if (self->future) { free an unconsumed string result; release; zero fields }
|
|
396
|
-
ldr x0, [x19, #
|
|
388
|
+
ldr x0, [x19, #24]
|
|
397
389
|
cbz x0, .Ldestroy_done
|
|
398
390
|
// An unconsumed string result: the cell still holds (ptr, len) —
|
|
399
391
|
// result() zeroes it when it moves the value out. The T_FAT dispatch
|
|
400
392
|
// folds to a constant branch after monomorphization.
|
|
401
|
-
ldr x8, [x19, #
|
|
393
|
+
ldr x8, [x19, #8]
|
|
402
394
|
mov x0, #T_FAT
|
|
403
395
|
cmp x0, #0
|
|
404
396
|
b.eq .Ldestroy_nofree
|
|
@@ -411,11 +403,11 @@ pub mv struct Task<T> : Awaitable {
|
|
|
411
403
|
stp xzr, xzr, [x8]
|
|
412
404
|
bl _free
|
|
413
405
|
.Ldestroy_nofree:
|
|
414
|
-
ldr x0, [x19, #
|
|
406
|
+
ldr x0, [x19, #24]
|
|
415
407
|
bl ___nomen_future_release
|
|
416
|
-
str xzr, [x19, #40]
|
|
417
408
|
str xzr, [x19, #24]
|
|
418
|
-
str xzr, [x19, #
|
|
409
|
+
str xzr, [x19, #8]
|
|
410
|
+
str xzr, [x19, #16]
|
|
419
411
|
.Ldestroy_done:
|
|
420
412
|
```
|
|
421
413
|
}
|
package/core/System/Test.nm
CHANGED
|
@@ -96,7 +96,7 @@ pub mv struct Tester {
|
|
|
96
96
|
|
|
97
97
|
// (harness) Start timing `name` and clear the per-test state. Called by
|
|
98
98
|
// the `main` that `nomen test` generates — not by test code.
|
|
99
|
-
pub func begin_test = (ref self, string name) {
|
|
99
|
+
pub func begin_test = (ref self { current }, string name) {
|
|
100
100
|
self.has_failed = false
|
|
101
101
|
self.passed = 0
|
|
102
102
|
self.failed = 0
|
|
@@ -106,7 +106,7 @@ pub mv struct Tester {
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
// (harness) Close the current test and emit its `done` record.
|
|
109
|
-
pub func end_test = (ref self) {
|
|
109
|
+
pub func end_test = (ref self {}) {
|
|
110
110
|
const uint64 elapsed = Time.now_ns() - self.started
|
|
111
111
|
Console.write_line(
|
|
112
112
|
"\\nomen|done|" + self.current + "|\{self.passed}|\{self.failed}|\{elapsed}",
|
|
@@ -124,7 +124,7 @@ pub mv struct Tester {
|
|
|
124
124
|
*
|
|
125
125
|
* t.expect(result == 2, "add(1, 1) should be 2")
|
|
126
126
|
**/
|
|
127
|
-
pub func expect = (ref self, bool condition, string message) {
|
|
127
|
+
pub func expect = (ref self {}, bool condition, string message) {
|
|
128
128
|
if self.has_failed {
|
|
129
129
|
return
|
|
130
130
|
}
|
|
@@ -143,7 +143,7 @@ pub mv struct Tester {
|
|
|
143
143
|
* t.fail("expected to find the key")
|
|
144
144
|
* }
|
|
145
145
|
**/
|
|
146
|
-
pub func fail = (ref self, string message) {
|
|
146
|
+
pub func fail = (ref self {}, string message) {
|
|
147
147
|
if self.has_failed {
|
|
148
148
|
return
|
|
149
149
|
}
|
|
@@ -154,43 +154,43 @@ pub mv struct Tester {
|
|
|
154
154
|
//
|
|
155
155
|
// Nomen has no generic methods, so there is one overload per nullable
|
|
156
156
|
// type the compiler can compare against `null`.
|
|
157
|
-
pub func assert = (ref self, int? value, string message) {
|
|
157
|
+
pub func assert = (ref self {}, int? value, string message) {
|
|
158
158
|
self.expect(value != null, message)
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
pub func assert = (ref self, uint? value, string message) {
|
|
161
|
+
pub func assert = (ref self {}, uint? value, string message) {
|
|
162
162
|
self.expect(value != null, message)
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
pub func assert = (ref self, int64? value, string message) {
|
|
165
|
+
pub func assert = (ref self {}, int64? value, string message) {
|
|
166
166
|
self.expect(value != null, message)
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
pub func assert = (ref self, uint64? value, string message) {
|
|
169
|
+
pub func assert = (ref self {}, uint64? value, string message) {
|
|
170
170
|
self.expect(value != null, message)
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
pub func assert = (ref self, int8? value, string message) {
|
|
173
|
+
pub func assert = (ref self {}, int8? value, string message) {
|
|
174
174
|
self.expect(value != null, message)
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
pub func assert = (ref self, uint8? value, string message) {
|
|
177
|
+
pub func assert = (ref self {}, uint8? value, string message) {
|
|
178
178
|
self.expect(value != null, message)
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
pub func assert = (ref self, float? value, string message) {
|
|
181
|
+
pub func assert = (ref self {}, float? value, string message) {
|
|
182
182
|
self.expect(value != null, message)
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
-
pub func assert = (ref self, bool? value, string message) {
|
|
185
|
+
pub func assert = (ref self {}, bool? value, string message) {
|
|
186
186
|
self.expect(value != null, message)
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
-
pub func assert = (ref self, char? value, string message) {
|
|
189
|
+
pub func assert = (ref self {}, char? value, string message) {
|
|
190
190
|
self.expect(value != null, message)
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
pub func assert = (ref self, string? value, string message) {
|
|
193
|
+
pub func assert = (ref self {}, string? value, string message) {
|
|
194
194
|
self.expect(value != null, message)
|
|
195
195
|
}
|
|
196
196
|
|
|
@@ -212,7 +212,7 @@ pub mv struct Tester {
|
|
|
212
212
|
* (default 1000, override with `bench_n`) and computes min / median /
|
|
213
213
|
* mean / max / stddev over the per-iteration durations.
|
|
214
214
|
**/
|
|
215
|
-
pub func bench = (ref self, string label, func() body) {
|
|
215
|
+
pub func bench = (ref self { bench_label }, string label, func() body) {
|
|
216
216
|
if self.has_failed {
|
|
217
217
|
return
|
|
218
218
|
}
|
|
@@ -226,7 +226,7 @@ pub mv struct Tester {
|
|
|
226
226
|
* for slow functions (where the default 1000 would dominate the suite) or
|
|
227
227
|
* for reproducible sample counts.
|
|
228
228
|
**/
|
|
229
|
-
pub func bench_n = (ref self, string label, func() body, int samples) {
|
|
229
|
+
pub func bench_n = (ref self { bench_label }, string label, func() body, int samples) {
|
|
230
230
|
if self.has_failed {
|
|
231
231
|
return
|
|
232
232
|
}
|
|
@@ -244,7 +244,7 @@ pub mv struct Tester {
|
|
|
244
244
|
* this is a no-op.
|
|
245
245
|
**/
|
|
246
246
|
pub func record_bench = (
|
|
247
|
-
ref self,
|
|
247
|
+
ref self {},
|
|
248
248
|
string label,
|
|
249
249
|
int n,
|
|
250
250
|
int min,
|
|
@@ -263,7 +263,7 @@ pub mv struct Tester {
|
|
|
263
263
|
)
|
|
264
264
|
}
|
|
265
265
|
|
|
266
|
-
func record_failure = (ref self, string message) {
|
|
266
|
+
func record_failure = (ref self {}, string message) {
|
|
267
267
|
self.failed += 1
|
|
268
268
|
self.total_failed += 1
|
|
269
269
|
self.has_failed = true
|
|
@@ -51,16 +51,16 @@ pub mv struct CharIndex {
|
|
|
51
51
|
chars = chars + 1
|
|
52
52
|
}
|
|
53
53
|
} else {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
open_at = pos
|
|
58
|
-
open_start = chars
|
|
59
|
-
open_width = w
|
|
60
|
-
open_count = 1
|
|
61
|
-
pos = pos + w
|
|
62
|
-
chars = chars + 1
|
|
54
|
+
if open_at >= 0 {
|
|
55
|
+
self.runs.push(CharRun(open_at, open_start, open_width, open_count))
|
|
63
56
|
}
|
|
57
|
+
open_at = pos
|
|
58
|
+
open_start = chars
|
|
59
|
+
open_width = w
|
|
60
|
+
open_count = 1
|
|
61
|
+
pos = pos + w
|
|
62
|
+
chars = chars + 1
|
|
63
|
+
}
|
|
64
64
|
}
|
|
65
65
|
if open_at >= 0 {
|
|
66
66
|
self.runs.push(CharRun(open_at, open_start, open_width, open_count))
|
|
@@ -21,7 +21,7 @@ pub struct Chars {
|
|
|
21
21
|
|
|
22
22
|
// The code point at the cursor, advancing past it. Malformed sequences
|
|
23
23
|
// yield U+FFFD. Panics when exhausted — check has_next first.
|
|
24
|
-
pub func next = (ref self, out int) {
|
|
24
|
+
pub func next = (ref self {}, out int) {
|
|
25
25
|
if self.byte_pos >= self.source.length {
|
|
26
26
|
panic("Chars exhausted; check has_next first")
|
|
27
27
|
}
|
package/core/System/Text/Json.nm
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// Integer (de)serialization is handled by int.to_string() and int.parse()
|
|
6
6
|
// (see int.nm), so this struct only deals with JSON strings.
|
|
7
7
|
//
|
|
8
|
-
// `parse` / `stringify` use a JsonTree pool (flat
|
|
8
|
+
// `parse` / `stringify` use a JsonTree pool (a flat StableList of nodes)
|
|
9
9
|
// instead of per-node heap allocation, avoiding O(nodes) malloc/free per
|
|
10
10
|
// parse cycle. The caller creates a JsonTree once and resets it per document.
|
|
11
11
|
|
|
@@ -41,8 +41,8 @@ pub struct Json {
|
|
|
41
41
|
sb.append_string("\x5cr")
|
|
42
42
|
}
|
|
43
43
|
} else {
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
sb.append_char(c)
|
|
45
|
+
}
|
|
46
46
|
}
|
|
47
47
|
sb.append_char('"')
|
|
48
48
|
return sb.to_string()
|
|
@@ -90,8 +90,8 @@ pub struct Json {
|
|
|
90
90
|
sb.append_string("\x5c")
|
|
91
91
|
}
|
|
92
92
|
} else {
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
sb.append_char(e)
|
|
94
|
+
}
|
|
95
95
|
}
|
|
96
96
|
} else {
|
|
97
97
|
sb.append_char(c)
|
|
@@ -102,7 +102,7 @@ pub struct Json {
|
|
|
102
102
|
|
|
103
103
|
// Parse a JSON document into tree. The caller provides a JsonTree pool
|
|
104
104
|
// which is reset and filled. Returns the index of the root node.
|
|
105
|
-
pub func parse = (string s, ref JsonTree tree, out int) {
|
|
105
|
+
pub func parse = (string s, ref JsonTree tree { nodes }, out int) {
|
|
106
106
|
tree.reset()
|
|
107
107
|
var pos = Buffer<int>()
|
|
108
108
|
pos.alloc_int(1)
|
|
@@ -112,14 +112,14 @@ pub struct Json {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
// Serialize a node in the tree back to a JSON string.
|
|
115
|
-
pub func stringify = (ref JsonTree tree, int root, out string) {
|
|
115
|
+
pub func stringify = (ref JsonTree tree { nodes }, int root, out string) {
|
|
116
116
|
var sb = StringBuilder()
|
|
117
117
|
json_ser_value(ref tree, root, ref sb)
|
|
118
118
|
return sb.to_string()
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
func json_skip_ws = (string s, ref Buffer<int> pos, int len) {
|
|
122
|
+
func json_skip_ws = (string s, ref Buffer<int> pos {}, int len) {
|
|
123
123
|
while pos.load_int(0) < len {
|
|
124
124
|
var int i = pos.load_int(0)
|
|
125
125
|
var int code = s.at(i) as int
|
|
@@ -131,54 +131,69 @@ func json_skip_ws = (string s, ref Buffer<int> pos, int len) {
|
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
func json_parse_string = (string s, ref Buffer<int> pos, int len, ref JsonTree tree, out int) {
|
|
134
|
+
func json_parse_string = (string s, ref Buffer<int> pos {}, int len, ref JsonTree tree { nodes }, out int) {
|
|
135
135
|
var int i = pos.load_int(0)
|
|
136
136
|
i += 1
|
|
137
137
|
var sb = StringBuilder()
|
|
138
|
-
while i < len
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
138
|
+
while i < len {
|
|
139
|
+
// Scan-to-next-escape fast path: copy the run of plain bytes in ONE
|
|
140
|
+
// bulk append instead of a per-byte append_char (each of which pays
|
|
141
|
+
// the builder's capacity check and length bump).
|
|
142
|
+
var int run_start = i
|
|
143
|
+
var int code = 0
|
|
144
|
+
while i < len {
|
|
145
|
+
code = s.at(i) as int
|
|
146
|
+
if code == 34 || code == 92 {
|
|
147
|
+
break
|
|
148
|
+
}
|
|
142
149
|
i += 1
|
|
150
|
+
}
|
|
151
|
+
if i > run_start {
|
|
152
|
+
sb.append_string_view(s.slice(run_start, i))
|
|
153
|
+
}
|
|
154
|
+
if i >= len {
|
|
143
155
|
break
|
|
144
156
|
}
|
|
145
|
-
if code ==
|
|
157
|
+
if code == 34 {
|
|
146
158
|
i += 1
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
159
|
+
break
|
|
160
|
+
}
|
|
161
|
+
// code == 92 — escape sequence: consume the backslash, append the
|
|
162
|
+
// decoded byte (an unknown escape appends the byte verbatim).
|
|
163
|
+
i += 1
|
|
164
|
+
if i < len {
|
|
165
|
+
var char e = s.at(i)
|
|
166
|
+
var int ec = e as int
|
|
167
|
+
switch {
|
|
168
|
+
case ec == 110 {
|
|
169
|
+
sb.append_string("\n")
|
|
170
|
+
}
|
|
171
|
+
case ec == 116 {
|
|
172
|
+
sb.append_string("\t")
|
|
173
|
+
}
|
|
174
|
+
case ec == 114 {
|
|
175
|
+
sb.append_string("\r")
|
|
176
|
+
}
|
|
177
|
+
case ec == 34 {
|
|
178
|
+
sb.append_char('"')
|
|
179
|
+
}
|
|
180
|
+
case ec == 92 {
|
|
181
|
+
sb.append_string("\x5c")
|
|
182
|
+
}
|
|
183
|
+
} else {
|
|
184
|
+
sb.append_char(e)
|
|
169
185
|
}
|
|
170
|
-
|
|
171
|
-
sb.append_char(c)
|
|
186
|
+
i += 1
|
|
172
187
|
}
|
|
173
188
|
}
|
|
174
189
|
pos.store_int(0, i)
|
|
175
190
|
var int n = tree.alloc_node()
|
|
176
191
|
tree.set_kind(n, 3)
|
|
177
|
-
tree.
|
|
192
|
+
tree.take_text(n, ref sb)
|
|
178
193
|
return n
|
|
179
194
|
}
|
|
180
195
|
|
|
181
|
-
func json_parse_number = (string s, ref Buffer<int> pos, int len, ref JsonTree tree, out int) {
|
|
196
|
+
func json_parse_number = (string s, ref Buffer<int> pos {}, int len, ref JsonTree tree { nodes }, out int) {
|
|
182
197
|
var int i = pos.load_int(0)
|
|
183
198
|
var int start = i
|
|
184
199
|
while i < len {
|
|
@@ -191,17 +206,16 @@ func json_parse_number = (string s, ref Buffer<int> pos, int len, ref JsonTree t
|
|
|
191
206
|
}
|
|
192
207
|
pos.store_int(0, i)
|
|
193
208
|
var sb = StringBuilder()
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
}
|
|
209
|
+
// One bulk append for the whole run of number bytes (the scan above
|
|
210
|
+
// already found its end) — no per-byte append_char.
|
|
211
|
+
sb.append_string_view(s.slice(start, i))
|
|
198
212
|
var int n = tree.alloc_node()
|
|
199
213
|
tree.set_kind(n, 2)
|
|
200
|
-
tree.
|
|
214
|
+
tree.take_text(n, ref sb)
|
|
201
215
|
return n
|
|
202
216
|
}
|
|
203
217
|
|
|
204
|
-
func json_parse_literal = (string s, ref Buffer<int> pos, int len, ref JsonTree tree, out int) {
|
|
218
|
+
func json_parse_literal = (string s, ref Buffer<int> pos {}, int len, ref JsonTree tree { nodes }, out int) {
|
|
205
219
|
var int i = pos.load_int(0)
|
|
206
220
|
var int code = s.at(i) as int
|
|
207
221
|
var int n = tree.alloc_node()
|
|
@@ -216,14 +230,14 @@ func json_parse_literal = (string s, ref Buffer<int> pos, int len, ref JsonTree
|
|
|
216
230
|
adv = 5
|
|
217
231
|
}
|
|
218
232
|
} else {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
233
|
+
tree.set_kind(n, 0)
|
|
234
|
+
adv = 4
|
|
235
|
+
}
|
|
222
236
|
pos.store_int(0, i + adv)
|
|
223
237
|
return n
|
|
224
238
|
}
|
|
225
239
|
|
|
226
|
-
func json_parse_value = (string s, ref Buffer<int> pos, int len, ref JsonTree tree, out int) {
|
|
240
|
+
func json_parse_value = (string s, ref Buffer<int> pos {}, int len, ref JsonTree tree { nodes }, out int) {
|
|
227
241
|
json_skip_ws(s, ref pos, len)
|
|
228
242
|
var int i = pos.load_int(0)
|
|
229
243
|
if i >= len {
|
|
@@ -253,7 +267,7 @@ func json_parse_value = (string s, ref Buffer<int> pos, int len, ref JsonTree tr
|
|
|
253
267
|
return json_parse_number(s, ref pos, len, ref tree)
|
|
254
268
|
}
|
|
255
269
|
|
|
256
|
-
func json_parse_elements = (string s, ref Buffer<int> pos, int len, ref JsonTree tree, out int) {
|
|
270
|
+
func json_parse_elements = (string s, ref Buffer<int> pos {}, int len, ref JsonTree tree { nodes }, out int) {
|
|
257
271
|
json_skip_ws(s, ref pos, len)
|
|
258
272
|
var int i = pos.load_int(0)
|
|
259
273
|
if i >= len {
|
|
@@ -275,7 +289,7 @@ func json_parse_elements = (string s, ref Buffer<int> pos, int len, ref JsonTree
|
|
|
275
289
|
return elem
|
|
276
290
|
}
|
|
277
291
|
|
|
278
|
-
func json_parse_pairs = (string s, ref Buffer<int> pos, int len, ref JsonTree tree, out int) {
|
|
292
|
+
func json_parse_pairs = (string s, ref Buffer<int> pos {}, int len, ref JsonTree tree { nodes }, out int) {
|
|
279
293
|
json_skip_ws(s, ref pos, len)
|
|
280
294
|
var int i = pos.load_int(0)
|
|
281
295
|
if i >= len {
|
|
@@ -307,7 +321,7 @@ func json_parse_pairs = (string s, ref Buffer<int> pos, int len, ref JsonTree tr
|
|
|
307
321
|
return key
|
|
308
322
|
}
|
|
309
323
|
|
|
310
|
-
func json_ser_list = (ref JsonTree tree, int first, ref StringBuilder sb) {
|
|
324
|
+
func json_ser_list = (ref JsonTree tree { nodes }, int first, ref StringBuilder sb {}) {
|
|
311
325
|
json_ser_value(ref tree, first, ref sb)
|
|
312
326
|
var int next = tree.get_next(first)
|
|
313
327
|
if next >= 0 {
|
|
@@ -316,9 +330,9 @@ func json_ser_list = (ref JsonTree tree, int first, ref StringBuilder sb) {
|
|
|
316
330
|
}
|
|
317
331
|
}
|
|
318
332
|
|
|
319
|
-
func json_ser_pairs = (ref JsonTree tree, int first, ref StringBuilder sb) {
|
|
333
|
+
func json_ser_pairs = (ref JsonTree tree { nodes }, int first, ref StringBuilder sb {}) {
|
|
320
334
|
sb.append_char('"')
|
|
321
|
-
sb.
|
|
335
|
+
sb.append_string_view(tree.get_text_view(first))
|
|
322
336
|
sb.append_string("\":")
|
|
323
337
|
var int v = tree.get_val(first)
|
|
324
338
|
if v < 0 {
|
|
@@ -333,7 +347,7 @@ func json_ser_pairs = (ref JsonTree tree, int first, ref StringBuilder sb) {
|
|
|
333
347
|
}
|
|
334
348
|
}
|
|
335
349
|
|
|
336
|
-
func json_ser_value = (ref JsonTree tree, int n, ref StringBuilder sb) {
|
|
350
|
+
func json_ser_value = (ref JsonTree tree { nodes }, int n, ref StringBuilder sb {}) {
|
|
337
351
|
var int k = tree.get_kind(n)
|
|
338
352
|
if k == 0 {
|
|
339
353
|
sb.append_string("null")
|
|
@@ -348,12 +362,12 @@ func json_ser_value = (ref JsonTree tree, int n, ref StringBuilder sb) {
|
|
|
348
362
|
return
|
|
349
363
|
}
|
|
350
364
|
if k == 2 {
|
|
351
|
-
sb.
|
|
365
|
+
sb.append_string_view(tree.get_text_view(n))
|
|
352
366
|
return
|
|
353
367
|
}
|
|
354
368
|
if k == 3 {
|
|
355
369
|
sb.append_char('"')
|
|
356
|
-
sb.
|
|
370
|
+
sb.append_string_view(tree.get_text_view(n))
|
|
357
371
|
sb.append_char('"')
|
|
358
372
|
return
|
|
359
373
|
}
|