Ruby  3.4.0dev (2024-12-06 revision 892c46283a5ea4179500d951c9d4866c0051f27b)
compar.c (892c46283a5ea4179500d951c9d4866c0051f27b)
1 /**********************************************************************
2 
3  compar.c -
4 
5  $Author$
6  created at: Thu Aug 26 14:39:48 JST 1993
7 
8  Copyright (C) 1993-2007 Yukihiro Matsumoto
9 
10 **********************************************************************/
11 
12 #include "id.h"
13 #include "internal.h"
14 #include "internal/compar.h"
15 #include "internal/error.h"
16 #include "internal/vm.h"
17 #include "ruby/ruby.h"
18 
20 
21 static VALUE
22 rb_cmp(VALUE x, VALUE y)
23 {
24  return rb_funcallv(x, idCmp, 1, &y);
25 }
26 
27 void
29 {
30  VALUE classname;
31 
32  if (SPECIAL_CONST_P(y) || BUILTIN_TYPE(y) == T_FLOAT) {
33  classname = rb_inspect(y);
34  }
35  else {
36  classname = rb_obj_class(y);
37  }
38  rb_raise(rb_eArgError, "comparison of %"PRIsVALUE" with %"PRIsVALUE" failed",
39  rb_obj_class(x), classname);
40 }
41 
42 static VALUE
43 invcmp_recursive(VALUE x, VALUE y, int recursive)
44 {
45  if (recursive) return Qnil;
46  return rb_cmp(y, x);
47 }
48 
49 VALUE
50 rb_invcmp(VALUE x, VALUE y)
51 {
52  VALUE invcmp = rb_exec_recursive(invcmp_recursive, x, y);
53  if (NIL_OR_UNDEF_P(invcmp)) {
54  return Qnil;
55  }
56  else {
57  int result = -rb_cmpint(invcmp, x, y);
58  return INT2FIX(result);
59  }
60 }
61 
62 static VALUE
63 cmp_eq_recursive(VALUE arg1, VALUE arg2, int recursive)
64 {
65  if (recursive) return Qnil;
66  return rb_cmp(arg1, arg2);
67 }
68 
69 /*
70  * call-seq:
71  * obj == other -> true or false
72  *
73  * Compares two objects based on the receiver's <code><=></code>
74  * method, returning true if it returns 0. Also returns true if
75  * _obj_ and _other_ are the same object.
76  */
77 
78 static VALUE
79 cmp_equal(VALUE x, VALUE y)
80 {
81  VALUE c;
82  if (x == y) return Qtrue;
83 
84  c = rb_exec_recursive_paired_outer(cmp_eq_recursive, x, y, y);
85 
86  if (NIL_P(c)) return Qfalse;
87  return RBOOL(rb_cmpint(c, x, y) == 0);
88 }
89 
90 static int
91 cmpint(VALUE x, VALUE y)
92 {
93  return rb_cmpint(rb_cmp(x, y), x, y);
94 }
95 
96 /*
97  * call-seq:
98  * obj > other -> true or false
99  *
100  * Compares two objects based on the receiver's <code><=></code>
101  * method, returning true if it returns a value greater than 0.
102  */
103 
104 static VALUE
105 cmp_gt(VALUE x, VALUE y)
106 {
107  return RBOOL(cmpint(x, y) > 0);
108 }
109 
110 /*
111  * call-seq:
112  * obj >= other -> true or false
113  *
114  * Compares two objects based on the receiver's <code><=></code>
115  * method, returning true if it returns a value greater than or equal to 0.
116  */
117 
118 static VALUE
119 cmp_ge(VALUE x, VALUE y)
120 {
121  return RBOOL(cmpint(x, y) >= 0);
122 }
123 
124 /*
125  * call-seq:
126  * obj < other -> true or false
127  *
128  * Compares two objects based on the receiver's <code><=></code>
129  * method, returning true if it returns a value less than 0.
130  */
131 
132 static VALUE
133 cmp_lt(VALUE x, VALUE y)
134 {
135  return RBOOL(cmpint(x, y) < 0);
136 }
137 
138 /*
139  * call-seq:
140  * obj <= other -> true or false
141  *
142  * Compares two objects based on the receiver's <code><=></code>
143  * method, returning true if it returns a value less than or equal to 0.
144  */
145 
146 static VALUE
147 cmp_le(VALUE x, VALUE y)
148 {
149  return RBOOL(cmpint(x, y) <= 0);
150 }
151 
152 /*
153  * call-seq:
154  * obj.between?(min, max) -> true or false
155  *
156  * Returns <code>false</code> if _obj_ <code><=></code> _min_ is less
157  * than zero or if _obj_ <code><=></code> _max_ is greater than zero,
158  * <code>true</code> otherwise.
159  *
160  * 3.between?(1, 5) #=> true
161  * 6.between?(1, 5) #=> false
162  * 'cat'.between?('ant', 'dog') #=> true
163  * 'gnu'.between?('ant', 'dog') #=> false
164  *
165  */
166 
167 static VALUE
168 cmp_between(VALUE x, VALUE min, VALUE max)
169 {
170  return RBOOL((cmpint(x, min) >= 0 && cmpint(x, max) <= 0));
171 }
172 
173 /*
174  * call-seq:
175  * obj.clamp(min, max) -> obj
176  * obj.clamp(range) -> obj
177  *
178  * In <code>(min, max)</code> form, returns _min_ if _obj_
179  * <code><=></code> _min_ is less than zero, _max_ if _obj_
180  * <code><=></code> _max_ is greater than zero, and _obj_
181  * otherwise.
182  *
183  * 12.clamp(0, 100) #=> 12
184  * 523.clamp(0, 100) #=> 100
185  * -3.123.clamp(0, 100) #=> 0
186  *
187  * 'd'.clamp('a', 'f') #=> 'd'
188  * 'z'.clamp('a', 'f') #=> 'f'
189  *
190  * If _min_ is +nil+, it is considered smaller than _obj_,
191  * and if _max_ is +nil+, it is considered greater than _obj_.
192  *
193  * -20.clamp(0, nil) #=> 0
194  * 523.clamp(nil, 100) #=> 100
195  *
196  * In <code>(range)</code> form, returns _range.begin_ if _obj_
197  * <code><=></code> _range.begin_ is less than zero, _range.end_
198  * if _obj_ <code><=></code> _range.end_ is greater than zero, and
199  * _obj_ otherwise.
200  *
201  * 12.clamp(0..100) #=> 12
202  * 523.clamp(0..100) #=> 100
203  * -3.123.clamp(0..100) #=> 0
204  *
205  * 'd'.clamp('a'..'f') #=> 'd'
206  * 'z'.clamp('a'..'f') #=> 'f'
207  *
208  * If _range.begin_ is +nil+, it is considered smaller than _obj_,
209  * and if _range.end_ is +nil+, it is considered greater than
210  * _obj_.
211  *
212  * -20.clamp(0..) #=> 0
213  * 523.clamp(..100) #=> 100
214  *
215  * When _range.end_ is excluded and not +nil+, an exception is
216  * raised.
217  *
218  * 100.clamp(0...100) # ArgumentError
219  */
220 
221 static VALUE
222 cmp_clamp(int argc, VALUE *argv, VALUE x)
223 {
224  VALUE min, max;
225  int c, excl = 0;
226 
227  if (rb_scan_args(argc, argv, "11", &min, &max) == 1) {
228  VALUE range = min;
229  if (!rb_range_values(range, &min, &max, &excl)) {
230  rb_raise(rb_eTypeError, "wrong argument type %s (expected Range)",
231  rb_builtin_class_name(range));
232  }
233  if (!NIL_P(max)) {
234  if (excl) rb_raise(rb_eArgError, "cannot clamp with an exclusive range");
235  }
236  }
237  if (!NIL_P(min) && !NIL_P(max) && cmpint(min, max) > 0) {
238  rb_raise(rb_eArgError, "min argument must be less than or equal to max argument");
239  }
240 
241  if (!NIL_P(min)) {
242  c = cmpint(x, min);
243  if (c == 0) return x;
244  if (c < 0) return min;
245  }
246  if (!NIL_P(max)) {
247  c = cmpint(x, max);
248  if (c > 0) return max;
249  }
250  return x;
251 }
252 
253 /*
254  * The Comparable mixin is used by classes whose objects may be
255  * ordered. The class must define the <code><=></code> operator,
256  * which compares the receiver against another object, returning a
257  * value less than 0, returning 0, or returning a value greater than 0,
258  * depending on whether the receiver is less than, equal to,
259  * or greater than the other object. If the other object is not
260  * comparable then the <code><=></code> operator should return +nil+.
261  * Comparable uses <code><=></code> to implement the conventional
262  * comparison operators (<code><</code>, <code><=</code>,
263  * <code>==</code>, <code>>=</code>, and <code>></code>) and the
264  * method <code>between?</code>.
265  *
266  * class StringSorter
267  * include Comparable
268  *
269  * attr :str
270  * def <=>(other)
271  * str.size <=> other.str.size
272  * end
273  *
274  * def initialize(str)
275  * @str = str
276  * end
277  *
278  * def inspect
279  * @str
280  * end
281  * end
282  *
283  * s1 = StringSorter.new("Z")
284  * s2 = StringSorter.new("YY")
285  * s3 = StringSorter.new("XXX")
286  * s4 = StringSorter.new("WWWW")
287  * s5 = StringSorter.new("VVVVV")
288  *
289  * s1 < s2 #=> true
290  * s4.between?(s1, s3) #=> false
291  * s4.between?(s3, s5) #=> true
292  * [ s3, s2, s5, s4, s1 ].sort #=> [Z, YY, XXX, WWWW, VVVVV]
293  *
294  * == What's Here
295  *
296  * \Module \Comparable provides these methods, all of which use method <tt>#<=></tt>:
297  *
298  * - #<: Returns whether +self+ is less than the given object.
299  * - #<=: Returns whether +self+ is less than or equal to the given object.
300  * - #==: Returns whether +self+ is equal to the given object.
301  * - #>: Returns whether +self+ is greater than the given object.
302  * - #>=: Returns whether +self+ is greater than or equal to the given object.
303  * - #between?: Returns +true+ if +self+ is between two given objects.
304  * - #clamp: For given objects +min+ and +max+, or range <tt>(min..max)</tt>, returns:
305  *
306  * - +min+ if <tt>(self <=> min) < 0</tt>.
307  * - +max+ if <tt>(self <=> max) > 0</tt>.
308  * - +self+ otherwise.
309  *
310  */
311 
312 void
313 Init_Comparable(void)
314 {
315  rb_mComparable = rb_define_module("Comparable");
316  rb_define_method(rb_mComparable, "==", cmp_equal, 1);
317  rb_define_method(rb_mComparable, ">", cmp_gt, 1);
318  rb_define_method(rb_mComparable, ">=", cmp_ge, 1);
319  rb_define_method(rb_mComparable, "<", cmp_lt, 1);
320  rb_define_method(rb_mComparable, "<=", cmp_le, 1);
321  rb_define_method(rb_mComparable, "between?", cmp_between, 2);
322  rb_define_method(rb_mComparable, "clamp", cmp_clamp, -1);
323 }
VALUE rb_define_module(const char *name)
Defines a top-level module.
Definition: class.c:1095
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Retrieves argument from argc and argv to given VALUE references according to the format string.
Definition: class.c:2635
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a method.
Definition: class.c:2142
#define INT2FIX
Old name of RB_INT2FIX.
Definition: long.h:48
#define T_FLOAT
Old name of RUBY_T_FLOAT.
Definition: value_type.h:64
#define SPECIAL_CONST_P
Old name of RB_SPECIAL_CONST_P.
#define Qtrue
Old name of RUBY_Qtrue.
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define NIL_P
Old name of RB_NIL_P.
#define BUILTIN_TYPE
Old name of RB_BUILTIN_TYPE.
Definition: value_type.h:85
void rb_raise(VALUE exc_class, const char *fmt,...)
Exception entry point.
Definition: error.c:3635
VALUE rb_eTypeError
TypeError exception.
Definition: error.c:1408
VALUE rb_eArgError
ArgumentError exception.
Definition: error.c:1409
VALUE rb_obj_class(VALUE obj)
Queries the class of an object.
Definition: object.c:247
VALUE rb_inspect(VALUE obj)
Generates a human-readable textual representation of the given object.
Definition: object.c:680
VALUE rb_mComparable
Comparable module.
Definition: compar.c:19
VALUE rb_funcallv(VALUE recv, ID mid, int argc, const VALUE *argv)
Identical to rb_funcall(), except it takes the method arguments as a C array.
Definition: vm_eval.c:1058
int rb_cmpint(VALUE val, VALUE a, VALUE b)
Canonicalises the passed val, which is the return value of a <=> b, into C's {-1, 0,...
Definition: bignum.c:2965
void rb_cmperr(VALUE a, VALUE b)
Raises "comparison failed" error.
Definition: compar.c:28
int rb_range_values(VALUE range, VALUE *begp, VALUE *endp, int *exclp)
Deconstructs a range into its components.
Definition: range.c:1754
VALUE rb_exec_recursive(VALUE(*f)(VALUE g, VALUE h, int r), VALUE g, VALUE h)
"Recursion" API entry point.
VALUE rb_exec_recursive_paired_outer(VALUE(*f)(VALUE g, VALUE h, int r), VALUE g, VALUE p, VALUE h)
Identical to rb_exec_recursive_outer(), except it checks for the recursion on the ordered pair of { g...
uintptr_t VALUE
Type that represents a Ruby object.
Definition: value.h:40