From 2bea39f339f62d472a18961c1cd863cc9ef2dcff Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sat, 23 Apr 2022 13:35:27 +0900 Subject: [PATCH] mruby-range-ext/range.c: `Range#cover?()` takes a range too. --- mrbgems/mruby-range-ext/src/range.c | 83 +++++++++++++++++++-------- mrbgems/mruby-range-ext/test/range.rb | 16 +++++- 2 files changed, 75 insertions(+), 24 deletions(-) diff --git a/mrbgems/mruby-range-ext/src/range.c b/mrbgems/mruby-range-ext/src/range.c index 9a88ef542..70bf0976e 100644 --- a/mrbgems/mruby-range-ext/src/range.c +++ b/mrbgems/mruby-range-ext/src/range.c @@ -2,29 +2,33 @@ #include static mrb_bool -r_le(mrb_state *mrb, mrb_value a, mrb_value b) +r_less(mrb_state *mrb, mrb_value a, mrb_value b, mrb_bool excl) { - mrb_int n = mrb_cmp(mrb, a, b); - - if (n == 0 || n == -1) return TRUE; - return FALSE; -} - -static mrb_bool -r_lt(mrb_state *mrb, mrb_value a, mrb_value b) -{ - return mrb_cmp(mrb, a, b) == -1; + switch (mrb_cmp(mrb, a, b)) { + case -2: /* failure */ + case 1: + return FALSE; + case 0: + return !excl; + case -1: + default: /* just in case */ + return TRUE; + } } /* * call-seq: * rng.cover?(obj) -> true or false + * rng.cover?(range) -> true or false * - * Returns true if +obj+ is between the begin and end of - * the range. + * Returns +true+ if the given argument is within +self+, +false+ otherwise. * - * This tests begin <= obj <= end when #exclude_end? is +false+ - * and begin <= obj < end when #exclude_end? is +true+. + * With non-range argument +object+, evaluates with <= and <. + * + * For range +self+ with included end value (#exclude_end? == false), + * evaluates thus: + * + * self.begin <= object <= self.end * * ("a".."z").cover?("c") #=> true * ("a".."z").cover?("5") #=> false @@ -40,20 +44,53 @@ range_cover(mrb_state *mrb, mrb_value range) beg = RANGE_BEG(r); end = RANGE_END(r); - if (mrb_nil_p(beg) || r_le(mrb, beg, val)) { - if (mrb_nil_p(end)) { + if (mrb_nil_p(beg) && mrb_nil_p(end)) return mrb_true_value(); + + if (mrb_range_p(val)) { + struct RRange *r2 = mrb_range_ptr(mrb, val); + mrb_value beg2 = RANGE_BEG(r2); + mrb_value end2 = RANGE_END(r2); + + /* range.cover?(nil..nil) => true */ + if (mrb_nil_p(beg2) && mrb_nil_p(end2)) return mrb_true_value(); + + /* (a..b).cover?(c..d) */ + if (mrb_nil_p(end)) { /* a.. */ + /* (a..).cover?(c..) => true */ + if (mrb_nil_p(end2)) return mrb_bool_value(mrb_cmp(mrb, beg, beg2) != -2); + /* (a..).cover?(c..d) where d false */ + if (r_less(mrb, end2, beg, RANGE_EXCL(r2))) return mrb_false_value(); return mrb_true_value(); } - if (RANGE_EXCL(r)) { - if (r_lt(mrb, val, end)) - return mrb_true_value(); + else if (mrb_nil_p(beg)) { /* ..b */ + /* (..b).cover?(..d) => true */ + if (mrb_nil_p(beg2)) return mrb_bool_value(mrb_cmp(mrb, end, end2) != -2); + /* (..b).cover?(c..d) where b false */ + if (r_less(mrb, end, beg2, RANGE_EXCL(r))) return mrb_false_value(); + return mrb_true_value(); } - else { - if (r_le(mrb, val, end)) - return mrb_true_value(); + else { /* a..b */ + /* (a..b).cover?(c..) => (c (a false */ + if (r_less(mrb, end, beg2, RANGE_EXCL(r))) return mrb_false_value(); + /* (a..b).cover?(c..d) where (d false */ + if (r_less(mrb, end2, beg, RANGE_EXCL(r2))) return mrb_false_value(); + return mrb_true_value(); } } + if (mrb_nil_p(beg) || r_less(mrb, beg, val, FALSE)) { + if (mrb_nil_p(end)) { + return mrb_true_value(); + } + if (r_less(mrb, val, end, RANGE_EXCL(r))) + return mrb_true_value(); + } return mrb_false_value(); } diff --git a/mrbgems/mruby-range-ext/test/range.rb b/mrbgems/mruby-range-ext/test/range.rb index 863c619c8..4ae575669 100644 --- a/mrbgems/mruby-range-ext/test/range.rb +++ b/mrbgems/mruby-range-ext/test/range.rb @@ -3,11 +3,25 @@ assert('Range#cover?') do assert_true ("a".."z").cover?("c") - assert_true !("a".."z").cover?("5") + assert_false ("a".."z").cover?("5") assert_true ("a".."z").cover?("cc") + assert_false ("a".."z").cover?(nil) assert_true ("a"..).cover?("c") assert_false ("a"..).cover?("5") assert_true ("a"..).cover?("cc") + assert_true (.."z").cover?("a") + assert_false (..."z").cover?("z") + assert_true (.."z").cover?("z") + assert_true (nil..nil).cover?(nil) + + assert_true ("a".."c").cover?("b".."d") + assert_true ("a"..).cover?("b"..) + assert_false ("a"..).cover?(1..) + assert_false ("d"..).cover?(.."b") + assert_true (.."c").cover?("b".."d") + assert_true (.."c").cover?(.."d") + assert_false (.."c").cover?(..2) + assert_false (.."c").cover?("d"..) end assert('Range#first') do