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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +1 -1
  3. data/.github/workflows/gouteur.yml +20 -0
  4. data/.github/workflows/lint.yml +1 -1
  5. data/.github/workflows/tests.yml +8 -2
  6. data/.gitignore +1 -0
  7. data/.gouteur.yml +2 -0
  8. data/.rubocop.yml +10 -1
  9. data/BENCHMARK.md +35 -31
  10. data/CHANGELOG.md +56 -1
  11. data/Gemfile +15 -0
  12. data/LICENSE.txt +1 -1
  13. data/README.md +24 -8
  14. data/Rakefile +2 -120
  15. data/character_set.gemspec +0 -20
  16. data/ext/character_set/character_set.c +122 -119
  17. data/ext/character_set/unicode_casefold_table.h +44 -1
  18. data/lib/character_set/core_ext/regexp_ext.rb +8 -0
  19. data/lib/character_set/core_ext/string_ext.rb +1 -1
  20. data/lib/character_set/expression_converter.rb +39 -56
  21. data/lib/character_set/parser.rb +8 -4
  22. data/lib/character_set/predefined_sets/assigned.cps +110 -78
  23. data/lib/character_set/predefined_sets/emoji.cps +16 -14
  24. data/lib/character_set/predefined_sets.rb +11 -0
  25. data/lib/character_set/ruby_fallback/character_set_methods.rb +16 -19
  26. data/lib/character_set/ruby_fallback/set_methods.rb +6 -21
  27. data/lib/character_set/ruby_fallback/vendored_set_classes.rb +385 -0
  28. data/lib/character_set/ruby_fallback.rb +18 -6
  29. data/lib/character_set/set_method_adapters.rb +4 -3
  30. data/lib/character_set/shared_methods.rb +21 -3
  31. data/lib/character_set/version.rb +1 -1
  32. data/tasks/benchmark.rake +20 -0
  33. data/{benchmarks → tasks/benchmarks}/delete_in.rb +5 -1
  34. data/{benchmarks → tasks/benchmarks}/keep_in.rb +5 -1
  35. data/tasks/benchmarks/shared.rb +28 -0
  36. data/tasks/sync_casefold_data.rake +20 -0
  37. data/tasks/sync_predefined_sets.rake +9 -0
  38. data/tasks/sync_ruby_spec.rake +65 -0
  39. metadata +22 -169
  40. data/benchmarks/shared.rb +0 -30
  41. /data/{benchmarks → tasks/benchmarks}/count_in.rb +0 -0
  42. /data/{benchmarks → tasks/benchmarks}/cover.rb +0 -0
  43. /data/{benchmarks → tasks/benchmarks}/scan.rb +0 -0
  44. /data/{benchmarks → tasks/benchmarks}/used_by.rb +0 -0
  45. /data/{benchmarks → tasks/benchmarks}/z_add.rb +0 -0
  46. /data/{benchmarks → tasks/benchmarks}/z_delete.rb +0 -0
  47. /data/{benchmarks → tasks/benchmarks}/z_merge.rb +0 -0
  48. /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 && (!tst_cp(cps, len, cp) == !on))
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
- if (on)
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
- else if (TYPE(other) == T_ARRAY)
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 *memo);
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 *memo)
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 *memo)
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 *memo)
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 *enc;
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, enc);
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 *memo)
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
- cs_class_method_of(VALUE self, VALUE str)
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
- raise_arg_err_unless_string(str);
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 *memo)
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
- VALUE count;
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 INT2NUM((int)count);
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 *memo)
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 *memo)
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[0], rb_enc_uint_chr((int)str_cp, (rb_encoding *)memo[1]));
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[2];
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[0] = rb_ary_new();
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[0];
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 *memo)
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
- static void
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 len;
1178
- rb_encoding *str_enc;
1179
- VALUE orig_len, new_str_buf;
1180
- int cp_len;
1181
- unsigned int str_cp;
1182
- const char *ptr, *end;
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
- cps = cs_fetch_cps(set, &len);
1173
+ orig_str_len = RSTRING_LEN(str);
1174
+
1175
+ if (orig_str_len == 0)
1176
+ {
1177
+ return bang ? Qnil : str;
1178
+ }
1187
1179
 
