1/********************************************************************** 2 3 compar.c - 4 5 $Author: nagachika $ 6 created at: Thu Aug 26 14:39:48 JST 1993 7 8 Copyright (C) 1993-2007 Yukihiro Matsumoto 9 10**********************************************************************/ 11 12#include "ruby/ruby.h" 13 14VALUE rb_mComparable; 15 16static ID cmp; 17 18void 19rb_cmperr(VALUE x, VALUE y) 20{ 21 const char *classname; 22 23 if (SPECIAL_CONST_P(y)) { 24 y = rb_inspect(y); 25 classname = StringValuePtr(y); 26 } 27 else { 28 classname = rb_obj_classname(y); 29 } 30 rb_raise(rb_eArgError, "comparison of %s with %s failed", 31 rb_obj_classname(x), classname); 32} 33 34static VALUE 35invcmp_recursive(VALUE x, VALUE y, int recursive) 36{ 37 if (recursive) return Qnil; 38 return rb_check_funcall(y, cmp, 1, &x); 39} 40 41VALUE 42rb_invcmp(VALUE x, VALUE y) 43{ 44 VALUE invcmp = rb_exec_recursive(invcmp_recursive, x, y); 45 if (invcmp == Qundef || NIL_P(invcmp)) { 46 return Qnil; 47 } 48 else { 49 int result = -rb_cmpint(invcmp, x, y); 50 return INT2FIX(result); 51 } 52} 53 54static VALUE 55cmp_eq_recursive(VALUE arg1, VALUE arg2, int recursive) 56{ 57 if (recursive) return Qfalse; 58 return rb_funcall(arg1, cmp, 1, arg2); 59} 60 61static VALUE 62cmp_eq(VALUE *a) 63{ 64 VALUE c = rb_exec_recursive_paired_outer(cmp_eq_recursive, a[0], a[1], a[1]); 65 66 if (NIL_P(c)) return Qfalse; 67 if (rb_cmpint(c, a[0], a[1]) == 0) return Qtrue; 68 return Qfalse; 69} 70 71static VALUE 72cmp_failed(void) 73{ 74 return Qfalse; 75} 76 77/* 78 * call-seq: 79 * obj == other -> true or false 80 * 81 * Compares two objects based on the receiver's <code><=></code> 82 * method, returning true if it returns 0. Also returns true if 83 * _obj_ and _other_ are the same object. 84 * 85 * Even if _obj_ <=> _other_ raised an exception, the exception 86 * is ignoread and returns false. 87 */ 88 89static VALUE 90cmp_equal(VALUE x, VALUE y) 91{ 92 VALUE a[2]; 93 94 if (x == y) return Qtrue; 95 96 a[0] = x; a[1] = y; 97 return rb_rescue(cmp_eq, (VALUE)a, cmp_failed, 0); 98} 99 100/* 101 * call-seq: 102 * obj > other -> true or false 103 * 104 * Compares two objects based on the receiver's <code><=></code> 105 * method, returning true if it returns 1. 106 */ 107 108static VALUE 109cmp_gt(VALUE x, VALUE y) 110{ 111 VALUE c = rb_funcall(x, cmp, 1, y); 112 113 if (rb_cmpint(c, x, y) > 0) return Qtrue; 114 return Qfalse; 115} 116 117/* 118 * call-seq: 119 * obj >= other -> true or false 120 * 121 * Compares two objects based on the receiver's <code><=></code> 122 * method, returning true if it returns 0 or 1. 123 */ 124 125static VALUE 126cmp_ge(VALUE x, VALUE y) 127{ 128 VALUE c = rb_funcall(x, cmp, 1, y); 129 130 if (rb_cmpint(c, x, y) >= 0) return Qtrue; 131 return Qfalse; 132} 133 134/* 135 * call-seq: 136 * obj < other -> true or false 137 * 138 * Compares two objects based on the receiver's <code><=></code> 139 * method, returning true if it returns -1. 140 */ 141 142static VALUE 143cmp_lt(VALUE x, VALUE y) 144{ 145 VALUE c = rb_funcall(x, cmp, 1, y); 146 147 if (rb_cmpint(c, x, y) < 0) return Qtrue; 148 return Qfalse; 149} 150 151/* 152 * call-seq: 153 * obj <= other -> true or false 154 * 155 * Compares two objects based on the receiver's <code><=></code> 156 * method, returning true if it returns -1 or 0. 157 */ 158 159static VALUE 160cmp_le(VALUE x, VALUE y) 161{ 162 VALUE c = rb_funcall(x, cmp, 1, y); 163 164 if (rb_cmpint(c, x, y) <= 0) return Qtrue; 165 return Qfalse; 166} 167 168/* 169 * call-seq: 170 * obj.between?(min, max) -> true or false 171 * 172 * Returns <code>false</code> if <i>obj</i> <code><=></code> 173 * <i>min</i> is less than zero or if <i>anObject</i> <code><=></code> 174 * <i>max</i> is greater than zero, <code>true</code> otherwise. 175 * 176 * 3.between?(1, 5) #=> true 177 * 6.between?(1, 5) #=> false 178 * 'cat'.between?('ant', 'dog') #=> true 179 * 'gnu'.between?('ant', 'dog') #=> false 180 * 181 */ 182 183static VALUE 184cmp_between(VALUE x, VALUE min, VALUE max) 185{ 186 if (RTEST(cmp_lt(x, min))) return Qfalse; 187 if (RTEST(cmp_gt(x, max))) return Qfalse; 188 return Qtrue; 189} 190 191/* 192 * The <code>Comparable</code> mixin is used by classes whose objects 193 * may be ordered. The class must define the <code><=></code> operator, 194 * which compares the receiver against another object, returning -1, 0, 195 * or +1 depending on whether the receiver is less than, equal to, or 196 * greater than the other object. If the other object is not comparable 197 * then the <code><=></code> operator should return nil. 198 * <code>Comparable</code> uses 199 * <code><=></code> to implement the conventional comparison operators 200 * (<code><</code>, <code><=</code>, <code>==</code>, <code>>=</code>, 201 * and <code>></code>) and the method <code>between?</code>. 202 * 203 * class SizeMatters 204 * include Comparable 205 * attr :str 206 * def <=>(anOther) 207 * str.size <=> anOther.str.size 208 * end 209 * def initialize(str) 210 * @str = str 211 * end 212 * def inspect 213 * @str 214 * end 215 * end 216 * 217 * s1 = SizeMatters.new("Z") 218 * s2 = SizeMatters.new("YY") 219 * s3 = SizeMatters.new("XXX") 220 * s4 = SizeMatters.new("WWWW") 221 * s5 = SizeMatters.new("VVVVV") 222 * 223 * s1 < s2 #=> true 224 * s4.between?(s1, s3) #=> false 225 * s4.between?(s3, s5) #=> true 226 * [ s3, s2, s5, s4, s1 ].sort #=> [Z, YY, XXX, WWWW, VVVVV] 227 * 228 */ 229 230void 231Init_Comparable(void) 232{ 233#undef rb_intern 234#define rb_intern(str) rb_intern_const(str) 235 236 rb_mComparable = rb_define_module("Comparable"); 237 rb_define_method(rb_mComparable, "==", cmp_equal, 1); 238 rb_define_method(rb_mComparable, ">", cmp_gt, 1); 239 rb_define_method(rb_mComparable, ">=", cmp_ge, 1); 240 rb_define_method(rb_mComparable, "<", cmp_lt, 1); 241 rb_define_method(rb_mComparable, "<=", cmp_le, 1); 242 rb_define_method(rb_mComparable, "between?", cmp_between, 2); 243 244 cmp = rb_intern("<=>"); 245} 246