diff --git a/internal.h b/internal.h
index 71ded35948..7aefcda8e8 100644
--- a/internal.h
+++ b/internal.h
@@ -1394,6 +1394,7 @@ VALUE rb_rational_cmp(VALUE self, VALUE other);
VALUE rb_reg_compile(VALUE str, int options, const char *sourcefile, int sourceline);
VALUE rb_reg_check_preprocess(VALUE);
long rb_reg_search0(VALUE, VALUE, long, int, int);
+VALUE rb_reg_match_p(VALUE re, VALUE str, long pos);
void rb_backref_set_string(VALUE string, long pos, long len);
int rb_match_count(VALUE match);
int rb_match_nth_defined(int nth, VALUE match);
diff --git a/re.c b/re.c
index 95ba903480..17893dd8e8 100644
--- a/re.c
+++ b/re.c
@@ -3225,19 +3225,22 @@ rb_reg_match_m(int argc, VALUE *argv, VALUE re)
static VALUE
rb_reg_match_m_p(int argc, VALUE *argv, VALUE re)
{
- VALUE str, initpos;
- long pos = 0;
+ long pos = rb_check_arity(argc, 1, 2) > 1 ? NUM2LONG(argv[1]) : 0;
+ return rb_reg_match_p(re, argv[0], pos);
+}
+
+VALUE
+rb_reg_match_p(VALUE re, VALUE str, long pos)
+{
regex_t *reg;
onig_errmsg_buffer err = "";
OnigPosition result;
const UChar *start, *end;
int tmpreg;
- rb_scan_args(argc, argv, "11", &str, &initpos);
if (NIL_P(str)) return Qfalse;
- str = SYMBOL_P(str) ? rb_sym2str(str) : rb_str_to_str(str);
- if (argc == 2) {
- pos = NUM2LONG(initpos);
+ str = SYMBOL_P(str) ? rb_sym2str(str) : StringValue(str);
+ if (pos) {
if (pos < 0) {
pos += NUM2LONG(rb_str_length(str));
if (pos < 0) return Qfalse;
diff --git a/string.c b/string.c
index 9b96843f54..97c3e2527b 100644
--- a/string.c
+++ b/string.c
@@ -3601,6 +3601,32 @@ rb_str_match_m(int argc, VALUE *argv, VALUE str)
return result;
}
+/*
+ * call-seq:
+ * str.match?(pattern) -> true or false
+ * str.match?(pattern, pos) -> true or false
+ *
+ * Converts _pattern_ to a +Regexp+ (if it isn't already one), then
+ * returns a +true+ or +false+ indicates whether the regexp is
+ * matched _str_ or not without updating $~
and other
+ * related variables. If the second parameter is present, it
+ * specifies the position in the string to begin the search.
+ *
+ * "Ruby".match?(/R.../) #=> true
+ * "Ruby".match?(/R.../, 1) #=> false
+ * "Ruby".match?(/P.../) #=> false
+ * $& #=> nil
+ */
+
+static VALUE
+rb_str_match_m_p(int argc, VALUE *argv, VALUE str)
+{
+ VALUE re;
+ rb_check_arity(argc, 1, 2);
+ re = get_pat(argv[0]);
+ return rb_reg_match_p(re, str, argc > 1 ? NUM2LONG(argv[1]) : 0);
+}
+
enum neighbor_char {
NEIGHBOR_NOT_CHAR,
NEIGHBOR_FOUND,
@@ -9738,6 +9764,19 @@ sym_match_m(int argc, VALUE *argv, VALUE sym)
return rb_str_match_m(argc, argv, rb_sym2str(sym));
}
+/*
+ * call-seq:
+ * sym.match?(obj) -> true or false
+ *
+ * Returns sym.to_s.match?(obj)
.
+ */
+
+static VALUE
+sym_match_m_p(int argc, VALUE *argv, VALUE sym)
+{
+ return rb_str_match_m_p(argc, argv, sym);
+}
+
/*
* call-seq:
* sym[idx] -> char
@@ -9924,6 +9963,7 @@ Init_String(void)
rb_define_method(rb_cString, "empty?", rb_str_empty, 0);
rb_define_method(rb_cString, "=~", rb_str_match, 1);
rb_define_method(rb_cString, "match", rb_str_match_m, -1);
+ rb_define_method(rb_cString, "match?", rb_str_match_m_p, -1);
rb_define_method(rb_cString, "succ", rb_str_succ, 0);
rb_define_method(rb_cString, "succ!", rb_str_succ_bang, 0);
rb_define_method(rb_cString, "next", rb_str_succ, 0);
@@ -10070,6 +10110,7 @@ Init_String(void)
rb_define_method(rb_cSymbol, "size", sym_length, 0);
rb_define_method(rb_cSymbol, "empty?", sym_empty, 0);
rb_define_method(rb_cSymbol, "match", sym_match_m, -1);
+ rb_define_method(rb_cSymbol, "match?", sym_match_m_p, -1);
rb_define_method(rb_cSymbol, "upcase", sym_upcase, -1);
rb_define_method(rb_cSymbol, "downcase", sym_downcase, -1);
diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb
index 2bd35b1703..39477be220 100644
--- a/test/ruby/test_string.rb
+++ b/test/ruby/test_string.rb
@@ -2111,6 +2111,50 @@ CODE
assert_raise(ArgumentError) { "foo".match }
end
+ def test_match_p_regexp
+ /backref/ =~ 'backref'
+ # must match here, but not in a separate method, e.g., assert_send,
+ # to check if $~ is affected or not.
+ assert_equal(true, "".match?(//))
+ assert_equal(true, :abc.match?(/.../))
+ assert_equal(true, 'abc'.match?(/b/))
+ assert_equal(true, 'abc'.match?(/b/, 1))
+ assert_equal(true, 'abc'.match?(/../, 1))
+ assert_equal(true, 'abc'.match?(/../, -2))
+ assert_equal(false, 'abc'.match?(/../, -4))
+ assert_equal(false, 'abc'.match?(/../, 4))
+ assert_equal(true, "\u3042xx".match?(/../, 1))
+ assert_equal(false, "\u3042x".match?(/../, 1))
+ assert_equal(true, ''.match?(/\z/))
+ assert_equal(true, 'abc'.match?(/\z/))
+ assert_equal(true, 'Ruby'.match?(/R.../))
+ assert_equal(false, 'Ruby'.match?(/R.../, 1))
+ assert_equal(false, 'Ruby'.match?(/P.../))
+ assert_equal('backref', $&)
+ end
+
+ def test_match_p_string
+ /backref/ =~ 'backref'
+ # must match here, but not in a separate method, e.g., assert_send,
+ # to check if $~ is affected or not.
+ assert_equal(true, "".match?(''))
+ assert_equal(true, :abc.match?('...'))
+ assert_equal(true, 'abc'.match?('b'))
+ assert_equal(true, 'abc'.match?('b', 1))
+ assert_equal(true, 'abc'.match?('..', 1))
+ assert_equal(true, 'abc'.match?('..', -2))
+ assert_equal(false, 'abc'.match?('..', -4))
+ assert_equal(false, 'abc'.match?('..', 4))
+ assert_equal(true, "\u3042xx".match?('..', 1))
+ assert_equal(false, "\u3042x".match?('..', 1))
+ assert_equal(true, ''.match?('\z'))
+ assert_equal(true, 'abc'.match?('\z'))
+ assert_equal(true, 'Ruby'.match?('R...'))
+ assert_equal(false, 'Ruby'.match?('R...', 1))
+ assert_equal(false, 'Ruby'.match?('P...'))
+ assert_equal('backref', $&)
+ end
+
def test_clear
s = "foo" * 100
s.clear