yarp 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CODE_OF_CONDUCT.md +76 -0
- data/CONTRIBUTING.md +51 -0
- data/LICENSE.md +7 -0
- data/Makefile.in +79 -0
- data/README.md +86 -0
- data/config.h.in +25 -0
- data/config.yml +2147 -0
- data/configure +4487 -0
- data/docs/build_system.md +85 -0
- data/docs/building.md +26 -0
- data/docs/configuration.md +56 -0
- data/docs/design.md +53 -0
- data/docs/encoding.md +116 -0
- data/docs/extension.md +20 -0
- data/docs/fuzzing.md +93 -0
- data/docs/heredocs.md +36 -0
- data/docs/mapping.md +117 -0
- data/docs/ripper.md +36 -0
- data/docs/serialization.md +130 -0
- data/docs/testing.md +55 -0
- data/ext/yarp/api_node.c +3680 -0
- data/ext/yarp/api_pack.c +256 -0
- data/ext/yarp/extconf.rb +131 -0
- data/ext/yarp/extension.c +547 -0
- data/ext/yarp/extension.h +18 -0
- data/include/yarp/ast.h +1412 -0
- data/include/yarp/defines.h +54 -0
- data/include/yarp/diagnostic.h +24 -0
- data/include/yarp/enc/yp_encoding.h +94 -0
- data/include/yarp/node.h +36 -0
- data/include/yarp/pack.h +141 -0
- data/include/yarp/parser.h +389 -0
- data/include/yarp/regexp.h +19 -0
- data/include/yarp/unescape.h +42 -0
- data/include/yarp/util/yp_buffer.h +39 -0
- data/include/yarp/util/yp_char.h +75 -0
- data/include/yarp/util/yp_constant_pool.h +64 -0
- data/include/yarp/util/yp_list.h +67 -0
- data/include/yarp/util/yp_memchr.h +14 -0
- data/include/yarp/util/yp_newline_list.h +54 -0
- data/include/yarp/util/yp_state_stack.h +24 -0
- data/include/yarp/util/yp_string.h +57 -0
- data/include/yarp/util/yp_string_list.h +28 -0
- data/include/yarp/util/yp_strpbrk.h +29 -0
- data/include/yarp/version.h +5 -0
- data/include/yarp.h +69 -0
- data/lib/yarp/lex_compat.rb +759 -0
- data/lib/yarp/node.rb +7428 -0
- data/lib/yarp/pack.rb +185 -0
- data/lib/yarp/ripper_compat.rb +174 -0
- data/lib/yarp/serialize.rb +389 -0
- data/lib/yarp.rb +330 -0
- data/src/diagnostic.c +25 -0
- data/src/enc/yp_big5.c +79 -0
- data/src/enc/yp_euc_jp.c +85 -0
- data/src/enc/yp_gbk.c +88 -0
- data/src/enc/yp_shift_jis.c +83 -0
- data/src/enc/yp_tables.c +509 -0
- data/src/enc/yp_unicode.c +2320 -0
- data/src/enc/yp_windows_31j.c +83 -0
- data/src/node.c +2011 -0
- data/src/pack.c +493 -0
- data/src/prettyprint.c +1782 -0
- data/src/regexp.c +580 -0
- data/src/serialize.c +1576 -0
- data/src/token_type.c +347 -0
- data/src/unescape.c +576 -0
- data/src/util/yp_buffer.c +78 -0
- data/src/util/yp_char.c +229 -0
- data/src/util/yp_constant_pool.c +147 -0
- data/src/util/yp_list.c +50 -0
- data/src/util/yp_memchr.c +31 -0
- data/src/util/yp_newline_list.c +119 -0
- data/src/util/yp_state_stack.c +25 -0
- data/src/util/yp_string.c +207 -0
- data/src/util/yp_string_list.c +32 -0
- data/src/util/yp_strncasecmp.c +20 -0
- data/src/util/yp_strpbrk.c +66 -0
- data/src/yarp.c +13211 -0
- data/yarp.gemspec +100 -0
- metadata +125 -0
data/src/regexp.c
ADDED
@@ -0,0 +1,580 @@
|
|
1
|
+
#include "yarp/regexp.h"
|
2
|
+
|
3
|
+
// This is the parser that is going to handle parsing regular expressions.
|
4
|
+
typedef struct {
|
5
|
+
const char *start;
|
6
|
+
const char *cursor;
|
7
|
+
const char *end;
|
8
|
+
yp_string_list_t *named_captures;
|
9
|
+
bool encoding_changed;
|
10
|
+
yp_encoding_t *encoding;
|
11
|
+
} yp_regexp_parser_t;
|
12
|
+
|
13
|
+
// This initializes a new parser with the given source.
|
14
|
+
static void
|
15
|
+
yp_regexp_parser_init(yp_regexp_parser_t *parser, const char *start, const char *end, yp_string_list_t *named_captures, bool encoding_changed, yp_encoding_t *encoding) {
|
16
|
+
*parser = (yp_regexp_parser_t) {
|
17
|
+
.start = start,
|
18
|
+
.cursor = start,
|
19
|
+
.end = end,
|
20
|
+
.named_captures = named_captures,
|
21
|
+
.encoding_changed = encoding_changed,
|
22
|
+
.encoding = encoding
|
23
|
+
};
|
24
|
+
}
|
25
|
+
|
26
|
+
// This appends a new string to the list of named captures.
|
27
|
+
static void
|
28
|
+
yp_regexp_parser_named_capture(yp_regexp_parser_t *parser, const char *start, const char *end) {
|
29
|
+
yp_string_t string;
|
30
|
+
yp_string_shared_init(&string, start, end);
|
31
|
+
yp_string_list_append(parser->named_captures, &string);
|
32
|
+
yp_string_free(&string);
|
33
|
+
}
|
34
|
+
|
35
|
+
// Returns true if the next character is the end of the source.
|
36
|
+
static inline bool
|
37
|
+
yp_regexp_char_is_eof(yp_regexp_parser_t *parser) {
|
38
|
+
return parser->cursor >= parser->end;
|
39
|
+
}
|
40
|
+
|
41
|
+
// Optionally accept a char and consume it if it exists.
|
42
|
+
static inline bool
|
43
|
+
yp_regexp_char_accept(yp_regexp_parser_t *parser, char value) {
|
44
|
+
if (!yp_regexp_char_is_eof(parser) && *parser->cursor == value) {
|
45
|
+
parser->cursor++;
|
46
|
+
return true;
|
47
|
+
}
|
48
|
+
return false;
|
49
|
+
}
|
50
|
+
|
51
|
+
// Expect a character to be present and consume it.
|
52
|
+
static inline bool
|
53
|
+
yp_regexp_char_expect(yp_regexp_parser_t *parser, char value) {
|
54
|
+
if (!yp_regexp_char_is_eof(parser) && *parser->cursor == value) {
|
55
|
+
parser->cursor++;
|
56
|
+
return true;
|
57
|
+
}
|
58
|
+
return false;
|
59
|
+
}
|
60
|
+
|
61
|
+
// This advances the current token to the next instance of the given character.
|
62
|
+
static bool
|
63
|
+
yp_regexp_char_find(yp_regexp_parser_t *parser, char value) {
|
64
|
+
if (yp_regexp_char_is_eof(parser)) {
|
65
|
+
return false;
|
66
|
+
}
|
67
|
+
|
68
|
+
const char *end = (const char *) yp_memchr(parser->cursor, value, (size_t) (parser->end - parser->cursor), parser->encoding_changed, parser->encoding);
|
69
|
+
if (end == NULL) {
|
70
|
+
return false;
|
71
|
+
}
|
72
|
+
|
73
|
+
parser->cursor = end + 1;
|
74
|
+
return true;
|
75
|
+
}
|
76
|
+
|
77
|
+
// Range quantifiers are a special class of quantifiers that look like
|
78
|
+
//
|
79
|
+
// * {digit}
|
80
|
+
// * {digit,}
|
81
|
+
// * {digit,digit}
|
82
|
+
// * {,digit}
|
83
|
+
//
|
84
|
+
// Unfortunately, if there are any spaces in between, then this just becomes a
|
85
|
+
// regular character match expression and we have to backtrack. So when this
|
86
|
+
// function first starts running, we'll create a "save" point and then attempt
|
87
|
+
// to parse the quantifier. If it fails, we'll restore the save point and
|
88
|
+
// return.
|
89
|
+
//
|
90
|
+
// The properly track everything, we're going to build a little state machine.
|
91
|
+
// It looks something like the following:
|
92
|
+
//
|
93
|
+
// ┌───────┐ ┌─────────┐ ────────────┐
|
94
|
+
// ──── lbrace ───> │ start │ ──── digit ───> │ minimum │ │
|
95
|
+
// └───────┘ └─────────┘ <─── digit ─┘
|
96
|
+
// │ │ │
|
97
|
+
// ┌───────┐ │ │ rbrace
|
98
|
+
// │ comma │ <───── comma ┌──── comma ───────┘ │
|
99
|
+
// └───────┘ V V
|
100
|
+
// │ ┌─────────┐ ┌─────────┐
|
101
|
+
// └── digit ──> │ maximum │ ── rbrace ──> │| final |│
|
102
|
+
// └─────────┘ └─────────┘
|
103
|
+
// │ ^
|
104
|
+
// └─ digit ─┘
|
105
|
+
//
|
106
|
+
// Note that by the time we've hit this function, the lbrace has already been
|
107
|
+
// consumed so we're in the start state.
|
108
|
+
static bool
|
109
|
+
yp_regexp_parse_range_quantifier(yp_regexp_parser_t *parser) {
|
110
|
+
const char *savepoint = parser->cursor;
|
111
|
+
|
112
|
+
enum {
|
113
|
+
YP_REGEXP_RANGE_QUANTIFIER_STATE_START,
|
114
|
+
YP_REGEXP_RANGE_QUANTIFIER_STATE_MINIMUM,
|
115
|
+
YP_REGEXP_RANGE_QUANTIFIER_STATE_MAXIMUM,
|
116
|
+
YP_REGEXP_RANGE_QUANTIFIER_STATE_COMMA
|
117
|
+
} state = YP_REGEXP_RANGE_QUANTIFIER_STATE_START;
|
118
|
+
|
119
|
+
while (1) {
|
120
|
+
switch (state) {
|
121
|
+
case YP_REGEXP_RANGE_QUANTIFIER_STATE_START:
|
122
|
+
switch (*parser->cursor) {
|
123
|
+
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
|
124
|
+
parser->cursor++;
|
125
|
+
state = YP_REGEXP_RANGE_QUANTIFIER_STATE_MINIMUM;
|
126
|
+
break;
|
127
|
+
case ',':
|
128
|
+
parser->cursor++;
|
129
|
+
state = YP_REGEXP_RANGE_QUANTIFIER_STATE_COMMA;
|
130
|
+
break;
|
131
|
+
default:
|
132
|
+
parser->cursor = savepoint;
|
133
|
+
return true;
|
134
|
+
}
|
135
|
+
break;
|
136
|
+
case YP_REGEXP_RANGE_QUANTIFIER_STATE_MINIMUM:
|
137
|
+
switch (*parser->cursor) {
|
138
|
+
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
|
139
|
+
parser->cursor++;
|
140
|
+
break;
|
141
|
+
case ',':
|
142
|
+
parser->cursor++;
|
143
|
+
state = YP_REGEXP_RANGE_QUANTIFIER_STATE_MAXIMUM;
|
144
|
+
break;
|
145
|
+
case '}':
|
146
|
+
parser->cursor++;
|
147
|
+
return true;
|
148
|
+
default:
|
149
|
+
parser->cursor = savepoint;
|
150
|
+
return true;
|
151
|
+
}
|
152
|
+
break;
|
153
|
+
case YP_REGEXP_RANGE_QUANTIFIER_STATE_COMMA:
|
154
|
+
switch (*parser->cursor) {
|
155
|
+
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
|
156
|
+
parser->cursor++;
|
157
|
+
state = YP_REGEXP_RANGE_QUANTIFIER_STATE_MAXIMUM;
|
158
|
+
break;
|
159
|
+
default:
|
160
|
+
parser->cursor = savepoint;
|
161
|
+
return true;
|
162
|
+
}
|
163
|
+
break;
|
164
|
+
case YP_REGEXP_RANGE_QUANTIFIER_STATE_MAXIMUM:
|
165
|
+
switch (*parser->cursor) {
|
166
|
+
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
|
167
|
+
parser->cursor++;
|
168
|
+
break;
|
169
|
+
case '}':
|
170
|
+
parser->cursor++;
|
171
|
+
return true;
|
172
|
+
default:
|
173
|
+
parser->cursor = savepoint;
|
174
|
+
return true;
|
175
|
+
}
|
176
|
+
break;
|
177
|
+
}
|
178
|
+
}
|
179
|
+
|
180
|
+
return true;
|
181
|
+
}
|
182
|
+
|
183
|
+
// quantifier : star-quantifier
|
184
|
+
// | plus-quantifier
|
185
|
+
// | optional-quantifier
|
186
|
+
// | range-quantifier
|
187
|
+
// | <empty>
|
188
|
+
// ;
|
189
|
+
static bool
|
190
|
+
yp_regexp_parse_quantifier(yp_regexp_parser_t *parser) {
|
191
|
+
switch (*parser->cursor) {
|
192
|
+
case '*':
|
193
|
+
case '+':
|
194
|
+
case '?':
|
195
|
+
parser->cursor++;
|
196
|
+
return true;
|
197
|
+
case '{':
|
198
|
+
parser->cursor++;
|
199
|
+
return yp_regexp_parse_range_quantifier(parser);
|
200
|
+
default:
|
201
|
+
// In this case there is no quantifier.
|
202
|
+
return true;
|
203
|
+
}
|
204
|
+
}
|
205
|
+
|
206
|
+
// match-posix-class : '[' '[' ':' '^'? CHAR+ ':' ']' ']'
|
207
|
+
// ;
|
208
|
+
static bool
|
209
|
+
yp_regexp_parse_posix_class(yp_regexp_parser_t *parser) {
|
210
|
+
if (!yp_regexp_char_expect(parser, ':')) {
|
211
|
+
return false;
|
212
|
+
}
|
213
|
+
|
214
|
+
yp_regexp_char_accept(parser, '^');
|
215
|
+
|
216
|
+
return (
|
217
|
+
yp_regexp_char_find(parser, ':') &&
|
218
|
+
yp_regexp_char_expect(parser, ']') &&
|
219
|
+
yp_regexp_char_expect(parser, ']')
|
220
|
+
);
|
221
|
+
}
|
222
|
+
|
223
|
+
// Forward declaration because character sets can be nested.
|
224
|
+
static bool
|
225
|
+
yp_regexp_parse_lbracket(yp_regexp_parser_t *parser);
|
226
|
+
|
227
|
+
// match-char-set : '[' '^'? (match-range | match-char)* ']'
|
228
|
+
// ;
|
229
|
+
static bool
|
230
|
+
yp_regexp_parse_character_set(yp_regexp_parser_t *parser) {
|
231
|
+
yp_regexp_char_accept(parser, '^');
|
232
|
+
|
233
|
+
while (!yp_regexp_char_is_eof(parser) && *parser->cursor != ']') {
|
234
|
+
switch (*parser->cursor++) {
|
235
|
+
case '[':
|
236
|
+
yp_regexp_parse_lbracket(parser);
|
237
|
+
break;
|
238
|
+
case '\\':
|
239
|
+
if (!yp_regexp_char_is_eof(parser)) {
|
240
|
+
parser->cursor++;
|
241
|
+
}
|
242
|
+
break;
|
243
|
+
default:
|
244
|
+
// do nothing, we've already advanced the cursor
|
245
|
+
break;
|
246
|
+
}
|
247
|
+
}
|
248
|
+
|
249
|
+
return yp_regexp_char_expect(parser, ']');
|
250
|
+
}
|
251
|
+
|
252
|
+
// A left bracket can either mean a POSIX class or a character set.
|
253
|
+
static bool
|
254
|
+
yp_regexp_parse_lbracket(yp_regexp_parser_t *parser) {
|
255
|
+
const char *reset = parser->cursor;
|
256
|
+
|
257
|
+
if ((parser->cursor + 2 < parser->end) && parser->cursor[0] == '[' && parser->cursor[1] == ':') {
|
258
|
+
parser->cursor++;
|
259
|
+
if (yp_regexp_parse_posix_class(parser)) return true;
|
260
|
+
|
261
|
+
parser->cursor = reset;
|
262
|
+
}
|
263
|
+
|
264
|
+
return yp_regexp_parse_character_set(parser);
|
265
|
+
}
|
266
|
+
|
267
|
+
// Forward declaration here since parsing groups needs to go back up the grammar
|
268
|
+
// to parse expressions within them.
|
269
|
+
static bool
|
270
|
+
yp_regexp_parse_expression(yp_regexp_parser_t *parser);
|
271
|
+
|
272
|
+
// These are the states of the options that are configurable on the regular
|
273
|
+
// expression (or from within a group).
|
274
|
+
typedef enum {
|
275
|
+
YP_REGEXP_OPTION_STATE_INVALID,
|
276
|
+
YP_REGEXP_OPTION_STATE_TOGGLEABLE,
|
277
|
+
YP_REGEXP_OPTION_STATE_ADDABLE,
|
278
|
+
YP_REGEXP_OPTION_STATE_ADDED,
|
279
|
+
YP_REGEXP_OPTION_STATE_REMOVED
|
280
|
+
} yp_regexp_option_state_t;
|
281
|
+
|
282
|
+
// These are the options that are configurable on the regular expression (or
|
283
|
+
// from within a group).
|
284
|
+
#define YP_REGEXP_OPTION_STATE_SLOT_MINIMUM 'a'
|
285
|
+
#define YP_REGEXP_OPTION_STATE_SLOT_MAXIMUM 'x'
|
286
|
+
#define YP_REGEXP_OPTION_STATE_SLOTS (YP_REGEXP_OPTION_STATE_SLOT_MAXIMUM - YP_REGEXP_OPTION_STATE_SLOT_MINIMUM + 1)
|
287
|
+
|
288
|
+
// This is the set of options that are configurable on the regular expression.
|
289
|
+
typedef struct {
|
290
|
+
unsigned char values[YP_REGEXP_OPTION_STATE_SLOTS];
|
291
|
+
} yp_regexp_options_t;
|
292
|
+
|
293
|
+
// Initialize a new set of options to their default values.
|
294
|
+
static void
|
295
|
+
yp_regexp_options_init(yp_regexp_options_t *options) {
|
296
|
+
memset(options, YP_REGEXP_OPTION_STATE_INVALID, sizeof(uint8_t) * YP_REGEXP_OPTION_STATE_SLOTS);
|
297
|
+
options->values['i' - YP_REGEXP_OPTION_STATE_SLOT_MINIMUM] = YP_REGEXP_OPTION_STATE_TOGGLEABLE;
|
298
|
+
options->values['m' - YP_REGEXP_OPTION_STATE_SLOT_MINIMUM] = YP_REGEXP_OPTION_STATE_TOGGLEABLE;
|
299
|
+
options->values['x' - YP_REGEXP_OPTION_STATE_SLOT_MINIMUM] = YP_REGEXP_OPTION_STATE_TOGGLEABLE;
|
300
|
+
options->values['d' - YP_REGEXP_OPTION_STATE_SLOT_MINIMUM] = YP_REGEXP_OPTION_STATE_ADDABLE;
|
301
|
+
options->values['a' - YP_REGEXP_OPTION_STATE_SLOT_MINIMUM] = YP_REGEXP_OPTION_STATE_ADDABLE;
|
302
|
+
options->values['u' - YP_REGEXP_OPTION_STATE_SLOT_MINIMUM] = YP_REGEXP_OPTION_STATE_ADDABLE;
|
303
|
+
}
|
304
|
+
|
305
|
+
// Attempt to add the given option to the set of options. Returns true if it was
|
306
|
+
// added, false if it was already present.
|
307
|
+
static bool
|
308
|
+
yp_regexp_options_add(yp_regexp_options_t *options, unsigned char key) {
|
309
|
+
if (key >= YP_REGEXP_OPTION_STATE_SLOT_MINIMUM && key <= YP_REGEXP_OPTION_STATE_SLOT_MAXIMUM) {
|
310
|
+
key = (unsigned char) (key - YP_REGEXP_OPTION_STATE_SLOT_MINIMUM);
|
311
|
+
|
312
|
+
switch (options->values[key]) {
|
313
|
+
case YP_REGEXP_OPTION_STATE_INVALID:
|
314
|
+
case YP_REGEXP_OPTION_STATE_REMOVED:
|
315
|
+
return false;
|
316
|
+
case YP_REGEXP_OPTION_STATE_TOGGLEABLE:
|
317
|
+
case YP_REGEXP_OPTION_STATE_ADDABLE:
|
318
|
+
options->values[key] = YP_REGEXP_OPTION_STATE_ADDED;
|
319
|
+
return true;
|
320
|
+
case YP_REGEXP_OPTION_STATE_ADDED:
|
321
|
+
return true;
|
322
|
+
}
|
323
|
+
}
|
324
|
+
|
325
|
+
return false;
|
326
|
+
}
|
327
|
+
|
328
|
+
// Attempt to remove the given option from the set of options. Returns true if
|
329
|
+
// it was removed, false if it was already absent.
|
330
|
+
static bool
|
331
|
+
yp_regexp_options_remove(yp_regexp_options_t *options, unsigned char key) {
|
332
|
+
if (key >= YP_REGEXP_OPTION_STATE_SLOT_MINIMUM && key <= YP_REGEXP_OPTION_STATE_SLOT_MAXIMUM) {
|
333
|
+
key = (unsigned char) (key - YP_REGEXP_OPTION_STATE_SLOT_MINIMUM);
|
334
|
+
|
335
|
+
switch (options->values[key]) {
|
336
|
+
case YP_REGEXP_OPTION_STATE_INVALID:
|
337
|
+
case YP_REGEXP_OPTION_STATE_ADDABLE:
|
338
|
+
return false;
|
339
|
+
case YP_REGEXP_OPTION_STATE_TOGGLEABLE:
|
340
|
+
case YP_REGEXP_OPTION_STATE_ADDED:
|
341
|
+
case YP_REGEXP_OPTION_STATE_REMOVED:
|
342
|
+
options->values[key] = YP_REGEXP_OPTION_STATE_REMOVED;
|
343
|
+
return true;
|
344
|
+
}
|
345
|
+
}
|
346
|
+
|
347
|
+
return false;
|
348
|
+
}
|
349
|
+
|
350
|
+
// Groups can have quite a few different patterns for syntax. They basically
|
351
|
+
// just wrap a set of expressions, but they can potentially have options after a
|
352
|
+
// question mark. If there _isn't_ a question mark, then it's just a set of
|
353
|
+
// expressions. If there _is_, then here are the options:
|
354
|
+
//
|
355
|
+
// * (?#...) - inline comments
|
356
|
+
// * (?:subexp) - non-capturing group
|
357
|
+
// * (?=subexp) - positive lookahead
|
358
|
+
// * (?!subexp) - negative lookahead
|
359
|
+
// * (?>subexp) - atomic group
|
360
|
+
// * (?~subexp) - absence operator
|
361
|
+
// * (?<=subexp) - positive lookbehind
|
362
|
+
// * (?<!subexp) - negative lookbehind
|
363
|
+
// * (?<name>subexp) - named capturing group
|
364
|
+
// * (?'name'subexp) - named capturing group
|
365
|
+
// * (?(cond)yes-subexp) - conditional expression
|
366
|
+
// * (?(cond)yes-subexp|no-subexp) - conditional expression
|
367
|
+
// * (?imxdau-imx) - turn on and off configuration
|
368
|
+
// * (?imxdau-imx:subexp) - turn on and off configuration for an expression
|
369
|
+
//
|
370
|
+
static bool
|
371
|
+
yp_regexp_parse_group(yp_regexp_parser_t *parser) {
|
372
|
+
// First, parse any options for the group.
|
373
|
+
if (yp_regexp_char_accept(parser, '?')) {
|
374
|
+
if (yp_regexp_char_is_eof(parser)) {
|
375
|
+
return false;
|
376
|
+
}
|
377
|
+
yp_regexp_options_t options;
|
378
|
+
yp_regexp_options_init(&options);
|
379
|
+
|
380
|
+
switch (*parser->cursor) {
|
381
|
+
case '#': { // inline comments
|
382
|
+
if (parser->encoding_changed && parser->encoding->multibyte) {
|
383
|
+
bool escaped = false;
|
384
|
+
|
385
|
+
// Here we're going to take a slow path and iterate through
|
386
|
+
// each multibyte character to find the close paren. We do
|
387
|
+
// this because \ can be a trailing byte in some encodings.
|
388
|
+
while (parser->cursor < parser->end) {
|
389
|
+
if (!escaped && *parser->cursor == ')') {
|
390
|
+
parser->cursor++;
|
391
|
+
return true;
|
392
|
+
}
|
393
|
+
|
394
|
+
size_t width = parser->encoding->char_width(parser->cursor, (ptrdiff_t) (parser->end - parser->cursor));
|
395
|
+
if (width == 0) return false;
|
396
|
+
|
397
|
+
escaped = (width == 1) && (*parser->cursor == '\\');
|
398
|
+
parser->cursor += width;
|
399
|
+
}
|
400
|
+
|
401
|
+
return false;
|
402
|
+
} else {
|
403
|
+
// Here we can take the fast path and use memchr to find the
|
404
|
+
// next ) because we are safe checking backward for \ since
|
405
|
+
// it cannot be a trailing character.
|
406
|
+
bool found = yp_regexp_char_find(parser, ')');
|
407
|
+
|
408
|
+
while (found && (parser->start <= parser->cursor - 2) && (*(parser->cursor - 2) == '\\')) {
|
409
|
+
found = yp_regexp_char_find(parser, ')');
|
410
|
+
}
|
411
|
+
|
412
|
+
return found;
|
413
|
+
}
|
414
|
+
}
|
415
|
+
case ':': // non-capturing group
|
416
|
+
case '=': // positive lookahead
|
417
|
+
case '!': // negative lookahead
|
418
|
+
case '>': // atomic group
|
419
|
+
case '~': // absence operator
|
420
|
+
parser->cursor++;
|
421
|
+
break;
|
422
|
+
case '<':
|
423
|
+
parser->cursor++;
|
424
|
+
if (yp_regexp_char_is_eof(parser)) {
|
425
|
+
return false;
|
426
|
+
}
|
427
|
+
|
428
|
+
switch (*parser->cursor) {
|
429
|
+
case '=': // positive lookbehind
|
430
|
+
case '!': // negative lookbehind
|
431
|
+
parser->cursor++;
|
432
|
+
break;
|
433
|
+
default: { // named capture group
|
434
|
+
const char *start = parser->cursor;
|
435
|
+
if (!yp_regexp_char_find(parser, '>')) {
|
436
|
+
return false;
|
437
|
+
}
|
438
|
+
yp_regexp_parser_named_capture(parser, start, parser->cursor - 1);
|
439
|
+
break;
|
440
|
+
}
|
441
|
+
}
|
442
|
+
break;
|
443
|
+
case '\'': { // named capture group
|
444
|
+
const char *start = ++parser->cursor;
|
445
|
+
if (!yp_regexp_char_find(parser, '\'')) {
|
446
|
+
return false;
|
447
|
+
}
|
448
|
+
|
449
|
+
yp_regexp_parser_named_capture(parser, start, parser->cursor - 1);
|
450
|
+
break;
|
451
|
+
}
|
452
|
+
case '(': // conditional expression
|
453
|
+
if (!yp_regexp_char_find(parser, ')')) {
|
454
|
+
return false;
|
455
|
+
}
|
456
|
+
break;
|
457
|
+
case 'i': case 'm': case 'x': case 'd': case 'a': case 'u': // options
|
458
|
+
while (!yp_regexp_char_is_eof(parser) && *parser->cursor != '-' && *parser->cursor != ':' && *parser->cursor != ')') {
|
459
|
+
if (!yp_regexp_options_add(&options, (unsigned char) *parser->cursor)) {
|
460
|
+
return false;
|
461
|
+
}
|
462
|
+
parser->cursor++;
|
463
|
+
}
|
464
|
+
|
465
|
+
if (yp_regexp_char_is_eof(parser)) {
|
466
|
+
return false;
|
467
|
+
}
|
468
|
+
|
469
|
+
// If we hit a -, then we're done parsing options.
|
470
|
+
if (*parser->cursor != '-') break;
|
471
|
+
|
472
|
+
// Otherwise, fallthrough to the - case.
|
473
|
+
/* fallthrough */
|
474
|
+
case '-':
|
475
|
+
parser->cursor++;
|
476
|
+
while (!yp_regexp_char_is_eof(parser) && *parser->cursor != ':' && *parser->cursor != ')') {
|
477
|
+
if (!yp_regexp_options_remove(&options, (unsigned char) *parser->cursor)) {
|
478
|
+
return false;
|
479
|
+
}
|
480
|
+
parser->cursor++;
|
481
|
+
}
|
482
|
+
|
483
|
+
if (yp_regexp_char_is_eof(parser)) {
|
484
|
+
return false;
|
485
|
+
}
|
486
|
+
break;
|
487
|
+
default:
|
488
|
+
return false;
|
489
|
+
}
|
490
|
+
}
|
491
|
+
|
492
|
+
// Now, parse the expressions within this group.
|
493
|
+
while (!yp_regexp_char_is_eof(parser) && *parser->cursor != ')') {
|
494
|
+
if (!yp_regexp_parse_expression(parser)) {
|
495
|
+
return false;
|
496
|
+
}
|
497
|
+
yp_regexp_char_accept(parser, '|');
|
498
|
+
}
|
499
|
+
|
500
|
+
// Finally, make sure we have a closing parenthesis.
|
501
|
+
return yp_regexp_char_expect(parser, ')');
|
502
|
+
}
|
503
|
+
|
504
|
+
// item : anchor
|
505
|
+
// | match-posix-class
|
506
|
+
// | match-char-set
|
507
|
+
// | match-char-class
|
508
|
+
// | match-char-prop
|
509
|
+
// | match-char
|
510
|
+
// | match-any
|
511
|
+
// | group
|
512
|
+
// | quantified
|
513
|
+
// ;
|
514
|
+
static bool
|
515
|
+
yp_regexp_parse_item(yp_regexp_parser_t *parser) {
|
516
|
+
switch (*parser->cursor++) {
|
517
|
+
case '^':
|
518
|
+
case '$':
|
519
|
+
return true;
|
520
|
+
case '\\':
|
521
|
+
if (!yp_regexp_char_is_eof(parser)) {
|
522
|
+
parser->cursor++;
|
523
|
+
}
|
524
|
+
return yp_regexp_parse_quantifier(parser);
|
525
|
+
case '(':
|
526
|
+
return yp_regexp_parse_group(parser) && yp_regexp_parse_quantifier(parser);
|
527
|
+
case '[':
|
528
|
+
return yp_regexp_parse_lbracket(parser) && yp_regexp_parse_quantifier(parser);
|
529
|
+
default:
|
530
|
+
return yp_regexp_parse_quantifier(parser);
|
531
|
+
}
|
532
|
+
}
|
533
|
+
|
534
|
+
// expression : item+
|
535
|
+
// ;
|
536
|
+
static bool
|
537
|
+
yp_regexp_parse_expression(yp_regexp_parser_t *parser) {
|
538
|
+
if (!yp_regexp_parse_item(parser)) {
|
539
|
+
return false;
|
540
|
+
}
|
541
|
+
|
542
|
+
while (!yp_regexp_char_is_eof(parser) && *parser->cursor != ')' && *parser->cursor != '|') {
|
543
|
+
if (!yp_regexp_parse_item(parser)) {
|
544
|
+
return false;
|
545
|
+
}
|
546
|
+
}
|
547
|
+
|
548
|
+
return true;
|
549
|
+
}
|
550
|
+
|
551
|
+
// pattern : EOF
|
552
|
+
// | expression EOF
|
553
|
+
// | expression '|' pattern
|
554
|
+
// ;
|
555
|
+
static bool
|
556
|
+
yp_regexp_parse_pattern(yp_regexp_parser_t *parser) {
|
557
|
+
return (
|
558
|
+
(
|
559
|
+
// Exit early if the pattern is empty.
|
560
|
+
yp_regexp_char_is_eof(parser) ||
|
561
|
+
// Parse the first expression in the pattern.
|
562
|
+
yp_regexp_parse_expression(parser)
|
563
|
+
) &&
|
564
|
+
(
|
565
|
+
// Return now if we've parsed the entire pattern.
|
566
|
+
yp_regexp_char_is_eof(parser) ||
|
567
|
+
// Otherwise, we should have a pipe character.
|
568
|
+
(yp_regexp_char_expect(parser, '|') && yp_regexp_parse_pattern(parser))
|
569
|
+
)
|
570
|
+
);
|
571
|
+
}
|
572
|
+
|
573
|
+
// Parse a regular expression and extract the names of all of the named capture
|
574
|
+
// groups.
|
575
|
+
YP_EXPORTED_FUNCTION bool
|
576
|
+
yp_regexp_named_capture_group_names(const char *source, size_t size, yp_string_list_t *named_captures, bool encoding_changed, yp_encoding_t *encoding) {
|
577
|
+
yp_regexp_parser_t parser;
|
578
|
+
yp_regexp_parser_init(&parser, source, source + size, named_captures, encoding_changed, encoding);
|
579
|
+
return yp_regexp_parse_pattern(&parser);
|
580
|
+
}
|