Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
range.c
Go to the documentation of this file.
1 /*
2 ** range.c - Range class
3 **
4 ** See Copyright Notice in mruby.h
5 */
6 
7 #include "mruby.h"
8 #include "mruby/class.h"
9 #include "mruby/range.h"
10 #include "mruby/string.h"
11 
12 #define RANGE_CLASS (mrb_class_get(mrb, "Range"))
13 
14 static void
15 range_check(mrb_state *mrb, mrb_value a, mrb_value b)
16 {
17  mrb_value ans;
18  enum mrb_vtype ta;
19  enum mrb_vtype tb;
20 
21  ta = mrb_type(a);
22  tb = mrb_type(b);
23  if ((ta == MRB_TT_FIXNUM || ta == MRB_TT_FLOAT) &&
24  (tb == MRB_TT_FIXNUM || tb == MRB_TT_FLOAT)) {
25  return;
26  }
27 
28  ans = mrb_funcall(mrb, a, "<=>", 1, b);
29  if (mrb_nil_p(ans)) {
30  /* can not be compared */
31  mrb_raise(mrb, E_ARGUMENT_ERROR, "bad value for range");
32  }
33 }
34 
36 mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, int excl)
37 {
38  struct RRange *r;
39 
40  r = (struct RRange*)mrb_obj_alloc(mrb, MRB_TT_RANGE, RANGE_CLASS);
41  range_check(mrb, beg, end);
42  r->edges = (mrb_range_edges *)mrb_malloc(mrb, sizeof(mrb_range_edges));
43  r->edges->beg = beg;
44  r->edges->end = end;
45  r->excl = excl;
46  return mrb_range_value(r);
47 }
48 
49 /*
50  * call-seq:
51  * rng.first => obj
52  * rng.begin => obj
53  *
54  * Returns the first object in <i>rng</i>.
55  */
58 {
59  struct RRange *r = mrb_range_ptr(range);
60 
61  return r->edges->beg;
62 }
63 
64 /*
65  * call-seq:
66  * rng.end => obj
67  * rng.last => obj
68  *
69  * Returns the object that defines the end of <i>rng</i>.
70  *
71  * (1..10).end #=> 10
72  * (1...10).end #=> 10
73  */
74 
77 {
78  struct RRange *r = mrb_range_ptr(range);
79 
80  return r->edges->end;
81 }
82 
83 /*
84  * call-seq:
85  * range.exclude_end? => true or false
86  *
87  * Returns <code>true</code> if <i>range</i> excludes its end value.
88  */
91 {
92  struct RRange *r = mrb_range_ptr(range);
93 
94  return mrb_bool_value(r->excl);
95 }
96 
97 static void
98 range_init(mrb_state *mrb, mrb_value range, mrb_value beg, mrb_value end, int exclude_end)
99 {
100  struct RRange *r = mrb_range_ptr(range);
101 
102  range_check(mrb, beg, end);
103  r->excl = exclude_end;
104  if (!r->edges) {
105  r->edges = (mrb_range_edges *)mrb_malloc(mrb, sizeof(mrb_range_edges));
106  }
107  r->edges->beg = beg;
108  r->edges->end = end;
109 }
110 /*
111  * call-seq:
112  * Range.new(start, end, exclusive=false) => range
113  *
114  * Constructs a range using the given <i>start</i> and <i>end</i>. If the third
115  * parameter is omitted or is <code>false</code>, the <i>range</i> will include
116  * the end object; otherwise, it will be excluded.
117  */
118 
119 mrb_value
121 {
122  mrb_value beg, end;
123  mrb_bool exclusive;
124  int n;
125 
126  n = mrb_get_args(mrb, "oo|b", &beg, &end, &exclusive);
127  if (n != 3) {
128  exclusive = 0;
129  }
130  /* Ranges are immutable, so that they should be initialized only once. */
131  range_init(mrb, range, beg, end, exclusive);
132  return range;
133 }
134 /*
135  * call-seq:
136  * range == obj => true or false
137  *
138  * Returns <code>true</code> only if
139  * 1) <i>obj</i> is a Range,
140  * 2) <i>obj</i> has equivalent beginning and end items (by comparing them with <code>==</code>),
141  * 3) <i>obj</i> has the same #exclude_end? setting as <i>rng</t>.
142  *
143  * (0..2) == (0..2) #=> true
144  * (0..2) == Range.new(0,2) #=> true
145  * (0..2) == (0...2) #=> false
146  *
147  */
148 
149 mrb_value
151 {
152  struct RRange *rr;
153  struct RRange *ro;
154  mrb_value obj;
155 
156  mrb_get_args(mrb, "o", &obj);
157 
158  if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value();
159  if (!mrb_obj_is_instance_of(mrb, obj, mrb_obj_class(mrb, range))) { /* same class? */
160  return mrb_false_value();
161  }
162 
163  rr = mrb_range_ptr(range);
164  ro = mrb_range_ptr(obj);
165  if (!mrb_bool(mrb_funcall(mrb, rr->edges->beg, "==", 1, ro->edges->beg)) ||
166  !mrb_bool(mrb_funcall(mrb, rr->edges->end, "==", 1, ro->edges->end)) ||
167  rr->excl != ro->excl) {
168  return mrb_false_value();
169  }
170  return mrb_true_value();
171 }
172 
173 static mrb_bool
174 r_le(mrb_state *mrb, mrb_value a, mrb_value b)
175 {
176  mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */
177  /* output :a < b => -1, a = b => 0, a > b => +1 */
178 
179  if (mrb_type(r) == MRB_TT_FIXNUM) {
180  mrb_int c = mrb_fixnum(r);
181  if (c == 0 || c == -1) return TRUE;
182  }
183 
184  return FALSE;
185 }
186 
187 static mrb_bool
188 r_gt(mrb_state *mrb, mrb_value a, mrb_value b)
189 {
190  mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b);
191  /* output :a < b => -1, a = b => 0, a > b => +1 */
192 
193  if (mrb_type(r) == MRB_TT_FIXNUM) {
194  if (mrb_fixnum(r) == 1) return TRUE;
195  }
196 
197  return FALSE;
198 }
199 
200 static mrb_bool
201 r_ge(mrb_state *mrb, mrb_value a, mrb_value b)
202 {
203  mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */
204  /* output :a < b => -1, a = b => 0, a > b => +1 */
205 
206  if (mrb_type(r) == MRB_TT_FIXNUM) {
207  mrb_int c = mrb_fixnum(r);
208  if (c == 0 || c == 1) return TRUE;
209  }
210 
211  return FALSE;
212 }
213 
214 /*
215  * call-seq:
216  * range === obj => true or false
217  * range.member?(val) => true or false
218  * range.include?(val) => true or false
219  *
220  */
221 mrb_value
223 {
224  mrb_value val;
225  struct RRange *r = mrb_range_ptr(range);
226  mrb_value beg, end;
227  mrb_bool include_p;
228 
229  mrb_get_args(mrb, "o", &val);
230 
231  beg = r->edges->beg;
232  end = r->edges->end;
233  include_p = r_le(mrb, beg, val) && /* beg <= val */
234  ((r->excl && r_gt(mrb, end, val)) || /* end > val */
235  (r_ge(mrb, end, val))); /* end >= val */
236 
237  return mrb_bool_value(include_p);
238 }
239 
240 /*
241  * call-seq:
242  * rng.each {| i | block } => rng
243  *
244  * Iterates over the elements <i>rng</i>, passing each in turn to the
245  * block. You can only iterate if the start object of the range
246  * supports the +succ+ method (which means that you can't iterate over
247  * ranges of +Float+ objects).
248  *
249  * (10..15).each do |n|
250  * print n, ' '
251  * end
252  *
253  * <em>produces:</em>
254  *
255  * 10 11 12 13 14 15
256  */
257 
258 mrb_value
260 {
261  return range;
262 }
263 
264 mrb_int
266 {
267  mrb_int beg, end, b, e;
268  struct RRange *r = mrb_range_ptr(range);
269 
270  if (mrb_type(range) != MRB_TT_RANGE) {
271  mrb_raise(mrb, E_TYPE_ERROR, "expected Range.");
272  }
273 
274  beg = b = mrb_fixnum(r->edges->beg);
275  end = e = mrb_fixnum(r->edges->end);
276 
277  if (beg < 0) {
278  beg += len;
279  if (beg < 0) return FALSE;
280  }
281 
282  if (beg > len) return FALSE;
283  if (end > len) end = len;
284 
285  if (end < 0) end += len;
286  if (!r->excl && end < len) end++; /* include end point */
287  len = end - beg;
288  if (len < 0) len = 0;
289 
290  *begp = beg;
291  *lenp = len;
292  return TRUE;
293 }
294 
295 /* 15.2.14.4.12(x) */
296 /*
297  * call-seq:
298  * rng.to_s -> string
299  *
300  * Convert this range object to a printable form.
301  */
302 
303 static mrb_value
304 range_to_s(mrb_state *mrb, mrb_value range)
305 {
306  mrb_value str, str2;
307  struct RRange *r = mrb_range_ptr(range);
308 
309  str = mrb_obj_as_string(mrb, r->edges->beg);
310  str2 = mrb_obj_as_string(mrb, r->edges->end);
311  str = mrb_str_dup(mrb, str);
312  mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2);
313  mrb_str_append(mrb, str, str2);
314 
315  return str;
316 }
317 
318 /* 15.2.14.4.13(x) */
319 /*
320  * call-seq:
321  * rng.inspect -> string
322  *
323  * Convert this range object to a printable form (using
324  * <code>inspect</code> to convert the start and end
325  * objects).
326  */
327 
328 static mrb_value
329 range_inspect(mrb_state *mrb, mrb_value range)
330 {
331  mrb_value str, str2;
332  struct RRange *r = mrb_range_ptr(range);
333 
334  str = mrb_inspect(mrb, r->edges->beg);
335  str2 = mrb_inspect(mrb, r->edges->end);
336  str = mrb_str_dup(mrb, str);
337  mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2);
338  mrb_str_append(mrb, str, str2);
339 
340  return str;
341 }
342 
343 /* 15.2.14.4.14(x) */
344 /*
345  * call-seq:
346  * rng.eql?(obj) -> true or false
347  *
348  * Returns <code>true</code> only if <i>obj</i> is a Range, has equivalent
349  * beginning and end items (by comparing them with #eql?), and has the same
350  * #exclude_end? setting as <i>rng</i>.
351  *
352  * (0..2).eql?(0..2) #=> true
353  * (0..2).eql?(Range.new(0,2)) #=> true
354  * (0..2).eql?(0...2) #=> false
355  *
356  */
357 
358 static mrb_value
359 range_eql(mrb_state *mrb, mrb_value range)
360 {
361  mrb_value obj;
362  struct RRange *r, *o;
363 
364  mrb_get_args(mrb, "o", &obj);
365 
366  if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value();
367  if (!mrb_obj_is_kind_of(mrb, obj, RANGE_CLASS)) {
368  return mrb_false_value();
369  }
370  if (mrb_type(obj) != MRB_TT_RANGE) return mrb_false_value();
371 
372  r = mrb_range_ptr(range);
373  o = mrb_range_ptr(obj);
374  if (!mrb_eql(mrb, r->edges->beg, o->edges->beg) ||
375  !mrb_eql(mrb, r->edges->end, o->edges->end) ||
376  (r->excl != o->excl)) {
377  return mrb_false_value();
378  }
379  return mrb_true_value();
380 }
381 
382 /* 15.2.14.4.15(x) */
383 mrb_value
385 {
386  mrb_value src;
387  struct RRange *r;
388 
389  mrb_get_args(mrb, "o", &src);
390 
391  if (mrb_obj_equal(mrb, copy, src)) return copy;
392  if (!mrb_obj_is_instance_of(mrb, src, mrb_obj_class(mrb, copy))) {
393  mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
394  }
395 
396  r = mrb_range_ptr(src);
397  range_init(mrb, copy, r->edges->beg, r->edges->end, r->excl);
398 
399  return copy;
400 }
401 
402 void
404 {
405  struct RClass *r;
406 
407  r = mrb_define_class(mrb, "Range", mrb->object_class);
409 
410  mrb_include_module(mrb, r, mrb_class_get(mrb, "Enumerable"));
411 
412  mrb_define_method(mrb, r, "begin", mrb_range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.3 */
413  mrb_define_method(mrb, r, "end", mrb_range_end, MRB_ARGS_NONE()); /* 15.2.14.4.5 */
414  mrb_define_method(mrb, r, "==", mrb_range_eq, MRB_ARGS_REQ(1)); /* 15.2.14.4.1 */
415  mrb_define_method(mrb, r, "===", mrb_range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.2 */
416  mrb_define_method(mrb, r, "each", mrb_range_each, MRB_ARGS_NONE()); /* 15.2.14.4.4 */
417  mrb_define_method(mrb, r, "exclude_end?", mrb_range_excl, MRB_ARGS_NONE()); /* 15.2.14.4.6 */
418  mrb_define_method(mrb, r, "first", mrb_range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.7 */
419  mrb_define_method(mrb, r, "include?", mrb_range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.8 */
420  mrb_define_method(mrb, r, "initialize", mrb_range_initialize, MRB_ARGS_ANY()); /* 15.2.14.4.9 */
421  mrb_define_method(mrb, r, "last", mrb_range_end, MRB_ARGS_NONE()); /* 15.2.14.4.10 */
422  mrb_define_method(mrb, r, "member?", mrb_range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.11 */
423 
424  mrb_define_method(mrb, r, "to_s", range_to_s, MRB_ARGS_NONE()); /* 15.2.14.4.12(x) */
425  mrb_define_method(mrb, r, "inspect", range_inspect, MRB_ARGS_NONE()); /* 15.2.14.4.13(x) */
426  mrb_define_method(mrb, r, "eql?", range_eql, MRB_ARGS_REQ(1)); /* 15.2.14.4.14(x) */
427  mrb_define_method(mrb, r, "initialize_copy", range_initialize_copy, MRB_ARGS_REQ(1)); /* 15.2.14.4.15(x) */
428 }