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.
- package/NOMEN_AGENTS.md +235 -0
- package/core/System/Ansi.nm +150 -0
- package/core/System/Array.nm +358 -0
- package/core/System/BigInt.nm +887 -0
- package/core/System/Buffer.nm +526 -0
- package/core/System/Channel.nm +198 -0
- package/core/System/ClassBuffer.nm +229 -0
- package/core/System/Console.nm +211 -0
- package/core/System/Controls/Button.nm +138 -0
- package/core/System/Controls/CheckBox.nm +158 -0
- package/core/System/Controls/Container.nm +1562 -0
- package/core/System/Controls/Control.nm +28 -0
- package/core/System/Controls/Geometry.nm +101 -0
- package/core/System/Controls/Layout.nm +228 -0
- package/core/System/Controls/LayoutParams.nm +50 -0
- package/core/System/Controls/Text.nm +129 -0
- package/core/System/Controls/TextBox.nm +157 -0
- package/core/System/Controls/Window.nm +280 -0
- package/core/System/Disposable.nm +8 -0
- package/core/System/Enumerable.nm +10 -0
- package/core/System/Equatable.nm +10 -0
- package/core/System/Graph.nm +68 -0
- package/core/System/Hashable.nm +13 -0
- package/core/System/Init.nm +21 -0
- package/core/System/LinkedList.nm +64 -0
- package/core/System/List.nm +44 -0
- package/core/System/Map.nm +153 -0
- package/core/System/Math.nm +54 -0
- package/core/System/Mutex.nm +78 -0
- package/core/System/Nursery.nm +27 -0
- package/core/System/Sendable.nm +9 -0
- package/core/System/Set.nm +97 -0
- package/core/System/Stream/Directory.nm +176 -0
- package/core/System/Stream/File.nm +432 -0
- package/core/System/Stream/Http.nm +453 -0
- package/core/System/String.nm +154 -0
- package/core/System/StringBuilder.nm +249 -0
- package/core/System/Stringable.nm +8 -0
- package/core/System/Task.nm +263 -0
- package/core/System/Test.nm +241 -0
- package/core/System/Text/Json.nm +377 -0
- package/core/System/Text/JsonTree.nm +187 -0
- package/core/System/Text/Regex.nm +655 -0
- package/core/System/Time.nm +112 -0
- package/core/System/Tree.nm +70 -0
- package/core/System/Viewable.nm +27 -0
- package/core/System/bool.nm +66 -0
- package/core/System/char.nm +42 -0
- package/core/System/float.nm +43 -0
- package/core/System/int.nm +54 -0
- package/core/System/int64.nm +42 -0
- package/core/System/int8.nm +42 -0
- package/core/System/uint.nm +42 -0
- package/core/System/uint64.nm +42 -0
- package/core/System/uint8.nm +42 -0
- package/core/docs/System/Ansi.md +40 -0
- package/core/docs/System/Array.md +16 -0
- package/core/docs/System/BigInt.md +30 -0
- package/core/docs/System/Buffer.md +31 -0
- package/core/docs/System/Channel.md +10 -0
- package/core/docs/System/ClassBuffer.md +15 -0
- package/core/docs/System/Console.md +13 -0
- package/core/docs/System/Controls/Button.md +12 -0
- package/core/docs/System/Controls/CheckBox.md +15 -0
- package/core/docs/System/Controls/Container.md +82 -0
- package/core/docs/System/Controls/Control.md +5 -0
- package/core/docs/System/Controls/Geometry.md +26 -0
- package/core/docs/System/Controls/Layout.md +37 -0
- package/core/docs/System/Controls/LayoutParams.md +13 -0
- package/core/docs/System/Controls/Text.md +12 -0
- package/core/docs/System/Controls/TextBox.md +13 -0
- package/core/docs/System/Controls/Window.md +20 -0
- package/core/docs/System/Disposable.md +5 -0
- package/core/docs/System/Enumerable.md +5 -0
- package/core/docs/System/Graph.md +17 -0
- package/core/docs/System/Init.md +9 -0
- package/core/docs/System/LinkedList.md +16 -0
- package/core/docs/System/List.md +13 -0
- package/core/docs/System/Map.md +14 -0
- package/core/docs/System/Math.md +11 -0
- package/core/docs/System/Mutex.md +10 -0
- package/core/docs/System/Nursery.md +5 -0
- package/core/docs/System/Sendable.md +5 -0
- package/core/docs/System/Set.md +14 -0
- package/core/docs/System/Stream/Directory.md +12 -0
- package/core/docs/System/Stream/File.md +21 -0
- package/core/docs/System/String.md +14 -0
- package/core/docs/System/StringBuilder.md +12 -0
- package/core/docs/System/Stringable.md +5 -0
- package/core/docs/System/Task.md +15 -0
- package/core/docs/System/Text/Json.md +12 -0
- package/core/docs/System/Text/JsonTree.md +21 -0
- package/core/docs/System/Text/Regex.md +12 -0
- package/core/docs/System/Time.md +11 -0
- package/core/docs/System/Tree.md +18 -0
- package/core/docs/System/Viewable.md +5 -0
- package/core/docs/System/bool.md +9 -0
- package/core/docs/System/char.md +9 -0
- package/core/docs/System/float.md +9 -0
- package/core/docs/System/int.md +10 -0
- package/core/docs/System/int64.md +9 -0
- package/core/docs/System/int8.md +9 -0
- package/core/docs/System/uint.md +9 -0
- package/core/docs/System/uint64.md +9 -0
- package/core/docs/System/uint8.md +9 -0
- package/core/package.jsonc +4 -0
- package/dist/index.mjs +417 -5490
- package/package.json +33 -30
- package/src/args.ts +171 -0
- package/src/index.ts +53 -104
- package/src/init.ts +128 -0
- package/src/test.ts +6 -2
- package/test/fixtures/build/calc.test +0 -0
- package/test/fixtures/build/calc.test.c +0 -969
- package/test/fixtures/build/main.h +0 -170
- package/test/fixtures/calc.test.nm +0 -32
- package/tsconfig.json +0 -21
- 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
|
+
}
|