prism 0.13.0 → 0.14.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.
data/src/unescape.c DELETED
@@ -1,637 +0,0 @@
1
- #include "prism.h"
2
-
3
- /******************************************************************************/
4
- /* Character checks */
5
- /******************************************************************************/
6
-
7
- static inline bool
8
- pm_char_is_hexadecimal_digits(const uint8_t *string, size_t length) {
9
- for (size_t index = 0; index < length; index++) {
10
- if (!pm_char_is_hexadecimal_digit(string[index])) {
11
- return false;
12
- }
13
- }
14
- return true;
15
- }
16
-
17
- // We don't call the char_width function unless we have to because it's
18
- // expensive to go through the indirection of the function pointer. Instead we
19
- // provide a fast path that will check if we can just return 1.
20
- static inline size_t
21
- pm_char_width(pm_parser_t *parser, const uint8_t *start, const uint8_t *end) {
22
- if (parser->encoding_changed || (*start >= 0x80)) {
23
- return parser->encoding.char_width(start, end - start);
24
- } else {
25
- return 1;
26
- }
27
- }
28
-
29
- /******************************************************************************/
30
- /* Lookup tables for characters */
31
- /******************************************************************************/
32
-
33
- // This is a lookup table for unescapes that only take up a single character.
34
- static const uint8_t unescape_chars[] = {
35
- ['\''] = '\'',
36
- ['\\'] = '\\',
37
- ['a'] = '\a',
38
- ['b'] = '\b',
39
- ['e'] = '\033',
40
- ['f'] = '\f',
41
- ['n'] = '\n',
42
- ['r'] = '\r',
43
- ['s'] = ' ',
44
- ['t'] = '\t',
45
- ['v'] = '\v'
46
- };
47
-
48
- // This is a lookup table for whether or not an ASCII character is printable.
49
- static const bool ascii_printable_chars[] = {
50
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
51
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
52
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
53
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
54
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
55
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
56
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
57
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0
58
- };
59
-
60
- static inline bool
61
- char_is_ascii_printable(const uint8_t b) {
62
- return (b < 0x80) && ascii_printable_chars[b];
63
- }
64
-
65
- /******************************************************************************/
66
- /* Unescaping for segments */
67
- /******************************************************************************/
68
-
69
- // Scan the 1-3 digits of octal into the value. Returns the number of digits
70
- // scanned.
71
- static inline size_t
72
- unescape_octal(const uint8_t *backslash, uint8_t *value, const uint8_t *end) {
73
- *value = (uint8_t) (backslash[1] - '0');
74
- if (backslash + 2 >= end || !pm_char_is_octal_digit(backslash[2])) {
75
- return 2;
76
- }
77
- *value = (uint8_t) ((*value << 3) | (backslash[2] - '0'));
78
- if (backslash + 3 >= end || !pm_char_is_octal_digit(backslash[3])) {
79
- return 3;
80
- }
81
- *value = (uint8_t) ((*value << 3) | (backslash[3] - '0'));
82
- return 4;
83
- }
84
-
85
- // Convert a hexadecimal digit into its equivalent value.
86
- static inline uint8_t
87
- unescape_hexadecimal_digit(const uint8_t value) {
88
- return (uint8_t) ((value <= '9') ? (value - '0') : (value & 0x7) + 9);
89
- }
90
-
91
- // Scan the 1-2 digits of hexadecimal into the value. Returns the number of
92
- // digits scanned.
93
- static inline size_t
94
- unescape_hexadecimal(const uint8_t *backslash, uint8_t *value, const uint8_t *end, pm_list_t *error_list) {
95
- *value = 0;
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
- return 2;
99
- }
100
- *value = unescape_hexadecimal_digit(backslash[2]);
101
- if (backslash + 3 >= end || !pm_char_is_hexadecimal_digit(backslash[3])) {
102
- return 3;
103
- }
104
- *value = (uint8_t) ((*value << 4) | unescape_hexadecimal_digit(backslash[3]));
105
- return 4;
106
- }
107
-
108
- // Scan the 4 digits of a Unicode escape into the value. Returns the number of
109
- // digits scanned. This function assumes that the characters have already been
110
- // validated.
111
- static inline void
112
- unescape_unicode(const uint8_t *string, size_t length, uint32_t *value) {
113
- *value = 0;
114
- for (size_t index = 0; index < length; index++) {
115
- if (index != 0) *value <<= 4;
116
- *value |= unescape_hexadecimal_digit(string[index]);
117
- }
118
- }
119
-
120
- // Accepts the pointer to the string to write the unicode value along with the
121
- // 32-bit value to write. Writes the UTF-8 representation of the value to the
122
- // string and returns the number of bytes written.
123
- static inline size_t
124
- unescape_unicode_write(uint8_t *dest, uint32_t value, const uint8_t *start, const uint8_t *end, pm_list_t *error_list) {
125
- if (value <= 0x7F) {
126
- // 0xxxxxxx
127
- dest[0] = (uint8_t) value;
128
- return 1;
129
- }
130
-
131
- if (value <= 0x7FF) {
132
- // 110xxxxx 10xxxxxx
133
- dest[0] = (uint8_t) (0xC0 | (value >> 6));
134
- dest[1] = (uint8_t) (0x80 | (value & 0x3F));
135
- return 2;
136
- }
137
-
138
- if (value <= 0xFFFF) {
139
- // 1110xxxx 10xxxxxx 10xxxxxx
140
- dest[0] = (uint8_t) (0xE0 | (value >> 12));
141
- dest[1] = (uint8_t) (0x80 | ((value >> 6) & 0x3F));
142
- dest[2] = (uint8_t) (0x80 | (value & 0x3F));
143
- return 3;
144
- }
145
-
146
- // At this point it must be a 4 digit UTF-8 representation. If it's not, then
147
- // the input is invalid.
148
- if (value <= 0x10FFFF) {
149
- // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
150
- dest[0] = (uint8_t) (0xF0 | (value >> 18));
151
- dest[1] = (uint8_t) (0x80 | ((value >> 12) & 0x3F));
152
- dest[2] = (uint8_t) (0x80 | ((value >> 6) & 0x3F));
153
- dest[3] = (uint8_t) (0x80 | (value & 0x3F));
154
- return 4;
155
- }
156
-
157
- // If we get here, then the value is too big. This is an error, but we don't
158
- // want to just crash, so instead we'll add an error to the error list and put
159
- // in a replacement character instead.
160
- if (error_list) pm_diagnostic_list_append(error_list, start, end, PM_ERR_ESCAPE_INVALID_UNICODE);
161
- dest[0] = 0xEF;
162
- dest[1] = 0xBF;
163
- dest[2] = 0xBD;
164
- return 3;
165
- }
166
-
167
- typedef enum {
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
-
174
- // Unescape a single character value based on the given flags.
175
- static inline uint8_t
176
- unescape_char(uint8_t value, const uint8_t flags) {
177
- if (flags & PM_UNESCAPE_FLAG_CONTROL) {
178
- value &= 0x1f;
179
- }
180
-
181
- if (flags & PM_UNESCAPE_FLAG_META) {
182
- value |= 0x80;
183
- }
184
-
185
- return value;
186
- }
187
-
188
- // Read a specific escape sequence into the given destination.
189
- static const uint8_t *
190
- unescape(
191
- pm_parser_t *parser,
192
- uint8_t *dest,
193
- size_t *dest_length,
194
- const uint8_t *backslash,
195
- const uint8_t *end,
196
- const uint8_t flags,
197
- pm_list_t *error_list
198
- ) {
199
- switch (backslash[1]) {
200
- case 'a':
201
- case 'b':
202
- case 'e':
203
- case 'f':
204
- case 'n':
205
- case 'r':
206
- case 's':
207
- case 't':
208
- case 'v':
209
- if (dest) {
210
- dest[(*dest_length)++] = unescape_char(unescape_chars[backslash[1]], flags);
211
- }
212
- return backslash + 2;
213
- // \nnn octal bit pattern, where nnn is 1-3 octal digits ([0-7])
214
- case '0': case '1': case '2': case '3': case '4':
215
- case '5': case '6': case '7': case '8': case '9': {
216
- uint8_t value;
217
- const uint8_t *cursor = backslash + unescape_octal(backslash, &value, end);
218
-
219
- if (dest) {
220
- dest[(*dest_length)++] = unescape_char(value, flags);
221
- }
222
- return cursor;
223
- }
224
- // \xnn hexadecimal bit pattern, where nn is 1-2 hexadecimal digits ([0-9a-fA-F])
225
- case 'x': {
226
- uint8_t value;
227
- const uint8_t *cursor = backslash + unescape_hexadecimal(backslash, &value, end, error_list);
228
-
229
- if (dest) {
230
- dest[(*dest_length)++] = unescape_char(value, flags);
231
- }
232
- return cursor;
233
- }
234
- // \u{nnnn ...} Unicode character(s), where each nnnn is 1-6 hexadecimal digits ([0-9a-fA-F])
235
- // \unnnn Unicode character, where nnnn is exactly 4 hexadecimal digits ([0-9a-fA-F])
236
- case 'u': {
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
- return backslash + 2;
240
- }
241
-
242
- if ((backslash + 3) < end && backslash[2] == '{') {
243
- const uint8_t *unicode_cursor = backslash + 3;
244
- const uint8_t *extra_codepoints_start = NULL;
245
- int codepoints_count = 0;
246
-
247
- unicode_cursor += pm_strspn_whitespace(unicode_cursor, end - unicode_cursor);
248
-
249
- while ((unicode_cursor < end) && (*unicode_cursor != '}')) {
250
- const uint8_t *unicode_start = unicode_cursor;
251
- size_t hexadecimal_length = pm_strspn_hexadecimal_digit(unicode_cursor, end - unicode_cursor);
252
-
253
- // \u{nnnn} character literal allows only 1-6 hexadecimal digits
254
- if (hexadecimal_length > 6) {
255
- if (error_list) pm_diagnostic_list_append(error_list, unicode_cursor, unicode_cursor + hexadecimal_length, PM_ERR_ESCAPE_INVALID_UNICODE_LONG);
256
- }
257
- // there are not hexadecimal characters
258
- else if (hexadecimal_length == 0) {
259
- if (error_list) pm_diagnostic_list_append(error_list, unicode_cursor, unicode_cursor + hexadecimal_length, PM_ERR_ESCAPE_INVALID_UNICODE);
260
- return unicode_cursor;
261
- }
262
-
263
- unicode_cursor += hexadecimal_length;
264
-
265
- codepoints_count++;
266
- if (flags & PM_UNESCAPE_FLAG_EXPECT_SINGLE && codepoints_count == 2)
267
- extra_codepoints_start = unicode_start;
268
-
269
- uint32_t value;
270
- unescape_unicode(unicode_start, (size_t) (unicode_cursor - unicode_start), &value);
271
- if (dest) {
272
- *dest_length += unescape_unicode_write(dest + *dest_length, value, unicode_start, unicode_cursor, error_list);
273
- }
274
-
275
- unicode_cursor += pm_strspn_whitespace(unicode_cursor, end - unicode_cursor);
276
- }
277
-
278
- // ?\u{nnnn} character literal should contain only one codepoint and cannot be like ?\u{nnnn mmmm}
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
- }
282
-
283
- if (unicode_cursor < end && *unicode_cursor == '}') {
284
- unicode_cursor++;
285
- } else {
286
- if (error_list) pm_diagnostic_list_append(error_list, backslash, unicode_cursor, PM_ERR_ESCAPE_INVALID_UNICODE_TERM);
287
- }
288
-
289
- return unicode_cursor;
290
- }
291
- else if ((backslash + 5) < end && pm_char_is_hexadecimal_digits(backslash + 2, 4)) {
292
- uint32_t value;
293
- unescape_unicode(backslash + 2, 4, &value);
294
-
295
- if (dest) {
296
- *dest_length += unescape_unicode_write(dest + *dest_length, value, backslash + 2, backslash + 6, error_list);
297
- }
298
- return backslash + 6;
299
- }
300
-
301
- if (error_list) pm_diagnostic_list_append(error_list, backslash, backslash + 2, PM_ERR_ESCAPE_INVALID_UNICODE);
302
- return backslash + 2;
303
- }
304
- // \c\M-x meta control character, where x is an ASCII printable character
305
- // \c? delete, ASCII 7Fh (DEL)
306
- // \cx control character, where x is an ASCII printable character
307
- case 'c':
308
- if (backslash + 2 >= end) {
309
- if (error_list) pm_diagnostic_list_append(error_list, backslash, backslash + 1, PM_ERR_ESCAPE_INVALID_CONTROL);
310
- return end;
311
- }
312
-
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
- return backslash + 2;
316
- }
317
-
318
- switch (backslash[2]) {
319
- case '\\':
320
- return unescape(parser, dest, dest_length, backslash + 2, end, flags | PM_UNESCAPE_FLAG_CONTROL, error_list);
321
- case '?':
322
- if (dest) {
323
- dest[(*dest_length)++] = unescape_char(0x7f, flags);
324
- }
325
- return backslash + 3;
326
- default: {
327
- if (!char_is_ascii_printable(backslash[2])) {
328
- if (error_list) pm_diagnostic_list_append(error_list, backslash, backslash + 1, PM_ERR_ESCAPE_INVALID_CONTROL);
329
- return backslash + 2;
330
- }
331
-
332
- if (dest) {
333
- dest[(*dest_length)++] = unescape_char(backslash[2], flags | PM_UNESCAPE_FLAG_CONTROL);
334
- }
335
- return backslash + 3;
336
- }
337
- }
338
- // \C-x control character, where x is an ASCII printable character
339
- // \C-? delete, ASCII 7Fh (DEL)
340
- case 'C':
341
- if (backslash + 3 >= end) {
342
- if (error_list) pm_diagnostic_list_append(error_list, backslash, backslash + 1, PM_ERR_ESCAPE_INVALID_CONTROL);
343
- return end;
344
- }
345
-
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
- return backslash + 2;
349
- }
350
-
351
- if (backslash[2] != '-') {
352
- if (error_list) pm_diagnostic_list_append(error_list, backslash, backslash + 1, PM_ERR_ESCAPE_INVALID_CONTROL);
353
- return backslash + 2;
354
- }
355
-
356
- switch (backslash[3]) {
357
- case '\\':
358
- return unescape(parser, dest, dest_length, backslash + 3, end, flags | PM_UNESCAPE_FLAG_CONTROL, error_list);
359
- case '?':
360
- if (dest) {
361
- dest[(*dest_length)++] = unescape_char(0x7f, flags);
362
- }
363
- return backslash + 4;
364
- default:
365
- if (!char_is_ascii_printable(backslash[3])) {
366
- if (error_list) pm_diagnostic_list_append(error_list, backslash, backslash + 2, PM_ERR_ESCAPE_INVALID_CONTROL);
367
- return backslash + 2;
368
- }
369
-
370
- if (dest) {
371
- dest[(*dest_length)++] = unescape_char(backslash[3], flags | PM_UNESCAPE_FLAG_CONTROL);
372
- }
373
- return backslash + 4;
374
- }
375
- // \M-\C-x meta control character, where x is an ASCII printable character
376
- // \M-\cx meta control character, where x is an ASCII printable character
377
- // \M-x meta character, where x is an ASCII printable character
378
- case 'M': {
379
- if (backslash + 3 >= end) {
380
- if (error_list) pm_diagnostic_list_append(error_list, backslash, backslash + 1, PM_ERR_ESCAPE_INVALID_META);
381
- return end;
382
- }
383
-
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
- return backslash + 2;
387
- }
388
-
389
- if (backslash[2] != '-') {
390
- if (error_list) pm_diagnostic_list_append(error_list, backslash, backslash + 2, PM_ERR_ESCAPE_INVALID_META);
391
- return backslash + 2;
392
- }
393
-
394
- if (backslash[3] == '\\') {
395
- return unescape(parser, dest, dest_length, backslash + 3, end, flags | PM_UNESCAPE_FLAG_META, error_list);
396
- }
397
-
398
- if (char_is_ascii_printable(backslash[3])) {
399
- if (dest) {
400
- dest[(*dest_length)++] = unescape_char(backslash[3], flags | PM_UNESCAPE_FLAG_META);
401
- }
402
- return backslash + 4;
403
- }
404
-
405
- if (error_list) pm_diagnostic_list_append(error_list, backslash, backslash + 2, PM_ERR_ESCAPE_INVALID_META);
406
- return backslash + 3;
407
- }
408
- // \n
409
- case '\n':
410
- return backslash + 2;
411
- // \r
412
- case '\r':
413
- if (backslash + 2 < end && backslash[2] == '\n') {
414
- return backslash + 3;
415
- }
416
- /* fallthrough */
417
- // In this case we're escaping something that doesn't need escaping.
418
- default: {
419
- size_t width = pm_char_width(parser, backslash + 1, end);
420
-
421
- if (dest) {
422
- memcpy(dest + *dest_length, backslash + 1, width);
423
- *dest_length += width;
424
- }
425
-
426
- return backslash + 1 + width;
427
- }
428
- }
429
- }
430
-
431
- /******************************************************************************/
432
- /* Public functions and entrypoints */
433
- /******************************************************************************/
434
-
435
- // Unescape the contents of the given token into the given string using the
436
- // given unescape mode. The supported escapes are:
437
- //
438
- // \a bell, ASCII 07h (BEL)
439
- // \b backspace, ASCII 08h (BS)
440
- // \t horizontal tab, ASCII 09h (TAB)
441
- // \n newline (line feed), ASCII 0Ah (LF)
442
- // \v vertical tab, ASCII 0Bh (VT)
443
- // \f form feed, ASCII 0Ch (FF)
444
- // \r carriage return, ASCII 0Dh (CR)
445
- // \e escape, ASCII 1Bh (ESC)
446
- // \s space, ASCII 20h (SPC)
447
- // \\ backslash
448
- // \nnn octal bit pattern, where nnn is 1-3 octal digits ([0-7])
449
- // \xnn hexadecimal bit pattern, where nn is 1-2 hexadecimal digits ([0-9a-fA-F])
450
- // \unnnn Unicode character, where nnnn is exactly 4 hexadecimal digits ([0-9a-fA-F])
451
- // \u{nnnn ...} Unicode character(s), where each nnnn is 1-6 hexadecimal digits ([0-9a-fA-F])
452
- // \cx or \C-x control character, where x is an ASCII printable character
453
- // \M-x meta character, where x is an ASCII printable character
454
- // \M-\C-x meta control character, where x is an ASCII printable character
455
- // \M-\cx same as above
456
- // \c\M-x same as above
457
- // \c? or \C-? delete, ASCII 7Fh (DEL)
458
- //
459
- static void
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
- // If we're not unescaping then we can reference the source directly.
463
- return;
464
- }
465
-
466
- const uint8_t *backslash = pm_memchr(string->source, '\\', string->length, parser->encoding_changed, &parser->encoding);
467
-
468
- if (backslash == NULL) {
469
- // Here there are no escapes, so we can reference the source directly.
470
- return;
471
- }
472
-
473
- // Here we have found an escape character, so we need to handle all escapes
474
- // within the string.
475
- uint8_t *allocated = malloc(string->length);
476
- if (allocated == NULL) {
477
- pm_diagnostic_list_append(&parser->error_list, string->source, string->source + string->length, PM_ERR_MALLOC_FAILED);
478
- return;
479
- }
480
-
481
- // This is the memory address where we're putting the unescaped string.
482
- uint8_t *dest = allocated;
483
- size_t dest_length = 0;
484
-
485
- // This is the current position in the source string that we're looking at.
486
- // It's going to move along behind the backslash so that we can copy each
487
- // segment of the string that doesn't contain an escape.
488
- const uint8_t *cursor = string->source;
489
- const uint8_t *end = string->source + string->length;
490
-
491
- // For each escape found in the source string, we will handle it and update
492
- // the moving cursor->backslash window.
493
- while (backslash != NULL && backslash + 1 < end) {
494
- assert(dest_length < string->length);
495
-
496
- // This is the size of the segment of the string from the previous escape
497
- // or the start of the string to the current escape.
498
- size_t segment_size = (size_t) (backslash - cursor);
499
-
500
- // Here we're going to copy everything up until the escape into the
501
- // destination buffer.
502
- memcpy(dest + dest_length, cursor, segment_size);
503
- dest_length += segment_size;
504
-
505
- switch (backslash[1]) {
506
- case '\\':
507
- case '\'':
508
- dest[dest_length++] = unescape_chars[backslash[1]];
509
- cursor = backslash + 2;
510
- break;
511
- default:
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) {
523
- // In this case we're escaping something that doesn't need escaping.
524
- dest[dest_length++] = '\\';
525
- cursor = backslash + 1;
526
- break;
527
- }
528
-
529
- // This is the only type of unescaping left. In this case we need to
530
- // handle all of the different unescapes.
531
- assert(unescape_type == PM_UNESCAPE_ALL);
532
-
533
- uint8_t flags = PM_UNESCAPE_FLAG_NONE;
534
- if (expect_single_codepoint) {
535
- flags |= PM_UNESCAPE_FLAG_EXPECT_SINGLE;
536
- }
537
-
538
- cursor = unescape(parser, dest, &dest_length, backslash, end, flags, &parser->error_list);
539
- break;
540
- }
541
-
542
- if (end > cursor) {
543
- backslash = pm_memchr(cursor, '\\', (size_t) (end - cursor), parser->encoding_changed, &parser->encoding);
544
- } else {
545
- backslash = NULL;
546
- }
547
- }
548
-
549
- // We need to copy the final segment of the string after the last escape.
550
- if (end > cursor) {
551
- memcpy(dest + dest_length, cursor, (size_t) (end - cursor));
552
- } else {
553
- cursor = end;
554
- }
555
-
556
- // If the string was already allocated, then we need to free that memory
557
- // here. That's because we're about to override it with the escaped string.
558
- pm_string_free(string);
559
-
560
- // We also need to update the length at the end. This is because every escape
561
- // reduces the length of the final string, and we don't want garbage at the
562
- // end.
563
- pm_string_owned_init(string, allocated, dest_length + ((size_t) (end - cursor)));
564
- }
565
-
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);
569
- }
570
-
571
- void
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);
574
- }
575
-
576
- // This function is similar to pm_unescape_manipulate_string, except it doesn't
577
- // actually perform any string manipulations. Instead, it calculates how long
578
- // the unescaped character is, and returns that value
579
- size_t
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);
582
-
583
- if (backslash + 1 >= parser->end) {
584
- return 0;
585
- }
586
-
587
- switch (backslash[1]) {
588
- case '\\':
589
- case '\'':
590
- return 2;
591
- default: {
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);
603
- }
604
-
605
- // This is the only type of unescaping left. In this case we need to
606
- // handle all of the different unescapes.
607
- assert(unescape_type == PM_UNESCAPE_ALL);
608
-
609
- uint8_t flags = PM_UNESCAPE_FLAG_NONE;
610
- if (expect_single_codepoint) {
611
- flags |= PM_UNESCAPE_FLAG_EXPECT_SINGLE;
612
- }
613
-
614
- const uint8_t *cursor = unescape(parser, NULL, 0, backslash, parser->end, flags, NULL);
615
- assert(cursor > backslash);
616
-
617
- return (size_t) (cursor - backslash);
618
- }
619
- }
620
- }
621
-
622
- // This is one of the main entry points into the extension. It accepts a source
623
- // string, a type of unescaping, and a pointer to a result string. It returns a
624
- // boolean indicating whether or not the unescaping was successful.
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);
629
-
630
- pm_string_shared_init(result, start, start + length);
631
- pm_unescape_manipulate_string(&parser, result, unescape_type);
632
-
633
- bool success = pm_list_empty_p(&parser.error_list);
634
- pm_parser_free(&parser);
635
-
636
- return success;
637
- }