nomen-lang 0.0.7 → 0.0.8

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.
Files changed (118) hide show
  1. package/NOMEN_AGENTS.md +235 -0
  2. package/core/System/Ansi.nm +150 -0
  3. package/core/System/Array.nm +358 -0
  4. package/core/System/BigInt.nm +887 -0
  5. package/core/System/Buffer.nm +526 -0
  6. package/core/System/Channel.nm +198 -0
  7. package/core/System/ClassBuffer.nm +229 -0
  8. package/core/System/Console.nm +211 -0
  9. package/core/System/Controls/Button.nm +138 -0
  10. package/core/System/Controls/CheckBox.nm +158 -0
  11. package/core/System/Controls/Container.nm +1562 -0
  12. package/core/System/Controls/Control.nm +28 -0
  13. package/core/System/Controls/Geometry.nm +101 -0
  14. package/core/System/Controls/Layout.nm +228 -0
  15. package/core/System/Controls/LayoutParams.nm +50 -0
  16. package/core/System/Controls/Text.nm +129 -0
  17. package/core/System/Controls/TextBox.nm +157 -0
  18. package/core/System/Controls/Window.nm +280 -0
  19. package/core/System/Disposable.nm +8 -0
  20. package/core/System/Enumerable.nm +10 -0
  21. package/core/System/Equatable.nm +10 -0
  22. package/core/System/Graph.nm +68 -0
  23. package/core/System/Hashable.nm +13 -0
  24. package/core/System/Init.nm +21 -0
  25. package/core/System/LinkedList.nm +64 -0
  26. package/core/System/List.nm +44 -0
  27. package/core/System/Map.nm +153 -0
  28. package/core/System/Math.nm +54 -0
  29. package/core/System/Mutex.nm +78 -0
  30. package/core/System/Nursery.nm +27 -0
  31. package/core/System/Sendable.nm +9 -0
  32. package/core/System/Set.nm +97 -0
  33. package/core/System/Stream/Directory.nm +176 -0
  34. package/core/System/Stream/File.nm +432 -0
  35. package/core/System/Stream/Http.nm +453 -0
  36. package/core/System/String.nm +154 -0
  37. package/core/System/StringBuilder.nm +249 -0
  38. package/core/System/Stringable.nm +8 -0
  39. package/core/System/Task.nm +263 -0
  40. package/core/System/Test.nm +241 -0
  41. package/core/System/Text/Json.nm +377 -0
  42. package/core/System/Text/JsonTree.nm +187 -0
  43. package/core/System/Text/Regex.nm +655 -0
  44. package/core/System/Time.nm +112 -0
  45. package/core/System/Tree.nm +70 -0
  46. package/core/System/Viewable.nm +27 -0
  47. package/core/System/bool.nm +66 -0
  48. package/core/System/char.nm +42 -0
  49. package/core/System/float.nm +43 -0
  50. package/core/System/int.nm +54 -0
  51. package/core/System/int64.nm +42 -0
  52. package/core/System/int8.nm +42 -0
  53. package/core/System/uint.nm +42 -0
  54. package/core/System/uint64.nm +42 -0
  55. package/core/System/uint8.nm +42 -0
  56. package/core/docs/System/Ansi.md +40 -0
  57. package/core/docs/System/Array.md +16 -0
  58. package/core/docs/System/BigInt.md +30 -0
  59. package/core/docs/System/Buffer.md +31 -0
  60. package/core/docs/System/Channel.md +10 -0
  61. package/core/docs/System/ClassBuffer.md +15 -0
  62. package/core/docs/System/Console.md +13 -0
  63. package/core/docs/System/Controls/Button.md +12 -0
  64. package/core/docs/System/Controls/CheckBox.md +15 -0
  65. package/core/docs/System/Controls/Container.md +82 -0
  66. package/core/docs/System/Controls/Control.md +5 -0
  67. package/core/docs/System/Controls/Geometry.md +26 -0
  68. package/core/docs/System/Controls/Layout.md +37 -0
  69. package/core/docs/System/Controls/LayoutParams.md +13 -0
  70. package/core/docs/System/Controls/Text.md +12 -0
  71. package/core/docs/System/Controls/TextBox.md +13 -0
  72. package/core/docs/System/Controls/Window.md +20 -0
  73. package/core/docs/System/Disposable.md +5 -0
  74. package/core/docs/System/Enumerable.md +5 -0
  75. package/core/docs/System/Graph.md +17 -0
  76. package/core/docs/System/Init.md +9 -0
  77. package/core/docs/System/LinkedList.md +16 -0
  78. package/core/docs/System/List.md +13 -0
  79. package/core/docs/System/Map.md +14 -0
  80. package/core/docs/System/Math.md +11 -0
  81. package/core/docs/System/Mutex.md +10 -0
  82. package/core/docs/System/Nursery.md +5 -0
  83. package/core/docs/System/Sendable.md +5 -0
  84. package/core/docs/System/Set.md +14 -0
  85. package/core/docs/System/Stream/Directory.md +12 -0
  86. package/core/docs/System/Stream/File.md +21 -0
  87. package/core/docs/System/String.md +14 -0
  88. package/core/docs/System/StringBuilder.md +12 -0
  89. package/core/docs/System/Stringable.md +5 -0
  90. package/core/docs/System/Task.md +15 -0
  91. package/core/docs/System/Text/Json.md +12 -0
  92. package/core/docs/System/Text/JsonTree.md +21 -0
  93. package/core/docs/System/Text/Regex.md +12 -0
  94. package/core/docs/System/Time.md +11 -0
  95. package/core/docs/System/Tree.md +18 -0
  96. package/core/docs/System/Viewable.md +5 -0
  97. package/core/docs/System/bool.md +9 -0
  98. package/core/docs/System/char.md +9 -0
  99. package/core/docs/System/float.md +9 -0
  100. package/core/docs/System/int.md +10 -0
  101. package/core/docs/System/int64.md +9 -0
  102. package/core/docs/System/int8.md +9 -0
  103. package/core/docs/System/uint.md +9 -0
  104. package/core/docs/System/uint64.md +9 -0
  105. package/core/docs/System/uint8.md +9 -0
  106. package/core/package.jsonc +4 -0
  107. package/dist/index.mjs +417 -5490
  108. package/package.json +33 -30
  109. package/src/args.ts +171 -0
  110. package/src/index.ts +53 -104
  111. package/src/init.ts +128 -0
  112. package/src/test.ts +6 -2
  113. package/test/fixtures/build/calc.test +0 -0
  114. package/test/fixtures/build/calc.test.c +0 -969
  115. package/test/fixtures/build/main.h +0 -170
  116. package/test/fixtures/calc.test.nm +0 -32
  117. package/tsconfig.json +0 -21
  118. package/vite.config.ts +0 -7
