nomen-lang 0.5.0 → 0.6.1
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 +116 -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 +176 -58
- 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 +1201 -1101
- package/package.json +1 -1
- package/src/test.ts +22 -4
|
@@ -65,7 +65,12 @@ pub struct Regex {
|
|
|
65
65
|
// group that did not participate in the match) and return true. Return
|
|
66
66
|
// false with `out` untouched when nothing matches. A group that repeats
|
|
67
67
|
// (`(a)+`) reports its LAST iteration.
|
|
68
|
-
pub func captures = (
|
|
68
|
+
pub func captures = (
|
|
69
|
+
string pattern,
|
|
70
|
+
string input,
|
|
71
|
+
ref List<string> dst,
|
|
72
|
+
out bool
|
|
73
|
+
) { var int pat_len = pattern.length
|
|
69
74
|
var int len = input.length
|
|
70
75
|
var string charset = first_byte_set(pattern, pat_len)
|
|
71
76
|
var int clen = charset.length
|
|
@@ -83,7 +88,7 @@ pub struct Regex {
|
|
|
83
88
|
if end >= 0 {
|
|
84
89
|
var string whole = input.substring(i, end)
|
|
85
90
|
dst.push(whole)
|
|
86
|
-
var
|
|
91
|
+
var g = 1
|
|
87
92
|
while g <= groups; g += 1 {
|
|
88
93
|
var int at = last_capture_index(caps, g)
|
|
89
94
|
if at >= 0 {
|
|
@@ -166,7 +171,12 @@ pub struct Regex {
|
|
|
166
171
|
}
|
|
167
172
|
|
|
168
173
|
// Replace all non-overlapping occurrences of pattern in input with replacement.
|
|
169
|
-
pub func replace_all = (
|
|
174
|
+
pub func replace_all = (
|
|
175
|
+
string pattern,
|
|
176
|
+
string input,
|
|
177
|
+
string replacement,
|
|
178
|
+
out string
|
|
179
|
+
) { var sb = StringBuilder()
|
|
170
180
|
var int pat_len = pattern.length
|
|
171
181
|
var int len = input.length
|
|
172
182
|
var string charset = first_byte_set(pattern, pat_len)
|
|
@@ -241,7 +251,7 @@ pub struct Regex {
|
|
|
241
251
|
func fold_pattern = (string pattern, move out string) {
|
|
242
252
|
var int n = pattern.length
|
|
243
253
|
var sb = StringBuilder()
|
|
244
|
-
var
|
|
254
|
+
var i = 0
|
|
245
255
|
while i < n; i += 1 {
|
|
246
256
|
var char c = pattern.at(i)
|
|
247
257
|
if (c as int) == 92 {
|
|
@@ -412,7 +422,13 @@ func first_byte_set = (string pattern, int pat_len, out string) {
|
|
|
412
422
|
// byte(s) of each alternative to sb. Alternations at depth 0 are split on '|'.
|
|
413
423
|
// Returns -1 when any alternative's first-byte set can't be determined (the
|
|
414
424
|
// caller must then disable the prefilter), 0 otherwise.
|
|
415
|
-
func collect_first_bytes = (
|
|
425
|
+
func collect_first_bytes = (
|
|
426
|
+
string pattern,
|
|
427
|
+
int pat_pos,
|
|
428
|
+
int pat_end,
|
|
429
|
+
ref StringBuilder sb,
|
|
430
|
+
out int
|
|
431
|
+
) {
|
|
416
432
|
var depth = 0
|
|
417
433
|
var int i = pat_pos
|
|
418
434
|
var int alt_start = pat_pos
|
|
@@ -453,10 +469,10 @@ func collect_first_bytes = (string pattern, int pat_pos, int pat_end, ref String
|
|
|
453
469
|
//
|
|
454
470
|
// Handles the shapes the engine's matcher can produce: a leading anchor is
|
|
455
471
|
// zero-width (skipped), a leading group contributes its CONTENT's first bytes
|
|
456
|
-
// (recursively over the group's own top-level alternatives), and
|
|
457
|
-
// quantified with '?'/'*' is
|
|
458
|
-
//
|
|
459
|
-
// set indeterminable.
|
|
472
|
+
// (recursively over the group's own top-level alternatives), and ANY leading
|
|
473
|
+
// atom — literal, escaped, class, or group — quantified with '?'/'*' is
|
|
474
|
+
// nullable: what FOLLOWS it can also be first, so the scan continues past it.
|
|
475
|
+
// A wildcard '.' or an empty alternative makes the set indeterminable.
|
|
460
476
|
func alt_first_bytes = (string pattern, int start, int end, ref StringBuilder sb, out int) {
|
|
461
477
|
var int i = start
|
|
462
478
|
while i < end {
|
|
@@ -474,9 +490,17 @@ func alt_first_bytes = (string pattern, int start, int end, ref StringBuilder sb
|
|
|
474
490
|
if shorthand_members(e, ref sb) < 0 {
|
|
475
491
|
return -1
|
|
476
492
|
}
|
|
493
|
+
} else {
|
|
494
|
+
sb.append_char(e)
|
|
495
|
+
}
|
|
496
|
+
// A '?'/'*' quantifier makes the atom nullable — the first
|
|
497
|
+
// byte can also come from whatever follows it.
|
|
498
|
+
var int cont = nullable_quant_after(pattern, i + 2, end)
|
|
499
|
+
if cont < 0 {
|
|
477
500
|
return 0
|
|
478
501
|
}
|
|
479
|
-
|
|
502
|
+
i = cont
|
|
503
|
+
continue
|
|
480
504
|
}
|
|
481
505
|
return 0
|
|
482
506
|
}
|
|
@@ -493,7 +517,12 @@ func alt_first_bytes = (string pattern, int start, int end, ref StringBuilder sb
|
|
|
493
517
|
}
|
|
494
518
|
}
|
|
495
519
|
add_class_chars(pattern, i + 1, a_end - 1, ref sb)
|
|
496
|
-
|
|
520
|
+
var int cont = nullable_quant_after(pattern, a_end, end)
|
|
521
|
+
if cont < 0 {
|
|
522
|
+
return 0
|
|
523
|
+
}
|
|
524
|
+
i = cont
|
|
525
|
+
continue
|
|
497
526
|
}
|
|
498
527
|
if c == '(' {
|
|
499
528
|
var int a_end = atom_end_pos(pattern, i, end)
|
|
@@ -502,27 +531,11 @@ func alt_first_bytes = (string pattern, int start, int end, ref StringBuilder sb
|
|
|
502
531
|
if collect_first_bytes(pattern, i + 1, a_end - 1, ref sb) < 0 {
|
|
503
532
|
return -1
|
|
504
533
|
}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
// quantifier lazy and is skipped either way.
|
|
508
|
-
var int after = a_end
|
|
509
|
-
var int nullable = 0
|
|
510
|
-
if after < end {
|
|
511
|
-
var char q = pattern.at(after)
|
|
512
|
-
if q == '?' || q == '*' {
|
|
513
|
-
nullable = 1
|
|
514
|
-
after += 1
|
|
515
|
-
if after < end {
|
|
516
|
-
if pattern.at(after) == '?' {
|
|
517
|
-
after += 1
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
if nullable == 0 {
|
|
534
|
+
var int cont = nullable_quant_after(pattern, a_end, end)
|
|
535
|
+
if cont < 0 {
|
|
523
536
|
return 0
|
|
524
537
|
}
|
|
525
|
-
i =
|
|
538
|
+
i = cont
|
|
526
539
|
continue
|
|
527
540
|
}
|
|
528
541
|
if c == '^' || c == '$' {
|
|
@@ -536,9 +549,40 @@ func alt_first_bytes = (string pattern, int start, int end, ref StringBuilder sb
|
|
|
536
549
|
return -1
|
|
537
550
|
}
|
|
538
551
|
sb.append_char(c)
|
|
539
|
-
|
|
552
|
+
var int cont = nullable_quant_after(pattern, i + 1, end)
|
|
553
|
+
if cont < 0 {
|
|
554
|
+
return 0
|
|
555
|
+
}
|
|
556
|
+
i = cont
|
|
557
|
+
continue
|
|
558
|
+
}
|
|
559
|
+
// An empty alternative (nothing left after nullable atoms — or nothing at
|
|
560
|
+
// all) can match at any position — indeterminable.
|
|
561
|
+
return -1
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// If the atom ending at `after` carries a nullable quantifier ('?' or '*',
|
|
565
|
+
// optionally marked lazy by a second '?'), return the position just past the
|
|
566
|
+
// quantifier; otherwise return -1. Callers keep scanning from the returned
|
|
567
|
+
// position: a nullable leading atom means the alternative's match can also
|
|
568
|
+
// START with whatever follows, so those bytes belong in the first-byte set
|
|
569
|
+
// too. ('+' is NOT nullable — the atom must match once — so it leaves the
|
|
570
|
+
// atom's own bytes as the whole first set.)
|
|
571
|
+
func nullable_quant_after = (string pattern, int after, int end, out int) {
|
|
572
|
+
var int j = after
|
|
573
|
+
if j >= end {
|
|
574
|
+
return -1
|
|
575
|
+
}
|
|
576
|
+
var char q = pattern.at(j)
|
|
577
|
+
if q == '?' || q == '*' {
|
|
578
|
+
j += 1
|
|
579
|
+
if j < end {
|
|
580
|
+
if pattern.at(j) == '?' {
|
|
581
|
+
j += 1
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
return j
|
|
540
585
|
}
|
|
541
|
-
// An empty alternative can match at any position — indeterminable.
|
|
542
586
|
return -1
|
|
543
587
|
}
|
|
544
588
|
|
|
@@ -568,7 +612,7 @@ func add_class_chars = (string pattern, int start, int end, ref StringBuilder sb
|
|
|
568
612
|
}
|
|
569
613
|
} else {
|
|
570
614
|
sb.append_char(pattern.at(i))
|
|
571
|
-
|
|
615
|
+
i += 1
|
|
572
616
|
}
|
|
573
617
|
}
|
|
574
618
|
}
|
|
@@ -613,7 +657,15 @@ func match_here = (
|
|
|
613
657
|
}
|
|
614
658
|
if (c as int) == 124 {
|
|
615
659
|
if depth == 0 {
|
|
616
|
-
var int left = match_here(
|
|
660
|
+
var int left = match_here(
|
|
661
|
+
pattern,
|
|
662
|
+
pat_pos,
|
|
663
|
+
i,
|
|
664
|
+
input,
|
|
665
|
+
in_pos,
|
|
666
|
+
in_len,
|
|
667
|
+
ref caps,
|
|
668
|
+
)
|
|
617
669
|
if left >= 0 {
|
|
618
670
|
return left
|
|
619
671
|
}
|
|
@@ -655,7 +707,7 @@ func match_here = (
|
|
|
655
707
|
if a_end < pat_end {
|
|
656
708
|
var char q = pattern.at(a_end)
|
|
657
709
|
if q == '*' || q == '+' || q == '?' {
|
|
658
|
-
var
|
|
710
|
+
var lazy = 0
|
|
659
711
|
var int rest = a_end + 1
|
|
660
712
|
if rest < pat_end {
|
|
661
713
|
if pattern.at(rest) == '?' {
|
|
@@ -664,12 +716,47 @@ func match_here = (
|
|
|
664
716
|
}
|
|
665
717
|
}
|
|
666
718
|
if q == '*' {
|
|
667
|
-
return match_star(
|
|
719
|
+
return match_star(
|
|
720
|
+
pattern,
|
|
721
|
+
pat_pos,
|
|
722
|
+
a_end,
|
|
723
|
+
rest,
|
|
724
|
+
pat_end,
|
|
725
|
+
input,
|
|
726
|
+
in_pos,
|
|
727
|
+
0,
|
|
728
|
+
lazy,
|
|
729
|
+
in_len,
|
|
730
|
+
ref caps,
|
|
731
|
+
)
|
|
668
732
|
}
|
|
669
733
|
if q == '+' {
|
|
670
|
-
return match_star(
|
|
734
|
+
return match_star(
|
|
735
|
+
pattern,
|
|
736
|
+
pat_pos,
|
|
737
|
+
a_end,
|
|
738
|
+
rest,
|
|
739
|
+
pat_end,
|
|
740
|
+
input,
|
|
741
|
+
in_pos,
|
|
742
|
+
1,
|
|
743
|
+
lazy,
|
|
744
|
+
in_len,
|
|
745
|
+
ref caps,
|
|
746
|
+
)
|
|
671
747
|
}
|
|
672
|
-
return match_optional(
|
|
748
|
+
return match_optional(
|
|
749
|
+
pattern,
|
|
750
|
+
pat_pos,
|
|
751
|
+
a_end,
|
|
752
|
+
rest,
|
|
753
|
+
pat_end,
|
|
754
|
+
input,
|
|
755
|
+
in_pos,
|
|
756
|
+
lazy,
|
|
757
|
+
in_len,
|
|
758
|
+
ref caps,
|
|
759
|
+
)
|
|
673
760
|
}
|
|
674
761
|
}
|
|
675
762
|
|
|
@@ -768,7 +855,15 @@ func match_single_atom = (
|
|
|
768
855
|
old_start = caps.at(at + 1)
|
|
769
856
|
old_end = caps.at(at + 2)
|
|
770
857
|
}
|
|
771
|
-
var int next = match_here(
|
|
858
|
+
var int next = match_here(
|
|
859
|
+
pattern,
|
|
860
|
+
atom_start + 1,
|
|
861
|
+
atom_end - 1,
|
|
862
|
+
input,
|
|
863
|
+
in_pos,
|
|
864
|
+
in_len,
|
|
865
|
+
ref caps,
|
|
866
|
+
)
|
|
772
867
|
if next >= 0 {
|
|
773
868
|
record_capture(ref caps, g, in_pos, next)
|
|
774
869
|
return next
|
|
@@ -958,7 +1053,15 @@ func match_star_group = (
|
|
|
958
1053
|
return stop
|
|
959
1054
|
}
|
|
960
1055
|
}
|
|
961
|
-
var int next = match_single_atom(
|
|
1056
|
+
var int next = match_single_atom(
|
|
1057
|
+
pattern,
|
|
1058
|
+
atom_start,
|
|
1059
|
+
atom_end,
|
|
1060
|
+
input,
|
|
1061
|
+
in_pos,
|
|
1062
|
+
in_len,
|
|
1063
|
+
ref caps,
|
|
1064
|
+
)
|
|
962
1065
|
if next >= 0 {
|
|
963
1066
|
if next != in_pos {
|
|
964
1067
|
var new_min = 0
|
|
@@ -1035,7 +1138,15 @@ func match_optional = (
|
|
|
1035
1138
|
if skip >= 0 {
|
|
1036
1139
|
return skip
|
|
1037
1140
|
}
|
|
1038
|
-
var int next = match_single_atom(
|
|
1141
|
+
var int next = match_single_atom(
|
|
1142
|
+
pattern,
|
|
1143
|
+
atom_start,
|
|
1144
|
+
atom_end,
|
|
1145
|
+
input,
|
|
1146
|
+
in_pos,
|
|
1147
|
+
in_len,
|
|
1148
|
+
ref caps,
|
|
1149
|
+
)
|
|
1039
1150
|
if next >= 0 {
|
|
1040
1151
|
return match_here(pattern, rest, pat_end, input, next, in_len, ref caps)
|
|
1041
1152
|
}
|
|
@@ -1136,8 +1247,8 @@ func char_in_class = (
|
|
|
1136
1247
|
// groups).
|
|
1137
1248
|
func capture_count = (string pattern, out int) {
|
|
1138
1249
|
var int n = pattern.length
|
|
1139
|
-
var
|
|
1140
|
-
var
|
|
1250
|
+
var count = 0
|
|
1251
|
+
var i = 0
|
|
1141
1252
|
while i < n; i += 1 {
|
|
1142
1253
|
var char c = pattern.at(i)
|
|
1143
1254
|
if (c as int) == 92 {
|
|
@@ -1159,8 +1270,8 @@ func capture_count = (string pattern, out int) {
|
|
|
1159
1270
|
// open_pos: one more than the number of capture opens before it in the
|
|
1160
1271
|
// pattern (the whole match is group 0).
|
|
1161
1272
|
func group_index_at = (string pattern, int open_pos, out int) {
|
|
1162
|
-
var
|
|
1163
|
-
var
|
|
1273
|
+
var count = 0
|
|
1274
|
+
var i = 0
|
|
1164
1275
|
while i < open_pos; i += 1 {
|
|
1165
1276
|
var char c = pattern.at(i)
|
|
1166
1277
|
if (c as int) == 92 {
|
|
@@ -1198,7 +1309,7 @@ func record_capture = (ref List<int> caps, int group, int start, int end) {
|
|
|
1198
1309
|
// has no entry. Entries are (group, start, end) triples.
|
|
1199
1310
|
func last_capture_index = (List<int> caps, int group, out int) {
|
|
1200
1311
|
var int best = -1
|
|
1201
|
-
var
|
|
1312
|
+
var i = 0
|
|
1202
1313
|
while i < caps.length; i += 3 {
|
|
1203
1314
|
if caps.at(i) == group {
|
|
1204
1315
|
best = i
|
|
@@ -1211,11 +1322,11 @@ func last_capture_index = (List<int> caps, int group, out int) {
|
|
|
1211
1322
|
* A Regex.find result: whether the pattern matched, where, and what
|
|
1212
1323
|
**/
|
|
1213
1324
|
pub struct RegexMatch {
|
|
1214
|
-
var
|
|
1215
|
-
var
|
|
1216
|
-
var
|
|
1217
|
-
var
|
|
1218
|
-
var
|
|
1325
|
+
var found = false
|
|
1326
|
+
var start = 0
|
|
1327
|
+
var end = 0
|
|
1328
|
+
var length = 0
|
|
1329
|
+
var text = ""
|
|
1219
1330
|
}
|
|
1220
1331
|
|
|
1221
1332
|
// Whether the escaped character c (the byte after a backslash) is a
|
|
@@ -1233,7 +1344,14 @@ func backref_group = (char c, out int) {
|
|
|
1233
1344
|
// Match the text capture group `group` last matched, at in_pos. Returns the
|
|
1234
1345
|
// position after the span, or -1 when the group has no span here (it did not
|
|
1235
1346
|
// participate) or the text differs.
|
|
1236
|
-
func match_backref = (
|
|
1347
|
+
func match_backref = (
|
|
1348
|
+
string input,
|
|
1349
|
+
int group,
|
|
1350
|
+
int in_pos,
|
|
1351
|
+
int in_len,
|
|
1352
|
+
ref List<int> caps,
|
|
1353
|
+
out int
|
|
1354
|
+
) {
|
|
1237
1355
|
var int at = last_capture_index(caps, group)
|
|
1238
1356
|
if at < 0 {
|
|
1239
1357
|
return -1
|
|
@@ -1246,7 +1364,7 @@ func match_backref = (string input, int group, int in_pos, int in_len, ref List<
|
|
|
1246
1364
|
if in_pos + len > in_len {
|
|
1247
1365
|
return -1
|
|
1248
1366
|
}
|
|
1249
|
-
var
|
|
1367
|
+
var i = 0
|
|
1250
1368
|
while i < len; i += 1 {
|
|
1251
1369
|
if input.at(in_pos + i) != input.at(s + i) {
|
|
1252
1370
|
return -1
|
|
@@ -1372,22 +1490,22 @@ func shorthand_members = (char c, ref StringBuilder sb, out int) {
|
|
|
1372
1490
|
return -1
|
|
1373
1491
|
}
|
|
1374
1492
|
if kind == 100 {
|
|
1375
|
-
var
|
|
1493
|
+
var code = 48
|
|
1376
1494
|
while code <= 57; code += 1 {
|
|
1377
1495
|
sb.append_char(code as char)
|
|
1378
1496
|
}
|
|
1379
1497
|
return 0
|
|
1380
1498
|
}
|
|
1381
1499
|
if kind == 119 {
|
|
1382
|
-
var
|
|
1500
|
+
var code = 48
|
|
1383
1501
|
while code <= 57; code += 1 {
|
|
1384
1502
|
sb.append_char(code as char)
|
|
1385
1503
|
}
|
|
1386
|
-
var
|
|
1504
|
+
var upper = 65
|
|
1387
1505
|
while upper <= 90; upper += 1 {
|
|
1388
1506
|
sb.append_char(upper as char)
|
|
1389
1507
|
}
|
|
1390
|
-
var
|
|
1508
|
+
var lower = 97
|
|
1391
1509
|
while lower <= 122; lower += 1 {
|
|
1392
1510
|
sb.append_char(lower as char)
|
|
1393
1511
|
}
|
|
@@ -1395,7 +1513,7 @@ func shorthand_members = (char c, ref StringBuilder sb, out int) {
|
|
|
1395
1513
|
return 0
|
|
1396
1514
|
}
|
|
1397
1515
|
// \s: tab, LF, VT, FF, CR, space
|
|
1398
|
-
var
|
|
1516
|
+
var w = 9
|
|
1399
1517
|
while w <= 13; w += 1 {
|
|
1400
1518
|
sb.append_char(w as char)
|
|
1401
1519
|
}
|
package/core/System/Text/Utf8.nm
CHANGED
|
@@ -45,8 +45,8 @@ pub struct Utf8 {
|
|
|
45
45
|
if b0 < 0xF0 {
|
|
46
46
|
// E0 forbids second bytes below A0 (overlong); ED forbids
|
|
47
47
|
// AA-BF (surrogate halves).
|
|
48
|
-
var
|
|
49
|
-
var
|
|
48
|
+
var lo = 0x80
|
|
49
|
+
var hi = 0xBF
|
|
50
50
|
if b0 == 0xE0 {
|
|
51
51
|
lo = 0xA0
|
|
52
52
|
}
|
|
@@ -68,8 +68,8 @@ pub struct Utf8 {
|
|
|
68
68
|
}
|
|
69
69
|
// F0 forbids second bytes below 90 (overlong); F4 forbids above 8F
|
|
70
70
|
// (past U+10FFFF).
|
|
71
|
-
var
|
|
72
|
-
var
|
|
71
|
+
var lo = 0x80
|
|
72
|
+
var hi = 0xBF
|
|
73
73
|
if b0 == 0xF0 {
|
|
74
74
|
lo = 0x90
|
|
75
75
|
}
|
|
@@ -138,8 +138,8 @@ pub struct Utf8 {
|
|
|
138
138
|
// Count the code points in text (one pass; malformed bytes each count
|
|
139
139
|
// as one U+FFFD).
|
|
140
140
|
pub func char_count = (view string text, out int) {
|
|
141
|
-
var
|
|
142
|
-
var
|
|
141
|
+
var n = 0
|
|
142
|
+
var pos = 0
|
|
143
143
|
var int len = text.length
|
|
144
144
|
while pos < len {
|
|
145
145
|
pos = pos + Utf8.width_at(text, pos)
|
package/core/System/Thread.nm
CHANGED
|
@@ -21,7 +21,9 @@
|
|
|
21
21
|
// The pool infrastructure (types, worker loop, submit, shutdown, cancel
|
|
22
22
|
// thread-local) lives in build_spawn_node and is emitted as file-scope C
|
|
23
23
|
// on the first spawn — not in Thread's own methods — so that Thread can be
|
|
24
|
-
// generic without duplicating the pool per instantiation.
|
|
24
|
+
// generic without duplicating the pool per instantiation. The construction
|
|
25
|
+
// (`Thread(fn(args))`, the deferred-call special form) is compiler-emitted;
|
|
26
|
+
// `.start()` / `.detach()` are regular methods over the runtime seam.
|
|
25
27
|
//
|
|
26
28
|
// MUST-START: a Thread value that is destroyed without ever being started
|
|
27
29
|
// is a programming error — #destroy reports it and aborts. start/detach
|
|
@@ -30,10 +32,23 @@
|
|
|
30
32
|
//
|
|
31
33
|
// See ASYNC.md for the design.
|
|
32
34
|
|
|
35
|
+
// The launch surface's return type (ASYNC.md: the dependency is
|
|
36
|
+
// declared by the library file, not inferred from user-source tokens).
|
|
37
|
+
import Task
|
|
38
|
+
|
|
33
39
|
/**
|
|
34
40
|
* A deferred function call, startable on the shared worker pool
|
|
35
41
|
**/
|
|
36
|
-
pub class Thread<T> : Sendable {
|
|
42
|
+
pub class Thread<T> : Sendable, Spawnable<T> {
|
|
43
|
+
// The construction hook (ASYNC.md): declares this class's
|
|
44
|
+
// construction as the deferred-call special form — the single argument
|
|
45
|
+
// is a call (args packed eagerly, call deferred) or a zero-arg function
|
|
46
|
+
// value. The compiler matches this member, never the class name; the
|
|
47
|
+
// construction itself is compiler-generated (pack the args, allocate the
|
|
48
|
+
// future machinery, build the instance), so the body is a declaration.
|
|
49
|
+
#spawn = () {
|
|
50
|
+
}
|
|
51
|
+
|
|
37
52
|
// The packed task closure (a `struct nomen_closure *`); owned by this
|
|
38
53
|
// value until start/detach/nursery.start transfers it to the runtime.
|
|
39
54
|
pub var uint64 task = 0
|
|
@@ -43,6 +58,107 @@ pub class Thread<T> : Sendable {
|
|
|
43
58
|
pub var uint64 future = 0
|
|
44
59
|
// Must-start flag: set by .start() / .detach() / nursery.start.
|
|
45
60
|
pub var started = false
|
|
61
|
+
// LEXICAL NURSERY CAPTURE (ASYNC.md): the tracking slots of the
|
|
62
|
+
// `async` block that lexically encloses the CONSTRUCTION, or 0 when the
|
|
63
|
+
// construction is outside any block. `.start()` registers the future
|
|
64
|
+
// through these stored pointers, so registration follows the scope that
|
|
65
|
+
// created the deferred call — not the one that happens to start it — and
|
|
66
|
+
// works through an indirect receiver. The pointers address the block's
|
|
67
|
+
// own futures list slots (the block cannot exit until every registered
|
|
68
|
+
// task has finished, so a started-before-exit task never registers into
|
|
69
|
+
// dead storage).
|
|
70
|
+
pub var uint64 nursery_futures = 0
|
|
71
|
+
pub var uint64 nursery_count = 0
|
|
72
|
+
pub var uint64 nursery_cap = 0
|
|
73
|
+
|
|
74
|
+
// Start the deferred call on the shared worker pool and yield the
|
|
75
|
+
// Task<T> handle. Consumes the instance's launch machinery (submit, then
|
|
76
|
+
// register with the construction-time nursery, if any) and zeroes the
|
|
77
|
+
// fields, so #destroy becomes a no-op afterwards. The future's refs
|
|
78
|
+
// account for the trampoline, the returned handle, and the nursery (when
|
|
79
|
+
// captured).
|
|
80
|
+
pub func start = (ref self, move out Task<T>) {
|
|
81
|
+
```
|
|
82
|
+
#arch: c
|
|
83
|
+
struct nomen_future *_f = (struct nomen_future *)self->future;
|
|
84
|
+
_f->refs = self->nursery_futures ? 3 : 2;
|
|
85
|
+
__nomen_pool_submit((struct nomen_closure *)self->task);
|
|
86
|
+
if (self->nursery_futures) {
|
|
87
|
+
__nomen_nursery_track((void **)self->nursery_futures, (int *)self->nursery_count, (int *)self->nursery_cap, _f);
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
```
|
|
91
|
+
#arch: aarch64
|
|
92
|
+
// x19 = self. f = self->future (#32); nursery_futures at #48.
|
|
93
|
+
ldr x0, [x19, #32]
|
|
94
|
+
ldr x1, [x19, #48]
|
|
95
|
+
mov w2, #2
|
|
96
|
+
cbz x1, .Lnomen_start_refs
|
|
97
|
+
mov w2, #3
|
|
98
|
+
.Lnomen_start_refs:
|
|
99
|
+
str w2, [x0, #116]
|
|
100
|
+
// __nomen_pool_submit(self->task)
|
|
101
|
+
ldr x0, [x19, #8]
|
|
102
|
+
bl ___nomen_pool_submit
|
|
103
|
+
// if (self->nursery_futures) __nomen_nursery_track(futures, count, cap, f)
|
|
104
|
+
ldr x0, [x19, #48]
|
|
105
|
+
cbz x0, .Lnomen_start_notrack
|
|
106
|
+
ldr x1, [x19, #56]
|
|
107
|
+
ldr x2, [x19, #64]
|
|
108
|
+
ldr x3, [x19, #32]
|
|
109
|
+
bl ___nomen_nursery_track
|
|
110
|
+
.Lnomen_start_notrack:
|
|
111
|
+
```
|
|
112
|
+
// The handle is a fresh heap Task built through the internal #init;
|
|
113
|
+
// the caller owns it (a discarded statement form is reclaimed at
|
|
114
|
+
// scope exit, releasing its refs share).
|
|
115
|
+
var t = Task<T>()
|
|
116
|
+
t.result_slot = self.result_slot
|
|
117
|
+
t.cancel_flag = self.cancel_flag
|
|
118
|
+
t.future = self.future
|
|
119
|
+
// Transfer the handles out of the instance: the runtime owns
|
|
120
|
+
// everything now, and #destroy must not touch (or must-start-abort).
|
|
121
|
+
self.task = 0
|
|
122
|
+
self.future = 0
|
|
123
|
+
self.result_slot = 0
|
|
124
|
+
self.cancel_flag = 0
|
|
125
|
+
self.started = true
|
|
126
|
+
return t
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Run the deferred call on a dedicated detached pthread — the daemon
|
|
130
|
+
// form. Never a pool worker, never joined; process exit kills it
|
|
131
|
+
// mid-execution BY DESIGN. The unused future (a Thread always carries
|
|
132
|
+
// the full machinery) is released — first detaching the closure from it,
|
|
133
|
+
// so the daemon runner owns the closure now.
|
|
134
|
+
pub func detach = (ref self) {
|
|
135
|
+
```
|
|
136
|
+
#arch: c
|
|
137
|
+
struct nomen_future *_uf = (struct nomen_future *)self->future;
|
|
138
|
+
if (_uf) {
|
|
139
|
+
_uf->owner_args = NULL;
|
|
140
|
+
__nomen_future_release(_uf);
|
|
141
|
+
}
|
|
142
|
+
__nomen_task_detach((struct nomen_closure *)self->task);
|
|
143
|
+
```
|
|
144
|
+
```
|
|
145
|
+
#arch: aarch64
|
|
146
|
+
// if (self->future) { owner_args = NULL; release it }
|
|
147
|
+
ldr x0, [x19, #32]
|
|
148
|
+
cbz x0, .Lnomen_detach_nofut
|
|
149
|
+
str xzr, [x0, #136]
|
|
150
|
+
bl ___nomen_future_release
|
|
151
|
+
.Lnomen_detach_nofut:
|
|
152
|
+
// __nomen_task_detach(self->task)
|
|
153
|
+
ldr x0, [x19, #8]
|
|
154
|
+
bl ___nomen_task_detach
|
|
155
|
+
```
|
|
156
|
+
self.task = 0
|
|
157
|
+
self.future = 0
|
|
158
|
+
self.result_slot = 0
|
|
159
|
+
self.cancel_flag = 0
|
|
160
|
+
self.started = true
|
|
161
|
+
}
|
|
46
162
|
|
|
47
163
|
// Destroying an unstarted Thread is a programming error (the wrapped
|
|
48
164
|
// call would silently never run) — report it and abort. A started
|
package/core/System/Tree.nm
CHANGED