Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 506 lines (435 sloc) 13.391 kb
8354436 Hiroshi Mimaki add file header
mimaki authored
1 /*
2 ** range.c - Range class
3 **
4 ** See Copyright Notice in mruby.h
5 */
6
e0d6430 Hiroshi 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.