Ruby 4.1.0dev (2026-03-06 revision 9aca729140424bbf465c11ab8ab53e5cc6602c01)
compar.c (9aca729140424bbf465c11ab8ab53e5cc6602c01)
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
21static VALUE
22rb_cmp(VALUE x, VALUE y)
23{
24 return rb_funcallv(x, idCmp, 1, &y);
25}
26
27static VALUE
28cmperr_subject(VALUE y)
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 return classname;
39}
40
41void
42rb_cmperr(VALUE x, VALUE y)
43{
44 VALUE classname = cmperr_subject(y);
45 rb_raise(rb_eArgError, "comparison of %"PRIsVALUE" with %"PRIsVALUE" failed",
46 rb_obj_class(x), classname);
47}
48
49void
50rb_cmperr_reason(VALUE x, VALUE y, const char *reason)
51{
52 VALUE classname = cmperr_subject(y);
53 rb_raise(rb_eArgError, "comparison of %"PRIsVALUE" with %"PRIsVALUE" failed: %s",
54 rb_obj_class(x), classname, reason);
55}
56
57static VALUE
58invcmp_recursive(VALUE x, VALUE y, int recursive)
59{
60 if (recursive) return Qnil;
61 return rb_cmp(y, x);
62}
63
65rb_invcmp(VALUE x, VALUE y)
66{
67 VALUE invcmp = rb_exec_recursive(invcmp_recursive, x, y);
68 if (NIL_OR_UNDEF_P(invcmp)) {
69 return Qnil;
70 }
71 else {
72 int result = -rb_cmpint(invcmp, x, y);
73 return INT2FIX(result);
74 }
75}
76
77static VALUE
78cmp_eq_recursive(VALUE arg1, VALUE arg2, int recursive)
79{
80 if (recursive) return Qnil;
81 return rb_cmp(arg1, arg2);
82}
83
84/*
85 * call-seq:
86 * obj == other -> true or false
87 *
88 * Compares two objects based on the receiver's <code><=></code>
89 * method, returning true if it returns 0. Also returns true if
90 * _obj_ and _other_ are the same object.
91 */
92
93static VALUE
94cmp_equal(VALUE x, VALUE y)
95{
96 VALUE c;
97 if (x == y) return Qtrue;
98
99 c = rb_exec_recursive_paired_outer(cmp_eq_recursive, x, y, y);
100
101 if (NIL_P(c)) return Qfalse;
102 return RBOOL(rb_cmpint(c, x, y) == 0);
103}
104
105static int
106cmpint(VALUE x, VALUE y)
107{
108 return rb_cmpint(rb_cmp(x, y), x, y);
109}
110
111/*
112 * call-seq:
113 * self > other -> true or false
114 *
115 * Returns whether +self+ is "greater than" +other+;
116 * equivalent to <tt>(self <=> other) > 0</tt>:
117 *
118 * 'foo' > 'foo' # => false
119 * 'food' > 'foo' # => true
120 */
121
122static VALUE
123cmp_gt(VALUE x, VALUE y)
124{
125 return RBOOL(cmpint(x, y) > 0);
126}
127
128/*
129 * call-seq:
130 * self >= other -> true or false
131 *
132 * Returns whether +self+ is "greater than or equal to" +other+;
133 * equivalent to <tt>(self <=> other) >= 0</tt>:
134 *
135 * 'food' >= 'foo' # => true
136 * 'foo' >= 'foo' # => true
137 * 'foo' >= 'food' # => false
138 *
139 */
140
141static VALUE
142cmp_ge(VALUE x, VALUE y)
143{
144 return RBOOL(cmpint(x, y) >= 0);
145}
146
147/*
148 * call-seq:
149 * self < other -> true or false
150 *
151 * Returns whether +self+ is "less than" +other+;
152 * equivalent to <tt>(self <=> other) < 0</tt>:
153 *
154 * 'foo' < 'foo' # => false
155 * 'foo' < 'food' # => true
156 *
157 */
158
159static VALUE
160cmp_lt(VALUE x, VALUE y)
161{
162 return RBOOL(cmpint(x, y) < 0);
163}
164
165/*
166 * call-seq:
167 * self <= other -> true or false
168 *
169 * Returns whether +self+ is "less than or equal to" +other+;
170 * equivalent to <tt>(self <=> other) <= 0</tt>:
171 *
172 * 'foo' <= 'foo' # => true
173 * 'foo' <= 'food' # => true
174 * 'food' <= 'foo' # => false
175 *
176 */
177
178static VALUE
179cmp_le(VALUE x, VALUE y)
180{
181 return RBOOL(cmpint(x, y) <= 0);
182}
183
184/*
185 * call-seq:
186 * obj.between?(min, max) -> true or false
187 *
188 * Returns <code>false</code> if _obj_ <code><=></code> _min_ is less
189 * than zero or if _obj_ <code><=></code> _max_ is greater than zero,
190 * <code>true</code> otherwise.
191 *
192 * 3.between?(1, 5) #=> true
193 * 6.between?(1, 5) #=> false
194 * 'cat'.between?('ant', 'dog') #=> true
195 * 'gnu'.between?('ant', 'dog') #=> false
196 *
197 */
198
199static VALUE
200cmp_between(VALUE x, VALUE min, VALUE max)
201{
202 return RBOOL((cmpint(x, min) >= 0 && cmpint(x, max) <= 0));
203}
204
205/*
206 * call-seq:
207 * obj.clamp(min, max) -> obj
208 * obj.clamp(range) -> obj
209 *
210 * In <code>(min, max)</code> form, returns _min_ if _obj_
211 * <code><=></code> _min_ is less than zero, _max_ if _obj_
212 * <code><=></code> _max_ is greater than zero, and _obj_
213 * otherwise.
214 *
215 * 12.clamp(0, 100) #=> 12
216 * 523.clamp(0, 100) #=> 100
217 * -3.123.clamp(0, 100) #=> 0
218 *
219 * 'd'.clamp('a', 'f') #=> 'd'
220 * 'z'.clamp('a', 'f') #=> 'f'
221 *
222 * If _min_ is +nil+, it is considered smaller than _obj_,
223 * and if _max_ is +nil+, it is considered greater than _obj_.
224 *
225 * -20.clamp(0, nil) #=> 0
226 * 523.clamp(nil, 100) #=> 100
227 *
228 * In <code>(range)</code> form, returns _range.begin_ if _obj_
229 * <code><=></code> _range.begin_ is less than zero, _range.end_
230 * if _obj_ <code><=></code> _range.end_ is greater than zero, and
231 * _obj_ otherwise.
232 *
233 * 12.clamp(0..100) #=> 12
234 * 523.clamp(0..100) #=> 100
235 * -3.123.clamp(0..100) #=> 0
236 *
237 * 'd'.clamp('a'..'f') #=> 'd'
238 * 'z'.clamp('a'..'f') #=> 'f'
239 *
240 * If _range.begin_ is +nil+, it is considered smaller than _obj_,
241 * and if _range.end_ is +nil+, it is considered greater than
242 * _obj_.
243 *
244 * -20.clamp(0..) #=> 0
245 * 523.clamp(..100) #=> 100
246 *
247 * When _range.end_ is excluded and not +nil+, an exception is
248 * raised.
249 *
250 * 100.clamp(0...100) # ArgumentError
251 */
252
253static VALUE
254cmp_clamp(int argc, VALUE *argv, VALUE x)
255{
256 VALUE min, max;
257 int c, excl = 0;
258
259 if (rb_scan_args(argc, argv, "11", &min, &max) == 1) {
260 VALUE range = min;
261 if (!rb_range_values(range, &min, &max, &excl)) {
262 rb_raise(rb_eTypeError, "wrong argument type %s (expected Range)",
263 rb_builtin_class_name(range));
264 }
265 if (!NIL_P(max)) {
266 if (excl) rb_raise(rb_eArgError, "cannot clamp with an exclusive range");
267 }
268 }
269 if (!NIL_P(min) && !NIL_P(max) && cmpint(min, max) > 0) {
270 rb_raise(rb_eArgError, "min argument must be less than or equal to max argument");
271 }
272
273 if (!NIL_P(min)) {
274 c = cmpint(x, min);
275 if (c == 0) return x;
276 if (c < 0) return min;
277 }
278 if (!NIL_P(max)) {
279 c = cmpint(x, max);
280 if (c > 0) return max;
281 }
282 return x;
283}
284
285/*
286 * The Comparable mixin is used by classes whose objects may be
287 * ordered. The class must define the <code><=></code> operator,
288 * which compares the receiver against another object, returning a
289 * value less than 0, returning 0, or returning a value greater than 0,
290 * depending on whether the receiver is less than, equal to,
291 * or greater than the other object. If the other object is not
292 * comparable then the <code><=></code> operator should return +nil+.
293 * Comparable uses <code><=></code> to implement the conventional
294 * comparison operators (<code><</code>, <code><=</code>,
295 * <code>==</code>, <code>>=</code>, and <code>></code>) and the
296 * method <code>between?</code>.
297 *
298 * class StringSorter
299 * include Comparable
300 *
301 * attr :str
302 * def <=>(other)
303 * str.size <=> other.str.size
304 * end
305 *
306 * def initialize(str)
307 * @str = str
308 * end
309 *
310 * def inspect
311 * @str
312 * end
313 * end
314 *
315 * s1 = StringSorter.new("Z")
316 * s2 = StringSorter.new("YY")
317 * s3 = StringSorter.new("XXX")
318 * s4 = StringSorter.new("WWWW")
319 * s5 = StringSorter.new("VVVVV")
320 *
321 * s1 < s2 #=> true
322 * s4.between?(s1, s3) #=> false
323 * s4.between?(s3, s5) #=> true
324 * [ s3, s2, s5, s4, s1 ].sort #=> [Z, YY, XXX, WWWW, VVVVV]
325 *
326 * == What's Here
327 *
328 * Module \Comparable provides these methods, all of which use method <tt>#<=></tt>:
329 *
330 * - #<: Returns whether +self+ is less than the given object.
331 * - #<=: Returns whether +self+ is less than or equal to the given object.
332 * - #==: Returns whether +self+ is equal to the given object.
333 * - #>: Returns whether +self+ is greater than the given object.
334 * - #>=: Returns whether +self+ is greater than or equal to the given object.
335 * - #between?: Returns +true+ if +self+ is between two given objects.
336 * - #clamp: For given objects +min+ and +max+, or range <tt>(min..max)</tt>, returns:
337 *
338 * - +min+ if <tt>(self <=> min) < 0</tt>.
339 * - +max+ if <tt>(self <=> max) > 0</tt>.
340 * - +self+ otherwise.
341 *
342 */
343
344void
345Init_Comparable(void)
346{
347 rb_mComparable = rb_define_module("Comparable");
348 rb_define_method(rb_mComparable, "==", cmp_equal, 1);
349 rb_define_method(rb_mComparable, ">", cmp_gt, 1);
350 rb_define_method(rb_mComparable, ">=", cmp_ge, 1);
351 rb_define_method(rb_mComparable, "<", cmp_lt, 1);
352 rb_define_method(rb_mComparable, "<=", cmp_le, 1);
353 rb_define_method(rb_mComparable, "between?", cmp_between, 2);
354 rb_define_method(rb_mComparable, "clamp", cmp_clamp, -1);
355}
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
VALUE rb_define_module(const char *name)
Defines a top-level module.
Definition class.c:1709
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:3255
#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
VALUE rb_eTypeError
TypeError exception.
Definition error.c:1418
VALUE rb_obj_class(VALUE obj)
Queries the class of an object.
Definition object.c:264
VALUE rb_inspect(VALUE obj)
Generates a human-readable textual representation of the given object.
Definition object.c:686
VALUE rb_mComparable
Comparable module.
Definition compar.c:19
int rb_range_values(VALUE range, VALUE *begp, VALUE *endp, int *exclp)
Deconstructs a range into its components.
Definition range.c:1861
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