1#include "prism/internal/regexp.h"
5#include "prism/internal/buffer.h"
6#include "prism/internal/char.h"
7#include "prism/internal/diagnostic.h"
8#include "prism/internal/encoding.h"
9#include "prism/internal/memchr.h"
10#include "prism/internal/parser.h"
11#include "prism/internal/stringy.h"
12#include "prism/internal/strncasecmp.h"
18#define PM_REGEXP_PARSE_DEPTH_MAX 4096
135pm_regexp_parse_error(
pm_regexp_parser_t *regexp_parser,
const uint8_t *start,
const uint8_t *end,
const char *message) {
138 pm_diagnostic_list_append_format(&parser->metadata_arena, &parser->error_list, (uint32_t) (start - parser->start), (uint32_t) (end - start), PM_ERR_REGEXP_PARSE_ERROR, message);
147#define pm_regexp_parse_error_format(regexp_parser_, err_start_, err_end_, diag_id, ...) \
149 if ((regexp_parser_)->report_errors) { \
150 pm_parser_t *parser_ = (regexp_parser_)->parser; \
151 pm_diagnostic_list_append_format(&parser_->metadata_arena, &parser_->error_list, (uint32_t) ((err_start_) - parser_->start), (uint32_t) ((err_end_) - (err_start_)), diag_id, __VA_ARGS__); \
160pm_regexp_parser_named_capture(
pm_regexp_parser_t *parser,
const uint8_t *start,
const uint8_t *end) {
162 pm_string_shared_init(&
string, start, end);
179 if (!pm_regexp_char_is_eof(parser) && *parser->
cursor == value) {
191 if (!pm_regexp_char_is_eof(parser) && *parser->
cursor == value) {
203 if (pm_regexp_char_is_eof(parser)) {
242 pm_regexp_hex_group_boundary(parser);
250pm_regexp_hex_digit_value(uint8_t
byte) {
251 if (
byte >=
'0' &&
byte <=
'9')
return byte -
'0';
252 if (
byte >=
'a' &&
byte <=
'f')
return byte -
'a' + 10;
253 if (
byte >=
'A' &&
byte <=
'F')
return byte -
'A' + 10;
291 const uint8_t *savepoint = parser->
cursor;
294 PM_REGEXP_RANGE_QUANTIFIER_STATE_START,
295 PM_REGEXP_RANGE_QUANTIFIER_STATE_MINIMUM,
296 PM_REGEXP_RANGE_QUANTIFIER_STATE_MAXIMUM,
297 PM_REGEXP_RANGE_QUANTIFIER_STATE_COMMA
298 } state = PM_REGEXP_RANGE_QUANTIFIER_STATE_START;
302 parser->
cursor = savepoint;
307 case PM_REGEXP_RANGE_QUANTIFIER_STATE_START:
308 switch (*parser->
cursor) {
309 case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
311 state = PM_REGEXP_RANGE_QUANTIFIER_STATE_MINIMUM;
315 state = PM_REGEXP_RANGE_QUANTIFIER_STATE_COMMA;
318 parser->
cursor = savepoint;
322 case PM_REGEXP_RANGE_QUANTIFIER_STATE_MINIMUM:
323 switch (*parser->
cursor) {
324 case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
329 state = PM_REGEXP_RANGE_QUANTIFIER_STATE_MAXIMUM;
335 parser->
cursor = savepoint;
339 case PM_REGEXP_RANGE_QUANTIFIER_STATE_COMMA:
340 switch (*parser->
cursor) {
341 case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
343 state = PM_REGEXP_RANGE_QUANTIFIER_STATE_MAXIMUM;
346 parser->
cursor = savepoint;
350 case PM_REGEXP_RANGE_QUANTIFIER_STATE_MAXIMUM:
351 switch (*parser->
cursor) {
352 case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
359 parser->
cursor = savepoint;
379 while (!pm_regexp_char_is_eof(parser)) {
380 switch (*parser->
cursor) {
388 if (!pm_regexp_parse_range_quantifier(parser))
return false;
405 if (!pm_regexp_char_expect(parser,
':')) {
409 pm_regexp_char_accept(parser,
'^');
412 pm_regexp_char_find(parser,
':') &&
413 pm_regexp_char_expect(parser,
']') &&
414 pm_regexp_char_expect(parser,
']')
430 PM_REGEXP_PROPERTY_POSIX,
431 PM_REGEXP_PROPERTY_SCRIPT,
432 PM_REGEXP_PROPERTY_UNICODE
433} pm_regexp_property_type_t;
439static pm_regexp_property_type_t
440pm_regexp_classify_property(
const uint8_t *name,
size_t length) {
442 if (length > 0 && name[0] ==
'^') {
447#define PM_REGEXP_CASECMP(str_) (pm_strncasecmp(name, (const uint8_t *) (str_), length) == 0)
451 if (PM_REGEXP_CASECMP(
"Han"))
return PM_REGEXP_PROPERTY_SCRIPT;
454 if (PM_REGEXP_CASECMP(
"Word"))
return PM_REGEXP_PROPERTY_POSIX;
458 switch (name[0] | 0x20) {
460 if (PM_REGEXP_CASECMP(
"Alnum"))
return PM_REGEXP_PROPERTY_POSIX;
461 if (PM_REGEXP_CASECMP(
"Alpha"))
return PM_REGEXP_PROPERTY_POSIX;
462 if (PM_REGEXP_CASECMP(
"ASCII"))
return PM_REGEXP_PROPERTY_POSIX;
465 if (PM_REGEXP_CASECMP(
"Blank"))
return PM_REGEXP_PROPERTY_POSIX;
468 if (PM_REGEXP_CASECMP(
"Cntrl"))
return PM_REGEXP_PROPERTY_POSIX;
471 if (PM_REGEXP_CASECMP(
"Digit"))
return PM_REGEXP_PROPERTY_POSIX;
474 if (PM_REGEXP_CASECMP(
"Graph"))
return PM_REGEXP_PROPERTY_POSIX;
475 if (PM_REGEXP_CASECMP(
"Greek"))
return PM_REGEXP_PROPERTY_SCRIPT;
478 if (PM_REGEXP_CASECMP(
"Lower"))
return PM_REGEXP_PROPERTY_POSIX;
479 if (PM_REGEXP_CASECMP(
"Latin"))
return PM_REGEXP_PROPERTY_SCRIPT;
482 if (PM_REGEXP_CASECMP(
"Print"))
return PM_REGEXP_PROPERTY_POSIX;
483 if (PM_REGEXP_CASECMP(
"Punct"))
return PM_REGEXP_PROPERTY_POSIX;
486 if (PM_REGEXP_CASECMP(
"Space"))
return PM_REGEXP_PROPERTY_POSIX;
489 if (PM_REGEXP_CASECMP(
"Upper"))
return PM_REGEXP_PROPERTY_POSIX;
494 if (PM_REGEXP_CASECMP(
"XDigit"))
return PM_REGEXP_PROPERTY_POSIX;
497 if (PM_REGEXP_CASECMP(
"Hiragana"))
return PM_REGEXP_PROPERTY_SCRIPT;
498 if (PM_REGEXP_CASECMP(
"Katakana"))
return PM_REGEXP_PROPERTY_SCRIPT;
499 if (PM_REGEXP_CASECMP(
"Cyrillic"))
return PM_REGEXP_PROPERTY_SCRIPT;
503#undef PM_REGEXP_CASECMP
506 return PM_REGEXP_PROPERTY_UNICODE;
520 assert(*parser->
cursor ==
'p' || *parser->
cursor ==
'P');
523 const uint8_t *name_start = parser->
cursor + 2;
524 const uint8_t *search = name_start;
526 while (search < parser->end && *search !=
'}') search++;
528 if (search < parser->end) {
529 size_t name_length = (size_t) (search - name_start);
532 pm_regexp_property_type_t
type = pm_regexp_classify_property(name_start, name_length);
535 if (type >= PM_REGEXP_PROPERTY_SCRIPT && parser->
property_name == NULL) {
541 if (type == PM_REGEXP_PROPERTY_UNICODE) {
549 parser->
cursor = search + 1;
567 const uint8_t *escape_start = parser->
cursor - 2;
569 if (pm_regexp_char_is_eof(parser)) {
570 pm_regexp_parse_error(parser, escape_start, parser->
cursor,
"invalid Unicode escape");
574 if (*parser->
cursor ==
'{') {
578 while (!pm_regexp_char_is_eof(parser) && pm_char_is_whitespace(*parser->
cursor)) {
582 bool has_codepoint =
false;
584 while (!pm_regexp_char_is_eof(parser) && *parser->
cursor !=
'}') {
587 size_t hex_count = 0;
590 while (!pm_regexp_char_is_eof(parser) && (digit = pm_regexp_hex_digit_value(*parser->
cursor)) >= 0) {
591 value = (value << 4) | (uint32_t) digit;
596 if (hex_count == 0) {
598 while (!pm_regexp_char_is_eof(parser) && *parser->
cursor !=
'}') {
602 const uint8_t *escape_end = parser->
cursor;
603 if (!pm_regexp_char_is_eof(parser)) {
608 pm_regexp_parse_error_format(parser, escape_start, escape_end, PM_ERR_ESCAPE_INVALID_UNICODE_LIST, (
int) (escape_end - escape_start), (
const char *) escape_start);
613 pm_regexp_parse_error(parser, escape_start, parser->
cursor,
"invalid Unicode range");
621 pm_regexp_hex_group_boundary(parser);
625 if (value > 0x10FFFF || (value >= 0xD800 && value <= 0xDFFF)) {
629 has_codepoint =
true;
632 while (!pm_regexp_char_is_eof(parser) && pm_char_is_whitespace(*parser->
cursor)) {
637 if (pm_regexp_char_is_eof(parser)) {
638 pm_regexp_parse_error(parser, escape_start, parser->
cursor,
"unterminated Unicode escape");
640 if (!has_codepoint) {
641 pm_regexp_parse_error_format(parser, escape_start, parser->
cursor + 1, PM_ERR_ESCAPE_INVALID_UNICODE_LIST, (
int) (parser->
cursor + 1 - escape_start), (
const char *) escape_start);
648 size_t hex_count = 0;
651 while (hex_count < 4 && !pm_regexp_char_is_eof(parser) && (digit = pm_regexp_hex_digit_value(*parser->
cursor)) >= 0) {
652 value = (value << 4) | (uint32_t) digit;
658 pm_regexp_parse_error(parser, escape_start, parser->
cursor,
"invalid Unicode escape");
659 }
else if (value >= 0x80) {
663 pm_regexp_hex_group_boundary(parser);
667 if (hex_count == 4 && (value > 0x10FFFF || (value >= 0xD800 && value <= 0xDFFF))) {
685 if (!pm_regexp_char_is_eof(parser)) {
686 int digit = pm_regexp_hex_digit_value(*parser->
cursor);
691 if (!pm_regexp_char_is_eof(parser)) {
692 digit = pm_regexp_hex_digit_value(*parser->
cursor);
694 value = (value << 4) | digit;
702 pm_regexp_track_hex_escape(parser, (uint8_t) value);
715 if (pm_regexp_char_is_eof(parser))
return;
717 switch (*parser->
cursor) {
720 pm_regexp_parse_unicode_escape(parser);
724 pm_regexp_parse_property_escape(parser);
728 pm_regexp_parse_hex_escape(parser);
734 if (!pm_regexp_char_is_eof(parser)) {
735 if (*parser->
cursor ==
'\\') {
739 pm_regexp_parse_backslash_escape(parser);
744 pm_regexp_track_hex_escape(parser, 0x80);
754 if (!pm_regexp_char_is_eof(parser)) {
755 if (*parser->
cursor ==
'\\') {
757 pm_regexp_parse_backslash_escape(parser);
769 if (!pm_regexp_char_is_eof(parser)) {
770 if (*parser->
cursor ==
'\\') {
772 pm_regexp_parse_backslash_escape(parser);
779 pm_regexp_hex_group_boundary(parser);
792 uint8_t
byte = *cursor;
794 size_t width = parser->
encoding->char_width(cursor, (ptrdiff_t) (parser->
end - cursor));
796 parser->
cursor += width - 1;
797 }
else if (width == 0) {
799 pm_regexp_parse_error_format(parser, cursor, cursor + 1, PM_ERR_INVALID_MULTIBYTE_CHAR, parser->
encoding->name);
810 pm_regexp_char_accept(parser,
'^');
812 while (!pm_regexp_char_is_eof(parser) && *parser->
cursor !=
']') {
813 switch (*parser->
cursor++) {
815 pm_regexp_parse_lbracket(parser, (uint16_t) (depth + 1));
818 pm_regexp_parse_backslash_escape(parser);
824 if (*(parser->
cursor - 1) >= 0x80) {
827 pm_regexp_parse_invalid_multibyte(parser, parser->
cursor - 1);
832 return pm_regexp_char_expect(parser,
']');
840 if (depth >= PM_REGEXP_PARSE_DEPTH_MAX) {
841 pm_regexp_parse_error(parser, parser->
start, parser->
end,
"parse depth limit over");
847 pm_regexp_parse_error(parser, parser->
cursor - 1, parser->
cursor,
"empty char-class");
851 const uint8_t *reset = parser->
cursor;
855 if (pm_regexp_parse_posix_class(parser))
return true;
860 return pm_regexp_parse_character_set(parser, depth);
873 PM_REGEXP_OPTION_STATE_INVALID,
874 PM_REGEXP_OPTION_STATE_TOGGLEABLE,
875 PM_REGEXP_OPTION_STATE_ADDABLE,
876 PM_REGEXP_OPTION_STATE_ADDED,
877 PM_REGEXP_OPTION_STATE_REMOVED
878} pm_regexp_option_state_t;
884#define PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM 'a'
887#define PRISM_REGEXP_OPTION_STATE_SLOT_MAXIMUM 'x'
890#define PRISM_REGEXP_OPTION_STATE_SLOTS (PRISM_REGEXP_OPTION_STATE_SLOT_MAXIMUM - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM + 1)
897 uint8_t values[PRISM_REGEXP_OPTION_STATE_SLOTS];
905 memset(options, PM_REGEXP_OPTION_STATE_INVALID,
sizeof(uint8_t) * PRISM_REGEXP_OPTION_STATE_SLOTS);
906 options->
values[
'i' - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM] = PM_REGEXP_OPTION_STATE_TOGGLEABLE;
907 options->
values[
'm' - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM] = PM_REGEXP_OPTION_STATE_TOGGLEABLE;
908 options->
values[
'x' - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM] = PM_REGEXP_OPTION_STATE_TOGGLEABLE;
909 options->
values[
'd' - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM] = PM_REGEXP_OPTION_STATE_ADDABLE;
910 options->
values[
'a' - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM] = PM_REGEXP_OPTION_STATE_ADDABLE;
911 options->
values[
'u' - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM] = PM_REGEXP_OPTION_STATE_ADDABLE;
920 if (key >= PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM && key <= PRISM_REGEXP_OPTION_STATE_SLOT_MAXIMUM) {
921 key = (uint8_t) (key - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM);
923 switch (options->
values[key]) {
924 case PM_REGEXP_OPTION_STATE_INVALID:
925 case PM_REGEXP_OPTION_STATE_REMOVED:
927 case PM_REGEXP_OPTION_STATE_TOGGLEABLE:
928 case PM_REGEXP_OPTION_STATE_ADDABLE:
929 options->
values[key] = PM_REGEXP_OPTION_STATE_ADDED;
931 case PM_REGEXP_OPTION_STATE_ADDED:
945 if (key >= PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM && key <= PRISM_REGEXP_OPTION_STATE_SLOT_MAXIMUM) {
946 key = (uint8_t) (key - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM);
948 switch (options->
values[key]) {
949 case PM_REGEXP_OPTION_STATE_INVALID:
950 case PM_REGEXP_OPTION_STATE_ADDABLE:
952 case PM_REGEXP_OPTION_STATE_TOGGLEABLE:
953 case PM_REGEXP_OPTION_STATE_ADDED:
954 case PM_REGEXP_OPTION_STATE_REMOVED:
955 options->
values[key] = PM_REGEXP_OPTION_STATE_REMOVED;
968 if (key >= PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM && key <= PRISM_REGEXP_OPTION_STATE_SLOT_MAXIMUM) {
969 key = (uint8_t) (key - PRISM_REGEXP_OPTION_STATE_SLOT_MINIMUM);
970 return options->
values[key];
999 const uint8_t *group_start = parser->
cursor;
1002 pm_regexp_options_init(&options);
1005 if (pm_regexp_char_accept(parser,
'?')) {
1006 if (pm_regexp_char_is_eof(parser)) {
1007 pm_regexp_parse_error(parser, group_start, parser->
cursor,
"end pattern in group");
1011 switch (*parser->
cursor) {
1014 if (pm_regexp_char_is_eof(parser)) {
1015 pm_regexp_parse_error(parser, group_start, parser->
cursor,
"end pattern in group");
1020 bool escaped =
false;
1026 if (!escaped && *parser->
cursor ==
')') {
1033 if (*parser->
cursor >= 0x80) {
1035 pm_regexp_parse_error_format(parser, parser->
cursor, parser->
cursor + 1, PM_ERR_INVALID_MULTIBYTE_CHAR, parser->
encoding->name);
1042 escaped = (width == 1) && (*parser->
cursor ==
'\\');
1051 bool found = pm_regexp_char_find(parser,
')');
1053 while (found && (parser->
start <= parser->
cursor - 2) && (*(parser->
cursor - 2) ==
'\\')) {
1054 found = pm_regexp_char_find(parser,
')');
1069 if (pm_regexp_char_is_eof(parser)) {
1070 pm_regexp_parse_error(parser, group_start, parser->
cursor,
"end pattern with unmatched parenthesis");
1074 switch (*parser->
cursor) {
1080 const uint8_t *start = parser->
cursor;
1081 if (!pm_regexp_char_find(parser,
'>')) {
1085 if (parser->
cursor - start == 1) {
1086 pm_regexp_parse_error(parser, start, parser->
cursor,
"group name is empty");
1090 pm_regexp_parser_named_capture(parser, start, parser->
cursor - 1);
1098 const uint8_t *start = ++parser->
cursor;
1099 if (!pm_regexp_char_find(parser,
'\'')) {
1104 pm_regexp_parser_named_capture(parser, start, parser->
cursor - 1);
1110 if (!pm_regexp_char_find(parser,
')')) {
1114 case 'i':
case 'm':
case 'x':
case 'd':
case 'a':
case 'u':
1115 while (!pm_regexp_char_is_eof(parser) && *parser->
cursor !=
'-' && *parser->
cursor !=
':' && *parser->
cursor !=
')') {
1116 if (!pm_regexp_options_add(&options, *parser->
cursor)) {
1122 if (pm_regexp_char_is_eof(parser)) {
1129 if (*parser->
cursor ==
')') {
1130 if (pm_regexp_options_state(&options,
'x') == PM_REGEXP_OPTION_STATE_ADDED) {
1139 if (*parser->
cursor !=
'-')
break;
1144 while (!pm_regexp_char_is_eof(parser) && *parser->
cursor !=
':' && *parser->
cursor !=
')') {
1145 if (!pm_regexp_options_remove(&options, *parser->
cursor)) {
1151 if (pm_regexp_char_is_eof(parser)) {
1158 if (*parser->
cursor ==
')') {
1159 switch (pm_regexp_options_state(&options,
'x')) {
1160 case PM_REGEXP_OPTION_STATE_ADDED:
1163 case PM_REGEXP_OPTION_STATE_REMOVED:
1175 pm_regexp_parse_error(parser, parser->
cursor - 1, parser->
cursor,
"undefined group option");
1181 switch (pm_regexp_options_state(&options,
'x')) {
1182 case PM_REGEXP_OPTION_STATE_ADDED:
1185 case PM_REGEXP_OPTION_STATE_REMOVED:
1191 while (!pm_regexp_char_is_eof(parser) && *parser->
cursor !=
')') {
1192 if (!pm_regexp_parse_expression(parser, (uint16_t) (depth + 1))) {
1196 pm_regexp_char_accept(parser,
'|');
1201 if (pm_regexp_char_expect(parser,
')'))
return true;
1203 pm_regexp_parse_error(parser, group_start, parser->
cursor,
"end pattern with unmatched parenthesis");
1221 switch (*parser->
cursor) {
1225 return pm_regexp_parse_quantifier(parser);
1228 pm_regexp_parse_backslash_escape(parser);
1229 return pm_regexp_parse_quantifier(parser);
1232 return pm_regexp_parse_group(parser, depth) && pm_regexp_parse_quantifier(parser);
1235 return pm_regexp_parse_lbracket(parser, depth) && pm_regexp_parse_quantifier(parser);
1240 pm_regexp_parse_error(parser, parser->
cursor - 1, parser->
cursor,
"target of repeat operator is not specified");
1244 pm_regexp_parse_error(parser, parser->
cursor - 1, parser->
cursor,
"unmatched close parenthesis");
1248 if (!pm_regexp_char_find(parser,
'\n')) parser->
cursor = parser->
end;
1255 width = pm_encoding_utf_8_char_width(parser->
cursor, (ptrdiff_t) (parser->
end - parser->
cursor));
1266 pm_regexp_parse_error_format(parser, parser->
cursor, parser->
cursor + 1, PM_ERR_INVALID_MULTIBYTE_CHAR, parser->
encoding->name);
1273 return pm_regexp_parse_quantifier(parser);
1279 for (
size_t i = 0; i < width; i++) {
1284 return pm_regexp_parse_quantifier(parser);
1295 if (depth >= PM_REGEXP_PARSE_DEPTH_MAX) {
1296 pm_regexp_parse_error(parser, parser->
start, parser->
end,
"parse depth limit over");
1300 if (!pm_regexp_parse_item(parser, depth)) {
1304 while (!pm_regexp_char_is_eof(parser) && *parser->
cursor !=
')' && *parser->
cursor !=
'|') {
1305 if (!pm_regexp_parse_item(parser, depth)) {
1322 if (pm_regexp_char_is_eof(parser))
return true;
1323 if (!pm_regexp_parse_expression(parser, 0))
return false;
1324 }
while (pm_regexp_char_accept(parser,
'|'));
1326 return pm_regexp_char_is_eof(parser);
1339 const uint8_t *data = (
const uint8_t *) pm_buffer_value(buffer);
1340 size_t len = pm_buffer_length(buffer);
1344 size_t group_start = i;
1345 while (i <
len && data[i] != 0x00) i++;
1347 for (
size_t j = group_start; j < i; ) {
1348 size_t width = encoding->char_width(data + j, (ptrdiff_t) (i - j));
1349 if (width == 0)
return false;
1364pm_regexp_format_for_error(
pm_buffer_t *buffer,
const pm_encoding_t *encoding,
const uint8_t *source,
size_t length) {
1367 if (encoding == PM_ENCODING_UTF_8_ENTRY) {
1368 pm_buffer_append_string(buffer, (
const char *) source, length);
1372 while (index < length) {
1373 if (source[index] < 0x80) {
1374 pm_buffer_append_byte(buffer, source[index]);
1376 }
else if (encoding->multibyte) {
1377 size_t width = encoding->char_width(source + index, (ptrdiff_t) (length - index));
1380 pm_buffer_append_string(buffer,
"\\x{", 3);
1381 for (
size_t i = 0; i < width; i++) {
1382 pm_buffer_append_format(buffer,
"%02X", source[index + i]);
1384 pm_buffer_append_byte(buffer,
'}');
1387 pm_buffer_append_format(buffer,
"\\x%02X", source[index]);
1391 pm_buffer_append_format(buffer,
"\\x%02X", source[index]);
1400#define PM_REGEXP_ENCODING_ERROR(parser, diag_id, ...) \
1401 pm_diagnostic_list_append_format( \
1402 &(parser)->parser->metadata_arena, \
1403 &(parser)->parser->error_list, \
1404 (uint32_t) ((parser)->node_start - (parser)->parser->start), \
1405 (uint32_t) ((parser)->node_end - (parser)->node_start), \
1406 diag_id, __VA_ARGS__)
1427static pm_node_flags_t
1428pm_regexp_validate_encoding_modifier(
pm_regexp_parser_t *parser,
bool ascii_only, pm_node_flags_t flags,
char modifier,
const pm_encoding_t *modifier_encoding,
const char *source_start,
int source_length) {
1437 PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_REGEXP_INVALID_CHAR_PROPERTY,
1439 source_length, source_start);
1441 PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_REGEXP_INVALID_CHAR_PROPERTY,
1443 source_length, source_start);
1445 return modifier ==
'n' ? PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING : flags;
1448 if (parser->
encoding == PM_ENCODING_US_ASCII_ENTRY) {
1450 PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_INVALID_MULTIBYTE_CHAR, parser->
encoding->name);
1452 }
else if (parser->
encoding != modifier_encoding) {
1453 PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_REGEXP_ENCODING_OPTION_MISMATCH, modifier, parser->
encoding->name);
1455 if (modifier ==
'n' && !ascii_only) {
1457 pm_regexp_format_for_error(&formatted, parser->
encoding, (
const uint8_t *) source_start, (
size_t) source_length);
1458 PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_REGEXP_NON_ESCAPED_MBC, (
int) formatted.length, (
const char *) formatted.value);
1459 pm_buffer_cleanup(&formatted);
1468 if (modifier ==
'n') {
1470 PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_REGEXP_UTF8_CHAR_NON_UTF8_REGEXP, source_length, source_start);
1472 PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_REGEXP_ESCAPED_NON_ASCII_IN_UTF8, source_length, source_start);
1475 if (!pm_regexp_validate_hex_escapes(modifier_encoding, &parser->
hex_escape_buffer)) {
1476 PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_INVALID_MULTIBYTE_ESCAPE, source_length, source_start);
1485 PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_REGEXP_INCOMPAT_CHAR_ENCODING, source_length, source_start);
1486 }
else if (parser->
encoding != PM_ENCODING_UTF_8_ENTRY) {
1487 PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_REGEXP_INCOMPAT_CHAR_ENCODING, source_length, source_start);
1491 if (modifier !=
'n' && !pm_regexp_validate_hex_escapes(modifier_encoding, &parser->
hex_escape_buffer)) {
1492 PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_INVALID_MULTIBYTE_ESCAPE, source_length, source_start);
1514static pm_node_flags_t
1515pm_regexp_validate_encoding(
pm_regexp_parser_t *parser,
bool ascii_only, pm_node_flags_t flags,
const char *source_start,
int source_length) {
1524 PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_REGEXP_INVALID_UNICODE_RANGE, source_length, source_start);
1529 if (flags & PM_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT) {
1530 return pm_regexp_validate_encoding_modifier(parser, ascii_only, flags,
'n', PM_ENCODING_ASCII_8BIT_ENTRY, source_start, source_length);
1532 if (flags & PM_REGULAR_EXPRESSION_FLAGS_UTF_8) {
1533 return pm_regexp_validate_encoding_modifier(parser, ascii_only, flags,
'u', PM_ENCODING_UTF_8_ENTRY, source_start, source_length);
1535 if (flags & PM_REGULAR_EXPRESSION_FLAGS_EUC_JP) {
1536 return pm_regexp_validate_encoding_modifier(parser, ascii_only, flags,
'e', PM_ENCODING_EUC_JP_ENTRY, source_start, source_length);
1538 if (flags & PM_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J) {
1539 return pm_regexp_validate_encoding_modifier(parser, ascii_only, flags,
's', PM_ENCODING_WINDOWS_31J_ENTRY, source_start, source_length);
1545 PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_INVALID_MULTIBYTE_CHAR, parser->
encoding->name);
1553 return PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING;
1555 return PM_REGULAR_EXPRESSION_FLAGS_FORCED_US_ASCII_ENCODING;
1562 if (parser->
encoding != PM_ENCODING_US_ASCII_ENTRY &&
1563 parser->
encoding != PM_ENCODING_ASCII_8BIT_ENTRY &&
1565 PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_INVALID_MULTIBYTE_ESCAPE, source_length, source_start);
1567 PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_REGEXP_UTF8_CHAR_NON_UTF8_REGEXP, source_length, source_start);
1569 PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_REGEXP_ESCAPED_NON_ASCII_IN_UTF8, source_length, source_start);
1577 PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_INVALID_MULTIBYTE_ESCAPE, source_length, source_start);
1580 return PM_REGULAR_EXPRESSION_FLAGS_FORCED_UTF8_ENCODING;
1581 }
else if (parser->
encoding == PM_ENCODING_US_ASCII_ENTRY) {
1582 return PM_REGULAR_EXPRESSION_FLAGS_FORCED_BINARY_ENCODING;
1585 PM_REGEXP_ENCODING_ERROR(parser, PM_ERR_INVALID_MULTIBYTE_ESCAPE, source_length, source_start);
1604 bool extended_mode = PM_NODE_FLAG_P(node, PM_REGULAR_EXPRESSION_FLAGS_EXTENDED);
1605 pm_node_flags_t flags = PM_NODE_FLAGS(node);
1615 .end = source + size,
1616 .extended_mode = extended_mode,
1617 .encoding_changed = parser->encoding_changed,
1618 .encoding = parser->encoding,
1619 .name_callback = NULL,
1621 .report_errors =
true,
1622 .node_start = node_start,
1623 .node_end = node_end,
1624 .has_unicode_escape =
false,
1625 .has_hex_escape =
false,
1626 .last_escape_was_unicode =
false,
1627 .explicit_encoding = NULL,
1628 .has_property_escape =
false,
1629 .has_unicode_property_escape =
false,
1630 .property_name = NULL,
1631 .property_name_length = 0,
1632 .unicode_property_name = NULL,
1633 .unicode_property_name_length = 0,
1634 .non_ascii_literal_count = 0,
1635 .invalid_unicode_range =
false,
1636 .hex_escape_buffer = { 0 },
1637 .hex_group_active =
false,
1638 .has_invalid_multibyte =
false,
1641 pm_regexp_parse_pattern(®exp_parser);
1650 const char *error_source = (
const char *) pm_string_source(&node->
unescaped);
1651 int error_source_length = (int) pm_string_length(&node->
unescaped);
1652 pm_node_flags_t encoding_flags = pm_regexp_validate_encoding(®exp_parser, ascii_only, flags, error_source, error_source_length);
1656 if (name_callback != NULL) {
1657 pm_regexp_parse_named_captures(parser, pm_string_source(&node->
unescaped), pm_string_length(&node->
unescaped), extended_mode, name_callback, name_data);
1660 return encoding_flags;
1674pm_regexp_parse_named_captures(
pm_parser_t *parser,
const uint8_t *source,
size_t size,
bool extended_mode, pm_regexp_name_callback_t name_callback,
void *name_data) {
1679 .end = source + size,
1680 .extended_mode = extended_mode,
1681 .encoding_changed = parser->encoding_changed,
1682 .encoding = parser->encoding,
1683 .name_callback = name_callback,
1684 .name_data = name_data,
1685 .report_errors =
false,
1686 .has_unicode_escape =
false,
1687 .has_hex_escape =
false,
1688 .last_escape_was_unicode =
false,
1689 .explicit_encoding = NULL,
1690 .has_property_escape =
false,
1691 .has_unicode_property_escape =
false,
1692 .property_name = NULL,
1693 .property_name_length = 0,
1694 .unicode_property_name = NULL,
1695 .unicode_property_name_length = 0,
1696 .non_ascii_literal_count = 0,
1697 .invalid_unicode_range =
false,
1698 .hex_escape_buffer = { 0 },
1699 .hex_group_active =
false,
1700 .has_invalid_multibyte =
false,
1703 pm_regexp_parse_pattern(®exp_parser);
#define PRISM_FALLTHROUGH
We use -Wimplicit-fallthrough to guard potentially unintended fall-through between cases of a switch.
int len
Length of the buffer.
#define PRISM_INLINE
Old Visual Studio versions do not support the inline keyword, so we need to define it to be __inline.
VALUE type(ANYARGS)
ANYARGS-ed function type.
uint32_t start
The offset of the location from the start of the source.
uint32_t length
The length of the location.
pm_location_t location
This is the location of the node in the source.
This is the set of options that are configurable on the regular expression.
uint8_t values[PRISM_REGEXP_OPTION_STATE_SLOTS]
The current state of each option.
This is the parser that is going to handle parsing regular expressions.
const uint8_t * cursor
A pointer to the current position in the source.
bool has_unicode_escape
Whether a \u{...} escape with value >= 0x80 was seen.
size_t unicode_property_name_length
Length of the first Unicode-only property name found.
bool has_hex_escape
Whether a \xNN escape (or \M-x, etc.) with value >= 0x80 was seen.
const uint8_t * start
A pointer to the start of the source that we are parsing.
const uint8_t * end
A pointer to the end of the source that we are parsing.
void * name_data
The data to pass to the name callback.
bool hex_group_active
Whether we are accumulating consecutive hex escape bytes.
pm_buffer_t hex_escape_buffer
Buffer of hex escape byte values >= 0x80, separated by 0x00 sentinels.
bool extended_mode
Whether or not the regular expression currently being parsed is in extended mode, wherein whitespace ...
pm_parser_t * parser
The parser that is currently being used.
size_t property_name_length
Length of the first non-POSIX property name found.
bool invalid_unicode_range
Whether a \u escape with invalid range (surrogate or > 0x10FFFF) was seen.
const uint8_t * property_name
Pointer to the first non-POSIX property name (for /n error messages).
const pm_encoding_t * encoding
The encoding of the source.
const pm_encoding_t * explicit_encoding
The explicit encoding determined by escape sequences.
const uint8_t * node_end
The end of the regexp node (for error locations).
uint32_t non_ascii_literal_count
Count of non-ASCII literal bytes (not from escapes).
const uint8_t * node_start
The start of the regexp node (for error locations).
bool report_errors
Whether syntax errors are reported.
const uint8_t * unicode_property_name
Pointer to the first Unicode-only property name (for /e, /s error messages).
pm_regexp_name_callback_t name_callback
The callback to call when a named capture group is found.
bool encoding_changed
Whether the encoding has changed from the default.
bool has_unicode_property_escape
Whether a Unicode-only property escape was found (not POSIX or script).
bool has_invalid_multibyte
Whether an invalid multibyte character was found during parsing.
bool last_escape_was_unicode
Tracks whether the last encoding-setting escape was \u (true) or \x (false).
bool has_property_escape
Whether any \p{...} or \P{...} property escape was found.
pm_node_t base
The embedded base node.
pm_string_t unescaped
RegularExpressionNode::unescaped.
pm_location_t content_loc
RegularExpressionNode::content_loc.
A view of a sequence of bytes.