Ruby 2.7.7p221 (2022-11-24 revision 168ec2b1e5ad0e4688e963d9de019557c78feed9)
compar.c
Go to the documentation of this file.
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 "ruby/ruby.h"
13#include "id.h"
14#include "internal.h"
15
17
18static VALUE
19rb_cmp(VALUE x, VALUE y)
20{
21 return rb_funcallv(x, idCmp, 1, &y);
22}
23
24void
26{
27 VALUE classname;
28
29 if (SPECIAL_CONST_P(y) || BUILTIN_TYPE(y) == T_FLOAT) {
30 classname = rb_inspect(y);
31 }
32 else {
33 classname = rb_obj_class(y);
34 }
35 rb_raise(rb_eArgError, "comparison of %"PRIsVALUE" with %"PRIsVALUE" failed",
36 rb_obj_class(x), classname);
37}
38
39static VALUE
40invcmp_recursive(VALUE x, VALUE y, int recursive)
41{
42 if (recursive) return Qnil;
43 return rb_cmp(y, x);
44}
45
48{
49 VALUE invcmp = rb_exec_recursive(invcmp_recursive, x, y);
50 if (invcmp == Qundef || NIL_P(invcmp)) {
51 return Qnil;
52 }
53 else {
54 int result = -rb_cmpint(invcmp, x, y);
55 return INT2FIX(result);
56 }
57}
58
59static VALUE
60cmp_eq_recursive(VALUE arg1, VALUE arg2, int recursive)
61{
62 if (recursive) return Qnil;
63 return rb_cmp(arg1, arg2);
64}
65
66/*
67 * call-seq:
68 * obj == other -> true or false
69 *
70 * Compares two objects based on the receiver's <code><=></code>
71 * method, returning true if it returns 0. Also returns true if
72 * _obj_ and _other_ are the same object.
73 */
74
75static VALUE
76cmp_equal(VALUE x, VALUE y)
77{
78 VALUE c;
79 if (x == y) return Qtrue;
80
81 c = rb_exec_recursive_paired_outer(cmp_eq_recursive, x, y, y);
82
83 if (NIL_P(c)) return Qfalse;
84 if (rb_cmpint(c, x, y) == 0) return Qtrue;
85 return Qfalse;
86}
87
88static int
89cmpint(VALUE x, VALUE y)
90{
91 return rb_cmpint(rb_cmp(x, y), x, y);
92}
93
94/*
95 * call-seq:
96 * obj > other -> true or false
97 *
98 * Compares two objects based on the receiver's <code><=></code>
99 * method, returning true if it returns a value greater than 0.
100 */
101
102static VALUE
103cmp_gt(VALUE x, VALUE y)
104{
105 if (cmpint(x, y) > 0) return Qtrue;
106 return Qfalse;
107}
108
109/*
110 * call-seq:
111 * obj >= other -> true or false
112 *
113 * Compares two objects based on the receiver's <code><=></code>
114 * method, returning true if it returns a value greater than or equal to 0.
115 */
116
117static VALUE
118cmp_ge(VALUE x, VALUE y)
119{
120 if (cmpint(x, y) >= 0) return Qtrue;
121 return Qfalse;
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
132static VALUE
133cmp_lt(VALUE x, VALUE y)
134{
135 if (cmpint(x, y) < 0) return Qtrue;
136 return Qfalse;
137}
138
139/*
140 * call-seq:
141 * obj <= other -> true or false
142 *
143 * Compares two objects based on the receiver's <code><=></code>
144 * method, returning true if it returns a value less than or equal to 0.
145 */
146
147static VALUE
148cmp_le(VALUE x, VALUE y)
149{
150 if (cmpint(x, y) <= 0) return Qtrue;
151 return Qfalse;
152}
153
154/*
155 * call-seq:
156 * obj.between?(min, max) -> true or false
157 *
158 * Returns <code>false</code> if _obj_ <code><=></code> _min_ is less
159 * than zero or if _obj_ <code><=></code> _max_ is greater than zero,
160 * <code>true</code> otherwise.
161 *
162 * 3.between?(1, 5) #=> true
163 * 6.between?(1, 5) #=> false
164 * 'cat'.between?('ant', 'dog') #=> true
165 * 'gnu'.between?('ant', 'dog') #=> false
166 *
167 */
168
169static VALUE
170cmp_between(VALUE x, VALUE min, VALUE max)
171{
172 if (cmpint(x, min) < 0) return Qfalse;
173 if (cmpint(x, max) > 0) return Qfalse;
174 return Qtrue;
175}
176
177/*
178 * call-seq:
179 * obj.clamp(min, max) -> obj
180 * obj.clamp(range) -> obj
181 *
182 * In <code>(min, max)</code> form, returns _min_ if _obj_
183 * <code><=></code> _min_ is less than zero, _max_ if _obj_
184 * <code><=></code> _max_ is greater than zero, and _obj_
185 * otherwise.
186 *
187 * 12.clamp(0, 100) #=> 12
188 * 523.clamp(0, 100) #=> 100
189 * -3.123.clamp(0, 100) #=> 0
190 *
191 * 'd'.clamp('a', 'f') #=> 'd'
192 * 'z'.clamp('a', 'f') #=> 'f'
193 *
194 * In <code>(range)</code> form, returns _range.begin_ if _obj_
195 * <code><=></code> _range.begin_ is less than zero, _range.end_
196 * if _obj_ <code><=></code> _range.end_ is greater than zero, and
197 * _obj_ otherwise.
198 *
199 * 12.clamp(0..100) #=> 12
200 * 523.clamp(0..100) #=> 100
201 * -3.123.clamp(0..100) #=> 0
202 *
203 * 'd'.clamp('a'..'f') #=> 'd'
204 * 'z'.clamp('a'..'f') #=> 'f'
205 *
206 * If _range.begin_ is +nil+, it is considered smaller than _obj_,
207 * and if _range.end_ is +nil+, it is considered greater than
208 * _obj_.
209 *
210 * -20.clamp(0..) #=> 0
211 * 523.clamp(..100) #=> 100
212 *
213 * When _range.end_ is excluded and not +nil+, an exception is
214 * raised.
215 *
216 * 100.clamp(0...100) # ArgumentError
217 */
218
219static VALUE
220cmp_clamp(int argc, VALUE *argv, VALUE x)
221{
222 VALUE min, max;
223 int c, excl = 0;
224
225 if (rb_scan_args(argc, argv, "11", &min, &max) == 1) {
226 VALUE range = min;
227 if (!rb_range_values(range, &min, &max, &excl)) {
228 rb_raise(rb_eTypeError, "wrong argument type %s (expected Range)",
230 }
231 if (!NIL_P(max)) {
232 if (excl) rb_raise(rb_eArgError, "cannot clamp with an exclusive range");
233 if (!NIL_P(min) && cmpint(min, max) > 0) goto arg_error;
234 }
235 }
236 else if (cmpint(min, max) > 0) {
237 arg_error:
238 rb_raise(rb_eArgError, "min argument must be smaller than 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 SizeMatters
267 * include Comparable
268 * attr :str
269 * def <=>(other)
270 * str.size <=> other.str.size
271 * end
272 * def initialize(str)
273 * @str = str
274 * end
275 * def inspect
276 * @str
277 * end
278 * end
279 *
280 * s1 = SizeMatters.new("Z")
281 * s2 = SizeMatters.new("YY")
282 * s3 = SizeMatters.new("XXX")
283 * s4 = SizeMatters.new("WWWW")
284 * s5 = SizeMatters.new("VVVVV")
285 *
286 * s1 < s2 #=> true
287 * s4.between?(s1, s3) #=> false
288 * s4.between?(s3, s5) #=> true
289 * [ s3, s2, s5, s4, s1 ].sort #=> [Z, YY, XXX, WWWW, VVVVV]
290 *
291 */
292
293void
295{
296#undef rb_intern
297#define rb_intern(str) rb_intern_const(str)
298
299 rb_mComparable = rb_define_module("Comparable");
300 rb_define_method(rb_mComparable, "==", cmp_equal, 1);
301 rb_define_method(rb_mComparable, ">", cmp_gt, 1);
302 rb_define_method(rb_mComparable, ">=", cmp_ge, 1);
303 rb_define_method(rb_mComparable, "<", cmp_lt, 1);
304 rb_define_method(rb_mComparable, "<=", cmp_le, 1);
305 rb_define_method(rb_mComparable, "between?", cmp_between, 2);
306 rb_define_method(rb_mComparable, "clamp", cmp_clamp, -1);
307}
VALUE rb_invcmp(VALUE x, VALUE y)
Definition: compar.c:47
void Init_Comparable(void)
Definition: compar.c:294
VALUE rb_mComparable
Definition: compar.c:16
void rb_cmperr(VALUE x, VALUE y)
Definition: compar.c:25
#define range(low, item, hi)
Definition: date_strftime.c:21
#define rb_cmpint(cmp, a, b)
VALUE rb_define_module(const char *)
Definition: class.c:785
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2671
VALUE rb_eTypeError
Definition: error.c:924
const char * rb_builtin_class_name(VALUE x)
Definition: error.c:799
VALUE rb_eArgError
Definition: error.c:925
VALUE rb_obj_class(VALUE)
Equivalent to Object#class in Ruby.
Definition: object.c:217
VALUE rb_inspect(VALUE)
Convenient wrapper of Object::inspect.
Definition: object.c:551
#define rb_funcallv(recv, mid, argc, argv)
int rb_range_values(VALUE range, VALUE *begp, VALUE *endp, int *exclp)
Definition: range.c:1248
#define Qundef
#define T_FLOAT
#define NIL_P(v)
VALUE rb_exec_recursive_paired_outer(VALUE(*)(VALUE, VALUE, int), VALUE, VALUE, VALUE)
Definition: thread.c:5109
#define PRIsVALUE
#define rb_scan_args(argc, argvp, fmt,...)
#define Qtrue
#define Qnil
#define Qfalse
#define INT2FIX(i)
#define SPECIAL_CONST_P(x)
const VALUE * argv
VALUE rb_exec_recursive(VALUE(*)(VALUE, VALUE, int), VALUE, VALUE)
Definition: thread.c:5074
void rb_define_method(VALUE, const char *, VALUE(*)(), int)
#define BUILTIN_TYPE(x)
unsigned long VALUE
Definition: ruby.h:102