character_set 1.4.1 → 1.8.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/.gitattributes +1 -1
- data/.github/workflows/gouteur.yml +20 -0
- data/.github/workflows/lint.yml +1 -1
- data/.github/workflows/tests.yml +8 -2
- data/.gitignore +1 -0
- data/.gouteur.yml +2 -0
- data/.rubocop.yml +10 -1
- data/BENCHMARK.md +35 -31
- data/CHANGELOG.md +56 -1
- data/Gemfile +15 -0
- data/LICENSE.txt +1 -1
- data/README.md +24 -8
- data/Rakefile +2 -120
- data/character_set.gemspec +0 -20
- data/ext/character_set/character_set.c +122 -119
- data/ext/character_set/unicode_casefold_table.h +44 -1
- data/lib/character_set/core_ext/regexp_ext.rb +8 -0
- data/lib/character_set/core_ext/string_ext.rb +1 -1
- data/lib/character_set/expression_converter.rb +39 -56
- data/lib/character_set/parser.rb +8 -4
- data/lib/character_set/predefined_sets/assigned.cps +110 -78
- data/lib/character_set/predefined_sets/emoji.cps +16 -14
- data/lib/character_set/predefined_sets.rb +11 -0
- data/lib/character_set/ruby_fallback/character_set_methods.rb +16 -19
- data/lib/character_set/ruby_fallback/set_methods.rb +6 -21
- data/lib/character_set/ruby_fallback/vendored_set_classes.rb +385 -0
- data/lib/character_set/ruby_fallback.rb +18 -6
- data/lib/character_set/set_method_adapters.rb +4 -3
- data/lib/character_set/shared_methods.rb +21 -3
- data/lib/character_set/version.rb +1 -1
- data/tasks/benchmark.rake +20 -0
- data/{benchmarks → tasks/benchmarks}/delete_in.rb +5 -1
- data/{benchmarks → tasks/benchmarks}/keep_in.rb +5 -1
- data/tasks/benchmarks/shared.rb +28 -0
- data/tasks/sync_casefold_data.rake +20 -0
- data/tasks/sync_predefined_sets.rake +9 -0
- data/tasks/sync_ruby_spec.rake +65 -0
- metadata +22 -169
- data/benchmarks/shared.rb +0 -30
- /data/{benchmarks → tasks/benchmarks}/count_in.rb +0 -0
- /data/{benchmarks → tasks/benchmarks}/cover.rb +0 -0
- /data/{benchmarks → tasks/benchmarks}/scan.rb +0 -0
- /data/{benchmarks → tasks/benchmarks}/used_by.rb +0 -0
- /data/{benchmarks → tasks/benchmarks}/z_add.rb +0 -0
- /data/{benchmarks → tasks/benchmarks}/z_delete.rb +0 -0
- /data/{benchmarks → tasks/benchmarks}/z_merge.rb +0 -0
- /data/{benchmarks → tasks/benchmarks}/z_minmax.rb +0 -0
|
@@ -82,7 +82,11 @@ static const rb_data_type_t cs_type = {
|
|
|
82
82
|
.dsize = cs_memsize,
|
|
83
83
|
},
|
|
84
84
|
.data = NULL,
|
|
85
|
+
#ifdef RUBY_TYPED_FROZEN_SHAREABLE
|
|
86
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE,
|
|
87
|
+
#else
|
|
85
88
|
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
|
89
|
+
#endif
|
|
86
90
|
};
|
|
87
91
|
|
|
88
92
|
static inline VALUE
|
|
@@ -315,9 +319,9 @@ cs_method_minmax(VALUE self)
|
|
|
315
319
|
cs_cp cp, alen, blen; \
|
|
316
320
|
cs_ar *acps, *bcps; \
|
|
317
321
|
struct cs_data *new_data; \
|
|
318
|
-
new_cs = cs_alloc(RBASIC(self)->klass, &new_data); \
|
|
319
322
|
acps = cs_fetch_cps(cs_a, &alen); \
|
|
320
323
|
bcps = cs_fetch_cps(cs_b, &blen); \
|
|
324
|
+
new_cs = cs_alloc(RBASIC(self)->klass, &new_data); \
|
|
321
325
|
for (cp = 0; cp < UNICODE_CP_COUNT; cp++) \
|
|
322
326
|
{ \
|
|
323
327
|
if (tst_cp(acps, alen, cp) comp_op tst_cp(bcps, blen, cp)) \
|
|
@@ -372,22 +376,20 @@ cs_toggle_codepoint(VALUE cs, VALUE cp_num, int on, int return_nil_if_noop)
|
|
|
372
376
|
cps = data->cps;
|
|
373
377
|
len = data->len;
|
|
374
378
|
cp = FIX2ULONG(cp_num);
|
|
375
|
-
if (return_nil_if_noop &&
|
|
379
|
+
if (return_nil_if_noop && tst_cp(cps, len, cp) == on)
|
|
376
380
|
{
|
|
377
381
|
return Qnil;
|
|
378
382
|
}
|
|
383
|
+
|
|
384
|
+
if (on)
|
|
385
|
+
{
|
|
386
|
+
set_cp(data, cp);
|
|
387
|
+
}
|
|
379
388
|
else
|
|
380
389
|
{
|
|
381
|
-
|
|
382
|
-
{
|
|
383
|
-
set_cp(data, cp);
|
|
384
|
-
}
|
|
385
|
-
else
|
|
386
|
-
{
|
|
387
|
-
clr_cp(cps, len, cp);
|
|
388
|
-
}
|
|
389
|
-
return cs;
|
|
390
|
+
clr_cp(cps, len, cp);
|
|
390
391
|
}
|
|
392
|
+
return cs;
|
|
391
393
|
}
|
|
392
394
|
|
|
393
395
|
static VALUE
|
|
@@ -571,7 +573,7 @@ cs_method_merge(VALUE self, VALUE other)
|
|
|
571
573
|
{
|
|
572
574
|
return cs_merge_cs(self, other);
|
|
573
575
|
}
|
|
574
|
-
|
|
576
|
+
if (TYPE(other) == T_ARRAY)
|
|
575
577
|
{
|
|
576
578
|
return cs_merge_rb_array(self, other);
|
|
577
579
|
}
|
|
@@ -673,6 +675,18 @@ cs_method_proper_superset_p(VALUE self, VALUE other)
|
|
|
673
675
|
return (is_superset && is_proper) ? Qtrue : Qfalse;
|
|
674
676
|
}
|
|
675
677
|
|
|
678
|
+
static VALUE
|
|
679
|
+
cs_method_spaceship_operator(VALUE self, VALUE other)
|
|
680
|
+
{
|
|
681
|
+
if (cs_method_eql_p(self, other))
|
|
682
|
+
return INT2FIX(0);
|
|
683
|
+
if (cs_method_proper_subset_p(self, other))
|
|
684
|
+
return INT2FIX(-1);
|
|
685
|
+
if (cs_method_proper_superset_p(self, other))
|
|
686
|
+
return INT2FIX(1);
|
|
687
|
+
return Qnil;
|
|
688
|
+
}
|
|
689
|
+
|
|
676
690
|
// *******************************
|
|
677
691
|
// `CharacterSet`-specific methods
|
|
678
692
|
// *******************************
|
|
@@ -913,10 +927,10 @@ cs_method_ext_inversion(int argc, VALUE *argv, VALUE self)
|
|
|
913
927
|
return new_cs;
|
|
914
928
|
}
|
|
915
929
|
|
|
916
|
-
typedef int (*str_cp_handler)(unsigned int, cs_ar *, cs_cp len, struct cs_data *data, VALUE
|
|
930
|
+
typedef int (*str_cp_handler)(unsigned int, cs_ar *, cs_cp len, struct cs_data *data, VALUE memo);
|
|
917
931
|
|
|
918
932
|
static inline int
|
|
919
|
-
add_str_cp_to_arr(unsigned int str_cp, cs_ar *cp_arr, cs_cp len, struct cs_data *data, VALUE
|
|
933
|
+
add_str_cp_to_arr(unsigned int str_cp, cs_ar *cp_arr, cs_cp len, struct cs_data *data, VALUE memo)
|
|
920
934
|
{
|
|
921
935
|
set_cp(data, str_cp);
|
|
922
936
|
return 1;
|
|
@@ -963,7 +977,7 @@ cs_method_case_insensitive(VALUE self)
|
|
|
963
977
|
}
|
|
964
978
|
|
|
965
979
|
static inline VALUE
|
|
966
|
-
each_sb_cp(VALUE str, str_cp_handler func, cs_ar *cp_arr, cs_cp len, struct cs_data *data, VALUE
|
|
980
|
+
each_sb_cp(VALUE str, str_cp_handler func, cs_ar *cp_arr, cs_cp len, struct cs_data *data, VALUE memo)
|
|
967
981
|
{
|
|
968
982
|
long i, str_len;
|
|
969
983
|
unsigned int str_cp;
|
|
@@ -982,21 +996,29 @@ each_sb_cp(VALUE str, str_cp_handler func, cs_ar *cp_arr, cs_cp len, struct cs_d
|
|
|
982
996
|
}
|
|
983
997
|
|
|
984
998
|
static inline VALUE
|
|
985
|
-
each_mb_cp(VALUE str, str_cp_handler func, cs_ar *cp_arr, cs_cp len, struct cs_data *data, VALUE
|
|
999
|
+
each_mb_cp(VALUE str, str_cp_handler func, cs_ar *cp_arr, cs_cp len, struct cs_data *data, VALUE memo)
|
|
986
1000
|
{
|
|
987
1001
|
int n;
|
|
988
1002
|
unsigned int str_cp;
|
|
989
1003
|
const char *ptr, *end;
|
|
990
|
-
rb_encoding *
|
|
1004
|
+
rb_encoding *utf8;
|
|
1005
|
+
|
|
1006
|
+
utf8 = rb_utf8_encoding();
|
|
1007
|
+
if (rb_enc_get(str) == utf8)
|
|
1008
|
+
{
|
|
1009
|
+
str = rb_str_new_frozen(str);
|
|
1010
|
+
}
|
|
1011
|
+
else
|
|
1012
|
+
{
|
|
1013
|
+
str = rb_str_encode(str, rb_enc_from_encoding(utf8), 0, Qnil);
|
|
1014
|
+
}
|
|
991
1015
|
|
|
992
|
-
str = rb_str_new_frozen(str);
|
|
993
1016
|
ptr = RSTRING_PTR(str);
|
|
994
1017
|
end = RSTRING_END(str);
|
|
995
|
-
enc = rb_enc_get(str);
|
|
996
1018
|
|
|
997
1019
|
while (ptr < end)
|
|
998
1020
|
{
|
|
999
|
-
str_cp = rb_enc_codepoint_len(ptr, end, &n,
|
|
1021
|
+
str_cp = rb_enc_codepoint_len(ptr, end, &n, utf8);
|
|
1000
1022
|
if (!(*func)(str_cp, cp_arr, len, data, memo))
|
|
1001
1023
|
{
|
|
1002
1024
|
return Qfalse;
|
|
@@ -1027,12 +1049,13 @@ single_byte_optimizable(VALUE str)
|
|
|
1027
1049
|
}
|
|
1028
1050
|
|
|
1029
1051
|
static inline VALUE
|
|
1030
|
-
each_cp(VALUE str, str_cp_handler func, cs_ar *cp_arr, cs_cp len, struct cs_data *data, VALUE
|
|
1052
|
+
each_cp(VALUE str, str_cp_handler func, cs_ar *cp_arr, cs_cp len, struct cs_data *data, VALUE memo)
|
|
1031
1053
|
{
|
|
1032
1054
|
if (single_byte_optimizable(str))
|
|
1033
1055
|
{
|
|
1034
1056
|
return each_sb_cp(str, func, cp_arr, len, data, memo);
|
|
1035
1057
|
}
|
|
1058
|
+
|
|
1036
1059
|
return each_mb_cp(str, func, cp_arr, len, data, memo);
|
|
1037
1060
|
}
|
|
1038
1061
|
|
|
@@ -1046,22 +1069,23 @@ raise_arg_err_unless_string(VALUE val)
|
|
|
1046
1069
|
}
|
|
1047
1070
|
|
|
1048
1071
|
static VALUE
|
|
1049
|
-
|
|
1072
|
+
cs_class_method_of_string(VALUE self, VALUE string)
|
|
1050
1073
|
{
|
|
1051
1074
|
VALUE new_cs;
|
|
1052
1075
|
struct cs_data *new_data;
|
|
1076
|
+
|
|
1077
|
+
raise_arg_err_unless_string(string);
|
|
1053
1078
|
new_cs = cs_alloc(self, &new_data);
|
|
1054
|
-
|
|
1055
|
-
each_cp(str, add_str_cp_to_arr, 0, 0, new_data, 0);
|
|
1079
|
+
each_cp(string, add_str_cp_to_arr, 0, 0, new_data, 0);
|
|
1056
1080
|
return new_cs;
|
|
1057
1081
|
}
|
|
1058
1082
|
|
|
1059
1083
|
static inline int
|
|
1060
|
-
count_str_cp(unsigned int str_cp, cs_ar *cp_arr, cs_cp len, struct cs_data *data, VALUE
|
|
1084
|
+
count_str_cp(unsigned int str_cp, cs_ar *cp_arr, cs_cp len, struct cs_data *data, VALUE memo)
|
|
1061
1085
|
{
|
|
1062
1086
|
if (tst_cp(cp_arr, len, str_cp))
|
|
1063
1087
|
{
|
|
1064
|
-
*memo += 1;
|
|
1088
|
+
*((VALUE *)memo) += 1;
|
|
1065
1089
|
}
|
|
1066
1090
|
return 1;
|
|
1067
1091
|
}
|
|
@@ -1069,17 +1093,17 @@ count_str_cp(unsigned int str_cp, cs_ar *cp_arr, cs_cp len, struct cs_data *data
|
|
|
1069
1093
|
static VALUE
|
|
1070
1094
|
cs_method_count_in(VALUE self, VALUE str)
|
|
1071
1095
|
{
|
|
1072
|
-
|
|
1096
|
+
long count;
|
|
1073
1097
|
struct cs_data *data;
|
|
1074
1098
|
raise_arg_err_unless_string(str);
|
|
1075
1099
|
data = cs_fetch_data(self);
|
|
1076
1100
|
count = 0;
|
|
1077
|
-
each_cp(str, count_str_cp, data->cps, data->len, data, &count);
|
|
1078
|
-
return
|
|
1101
|
+
each_cp(str, count_str_cp, data->cps, data->len, data, (VALUE)&count);
|
|
1102
|
+
return LONG2FIX(count);
|
|
1079
1103
|
}
|
|
1080
1104
|
|
|
1081
1105
|
static inline int
|
|
1082
|
-
str_cp_in_arr(unsigned int str_cp, cs_ar *cp_arr, cs_cp len, struct cs_data *data, VALUE
|
|
1106
|
+
str_cp_in_arr(unsigned int str_cp, cs_ar *cp_arr, cs_cp len, struct cs_data *data, VALUE memo)
|
|
1083
1107
|
{
|
|
1084
1108
|
return tst_cp(cp_arr, len, str_cp);
|
|
1085
1109
|
}
|
|
@@ -1094,11 +1118,11 @@ cs_method_cover_p(VALUE self, VALUE str)
|
|
|
1094
1118
|
}
|
|
1095
1119
|
|
|
1096
1120
|
static inline int
|
|
1097
|
-
add_str_cp_to_str_arr(unsigned int str_cp, cs_ar *cp_arr, cs_cp len, struct cs_data *data, VALUE
|
|
1121
|
+
add_str_cp_to_str_arr(unsigned int str_cp, cs_ar *cp_arr, cs_cp len, struct cs_data *data, VALUE memo)
|
|
1098
1122
|
{
|
|
1099
1123
|
if (tst_cp(cp_arr, len, str_cp))
|
|
1100
1124
|
{
|
|
1101
|
-
rb_ary_push(memo
|
|
1125
|
+
rb_ary_push(memo, rb_enc_uint_chr((int)str_cp, rb_utf8_encoding()));
|
|
1102
1126
|
}
|
|
1103
1127
|
return 1;
|
|
1104
1128
|
}
|
|
@@ -1106,18 +1130,17 @@ add_str_cp_to_str_arr(unsigned int str_cp, cs_ar *cp_arr, cs_cp len, struct cs_d
|
|
|
1106
1130
|
static VALUE
|
|
1107
1131
|
cs_method_scan(VALUE self, VALUE str)
|
|
1108
1132
|
{
|
|
1109
|
-
VALUE memo
|
|
1133
|
+
VALUE memo;
|
|
1110
1134
|
struct cs_data *data;
|
|
1111
1135
|
raise_arg_err_unless_string(str);
|
|
1112
1136
|
data = cs_fetch_data(self);
|
|
1113
|
-
memo
|
|
1114
|
-
memo[1] = (VALUE)rb_enc_get(str);
|
|
1137
|
+
memo = rb_ary_new();
|
|
1115
1138
|
each_cp(str, add_str_cp_to_str_arr, data->cps, data->len, data, memo);
|
|
1116
|
-
return memo
|
|
1139
|
+
return memo;
|
|
1117
1140
|
}
|
|
1118
1141
|
|
|
1119
1142
|
static inline int
|
|
1120
|
-
str_cp_not_in_arr(unsigned int str_cp, cs_ar *cp_arr, cs_cp len, struct cs_data *data, VALUE
|
|
1143
|
+
str_cp_not_in_arr(unsigned int str_cp, cs_ar *cp_arr, cs_cp len, struct cs_data *data, VALUE memo)
|
|
1121
1144
|
{
|
|
1122
1145
|
return !tst_cp(cp_arr, len, str_cp);
|
|
1123
1146
|
}
|
|
@@ -1133,116 +1156,91 @@ cs_method_used_by_p(VALUE self, VALUE str)
|
|
|
1133
1156
|
return only_uses_other_cps == Qfalse ? Qtrue : Qfalse;
|
|
1134
1157
|
}
|
|
1135
1158
|
|
|
1136
|
-
|
|
1137
|
-
cs_str_buf_cat(VALUE str, const char *ptr, long len)
|
|
1138
|
-
{
|
|
1139
|
-
long total, olen;
|
|
1140
|
-
char *sptr;
|
|
1141
|
-
|
|
1142
|
-
RSTRING_GETMEM(str, sptr, olen);
|
|
1143
|
-
sptr = RSTRING(str)->as.heap.ptr;
|
|
1144
|
-
olen = RSTRING(str)->as.heap.len;
|
|
1145
|
-
total = olen + len;
|
|
1146
|
-
memcpy(sptr + olen, ptr, len);
|
|
1147
|
-
RSTRING(str)->as.heap.len = total;
|
|
1148
|
-
}
|
|
1149
|
-
|
|
1150
|
-
#ifndef TERM_FILL
|
|
1151
|
-
#define TERM_FILL(ptr, termlen) \
|
|
1152
|
-
do \
|
|
1153
|
-
{ \
|
|
1154
|
-
char *const term_fill_ptr = (ptr); \
|
|
1155
|
-
const int term_fill_len = (termlen); \
|
|
1156
|
-
*term_fill_ptr = '\0'; \
|
|
1157
|
-
if (__builtin_expect(!!(term_fill_len > 1), 0)) \
|
|
1158
|
-
memset(term_fill_ptr, 0, term_fill_len); \
|
|
1159
|
-
} while (0)
|
|
1160
|
-
#endif
|
|
1161
|
-
|
|
1162
|
-
static void
|
|
1163
|
-
cs_str_buf_terminate(VALUE str, rb_encoding *enc)
|
|
1164
|
-
{
|
|
1165
|
-
char *ptr;
|
|
1166
|
-
long len;
|
|
1167
|
-
|
|
1168
|
-
ptr = RSTRING(str)->as.heap.ptr;
|
|
1169
|
-
len = RSTRING(str)->as.heap.len;
|
|
1170
|
-
TERM_FILL(ptr + len, rb_enc_mbminlen(enc));
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1159
|
+
// partially based on rb_str_delete_bang
|
|
1173
1160
|
static inline VALUE
|
|
1174
1161
|
cs_apply_to_str(VALUE set, VALUE str, int delete, int bang)
|
|
1175
1162
|
{
|
|
1176
1163
|
cs_ar *cps;
|
|
1177
|
-
cs_cp
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1164
|
+
cs_cp cs_len;
|
|
1165
|
+
VALUE orig_str_len;
|
|
1166
|
+
|
|
1167
|
+
rb_encoding *orig_enc, *utf8;
|
|
1168
|
+
char *s, *send, *t;
|
|
1169
|
+
int orig_was_utf8, cr;
|
|
1183
1170
|
|
|
1184
1171
|
raise_arg_err_unless_string(str);
|
|
1185
1172
|
|
|
1186
|
-
|
|
1173
|
+
orig_str_len = RSTRING_LEN(str);
|
|
1174
|
+
|
|
1175
|
+
if (orig_str_len == 0)
|
|
1176
|
+
{
|
|
1177
|
+
return bang ? Qnil : str;
|
|
1178
|
+
}
|
|
1187
1179
|
|
|
1188
|
-
|
|
1189
|
-
|
|
1180
|
+
orig_enc = rb_enc_get(str);
|
|
1181
|
+
utf8 = rb_utf8_encoding();
|
|
1182
|
+
orig_was_utf8 = orig_enc == utf8;
|
|
1183
|
+
|
|
1184
|
+
if (!orig_was_utf8 && orig_enc != rb_usascii_encoding())
|
|
1185
|
+
{
|
|
1186
|
+
str = rb_str_encode(str, rb_enc_from_encoding(utf8), 0, Qnil);
|
|
1187
|
+
}
|
|
1188
|
+
else
|
|
1190
1189
|
{
|
|
1191
|
-
if (bang)
|
|
1190
|
+
if (!bang)
|
|
1192
1191
|
{
|
|
1193
|
-
|
|
1192
|
+
str = rb_str_dup(str);
|
|
1194
1193
|
}
|
|
1195
|
-
return rb_str_dup(str);
|
|
1196
1194
|
}
|
|
1197
1195
|
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1196
|
+
cps = cs_fetch_cps(set, &cs_len);
|
|
1197
|
+
rb_str_modify(str);
|
|
1198
|
+
s = t = RSTRING_PTR(str);
|
|
1199
|
+
send = RSTRING_END(str);
|
|
1200
|
+
cr = ENC_CODERANGE_7BIT;
|
|
1203
1201
|
|
|
1204
|
-
|
|
1205
|
-
end = RSTRING_END(str);
|
|
1206
|
-
|
|
1207
|
-
if (single_byte_optimizable(str))
|
|
1202
|
+
while (s < send)
|
|
1208
1203
|
{
|
|
1209
|
-
|
|
1204
|
+
unsigned int c;
|
|
1205
|
+
int clen;
|
|
1206
|
+
|
|
1207
|
+
if ((c = *(unsigned char *)s) < 0x80)
|
|
1210
1208
|
{
|
|
1211
|
-
|
|
1212
|
-
if ((!tst_cp(cps, len, str_cp)) == delete)
|
|
1209
|
+
if (tst_cp(cps, cs_len, c) != delete)
|
|
1213
1210
|
{
|
|
1214
|
-
|
|
1211
|
+
if (t != s)
|
|
1212
|
+
*t = c;
|
|
1213
|
+
t++;
|
|
1215
1214
|
}
|
|
1216
|
-
|
|
1215
|
+
s++;
|
|
1217
1216
|
}
|
|
1218
|
-
|
|
1219
|
-
else // likely to be multibyte string
|
|
1220
|
-
{
|
|
1221
|
-
while (ptr < end)
|
|
1217
|
+
else
|
|
1222
1218
|
{
|
|
1223
|
-
|
|
1224
|
-
|
|
1219
|
+
c = rb_enc_codepoint_len(s, send, &clen, utf8);
|
|
1220
|
+
|
|
1221
|
+
if (tst_cp(cps, cs_len, c) != delete)
|
|
1225
1222
|
{
|
|
1226
|
-
|
|
1223
|
+
if (t != s)
|
|
1224
|
+
rb_enc_mbcput(c, t, utf8);
|
|
1225
|
+
t += clen;
|
|
1226
|
+
if (cr == ENC_CODERANGE_7BIT)
|
|
1227
|
+
cr = ENC_CODERANGE_VALID;
|
|
1227
1228
|
}
|
|
1228
|
-
|
|
1229
|
+
s += clen;
|
|
1229
1230
|
}
|
|
1230
1231
|
}
|
|
1231
1232
|
|
|
1232
|
-
|
|
1233
|
+
rb_str_set_len(str, t - RSTRING_PTR(str));
|
|
1234
|
+
ENC_CODERANGE_SET(str, cr);
|
|
1233
1235
|
|
|
1234
|
-
if (bang)
|
|
1236
|
+
if (bang && (RSTRING_LEN(str) == (long)orig_str_len)) // string unchanged
|
|
1235
1237
|
{
|
|
1236
|
-
|
|
1237
|
-
{
|
|
1238
|
-
return Qnil;
|
|
1239
|
-
}
|
|
1240
|
-
rb_str_shared_replace(str, new_str_buf);
|
|
1238
|
+
return Qnil;
|
|
1241
1239
|
}
|
|
1242
|
-
|
|
1240
|
+
|
|
1241
|
+
if (!orig_was_utf8)
|
|
1243
1242
|
{
|
|
1244
|
-
|
|
1245
|
-
str = new_str_buf;
|
|
1243
|
+
return rb_str_encode(str, rb_enc_from_encoding(orig_enc), 0, Qnil);
|
|
1246
1244
|
}
|
|
1247
1245
|
|
|
1248
1246
|
return str;
|
|
@@ -1284,6 +1282,10 @@ cs_method_allocated_length(VALUE self)
|
|
|
1284
1282
|
|
|
1285
1283
|
void Init_character_set()
|
|
1286
1284
|
{
|
|
1285
|
+
#ifdef HAVE_RB_EXT_RACTOR_SAFE
|
|
1286
|
+
rb_ext_ractor_safe(true);
|
|
1287
|
+
#endif
|
|
1288
|
+
|
|
1287
1289
|
VALUE cs = rb_define_class("CharacterSet", rb_cObject);
|
|
1288
1290
|
|
|
1289
1291
|
rb_define_alloc_func(cs, cs_method_allocate);
|
|
@@ -1334,11 +1336,12 @@ void Init_character_set()
|
|
|
1334
1336
|
rb_define_method(cs, ">=", cs_method_superset_p, 1);
|
|
1335
1337
|
rb_define_method(cs, "proper_superset?", cs_method_proper_superset_p, 1);
|
|
1336
1338
|
rb_define_method(cs, ">", cs_method_proper_superset_p, 1);
|
|
1339
|
+
rb_define_method(cs, "<=>", cs_method_spaceship_operator, 1);
|
|
1337
1340
|
|
|
1338
1341
|
// `CharacterSet`-specific methods
|
|
1339
1342
|
|
|
1340
1343
|
rb_define_singleton_method(cs, "from_ranges", cs_class_method_from_ranges, -2);
|
|
1341
|
-
rb_define_singleton_method(cs, "
|
|
1344
|
+
rb_define_singleton_method(cs, "of_string", cs_class_method_of_string, 1);
|
|
1342
1345
|
|
|
1343
1346
|
rb_define_method(cs, "ranges", cs_method_ranges, 0);
|
|
1344
1347
|
rb_define_method(cs, "sample", cs_method_sample, -1);
|
|
@@ -6,7 +6,7 @@ typedef struct casefold_mapping {
|
|
|
6
6
|
unsigned long to;
|
|
7
7
|
} casefold_mapping;
|
|
8
8
|
|
|
9
|
-
#define CASEFOLD_COUNT
|
|
9
|
+
#define CASEFOLD_COUNT 1426
|
|
10
10
|
|
|
11
11
|
static const casefold_mapping unicode_casefold_table[CASEFOLD_COUNT] = {
|
|
12
12
|
{0x0041,0x0061},
|
|
@@ -564,6 +564,41 @@ static const casefold_mapping unicode_casefold_table[CASEFOLD_COUNT] = {
|
|
|
564
564
|
{0x104D1,0x104F9},
|
|
565
565
|
{0x104D2,0x104FA},
|
|
566
566
|
{0x104D3,0x104FB},
|
|
567
|
+
{0x10570,0x10597},
|
|
568
|
+
{0x10571,0x10598},
|
|
569
|
+
{0x10572,0x10599},
|
|
570
|
+
{0x10573,0x1059A},
|
|
571
|
+
{0x10574,0x1059B},
|
|
572
|
+
{0x10575,0x1059C},
|
|
573
|
+
{0x10576,0x1059D},
|
|
574
|
+
{0x10577,0x1059E},
|
|
575
|
+
{0x10578,0x1059F},
|
|
576
|
+
{0x10579,0x105A0},
|
|
577
|
+
{0x1057A,0x105A1},
|
|
578
|
+
{0x1057C,0x105A3},
|
|
579
|
+
{0x1057D,0x105A4},
|
|
580
|
+
{0x1057E,0x105A5},
|
|
581
|
+
{0x1057F,0x105A6},
|
|
582
|
+
{0x10580,0x105A7},
|
|
583
|
+
{0x10581,0x105A8},
|
|
584
|
+
{0x10582,0x105A9},
|
|
585
|
+
{0x10583,0x105AA},
|
|
586
|
+
{0x10584,0x105AB},
|
|
587
|
+
{0x10585,0x105AC},
|
|
588
|
+
{0x10586,0x105AD},
|
|
589
|
+
{0x10587,0x105AE},
|
|
590
|
+
{0x10588,0x105AF},
|
|
591
|
+
{0x10589,0x105B0},
|
|
592
|
+
{0x1058A,0x105B1},
|
|
593
|
+
{0x1058C,0x105B3},
|
|
594
|
+
{0x1058D,0x105B4},
|
|
595
|
+
{0x1058E,0x105B5},
|
|
596
|
+
{0x1058F,0x105B6},
|
|
597
|
+
{0x10590,0x105B7},
|
|
598
|
+
{0x10591,0x105B8},
|
|
599
|
+
{0x10592,0x105B9},
|
|
600
|
+
{0x10594,0x105BB},
|
|
601
|
+
{0x10595,0x105BC},
|
|
567
602
|
{0x10A0,0x2D00},
|
|
568
603
|
{0x10A1,0x2D01},
|
|
569
604
|
{0x10A2,0x2D02},
|
|
@@ -1102,6 +1137,7 @@ static const casefold_mapping unicode_casefold_table[CASEFOLD_COUNT] = {
|
|
|
1102
1137
|
{0x2C2C,0x2C5C},
|
|
1103
1138
|
{0x2C2D,0x2C5D},
|
|
1104
1139
|
{0x2C2E,0x2C5E},
|
|
1140
|
+
{0x2C2F,0x2C5F},
|
|
1105
1141
|
{0x2C60,0x2C61},
|
|
1106
1142
|
{0x2C62,0x026B},
|
|
1107
1143
|
{0x2C63,0x1D7D},
|
|
@@ -1282,10 +1318,17 @@ static const casefold_mapping unicode_casefold_table[CASEFOLD_COUNT] = {
|
|
|
1282
1318
|
{0xA7BA,0xA7BB},
|
|
1283
1319
|
{0xA7BC,0xA7BD},
|
|
1284
1320
|
{0xA7BE,0xA7BF},
|
|
1321
|
+
{0xA7C0,0xA7C1},
|
|
1285
1322
|
{0xA7C2,0xA7C3},
|
|
1286
1323
|
{0xA7C4,0xA794},
|
|
1287
1324
|
{0xA7C5,0x0282},
|
|
1288
1325
|
{0xA7C6,0x1D8E},
|
|
1326
|
+
{0xA7C7,0xA7C8},
|
|
1327
|
+
{0xA7C9,0xA7CA},
|
|
1328
|
+
{0xA7D0,0xA7D1},
|
|
1329
|
+
{0xA7D6,0xA7D7},
|
|
1330
|
+
{0xA7D8,0xA7D9},
|
|
1331
|
+
{0xA7F5,0xA7F6},
|
|
1289
1332
|
{0xAB70,0x13A0},
|
|
1290
1333
|
{0xAB71,0x13A1},
|
|
1291
1334
|
{0xAB72,0x13A2},
|
|
@@ -4,6 +4,14 @@ class CharacterSet
|
|
|
4
4
|
def character_set
|
|
5
5
|
CharacterSet.of_regexp(self)
|
|
6
6
|
end
|
|
7
|
+
|
|
8
|
+
def covered_by_character_set?(other)
|
|
9
|
+
other.superset?(character_set)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def uses_character_set?(other)
|
|
13
|
+
other.intersect?(character_set)
|
|
14
|
+
end
|
|
7
15
|
end
|
|
8
16
|
end
|
|
9
17
|
end
|
|
@@ -4,101 +4,84 @@ class CharacterSet
|
|
|
4
4
|
|
|
5
5
|
Error = Class.new(ArgumentError)
|
|
6
6
|
|
|
7
|
-
def convert(expression)
|
|
7
|
+
def convert(expression, to = CharacterSet, acc = [])
|
|
8
8
|
CharacterSet.require_optional_dependency('regexp_parser', __method__)
|
|
9
9
|
|
|
10
10
|
case expression
|
|
11
|
-
when Regexp::Expression::Root
|
|
12
|
-
if expression.count != 1
|
|
13
|
-
raise Error, 'Pass a Regexp with exactly one expression, e.g. /[a-z]/'
|
|
14
|
-
end
|
|
15
|
-
convert(expression[0])
|
|
16
|
-
|
|
17
11
|
when Regexp::Expression::CharacterSet
|
|
18
|
-
content = expression.map { |subexp| convert(subexp) }.reduce(:+)
|
|
19
|
-
content
|
|
20
|
-
expression.negative? ? content.inversion : content
|
|
12
|
+
content = expression.map { |subexp| convert(subexp, to) }.reduce(:+) || to[]
|
|
13
|
+
acc << (expression.negative? ? content.inversion : content)
|
|
21
14
|
|
|
22
15
|
when Regexp::Expression::CharacterSet::Intersection
|
|
23
|
-
expression.map { |subexp| convert(subexp) }.reduce(:&)
|
|
24
|
-
|
|
25
|
-
when Regexp::Expression::CharacterSet::IntersectedSequence
|
|
26
|
-
expression.map { |subexp| convert(subexp) }.reduce(:+) || CharacterSet[]
|
|
16
|
+
acc << expression.map { |subexp| convert(subexp, to) }.reduce(:&)
|
|
27
17
|
|
|
28
18
|
when Regexp::Expression::CharacterSet::Range
|
|
29
|
-
start, finish = expression.map { |subexp| convert(subexp) }
|
|
30
|
-
|
|
19
|
+
start, finish = expression.map { |subexp| convert(subexp, to) }
|
|
20
|
+
acc << to.new((start.min)..(finish.max))
|
|
21
|
+
|
|
22
|
+
when Regexp::Expression::Subexpression # root, group, alternation, etc.
|
|
23
|
+
expression.each { |subexp| convert(subexp, to, acc) }
|
|
31
24
|
|
|
32
25
|
when Regexp::Expression::CharacterType::Any
|
|
33
|
-
|
|
26
|
+
acc << to.unicode
|
|
34
27
|
|
|
35
28
|
when Regexp::Expression::CharacterType::Base
|
|
36
29
|
/(?<negative>non)?(?<base_name>.+)/ =~ expression.token
|
|
37
30
|
content =
|
|
38
31
|
if expression.unicode_classes?
|
|
39
|
-
# in u-mode, type shortcuts match the same as \p{<long type name>}
|
|
40
|
-
|
|
32
|
+
# in u-mode, most type shortcuts match the same as \p{<long type name>}
|
|
33
|
+
if base_name == 'linebreak'
|
|
34
|
+
to.from_ranges(10..13, 133..133, 8232..8233)
|
|
35
|
+
else
|
|
36
|
+
to.of_property(base_name)
|
|
37
|
+
end
|
|
41
38
|
else
|
|
42
39
|
# in normal mode, types match only ascii chars
|
|
43
40
|
case base_name.to_sym
|
|
44
|
-
when :digit
|
|
45
|
-
when :hex
|
|
46
|
-
when :
|
|
47
|
-
when :
|
|
41
|
+
when :digit then to.from_ranges(48..57)
|
|
42
|
+
when :hex then to.from_ranges(48..57, 65..70, 97..102)
|
|
43
|
+
when :linebreak then to.from_ranges(10..13)
|
|
44
|
+
when :space then to.from_ranges(9..13, 32..32)
|
|
45
|
+
when :word then to.from_ranges(48..57, 65..90, 95..95, 97..122)
|
|
48
46
|
else raise Error, "Unsupported CharacterType #{base_name}"
|
|
49
47
|
end
|
|
50
48
|
end
|
|
51
|
-
negative ? content.inversion : content
|
|
49
|
+
acc << (negative ? content.inversion : content)
|
|
52
50
|
|
|
53
51
|
when Regexp::Expression::EscapeSequence::CodepointList
|
|
54
|
-
|
|
52
|
+
content = to.new(expression.codepoints)
|
|
53
|
+
acc << (expression.i? ? content.case_insensitive : content)
|
|
55
54
|
|
|
56
55
|
when Regexp::Expression::EscapeSequence::Base
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
when Regexp::Expression::Group::Capture,
|
|
60
|
-
Regexp::Expression::Group::Passive,
|
|
61
|
-
Regexp::Expression::Group::Named,
|
|
62
|
-
Regexp::Expression::Group::Atomic,
|
|
63
|
-
Regexp::Expression::Group::Options
|
|
64
|
-
case expression.count
|
|
65
|
-
when 0 then CharacterSet[]
|
|
66
|
-
when 1 then convert(expression.first)
|
|
67
|
-
else
|
|
68
|
-
raise Error, 'Groups must contain exactly one expression, e.g. ([a-z])'
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
when Regexp::Expression::Alternation # rubocop:disable Lint/DuplicateBranch
|
|
72
|
-
expression.map { |subexp| convert(subexp) }.reduce(:+)
|
|
73
|
-
|
|
74
|
-
when Regexp::Expression::Alternative
|
|
75
|
-
case expression.count
|
|
76
|
-
when 0 then CharacterSet[]
|
|
77
|
-
when 1 then convert(expression.first)
|
|
78
|
-
else
|
|
79
|
-
raise Error, 'Alternatives must contain exactly one expression'
|
|
80
|
-
end
|
|
56
|
+
content = to[expression.codepoint]
|
|
57
|
+
acc << (expression.i? ? content.case_insensitive : content)
|
|
81
58
|
|
|
82
59
|
when Regexp::Expression::Literal
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
end
|
|
86
|
-
CharacterSet[expression.text.ord]
|
|
60
|
+
content = to[*expression.text.chars]
|
|
61
|
+
acc << (expression.i? ? content.case_insensitive : content)
|
|
87
62
|
|
|
88
63
|
when Regexp::Expression::UnicodeProperty::Base,
|
|
89
64
|
Regexp::Expression::PosixClass
|
|
90
|
-
content =
|
|
65
|
+
content = to.of_property(expression.token)
|
|
91
66
|
if expression.type == :posixclass && expression.ascii_classes?
|
|
92
67
|
content = content.ascii_part
|
|
93
68
|
end
|
|
94
|
-
expression.negative? ? content.inversion : content
|
|
69
|
+
acc << (expression.negative? ? content.inversion : content)
|
|
70
|
+
|
|
71
|
+
when Regexp::Expression::Anchor::Base,
|
|
72
|
+
Regexp::Expression::Backreference::Base,
|
|
73
|
+
Regexp::Expression::Keep::Mark,
|
|
74
|
+
Regexp::Expression::Quantifier
|
|
75
|
+
# ignore zero-length and repeat expressions
|
|
95
76
|
|
|
96
77
|
when Regexp::Expression::Base
|
|
97
78
|
raise Error, "Unsupported expression class `#{expression.class}`"
|
|
98
79
|
|
|
99
80
|
else
|
|
100
|
-
raise Error,
|
|
81
|
+
raise Error, 'Pass an expression (result of Regexp::Parser.parse)'
|
|
101
82
|
end
|
|
83
|
+
|
|
84
|
+
acc.reduce(:+) || to[]
|
|
102
85
|
end
|
|
103
86
|
end
|
|
104
87
|
end
|