Merge pull request #6288 from dearblue/closing
[mruby.git] / src / range.c
blob7edb630c97cac437f70f51cadb5c9c244da4d201
1 /*
2 ** range.c - Range class
3 **
4 ** See Copyright Notice in mruby.h
5 */
7 #include <mruby.h>
8 #include <mruby/class.h>
9 #include <mruby/range.h>
10 #include <mruby/string.h>
11 #include <mruby/array.h>
12 #include <mruby/numeric.h>
13 #include <mruby/presym.h>
15 #define RANGE_INITIALIZED_FLAG 1
16 #define RANGE_INITIALIZED(p) ((p)->flags |= RANGE_INITIALIZED_FLAG)
17 #define RANGE_INITIALIZED_P(p) ((p)->flags & RANGE_INITIALIZED_FLAG)
19 static void
20 r_check(mrb_state *mrb, mrb_value a, mrb_value b)
22 enum mrb_vtype ta;
23 enum mrb_vtype tb;
24 mrb_int n;
26 ta = mrb_type(a);
27 tb = mrb_type(b);
28 #ifdef MRB_NO_FLOAT
29 if (ta == MRB_TT_INTEGER && tb == MRB_TT_INTEGER ) return;
30 #else
31 if ((ta == MRB_TT_INTEGER || ta == MRB_TT_FLOAT) &&
32 (tb == MRB_TT_INTEGER || tb == MRB_TT_FLOAT)) {
33 return;
35 #endif
37 if (mrb_nil_p(a) || mrb_nil_p(b)) return;
39 n = mrb_cmp(mrb, a, b);
40 if (n == -2) { /* can not be compared */
41 mrb_raise(mrb, E_ARGUMENT_ERROR, "bad value for range");
45 static mrb_bool
46 r_le(mrb_state *mrb, mrb_value a, mrb_value b)
48 mrb_int n = mrb_cmp(mrb, a, b);
50 if (n == 0 || n == -1) return TRUE;
51 return FALSE;
54 static mrb_bool
55 r_gt(mrb_state *mrb, mrb_value a, mrb_value b)
57 return mrb_cmp(mrb, a, b) == 1;
60 static mrb_bool
61 r_ge(mrb_state *mrb, mrb_value a, mrb_value b)
63 mrb_int n = mrb_cmp(mrb, a, b);
65 if (n == 0 || n == 1) return TRUE;
66 return FALSE;
69 static void
70 range_ptr_alloc_edges(mrb_state *mrb, struct RRange *r)
72 #ifndef MRB_RANGE_EMBED
73 r->edges = (mrb_range_edges*)mrb_malloc(mrb, sizeof(mrb_range_edges));
74 #endif
77 static struct RRange *
78 range_ptr_init(mrb_state *mrb, struct RRange *r, mrb_value beg, mrb_value end, mrb_bool excl)
80 r_check(mrb, beg, end);
82 if (r) {
83 if (RANGE_INITIALIZED_P(r)) {
84 /* Ranges are immutable, so that they should be initialized only once. */
85 mrb_name_error(mrb, MRB_SYM(initialize), "'initialize' called twice");
87 else {
88 range_ptr_alloc_edges(mrb, r);
91 else {
92 r = MRB_OBJ_ALLOC(mrb, MRB_TT_RANGE, mrb->range_class);
93 range_ptr_alloc_edges(mrb, r);
96 RANGE_BEG(r) = beg;
97 RANGE_END(r) = end;
98 RANGE_EXCL(r) = excl;
99 RANGE_INITIALIZED(r);
101 return r;
104 static void
105 range_ptr_replace(mrb_state *mrb, struct RRange *r, mrb_value beg, mrb_value end, mrb_bool excl)
107 range_ptr_init(mrb, r, beg, end, excl);
108 mrb_write_barrier(mrb, (struct RBasic*)r);
112 * call-seq:
113 * rng.first => obj
114 * rng.begin => obj
116 * Returns the first object in <i>rng</i>.
118 static mrb_value
119 range_beg(mrb_state *mrb, mrb_value range)
121 return mrb_range_beg(mrb, range);
125 * call-seq:
126 * rng.end => obj
127 * rng.last => obj
129 * Returns the object that defines the end of <i>rng</i>.
131 * (1..10).end #=> 10
132 * (1...10).end #=> 10
134 static mrb_value
135 range_end(mrb_state *mrb, mrb_value range)
137 return mrb_range_end(mrb, range);
141 * call-seq:
142 * range.exclude_end? => true or false
144 * Returns <code>true</code> if <i>range</i> excludes its end value.
146 static mrb_value
147 range_excl(mrb_state *mrb, mrb_value range)
149 return mrb_bool_value(mrb_range_excl_p(mrb, range));
153 * call-seq:
154 * Range.new(start, end, exclusive=false) => range
156 * Constructs a range using the given <i>start</i> and <i>end</i>. If the third
157 * parameter is omitted or is <code>false</code>, the <i>range</i> will include
158 * the end object; otherwise, it will be excluded.
160 static mrb_value
161 range_initialize(mrb_state *mrb, mrb_value range)
163 mrb_value beg, end;
164 mrb_bool exclusive = FALSE;
166 mrb_get_args(mrb, "oo|b", &beg, &end, &exclusive);
167 range_ptr_replace(mrb, mrb_range_raw_ptr(range), beg, end, exclusive);
168 mrb_obj_freeze(mrb, range);
169 return range;
173 * call-seq:
174 * range == obj => true or false
176 * Returns <code>true</code> only if
177 * 1) <i>obj</i> is a Range,
178 * 2) <i>obj</i> has equivalent beginning and end items (by comparing them with <code>==</code>),
179 * 3) <i>obj</i> has the same #exclude_end? setting as <i>rng</t>.
181 * (0..2) == (0..2) #=> true
182 * (0..2) == Range.new(0,2) #=> true
183 * (0..2) == (0...2) #=> false
185 static mrb_value
186 range_eq(mrb_state *mrb, mrb_value range)
188 struct RRange *rr;
189 struct RRange *ro;
190 mrb_value obj = mrb_get_arg1(mrb);
191 mrb_bool v1, v2;
193 if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value();
194 if (!mrb_obj_is_instance_of(mrb, obj, mrb_obj_class(mrb, range))) { /* same class? */
195 return mrb_false_value();
198 rr = mrb_range_ptr(mrb, range);
199 ro = mrb_range_ptr(mrb, obj);
200 v1 = mrb_equal(mrb, RANGE_BEG(rr), RANGE_BEG(ro));
201 v2 = mrb_equal(mrb, RANGE_END(rr), RANGE_END(ro));
202 if (!v1 || !v2 || RANGE_EXCL(rr) != RANGE_EXCL(ro)) {
203 return mrb_false_value();
205 return mrb_true_value();
209 * call-seq:
210 * range === obj => true or false
211 * range.member?(val) => true or false
212 * range.include?(val) => true or false
214 static mrb_value
215 range_include(mrb_state *mrb, mrb_value range)
217 mrb_value val = mrb_get_arg1(mrb);
218 struct RRange *r = mrb_range_ptr(mrb, range);
219 mrb_value beg, end;
221 beg = RANGE_BEG(r);
222 end = RANGE_END(r);
223 if (mrb_nil_p(beg)) {
224 if (RANGE_EXCL(r) ? r_gt(mrb, end, val) /* end > val */
225 : r_ge(mrb, end, val)) { /* end >= val */
226 return mrb_true_value();
229 else if (r_le(mrb, beg, val)) { /* beg <= val */
230 if (mrb_nil_p(end)) {
231 return mrb_true_value();
233 if (RANGE_EXCL(r) ? r_gt(mrb, end, val) /* end > val */
234 : r_ge(mrb, end, val)) { /* end >= val */
235 return mrb_true_value();
238 return mrb_false_value();
241 /* 15.2.14.4.12(x) */
243 * call-seq:
244 * rng.to_s -> string
246 * Convert this range object to a printable form.
248 static mrb_value
249 range_to_s(mrb_state *mrb, mrb_value range)
251 mrb_value str, str2;
252 struct RRange *r = mrb_range_ptr(mrb, range);
254 str = mrb_obj_as_string(mrb, RANGE_BEG(r));
255 str2 = mrb_obj_as_string(mrb, RANGE_END(r));
256 str = mrb_str_dup(mrb, str);
257 mrb_str_cat(mrb, str, "...", RANGE_EXCL(r) ? 3 : 2);
258 mrb_str_cat_str(mrb, str, str2);
260 return str;
263 /* 15.2.14.4.13(x) */
265 * call-seq:
266 * rng.inspect -> string
268 * Convert this range object to a printable form (using
269 * <code>inspect</code> to convert the start and end
270 * objects).
272 static mrb_value
273 range_inspect(mrb_state *mrb, mrb_value range)
275 mrb_value str;
276 struct RRange *r = mrb_range_ptr(mrb, range);
278 if (!mrb_nil_p(RANGE_BEG(r))) {
279 str = mrb_inspect(mrb, RANGE_BEG(r));
280 str = mrb_str_dup(mrb, str);
281 mrb_str_cat(mrb, str, "...", RANGE_EXCL(r) ? 3 : 2);
283 else {
284 str = mrb_str_new(mrb, "...", RANGE_EXCL(r) ? 3 : 2);
286 if (!mrb_nil_p(RANGE_END(r))) {
287 mrb_value str2 = mrb_inspect(mrb, RANGE_END(r));
288 mrb_str_cat_str(mrb, str, str2);
291 return str;
294 /* 15.2.14.4.14(x) */
296 * call-seq:
297 * rng.eql?(obj) -> true or false
299 * Returns <code>true</code> only if <i>obj</i> is a Range, has equivalent
300 * beginning and end items (by comparing them with #eql?), and has the same
301 * #exclude_end? setting as <i>rng</i>.
303 * (0..2).eql?(0..2) #=> true
304 * (0..2).eql?(Range.new(0,2)) #=> true
305 * (0..2).eql?(0...2) #=> false
307 static mrb_value
308 range_eql(mrb_state *mrb, mrb_value range)
310 mrb_value obj = mrb_get_arg1(mrb);
311 struct RRange *r, *o;
313 if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value();
314 if (!mrb_range_p(obj)) return mrb_false_value();
316 r = mrb_range_ptr(mrb, range);
317 o = mrb_range_ptr(mrb, obj);
318 if (!mrb_eql(mrb, RANGE_BEG(r), RANGE_BEG(o)) ||
319 !mrb_eql(mrb, RANGE_END(r), RANGE_END(o)) ||
320 (RANGE_EXCL(r) != RANGE_EXCL(o))) {
321 return mrb_false_value();
323 return mrb_true_value();
326 /* 15.2.14.4.15(x) */
327 static mrb_value
328 range_initialize_copy(mrb_state *mrb, mrb_value copy)
330 mrb_value src = mrb_get_arg1(mrb);
331 struct RRange *r;
333 if (mrb_obj_equal(mrb, copy, src)) return copy;
334 if (!mrb_obj_is_instance_of(mrb, src, mrb_obj_class(mrb, copy))) {
335 mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
338 r = mrb_range_ptr(mrb, src);
339 range_ptr_replace(mrb, mrb_range_raw_ptr(copy), RANGE_BEG(r), RANGE_END(r), RANGE_EXCL(r));
340 mrb_obj_freeze(mrb, copy);
342 return copy;
345 static mrb_value
346 range_num_to_a(mrb_state *mrb, mrb_value range)
348 struct RRange *r = mrb_range_ptr(mrb, range);
349 mrb_value beg = RANGE_BEG(r);
350 mrb_value end = RANGE_END(r);
351 mrb_value ary;
353 mrb->c->ci->mid = 0;
354 if (mrb_nil_p(end)) {
355 mrb_raise(mrb, E_RANGE_ERROR, "cannot convert endless range to an array");
357 if (mrb_integer_p(beg)) {
358 if (mrb_integer_p(end)) {
359 mrb_int a = mrb_integer(beg);
360 mrb_int b = mrb_integer(end);
362 if (a > b) {
363 return mrb_ary_new_capa(mrb, 0);
365 mrb_int len;
367 if (mrb_int_sub_overflow(b, a, &len)) {
368 too_long:
369 mrb_raise(mrb, E_RANGE_ERROR, "integer range too long");
371 if (!RANGE_EXCL(r)) {
372 if (len == MRB_INT_MAX) goto too_long;
373 len++;
375 ary = mrb_ary_new_capa(mrb, len);
376 mrb_value *ptr = RARRAY_PTR(ary);
377 for (mrb_int i=0; i<len; i++) {
378 ptr[i] = mrb_int_value(mrb, a+i);
379 ARY_SET_LEN(RARRAY(ary), i+1);
381 return ary;
383 #ifndef MRB_NO_FLOAT
384 if (mrb_float_p(end)) {
385 mrb_float a = (mrb_float)mrb_integer(beg);
386 mrb_float b = mrb_float(end);
388 if (a > b) {
389 return mrb_ary_new_capa(mrb, 0);
391 ary = mrb_ary_new_capa(mrb, (mrb_int)(b - a) + 1);
392 mrb_value *ptr = RARRAY_PTR(ary);
393 mrb_int i = 0;
394 if (RANGE_EXCL(r)) {
395 while (a < b) {
396 ptr[i++] = mrb_int_value(mrb, (mrb_int)a);
397 ARY_SET_LEN(RARRAY(ary), i);
398 a += 1.0;
401 else {
402 while (a <= b) {
403 ptr[i++] = mrb_int_value(mrb, (mrb_int)a);
404 ARY_SET_LEN(RARRAY(ary), i);
405 a += 1.0;
408 return ary;
410 #endif
412 return mrb_nil_value();
415 mrb_value
416 mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, const mrb_value *argv, mrb_value (*func)(mrb_state*, mrb_value, mrb_int))
418 mrb_int i, j, beg, len;
419 mrb_value result;
420 result = mrb_ary_new(mrb);
422 for (i = 0; i < argc; i++) {
423 if (mrb_integer_p(argv[i])) {
424 mrb_ary_push(mrb, result, func(mrb, obj, mrb_integer(argv[i])));
426 else if (mrb_range_beg_len(mrb, argv[i], &beg, &len, olen, FALSE) == MRB_RANGE_OK) {
427 mrb_int const end = olen < beg + len ? olen : beg + len;
428 for (j = beg; j < end; j++) {
429 mrb_ary_push(mrb, result, func(mrb, obj, j));
432 for (; j < beg + len; j++) {
433 mrb_ary_push(mrb, result, mrb_nil_value());
436 else {
437 mrb_raisef(mrb, E_TYPE_ERROR, "invalid values selector: %v", argv[i]);
441 return result;
444 size_t
445 mrb_gc_mark_range(mrb_state *mrb, struct RRange *r)
447 if (!RANGE_INITIALIZED_P(r)) return 0;
448 mrb_gc_mark_value(mrb, RANGE_BEG(r));
449 mrb_gc_mark_value(mrb, RANGE_END(r));
450 return 2;
453 MRB_API struct RRange*
454 mrb_range_ptr(mrb_state *mrb, mrb_value range)
456 struct RRange *r = mrb_range_raw_ptr(range);
458 /* check for if #initialize_copy was removed [#3320] */
459 if (!RANGE_INITIALIZED_P(r)) {
460 mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized range");
462 return r;
465 MRB_API mrb_value
466 mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl)
468 struct RRange *r = range_ptr_init(mrb, NULL, beg, end, excl);
469 return mrb_range_value(r);
472 MRB_API enum mrb_range_beg_len
473 mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc)
475 mrb_int beg, end;
476 mrb_bool excl;
477 struct RRange *r;
479 if (!mrb_range_p(range)) return MRB_RANGE_TYPE_MISMATCH;
480 r = mrb_range_ptr(mrb, range);
482 beg = mrb_nil_p(RANGE_BEG(r)) ? 0 : mrb_as_int(mrb, RANGE_BEG(r));
483 end = mrb_nil_p(RANGE_END(r)) ? -1 : mrb_as_int(mrb, RANGE_END(r));
484 excl = mrb_nil_p(RANGE_END(r)) ? 0 : RANGE_EXCL(r);
486 if (beg < 0) {
487 beg += len;
488 if (beg < 0) return MRB_RANGE_OUT;
491 if (trunc) {
492 if (beg > len) return MRB_RANGE_OUT;
493 if (end > len) end = len;
496 if (end < 0) end += len;
497 if (!excl && (!trunc || end < len)) end++; /* include end point */
498 len = end - beg;
499 if (len < 0) len = 0;
501 *begp = beg;
502 *lenp = len;
503 return MRB_RANGE_OK;
506 void
507 mrb_init_range(mrb_state *mrb)
509 struct RClass *r;
511 r = mrb_define_class_id(mrb, MRB_SYM(Range), mrb->object_class); /* 15.2.14 */
512 mrb->range_class = r;
513 MRB_SET_INSTANCE_TT(r, MRB_TT_RANGE);
515 mrb_define_method_id(mrb, r, MRB_SYM(begin), range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.3 */
516 mrb_define_method_id(mrb, r, MRB_SYM(end), range_end, MRB_ARGS_NONE()); /* 15.2.14.4.5 */
517 mrb_define_method_id(mrb, r, MRB_OPSYM(eq), range_eq, MRB_ARGS_REQ(1)); /* 15.2.14.4.1 */
518 mrb_define_method_id(mrb, r, MRB_OPSYM(eqq), range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.2 */
519 mrb_define_method_id(mrb, r, MRB_SYM_Q(exclude_end), range_excl, MRB_ARGS_NONE()); /* 15.2.14.4.6 */
520 mrb_define_method_id(mrb, r, MRB_SYM(first), range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.7 */
521 mrb_define_method_id(mrb, r, MRB_SYM_Q(include), range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.8 */
522 mrb_define_method_id(mrb, r, MRB_SYM(initialize), range_initialize, MRB_ARGS_ANY()); /* 15.2.14.4.9 */
523 mrb_define_method_id(mrb, r, MRB_SYM(last), range_end, MRB_ARGS_NONE()); /* 15.2.14.4.10 */
524 mrb_define_method_id(mrb, r, MRB_SYM_Q(member), range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.11 */
525 mrb_define_method_id(mrb, r, MRB_SYM(to_s), range_to_s, MRB_ARGS_NONE()); /* 15.2.14.4.12(x) */
526 mrb_define_method_id(mrb, r, MRB_SYM(inspect), range_inspect, MRB_ARGS_NONE()); /* 15.2.14.4.13(x) */
527 mrb_define_method_id(mrb, r, MRB_SYM_Q(eql), range_eql, MRB_ARGS_REQ(1)); /* 15.2.14.4.14(x) */
528 mrb_define_method_id(mrb, r, MRB_SYM(initialize_copy), range_initialize_copy, MRB_ARGS_REQ(1)); /* 15.2.14.4.15(x) */
529 mrb_define_method_id(mrb, r, MRB_SYM(__num_to_a), range_num_to_a, MRB_ARGS_NONE());