Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 506 lines (435 sloc) 13.391 kB
8354436 @mimaki add file header
mimaki authored
1 /*
2 ** range.c - Range class
3 **
4 ** See Copyright Notice in mruby.h
5 */
6
e0d6430 @mimaki add mruby sources
mimaki authored
7 #include "mruby.h"
8 #include "mruby/class.h"
9 #include "mruby/range.h"
10 #include "variable.h"
11 #include "error.h"
12 #include "mruby/numeric.h"
13 #include "mruby/string.h"
14
15 #include <stdio.h>
16 #include <string.h>
17
18 #ifndef FALSE
19 #define FALSE 0
20 #endif
21
22 #ifndef TRUE
23 #define TRUE 1
24 #endif
25
26 #ifndef OTHER
27 #define OTHER 2
28 #endif
29
30 mrb_value mrb_exec_recursive_paired(mrb_state *mrb, mrb_value (*func) (mrb_state *, mrb_value, mrb_value, int),
31 mrb_value obj, mrb_value paired_obj, void* arg);
32
33 int printf (const char*, ...);
34 /*--------- <1.8.7>object.c ---------> */
35
36 /*
37 * call-seq:
38 * obj.instance_of?(class) => true or false
39 *
40 * Returns <code>true</code> if <i>obj</i> is an instance of the given
41 * class. See also <code>Object#kind_of?</code>.
42 */
43
44 int
45 mrb_obj_is_instance_of(mrb_state *mrb, mrb_value obj, struct RClass* c)
46 {
47 if (mrb_obj_class(mrb, obj) == c) return TRUE;
48 return FALSE;
49 }
50 /*--------- <1.8.7>object.c ---------< */
51
52 mrb_value
53 mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, int excl)
54 {
55 struct RRange *r;
56
57 r = mrb_obj_alloc(mrb, MRB_TT_RANGE, mrb->range_class);
58 r->edges = mrb_malloc(mrb, sizeof(struct mrb_range_edges));
59 r->edges->beg = beg;
60 r->edges->end = end;
61 r->excl = excl;
62 return mrb_range_value(r);
63 }
64
65 /*
66 * call-seq:
67 * rng.first => obj
68 * rng.begin => obj
69 *
70 * Returns the first object in <i>rng</i>.
71 */
72 mrb_value
73 mrb_range_beg(mrb_state *mrb, mrb_value range)
74 {
75 struct RRange *r = mrb_range_ptr(range);
76
77 return r->edges->beg;
78 }
79
80 /*
81 * call-seq:
82 * rng.end => obj
83 * rng.last => obj
84 *
85 * Returns the object that defines the end of <i>rng</i>.
86 *
87 * (1..10).end #=> 10
88 * (1...10).end #=> 10
89 */
90
91 mrb_value
92 mrb_range_end(mrb_state *mrb, mrb_value range)
93 {
94 struct RRange *r = mrb_range_ptr(range);
95
96 return r->edges->end;
97 }
98
99 /*
100 * call-seq:
101 * range.exclude_end? => true or false
102 *
103 * Returns <code>true</code> if <i>range</i> excludes its end value.
104 */
105 mrb_value
106 mrb_range_excl(mrb_state *mrb, mrb_value range)
107 {
108 struct RRange *r = mrb_range_ptr(range);
109
110 return r->excl ? mrb_true_value() : mrb_false_value();
111 }
112
113 /*
114 * call-seq:
115 * beg end
116 * args[0] <= args[1] => true
117 * args[0] > args[1] => false
118 */
119 static int
120 range_check(mrb_state *mrb, mrb_value *args)
121 {
122 mrb_value ans = mrb_funcall(mrb, args[0], "<=>", 1, args[1]);
123 /* beg end
124 ans :args[0] < args[1] => -1
125 args[0] = args[1] => 0
126 args[0] > args[1] => +1 */
127 if (mrb_nil_p(ans)) return FALSE;
128 //if (mrb_obj_equal(mrb, ans, mrb_fixnum_value(1))) return FALSE;
129 if (mrb_fixnum(ans) == 1) return FALSE;
130 return TRUE;
131 }
132
133 static void
134 range_init(mrb_state *mrb, mrb_value range, mrb_value beg, mrb_value end, mrb_int exclude_end)
135 {
136 mrb_value args[2];
137 struct RRange *r = mrb_range_ptr(range);
138
139 if ((mrb_type(beg) != MRB_TT_FIXNUM) || (mrb_type(end) != MRB_TT_FIXNUM)) {
140 args[0] = beg;
141 args[1] = end;
142 /* eroor.c v = mrb_rescue(range_check, (mrb_value)args, range_failed, 0);
143 if (mrb_nil_p(v)) range_failed(); */
144 if (!range_check(mrb, args)) {
145 printf("range_failed()\n");
146 }
147 }
148 r->excl = exclude_end;
149 r->edges->beg = beg;
150 r->edges->end = end;
151 }
152 /*
153 * call-seq:
154 * Range.new(start, end, exclusive=false) => range
155 *
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.
159 */
160
161 mrb_value
162 mrb_range_initialize(mrb_state *mrb, mrb_value range)
163 {
164 mrb_value beg, end;
165 mrb_value flags;
166
167 mrb_get_args(mrb, "ooo", &beg, &end, &flags);
168 /* Ranges are immutable, so that they should be initialized only once. */
169 range_init(mrb, range, beg, end, mrb_test(flags));
170 return range;
171 }
172 /*
173 * call-seq:
174 * range == obj => true or false
175 *
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>.
180 *
181 * (0..2) == (0..2) #=> true
182 * (0..2) == Range.new(0,2) #=> true
183 * (0..2) == (0...2) #=> false
184 *
185 */
186
187 mrb_value
188 mrb_range_eq(mrb_state *mrb, mrb_value range)
189 {
190 struct RRange *rr;
191 struct RRange *ro;
192 mrb_value obj;
193
194 mrb_get_args(mrb, "o", &obj);
195
196 if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value();
197
198 /* same class? */
199 // if (!rb_obj_is_instance_of(obj, rb_obj_class(range)))
200 if (!mrb_obj_is_instance_of(mrb, obj, mrb_obj_class(mrb, range)))
201 return mrb_false_value();
202
203 rr = mrb_range_ptr(range);
204 ro = mrb_range_ptr(obj);
205 if (!mrb_obj_equal(mrb, rr->edges->beg, ro->edges->beg))
206 return mrb_false_value();
207 if (!mrb_obj_equal(mrb, rr->edges->end, ro->edges->end))
208 return mrb_false_value();
209 if (rr->excl != ro->excl)
210 return mrb_false_value();
211
212 return mrb_true_value();
213 }
214
215 static int
216 r_le(mrb_state *mrb, mrb_value a, mrb_value b)
217 {
218 //int c;
219 mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */
220 /* output :a < b => -1, a = b => 0, a > b => +1 */
221
222 if (mrb_nil_p(r)) return FALSE;
223
224 /* mrb_value -> int */
225 //c = mrb_cmpint(mrb, r, a, b);
226 //if (c == 0) return TRUE;
227 //if (c < 0) return TRUE;
228 //return FALSE;
229 if (mrb_obj_equal(mrb, r, mrb_fixnum_value(0))) return TRUE;
230 if (mrb_obj_equal(mrb, r, mrb_fixnum_value(-1))) return TRUE;
231 return FALSE;
232 }
233
234 static int
235 r_gt(mrb_state *mrb, mrb_value a, mrb_value b)
236 {
237 //int c;
238 mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b);
239 /* output :a < b => -1, a = b => 0, a > b => +1 */
240
241 if (mrb_nil_p(r)) return FALSE;
242
243 /* mrb_value -> int */
244 //c = mrb_cmpint(mrb, r);
245 //if (c > 0) return TRUE;
246 //return FALSE;
247 if (mrb_obj_equal(mrb, r, mrb_fixnum_value(1))) return TRUE;
248 return FALSE;
249 }
250
251 static int
252 r_ge(mrb_state *mrb, mrb_value a, mrb_value b)
253 {
254 //int c;
255 mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */
256 /* output :a < b => -1, a = b => 0, a > b => +1 */
257
258 if (mrb_nil_p(r)) return FALSE;
259
260 /* mrb_value -> int */
261 //c = mrb_cmpint(mrb, r);
262 //if (c == 0) return TRUE;
263 //if (c > 0) return TRUE;
264 //return FALSE;
265 if (mrb_obj_equal(mrb, r, mrb_fixnum_value(0))) return TRUE;
266 if (mrb_obj_equal(mrb, r, mrb_fixnum_value(1))) return TRUE;
267 return FALSE;
268 }
269
270 /*
271 * call-seq:
272 * range === obj => true or false
273 * range.member?(val) => true or false
274 * range.include?(val) => true or false
275 *
276 */
277 mrb_value
278 mrb_range_include(mrb_state *mrb, mrb_value range)
279 {
280 mrb_value val;
281 struct RRange *r = mrb_range_ptr(range);
282 mrb_value beg, end;
283
284 mrb_get_args(mrb, "o", &val);
285
286 beg = r->edges->beg;
287 end = r->edges->end;
288 if (r_le(mrb, beg, val)) {
289 /* beg <= val */
290 if (r->excl) {
291 if (r_gt(mrb, end, val)) return mrb_true_value(); /* end > val */
292 }
293 else {
294 if (r_ge(mrb, end, val)) return mrb_true_value(); /* end >= val */
295 }
296 }
297 return mrb_false_value();
298 }
299
300 /*
301 * call-seq:
302 * rng.each {| i | block } => rng
303 *
304 * Iterates over the elements <i>rng</i>, passing each in turn to the
305 * block. You can only iterate if the start object of the range
306 * supports the +succ+ method (which means that you can't iterate over
307 * ranges of +Float+ objects).
308 *
309 * (10..15).each do |n|
310 * print n, ' '
311 * end
312 *
313 * <em>produces:</em>
314 *
315 * 10 11 12 13 14 15
316 */
317
318 mrb_value
319 mrb_range_each(mrb_state *mrb, mrb_value range)
320 {
321 return range;
322 }
323
324 mrb_int
325 mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_int err)
326 {
327 mrb_int beg, end, b, e;
328 struct RRange *r = mrb_range_ptr(range);
329
330 //if (!mrb_obj_is_kind_of(mrb, range, mrb->range_class)) return FALSE;
331 if (mrb_type(range) != MRB_TT_RANGE) return FALSE;
332
333 beg = b = mrb_fixnum(r->edges->beg);
334 end = e = mrb_fixnum(r->edges->end);
335
336 if (beg < 0) {
337 beg += len;
338 if (beg < 0) goto out_of_range;
339 }
340 if (err == 0 || err == 2) {
341 if (beg > len) goto out_of_range;
342 if (end > len) end = len;
343 }
344 if (end < 0) end += len;
345 if (!r->excl) end++; /* include end point */
346 len = end - beg;
347 if (len < 0) len = 0;
348
349 *begp = beg;
350 *lenp = len;
351 return TRUE;
352
353 out_of_range:
354 if (err) {
355 mrb_raise(mrb, E_RANGE_ERROR, "%ld..%s%ld out of range",
356 b, r->excl? "." : "", e);
357 }
358 return OTHER;
359 }
360
361 /* 15.2.14.4.12(x) */
362 /*
363 * call-seq:
364 * rng.to_s -> string
365 *
366 * Convert this range object to a printable form.
367 */
368
369 static mrb_value
370 range_to_s(mrb_state *mrb, mrb_value range)
371 {
372 mrb_value str, str2;
373 struct RRange *r = mrb_range_ptr(range);
374
375 str = mrb_obj_as_string(mrb, r->edges->beg);
376 str2 = mrb_obj_as_string(mrb, r->edges->end);
377 str = mrb_str_dup(mrb, str);
378 mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2);
379 mrb_str_append(mrb, str, str2);
380 //OBJ_INFECT(str, str2);
381
382 return str;
383 }
384
385 static mrb_value
386 inspect_range(mrb_state *mrb, mrb_value range, mrb_value dummy, int recur)
387 {
388 mrb_value str, str2;
389 struct RRange *r = mrb_range_ptr(range);
390
391 if (recur) {
392 return mrb_str_new2(mrb, r->excl ? "(... ... ...)" : "(... .. ...)");
393 }
394 str = mrb_inspect(mrb, r->edges->beg);
395 str2 = mrb_inspect(mrb, r->edges->end);
396 str = mrb_str_dup(mrb, str);
397 mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2);
398 mrb_str_append(mrb, str, str2);
399 // OBJ_INFECT(str, str2);
400
401 return str;
402 }
403
404 /* 15.2.14.4.13(x) */
405 /*
406 * call-seq:
407 * rng.inspect -> string
408 *
409 * Convert this range object to a printable form (using
410 * <code>inspect</code> to convert the start and end
411 * objects).
412 */
413
414 static mrb_value
415 range_inspect(mrb_state *mrb, mrb_value range)
416 {
417 return inspect_range(mrb, range, range, 0);
418 }
419
420 static mrb_value
421 recursive_eql(mrb_state *mrb, mrb_value range, mrb_value obj, int recur)
422 {
423 struct RRange *r = mrb_range_ptr(range);
424 struct RRange *o = mrb_range_ptr(obj);
425
426 if (recur) return mrb_true_value(); /* Subtle! */
427 if (!mrb_eql(mrb, r->edges->beg, o->edges->beg))
428 return mrb_false_value();
429 if (!mrb_eql(mrb, r->edges->end, o->edges->end))
430 return mrb_false_value();
431
432 if (r->excl != o->excl)
433 return mrb_false_value();
434 return mrb_true_value();
435 }
436
437 /* 15.2.14.4.14(x) */
438 /*
439 * call-seq:
440 * rng.eql?(obj) -> true or false
441 *
442 * Returns <code>true</code> only if <i>obj</i> is a Range, has equivalent
443 * beginning and end items (by comparing them with #eql?), and has the same
444 * #exclude_end? setting as <i>rng</i>.
445 *
446 * (0..2).eql?(0..2) #=> true
447 * (0..2).eql?(Range.new(0,2)) #=> true
448 * (0..2).eql?(0...2) #=> false
449 *
450 */
451
452 static mrb_value
453 range_eql(mrb_state *mrb, mrb_value range)
454 {
455 mrb_value obj;
456 mrb_get_args(mrb, "o", &obj);
457
458 if (mrb_obj_equal(mrb, range, obj))
459 return mrb_true_value();
460 if (!mrb_obj_is_kind_of(mrb, obj, mrb->range_class))
461 return mrb_false_value();
462 return mrb_exec_recursive_paired(mrb, recursive_eql, range, obj, &obj);
463 }
464
465 /* 15.2.14.4.15(x) */
466 mrb_value
467 range_initialize_copy(mrb_state *mrb, mrb_value copy)
468 {
469 mrb_value src;
470 mrb_get_args(mrb, "o", &src);
471
472 if (mrb_obj_equal(mrb, copy, src)) return copy;
473 //mrb_check_frozen(copy);
474 if (!mrb_obj_is_instance_of(mrb, src, mrb_obj_class(mrb, copy))) {
475 mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
476 }
477 memcpy(mrb_range_ptr(copy), mrb_range_ptr(src), sizeof(struct RRange));
478
479 return copy;
480 }
481
482 void
483 mrb_init_range(mrb_state *mrb)
484 {
485 struct RClass *r;
486 r = mrb->range_class = mrb_define_class(mrb, "Range", mrb->object_class);
487 mrb_include_module(mrb, r, mrb_class_get(mrb, "Enumerable"));
488
489 mrb_define_method(mrb, r, "begin", mrb_range_beg, ARGS_NONE()); /* 15.2.14.4.3 */
490 mrb_define_method(mrb, r, "end", mrb_range_end, ARGS_NONE()); /* 15.2.14.4.5 */
491 mrb_define_method(mrb, r, "==", mrb_range_eq, ARGS_REQ(1)); /* 15.2.14.4.1 */
492 mrb_define_method(mrb, r, "===", mrb_range_include, ARGS_REQ(1)); /* 15.2.14.4.2 */
493 mrb_define_method(mrb, r, "each", mrb_range_each, ARGS_NONE()); /* 15.2.14.4.4 */
494 mrb_define_method(mrb, r, "exclude_end?", mrb_range_excl, ARGS_NONE()); /* 15.2.14.4.6 */
495 mrb_define_method(mrb, r, "first", mrb_range_beg, ARGS_NONE()); /* 15.2.14.4.7 */
496 mrb_define_method(mrb, r, "include?", mrb_range_include, ARGS_REQ(1)); /* 15.2.14.4.8 */
497 mrb_define_method(mrb, r, "initialize", mrb_range_initialize, ARGS_REQ(4)); /* 15.2.14.4.9 */
498 mrb_define_method(mrb, r, "last", mrb_range_end, ARGS_NONE()); /* 15.2.14.4.10 */
499 mrb_define_method(mrb, r, "member?", mrb_range_include, ARGS_REQ(1)); /* 15.2.14.4.11 */
500
501 mrb_define_method(mrb, r, "to_s", range_to_s, ARGS_NONE()); /* 15.2.14.4.12(x) */
502 mrb_define_method(mrb, r, "inspect", range_inspect, ARGS_NONE()); /* 15.2.14.4.13(x) */
503 mrb_define_method(mrb, r, "eql?", range_eql, ARGS_REQ(1)); /* 15.2.14.4.14(x) */
504 mrb_define_method(mrb, r, "initialize_copy", range_initialize_copy, ARGS_REQ(1)); /* 15.2.14.4.15(x) */
505 }
Something went wrong with that request. Please try again.