yeptris 0.6.57.1-arm-linux-eabihf → 0.6.58.1-arm-linux-eabihf
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.
- checksums.yaml +4 -4
- data/ext/yeptris_native/version.h +2 -2
- data/lib/yeptris/3.4/native.so +0 -0
- data/lib/yeptris.rb +1 -1
- data/libyeptris.so +0 -0
- data/vendor/libyeptris/CMakeLists.txt +1 -1
- data/vendor/libyeptris/src/CMakeLists.txt +5 -0
- data/vendor/libyeptris/src/include/yeptris/error.h +2 -1
- data/vendor/libyeptris/src/include/yeptris/resolve.h +3 -1
- data/vendor/libyeptris/src/include/yeptris/validate.h +157 -0
- data/vendor/libyeptris/src/include/yeptris.h +1 -0
- data/vendor/libyeptris/src/yeptris/parse.c +16 -7
- data/vendor/libyeptris/src/yeptris/resolve/json12.c +103 -0
- data/vendor/libyeptris/src/yeptris/resolve/resolver.h +9 -0
- data/vendor/libyeptris/src/yeptris/validate/internal.h +102 -0
- data/vendor/libyeptris/src/yeptris/validate/jsonschema.c +1022 -0
- data/vendor/libyeptris/src/yeptris/validate/regexp.c +972 -0
- data/vendor/libyeptris/src/yeptris/validate/validate.c +939 -0
- metadata +7 -1
|
@@ -0,0 +1,972 @@
|
|
|
1
|
+
/* regexp.c — the bounded ECMA-regex subset behind JSON Schema's
|
|
2
|
+
* `pattern` keyword (#882 slice 2).
|
|
3
|
+
*
|
|
4
|
+
* Supported: literals, '.', [...] classes (ranges, negation, \d\w\s
|
|
5
|
+
* and their negations, \n\t\r\f\v\0, \xHH), groups (capturing ignored,
|
|
6
|
+
* (?:...) fine), alternation, the quantifiers * + ? {n} {n,} {n,m}
|
|
7
|
+
* (lazy markers parse and are ignored — match/no-match has no
|
|
8
|
+
* preference), the ^ $ \b \B assertions. Unsupported constructs
|
|
9
|
+
* (backreferences, lookaround, unknown letter escapes) are
|
|
10
|
+
* SyntaxErrors: the caller treats an invalid pattern as an absent
|
|
11
|
+
* keyword (2020-12's ignore posture), never a guess.
|
|
12
|
+
*
|
|
13
|
+
* Shape: byte-oriented — classes are ASCII, UTF-8 literals match
|
|
14
|
+
* byte-wise (ASCII patterns, the JSON Schema norm, are exact).
|
|
15
|
+
* Parse -> AST -> Thompson NFA -> Pike-style simulation: O(subject *
|
|
16
|
+
* program), NO backtracking blowup, no recursion over the subject.
|
|
17
|
+
* Every array is a fixed stack budget; overflow is a SyntaxError (-1),
|
|
18
|
+
* never a crash. Truth lives here once (MECE: a second consumer moves
|
|
19
|
+
* this to its own module).
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
#include <string.h>
|
|
23
|
+
|
|
24
|
+
#include "internal.h"
|
|
25
|
+
|
|
26
|
+
#define RE_MAX_PAT 1024
|
|
27
|
+
#define RE_MAX_AST 256
|
|
28
|
+
#define RE_MAX_INST 320
|
|
29
|
+
#define RE_MAX_CLS 24
|
|
30
|
+
#define RE_AST_DEPTH 64
|
|
31
|
+
#define RE_REP_INF 0xFFFFu
|
|
32
|
+
|
|
33
|
+
enum {
|
|
34
|
+
A_BYTE = 1, /* a: the literal byte */
|
|
35
|
+
A_CLASS, /* cls */
|
|
36
|
+
A_ASSERT, /* a: assert kind */
|
|
37
|
+
A_CAT, /* a, b */
|
|
38
|
+
A_ALT, /* a, b */
|
|
39
|
+
A_REP, /* a; min..max */
|
|
40
|
+
A_EMPTY,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
enum { RE_AS_BOL = 0, RE_AS_EOL, RE_AS_WBOUND, RE_AS_NWBOUND };
|
|
44
|
+
|
|
45
|
+
typedef struct {
|
|
46
|
+
uint8_t kind;
|
|
47
|
+
uint8_t a, b; /* children / literal byte / assert kind */
|
|
48
|
+
uint8_t cls; /* class id (A_CLASS) */
|
|
49
|
+
uint8_t lazy; /* parsed, ignored */
|
|
50
|
+
uint16_t min, max;
|
|
51
|
+
} re_ast;
|
|
52
|
+
|
|
53
|
+
typedef struct {
|
|
54
|
+
uint8_t op; /* RE_OP_* */
|
|
55
|
+
uint8_t cls; /* class id (CHAR) / assert kind (ASSERT) */
|
|
56
|
+
uint16_t x; /* jump target / literal byte */
|
|
57
|
+
uint16_t y; /* second jump target (SPLIT) */
|
|
58
|
+
} re_inst;
|
|
59
|
+
|
|
60
|
+
enum { RE_OP_CHAR = 1, RE_OP_BYTE, RE_OP_SPLIT, RE_OP_JMP, RE_OP_ASSERT, RE_OP_MATCH };
|
|
61
|
+
|
|
62
|
+
/* Pending exit arrows of one compiled fragment: a bitset over
|
|
63
|
+
* (pc * 2 + slot), exactly RE_MAX_INST * 2 bits. frag_connect resolves
|
|
64
|
+
* and CLEARS — an arrow points at exactly one target, once. */
|
|
65
|
+
#define RE_FRAG_BYTES ((RE_MAX_INST * 2 + 7) / 8)
|
|
66
|
+
typedef struct {
|
|
67
|
+
int in; /* entry pc; -1 = empty (arrows only) */
|
|
68
|
+
uint8_t bits[RE_FRAG_BYTES];
|
|
69
|
+
} re_frag;
|
|
70
|
+
|
|
71
|
+
typedef struct {
|
|
72
|
+
/* parse */
|
|
73
|
+
const char* pat;
|
|
74
|
+
uint32_t pn, pi;
|
|
75
|
+
int err;
|
|
76
|
+
re_ast ast[RE_MAX_AST];
|
|
77
|
+
int ast_n;
|
|
78
|
+
uint8_t cls[RE_MAX_CLS][32];
|
|
79
|
+
int cls_n;
|
|
80
|
+
/* compile */
|
|
81
|
+
re_inst inst[RE_MAX_INST];
|
|
82
|
+
int inst_n;
|
|
83
|
+
} re_ctx;
|
|
84
|
+
|
|
85
|
+
static int re_word(unsigned char b) {
|
|
86
|
+
return (b >= '0' && b <= '9') || (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* ---- parse ------------------------------------------------------------ */
|
|
90
|
+
|
|
91
|
+
static int ast_new(re_ctx* c, uint8_t kind) {
|
|
92
|
+
if (c->ast_n >= RE_MAX_AST) {
|
|
93
|
+
c->err = 1;
|
|
94
|
+
return -1;
|
|
95
|
+
}
|
|
96
|
+
int id = c->ast_n++;
|
|
97
|
+
memset(&c->ast[id], 0, sizeof(c->ast[id]));
|
|
98
|
+
c->ast[id].kind = kind;
|
|
99
|
+
c->ast[id].min = 1;
|
|
100
|
+
c->ast[id].max = 1;
|
|
101
|
+
return id;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
static int cls_new(re_ctx* c) {
|
|
105
|
+
if (c->cls_n >= RE_MAX_CLS) {
|
|
106
|
+
c->err = 1;
|
|
107
|
+
return -1;
|
|
108
|
+
}
|
|
109
|
+
int id = c->cls_n++;
|
|
110
|
+
memset(c->cls[id], 0, 32);
|
|
111
|
+
return id;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
static void cls_set(re_ctx* c, int id, unsigned b) {
|
|
115
|
+
c->cls[id][(b >> 3) & 31] |= (uint8_t)(1u << (b & 7));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
static void cls_add_named(re_ctx* c, int id, int neg, int kind) {
|
|
119
|
+
for (int b = 0; b < 256; b++) {
|
|
120
|
+
int m;
|
|
121
|
+
if (kind == 0) {
|
|
122
|
+
m = (b >= '0' && b <= '9');
|
|
123
|
+
} else if (kind == 1) {
|
|
124
|
+
m = (b >= '0' && b <= '9') || (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') ||
|
|
125
|
+
b == '_';
|
|
126
|
+
} else {
|
|
127
|
+
m = b == ' ' || b == '\t' || b == '\n' || b == '\r' || b == '\f' || b == '\v';
|
|
128
|
+
}
|
|
129
|
+
if (neg) {
|
|
130
|
+
m = !m;
|
|
131
|
+
}
|
|
132
|
+
if (m) {
|
|
133
|
+
cls_set(c, id, (unsigned)b);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
static int p_hex(re_ctx* c, int nibbles) {
|
|
139
|
+
int v = 0;
|
|
140
|
+
for (int i = 0; i < nibbles; i++) {
|
|
141
|
+
if (c->pi >= c->pn) {
|
|
142
|
+
return -1;
|
|
143
|
+
}
|
|
144
|
+
char ch = c->pat[c->pi];
|
|
145
|
+
int d;
|
|
146
|
+
if (ch >= '0' && ch <= '9') {
|
|
147
|
+
d = ch - '0';
|
|
148
|
+
} else if (ch >= 'a' && ch <= 'f') {
|
|
149
|
+
d = ch - 'a' + 10;
|
|
150
|
+
} else if (ch >= 'A' && ch <= 'F') {
|
|
151
|
+
d = ch - 'A' + 10;
|
|
152
|
+
} else {
|
|
153
|
+
return -1;
|
|
154
|
+
}
|
|
155
|
+
v = v * 16 + d;
|
|
156
|
+
c->pi++;
|
|
157
|
+
}
|
|
158
|
+
return v;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/* One escape OUTSIDE a class. 0: byte in *out; 1: class id in *out;
|
|
162
|
+
* -2: assertion kind in *out; -1: SyntaxError. */
|
|
163
|
+
static int p_escape(re_ctx* c, int* out) {
|
|
164
|
+
if (c->pi >= c->pn) {
|
|
165
|
+
c->err = 1;
|
|
166
|
+
return -1;
|
|
167
|
+
}
|
|
168
|
+
char e = c->pat[c->pi++];
|
|
169
|
+
switch (e) {
|
|
170
|
+
case 'd':
|
|
171
|
+
case 'w':
|
|
172
|
+
case 's': {
|
|
173
|
+
int id = cls_new(c);
|
|
174
|
+
if (id < 0) {
|
|
175
|
+
return -1;
|
|
176
|
+
}
|
|
177
|
+
cls_add_named(c, id, 0, e == 'd' ? 0 : (e == 'w' ? 1 : 2));
|
|
178
|
+
*out = id;
|
|
179
|
+
return 1;
|
|
180
|
+
}
|
|
181
|
+
case 'D':
|
|
182
|
+
case 'W':
|
|
183
|
+
case 'S': {
|
|
184
|
+
int id = cls_new(c);
|
|
185
|
+
if (id < 0) {
|
|
186
|
+
return -1;
|
|
187
|
+
}
|
|
188
|
+
cls_add_named(c, id, 1, e == 'D' ? 0 : (e == 'W' ? 1 : 2));
|
|
189
|
+
*out = id;
|
|
190
|
+
return 1;
|
|
191
|
+
}
|
|
192
|
+
case 'b':
|
|
193
|
+
*out = RE_AS_WBOUND;
|
|
194
|
+
return -2;
|
|
195
|
+
case 'B':
|
|
196
|
+
*out = RE_AS_NWBOUND;
|
|
197
|
+
return -2;
|
|
198
|
+
case 'n':
|
|
199
|
+
*out = '\n';
|
|
200
|
+
return 0;
|
|
201
|
+
case 't':
|
|
202
|
+
*out = '\t';
|
|
203
|
+
return 0;
|
|
204
|
+
case 'r':
|
|
205
|
+
*out = '\r';
|
|
206
|
+
return 0;
|
|
207
|
+
case 'f':
|
|
208
|
+
*out = '\f';
|
|
209
|
+
return 0;
|
|
210
|
+
case 'v':
|
|
211
|
+
*out = '\v';
|
|
212
|
+
return 0;
|
|
213
|
+
case '0':
|
|
214
|
+
*out = 0;
|
|
215
|
+
return 0;
|
|
216
|
+
case 'x': {
|
|
217
|
+
int v = p_hex(c, 2);
|
|
218
|
+
if (v < 0) {
|
|
219
|
+
c->err = 1;
|
|
220
|
+
return -1;
|
|
221
|
+
}
|
|
222
|
+
*out = v;
|
|
223
|
+
return 0;
|
|
224
|
+
}
|
|
225
|
+
default:
|
|
226
|
+
/* letters/digits outside the set (incl. backrefs): SyntaxError;
|
|
227
|
+
* punctuation: identity escape */
|
|
228
|
+
if ((e >= 'a' && e <= 'z') || (e >= 'A' && e <= 'Z') || (e >= '0' && e <= '9')) {
|
|
229
|
+
c->err = 1;
|
|
230
|
+
return -1;
|
|
231
|
+
}
|
|
232
|
+
*out = (unsigned char)e;
|
|
233
|
+
return 0;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
static int p_byte_atom(re_ctx* c, unsigned b) {
|
|
238
|
+
int id = ast_new(c, A_BYTE);
|
|
239
|
+
if (id < 0) {
|
|
240
|
+
return -1;
|
|
241
|
+
}
|
|
242
|
+
c->ast[id].a = (uint8_t)b;
|
|
243
|
+
return id;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
static int p_class_atom(re_ctx* c, int cid) {
|
|
247
|
+
int id = ast_new(c, A_CLASS);
|
|
248
|
+
if (id < 0) {
|
|
249
|
+
return -1;
|
|
250
|
+
}
|
|
251
|
+
c->ast[id].cls = (uint8_t)cid;
|
|
252
|
+
return id;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/* Parses the body of a [...] (the '[' already consumed); -1 on error. */
|
|
256
|
+
static int p_class(re_ctx* c) {
|
|
257
|
+
int id = cls_new(c);
|
|
258
|
+
if (id < 0) {
|
|
259
|
+
return -1;
|
|
260
|
+
}
|
|
261
|
+
int neg = 0;
|
|
262
|
+
if (c->pi < c->pn && c->pat[c->pi] == '^') {
|
|
263
|
+
neg = 1;
|
|
264
|
+
c->pi++;
|
|
265
|
+
}
|
|
266
|
+
int first = 1;
|
|
267
|
+
for (;;) {
|
|
268
|
+
if (c->pi >= c->pn) {
|
|
269
|
+
c->err = 1; /* unterminated class */
|
|
270
|
+
return -1;
|
|
271
|
+
}
|
|
272
|
+
char ch = c->pat[c->pi];
|
|
273
|
+
if (ch == ']' && !first) {
|
|
274
|
+
c->pi++;
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
first = 0;
|
|
278
|
+
int lo;
|
|
279
|
+
if (ch == '\\') {
|
|
280
|
+
c->pi++;
|
|
281
|
+
int v = 0;
|
|
282
|
+
int rc = p_escape(c, &v);
|
|
283
|
+
if (rc < 0) {
|
|
284
|
+
c->err = 1; /* \b-as-BACKSPACE unsupported on purpose */
|
|
285
|
+
return -1;
|
|
286
|
+
}
|
|
287
|
+
if (rc == 1) {
|
|
288
|
+
for (int b = 0; b < 256; b++) {
|
|
289
|
+
if (c->cls[v][(b >> 3) & 31] & (1u << (b & 7))) {
|
|
290
|
+
cls_set(c, id, (unsigned)b);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
lo = v;
|
|
296
|
+
} else {
|
|
297
|
+
lo = (unsigned char)ch;
|
|
298
|
+
c->pi++;
|
|
299
|
+
}
|
|
300
|
+
if (c->pi + 1 < c->pn && c->pat[c->pi] == '-' && c->pat[c->pi + 1] != ']') {
|
|
301
|
+
c->pi++; /* '-' */
|
|
302
|
+
char hc = c->pat[c->pi];
|
|
303
|
+
int hi;
|
|
304
|
+
if (hc == '\\') {
|
|
305
|
+
c->pi++;
|
|
306
|
+
int v = 0;
|
|
307
|
+
if (p_escape(c, &v) != 0) {
|
|
308
|
+
c->err = 1;
|
|
309
|
+
return -1;
|
|
310
|
+
}
|
|
311
|
+
hi = v;
|
|
312
|
+
} else {
|
|
313
|
+
hi = (unsigned char)hc;
|
|
314
|
+
c->pi++;
|
|
315
|
+
}
|
|
316
|
+
if (hi < lo) {
|
|
317
|
+
c->err = 1;
|
|
318
|
+
return -1;
|
|
319
|
+
}
|
|
320
|
+
for (int b = lo; b <= hi; b++) {
|
|
321
|
+
cls_set(c, id, (unsigned)b);
|
|
322
|
+
}
|
|
323
|
+
} else {
|
|
324
|
+
cls_set(c, id, (unsigned)lo);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (neg) {
|
|
328
|
+
for (int b = 0; b < 32; b++) {
|
|
329
|
+
c->cls[id][b] = (uint8_t)~c->cls[id][b];
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return id;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/* quantifier: 0 none, 1 '*' 2 '+' 3 '?' 4 '{n,m}'; -1 SyntaxError */
|
|
336
|
+
static int p_quant(re_ctx* c, uint16_t* min, uint16_t* max, int* lazy) {
|
|
337
|
+
if (c->pi >= c->pn) {
|
|
338
|
+
return 0;
|
|
339
|
+
}
|
|
340
|
+
char q = c->pat[c->pi];
|
|
341
|
+
int kind = 0;
|
|
342
|
+
if (q == '*') {
|
|
343
|
+
kind = 1;
|
|
344
|
+
*min = 0;
|
|
345
|
+
*max = RE_REP_INF;
|
|
346
|
+
c->pi++;
|
|
347
|
+
} else if (q == '+') {
|
|
348
|
+
kind = 2;
|
|
349
|
+
*min = 1;
|
|
350
|
+
*max = RE_REP_INF;
|
|
351
|
+
c->pi++;
|
|
352
|
+
} else if (q == '?') {
|
|
353
|
+
kind = 3;
|
|
354
|
+
*min = 0;
|
|
355
|
+
*max = 1;
|
|
356
|
+
c->pi++;
|
|
357
|
+
} else if (q == '{') {
|
|
358
|
+
uint32_t save = c->pi;
|
|
359
|
+
c->pi++;
|
|
360
|
+
uint32_t n = 0;
|
|
361
|
+
int dn = 0;
|
|
362
|
+
while (c->pi < c->pn && c->pat[c->pi] >= '0' && c->pat[c->pi] <= '9') {
|
|
363
|
+
n = n * 10 + (uint32_t)(c->pat[c->pi] - '0');
|
|
364
|
+
if (n > 255) {
|
|
365
|
+
c->pi = save;
|
|
366
|
+
return 0; /* literal '{' */
|
|
367
|
+
}
|
|
368
|
+
c->pi++;
|
|
369
|
+
dn++;
|
|
370
|
+
}
|
|
371
|
+
if (dn == 0) {
|
|
372
|
+
c->pi = save;
|
|
373
|
+
return 0;
|
|
374
|
+
}
|
|
375
|
+
uint32_t m = n;
|
|
376
|
+
int have_comma = 0;
|
|
377
|
+
if (c->pi < c->pn && c->pat[c->pi] == ',') {
|
|
378
|
+
have_comma = 1;
|
|
379
|
+
c->pi++;
|
|
380
|
+
m = 0;
|
|
381
|
+
int dm = 0;
|
|
382
|
+
while (c->pi < c->pn && c->pat[c->pi] >= '0' && c->pat[c->pi] <= '9') {
|
|
383
|
+
m = m * 10 + (uint32_t)(c->pat[c->pi] - '0');
|
|
384
|
+
if (m > 255) {
|
|
385
|
+
c->pi = save;
|
|
386
|
+
return 0;
|
|
387
|
+
}
|
|
388
|
+
c->pi++;
|
|
389
|
+
dm++;
|
|
390
|
+
}
|
|
391
|
+
if (dm == 0) {
|
|
392
|
+
m = RE_REP_INF; /* {n,} */
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
if (c->pi >= c->pn || c->pat[c->pi] != '}') {
|
|
396
|
+
c->pi = save;
|
|
397
|
+
return 0; /* literal '{' */
|
|
398
|
+
}
|
|
399
|
+
c->pi++;
|
|
400
|
+
if (!have_comma) {
|
|
401
|
+
m = n; /* {n} */
|
|
402
|
+
}
|
|
403
|
+
if (m != RE_REP_INF && m < n) {
|
|
404
|
+
c->err = 1;
|
|
405
|
+
return -1;
|
|
406
|
+
}
|
|
407
|
+
*min = (uint16_t)n;
|
|
408
|
+
*max = (uint16_t)m;
|
|
409
|
+
kind = 4;
|
|
410
|
+
}
|
|
411
|
+
if (kind != 0) {
|
|
412
|
+
*lazy = 0;
|
|
413
|
+
if (c->pi < c->pn && c->pat[c->pi] == '?') {
|
|
414
|
+
*lazy = 1;
|
|
415
|
+
c->pi++;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return kind;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
static int p_alt(re_ctx* c, int depth);
|
|
422
|
+
|
|
423
|
+
static int p_atom(re_ctx* c, int depth) {
|
|
424
|
+
if (c->pi >= c->pn) {
|
|
425
|
+
c->err = 1;
|
|
426
|
+
return -1;
|
|
427
|
+
}
|
|
428
|
+
char ch = c->pat[c->pi];
|
|
429
|
+
if (ch == '(') {
|
|
430
|
+
c->pi++;
|
|
431
|
+
if (c->pi + 1 < c->pn && c->pat[c->pi] == '?' && c->pat[c->pi + 1] == ':') {
|
|
432
|
+
c->pi += 2;
|
|
433
|
+
} else if (c->pi < c->pn && c->pat[c->pi] == '?') {
|
|
434
|
+
c->err = 1; /* lookaround/flags unsupported */
|
|
435
|
+
return -1;
|
|
436
|
+
}
|
|
437
|
+
int id = p_alt(c, depth + 1);
|
|
438
|
+
if (id < 0) {
|
|
439
|
+
return -1;
|
|
440
|
+
}
|
|
441
|
+
if (c->pi >= c->pn || c->pat[c->pi] != ')') {
|
|
442
|
+
c->err = 1;
|
|
443
|
+
return -1;
|
|
444
|
+
}
|
|
445
|
+
c->pi++;
|
|
446
|
+
return id;
|
|
447
|
+
}
|
|
448
|
+
if (ch == '[') {
|
|
449
|
+
c->pi++;
|
|
450
|
+
int cid = p_class(c);
|
|
451
|
+
return cid < 0 ? -1 : p_class_atom(c, cid);
|
|
452
|
+
}
|
|
453
|
+
if (ch == '.') {
|
|
454
|
+
c->pi++;
|
|
455
|
+
int cid = cls_new(c);
|
|
456
|
+
if (cid < 0) {
|
|
457
|
+
return -1;
|
|
458
|
+
}
|
|
459
|
+
for (int b = 0; b < 256; b++) {
|
|
460
|
+
if (b != '\n') {
|
|
461
|
+
cls_set(c, cid, (unsigned)b);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return p_class_atom(c, cid);
|
|
465
|
+
}
|
|
466
|
+
if (ch == '^' || ch == '$') {
|
|
467
|
+
c->pi++;
|
|
468
|
+
int id = ast_new(c, A_ASSERT);
|
|
469
|
+
if (id < 0) {
|
|
470
|
+
return -1;
|
|
471
|
+
}
|
|
472
|
+
c->ast[id].a = (ch == '^') ? RE_AS_BOL : RE_AS_EOL;
|
|
473
|
+
return id;
|
|
474
|
+
}
|
|
475
|
+
if (ch == '\\') {
|
|
476
|
+
c->pi++;
|
|
477
|
+
int v = 0;
|
|
478
|
+
int rc = p_escape(c, &v);
|
|
479
|
+
if (rc < 0) {
|
|
480
|
+
if (rc == -2) {
|
|
481
|
+
int id = ast_new(c, A_ASSERT);
|
|
482
|
+
if (id < 0) {
|
|
483
|
+
return -1;
|
|
484
|
+
}
|
|
485
|
+
c->ast[id].a = (uint8_t)v;
|
|
486
|
+
return id;
|
|
487
|
+
}
|
|
488
|
+
return -1;
|
|
489
|
+
}
|
|
490
|
+
return rc == 1 ? p_class_atom(c, v) : p_byte_atom(c, (unsigned)v);
|
|
491
|
+
}
|
|
492
|
+
if (ch == '*' || ch == '+' || ch == '?') {
|
|
493
|
+
c->err = 1; /* dangling quantifier */
|
|
494
|
+
return -1;
|
|
495
|
+
}
|
|
496
|
+
if (ch == ')' || ch == '|') {
|
|
497
|
+
c->err = 1; /* misplaced terminator */
|
|
498
|
+
return -1;
|
|
499
|
+
}
|
|
500
|
+
c->pi++; /* literal byte (UTF-8 rides byte-wise) */
|
|
501
|
+
return p_byte_atom(c, (unsigned)(unsigned char)ch);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
static int p_seq(re_ctx* c, int depth) {
|
|
505
|
+
int acc = -1;
|
|
506
|
+
for (;;) {
|
|
507
|
+
if (c->pi >= c->pn) {
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
char ch = c->pat[c->pi];
|
|
511
|
+
if (ch == '|' || ch == ')') {
|
|
512
|
+
break;
|
|
513
|
+
}
|
|
514
|
+
int atom = p_atom(c, depth);
|
|
515
|
+
if (atom < 0) {
|
|
516
|
+
return -1;
|
|
517
|
+
}
|
|
518
|
+
uint16_t qmin = 1, qmax = 1;
|
|
519
|
+
int lazy = 0;
|
|
520
|
+
int q = p_quant(c, &qmin, &qmax, &lazy);
|
|
521
|
+
if (q < 0) {
|
|
522
|
+
return -1;
|
|
523
|
+
}
|
|
524
|
+
if (q > 0) {
|
|
525
|
+
int rep = ast_new(c, A_REP);
|
|
526
|
+
if (rep < 0) {
|
|
527
|
+
return -1;
|
|
528
|
+
}
|
|
529
|
+
c->ast[rep].a = (uint8_t)atom;
|
|
530
|
+
c->ast[rep].min = qmin;
|
|
531
|
+
c->ast[rep].max = qmax;
|
|
532
|
+
c->ast[rep].lazy = (uint8_t)lazy;
|
|
533
|
+
atom = rep;
|
|
534
|
+
}
|
|
535
|
+
if (acc < 0) {
|
|
536
|
+
acc = atom;
|
|
537
|
+
} else {
|
|
538
|
+
int cat = ast_new(c, A_CAT);
|
|
539
|
+
if (cat < 0) {
|
|
540
|
+
return -1;
|
|
541
|
+
}
|
|
542
|
+
c->ast[cat].a = (uint8_t)acc;
|
|
543
|
+
c->ast[cat].b = (uint8_t)atom;
|
|
544
|
+
acc = cat;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
if (acc < 0) {
|
|
548
|
+
acc = ast_new(c, A_EMPTY);
|
|
549
|
+
if (acc < 0) {
|
|
550
|
+
return -1;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
return acc;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
static int p_alt(re_ctx* c, int depth) {
|
|
557
|
+
if (depth > RE_AST_DEPTH) {
|
|
558
|
+
c->err = 1;
|
|
559
|
+
return -1;
|
|
560
|
+
}
|
|
561
|
+
int first = p_seq(c, depth);
|
|
562
|
+
if (first < 0) {
|
|
563
|
+
return -1;
|
|
564
|
+
}
|
|
565
|
+
int acc = first;
|
|
566
|
+
while (c->pi < c->pn && c->pat[c->pi] == '|') {
|
|
567
|
+
c->pi++;
|
|
568
|
+
int right = p_seq(c, depth);
|
|
569
|
+
if (right < 0) {
|
|
570
|
+
return -1;
|
|
571
|
+
}
|
|
572
|
+
int id = ast_new(c, A_ALT);
|
|
573
|
+
if (id < 0) {
|
|
574
|
+
return -1;
|
|
575
|
+
}
|
|
576
|
+
c->ast[id].a = (uint8_t)acc;
|
|
577
|
+
c->ast[id].b = (uint8_t)right;
|
|
578
|
+
acc = id;
|
|
579
|
+
}
|
|
580
|
+
return acc;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/* ---- compile (AST -> NFA) --------------------------------------------- */
|
|
584
|
+
|
|
585
|
+
static int emit_inst(re_ctx* c, uint8_t op, uint8_t cls, uint16_t x, uint16_t y) {
|
|
586
|
+
if (c->inst_n >= RE_MAX_INST) {
|
|
587
|
+
c->err = 1;
|
|
588
|
+
return -1;
|
|
589
|
+
}
|
|
590
|
+
int pc = c->inst_n++;
|
|
591
|
+
c->inst[pc].op = op;
|
|
592
|
+
c->inst[pc].cls = cls;
|
|
593
|
+
c->inst[pc].x = x;
|
|
594
|
+
c->inst[pc].y = y;
|
|
595
|
+
return pc;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
static void frag_init(re_frag* f, int in) {
|
|
599
|
+
memset(f->bits, 0, sizeof(f->bits));
|
|
600
|
+
f->in = in;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
static void frag_bit(re_frag* f, int pc, int slot) {
|
|
604
|
+
int bit = pc * 2 + slot;
|
|
605
|
+
f->bits[bit >> 3] |= (uint8_t)(1u << (bit & 7));
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
static void frag_or(re_frag* dst, const re_frag* src) {
|
|
609
|
+
for (int i = 0; i < (int)RE_FRAG_BYTES; i++) {
|
|
610
|
+
dst->bits[i] |= src->bits[i];
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/* Resolves (and clears) every pending arrow to `target`. */
|
|
615
|
+
static void frag_connect(re_frag* f, re_inst* inst, int target) {
|
|
616
|
+
for (int bit = 0; bit < RE_MAX_INST * 2; bit++) {
|
|
617
|
+
if (f->bits[bit >> 3] & (1u << (bit & 7))) {
|
|
618
|
+
int pc = bit >> 1;
|
|
619
|
+
if (bit & 1) {
|
|
620
|
+
inst[pc].y = (uint16_t)target;
|
|
621
|
+
} else {
|
|
622
|
+
inst[pc].x = (uint16_t)target;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
memset(f->bits, 0, sizeof(f->bits));
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/* Adopts src's pending arrows (dst's own are already resolved). */
|
|
630
|
+
static void frag_adopt(re_frag* dst, const re_frag* src) {
|
|
631
|
+
memcpy(dst->bits, src->bits, sizeof(dst->bits));
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/* An empty fragment still needs an entry instruction to hang its
|
|
635
|
+
* pending exit on: a bare JMP. */
|
|
636
|
+
static int frag_force_entry(re_ctx* c, re_frag* f) {
|
|
637
|
+
if (f->in >= 0) {
|
|
638
|
+
return 0;
|
|
639
|
+
}
|
|
640
|
+
int j = emit_inst(c, RE_OP_JMP, 0, 0xFFFF, 0);
|
|
641
|
+
if (j < 0) {
|
|
642
|
+
return -1;
|
|
643
|
+
}
|
|
644
|
+
f->in = j;
|
|
645
|
+
frag_bit(f, j, 0);
|
|
646
|
+
return 0;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
static int c_node(re_ctx* c, int id, re_frag* out, int depth) {
|
|
650
|
+
if (depth > RE_AST_DEPTH + 8) { /* the left-folded CAT chain runs
|
|
651
|
+
* deeper than the parse tree */
|
|
652
|
+
c->err = 1;
|
|
653
|
+
return -1;
|
|
654
|
+
}
|
|
655
|
+
const re_ast* a = &c->ast[id];
|
|
656
|
+
switch (a->kind) {
|
|
657
|
+
case A_EMPTY: {
|
|
658
|
+
frag_init(out, -1);
|
|
659
|
+
return 0;
|
|
660
|
+
}
|
|
661
|
+
case A_BYTE: {
|
|
662
|
+
/* the literal rides cls: x/y are the jump-target pair, and the
|
|
663
|
+
* pending-exit machinery writes x — it must never carry data */
|
|
664
|
+
int pc = emit_inst(c, RE_OP_BYTE, a->a, 0, 0);
|
|
665
|
+
if (pc < 0) {
|
|
666
|
+
return -1;
|
|
667
|
+
}
|
|
668
|
+
frag_init(out, pc);
|
|
669
|
+
frag_bit(out, pc, 0); /* the fall-through exit */
|
|
670
|
+
return 0;
|
|
671
|
+
}
|
|
672
|
+
case A_CLASS: {
|
|
673
|
+
int pc = emit_inst(c, RE_OP_CHAR, a->cls, 0, 0);
|
|
674
|
+
if (pc < 0) {
|
|
675
|
+
return -1;
|
|
676
|
+
}
|
|
677
|
+
frag_init(out, pc);
|
|
678
|
+
frag_bit(out, pc, 0);
|
|
679
|
+
return 0;
|
|
680
|
+
}
|
|
681
|
+
case A_ASSERT: {
|
|
682
|
+
int pc = emit_inst(c, RE_OP_ASSERT, a->a, 0, 0);
|
|
683
|
+
if (pc < 0) {
|
|
684
|
+
return -1;
|
|
685
|
+
}
|
|
686
|
+
frag_init(out, pc);
|
|
687
|
+
frag_bit(out, pc, 0);
|
|
688
|
+
return 0;
|
|
689
|
+
}
|
|
690
|
+
case A_CAT: {
|
|
691
|
+
re_frag f1, f2;
|
|
692
|
+
if (c_node(c, a->a, &f1, depth + 1) < 0 || c_node(c, a->b, &f2, depth + 1) < 0) {
|
|
693
|
+
return -1;
|
|
694
|
+
}
|
|
695
|
+
if (f1.in < 0) {
|
|
696
|
+
*out = f2;
|
|
697
|
+
return 0;
|
|
698
|
+
}
|
|
699
|
+
if (f2.in >= 0) {
|
|
700
|
+
frag_connect(&f1, c->inst, f2.in); /* clears f1's arrows */
|
|
701
|
+
frag_or(&f1, &f2); /* f2's pending ride on */
|
|
702
|
+
}
|
|
703
|
+
*out = f1;
|
|
704
|
+
return 0;
|
|
705
|
+
}
|
|
706
|
+
case A_ALT: {
|
|
707
|
+
int split = emit_inst(c, RE_OP_SPLIT, 0, 0xFFFF, 0xFFFF);
|
|
708
|
+
if (split < 0) {
|
|
709
|
+
return -1;
|
|
710
|
+
}
|
|
711
|
+
re_frag f1, f2;
|
|
712
|
+
if (c_node(c, a->a, &f1, depth + 1) < 0 || c_node(c, a->b, &f2, depth + 1) < 0) {
|
|
713
|
+
return -1;
|
|
714
|
+
}
|
|
715
|
+
if (frag_force_entry(c, &f1) < 0 || frag_force_entry(c, &f2) < 0) {
|
|
716
|
+
return -1;
|
|
717
|
+
}
|
|
718
|
+
c->inst[split].x = (uint16_t)f1.in;
|
|
719
|
+
c->inst[split].y = (uint16_t)f2.in;
|
|
720
|
+
frag_init(out, split);
|
|
721
|
+
frag_or(out, &f1);
|
|
722
|
+
frag_or(out, &f2);
|
|
723
|
+
return 0;
|
|
724
|
+
}
|
|
725
|
+
case A_REP: {
|
|
726
|
+
/* the required prefix: min plain copies, left-folded */
|
|
727
|
+
re_frag acc;
|
|
728
|
+
frag_init(&acc, -1);
|
|
729
|
+
for (uint16_t i = 0; i < a->min; i++) {
|
|
730
|
+
re_frag f;
|
|
731
|
+
if (c_node(c, a->a, &f, depth + 1) < 0) {
|
|
732
|
+
return -1;
|
|
733
|
+
}
|
|
734
|
+
if (f.in < 0) {
|
|
735
|
+
continue; /* an empty child requires nothing */
|
|
736
|
+
}
|
|
737
|
+
if (acc.in < 0) {
|
|
738
|
+
acc = f;
|
|
739
|
+
} else {
|
|
740
|
+
frag_connect(&acc, c->inst, f.in); /* prior exits enter
|
|
741
|
+
* this copy */
|
|
742
|
+
frag_adopt(&acc, &f);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
if (a->max == RE_REP_INF) {
|
|
746
|
+
/* star over the child: SPLIT(body, EXIT); body -> SPLIT */
|
|
747
|
+
int split = emit_inst(c, RE_OP_SPLIT, 0, 0xFFFF, 0xFFFF);
|
|
748
|
+
if (split < 0) {
|
|
749
|
+
return -1;
|
|
750
|
+
}
|
|
751
|
+
re_frag f;
|
|
752
|
+
if (c_node(c, a->a, &f, depth + 1) < 0) {
|
|
753
|
+
return -1;
|
|
754
|
+
}
|
|
755
|
+
if (f.in >= 0) {
|
|
756
|
+
c->inst[split].x = (uint16_t)f.in;
|
|
757
|
+
frag_connect(&f, c->inst, split); /* the body loops back */
|
|
758
|
+
} else {
|
|
759
|
+
c->inst[split].x = (uint16_t)split; /* empty body: exit only */
|
|
760
|
+
}
|
|
761
|
+
if (acc.in >= 0) {
|
|
762
|
+
frag_connect(&acc, c->inst, split); /* the prefix enters */
|
|
763
|
+
}
|
|
764
|
+
*out = acc; /* every arrow resolved: bits are clear */
|
|
765
|
+
if (out->in < 0) {
|
|
766
|
+
out->in = split;
|
|
767
|
+
}
|
|
768
|
+
frag_bit(out, split, 1); /* the star's one exit */
|
|
769
|
+
return 0;
|
|
770
|
+
}
|
|
771
|
+
/* the bounded tail: (max - min) optional copies, each
|
|
772
|
+
* SPLIT(body_i, EXIT) — body exits chain into the next copy's
|
|
773
|
+
* split, every EXIT rides pending toward the final target */
|
|
774
|
+
re_frag chain, exits;
|
|
775
|
+
chain = acc; /* the prefix's exits fall into the first split */
|
|
776
|
+
frag_init(&exits, -1);
|
|
777
|
+
uint16_t opt = (uint16_t)(a->max - a->min);
|
|
778
|
+
for (uint16_t i = 0; i < opt; i++) {
|
|
779
|
+
int split = emit_inst(c, RE_OP_SPLIT, 0, 0xFFFF, 0xFFFF);
|
|
780
|
+
if (split < 0) {
|
|
781
|
+
return -1;
|
|
782
|
+
}
|
|
783
|
+
frag_connect(&chain, c->inst, split); /* entries -> this copy */
|
|
784
|
+
re_frag f;
|
|
785
|
+
if (c_node(c, a->a, &f, depth + 1) < 0) {
|
|
786
|
+
return -1;
|
|
787
|
+
}
|
|
788
|
+
if (f.in >= 0) {
|
|
789
|
+
c->inst[split].x = (uint16_t)f.in;
|
|
790
|
+
} else {
|
|
791
|
+
frag_bit(&chain, split, 0); /* empty copy: both arms exit */
|
|
792
|
+
}
|
|
793
|
+
frag_or(&chain, &f); /* the copy's exits join the chain */
|
|
794
|
+
frag_bit(&exits, split, 1); /* the skip arm */
|
|
795
|
+
if (acc.in < 0) {
|
|
796
|
+
acc.in = split; /* entry = the first split */
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
out->in = acc.in;
|
|
800
|
+
frag_init(out, out->in);
|
|
801
|
+
frag_or(out, &chain); /* the last body's exits reach the end */
|
|
802
|
+
frag_or(out, &exits);
|
|
803
|
+
return 0;
|
|
804
|
+
}
|
|
805
|
+
default:
|
|
806
|
+
c->err = 1;
|
|
807
|
+
return -1;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
/* ---- the public search: Pike VM over the subject ----------------------- */
|
|
812
|
+
|
|
813
|
+
int yep_re_search(const char* pat, uint32_t pat_len, const char* s, uint32_t s_len) {
|
|
814
|
+
if (pat == NULL || pat_len == 0 || pat_len > RE_MAX_PAT) {
|
|
815
|
+
return -1;
|
|
816
|
+
}
|
|
817
|
+
re_ctx c;
|
|
818
|
+
memset(&c, 0, sizeof(c));
|
|
819
|
+
c.pat = pat;
|
|
820
|
+
c.pn = pat_len;
|
|
821
|
+
|
|
822
|
+
int root = p_alt(&c, 0);
|
|
823
|
+
if (root < 0 || c.pi != c.pn) {
|
|
824
|
+
return -1; /* trailing garbage is a SyntaxError */
|
|
825
|
+
}
|
|
826
|
+
re_frag f;
|
|
827
|
+
if (c_node(&c, root, &f, 0) < 0) {
|
|
828
|
+
return -1;
|
|
829
|
+
}
|
|
830
|
+
if (emit_inst(&c, RE_OP_MATCH, 0, 0, 0) < 0) {
|
|
831
|
+
return -1;
|
|
832
|
+
}
|
|
833
|
+
frag_connect(&f, c.inst, c.inst_n - 1); /* MATCH emits last */
|
|
834
|
+
if (c.err) {
|
|
835
|
+
return -1;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
uint8_t mark[RE_MAX_INST];
|
|
839
|
+
uint16_t cur[RE_MAX_INST], nxt[RE_MAX_INST];
|
|
840
|
+
/* closure depth: the seeds (m+1: live threads + the unanchored
|
|
841
|
+
* restart) plus one net push per newly marked instruction (m) —
|
|
842
|
+
* 2m+2, not 2m (a 320-instruction program once had one 2-byte
|
|
843
|
+
* overflow slot) */
|
|
844
|
+
uint16_t stack[RE_MAX_INST * 2 + 2];
|
|
845
|
+
uint32_t gen = 1;
|
|
846
|
+
memset(mark, 0, sizeof(mark));
|
|
847
|
+
|
|
848
|
+
if (f.in < 0) {
|
|
849
|
+
return 1; /* an empty pattern matches everywhere */
|
|
850
|
+
}
|
|
851
|
+
/* initial closure at subject offset 0 (from the fragment entry;
|
|
852
|
+
* MATCH is emitted LAST, so pc 0 is a pattern instruction) */
|
|
853
|
+
int cur_n = 0;
|
|
854
|
+
{
|
|
855
|
+
int st_n = 0;
|
|
856
|
+
stack[st_n++] = (uint16_t)f.in;
|
|
857
|
+
while (st_n > 0) {
|
|
858
|
+
uint16_t pc = stack[--st_n];
|
|
859
|
+
if (mark[pc] == (uint8_t)gen) {
|
|
860
|
+
continue;
|
|
861
|
+
}
|
|
862
|
+
mark[pc] = (uint8_t)gen;
|
|
863
|
+
uint8_t op = c.inst[pc].op;
|
|
864
|
+
if (op == RE_OP_JMP) {
|
|
865
|
+
stack[st_n++] = c.inst[pc].x;
|
|
866
|
+
} else if (op == RE_OP_SPLIT) {
|
|
867
|
+
stack[st_n++] = c.inst[pc].x;
|
|
868
|
+
stack[st_n++] = c.inst[pc].y;
|
|
869
|
+
} else if (op == RE_OP_MATCH) {
|
|
870
|
+
return 1; /* the empty match at offset 0 */
|
|
871
|
+
} else if (op == RE_OP_ASSERT) {
|
|
872
|
+
/* offset 0: BOL always holds; EOL only on the empty
|
|
873
|
+
* subject; a word boundary holds exactly when the
|
|
874
|
+
* subject starts inside a word (no previous char) */
|
|
875
|
+
int ok = 0;
|
|
876
|
+
switch (c.inst[pc].cls) {
|
|
877
|
+
case RE_AS_BOL:
|
|
878
|
+
ok = 1;
|
|
879
|
+
break;
|
|
880
|
+
case RE_AS_EOL:
|
|
881
|
+
ok = (int)s_len == 0;
|
|
882
|
+
break;
|
|
883
|
+
default: {
|
|
884
|
+
int wn = s_len > 0 && re_word((unsigned char)s[0]);
|
|
885
|
+
ok = c.inst[pc].cls == RE_AS_WBOUND ? (int)wn : !(int)wn;
|
|
886
|
+
break;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
if (ok) {
|
|
890
|
+
stack[st_n++] = c.inst[pc].x; /* the connected exit */
|
|
891
|
+
}
|
|
892
|
+
} else {
|
|
893
|
+
cur[cur_n++] = pc; /* CHAR/BYTE: a live thread */
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
for (uint32_t sp = 0; sp < s_len; sp++) {
|
|
898
|
+
gen++;
|
|
899
|
+
if ((gen & 0xFF) == 0) { /* the mark generation wrapped */
|
|
900
|
+
memset(mark, 0, sizeof(mark));
|
|
901
|
+
gen++;
|
|
902
|
+
}
|
|
903
|
+
uint8_t byte = (unsigned char)s[sp];
|
|
904
|
+
int nxt_n = 0;
|
|
905
|
+
for (int i = 0; i < cur_n; i++) {
|
|
906
|
+
uint16_t pc = cur[i];
|
|
907
|
+
int hit = c.inst[pc].op == RE_OP_BYTE
|
|
908
|
+
? (c.inst[pc].cls == byte)
|
|
909
|
+
: (c.cls[c.inst[pc].cls][(byte >> 3) & 31] & (1u << (byte & 7))) != 0;
|
|
910
|
+
if (hit) {
|
|
911
|
+
/* the exit is the CONNECTED target (frag_connect), not
|
|
912
|
+
* a fall-through: fragments link arbitrary pcs */
|
|
913
|
+
nxt[nxt_n++] = c.inst[pc].x;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
nxt[nxt_n++] = (uint16_t)f.in; /* unanchored: the search restarts
|
|
917
|
+
* at every offset */
|
|
918
|
+
gen++;
|
|
919
|
+
if ((gen & 0xFF) == 0) {
|
|
920
|
+
memset(mark, 0, sizeof(mark));
|
|
921
|
+
gen++;
|
|
922
|
+
}
|
|
923
|
+
/* closure at subject offset sp + 1 */
|
|
924
|
+
int st_n = 0, live_n = 0, matched = 0;
|
|
925
|
+
for (int i = 0; i < nxt_n; i++) {
|
|
926
|
+
stack[st_n++] = nxt[i];
|
|
927
|
+
}
|
|
928
|
+
while (st_n > 0) {
|
|
929
|
+
uint16_t pc = stack[--st_n];
|
|
930
|
+
if (mark[pc] == (uint8_t)gen) {
|
|
931
|
+
continue;
|
|
932
|
+
}
|
|
933
|
+
mark[pc] = (uint8_t)gen;
|
|
934
|
+
uint8_t op = c.inst[pc].op;
|
|
935
|
+
if (op == RE_OP_JMP) {
|
|
936
|
+
stack[st_n++] = c.inst[pc].x;
|
|
937
|
+
} else if (op == RE_OP_SPLIT) {
|
|
938
|
+
stack[st_n++] = c.inst[pc].x;
|
|
939
|
+
stack[st_n++] = c.inst[pc].y;
|
|
940
|
+
} else if (op == RE_OP_MATCH) {
|
|
941
|
+
matched = 1;
|
|
942
|
+
} else if (op == RE_OP_ASSERT) {
|
|
943
|
+
int ok = 0;
|
|
944
|
+
switch (c.inst[pc].cls) {
|
|
945
|
+
case RE_AS_BOL:
|
|
946
|
+
ok = (sp + 1) == 0;
|
|
947
|
+
break;
|
|
948
|
+
case RE_AS_EOL:
|
|
949
|
+
ok = (int)(sp + 1) == (int)s_len;
|
|
950
|
+
break;
|
|
951
|
+
default: {
|
|
952
|
+
int wp = re_word((unsigned char)s[sp]);
|
|
953
|
+
int wn = (sp + 1) < (uint32_t)s_len && re_word((unsigned char)s[sp + 1]);
|
|
954
|
+
ok = c.inst[pc].cls == RE_AS_WBOUND ? (wp != wn) : (wp == wn);
|
|
955
|
+
break;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
if (ok) {
|
|
959
|
+
stack[st_n++] = c.inst[pc].x; /* the connected exit */
|
|
960
|
+
}
|
|
961
|
+
} else {
|
|
962
|
+
nxt[live_n++] = pc; /* a live thread for the next byte */
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
if (matched) {
|
|
966
|
+
return 1;
|
|
967
|
+
}
|
|
968
|
+
memcpy(cur, nxt, (size_t)live_n * sizeof(uint16_t));
|
|
969
|
+
cur_n = live_n;
|
|
970
|
+
}
|
|
971
|
+
return 0;
|
|
972
|
+
}
|