@@ -0,0 +1,655 @@
1
+ // Regular expression support implemented in pure Nomen.
2
+ //
3
+ // `test` reports whether a pattern matches anywhere in the input; `match`
4
+ // returns the first matched substring (or "" when there is no match).
5
+ // Patterns use POSIX Extended Regular Expression syntax.
6
+
7
+ /**
8
+ * Regular-expression helpers (`test`/`match`/`count`/`replace_all`) using POSIX ERE syntax
9
+ **/
10
+ pub struct Regex {
11
+ // Test whether pattern matches anywhere in input.
12
+ pub func test = (string pattern, string input, out bool) {
13
+ var int pat_len = pattern.length
14
+ var int len = input.length
15
+ var string charset = first_byte_set(pattern, pat_len)
16
+ var int clen = charset.length
17
+ var i = 0
18
+ while i <= len; i += 1 {
19
+ if clen > 0 && i < len {
20
+ i = find_next_byte(input, i, len, charset)
21
+ if i > len {
22
+ break
23
+ }
24
+ }
25
+ if match_here(pattern, 0, pat_len, input, i, len) >= 0 {
26
+ return true
27
+ }
28
+ }
29
+ return false
30
+ }
31
+
32
+ // Return the first substring of input matching pattern, or "" if none.
33
+ pub func match = (string pattern, string input, out string) {
34
+ var match_result = ""
35
+ var int pat_len = pattern.length
36
+ var int len = input.length
37
+ var string charset = first_byte_set(pattern, pat_len)
38
+ var int clen = charset.length
39
+ var i = 0
40
+ while i <= len; i += 1 {
41
+ if clen > 0 && i < len {
42
+ i = find_next_byte(input, i, len, charset)
43
+ if i > len {
44
+ break
45
+ }
46
+ }
47
+ var int end = match_here(pattern, 0, pat_len, input, i, len)
48
+ if end >= 0 {
49
+ var int j = i
50
+ while j < end; j += 1 {
51
+ match_result = match_result + "\{input.at(j)}"
52
+ }
53
+ break
54
+ }
55
+ }
56
+ return match_result
57
+ }
58
+
59
+ // Count the number of non-overlapping occurrences of pattern in input.
60
+ pub func count = (string pattern, string input, out int) {
61
+ var int pat_len = pattern.length
62
+ var int len = input.length
63
+ var string charset = first_byte_set(pattern, pat_len)
64
+ var int clen = charset.length
65
+ var total = 0
66
+ var pos = 0
67
+ while pos < len {
68
+ if clen > 0 {
69
+ pos = find_next_byte(input, pos, len, charset)
70
+ if pos >= len {
71
+ break
72
+ }
73
+ }
74
+ var int end = match_here(pattern, 0, pat_len, input, pos, len)
75
+ if end >= 0 {
76
+ total += 1
77
+ if end == pos {
78
+ pos += 1
79
+ } else {
80
+ pos = end
81
+ }
82
+ } else {
83
+ pos += 1
84
+ }
85
+ }
86
+ return total
87
+ }
88
+
89
+ // Replace all non-overlapping occurrences of pattern in input with replacement.
90
+ pub func replace_all = (string pattern, string input, string replacement, out string) {
91
+ var sb = StringBuilder()
92
+ var int pat_len = pattern.length
93
+ var int len = input.length
94
+ var string charset = first_byte_set(pattern, pat_len)
95
+ var int clen = charset.length
96
+ var pos = 0
97
+ while pos < len {
98
+ if clen > 0 {
99
+ var int next = find_next_byte(input, pos, len, charset)
100
+ // Copy the bytes we're skipping over.
101
+ while pos < next {
102
+ sb.append_char(input.at(pos))
103
+ pos += 1
104
+ }
105
+ if pos >= len {
106
+ break
107
+ }
108
+ }
109
+ var int end = match_here(pattern, 0, pat_len, input, pos, len)
110
+ if end >= 0 {
111
+ sb.append_string(replacement)
112
+ if end == pos {
113
+ sb.append_char(input.at(pos))
114
+ pos += 1
115
+ } else {
116
+ pos = end
117
+ }
118
+ } else {
119
+ sb.append_char(input.at(pos))
120
+ pos += 1
121
+ }
122
+ }
123
+ return sb.to_string()
124
+ }
125
+ }
126
+
127
+ // Scan input[pos..len) for the next byte that appears in charset (null-terminated).
128
+ // Returns the index of the match, or len if none found / charset is empty.
129
+ func find_next_byte = (string input, int pos, int len, string charset, out int) {
130
+ ```
131
+ #arch: c
132
+ if (pos >= len || charset[0] == 0) return len;
133
+ char* found = strpbrk(input + pos, charset);
134
+ if (found == NULL) return len;
135
+ return (int)(found - input);
136
+ ```
137
+ ```
138
+ #arch: aarch64
139
+ // Free function: x0 = input, x1 = pos, x2 = len, x3 = charset
140
+ sub sp, sp, #32
141
+ str x0, [sp, #0] // input
142
+ str x2, [sp, #8] // len (default return)
143
+ cmp x1, x2
144
+ b.ge .Lfnb_default
145
+ ldrb w9, [x3]
146
+ cbz w9, .Lfnb_default
147
+ add x0, x0, x1 // x0 = input + pos
148
+ mov x1, x3 // x1 = charset
149
+ bl _strpbrk
150
+ cbz x0, .Lfnb_default
151
+ ldr x9, [sp, #0] // input
152
+ sub x0, x0, x9 // result = found - input
153
+ b .Lfnb_end
154
+ .Lfnb_default:
155
+ ldr x0, [sp, #8] // return len
156
+ .Lfnb_end:
157
+ add sp, sp, #32
158
+ ```
159
+ }
160
+
161
+ // Extract the set of possible first bytes from a pattern into a charset string.
162
+ // Returns "" if every byte is possible (e.g. pattern starts with '.', '^', '(').
163
+ func first_byte_set = (string pattern, int pat_len, out string) {
164
+ if pat_len == 0 {
165
+ return ""
166
+ }
167
+ var sb = StringBuilder()
168
+ collect_first_bytes(pattern, 0, pat_len, ref sb)
169
+ return sb.to_string()
170
+ }
171
+
172
+ // Walk top-level alternatives of pattern[pat_pos..pat_end), appending the first
173
+ // byte(s) of each alternative to sb. Alternations at depth 0 are split on '|'.
174
+ func collect_first_bytes = (string pattern, int pat_pos, int pat_end, ref StringBuilder sb) {
175
+ var depth = 0
176
+ var int i = pat_pos
177
+ var int alt_start = pat_pos
178
+ while i < pat_end {
179
+ var char c = pattern.at(i)
180
+ switch {
181
+ case (c as int) == 92 {
182
+ // escaped literal: '\x' → first byte is x
183
+ if i == alt_start {
184
+ if i + 1 < pat_end {
185
+ sb.append_char(pattern.at(i + 1))
186
+ }
187
+ }
188
+ i += 2
189
+ }
190
+ case c == '[' {
191
+ if i == alt_start {
192
+ var int a_end = atom_end_pos(pattern, i, pat_end)
193
+ add_class_chars(pattern, i + 1, a_end - 1, ref sb)
194
+ }
195
+ i = atom_end_pos(pattern, i, pat_end)
196
+ }
197
+ case c == '(' {
198
+ depth = depth + 1
199
+ i += 1
200
+ }
201
+ case c == ')' {
202
+ if depth > 0 {
203
+ depth -= 1
204
+ }
205
+ i += 1
206
+ }
207
+ case (c as int) == 124 {
208
+ if depth == 0 {
209
+ alt_start = i + 1
210
+ }
211
+ i += 1
212
+ }
213
+ case i == alt_start && depth == 0 && c != '^' && c != '$' && c != '.' {
214
+ sb.append_char(c)
215
+ i += 1
216
+ }
217
+ else {
218
+ i += 1
219
+ }
220
+ }
221
+ }
222
+ }
223
+
224
+ // Append the members of character class [start..end) to sb. Ranges (a-z) are
225
+ // expanded; negation (^) is ignored (we want all candidates).
226
+ func add_class_chars = (string pattern, int start, int end, ref StringBuilder sb) {
227
+ var int cs = start
228
+ if cs < end {
229
+ if pattern.at(cs) == '^' {
230
+ cs += 1
231
+ }
232
+ }
233
+ var int i = cs
234
+ while i < end {
235
+ if i + 2 < end {
236
+ if pattern.at(i + 1) == '-' {
237
+ var int lo = pattern.at(i) as int
238
+ var int hi = pattern.at(i + 2) as int
239
+ while lo <= hi {
240
+ sb.append_char(lo as char)
241
+ lo += 1
242
+ }
243
+ i += 3
244
+ } else {
245
+ sb.append_char(pattern.at(i))
246
+ i += 1
247
+ }
248
+ } else {
249
+ sb.append_char(pattern.at(i))
250
+ i += 1
251
+ }
252
+ }
253
+ }
254
+
255
+ // Match pattern[pat_pos..pat_end) against input starting at in_pos.
256
+ // Returns the end position in input after a successful match, or -1.
257
+ func match_here = (
258
+ string pattern,
259
+ int pat_pos,
260
+ int pat_end,
261
+ string input,
262
+ int in_pos,
263
+ int in_len,
264
+ out int
265
+ ) {
266
+ // Handle top-level alternation: a '|' at bracket/paren depth 0 splits the
267
+ // range into alternatives. Char classes and escapes are skipped so a '|' or
268
+ // '\|' inside them is not mistaken for an alternation operator.
269
+ var depth = 0
270
+ var int i = pat_pos
271
+ while i < pat_end {
272
+ var char c = pattern.at(i)
273
+ if (c as int) == 92 {
274
+ i += 2
275
+ } else {
276
+ if c == '[' {
277
+ i = atom_end_pos(pattern, i, pat_end)
278
+ } else {
279
+ if c == '(' {
280
+ depth += 1
281
+ }
282
+ if c == ')' {
283
+ if depth > 0 {
284
+ depth -= 1
285
+ }
286
+ }
287
+ if (c as int) == 124 {
288
+ if depth == 0 {
289
+ var int left = match_here(pattern, pat_pos, i, input, in_pos, in_len)
290
+ if left >= 0 {
291
+ return left
292
+ }
293
+ return match_here(pattern, i + 1, pat_end, input, in_pos, in_len)
294
+ }
295
+ }
296
+ i += 1
297
+ }
298
+ }
299
+ }
300
+
301
+ // Pattern exhausted: match succeeds at the current position.
302
+ if pat_pos >= pat_end {
303
+ return in_pos
304
+ }
305
+
306
+ var char pc = pattern.at(pat_pos)
307
+
308
+ // Anchors (zero-width assertions).
309
+ if pc == '^' {
310
+ if in_pos != 0 {
311
+ return -1
312
+ }
313
+ return match_here(pattern, pat_pos + 1, pat_end, input, in_pos, in_len)
314
+ }
315
+ if pc == '$' {
316
+ if in_pos != in_len {
317
+ return -1
318
+ }
319
+ return match_here(pattern, pat_pos + 1, pat_end, input, in_pos, in_len)
320
+ }
321
+
322
+ // Determine the bounds of the current atom.
323
+ var int a_end = atom_end_pos(pattern, pat_pos, pat_end)
324
+
325
+ // Check for a quantifier following the atom.
326
+ if a_end < pat_end {
327
+ var char q = pattern.at(a_end)
328
+ if q == '*' {
329
+ return match_star(pattern, pat_pos, a_end, pat_end, input, in_pos, 0, in_len)
330
+ }
331
+ if q == '+' {
332
+ return match_star(pattern, pat_pos, a_end, pat_end, input, in_pos, 1, in_len)
333
+ }
334
+ if q == '?' {
335
+ return match_optional(pattern, pat_pos, a_end, pat_end, input, in_pos, in_len)
336
+ }
337
+ }
338
+
339
+ // No quantifier: match the atom once, then the rest of the pattern.
340
+ var int next = match_single_atom(pattern, pat_pos, a_end, input, in_pos, in_len)
341
+ if next < 0 {
342
+ return -1
343
+ }
344
+ return match_here(pattern, a_end, pat_end, input, next, in_len)
345
+ }
346
+
347
+ // Return the position just past the atom that starts at pat_pos. Handles
348
+ // character classes [...], groups (...), and escaped characters (\x).
349
+ func atom_end_pos = (string pattern, int pat_pos, int pat_end, out int) {
350
+ var char pc = pattern.at(pat_pos)
351
+ if pc == '[' {
352
+ var int cs = pat_pos + 1
353
+ if cs < pat_end {
354
+ if pattern.at(cs) == '^' {
355
+ cs += 1
356
+ }
357
+ }
358
+ while cs < pat_end; cs += 1 {
359
+ if pattern.at(cs) == ']' {
360
+ return cs + 1
361
+ }
362
+ }
363
+ return pat_end
364
+ }
365
+ if pc == '(' {
366
+ var d = 1
367
+ var int j = pat_pos + 1
368
+ while j < pat_end; j += 1 {
369
+ var char c = pattern.at(j)
370
+ if c == '(' {
371
+ d += 1
372
+ }
373
+ if c == ')' {
374
+ d -= 1
375
+ if d == 0 {
376
+ return j + 1
377
+ }
378
+ }
379
+ }
380
+ return pat_end
381
+ }
382
+ if (pc as int) == 92 {
383
+ if pat_pos + 1 < pat_end {
384
+ return pat_pos + 2
385
+ }
386
+ return pat_pos + 1
387
+ }
388
+ return pat_pos + 1
389
+ }
390
+
391
+ // Match the atom [atom_start..atom_end) exactly once at in_pos.
392
+ // Returns the new input position after consuming the atom, or -1.
393
+ func match_single_atom = (
394
+ string pattern,
395
+ int atom_start,
396
+ int atom_end,
397
+ string input,
398
+ int in_pos,
399
+ int in_len,
400
+ out int
401
+ ) {
402
+ var char pc = pattern.at(atom_start)
403
+
404
+ if pc == '.' {
405
+ if in_pos < in_len {
406
+ if (input.at(in_pos) as int) != 10 {
407
+ return in_pos + 1
408
+ }
409
+ }
410
+ return -1
411
+ }
412
+ if pc == '[' {
413
+ if in_pos < in_len {
414
+ if char_in_class(pattern, atom_start, atom_end, input, in_pos, in_len) {
415
+ return in_pos + 1
416
+ }
417
+ }
418
+ return -1
419
+ }
420
+ if pc == '(' {
421
+ // Match the group's inner content.
422
+ return match_here(pattern, atom_start + 1, atom_end - 1, input, in_pos, in_len)
423
+ }
424
+ if (pc as int) == 92 {
425
+ if atom_start + 1 < atom_end {
426
+ var char ec = pattern.at(atom_start + 1)
427
+ if in_pos < in_len {
428
+ if input.at(in_pos) == ec {
429
+ return in_pos + 1
430
+ }
431
+ }
432
+ }
433
+ return -1
434
+ }
435
+ if in_pos < in_len {
436
+ if input.at(in_pos) == pc {
437
+ return in_pos + 1
438
+ }
439
+ }
440
+ return -1
441
+ }
442
+
443
+ // Report whether input.at(pos) matches the single-character atom at
444
+ // [atom_start..atom_end). Used by the greedy star matcher.
445
+ func char_matches_atom = (
446
+ string pattern,
447
+ int atom_start,
448
+ int atom_end,
449
+ string input,
450
+ int pos,
451
+ int in_len,
452
+ out bool
453
+ ) {
454
+ var char pc = pattern.at(atom_start)
455
+ var char ic = input.at(pos)
456
+ if pc == '.' {
457
+ if (ic as int) == 10 {
458
+ return false
459
+ }
460
+ return true
461
+ }
462
+ if pc == '[' {
463
+ return char_in_class(pattern, atom_start, atom_end, input, pos, in_len)
464
+ }
465
+ if (pc as int) == 92 {
466
+ if atom_start + 1 < atom_end {
467
+ return ic == pattern.at(atom_start + 1)
468
+ }
469
+ return false
470
+ }
471
+ return pc == ic
472
+ }
473
+
474
+ // Greedily match the atom [atom_start..atom_end) followed by the rest of the
475
+ // pattern pattern[atom_end+1..pat_end). min_count is 0 for '*' and 1 for '+'.
476
+ // Single-character atoms are handled iteratively; groups recurse.
477
+ func match_star = (
478
+ string pattern,
479
+ int atom_start,
480
+ int atom_end,
481
+ int pat_end,
482
+ string input,
483
+ int in_pos,
484
+ int min_count,
485
+ int in_len,
486
+ out int
487
+ ) {
488
+ var char pc = pattern.at(atom_start)
489
+ if pc == '(' {
490
+ return match_star_group(
491
+ pattern,
492
+ atom_start,
493
+ atom_end,
494
+ pat_end,
495
+ input,
496
+ in_pos,
497
+ min_count,
498
+ in_len,
499
+ )
500
+ }
501
+
502
+ // Greedily consume the longest run of matching characters.
503
+ var int max_pos = in_pos
504
+ while max_pos < in_len {
505
+ if char_matches_atom(pattern, atom_start, atom_end, input, max_pos, in_len) {
506
+ max_pos += 1
507
+ } else {
508
+ break
509
+ }
510
+ }
511
+ // Backtrack from the longest match down to the minimum, trying the rest.
512
+ var int try_pos = max_pos
513
+ while try_pos >= in_pos + min_count; try_pos -= 1 {
514
+ var int r = match_here(pattern, atom_end + 1, pat_end, input, try_pos, in_len)
515
+ if r >= 0 {
516
+ return r
517
+ }
518
+ }
519
+ return -1
520
+ }
521
+
522
+ // Recursive greedy backtracking for group atoms quantified with '*' or '+'.
523
+ func match_star_group = (
524
+ string pattern,
525
+ int atom_start,
526
+ int atom_end,
527
+ int pat_end,
528
+ string input,
529
+ int in_pos,
530
+ int min_count,
531
+ int in_len,
532
+ out int
533
+ ) {
534
+ // Try matching the group one more time (greedy).
535
+ var int next = match_single_atom(pattern, atom_start, atom_end, input, in_pos, in_len)
536
+ if next >= 0 {
537
+ if next != in_pos {
538
+ var new_min = 0
539
+ if min_count > 0 {
540
+ new_min = min_count - 1
541
+ }
542
+ var int r = match_star_group(
543
+ pattern,
544
+ atom_start,
545
+ atom_end,
546
+ pat_end,
547
+ input,
548
+ next,
549
+ new_min,
550
+ in_len,
551
+ )
552
+ if r >= 0 {
553
+ return r
554
+ }
555
+ }
556
+ }
557
+ // Stop repeating and try the rest of the pattern, if the minimum is met.
558
+ if min_count <= 0 {
559
+ return match_here(pattern, atom_end + 1, pat_end, input, in_pos, in_len)
560
+ }
561
+ return -1
562
+ }
563
+
564
+ // Match the atom optionally ('?'), preferring to include it.
565
+ func match_optional = (
566
+ string pattern,
567
+ int atom_start,
568
+ int atom_end,
569
+ int pat_end,
570
+ string input,
571
+ int in_pos,
572
+ int in_len,
573
+ out int
574
+ ) {
575
+ var int next = match_single_atom(pattern, atom_start, atom_end, input, in_pos, in_len)
576
+ if next >= 0 {
577
+ var int r = match_here(pattern, atom_end + 1, pat_end, input, next, in_len)
578
+ if r >= 0 {
579
+ return r
580
+ }
581
+ }
582
+ return match_here(pattern, atom_end + 1, pat_end, input, in_pos, in_len)
583
+ }
584
+
585
+ // Report whether input.at(in_pos) belongs to the character class whose '[' is
586
+ // at open_pos and whose ']' is at atom_end - 1 (atom_end is just after ']').
587
+ func char_in_class = (
588
+ string pattern,
589
+ int open_pos,
590
+ int atom_end,
591
+ string input,
592
+ int in_pos,
593
+ int in_len,
594
+ out bool
595
+ ) {
596
+ if in_pos >= in_len {
597
+ return false
598
+ }
599
+ var char ic = input.at(in_pos)
600
+
601
+ var int content_start = open_pos + 1
602
+ var negated = false
603
+ if content_start < atom_end {
604
+ if pattern.at(content_start) == '^' {
605
+ negated = true
606
+ content_start += 1
607
+ }
608
+ }
609
+ var int content_end = atom_end - 1
610
+
611
+ var found = false
612
+ var int i = content_start
613
+ while i < content_end {
614
+ var char c = pattern.at(i)
615
+ if (c as int) == 92 {
616
+ if i + 1 < content_end {
617
+ if ic == pattern.at(i + 1) {
618
+ found = true
619
+ }
620
+ }
621
+ i = i + 2
622
+ } else {
623
+ if i + 2 < content_end {
624
+ if pattern.at(i + 1) == '-' {
625
+ var char lo = c
626
+ var char hi = pattern.at(i + 2)
627
+ if (ic as int) >= (lo as int) {
628
+ if (ic as int) <= (hi as int) {
629
+ found = true
630
+ }
631
+ }
632
+ i += 3
633
+ } else {
634
+ if ic == c {
635
+ found = true
636
+ }
637
+ i += 1
638
+ }
639
+ } else {
640
+ if ic == c {
641
+ found = true
642
+ }
643
+ i += 1
644
+ }
645
+ }
646
+ }
647
+
648
+ if negated {
649
+ if found {
650
+ return false
651
+ }
652
+ return true
653
+ }
654
+ return found
655
+ }