Ruby 2.7.0p0(2019-12-25revision647ee6f091eafcce70ffb75ddf7e121e192ab217)

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
16 VALUE rb_mComparable;
17
18 static VALUE
19 rb_cmp(VALUE x, VALUE y)
20 {
21 return rb_funcallv(x, idCmp, 1, &y);
22 }
23
24 void
25 rb_cmperr(VALUE x, VALUE y)
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
39 static VALUE
40 invcmp_recursive(VALUE x, VALUE y, int recursive)
41 {
42 if (recursive) return Qnil;
43 return rb_cmp(y, x);
44 }
45
46 VALUE
47 rb_invcmp(VALUE x, VALUE y)
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
59 static VALUE
60 cmp_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
75 static VALUE
76 cmp_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
88 static int
89 cmpint(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
102 static VALUE
103 cmp_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
117 static VALUE
118 cmp_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
132 static VALUE
133 cmp_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
147 static VALUE
148 cmp_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
169 static VALUE
170 cmp_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
219 static VALUE
220 cmp_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)",
229 rb_builtin_class_name(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
293 void
294 Init_Comparable(void)
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 }
T_FLOAT
#define T_FLOAT
Definition: ruby.h:527
range
#define range(low, item, hi)
Definition: date_strftime.c:21
rb_scan_args
#define rb_scan_args(argc, argvp, fmt,...)
Definition: rb_mjit_min_header-2.7.0.h:6372
INT2FIX
#define INT2FIX(i)
Definition: ruby.h:263
VALUE
unsigned long VALUE
Definition: ruby.h:102
rb_eArgError
VALUE rb_eArgError
Definition: error.c:923
rb_define_module
VALUE rb_define_module(const char *name)
Definition: class.c:772
rb_invcmp
VALUE rb_invcmp(VALUE x, VALUE y)
Definition: compar.c:47
id.h
rb_inspect
VALUE rb_inspect(VALUE)
Convenient wrapper of Object::inspect.
Definition: object.c:551
Qundef
#define Qundef
Definition: ruby.h:470
rb_define_method
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
Definition: class.c:1551
Qfalse
#define Qfalse
Definition: ruby.h:467
SPECIAL_CONST_P
#define SPECIAL_CONST_P(x)
Definition: ruby.h:1313
Init_Comparable
void Init_Comparable(void)
Definition: compar.c:294
rb_cmpint
#define rb_cmpint(cmp, a, b)
PRIsVALUE
#define PRIsVALUE
Definition: ruby.h:166
ruby.h
rb_funcallv
#define rb_funcallv(recv, mid, argc, argv)
Definition: rb_mjit_min_header-2.7.0.h:7899
rb_raise
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2669
rb_obj_class
VALUE rb_obj_class(VALUE)
Equivalent to Object#class in Ruby.
Definition: object.c:217
rb_eTypeError
VALUE rb_eTypeError
Definition: error.c:922
rb_exec_recursive_paired_outer
VALUE rb_exec_recursive_paired_outer(VALUE(*)(VALUE, VALUE, int), VALUE, VALUE, VALUE)
Definition: thread.c:5110
internal.h
argv
char ** argv
Definition: ruby.c:223
rb_cmperr
void rb_cmperr(VALUE x, VALUE y)
Definition: compar.c:25
rb_mComparable
VALUE rb_mComparable
Definition: compar.c:16
NIL_P
#define NIL_P(v)
Definition: ruby.h:482
argc
int argc
Definition: ruby.c:222
rb_exec_recursive
VALUE rb_exec_recursive(VALUE(*)(VALUE, VALUE, int), VALUE, VALUE)
Definition: thread.c:5075
BUILTIN_TYPE
#define BUILTIN_TYPE(x)
Definition: ruby.h:551
rb_range_values
int rb_range_values(VALUE range, VALUE *begp, VALUE *endp, int *exclp)
Definition: range.c:1243
Qtrue
#define Qtrue
Definition: ruby.h:468
idCmp
@ idCmp
Definition: id.h:84
rb_builtin_class_name
const char * rb_builtin_class_name(VALUE x)
Definition: error.c:797
Qnil
#define Qnil
Definition: ruby.h:469