mruby-regexp: add /regex/ literal support, $~, Regexp.compile

- /regex/ literal syntax now works (compiler generates Regexp.compile)
- Regexp#match and Regexp#=~ set $~ global variable
- Regexp.last_match(n) for accessing capture groups
- Regexp.compile as alias for Regexp.new
- Regexp#options

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2026-03-21 09:01:37 +09:00
parent 1cfa153ff3
commit 3bfb27b999
4 changed files with 51 additions and 4 deletions
+16
View File
@@ -0,0 +1,16 @@
class Regexp
def self.compile(pattern, *args)
new(pattern, *args)
end
def options
@flags.to_i
end
# $1-$9 convenience methods via $~
def self.last_match(n = nil)
md = $~
return md if n.nil?
md ? md[n] : nil
end
end
-1
View File
@@ -429,7 +429,6 @@ compile_quantified(re_compiler *c)
mrb_bool nongreedy = (peek(c) == '?');
if (nongreedy) next_char(c);
uint32_t atom_len = c->code_len - start;
if (ch == '*') {
/* e* → L: SPLIT(body, end); body; JMP L; end:
+11 -3
View File
@@ -105,6 +105,7 @@ create_matchdata(mrb_state *mrb, mrb_value str, int *captures, int ncap)
mrb_value obj = mrb_obj_value(mrb_data_object_alloc(mrb, md_class, md, &matchdata_type));
/* store in $~ */
mrb_gv_set(mrb, mrb_intern_lit(mrb, "$~"), obj);
return obj;
}
@@ -127,7 +128,10 @@ regexp_match(mrb_state *mrb, mrb_value self)
int ncap = re_exec(mrb, pat, RSTRING_PTR(str), RSTRING_LEN(str), pos,
captures, pat->num_captures * 2);
if (ncap == 0) return mrb_nil_value();
if (ncap == 0) {
mrb_gv_set(mrb, mrb_intern_lit(mrb, "$~"), mrb_nil_value());
return mrb_nil_value();
}
return create_matchdata(mrb, str, captures, pat->num_captures * 2);
}
@@ -171,7 +175,11 @@ regexp_match_op(mrb_state *mrb, mrb_value self)
int ncap = re_exec(mrb, pat, RSTRING_PTR(str), RSTRING_LEN(str), 0,
captures, pat->num_captures * 2);
if (ncap == 0) return mrb_nil_value();
if (ncap == 0) {
mrb_gv_set(mrb, mrb_intern_lit(mrb, "$~"), mrb_nil_value());
return mrb_nil_value();
}
create_matchdata(mrb, str, captures, pat->num_captures * 2);
return mrb_int_value(mrb, captures[0]);
}
@@ -393,7 +401,7 @@ mrb_mruby_regexp_gem_init(mrb_state *mrb)
/* Class methods */
mrb_define_method(mrb, re, "initialize", regexp_init, MRB_ARGS_ARG(1, 2));
mrb_define_class_method(mrb, re, "compile", regexp_init, MRB_ARGS_ARG(1, 2));
/* compile is defined in Ruby (mrblib) as alias for new */
mrb_define_class_method(mrb, re, "escape", regexp_escape, MRB_ARGS_REQ(1));
mrb_define_class_method(mrb, re, "quote", regexp_escape, MRB_ARGS_REQ(1));
+24
View File
@@ -139,3 +139,27 @@ end
assert("String#scan") do
assert_equal ["1", "2", "3"], "a1b2c3".scan(Regexp.new("\\d"))
end
assert("Regexp literal /regex/") do
assert_true /abc/.match?("abc")
assert_equal "123", /\d+/.match("abc123")[0]
assert_true /hello/i.match?("HELLO")
end
assert("$~ global variable") do
/(\w+)@(\w+)/ =~ "user@host"
assert_kind_of MatchData, $~
assert_equal "user", $~[1]
assert_equal "host", $~[2]
end
assert("$~ is nil on no match") do
/xyz/ =~ "abc"
assert_nil $~
end
assert("Regexp.last_match") do
/(\d+)/ =~ "abc123"
assert_equal "123", Regexp.last_match(1)
assert_equal "123", Regexp.last_match(0)
end