mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
mruby-regexp: implement Regexp#==, Regexp#eql?, and Regexp#hash
Two regexps are equal when they have the same source and flags. Hash is computed from source string hash mixed with flags. Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
#include <mruby/variable.h>
|
||||
#include <mruby/hash.h>
|
||||
#include <mruby/error.h>
|
||||
#include <mruby/internal.h>
|
||||
#include "re_internal.h"
|
||||
|
||||
#include <string.h>
|
||||
@@ -285,6 +286,41 @@ regexp_inspect(mrb_state *mrb, mrb_value self)
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Regexp#== (and eql?)
|
||||
*/
|
||||
static mrb_value
|
||||
regexp_eql(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_value other;
|
||||
mrb_get_args(mrb, "o", &other);
|
||||
if (!mrb_obj_is_kind_of(mrb, other, mrb_class_get(mrb, "Regexp"))) {
|
||||
return mrb_false_value();
|
||||
}
|
||||
mrb_value src1 = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@source"));
|
||||
mrb_value src2 = mrb_iv_get(mrb, other, mrb_intern_lit(mrb, "@source"));
|
||||
if (!mrb_str_equal(mrb, src1, src2)) return mrb_false_value();
|
||||
mrb_value f1 = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@flags"));
|
||||
mrb_value f2 = mrb_iv_get(mrb, other, mrb_intern_lit(mrb, "@flags"));
|
||||
mrb_int flags1 = mrb_nil_p(f1) ? 0 : mrb_integer(f1);
|
||||
mrb_int flags2 = mrb_nil_p(f2) ? 0 : mrb_integer(f2);
|
||||
return mrb_bool_value(flags1 == flags2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Regexp#hash
|
||||
*/
|
||||
static mrb_value
|
||||
regexp_hash(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_value src = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@source"));
|
||||
mrb_value flags_val = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@flags"));
|
||||
uint32_t flags = mrb_nil_p(flags_val) ? 0 : (uint32_t)mrb_integer(flags_val);
|
||||
uint32_t h = mrb_str_hash(mrb, src);
|
||||
h ^= flags * 0x9e3779b9; /* mix flags into hash */
|
||||
return mrb_int_value(mrb, (mrb_int)h);
|
||||
}
|
||||
|
||||
/*
|
||||
* Regexp.escape(str)
|
||||
*/
|
||||
@@ -532,6 +568,9 @@ mrb_mruby_regexp_gem_init(mrb_state *mrb)
|
||||
mrb_define_method(mrb, re, "source", regexp_source, MRB_ARGS_NONE());
|
||||
mrb_define_method(mrb, re, "inspect", regexp_inspect, MRB_ARGS_NONE());
|
||||
mrb_define_method(mrb, re, "to_s", regexp_to_s, MRB_ARGS_NONE());
|
||||
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());
|
||||
|
||||
/* MatchData class */
|
||||
struct RClass *md = mrb_define_class(mrb, "MatchData", mrb->object_class);
|
||||
|
||||
@@ -129,6 +129,26 @@ assert("Regexp#to_s") do
|
||||
assert_equal "(?im:abc)", Regexp.new("abc", Regexp::IGNORECASE | Regexp::MULTILINE).to_s
|
||||
end
|
||||
|
||||
assert("Regexp#== and Regexp#eql?") do
|
||||
r1 = Regexp.new("abc", Regexp::IGNORECASE)
|
||||
r2 = Regexp.new("abc", Regexp::IGNORECASE)
|
||||
r3 = Regexp.new("abc")
|
||||
r4 = Regexp.new("def", Regexp::IGNORECASE)
|
||||
assert_true r1 == r2
|
||||
assert_true r1.eql?(r2)
|
||||
assert_false r1 == r3 # different flags
|
||||
assert_false r1 == r4 # different source
|
||||
assert_false r1 == "abc" # not a Regexp
|
||||
end
|
||||
|
||||
assert("Regexp#hash") do
|
||||
r1 = Regexp.new("abc", Regexp::IGNORECASE)
|
||||
r2 = Regexp.new("abc", Regexp::IGNORECASE)
|
||||
r3 = Regexp.new("abc")
|
||||
assert_equal r1.hash, r2.hash
|
||||
assert_not_equal r1.hash, r3.hash
|
||||
end
|
||||
|
||||
assert("String#match") do
|
||||
md = "hello world".match(Regexp.new("(\\w+)\\s(\\w+)"))
|
||||
assert_equal "hello", md[1]
|
||||
|
||||
Reference in New Issue
Block a user