mruby-regexp: fix Regexp#options to return Ruby constant values

Internal flags (MULTILINE=2, DOTALL=4, EXTENDED=8) differ from
Ruby constants (EXTENDED=2, MULTILINE=4). Convert in C instead
of returning raw internal flags. Also add Regexp#casefold?.

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2026-03-21 14:32:45 +09:00
parent 3f627c0d7d
commit 893cb4edc4
3 changed files with 47 additions and 3 deletions
+1 -3
View File
@@ -8,9 +8,7 @@ class Regexp
@named_captures || {}
end
def options
@flags.to_i
end
# options is implemented in C (internal flags -> Ruby constants conversion)
def self.last_match(n = nil)
md = $~
+30
View File
@@ -253,6 +253,34 @@ regexp_source(mrb_state *mrb, mrb_value self)
return mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@source"));
}
/*
* Regexp#options - convert internal flags to Ruby constants
* Internal: IGNORECASE=1, MULTILINE=2, DOTALL=4, EXTENDED=8
* Ruby: IGNORECASE=1, EXTENDED=2, MULTILINE=4
*/
static mrb_value
regexp_options(mrb_state *mrb, mrb_value self)
{
mrb_value flags_val = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@flags"));
uint32_t iflags = mrb_nil_p(flags_val) ? 0 : (uint32_t)mrb_integer(flags_val);
mrb_int opts = 0;
if (iflags & RE_FLAG_IGNORECASE) opts |= 1; /* Regexp::IGNORECASE */
if (iflags & RE_FLAG_EXTENDED) opts |= 2; /* Regexp::EXTENDED */
if (iflags & RE_FLAG_MULTILINE) opts |= 4; /* Regexp::MULTILINE */
return mrb_fixnum_value(opts);
}
/*
* Regexp#casefold?
*/
static mrb_value
regexp_casefold_p(mrb_state *mrb, mrb_value self)
{
mrb_value flags_val = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@flags"));
uint32_t iflags = mrb_nil_p(flags_val) ? 0 : (uint32_t)mrb_integer(flags_val);
return mrb_bool_value((iflags & RE_FLAG_IGNORECASE) != 0);
}
/*
* Regexp#to_s - CRuby-compatible (?flags:source) format
*/
@@ -574,6 +602,8 @@ mrb_mruby_regexp_gem_init(mrb_state *mrb)
mrb_define_method(mrb, re, "==", regexp_eql, MRB_ARGS_REQ(1));
mrb_define_method(mrb, re, "eql?", regexp_eql, MRB_ARGS_REQ(1));
mrb_define_method(mrb, re, "hash", regexp_hash, MRB_ARGS_NONE());
mrb_define_method(mrb, re, "options", regexp_options, MRB_ARGS_NONE());
mrb_define_method(mrb, re, "casefold?", regexp_casefold_p, MRB_ARGS_NONE());
/* MatchData class */
struct RClass *md = mrb_define_class(mrb, "MatchData", mrb->object_class);
+16
View File
@@ -149,6 +149,22 @@ assert("Regexp#hash") do
assert_not_equal r1.hash, r3.hash
end
assert("Regexp#options") do
assert_equal 0, Regexp.new("abc").options
assert_equal Regexp::IGNORECASE, Regexp.new("abc", Regexp::IGNORECASE).options
assert_equal Regexp::MULTILINE, Regexp.new("abc", Regexp::MULTILINE).options
assert_equal Regexp::EXTENDED, Regexp.new("abc", Regexp::EXTENDED).options
assert_equal Regexp::IGNORECASE | Regexp::MULTILINE,
Regexp.new("abc", Regexp::IGNORECASE | Regexp::MULTILINE).options
assert_equal Regexp::IGNORECASE | Regexp::EXTENDED | Regexp::MULTILINE,
Regexp.new("abc", Regexp::IGNORECASE | Regexp::EXTENDED | Regexp::MULTILINE).options
end
assert("Regexp#casefold?") do
assert_true Regexp.new("abc", Regexp::IGNORECASE).casefold?
assert_false Regexp.new("abc").casefold?
end
assert("Regexp extended mode (x flag)") do
# whitespace is ignored
re = Regexp.new('a b c', Regexp::EXTENDED)