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 
17 
18 static VALUE
19 rb_cmp(VALUE x, VALUE y)
20 {
21  return rb_funcallv(x, idCmp, 1, &y);
22 }
23 
24 void
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
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)",
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
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