Ruby 4.1.0dev (2026-10-28 revision 130c9ec44bd35c7319cdc99c301419188510e3a9)
compar.c (130c9ec44bd35c7319cdc99c301419188510e3a9)
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 * :markup: markdown
207 *
208 * call-seq:
209 * clamp(min, max) -> min, max, or self
210 * clamp(range) -> range.begin, range.end, or self
211 *
212 * Returns an object of the same type as `self`.
213 *
214 * With non-`nil` arguments `min` and `max` given, returns:
215 *
216 * - `min`, if `self <= min`.
217 * - `max`, if `self >= max`.
218 * - `self`, otherwise.
219 *
220 * Examples:
221 *
222 * ```ruby
223 * 0.clamp(0, 9) # => 0
224 * -1.clamp(0, 9) # => 0
225 * 9.clamp(0, 9) # => 9
226 * 10.clamp(0, 9) # => 9
227 * 5.clamp(0, 9) # => 5
228 * ```
229 *
230 * With `nil` argument `min` given and non-`nil` argument `max` given, returns:
231 *
232 * - `max`, if `self >= max`.
233 * - `self`, otherwise.
234 *
235 * Examples:
236 *
237 * ```ruby
238 * 9.clamp(nil, 9) # => 9
239 * 10.clamp(nil, 9) # => 9
240 * 0.clamp(nil, 9) # => 0
241 * ```
242 *
243 * With non-`nil` argument `min` and`nil` argument `max` given, returns:
244 *
245 * - `min`, if `self <= min`.
246 * - `self`, otherwise.
247 *
248 * Examples:
249 *
250 * ```ruby
251 * 0.clamp(0, nil) # => 0
252 * -1.clamp(0, nil) # => 0
253 * 1.clamp(0, nil) # => 1
254 * ```
255 *
256 * With `nil` arguments `min` and `max` given, returns `self`:
257 *
258 * ```ruby
259 * 0.clamp(nil, nil) # => 0
260 * 0.clamp(nil, nil) # => 0
261 * ```
262 *
263 * In all cases above, raises ArgumentError if comparison fails.
264 *
265 * With [bounded range][bounded range] `range` given, returns:
266 *
267 * - `range.begin`, if `self <= range.begin`.
268 * - `range.end`, if `self >= range.end`.
269 * - `self`, otherwise.
270 *
271 * Examples:
272 *
273 * ```ruby
274 * 0.clamp(0..9) # => 0
275 * -1.clamp(0..9) # => 0
276 * 9.clamp(0..9) # => 9
277 * 10.clamp(0..9) # => 9
278 * 5.clamp(0..9) # => 5
279 * 5.clamp(0...9) # Raises ArgumentError:cannot clamp with an exclusive range.
280 * ```
281 *
282 * With [beginless range][beginless range] `range` given, returns:
283 *
284 * - `range.end`, if `self >= range.end`.
285 * - `self`, otherwise.
286 *
287 * Examples:
288 *
289 * ```ruby
290 * 9.clamp(nil..9) # => 9
291 * 10.clamp(nil..9) # => 9
292 * 0.clamp(nil..9) # => 0
293 * 0.clamp(nil...9) # Raises ArgumentError: cannot clamp with an exclusive range.
294 * ```
295 *
296 * With [endless range][endless range] `range` given, returns:
297 *
298 * - `range.begin`, if `self <= range.begin`.
299 * - `self`, otherwise.
300 *
301 * Examples:
302 *
303 * ```ruby
304 * 0.clamp(0..nil) # => 0
305 * -1.clamp(0..nil) # => 0
306 * 1.clamp(0..nil) # => 1
307 * 1.clamp(0...nil) # => 1
308 * ```
309 *
310 * With [unbounded range][unbounded range] `range` given, returns `self`:
311 *
312 * ```ruby
313 * 0.clamp(nil..nil) # => 0
314 * 0.clamp(nil...nil) # => 0
315 * ```
316 *
317 * [bounded range]: rdoc-ref:Range@Bounded+Ranges
318 * [beginless range]: rdoc-ref:Range@Beginless+Ranges
319 * [endless range]: rdoc-ref:Range@Endless+Ranges
320 * [unbounded range]: rdoc-ref:Range@Unbounded+Ranges
321 *
322 */
323
324static VALUE
325cmp_clamp(int argc, VALUE *argv, VALUE x)
326{
327 VALUE min, max;
328 int c, excl = 0;
329
330 if (rb_scan_args(argc, argv, "11", &min, &max) == 1) {
331 VALUE range = min;
332 if (!rb_range_values(range, &min, &max, &excl)) {
333 rb_raise(rb_eTypeError, "wrong argument type %s (expected Range)",
334 rb_builtin_class_name(range));
335 }
336 if (!NIL_P(max)) {
337 if (excl) rb_raise(rb_eArgError, "cannot clamp with an exclusive range");
338 }
339 }
340 if (!NIL_P(min) && !NIL_P(max) && cmpint(min, max) > 0) {
341 rb_raise(rb_eArgError, "min argument must be less than or equal to max argument");
342 }
343
344 if (!NIL_P(min)) {
345 c = cmpint(x, min);
346 if (c == 0) return x;
347 if (c < 0) return min;
348 }
349 if (!NIL_P(max)) {
350 c = cmpint(x, max);
351 if (c > 0) return max;
352 }
353 return x;
354}
355
356/*
357 * The Comparable mixin is used by classes whose objects may be
358 * ordered. The class must define the <code><=></code> operator,
359 * which compares the receiver against another object, returning a
360 * value less than 0, returning 0, or returning a value greater than 0,
361 * depending on whether the receiver is less than, equal to,
362 * or greater than the other object. If the other object is not
363 * comparable then the <code><=></code> operator should return +nil+.
364 * Comparable uses <code><=></code> to implement the conventional
365 * comparison operators (<code><</code>, <code><=</code>,
366 * <code>==</code>, <code>>=</code>, and <code>></code>) and the
367 * method <code>between?</code>.
368 *
369 * class StringSorter
370 * include Comparable
371 *
372 * attr :str
373 * def <=>(other)
374 * str.size <=> other.str.size
375 * end
376 *
377 * def initialize(str)
378 * @str = str
379 * end
380 *
381 * def inspect
382 * @str
383 * end
384 * end
385 *
386 * s1 = StringSorter.new("Z")
387 * s2 = StringSorter.new("YY")
388 * s3 = StringSorter.new("XXX")
389 * s4 = StringSorter.new("WWWW")
390 * s5 = StringSorter.new("VVVVV")
391 *
392 * s1 < s2 #=> true
393 * s4.between?(s1, s3) #=> false
394 * s4.between?(s3, s5) #=> true
395 * [ s3, s2, s5, s4, s1 ].sort #=> [Z, YY, XXX, WWWW, VVVVV]
396 *
397 * == What's Here
398 *
399 * Module \Comparable provides these methods, all of which use method <tt>#<=></tt>:
400 *
401 * - #<: Returns whether +self+ is less than the given object.
402 * - #<=: Returns whether +self+ is less than or equal to the given object.
403 * - #==: Returns whether +self+ is equal to the given object.
404 * - #>: Returns whether +self+ is greater than the given object.
405 * - #>=: Returns whether +self+ is greater than or equal to the given object.
406 * - #between?: Returns +true+ if +self+ is between two given objects.
407 * - #clamp: For given objects +min+ and +max+, or range <tt>(min..max)</tt>, returns:
408 *
409 * - +min+ if <tt>(self <=> min) < 0</tt>.
410 * - +max+ if <tt>(self <=> max) > 0</tt>.
411 * - +self+ otherwise.
412 *
413 */
414
415void
416Init_Comparable(void)
417{
418 rb_mComparable = rb_define_module("Comparable");
419 rb_define_method(rb_mComparable, "==", cmp_equal, 1);
420 rb_define_method(rb_mComparable, ">", cmp_gt, 1);
421 rb_define_method(rb_mComparable, ">=", cmp_ge, 1);
422 rb_define_method(rb_mComparable, "<", cmp_lt, 1);
423 rb_define_method(rb_mComparable, "<=", cmp_le, 1);
424 rb_define_method(rb_mComparable, "between?", cmp_between, 2);
425 rb_define_method(rb_mComparable, "clamp", cmp_clamp, -1);
426}
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
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:3350
#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:1457
VALUE rb_obj_class(VALUE obj)
Queries the class of an object.
Definition object.c:240
VALUE rb_inspect(VALUE obj)
Generates a human-readable textual representation of the given object.
Definition object.c:675
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:1106
int rb_range_values(VALUE range, VALUE *begp, VALUE *endp, int *exclp)
Deconstructs a range into its components.
Definition range.c:1857
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