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 | #include <mruby/array.h>
|
---|
12 |
|
---|
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)
|
---|
16 |
|
---|
17 | static void
|
---|
18 | r_check(mrb_state *mrb, mrb_value a, mrb_value b)
|
---|
19 | {
|
---|
20 | enum mrb_vtype ta;
|
---|
21 | enum mrb_vtype tb;
|
---|
22 | mrb_int n;
|
---|
23 |
|
---|
24 | ta = mrb_type(a);
|
---|
25 | tb = mrb_type(b);
|
---|
26 | #ifdef MRB_WITHOUT_FLOAT
|
---|
27 | if (ta == MRB_TT_FIXNUM && tb == MRB_TT_FIXNUM ) {
|
---|
28 | #else
|
---|
29 | if ((ta == MRB_TT_FIXNUM || ta == MRB_TT_FLOAT) &&
|
---|
30 | (tb == MRB_TT_FIXNUM || tb == MRB_TT_FLOAT)) {
|
---|
31 | #endif
|
---|
32 | return;
|
---|
33 | }
|
---|
34 |
|
---|
35 | n = mrb_cmp(mrb, a, b);
|
---|
36 | if (n == -2) { /* can not be compared */
|
---|
37 | mrb_raise(mrb, E_ARGUMENT_ERROR, "bad value for range");
|
---|
38 | }
|
---|
39 | }
|
---|
40 |
|
---|
41 | static mrb_bool
|
---|
42 | r_le(mrb_state *mrb, mrb_value a, mrb_value b)
|
---|
43 | {
|
---|
44 | mrb_int n = mrb_cmp(mrb, a, b);
|
---|
45 |
|
---|
46 | if (n == 0 || n == -1) return TRUE;
|
---|
47 | return FALSE;
|
---|
48 | }
|
---|
49 |
|
---|
50 | static mrb_bool
|
---|
51 | r_gt(mrb_state *mrb, mrb_value a, mrb_value b)
|
---|
52 | {
|
---|
53 | return mrb_cmp(mrb, a, b) == 1;
|
---|
54 | }
|
---|
55 |
|
---|
56 | static mrb_bool
|
---|
57 | r_ge(mrb_state *mrb, mrb_value a, mrb_value b)
|
---|
58 | {
|
---|
59 | mrb_int n = mrb_cmp(mrb, a, b);
|
---|
60 |
|
---|
61 | if (n == 0 || n == 1) return TRUE;
|
---|
62 | return FALSE;
|
---|
63 | }
|
---|
64 |
|
---|
65 | static void
|
---|
66 | range_ptr_alloc_edges(mrb_state *mrb, struct RRange *r)
|
---|
67 | {
|
---|
68 | #ifndef MRB_RANGE_EMBED
|
---|
69 | r->edges = (mrb_range_edges *)mrb_malloc(mrb, sizeof(mrb_range_edges));
|
---|
70 | #endif
|
---|
71 | }
|
---|
72 |
|
---|
73 | static struct RRange *
|
---|
74 | range_ptr_init(mrb_state *mrb, struct RRange *r, mrb_value beg, mrb_value end, mrb_bool excl)
|
---|
75 | {
|
---|
76 | r_check(mrb, beg, end);
|
---|
77 |
|
---|
78 | if (r) {
|
---|
79 | if (RANGE_INITIALIZED_P(r)) {
|
---|
80 | /* Ranges are immutable, so that they should be initialized only once. */
|
---|
81 | mrb_name_error(mrb, mrb_intern_lit(mrb, "initialize"), "'initialize' called twice");
|
---|
82 | }
|
---|
83 | else {
|
---|
84 | range_ptr_alloc_edges(mrb, r);
|
---|
85 | }
|
---|
86 | }
|
---|
87 | else {
|
---|
88 | r = (struct RRange*)mrb_obj_alloc(mrb, MRB_TT_RANGE, mrb->range_class);
|
---|
89 | range_ptr_alloc_edges(mrb, r);
|
---|
90 | }
|
---|
91 |
|
---|
92 | RANGE_BEG(r) = beg;
|
---|
93 | RANGE_END(r) = end;
|
---|
94 | RANGE_EXCL(r) = excl;
|
---|
95 | RANGE_INITIALIZED(r);
|
---|
96 |
|
---|
97 | return r;
|
---|
98 | }
|
---|
99 |
|
---|
100 | static void
|
---|
101 | range_ptr_replace(mrb_state *mrb, struct RRange *r, mrb_value beg, mrb_value end, mrb_bool excl)
|
---|
102 | {
|
---|
103 | range_ptr_init(mrb, r, beg, end, excl);
|
---|
104 | mrb_write_barrier(mrb, (struct RBasic*)r);
|
---|
105 | }
|
---|
106 |
|
---|
107 | /*
|
---|
108 | * call-seq:
|
---|
109 | * rng.first => obj
|
---|
110 | * rng.begin => obj
|
---|
111 | *
|
---|
112 | * Returns the first object in <i>rng</i>.
|
---|
113 | */
|
---|
114 | static mrb_value
|
---|
115 | range_beg(mrb_state *mrb, mrb_value range)
|
---|
116 | {
|
---|
117 | return mrb_range_beg(mrb, range);
|
---|
118 | }
|
---|
119 |
|
---|
120 | /*
|
---|
121 | * call-seq:
|
---|
122 | * rng.end => obj
|
---|
123 | * rng.last => obj
|
---|
124 | *
|
---|
125 | * Returns the object that defines the end of <i>rng</i>.
|
---|
126 | *
|
---|
127 | * (1..10).end #=> 10
|
---|
128 | * (1...10).end #=> 10
|
---|
129 | */
|
---|
130 | static mrb_value
|
---|
131 | range_end(mrb_state *mrb, mrb_value range)
|
---|
132 | {
|
---|
133 | return mrb_range_end(mrb, range);
|
---|
134 | }
|
---|
135 |
|
---|
136 | /*
|
---|
137 | * call-seq:
|
---|
138 | * range.exclude_end? => true or false
|
---|
139 | *
|
---|
140 | * Returns <code>true</code> if <i>range</i> excludes its end value.
|
---|
141 | */
|
---|
142 | static mrb_value
|
---|
143 | range_excl(mrb_state *mrb, mrb_value range)
|
---|
144 | {
|
---|
145 | return mrb_bool_value(mrb_range_excl_p(mrb, range));
|
---|
146 | }
|
---|
147 |
|
---|
148 | /*
|
---|
149 | * call-seq:
|
---|
150 | * Range.new(start, end, exclusive=false) => range
|
---|
151 | *
|
---|
152 | * Constructs a range using the given <i>start</i> and <i>end</i>. If the third
|
---|
153 | * parameter is omitted or is <code>false</code>, the <i>range</i> will include
|
---|
154 | * the end object; otherwise, it will be excluded.
|
---|
155 | */
|
---|
156 | static mrb_value
|
---|
157 | range_initialize(mrb_state *mrb, mrb_value range)
|
---|
158 | {
|
---|
159 | mrb_value beg, end;
|
---|
160 | mrb_bool exclusive = FALSE;
|
---|
161 |
|
---|
162 | mrb_get_args(mrb, "oo|b", &beg, &end, &exclusive);
|
---|
163 | range_ptr_replace(mrb, mrb_range_raw_ptr(range), beg, end, exclusive);
|
---|
164 | return range;
|
---|
165 | }
|
---|
166 |
|
---|
167 | /*
|
---|
168 | * call-seq:
|
---|
169 | * range == obj => true or false
|
---|
170 | *
|
---|
171 | * Returns <code>true</code> only if
|
---|
172 | * 1) <i>obj</i> is a Range,
|
---|
173 | * 2) <i>obj</i> has equivalent beginning and end items (by comparing them with <code>==</code>),
|
---|
174 | * 3) <i>obj</i> has the same #exclude_end? setting as <i>rng</t>.
|
---|
175 | *
|
---|
176 | * (0..2) == (0..2) #=> true
|
---|
177 | * (0..2) == Range.new(0,2) #=> true
|
---|
178 | * (0..2) == (0...2) #=> false
|
---|
179 | */
|
---|
180 | static mrb_value
|
---|
181 | range_eq(mrb_state *mrb, mrb_value range)
|
---|
182 | {
|
---|
183 | struct RRange *rr;
|
---|
184 | struct RRange *ro;
|
---|
185 | mrb_value obj;
|
---|
186 | mrb_bool v1, v2;
|
---|
187 |
|
---|
188 | mrb_get_args(mrb, "o", &obj);
|
---|
189 |
|
---|
190 | if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value();
|
---|
191 | if (!mrb_obj_is_instance_of(mrb, obj, mrb_obj_class(mrb, range))) { /* same class? */
|
---|
192 | return mrb_false_value();
|
---|
193 | }
|
---|
194 |
|
---|
195 | rr = mrb_range_ptr(mrb, range);
|
---|
196 | ro = mrb_range_ptr(mrb, obj);
|
---|
197 | v1 = mrb_equal(mrb, RANGE_BEG(rr), RANGE_BEG(ro));
|
---|
198 | v2 = mrb_equal(mrb, RANGE_END(rr), RANGE_END(ro));
|
---|
199 | if (!v1 || !v2 || RANGE_EXCL(rr) != RANGE_EXCL(ro)) {
|
---|
200 | return mrb_false_value();
|
---|
201 | }
|
---|
202 | return mrb_true_value();
|
---|
203 | }
|
---|
204 |
|
---|
205 | /*
|
---|
206 | * call-seq:
|
---|
207 | * range === obj => true or false
|
---|
208 | * range.member?(val) => true or false
|
---|
209 | * range.include?(val) => true or false
|
---|
210 | */
|
---|
211 | static mrb_value
|
---|
212 | range_include(mrb_state *mrb, mrb_value range)
|
---|
213 | {
|
---|
214 | mrb_value val;
|
---|
215 | struct RRange *r = mrb_range_ptr(mrb, range);
|
---|
216 | mrb_value beg, end;
|
---|
217 | mrb_bool include_p;
|
---|
218 |
|
---|
219 | mrb_get_args(mrb, "o", &val);
|
---|
220 |
|
---|
221 | beg = RANGE_BEG(r);
|
---|
222 | end = RANGE_END(r);
|
---|
223 | include_p = r_le(mrb, beg, val) && /* beg <= val */
|
---|
224 | (RANGE_EXCL(r) ? r_gt(mrb, end, val) /* end > val */
|
---|
225 | : r_ge(mrb, end, val)); /* end >= val */
|
---|
226 |
|
---|
227 | return mrb_bool_value(include_p);
|
---|
228 | }
|
---|
229 |
|
---|
230 | /* 15.2.14.4.12(x) */
|
---|
231 | /*
|
---|
232 | * call-seq:
|
---|
233 | * rng.to_s -> string
|
---|
234 | *
|
---|
235 | * Convert this range object to a printable form.
|
---|
236 | */
|
---|
237 | static mrb_value
|
---|
238 | range_to_s(mrb_state *mrb, mrb_value range)
|
---|
239 | {
|
---|
240 | mrb_value str, str2;
|
---|
241 | struct RRange *r = mrb_range_ptr(mrb, range);
|
---|
242 |
|
---|
243 | str = mrb_obj_as_string(mrb, RANGE_BEG(r));
|
---|
244 | str2 = mrb_obj_as_string(mrb, RANGE_END(r));
|
---|
245 | str = mrb_str_dup(mrb, str);
|
---|
246 | mrb_str_cat(mrb, str, "...", RANGE_EXCL(r) ? 3 : 2);
|
---|
247 | mrb_str_cat_str(mrb, str, str2);
|
---|
248 |
|
---|
249 | return str;
|
---|
250 | }
|
---|
251 |
|
---|
252 | /* 15.2.14.4.13(x) */
|
---|
253 | /*
|
---|
254 | * call-seq:
|
---|
255 | * rng.inspect -> string
|
---|
256 | *
|
---|
257 | * Convert this range object to a printable form (using
|
---|
258 | * <code>inspect</code> to convert the start and end
|
---|
259 | * objects).
|
---|
260 | */
|
---|
261 | static mrb_value
|
---|
262 | range_inspect(mrb_state *mrb, mrb_value range)
|
---|
263 | {
|
---|
264 | mrb_value str, str2;
|
---|
265 | struct RRange *r = mrb_range_ptr(mrb, range);
|
---|
266 |
|
---|
267 | str = mrb_inspect(mrb, RANGE_BEG(r));
|
---|
268 | str2 = mrb_inspect(mrb, RANGE_END(r));
|
---|
269 | str = mrb_str_dup(mrb, str);
|
---|
270 | mrb_str_cat(mrb, str, "...", RANGE_EXCL(r) ? 3 : 2);
|
---|
271 | mrb_str_cat_str(mrb, str, str2);
|
---|
272 |
|
---|
273 | return str;
|
---|
274 | }
|
---|
275 |
|
---|
276 | /* 15.2.14.4.14(x) */
|
---|
277 | /*
|
---|
278 | * call-seq:
|
---|
279 | * rng.eql?(obj) -> true or false
|
---|
280 | *
|
---|
281 | * Returns <code>true</code> only if <i>obj</i> is a Range, has equivalent
|
---|
282 | * beginning and end items (by comparing them with #eql?), and has the same
|
---|
283 | * #exclude_end? setting as <i>rng</i>.
|
---|
284 | *
|
---|
285 | * (0..2).eql?(0..2) #=> true
|
---|
286 | * (0..2).eql?(Range.new(0,2)) #=> true
|
---|
287 | * (0..2).eql?(0...2) #=> false
|
---|
288 | */
|
---|
289 | static mrb_value
|
---|
290 | range_eql(mrb_state *mrb, mrb_value range)
|
---|
291 | {
|
---|
292 | mrb_value obj;
|
---|
293 | struct RRange *r, *o;
|
---|
294 |
|
---|
295 | mrb_get_args(mrb, "o", &obj);
|
---|
296 |
|
---|
297 | if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value();
|
---|
298 | if (!mrb_obj_is_kind_of(mrb, obj, mrb->range_class)) return mrb_false_value();
|
---|
299 | if (!mrb_range_p(obj)) return mrb_false_value();
|
---|
300 |
|
---|
301 | r = mrb_range_ptr(mrb, range);
|
---|
302 | o = mrb_range_ptr(mrb, obj);
|
---|
303 | if (!mrb_eql(mrb, RANGE_BEG(r), RANGE_BEG(o)) ||
|
---|
304 | !mrb_eql(mrb, RANGE_END(r), RANGE_END(o)) ||
|
---|
305 | (RANGE_EXCL(r) != RANGE_EXCL(o))) {
|
---|
306 | return mrb_false_value();
|
---|
307 | }
|
---|
308 | return mrb_true_value();
|
---|
309 | }
|
---|
310 |
|
---|
311 | /* 15.2.14.4.15(x) */
|
---|
312 | static mrb_value
|
---|
313 | range_initialize_copy(mrb_state *mrb, mrb_value copy)
|
---|
314 | {
|
---|
315 | mrb_value src;
|
---|
316 | struct RRange *r;
|
---|
317 |
|
---|
318 | mrb_get_args(mrb, "o", &src);
|
---|
319 |
|
---|
320 | if (mrb_obj_equal(mrb, copy, src)) return copy;
|
---|
321 | if (!mrb_obj_is_instance_of(mrb, src, mrb_obj_class(mrb, copy))) {
|
---|
322 | mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
|
---|
323 | }
|
---|
324 |
|
---|
325 | r = mrb_range_ptr(mrb, src);
|
---|
326 | range_ptr_replace(mrb, mrb_range_raw_ptr(copy), RANGE_BEG(r), RANGE_END(r), RANGE_EXCL(r));
|
---|
327 |
|
---|
328 | return copy;
|
---|
329 | }
|
---|
330 |
|
---|
331 | mrb_value
|
---|
332 | 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))
|
---|
333 | {
|
---|
334 | mrb_int i, j, beg, len;
|
---|
335 | mrb_value result;
|
---|
336 | result = mrb_ary_new(mrb);
|
---|
337 |
|
---|
338 | for (i = 0; i < argc; ++i) {
|
---|
339 | if (mrb_fixnum_p(argv[i])) {
|
---|
340 | mrb_ary_push(mrb, result, func(mrb, obj, mrb_fixnum(argv[i])));
|
---|
341 | }
|
---|
342 | else if (mrb_range_beg_len(mrb, argv[i], &beg, &len, olen, FALSE) == MRB_RANGE_OK) {
|
---|
343 | mrb_int const end = olen < beg + len ? olen : beg + len;
|
---|
344 | for (j = beg; j < end; ++j) {
|
---|
345 | mrb_ary_push(mrb, result, func(mrb, obj, j));
|
---|
346 | }
|
---|
347 |
|
---|
348 | for (; j < beg + len; ++j) {
|
---|
349 | mrb_ary_push(mrb, result, mrb_nil_value());
|
---|
350 | }
|
---|
351 | }
|
---|
352 | else {
|
---|
353 | mrb_raisef(mrb, E_TYPE_ERROR, "invalid values selector: %v", argv[i]);
|
---|
354 | }
|
---|
355 | }
|
---|
356 |
|
---|
357 | return result;
|
---|
358 | }
|
---|
359 |
|
---|
360 | void
|
---|
361 | mrb_gc_mark_range(mrb_state *mrb, struct RRange *r)
|
---|
362 | {
|
---|
363 | if (RANGE_INITIALIZED_P(r)) {
|
---|
364 | mrb_gc_mark_value(mrb, RANGE_BEG(r));
|
---|
365 | mrb_gc_mark_value(mrb, RANGE_END(r));
|
---|
366 | }
|
---|
367 | }
|
---|
368 |
|
---|
369 | MRB_API struct RRange*
|
---|
370 | mrb_range_ptr(mrb_state *mrb, mrb_value range)
|
---|
371 | {
|
---|
372 | struct RRange *r = mrb_range_raw_ptr(range);
|
---|
373 |
|
---|
374 | /* check for if #initialize_copy was removed [#3320] */
|
---|
375 | if (!RANGE_INITIALIZED_P(r)) {
|
---|
376 | mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized range");
|
---|
377 | }
|
---|
378 | return r;
|
---|
379 | }
|
---|
380 |
|
---|
381 | MRB_API mrb_value
|
---|
382 | mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl)
|
---|
383 | {
|
---|
384 | struct RRange *r = range_ptr_init(mrb, NULL, beg, end, excl);
|
---|
385 | return mrb_range_value(r);
|
---|
386 | }
|
---|
387 |
|
---|
388 | MRB_API enum mrb_range_beg_len
|
---|
389 | mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc)
|
---|
390 | {
|
---|
391 | mrb_int beg, end;
|
---|
392 | struct RRange *r;
|
---|
393 |
|
---|
394 | if (!mrb_range_p(range)) return MRB_RANGE_TYPE_MISMATCH;
|
---|
395 | r = mrb_range_ptr(mrb, range);
|
---|
396 |
|
---|
397 | beg = mrb_int(mrb, RANGE_BEG(r));
|
---|
398 | end = mrb_int(mrb, RANGE_END(r));
|
---|
399 |
|
---|
400 | if (beg < 0) {
|
---|
401 | beg += len;
|
---|
402 | if (beg < 0) return MRB_RANGE_OUT;
|
---|
403 | }
|
---|
404 |
|
---|
405 | if (trunc) {
|
---|
406 | if (beg > len) return MRB_RANGE_OUT;
|
---|
407 | if (end > len) end = len;
|
---|
408 | }
|
---|
409 |
|
---|
410 | if (end < 0) end += len;
|
---|
411 | if (!RANGE_EXCL(r) && (!trunc || end < len)) end++; /* include end point */
|
---|
412 | len = end - beg;
|
---|
413 | if (len < 0) len = 0;
|
---|
414 |
|
---|
415 | *begp = beg;
|
---|
416 | *lenp = len;
|
---|
417 | return MRB_RANGE_OK;
|
---|
418 | }
|
---|
419 |
|
---|
420 | void
|
---|
421 | mrb_init_range(mrb_state *mrb)
|
---|
422 | {
|
---|
423 | struct RClass *r;
|
---|
424 |
|
---|
425 | r = mrb_define_class(mrb, "Range", mrb->object_class); /* 15.2.14 */
|
---|
426 | mrb->range_class = r;
|
---|
427 | MRB_SET_INSTANCE_TT(r, MRB_TT_RANGE);
|
---|
428 |
|
---|
429 | mrb_define_method(mrb, r, "begin", range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.3 */
|
---|
430 | mrb_define_method(mrb, r, "end", range_end, MRB_ARGS_NONE()); /* 15.2.14.4.5 */
|
---|
431 | mrb_define_method(mrb, r, "==", range_eq, MRB_ARGS_REQ(1)); /* 15.2.14.4.1 */
|
---|
432 | mrb_define_method(mrb, r, "===", range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.2 */
|
---|
433 | mrb_define_method(mrb, r, "exclude_end?", range_excl, MRB_ARGS_NONE()); /* 15.2.14.4.6 */
|
---|
434 | mrb_define_method(mrb, r, "first", range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.7 */
|
---|
435 | mrb_define_method(mrb, r, "include?", range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.8 */
|
---|
436 | mrb_define_method(mrb, r, "initialize", range_initialize, MRB_ARGS_ANY()); /* 15.2.14.4.9 */
|
---|
437 | mrb_define_method(mrb, r, "last", range_end, MRB_ARGS_NONE()); /* 15.2.14.4.10 */
|
---|
438 | mrb_define_method(mrb, r, "member?", range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.11 */
|
---|
439 | mrb_define_method(mrb, r, "to_s", range_to_s, MRB_ARGS_NONE()); /* 15.2.14.4.12(x) */
|
---|
440 | mrb_define_method(mrb, r, "inspect", range_inspect, MRB_ARGS_NONE()); /* 15.2.14.4.13(x) */
|
---|
441 | mrb_define_method(mrb, r, "eql?", range_eql, MRB_ARGS_REQ(1)); /* 15.2.14.4.14(x) */
|
---|
442 | mrb_define_method(mrb, r, "initialize_copy", range_initialize_copy, MRB_ARGS_REQ(1)); /* 15.2.14.4.15(x) */
|
---|
443 | }
|
---|