yarp 0.12.0 → 0.13.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 +4 -4
- data/CHANGELOG.md +29 -8
- data/CONTRIBUTING.md +2 -2
- data/Makefile +5 -5
- data/README.md +11 -12
- data/config.yml +6 -2
- data/docs/build_system.md +21 -21
- data/docs/building.md +4 -4
- data/docs/configuration.md +25 -21
- data/docs/design.md +2 -2
- data/docs/encoding.md +17 -17
- data/docs/fuzzing.md +4 -4
- data/docs/heredocs.md +3 -3
- data/docs/mapping.md +94 -94
- data/docs/ripper.md +4 -4
- data/docs/ruby_api.md +11 -11
- data/docs/serialization.md +17 -16
- data/docs/testing.md +6 -6
- data/ext/prism/api_node.c +4725 -0
- data/ext/{yarp → prism}/api_pack.c +82 -82
- data/ext/{yarp → prism}/extconf.rb +13 -13
- data/ext/{yarp → prism}/extension.c +175 -168
- data/ext/prism/extension.h +18 -0
- data/include/prism/ast.h +1932 -0
- data/include/prism/defines.h +45 -0
- data/include/prism/diagnostic.h +231 -0
- data/include/{yarp/enc/yp_encoding.h → prism/enc/pm_encoding.h} +40 -40
- data/include/prism/node.h +41 -0
- data/include/prism/pack.h +141 -0
- data/include/{yarp → prism}/parser.h +143 -142
- data/include/prism/regexp.h +19 -0
- data/include/prism/unescape.h +48 -0
- data/include/prism/util/pm_buffer.h +51 -0
- data/include/{yarp/util/yp_char.h → prism/util/pm_char.h} +20 -20
- data/include/{yarp/util/yp_constant_pool.h → prism/util/pm_constant_pool.h} +26 -22
- data/include/{yarp/util/yp_list.h → prism/util/pm_list.h} +21 -21
- data/include/prism/util/pm_memchr.h +14 -0
- data/include/{yarp/util/yp_newline_list.h → prism/util/pm_newline_list.h} +11 -11
- data/include/prism/util/pm_state_stack.h +24 -0
- data/include/{yarp/util/yp_string.h → prism/util/pm_string.h} +20 -20
- data/include/prism/util/pm_string_list.h +25 -0
- data/include/{yarp/util/yp_strpbrk.h → prism/util/pm_strpbrk.h} +7 -7
- data/include/prism/version.h +4 -0
- data/include/prism.h +82 -0
- data/lib/prism/compiler.rb +465 -0
- data/lib/prism/debug.rb +157 -0
- data/lib/{yarp/desugar_visitor.rb → prism/desugar_compiler.rb} +4 -2
- data/lib/prism/dispatcher.rb +2051 -0
- data/lib/prism/dsl.rb +750 -0
- data/lib/{yarp → prism}/ffi.rb +66 -67
- data/lib/{yarp → prism}/lex_compat.rb +40 -43
- data/lib/{yarp/mutation_visitor.rb → prism/mutation_compiler.rb} +3 -3
- data/lib/{yarp → prism}/node.rb +2012 -2593
- data/lib/prism/node_ext.rb +55 -0
- data/lib/prism/node_inspector.rb +68 -0
- data/lib/{yarp → prism}/pack.rb +1 -1
- data/lib/{yarp → prism}/parse_result/comments.rb +1 -1
- data/lib/{yarp → prism}/parse_result/newlines.rb +1 -1
- data/lib/prism/parse_result.rb +266 -0
- data/lib/{yarp → prism}/pattern.rb +14 -14
- data/lib/{yarp → prism}/ripper_compat.rb +5 -5
- data/lib/{yarp → prism}/serialize.rb +12 -7
- data/lib/prism/visitor.rb +470 -0
- data/lib/prism.rb +64 -0
- data/lib/yarp.rb +2 -614
- data/src/diagnostic.c +213 -208
- data/src/enc/pm_big5.c +52 -0
- data/src/enc/pm_euc_jp.c +58 -0
- data/src/enc/{yp_gbk.c → pm_gbk.c} +16 -16
- data/src/enc/pm_shift_jis.c +56 -0
- data/src/enc/{yp_tables.c → pm_tables.c} +69 -69
- data/src/enc/{yp_unicode.c → pm_unicode.c} +40 -40
- data/src/enc/pm_windows_31j.c +56 -0
- data/src/node.c +1293 -1233
- data/src/pack.c +247 -247
- data/src/prettyprint.c +1479 -1479
- data/src/{yarp.c → prism.c} +5205 -5083
- data/src/regexp.c +132 -132
- data/src/serialize.c +1121 -1121
- data/src/token_type.c +169 -167
- data/src/unescape.c +106 -87
- data/src/util/pm_buffer.c +103 -0
- data/src/util/{yp_char.c → pm_char.c} +72 -72
- data/src/util/{yp_constant_pool.c → pm_constant_pool.c} +85 -64
- data/src/util/{yp_list.c → pm_list.c} +10 -10
- data/src/util/{yp_memchr.c → pm_memchr.c} +6 -4
- data/src/util/{yp_newline_list.c → pm_newline_list.c} +21 -21
- data/src/util/{yp_state_stack.c → pm_state_stack.c} +4 -4
- data/src/util/{yp_string.c → pm_string.c} +38 -38
- data/src/util/pm_string_list.c +29 -0
- data/src/util/{yp_strncasecmp.c → pm_strncasecmp.c} +1 -1
- data/src/util/{yp_strpbrk.c → pm_strpbrk.c} +8 -8
- data/yarp.gemspec +68 -59
- metadata +70 -61
- data/ext/yarp/api_node.c +0 -4728
- data/ext/yarp/extension.h +0 -18
- data/include/yarp/ast.h +0 -1929
- data/include/yarp/defines.h +0 -45
- data/include/yarp/diagnostic.h +0 -226
- data/include/yarp/node.h +0 -42
- data/include/yarp/pack.h +0 -141
- data/include/yarp/regexp.h +0 -19
- data/include/yarp/unescape.h +0 -44
- data/include/yarp/util/yp_buffer.h +0 -51
- data/include/yarp/util/yp_memchr.h +0 -14
- data/include/yarp/util/yp_state_stack.h +0 -24
- data/include/yarp/util/yp_string_list.h +0 -25
- data/include/yarp/version.h +0 -4
- data/include/yarp.h +0 -82
- data/src/enc/yp_big5.c +0 -52
- data/src/enc/yp_euc_jp.c +0 -58
- data/src/enc/yp_shift_jis.c +0 -56
- data/src/enc/yp_windows_31j.c +0 -56
- data/src/util/yp_buffer.c +0 -101
- data/src/util/yp_string_list.c +0 -29
data/src/unescape.c
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
#include "
|
|
1
|
+
#include "prism.h"
|
|
2
2
|
|
|
3
3
|
/******************************************************************************/
|
|
4
4
|
/* Character checks */
|
|
5
5
|
/******************************************************************************/
|
|
6
6
|
|
|
7
7
|
static inline bool
|
|
8
|
-
|
|
8
|
+
pm_char_is_hexadecimal_digits(const uint8_t *string, size_t length) {
|
|
9
9
|
for (size_t index = 0; index < length; index++) {
|
|
10
|
-
if (!
|
|
10
|
+
if (!pm_char_is_hexadecimal_digit(string[index])) {
|
|
11
11
|
return false;
|
|
12
12
|
}
|
|
13
13
|
}
|
|
@@ -18,7 +18,7 @@ yp_char_is_hexadecimal_digits(const uint8_t *string, size_t length) {
|
|
|
18
18
|
// expensive to go through the indirection of the function pointer. Instead we
|
|
19
19
|
// provide a fast path that will check if we can just return 1.
|
|
20
20
|
static inline size_t
|
|
21
|
-
|
|
21
|
+
pm_char_width(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
|
|
22
22
|
if (parser->encoding_changed || (*start >= 0x80)) {
|
|
23
23
|
return parser->encoding.char_width(start, end - start);
|
|
24
24
|
} else {
|
|
@@ -71,11 +71,11 @@ char_is_ascii_printable(const uint8_t b) {
|
|
|
71
71
|
static inline size_t
|
|
72
72
|
unescape_octal(const uint8_t *backslash, uint8_t *value, const uint8_t *end) {
|
|
73
73
|
*value = (uint8_t) (backslash[1] - '0');
|
|
74
|
-
if (backslash + 2 >= end || !
|
|
74
|
+
if (backslash + 2 >= end || !pm_char_is_octal_digit(backslash[2])) {
|
|
75
75
|
return 2;
|
|
76
76
|
}
|
|
77
77
|
*value = (uint8_t) ((*value << 3) | (backslash[2] - '0'));
|
|
78
|
-
if (backslash + 3 >= end || !
|
|
78
|
+
if (backslash + 3 >= end || !pm_char_is_octal_digit(backslash[3])) {
|
|
79
79
|
return 3;
|
|
80
80
|
}
|
|
81
81
|
*value = (uint8_t) ((*value << 3) | (backslash[3] - '0'));
|
|
@@ -91,14 +91,14 @@ unescape_hexadecimal_digit(const uint8_t value) {
|
|
|
91
91
|
// Scan the 1-2 digits of hexadecimal into the value. Returns the number of
|
|
92
92
|
// digits scanned.
|
|
93
93
|
static inline size_t
|
|
94
|
-
unescape_hexadecimal(const uint8_t *backslash, uint8_t *value, const uint8_t *end,
|
|
94
|
+
unescape_hexadecimal(const uint8_t *backslash, uint8_t *value, const uint8_t *end, pm_list_t *error_list) {
|
|
95
95
|
*value = 0;
|
|
96
|
-
if (backslash + 2 >= end || !
|
|
97
|
-
if (error_list)
|
|
96
|
+
if (backslash + 2 >= end || !pm_char_is_hexadecimal_digit(backslash[2])) {
|
|
97
|
+
if (error_list) pm_diagnostic_list_append(error_list, backslash, backslash + 2, PM_ERR_ESCAPE_INVALID_HEXADECIMAL);
|
|
98
98
|
return 2;
|
|
99
99
|
}
|
|
100
100
|
*value = unescape_hexadecimal_digit(backslash[2]);
|
|
101
|
-
if (backslash + 3 >= end || !
|
|
101
|
+
if (backslash + 3 >= end || !pm_char_is_hexadecimal_digit(backslash[3])) {
|
|
102
102
|
return 3;
|
|
103
103
|
}
|
|
104
104
|
*value = (uint8_t) ((*value << 4) | unescape_hexadecimal_digit(backslash[3]));
|
|
@@ -121,7 +121,7 @@ unescape_unicode(const uint8_t *string, size_t length, uint32_t *value) {
|
|
|
121
121
|
// 32-bit value to write. Writes the UTF-8 representation of the value to the
|
|
122
122
|
// string and returns the number of bytes written.
|
|
123
123
|
static inline size_t
|
|
124
|
-
unescape_unicode_write(uint8_t *dest, uint32_t value, const uint8_t *start, const uint8_t *end,
|
|
124
|
+
unescape_unicode_write(uint8_t *dest, uint32_t value, const uint8_t *start, const uint8_t *end, pm_list_t *error_list) {
|
|
125
125
|
if (value <= 0x7F) {
|
|
126
126
|
// 0xxxxxxx
|
|
127
127
|
dest[0] = (uint8_t) value;
|
|
@@ -157,7 +157,7 @@ unescape_unicode_write(uint8_t *dest, uint32_t value, const uint8_t *start, cons
|
|
|
157
157
|
// If we get here, then the value is too big. This is an error, but we don't
|
|
158
158
|
// want to just crash, so instead we'll add an error to the error list and put
|
|
159
159
|
// in a replacement character instead.
|
|
160
|
-
if (error_list)
|
|
160
|
+
if (error_list) pm_diagnostic_list_append(error_list, start, end, PM_ERR_ESCAPE_INVALID_UNICODE);
|
|
161
161
|
dest[0] = 0xEF;
|
|
162
162
|
dest[1] = 0xBF;
|
|
163
163
|
dest[2] = 0xBD;
|
|
@@ -165,20 +165,20 @@ unescape_unicode_write(uint8_t *dest, uint32_t value, const uint8_t *start, cons
|
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
typedef enum {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
168
|
+
PM_UNESCAPE_FLAG_NONE = 0,
|
|
169
|
+
PM_UNESCAPE_FLAG_CONTROL = 1,
|
|
170
|
+
PM_UNESCAPE_FLAG_META = 2,
|
|
171
|
+
PM_UNESCAPE_FLAG_EXPECT_SINGLE = 4
|
|
172
|
+
} pm_unescape_flag_t;
|
|
173
173
|
|
|
174
174
|
// Unescape a single character value based on the given flags.
|
|
175
175
|
static inline uint8_t
|
|
176
176
|
unescape_char(uint8_t value, const uint8_t flags) {
|
|
177
|
-
if (flags &
|
|
177
|
+
if (flags & PM_UNESCAPE_FLAG_CONTROL) {
|
|
178
178
|
value &= 0x1f;
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
if (flags &
|
|
181
|
+
if (flags & PM_UNESCAPE_FLAG_META) {
|
|
182
182
|
value |= 0x80;
|
|
183
183
|
}
|
|
184
184
|
|
|
@@ -188,13 +188,13 @@ unescape_char(uint8_t value, const uint8_t flags) {
|
|
|
188
188
|
// Read a specific escape sequence into the given destination.
|
|
189
189
|
static const uint8_t *
|
|
190
190
|
unescape(
|
|
191
|
-
|
|
191
|
+
pm_parser_t *parser,
|
|
192
192
|
uint8_t *dest,
|
|
193
193
|
size_t *dest_length,
|
|
194
194
|
const uint8_t *backslash,
|
|
195
195
|
const uint8_t *end,
|
|
196
196
|
const uint8_t flags,
|
|
197
|
-
|
|
197
|
+
pm_list_t *error_list
|
|
198
198
|
) {
|
|
199
199
|
switch (backslash[1]) {
|
|
200
200
|
case 'a':
|
|
@@ -234,8 +234,8 @@ unescape(
|
|
|
234
234
|
// \u{nnnn ...} Unicode character(s), where each nnnn is 1-6 hexadecimal digits ([0-9a-fA-F])
|
|
235
235
|
// \unnnn Unicode character, where nnnn is exactly 4 hexadecimal digits ([0-9a-fA-F])
|
|
236
236
|
case 'u': {
|
|
237
|
-
if ((flags &
|
|
238
|
-
if (error_list)
|
|
237
|
+
if ((flags & PM_UNESCAPE_FLAG_CONTROL) | (flags & PM_UNESCAPE_FLAG_META)) {
|
|
238
|
+
if (error_list) pm_diagnostic_list_append(error_list, backslash, backslash + 2, PM_ERR_ESCAPE_INVALID_UNICODE_CM_FLAGS);
|
|
239
239
|
return backslash + 2;
|
|
240
240
|
}
|
|
241
241
|
|
|
@@ -244,26 +244,26 @@ unescape(
|
|
|
244
244
|
const uint8_t *extra_codepoints_start = NULL;
|
|
245
245
|
int codepoints_count = 0;
|
|
246
246
|
|
|
247
|
-
unicode_cursor +=
|
|
247
|
+
unicode_cursor += pm_strspn_whitespace(unicode_cursor, end - unicode_cursor);
|
|
248
248
|
|
|
249
249
|
while ((unicode_cursor < end) && (*unicode_cursor != '}')) {
|
|
250
250
|
const uint8_t *unicode_start = unicode_cursor;
|
|
251
|
-
size_t hexadecimal_length =
|
|
251
|
+
size_t hexadecimal_length = pm_strspn_hexadecimal_digit(unicode_cursor, end - unicode_cursor);
|
|
252
252
|
|
|
253
253
|
// \u{nnnn} character literal allows only 1-6 hexadecimal digits
|
|
254
254
|
if (hexadecimal_length > 6) {
|
|
255
|
-
if (error_list)
|
|
255
|
+
if (error_list) pm_diagnostic_list_append(error_list, unicode_cursor, unicode_cursor + hexadecimal_length, PM_ERR_ESCAPE_INVALID_UNICODE_LONG);
|
|
256
256
|
}
|
|
257
257
|
// there are not hexadecimal characters
|
|
258
258
|
else if (hexadecimal_length == 0) {
|
|
259
|
-
if (error_list)
|
|
259
|
+
if (error_list) pm_diagnostic_list_append(error_list, unicode_cursor, unicode_cursor + hexadecimal_length, PM_ERR_ESCAPE_INVALID_UNICODE);
|
|
260
260
|
return unicode_cursor;
|
|
261
261
|
}
|
|
262
262
|
|
|
263
263
|
unicode_cursor += hexadecimal_length;
|
|
264
264
|
|
|
265
265
|
codepoints_count++;
|
|
266
|
-
if (flags &
|
|
266
|
+
if (flags & PM_UNESCAPE_FLAG_EXPECT_SINGLE && codepoints_count == 2)
|
|
267
267
|
extra_codepoints_start = unicode_start;
|
|
268
268
|
|
|
269
269
|
uint32_t value;
|
|
@@ -272,23 +272,23 @@ unescape(
|
|
|
272
272
|
*dest_length += unescape_unicode_write(dest + *dest_length, value, unicode_start, unicode_cursor, error_list);
|
|
273
273
|
}
|
|
274
274
|
|
|
275
|
-
unicode_cursor +=
|
|
275
|
+
unicode_cursor += pm_strspn_whitespace(unicode_cursor, end - unicode_cursor);
|
|
276
276
|
}
|
|
277
277
|
|
|
278
278
|
// ?\u{nnnn} character literal should contain only one codepoint and cannot be like ?\u{nnnn mmmm}
|
|
279
|
-
if (flags &
|
|
280
|
-
if (error_list)
|
|
279
|
+
if (flags & PM_UNESCAPE_FLAG_EXPECT_SINGLE && codepoints_count > 1) {
|
|
280
|
+
if (error_list) pm_diagnostic_list_append(error_list, extra_codepoints_start, unicode_cursor - 1, PM_ERR_ESCAPE_INVALID_UNICODE_LITERAL);
|
|
281
281
|
}
|
|
282
282
|
|
|
283
283
|
if (unicode_cursor < end && *unicode_cursor == '}') {
|
|
284
284
|
unicode_cursor++;
|
|
285
285
|
} else {
|
|
286
|
-
if (error_list)
|
|
286
|
+
if (error_list) pm_diagnostic_list_append(error_list, backslash, unicode_cursor, PM_ERR_ESCAPE_INVALID_UNICODE_TERM);
|
|
287
287
|
}
|
|
288
288
|
|
|
289
289
|
return unicode_cursor;
|
|
290
290
|
}
|
|
291
|
-
else if ((backslash + 5) < end &&
|
|
291
|
+
else if ((backslash + 5) < end && pm_char_is_hexadecimal_digits(backslash + 2, 4)) {
|
|
292
292
|
uint32_t value;
|
|
293
293
|
unescape_unicode(backslash + 2, 4, &value);
|
|
294
294
|
|
|
@@ -298,7 +298,7 @@ unescape(
|
|
|
298
298
|
return backslash + 6;
|
|
299
299
|
}
|
|
300
300
|
|
|
301
|
-
if (error_list)
|
|
301
|
+
if (error_list) pm_diagnostic_list_append(error_list, backslash, backslash + 2, PM_ERR_ESCAPE_INVALID_UNICODE);
|
|
302
302
|
return backslash + 2;
|
|
303
303
|
}
|
|
304
304
|
// \c\M-x meta control character, where x is an ASCII printable character
|
|
@@ -306,18 +306,18 @@ unescape(
|
|
|
306
306
|
// \cx control character, where x is an ASCII printable character
|
|
307
307
|
case 'c':
|
|
308
308
|
if (backslash + 2 >= end) {
|
|
309
|
-
if (error_list)
|
|
309
|
+
if (error_list) pm_diagnostic_list_append(error_list, backslash, backslash + 1, PM_ERR_ESCAPE_INVALID_CONTROL);
|
|
310
310
|
return end;
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
-
if (flags &
|
|
314
|
-
if (error_list)
|
|
313
|
+
if (flags & PM_UNESCAPE_FLAG_CONTROL) {
|
|
314
|
+
if (error_list) pm_diagnostic_list_append(error_list, backslash, backslash + 1, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT);
|
|
315
315
|
return backslash + 2;
|
|
316
316
|
}
|
|
317
317
|
|
|
318
318
|
switch (backslash[2]) {
|
|
319
319
|
case '\\':
|
|
320
|
-
return unescape(parser, dest, dest_length, backslash + 2, end, flags |
|
|
320
|
+
return unescape(parser, dest, dest_length, backslash + 2, end, flags | PM_UNESCAPE_FLAG_CONTROL, error_list);
|
|
321
321
|
case '?':
|
|
322
322
|
if (dest) {
|
|
323
323
|
dest[(*dest_length)++] = unescape_char(0x7f, flags);
|
|
@@ -325,12 +325,12 @@ unescape(
|
|
|
325
325
|
return backslash + 3;
|
|
326
326
|
default: {
|
|
327
327
|
if (!char_is_ascii_printable(backslash[2])) {
|
|
328
|
-
if (error_list)
|
|
328
|
+
if (error_list) pm_diagnostic_list_append(error_list, backslash, backslash + 1, PM_ERR_ESCAPE_INVALID_CONTROL);
|
|
329
329
|
return backslash + 2;
|
|
330
330
|
}
|
|
331
331
|
|
|
332
332
|
if (dest) {
|
|
333
|
-
dest[(*dest_length)++] = unescape_char(backslash[2], flags |
|
|
333
|
+
dest[(*dest_length)++] = unescape_char(backslash[2], flags | PM_UNESCAPE_FLAG_CONTROL);
|
|
334
334
|
}
|
|
335
335
|
return backslash + 3;
|
|
336
336
|
}
|
|
@@ -339,23 +339,23 @@ unescape(
|
|
|
339
339
|
// \C-? delete, ASCII 7Fh (DEL)
|
|
340
340
|
case 'C':
|
|
341
341
|
if (backslash + 3 >= end) {
|
|
342
|
-
if (error_list)
|
|
342
|
+
if (error_list) pm_diagnostic_list_append(error_list, backslash, backslash + 1, PM_ERR_ESCAPE_INVALID_CONTROL);
|
|
343
343
|
return end;
|
|
344
344
|
}
|
|
345
345
|
|
|
346
|
-
if (flags &
|
|
347
|
-
if (error_list)
|
|
346
|
+
if (flags & PM_UNESCAPE_FLAG_CONTROL) {
|
|
347
|
+
if (error_list) pm_diagnostic_list_append(error_list, backslash, backslash + 1, PM_ERR_ESCAPE_INVALID_CONTROL_REPEAT);
|
|
348
348
|
return backslash + 2;
|
|
349
349
|
}
|
|
350
350
|
|
|
351
351
|
if (backslash[2] != '-') {
|
|
352
|
-
if (error_list)
|
|
352
|
+
if (error_list) pm_diagnostic_list_append(error_list, backslash, backslash + 1, PM_ERR_ESCAPE_INVALID_CONTROL);
|
|
353
353
|
return backslash + 2;
|
|
354
354
|
}
|
|
355
355
|
|
|
356
356
|
switch (backslash[3]) {
|
|
357
357
|
case '\\':
|
|
358
|
-
return unescape(parser, dest, dest_length, backslash + 3, end, flags |
|
|
358
|
+
return unescape(parser, dest, dest_length, backslash + 3, end, flags | PM_UNESCAPE_FLAG_CONTROL, error_list);
|
|
359
359
|
case '?':
|
|
360
360
|
if (dest) {
|
|
361
361
|
dest[(*dest_length)++] = unescape_char(0x7f, flags);
|
|
@@ -363,12 +363,12 @@ unescape(
|
|
|
363
363
|
return backslash + 4;
|
|
364
364
|
default:
|
|
365
365
|
if (!char_is_ascii_printable(backslash[3])) {
|
|
366
|
-
if (error_list)
|
|
366
|
+
if (error_list) pm_diagnostic_list_append(error_list, backslash, backslash + 2, PM_ERR_ESCAPE_INVALID_CONTROL);
|
|
367
367
|
return backslash + 2;
|
|
368
368
|
}
|
|
369
369
|
|
|
370
370
|
if (dest) {
|
|
371
|
-
dest[(*dest_length)++] = unescape_char(backslash[3], flags |
|
|
371
|
+
dest[(*dest_length)++] = unescape_char(backslash[3], flags | PM_UNESCAPE_FLAG_CONTROL);
|
|
372
372
|
}
|
|
373
373
|
return backslash + 4;
|
|
374
374
|
}
|
|
@@ -377,32 +377,32 @@ unescape(
|
|
|
377
377
|
// \M-x meta character, where x is an ASCII printable character
|
|
378
378
|
case 'M': {
|
|
379
379
|
if (backslash + 3 >= end) {
|
|
380
|
-
if (error_list)
|
|
380
|
+
if (error_list) pm_diagnostic_list_append(error_list, backslash, backslash + 1, PM_ERR_ESCAPE_INVALID_META);
|
|
381
381
|
return end;
|
|
382
382
|
}
|
|
383
383
|
|
|
384
|
-
if (flags &
|
|
385
|
-
if (error_list)
|
|
384
|
+
if (flags & PM_UNESCAPE_FLAG_META) {
|
|
385
|
+
if (error_list) pm_diagnostic_list_append(error_list, backslash, backslash + 2, PM_ERR_ESCAPE_INVALID_META_REPEAT);
|
|
386
386
|
return backslash + 2;
|
|
387
387
|
}
|
|
388
388
|
|
|
389
389
|
if (backslash[2] != '-') {
|
|
390
|
-
if (error_list)
|
|
390
|
+
if (error_list) pm_diagnostic_list_append(error_list, backslash, backslash + 2, PM_ERR_ESCAPE_INVALID_META);
|
|
391
391
|
return backslash + 2;
|
|
392
392
|
}
|
|
393
393
|
|
|
394
394
|
if (backslash[3] == '\\') {
|
|
395
|
-
return unescape(parser, dest, dest_length, backslash + 3, end, flags |
|
|
395
|
+
return unescape(parser, dest, dest_length, backslash + 3, end, flags | PM_UNESCAPE_FLAG_META, error_list);
|
|
396
396
|
}
|
|
397
397
|
|
|
398
398
|
if (char_is_ascii_printable(backslash[3])) {
|
|
399
399
|
if (dest) {
|
|
400
|
-
dest[(*dest_length)++] = unescape_char(backslash[3], flags |
|
|
400
|
+
dest[(*dest_length)++] = unescape_char(backslash[3], flags | PM_UNESCAPE_FLAG_META);
|
|
401
401
|
}
|
|
402
402
|
return backslash + 4;
|
|
403
403
|
}
|
|
404
404
|
|
|
405
|
-
if (error_list)
|
|
405
|
+
if (error_list) pm_diagnostic_list_append(error_list, backslash, backslash + 2, PM_ERR_ESCAPE_INVALID_META);
|
|
406
406
|
return backslash + 3;
|
|
407
407
|
}
|
|
408
408
|
// \n
|
|
@@ -416,7 +416,7 @@ unescape(
|
|
|
416
416
|
/* fallthrough */
|
|
417
417
|
// In this case we're escaping something that doesn't need escaping.
|
|
418
418
|
default: {
|
|
419
|
-
size_t width =
|
|
419
|
+
size_t width = pm_char_width(parser, backslash + 1, end);
|
|
420
420
|
|
|
421
421
|
if (dest) {
|
|
422
422
|
memcpy(dest + *dest_length, backslash + 1, width);
|
|
@@ -457,13 +457,13 @@ unescape(
|
|
|
457
457
|
// \c? or \C-? delete, ASCII 7Fh (DEL)
|
|
458
458
|
//
|
|
459
459
|
static void
|
|
460
|
-
|
|
461
|
-
if (unescape_type ==
|
|
460
|
+
pm_unescape_manipulate_string_or_char_literal(pm_parser_t *parser, pm_string_t *string, pm_unescape_type_t unescape_type, bool expect_single_codepoint) {
|
|
461
|
+
if (unescape_type == PM_UNESCAPE_NONE) {
|
|
462
462
|
// If we're not unescaping then we can reference the source directly.
|
|
463
463
|
return;
|
|
464
464
|
}
|
|
465
465
|
|
|
466
|
-
const uint8_t *backslash =
|
|
466
|
+
const uint8_t *backslash = pm_memchr(string->source, '\\', string->length, parser->encoding_changed, &parser->encoding);
|
|
467
467
|
|
|
468
468
|
if (backslash == NULL) {
|
|
469
469
|
// Here there are no escapes, so we can reference the source directly.
|
|
@@ -474,7 +474,7 @@ yp_unescape_manipulate_string_or_char_literal(yp_parser_t *parser, yp_string_t *
|
|
|
474
474
|
// within the string.
|
|
475
475
|
uint8_t *allocated = malloc(string->length);
|
|
476
476
|
if (allocated == NULL) {
|
|
477
|
-
|
|
477
|
+
pm_diagnostic_list_append(&parser->error_list, string->source, string->source + string->length, PM_ERR_MALLOC_FAILED);
|
|
478
478
|
return;
|
|
479
479
|
}
|
|
480
480
|
|
|
@@ -509,7 +509,17 @@ yp_unescape_manipulate_string_or_char_literal(yp_parser_t *parser, yp_string_t *
|
|
|
509
509
|
cursor = backslash + 2;
|
|
510
510
|
break;
|
|
511
511
|
default:
|
|
512
|
-
if (unescape_type ==
|
|
512
|
+
if (unescape_type == PM_UNESCAPE_WHITESPACE) {
|
|
513
|
+
if (backslash[1] == '\r' && backslash[2] == '\n') {
|
|
514
|
+
cursor = backslash + 2;
|
|
515
|
+
break;
|
|
516
|
+
}
|
|
517
|
+
if (pm_strspn_whitespace(backslash + 1, 1)) {
|
|
518
|
+
cursor = backslash + 1;
|
|
519
|
+
break;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
if (unescape_type == PM_UNESCAPE_WHITESPACE || unescape_type == PM_UNESCAPE_MINIMAL) {
|
|
513
523
|
// In this case we're escaping something that doesn't need escaping.
|
|
514
524
|
dest[dest_length++] = '\\';
|
|
515
525
|
cursor = backslash + 1;
|
|
@@ -518,11 +528,11 @@ yp_unescape_manipulate_string_or_char_literal(yp_parser_t *parser, yp_string_t *
|
|
|
518
528
|
|
|
519
529
|
// This is the only type of unescaping left. In this case we need to
|
|
520
530
|
// handle all of the different unescapes.
|
|
521
|
-
assert(unescape_type ==
|
|
531
|
+
assert(unescape_type == PM_UNESCAPE_ALL);
|
|
522
532
|
|
|
523
|
-
uint8_t flags =
|
|
533
|
+
uint8_t flags = PM_UNESCAPE_FLAG_NONE;
|
|
524
534
|
if (expect_single_codepoint) {
|
|
525
|
-
flags |=
|
|
535
|
+
flags |= PM_UNESCAPE_FLAG_EXPECT_SINGLE;
|
|
526
536
|
}
|
|
527
537
|
|
|
528
538
|
cursor = unescape(parser, dest, &dest_length, backslash, end, flags, &parser->error_list);
|
|
@@ -530,7 +540,7 @@ yp_unescape_manipulate_string_or_char_literal(yp_parser_t *parser, yp_string_t *
|
|
|
530
540
|
}
|
|
531
541
|
|
|
532
542
|
if (end > cursor) {
|
|
533
|
-
backslash =
|
|
543
|
+
backslash = pm_memchr(cursor, '\\', (size_t) (end - cursor), parser->encoding_changed, &parser->encoding);
|
|
534
544
|
} else {
|
|
535
545
|
backslash = NULL;
|
|
536
546
|
}
|
|
@@ -545,30 +555,30 @@ yp_unescape_manipulate_string_or_char_literal(yp_parser_t *parser, yp_string_t *
|
|
|
545
555
|
|
|
546
556
|
// If the string was already allocated, then we need to free that memory
|
|
547
557
|
// here. That's because we're about to override it with the escaped string.
|
|
548
|
-
|
|
558
|
+
pm_string_free(string);
|
|
549
559
|
|
|
550
560
|
// We also need to update the length at the end. This is because every escape
|
|
551
561
|
// reduces the length of the final string, and we don't want garbage at the
|
|
552
562
|
// end.
|
|
553
|
-
|
|
563
|
+
pm_string_owned_init(string, allocated, dest_length + ((size_t) (end - cursor)));
|
|
554
564
|
}
|
|
555
565
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
566
|
+
PRISM_EXPORTED_FUNCTION void
|
|
567
|
+
pm_unescape_manipulate_string(pm_parser_t *parser, pm_string_t *string, pm_unescape_type_t unescape_type) {
|
|
568
|
+
pm_unescape_manipulate_string_or_char_literal(parser, string, unescape_type, false);
|
|
559
569
|
}
|
|
560
570
|
|
|
561
571
|
void
|
|
562
|
-
|
|
563
|
-
|
|
572
|
+
pm_unescape_manipulate_char_literal(pm_parser_t *parser, pm_string_t *string, pm_unescape_type_t unescape_type) {
|
|
573
|
+
pm_unescape_manipulate_string_or_char_literal(parser, string, unescape_type, true);
|
|
564
574
|
}
|
|
565
575
|
|
|
566
|
-
// This function is similar to
|
|
576
|
+
// This function is similar to pm_unescape_manipulate_string, except it doesn't
|
|
567
577
|
// actually perform any string manipulations. Instead, it calculates how long
|
|
568
578
|
// the unescaped character is, and returns that value
|
|
569
579
|
size_t
|
|
570
|
-
|
|
571
|
-
assert(unescape_type !=
|
|
580
|
+
pm_unescape_calculate_difference(pm_parser_t *parser, const uint8_t *backslash, pm_unescape_type_t unescape_type, bool expect_single_codepoint) {
|
|
581
|
+
assert(unescape_type != PM_UNESCAPE_NONE);
|
|
572
582
|
|
|
573
583
|
if (backslash + 1 >= parser->end) {
|
|
574
584
|
return 0;
|
|
@@ -579,17 +589,26 @@ yp_unescape_calculate_difference(yp_parser_t *parser, const uint8_t *backslash,
|
|
|
579
589
|
case '\'':
|
|
580
590
|
return 2;
|
|
581
591
|
default: {
|
|
582
|
-
if (unescape_type ==
|
|
583
|
-
|
|
592
|
+
if (unescape_type == PM_UNESCAPE_WHITESPACE) {
|
|
593
|
+
if (backslash[1] == '\r' && backslash[2] == '\n') {
|
|
594
|
+
return 2;
|
|
595
|
+
}
|
|
596
|
+
size_t whitespace = pm_strspn_whitespace(backslash + 1, 1);
|
|
597
|
+
if (whitespace > 0) {
|
|
598
|
+
return whitespace;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
if (unescape_type == PM_UNESCAPE_WHITESPACE || unescape_type == PM_UNESCAPE_MINIMAL) {
|
|
602
|
+
return 1 + pm_char_width(parser, backslash + 1, parser->end);
|
|
584
603
|
}
|
|
585
604
|
|
|
586
605
|
// This is the only type of unescaping left. In this case we need to
|
|
587
606
|
// handle all of the different unescapes.
|
|
588
|
-
assert(unescape_type ==
|
|
607
|
+
assert(unescape_type == PM_UNESCAPE_ALL);
|
|
589
608
|
|
|
590
|
-
uint8_t flags =
|
|
609
|
+
uint8_t flags = PM_UNESCAPE_FLAG_NONE;
|
|
591
610
|
if (expect_single_codepoint) {
|
|
592
|
-
flags |=
|
|
611
|
+
flags |= PM_UNESCAPE_FLAG_EXPECT_SINGLE;
|
|
593
612
|
}
|
|
594
613
|
|
|
595
614
|
const uint8_t *cursor = unescape(parser, NULL, 0, backslash, parser->end, flags, NULL);
|
|
@@ -603,16 +622,16 @@ yp_unescape_calculate_difference(yp_parser_t *parser, const uint8_t *backslash,
|
|
|
603
622
|
// This is one of the main entry points into the extension. It accepts a source
|
|
604
623
|
// string, a type of unescaping, and a pointer to a result string. It returns a
|
|
605
624
|
// boolean indicating whether or not the unescaping was successful.
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
625
|
+
PRISM_EXPORTED_FUNCTION bool
|
|
626
|
+
pm_unescape_string(const uint8_t *start, size_t length, pm_unescape_type_t unescape_type, pm_string_t *result) {
|
|
627
|
+
pm_parser_t parser;
|
|
628
|
+
pm_parser_init(&parser, start, length, NULL);
|
|
610
629
|
|
|
611
|
-
|
|
612
|
-
|
|
630
|
+
pm_string_shared_init(result, start, start + length);
|
|
631
|
+
pm_unescape_manipulate_string(&parser, result, unescape_type);
|
|
613
632
|
|
|
614
|
-
bool success =
|
|
615
|
-
|
|
633
|
+
bool success = pm_list_empty_p(&parser.error_list);
|
|
634
|
+
pm_parser_free(&parser);
|
|
616
635
|
|
|
617
636
|
return success;
|
|
618
637
|
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#include "prism/util/pm_buffer.h"
|
|
2
|
+
|
|
3
|
+
#define PRISM_BUFFER_INITIAL_SIZE 1024
|
|
4
|
+
|
|
5
|
+
// Return the size of the pm_buffer_t struct.
|
|
6
|
+
size_t
|
|
7
|
+
pm_buffer_sizeof(void) {
|
|
8
|
+
return sizeof(pm_buffer_t);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Initialize a pm_buffer_t with its default values.
|
|
12
|
+
bool
|
|
13
|
+
pm_buffer_init(pm_buffer_t *buffer) {
|
|
14
|
+
buffer->length = 0;
|
|
15
|
+
buffer->capacity = PRISM_BUFFER_INITIAL_SIZE;
|
|
16
|
+
|
|
17
|
+
buffer->value = (char *) malloc(PRISM_BUFFER_INITIAL_SIZE);
|
|
18
|
+
return buffer->value != NULL;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
#undef PRISM_BUFFER_INITIAL_SIZE
|
|
22
|
+
|
|
23
|
+
// Return the value of the buffer.
|
|
24
|
+
char *
|
|
25
|
+
pm_buffer_value(pm_buffer_t *buffer) {
|
|
26
|
+
return buffer->value;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Return the length of the buffer.
|
|
30
|
+
size_t
|
|
31
|
+
pm_buffer_length(pm_buffer_t *buffer) {
|
|
32
|
+
return buffer->length;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Append the given amount of space to the buffer.
|
|
36
|
+
static inline void
|
|
37
|
+
pm_buffer_append_length(pm_buffer_t *buffer, size_t length) {
|
|
38
|
+
size_t next_length = buffer->length + length;
|
|
39
|
+
|
|
40
|
+
if (next_length > buffer->capacity) {
|
|
41
|
+
do {
|
|
42
|
+
buffer->capacity *= 2;
|
|
43
|
+
} while (next_length > buffer->capacity);
|
|
44
|
+
|
|
45
|
+
buffer->value = realloc(buffer->value, buffer->capacity);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
buffer->length = next_length;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Append a generic pointer to memory to the buffer.
|
|
52
|
+
static inline void
|
|
53
|
+
pm_buffer_append(pm_buffer_t *buffer, const void *source, size_t length) {
|
|
54
|
+
pm_buffer_append_length(buffer, length);
|
|
55
|
+
memcpy(buffer->value + (buffer->length - length), source, length);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Append the given amount of space as zeroes to the buffer.
|
|
59
|
+
void
|
|
60
|
+
pm_buffer_append_zeroes(pm_buffer_t *buffer, size_t length) {
|
|
61
|
+
pm_buffer_append_length(buffer, length);
|
|
62
|
+
memset(buffer->value + (buffer->length - length), 0, length);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Append a string to the buffer.
|
|
66
|
+
void
|
|
67
|
+
pm_buffer_append_str(pm_buffer_t *buffer, const char *value, size_t length) {
|
|
68
|
+
pm_buffer_append(buffer, value, length);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Append a list of bytes to the buffer.
|
|
72
|
+
void
|
|
73
|
+
pm_buffer_append_bytes(pm_buffer_t *buffer, const uint8_t *value, size_t length) {
|
|
74
|
+
pm_buffer_append(buffer, (const char *) value, length);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Append a single byte to the buffer.
|
|
78
|
+
void
|
|
79
|
+
pm_buffer_append_u8(pm_buffer_t *buffer, uint8_t value) {
|
|
80
|
+
const void *source = &value;
|
|
81
|
+
pm_buffer_append(buffer, source, sizeof(uint8_t));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Append a 32-bit unsigned integer to the buffer.
|
|
85
|
+
void
|
|
86
|
+
pm_buffer_append_u32(pm_buffer_t *buffer, uint32_t value) {
|
|
87
|
+
if (value < 128) {
|
|
88
|
+
pm_buffer_append_u8(buffer, (uint8_t) value);
|
|
89
|
+
} else {
|
|
90
|
+
uint32_t n = value;
|
|
91
|
+
while (n >= 128) {
|
|
92
|
+
pm_buffer_append_u8(buffer, (uint8_t) (n | 128));
|
|
93
|
+
n >>= 7;
|
|
94
|
+
}
|
|
95
|
+
pm_buffer_append_u8(buffer, (uint8_t) n);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Free the memory associated with the buffer.
|
|
100
|
+
void
|
|
101
|
+
pm_buffer_free(pm_buffer_t *buffer) {
|
|
102
|
+
free(buffer->value);
|
|
103
|
+
}
|