2 ** range.c - Range class
4 ** See Copyright Notice in mruby.h
8 #include <mruby/class.h>
9 #include <mruby/range.h>
10 #include <mruby/string.h>
11 #include <mruby/array.h>
13 #define RANGE_INITIALIZED_MASK 1
14 #define RANGE_INITIALIZED(p) ((p)->flags |= RANGE_INITIALIZED_MASK)
15 #define RANGE_INITIALIZED_P(p) ((p)->flags & RANGE_INITIALIZED_MASK)
18 r_check(mrb_state *mrb, mrb_value a, mrb_value b)
26 #ifdef MRB_WITHOUT_FLOAT
27 if (ta == MRB_TT_FIXNUM && tb == MRB_TT_FIXNUM ) {
29 if ((ta == MRB_TT_FIXNUM || ta == MRB_TT_FLOAT) &&
30 (tb == MRB_TT_FIXNUM || tb == MRB_TT_FLOAT)) {
35 ans = mrb_funcall(mrb, a, "<=>", 1, b);
37 /* can not be compared */
38 mrb_raise(mrb, E_ARGUMENT_ERROR, "bad value for range");
43 r_le(mrb_state *mrb, mrb_value a, mrb_value b)
45 mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */
46 /* output :a < b => -1, a = b => 0, a > b => +1 */
48 if (mrb_fixnum_p(r)) {
49 mrb_int c = mrb_fixnum(r);
50 if (c == 0 || c == -1) return TRUE;
57 r_gt(mrb_state *mrb, mrb_value a, mrb_value b)
59 mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b);
60 /* output :a < b => -1, a = b => 0, a > b => +1 */
62 return mrb_fixnum_p(r) && mrb_fixnum(r) == 1;
66 r_ge(mrb_state *mrb, mrb_value a, mrb_value b)
68 mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */
69 /* output :a < b => -1, a = b => 0, a > b => +1 */
71 if (mrb_fixnum_p(r)) {
72 mrb_int c = mrb_fixnum(r);
73 if (c == 0 || c == 1) return TRUE;
80 range_ptr_alloc_edges(mrb_state *mrb, struct RRange *r)
82 #ifndef MRB_RANGE_EMBED
83 r->edges = (mrb_range_edges *)mrb_malloc(mrb, sizeof(mrb_range_edges));
87 static struct RRange *
88 range_ptr_init(mrb_state *mrb, struct RRange *r, mrb_value beg, mrb_value end, mrb_bool excl)
90 r_check(mrb, beg, end);
93 if (RANGE_INITIALIZED_P(r)) {
94 /* Ranges are immutable, so that they should be initialized only once. */
95 mrb_name_error(mrb, mrb_intern_lit(mrb, "initialize"), "`initialize' called twice");
98 range_ptr_alloc_edges(mrb, r);
102 r = (struct RRange*)mrb_obj_alloc(mrb, MRB_TT_RANGE, mrb->range_class);
103 range_ptr_alloc_edges(mrb, r);
108 RANGE_EXCL(r) = excl;
109 RANGE_INITIALIZED(r);
115 range_ptr_replace(mrb_state *mrb, struct RRange *r, mrb_value beg, mrb_value end, mrb_bool excl)
117 range_ptr_init(mrb, r, beg, end, excl);
118 mrb_write_barrier(mrb, (struct RBasic*)r);
126 * Returns the first object in <i>rng</i>.
129 range_beg(mrb_state *mrb, mrb_value range)
131 return mrb_range_beg(mrb, range);
139 * Returns the object that defines the end of <i>rng</i>.
142 * (1...10).end #=> 10
145 range_end(mrb_state *mrb, mrb_value range)
147 return mrb_range_end(mrb, range);
152 * range.exclude_end? => true or false
154 * Returns <code>true</code> if <i>range</i> excludes its end value.
157 range_excl(mrb_state *mrb, mrb_value range)
159 return mrb_bool_value(mrb_range_excl_p(mrb, range));
164 * Range.new(start, end, exclusive=false) => range
166 * Constructs a range using the given <i>start</i> and <i>end</i>. If the third
167 * parameter is omitted or is <code>false</code>, the <i>range</i> will include
168 * the end object; otherwise, it will be excluded.
171 range_initialize(mrb_state *mrb, mrb_value range)
174 mrb_bool exclusive = FALSE;
176 mrb_get_args(mrb, "oo|b", &beg, &end, &exclusive);
177 range_ptr_replace(mrb, mrb_range_raw_ptr(range), beg, end, exclusive);
183 * range == obj => true or false
185 * Returns <code>true</code> only if
186 * 1) <i>obj</i> is a Range,
187 * 2) <i>obj</i> has equivalent beginning and end items (by comparing them with <code>==</code>),
188 * 3) <i>obj</i> has the same #exclude_end? setting as <i>rng</t>.
190 * (0..2) == (0..2) #=> true
191 * (0..2) == Range.new(0,2) #=> true
192 * (0..2) == (0...2) #=> false
195 range_eq(mrb_state *mrb, mrb_value range)
199 mrb_value obj, v1, v2;
201 mrb_get_args(mrb, "o", &obj);
203 if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value();
204 if (!mrb_obj_is_instance_of(mrb, obj, mrb_obj_class(mrb, range))) { /* same class? */
205 return mrb_false_value();
208 rr = mrb_range_ptr(mrb, range);
209 ro = mrb_range_ptr(mrb, obj);
210 v1 = mrb_funcall(mrb, RANGE_BEG(rr), "==", 1, RANGE_BEG(ro));
211 v2 = mrb_funcall(mrb, RANGE_END(rr), "==", 1, RANGE_END(ro));
212 if (!mrb_bool(v1) || !mrb_bool(v2) || RANGE_EXCL(rr) != RANGE_EXCL(ro)) {
213 return mrb_false_value();
215 return mrb_true_value();
220 * range === obj => true or false
221 * range.member?(val) => true or false
222 * range.include?(val) => true or false
225 range_include(mrb_state *mrb, mrb_value range)
228 struct RRange *r = mrb_range_ptr(mrb, range);
232 mrb_get_args(mrb, "o", &val);
236 include_p = r_le(mrb, beg, val) && /* beg <= val */
237 (RANGE_EXCL(r) ? r_gt(mrb, end, val) /* end > val */
238 : r_ge(mrb, end, val)); /* end >= val */
240 return mrb_bool_value(include_p);
243 /* 15.2.14.4.12(x) */
248 * Convert this range object to a printable form.
251 range_to_s(mrb_state *mrb, mrb_value range)
254 struct RRange *r = mrb_range_ptr(mrb, range);
256 str = mrb_obj_as_string(mrb, RANGE_BEG(r));
257 str2 = mrb_obj_as_string(mrb, RANGE_END(r));
258 str = mrb_str_dup(mrb, str);
259 mrb_str_cat(mrb, str, "...", RANGE_EXCL(r) ? 3 : 2);
260 mrb_str_cat_str(mrb, str, str2);
265 /* 15.2.14.4.13(x) */
268 * rng.inspect -> string
270 * Convert this range object to a printable form (using
271 * <code>inspect</code> to convert the start and end
275 range_inspect(mrb_state *mrb, mrb_value range)
278 struct RRange *r = mrb_range_ptr(mrb, range);
280 str = mrb_inspect(mrb, RANGE_BEG(r));
281 str2 = mrb_inspect(mrb, RANGE_END(r));
282 str = mrb_str_dup(mrb, str);
283 mrb_str_cat(mrb, str, "...", RANGE_EXCL(r) ? 3 : 2);
284 mrb_str_cat_str(mrb, str, str2);
289 /* 15.2.14.4.14(x) */
292 * rng.eql?(obj) -> true or false
294 * Returns <code>true</code> only if <i>obj</i> is a Range, has equivalent
295 * beginning and end items (by comparing them with #eql?), and has the same
296 * #exclude_end? setting as <i>rng</i>.
298 * (0..2).eql?(0..2) #=> true
299 * (0..2).eql?(Range.new(0,2)) #=> true
300 * (0..2).eql?(0...2) #=> false
303 range_eql(mrb_state *mrb, mrb_value range)
306 struct RRange *r, *o;
308 mrb_get_args(mrb, "o", &obj);
310 if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value();
311 if (!mrb_obj_is_kind_of(mrb, obj, mrb->range_class)) return mrb_false_value();
312 if (mrb_type(obj) != MRB_TT_RANGE) return mrb_false_value();
314 r = mrb_range_ptr(mrb, range);
315 o = mrb_range_ptr(mrb, obj);
316 if (!mrb_eql(mrb, RANGE_BEG(r), RANGE_BEG(o)) ||
317 !mrb_eql(mrb, RANGE_END(r), RANGE_END(o)) ||
318 (RANGE_EXCL(r) != RANGE_EXCL(o))) {
319 return mrb_false_value();
321 return mrb_true_value();
324 /* 15.2.14.4.15(x) */
326 range_initialize_copy(mrb_state *mrb, mrb_value copy)
331 mrb_get_args(mrb, "o", &src);
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));
345 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))
347 mrb_int i, j, beg, len;
349 result = mrb_ary_new(mrb);
351 for (i = 0; i < argc; ++i) {
352 if (mrb_fixnum_p(argv[i])) {
353 mrb_ary_push(mrb, result, func(mrb, obj, mrb_fixnum(argv[i])));
355 else if (mrb_range_beg_len(mrb, argv[i], &beg, &len, olen, FALSE) == 1) {
356 mrb_int const end = olen < beg + len ? olen : beg + len;
357 for (j = beg; j < end; ++j) {
358 mrb_ary_push(mrb, result, func(mrb, obj, j));
361 for (; j < beg + len; ++j) {
362 mrb_ary_push(mrb, result, mrb_nil_value());
366 mrb_raisef(mrb, E_TYPE_ERROR, "invalid values selector: %S", argv[i]);
374 mrb_gc_mark_range(mrb_state *mrb, struct RRange *r)
376 if (RANGE_INITIALIZED_P(r)) {
377 mrb_gc_mark_value(mrb, RANGE_BEG(r));
378 mrb_gc_mark_value(mrb, RANGE_END(r));
382 MRB_API struct RRange*
383 mrb_range_ptr(mrb_state *mrb, mrb_value range)
385 struct RRange *r = mrb_range_raw_ptr(range);
387 /* check for if #initialize_copy was removed [#3320] */
388 if (!RANGE_INITIALIZED_P(r)) {
389 mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized range");
395 mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl)
397 struct RRange *r = range_ptr_init(mrb, NULL, beg, end, excl);
398 return mrb_range_value(r);
402 mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc)
407 if (mrb_type(range) != MRB_TT_RANGE) return 0;
408 r = mrb_range_ptr(mrb, range);
410 beg = mrb_int(mrb, RANGE_BEG(r));
411 end = mrb_int(mrb, RANGE_END(r));
415 if (beg < 0) return 2;
419 if (beg > len) return 2;
420 if (end > len) end = len;
423 if (end < 0) end += len;
424 if (!RANGE_EXCL(r) && (!trunc || end < len)) end++; /* include end point */
426 if (len < 0) len = 0;
434 mrb_init_range(mrb_state *mrb)
438 r = mrb_define_class(mrb, "Range", mrb->object_class); /* 15.2.14 */
439 mrb->range_class = r;
440 MRB_SET_INSTANCE_TT(r, MRB_TT_RANGE);
442 mrb_define_method(mrb, r, "begin", range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.3 */
443 mrb_define_method(mrb, r, "end", range_end, MRB_ARGS_NONE()); /* 15.2.14.4.5 */
444 mrb_define_method(mrb, r, "==", range_eq, MRB_ARGS_REQ(1)); /* 15.2.14.4.1 */
445 mrb_define_method(mrb, r, "===", range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.2 */
446 mrb_define_method(mrb, r, "exclude_end?", range_excl, MRB_ARGS_NONE()); /* 15.2.14.4.6 */
447 mrb_define_method(mrb, r, "first", range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.7 */
448 mrb_define_method(mrb, r, "include?", range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.8 */
449 mrb_define_method(mrb, r, "initialize", range_initialize, MRB_ARGS_ANY()); /* 15.2.14.4.9 */
450 mrb_define_method(mrb, r, "last", range_end, MRB_ARGS_NONE()); /* 15.2.14.4.10 */
451 mrb_define_method(mrb, r, "member?", range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.11 */
452 mrb_define_method(mrb, r, "to_s", range_to_s, MRB_ARGS_NONE()); /* 15.2.14.4.12(x) */
453 mrb_define_method(mrb, r, "inspect", range_inspect, MRB_ARGS_NONE()); /* 15.2.14.4.13(x) */
454 mrb_define_method(mrb, r, "eql?", range_eql, MRB_ARGS_REQ(1)); /* 15.2.14.4.14(x) */
455 mrb_define_method(mrb, r, "initialize_copy", range_initialize_copy, MRB_ARGS_REQ(1)); /* 15.2.14.4.15(x) */