source: EcnlProtoTool/trunk/mruby-2.1.1/src/range.c@ 439

Last change on this file since 439 was 439, checked in by coas-nagasima, 4 years ago

mrubyを2.1.1に更新

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-csrc;charset=UTF-8
File size: 11.7 KB
Line 
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
17static void
18r_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
41static mrb_bool
42r_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
50static mrb_bool
51r_gt(mrb_state *mrb, mrb_value a, mrb_value b)
52{
53 return mrb_cmp(mrb, a, b) == 1;
54}
55
56static mrb_bool
57r_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
65static void
66range_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
73static struct RRange *
74range_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
100static void
101range_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 */
114static mrb_value
115range_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 */
130static mrb_value
131range_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 */
142static mrb_value
143range_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 */
156static mrb_value
157range_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 */
180static mrb_value
181range_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 */
211static mrb_value
212range_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 */
237static mrb_value
238range_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 */
261static mrb_value
262range_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 */
289static mrb_value
290range_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) */
312static mrb_value
313range_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
331mrb_value
332mrb_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
360void
361mrb_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
369MRB_API struct RRange*
370mrb_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
381MRB_API mrb_value
382mrb_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
388MRB_API enum mrb_range_beg_len
389mrb_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
420void
421mrb_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}
Note: See TracBrowser for help on using the repository browser.