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>
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)
20 r_check(mrb_state
*mrb
, mrb_value a
, mrb_value b
)
29 if (ta
== MRB_TT_INTEGER
&& tb
== MRB_TT_INTEGER
) return;
31 if ((ta
== MRB_TT_INTEGER
|| ta
== MRB_TT_FLOAT
) &&
32 (tb
== MRB_TT_INTEGER
|| tb
== MRB_TT_FLOAT
)) {
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");
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
;
55 r_gt(mrb_state
*mrb
, mrb_value a
, mrb_value b
)
57 return mrb_cmp(mrb
, a
, b
) == 1;
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
;
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
));
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
);
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");
88 range_ptr_alloc_edges(mrb
, r
);
92 r
= MRB_OBJ_ALLOC(mrb
, MRB_TT_RANGE
, mrb
->range_class
);
93 range_ptr_alloc_edges(mrb
, r
);
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
);
116 * Returns the first object in <i>rng</i>.
119 range_beg(mrb_state
*mrb
, mrb_value range
)
121 return mrb_range_beg(mrb
, range
);
129 * Returns the object that defines the end of <i>rng</i>.
132 * (1...10).end #=> 10
135 range_end(mrb_state
*mrb
, mrb_value range
)
137 return mrb_range_end(mrb
, range
);
142 * range.exclude_end? => true or false
144 * Returns <code>true</code> if <i>range</i> excludes its end value.
147 range_excl(mrb_state
*mrb
, mrb_value range
)
149 return mrb_bool_value(mrb_range_excl_p(mrb
, range
));
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.
161 range_initialize(mrb_state
*mrb
, mrb_value range
)
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
);
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
186 range_eq(mrb_state
*mrb
, mrb_value range
)
190 mrb_value obj
= mrb_get_arg1(mrb
);
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();
210 * range === obj => true or false
211 * range.member?(val) => true or false
212 * range.include?(val) => true or false
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
);
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) */
246 * Convert this range object to a printable form.
249 range_to_s(mrb_state
*mrb
, mrb_value range
)
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
);
263 /* 15.2.14.4.13(x) */
266 * rng.inspect -> string
268 * Convert this range object to a printable form (using
269 * <code>inspect</code> to convert the start and end
273 range_inspect(mrb_state
*mrb
, mrb_value range
)
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);
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
);
294 /* 15.2.14.4.14(x) */
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
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) */
328 range_initialize_copy(mrb_state
*mrb
, mrb_value copy
)
330 mrb_value src
= mrb_get_arg1(mrb
);
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
);
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
);
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
);
363 return mrb_ary_new_capa(mrb
, 0);
367 if (mrb_int_sub_overflow(b
, a
, &len
)) {
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
;
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);
384 if (mrb_float_p(end
)) {
385 mrb_float a
= (mrb_float
)mrb_integer(beg
);
386 mrb_float b
= mrb_float(end
);
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
);
396 ptr
[i
++] = mrb_int_value(mrb
, (mrb_int
)a
);
397 ARY_SET_LEN(RARRAY(ary
), i
);
403 ptr
[i
++] = mrb_int_value(mrb
, (mrb_int
)a
);
404 ARY_SET_LEN(RARRAY(ary
), i
);
412 return mrb_nil_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
;
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());
437 mrb_raisef(mrb
, E_TYPE_ERROR
, "invalid values selector: %v", argv
[i
]);
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
));
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");
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
)
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
);
488 if (beg
< 0) return MRB_RANGE_OUT
;
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 */
499 if (len
< 0) len
= 0;
507 mrb_init_range(mrb_state
*mrb
)
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());