1188
- orig_len = RSTRING_LEN(str);
1189
- if (orig_len < 1) // empty string, will never change
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
- return Qnil;
1192
+ str = rb_str_dup(str);
1194
1193
  }
1195
- return rb_str_dup(str);
1196
1194
  }
1197
1195
 
1198
- new_str_buf = rb_str_buf_new(orig_len + 30); // len + margin
1199
- str_enc = rb_enc_get(str);
1200
- rb_enc_associate(new_str_buf, str_enc);
1201
- rb_str_modify(new_str_buf);
1202
- ENC_CODERANGE_SET(new_str_buf, rb_enc_asciicompat(str_enc) ? ENC_CODERANGE_7BIT : ENC_CODERANGE_VALID);
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
- ptr = RSTRING_PTR(str);
1205
- end = RSTRING_END(str);
1206
-
1207
- if (single_byte_optimizable(str))
1202
+ while (s < send)
1208
1203
  {
1209
- while (ptr < end)
1204
+ unsigned int c;
1205
+ int clen;
1206
+
1207
+ if ((c = *(unsigned char *)s) < 0x80)
1210
1208
  {
1211
- str_cp = *ptr & 0xff;
1212
- if ((!tst_cp(cps, len, str_cp)) == delete)
1209
+ if (tst_cp(cps, cs_len, c) != delete)
1213
1210
  {
1214
- cs_str_buf_cat(new_str_buf, ptr, 1);
1211
+ if (t != s)
1212
+ *t = c;
1213
+ t++;
1215
1214
  }
1216
- ptr++;
1215
+ s++;
1217
1216
  }
1218
- }
1219
- else // likely to be multibyte string
1220
- {
1221
- while (ptr < end)
1217
+ else
1222
1218
  {
1223
- str_cp = rb_enc_codepoint_len(ptr, end, &cp_len, str_enc);
1224
- if ((!tst_cp(cps, len, str_cp)) == delete)
1219
+ c = rb_enc_codepoint_len(s, send, &clen, utf8);
1220
+
1221
+ if (tst_cp(cps, cs_len, c) != delete)
1225
1222
  {
1226
- cs_str_buf_cat(new_str_buf, ptr, cp_len);
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
- ptr += cp_len;
1229
+ s += clen;
1229
1230
  }
1230
1231
  }
1231
1232
 
1232
- cs_str_buf_terminate(new_str_buf, str_enc);
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
- if (RSTRING_LEN(new_str_buf) == (long)orig_len) // string unchanged
1237
- {
1238
- return Qnil;
1239
- }
1240
- rb_str_shared_replace(str, new_str_buf);
1238
+ return Qnil;
1241
1239
  }
1242
- else
1240
+
1241
+ if (!orig_was_utf8)
1243
1242
  {
1244
- RB_OBJ_WRITE(new_str_buf, &(RBASIC(new_str_buf))->klass, rb_obj_class(str));
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, "of", cs_class_method_of, 1);
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 1383
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
@@ -2,7 +2,7 @@ class CharacterSet
2
2
  module CoreExt
3
3
  module StringExt
4
4
  def character_set
5
- CharacterSet.of(self)
5
+ CharacterSet.of_string(self)
6
6
  end
7
7
 
8
8
  {
@@ -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 ||= CharacterSet[]
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
- CharacterSet.new((start.min)..(finish.max))
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
- CharacterSet.unicode
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
- CharacterSet.of_property(base_name)
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 then CharacterSet.from_ranges(48..57)
45
- when :hex then CharacterSet.from_ranges(48..57, 65..70, 97..102)
46
- when :space then CharacterSet.from_ranges(9..13, 32..32)
47
- when :word then CharacterSet.from_ranges(48..57, 65..90, 95..95, 97..122)
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
- CharacterSet.new(expression.codepoints)
52
+ content = to.new(expression.codepoints)
53
+ acc << (expression.i? ? content.case_insensitive : content)
55
54
 
56
55
  when Regexp::Expression::EscapeSequence::Base
57
- CharacterSet[expression.codepoint]
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
- if expression.set_level == 0 && expression.text.size != 1
84
- raise Error, 'Literal runs outside of sets are codepoint *sequences*'
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 = CharacterSet.of_property(expression.token)
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, "Pass an expression (result of Regexp::Parser.parse)"
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