summaryrefslogtreecommitdiffhomepage
path: root/src/range.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/range.c')
-rw-r--r--src/range.c499
1 files changed, 499 insertions, 0 deletions
diff --git a/src/range.c b/src/range.c
new file mode 100644
index 000000000..bc85f1f62
--- /dev/null
+++ b/src/range.c
@@ -0,0 +1,499 @@
+#include "mruby.h"
+#include "mruby/class.h"
+#include "mruby/range.h"
+#include "variable.h"
+#include "error.h"
+#include "mruby/numeric.h"
+#include "mruby/string.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef OTHER
+#define OTHER 2
+#endif
+
+mrb_value mrb_exec_recursive_paired(mrb_state *mrb, mrb_value (*func) (mrb_state *, mrb_value, mrb_value, int),
+ mrb_value obj, mrb_value paired_obj, void* arg);
+
+int printf (const char*, ...);
+/*--------- <1.8.7>object.c ---------> */
+
+/*
+ * call-seq:
+ * obj.instance_of?(class) => true or false
+ *
+ * Returns <code>true</code> if <i>obj</i> is an instance of the given
+ * class. See also <code>Object#kind_of?</code>.
+ */
+
+int
+mrb_obj_is_instance_of(mrb_state *mrb, mrb_value obj, struct RClass* c)
+{
+ if (mrb_obj_class(mrb, obj) == c) return TRUE;
+ return FALSE;
+}
+/*--------- <1.8.7>object.c ---------< */
+
+mrb_value
+mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, int excl)
+{
+ struct RRange *r;
+
+ r = mrb_obj_alloc(mrb, MRB_TT_RANGE, mrb->range_class);
+ r->edges = mrb_malloc(mrb, sizeof(struct mrb_range_edges));
+ r->edges->beg = beg;
+ r->edges->end = end;
+ r->excl = excl;
+ return mrb_range_value(r);
+}
+
+/*
+ * call-seq:
+ * rng.first => obj
+ * rng.begin => obj
+ *
+ * Returns the first object in <i>rng</i>.
+ */
+mrb_value
+mrb_range_beg(mrb_state *mrb, mrb_value range)
+{
+ struct RRange *r = mrb_range_ptr(range);
+
+ return r->edges->beg;
+}
+
+/*
+ * call-seq:
+ * rng.end => obj
+ * rng.last => obj
+ *
+ * Returns the object that defines the end of <i>rng</i>.
+ *
+ * (1..10).end #=> 10
+ * (1...10).end #=> 10
+ */
+
+mrb_value
+mrb_range_end(mrb_state *mrb, mrb_value range)
+{
+ struct RRange *r = mrb_range_ptr(range);
+
+ return r->edges->end;
+}
+
+/*
+ * call-seq:
+ * range.exclude_end? => true or false
+ *
+ * Returns <code>true</code> if <i>range</i> excludes its end value.
+ */
+mrb_value
+mrb_range_excl(mrb_state *mrb, mrb_value range)
+{
+ struct RRange *r = mrb_range_ptr(range);
+
+ return r->excl ? mrb_true_value() : mrb_false_value();
+}
+
+/*
+ * call-seq:
+ * beg end
+ * args[0] <= args[1] => true
+ * args[0] > args[1] => false
+ */
+static int
+range_check(mrb_state *mrb, mrb_value *args)
+{
+ mrb_value ans = mrb_funcall(mrb, args[0], "<=>", 1, args[1]);
+ /* beg end
+ ans :args[0] < args[1] => -1
+ args[0] = args[1] => 0
+ args[0] > args[1] => +1 */
+ if (mrb_nil_p(ans)) return FALSE;
+ //if (mrb_obj_equal(mrb, ans, mrb_fixnum_value(1))) return FALSE;
+ if (mrb_fixnum(ans) == 1) return FALSE;
+ return TRUE;
+}
+
+static void
+range_init(mrb_state *mrb, mrb_value range, mrb_value beg, mrb_value end, mrb_int exclude_end)
+{
+ mrb_value args[2];
+ struct RRange *r = mrb_range_ptr(range);
+
+ if ((mrb_type(beg) != MRB_TT_FIXNUM) || (mrb_type(end) != MRB_TT_FIXNUM)) {
+ args[0] = beg;
+ args[1] = end;
+ /* eroor.c v = mrb_rescue(range_check, (mrb_value)args, range_failed, 0);
+ if (mrb_nil_p(v)) range_failed(); */
+ if (!range_check(mrb, args)) {
+ printf("range_failed()\n");
+ }
+ }
+ r->excl = exclude_end;
+ r->edges->beg = beg;
+ r->edges->end = end;
+}
+/*
+ * call-seq:
+ * Range.new(start, end, exclusive=false) => range
+ *
+ * Constructs a range using the given <i>start</i> and <i>end</i>. If the third
+ * parameter is omitted or is <code>false</code>, the <i>range</i> will include
+ * the end object; otherwise, it will be excluded.
+ */
+
+mrb_value
+mrb_range_initialize(mrb_state *mrb, mrb_value range)
+{
+ mrb_value beg, end;
+ mrb_value flags;
+
+ mrb_get_args(mrb, "ooo", &beg, &end, &flags);
+ /* Ranges are immutable, so that they should be initialized only once. */
+ range_init(mrb, range, beg, end, mrb_test(flags));
+ return range;
+}
+/*
+ * call-seq:
+ * range == obj => true or false
+ *
+ * Returns <code>true</code> only if
+ * 1) <i>obj</i> is a Range,
+ * 2) <i>obj</i> has equivalent beginning and end items (by comparing them with <code>==</code>),
+ * 3) <i>obj</i> has the same #exclude_end? setting as <i>rng</t>.
+ *
+ * (0..2) == (0..2) #=> true
+ * (0..2) == Range.new(0,2) #=> true
+ * (0..2) == (0...2) #=> false
+ *
+ */
+
+mrb_value
+mrb_range_eq(mrb_state *mrb, mrb_value range)
+{
+ struct RRange *rr;
+ struct RRange *ro;
+ mrb_value obj;
+
+ mrb_get_args(mrb, "o", &obj);
+
+ if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value();
+
+ /* same class? */
+ // if (!rb_obj_is_instance_of(obj, rb_obj_class(range)))
+ if (!mrb_obj_is_instance_of(mrb, obj, mrb_obj_class(mrb, range)))
+ return mrb_false_value();
+
+ rr = mrb_range_ptr(range);
+ ro = mrb_range_ptr(obj);
+ if (!mrb_obj_equal(mrb, rr->edges->beg, ro->edges->beg))
+ return mrb_false_value();
+ if (!mrb_obj_equal(mrb, rr->edges->end, ro->edges->end))
+ return mrb_false_value();
+ if (rr->excl != ro->excl)
+ return mrb_false_value();
+
+ return mrb_true_value();
+}
+
+static int
+r_le(mrb_state *mrb, mrb_value a, mrb_value b)
+{
+ //int c;
+ mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */
+ /* output :a < b => -1, a = b => 0, a > b => +1 */
+
+ if (mrb_nil_p(r)) return FALSE;
+
+ /* mrb_value -> int */
+ //c = mrb_cmpint(mrb, r, a, b);
+ //if (c == 0) return TRUE;
+ //if (c < 0) return TRUE;
+ //return FALSE;
+ if (mrb_obj_equal(mrb, r, mrb_fixnum_value(0))) return TRUE;
+ if (mrb_obj_equal(mrb, r, mrb_fixnum_value(-1))) return TRUE;
+ return FALSE;
+}
+
+static int
+r_gt(mrb_state *mrb, mrb_value a, mrb_value b)
+{
+ //int c;
+ mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b);
+ /* output :a < b => -1, a = b => 0, a > b => +1 */
+
+ if (mrb_nil_p(r)) return FALSE;
+
+ /* mrb_value -> int */
+ //c = mrb_cmpint(mrb, r);
+ //if (c > 0) return TRUE;
+ //return FALSE;
+ if (mrb_obj_equal(mrb, r, mrb_fixnum_value(1))) return TRUE;
+ return FALSE;
+}
+
+static int
+r_ge(mrb_state *mrb, mrb_value a, mrb_value b)
+{
+ //int c;
+ mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */
+ /* output :a < b => -1, a = b => 0, a > b => +1 */
+
+ if (mrb_nil_p(r)) return FALSE;
+
+ /* mrb_value -> int */
+ //c = mrb_cmpint(mrb, r);
+ //if (c == 0) return TRUE;
+ //if (c > 0) return TRUE;
+ //return FALSE;
+ if (mrb_obj_equal(mrb, r, mrb_fixnum_value(0))) return TRUE;
+ if (mrb_obj_equal(mrb, r, mrb_fixnum_value(1))) return TRUE;
+ return FALSE;
+}
+
+/*
+ * call-seq:
+ * range === obj => true or false
+ * range.member?(val) => true or false
+ * range.include?(val) => true or false
+ *
+ */
+mrb_value
+mrb_range_include(mrb_state *mrb, mrb_value range)
+{
+ mrb_value val;
+ struct RRange *r = mrb_range_ptr(range);
+ mrb_value beg, end;
+
+ mrb_get_args(mrb, "o", &val);
+
+ beg = r->edges->beg;
+ end = r->edges->end;
+ if (r_le(mrb, beg, val)) {
+ /* beg <= val */
+ if (r->excl) {
+ if (r_gt(mrb, end, val)) return mrb_true_value(); /* end > val */
+ }
+ else {
+ if (r_ge(mrb, end, val)) return mrb_true_value(); /* end >= val */
+ }
+ }
+ return mrb_false_value();
+}
+
+/*
+ * call-seq:
+ * rng.each {| i | block } => rng
+ *
+ * Iterates over the elements <i>rng</i>, passing each in turn to the
+ * block. You can only iterate if the start object of the range
+ * supports the +succ+ method (which means that you can't iterate over
+ * ranges of +Float+ objects).
+ *
+ * (10..15).each do |n|
+ * print n, ' '
+ * end
+ *
+ * <em>produces:</em>
+ *
+ * 10 11 12 13 14 15
+ */
+
+mrb_value
+mrb_range_each(mrb_state *mrb, mrb_value range)
+{
+ return range;
+}
+
+mrb_int
+mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_int err)
+{
+ mrb_int beg, end, b, e;
+ struct RRange *r = mrb_range_ptr(range);
+
+ //if (!mrb_obj_is_kind_of(mrb, range, mrb->range_class)) return FALSE;
+ if (mrb_type(range) != MRB_TT_RANGE) return FALSE;
+
+ beg = b = mrb_fixnum(r->edges->beg);
+ end = e = mrb_fixnum(r->edges->end);
+
+ if (beg < 0) {
+ beg += len;
+ if (beg < 0) goto out_of_range;
+ }
+ if (err == 0 || err == 2) {
+ if (beg > len) goto out_of_range;
+ if (end > len) end = len;
+ }
+ if (end < 0) end += len;
+ if (!r->excl) end++; /* include end point */
+ len = end - beg;
+ if (len < 0) len = 0;
+
+ *begp = beg;
+ *lenp = len;
+ return TRUE;
+
+out_of_range:
+ if (err) {
+ mrb_raise(mrb, E_RANGE_ERROR, "%ld..%s%ld out of range",
+ b, r->excl? "." : "", e);
+ }
+ return OTHER;
+}
+
+/* 15.2.14.4.12(x) */
+/*
+ * call-seq:
+ * rng.to_s -> string
+ *
+ * Convert this range object to a printable form.
+ */
+
+static mrb_value
+range_to_s(mrb_state *mrb, mrb_value range)
+{
+ mrb_value str, str2;
+ struct RRange *r = mrb_range_ptr(range);
+
+ str = mrb_obj_as_string(mrb, r->edges->beg);
+ str2 = mrb_obj_as_string(mrb, r->edges->end);
+ str = mrb_str_dup(mrb, str);
+ mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2);
+ mrb_str_append(mrb, str, str2);
+ //OBJ_INFECT(str, str2);
+
+ return str;
+}
+
+static mrb_value
+inspect_range(mrb_state *mrb, mrb_value range, mrb_value dummy, int recur)
+{
+ mrb_value str, str2;
+ struct RRange *r = mrb_range_ptr(range);
+
+ if (recur) {
+ return mrb_str_new2(mrb, r->excl ? "(... ... ...)" : "(... .. ...)");
+ }
+ str = mrb_inspect(mrb, r->edges->beg);
+ str2 = mrb_inspect(mrb, r->edges->end);
+ str = mrb_str_dup(mrb, str);
+ mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2);
+ mrb_str_append(mrb, str, str2);
+ // OBJ_INFECT(str, str2);
+
+ return str;
+}
+
+/* 15.2.14.4.13(x) */
+/*
+ * call-seq:
+ * rng.inspect -> string
+ *
+ * Convert this range object to a printable form (using
+ * <code>inspect</code> to convert the start and end
+ * objects).
+ */
+
+static mrb_value
+range_inspect(mrb_state *mrb, mrb_value range)
+{
+ return inspect_range(mrb, range, range, 0);
+}
+
+static mrb_value
+recursive_eql(mrb_state *mrb, mrb_value range, mrb_value obj, int recur)
+{
+ struct RRange *r = mrb_range_ptr(range);
+ struct RRange *o = mrb_range_ptr(obj);
+
+ if (recur) return mrb_true_value(); /* Subtle! */
+ if (!mrb_eql(mrb, r->edges->beg, o->edges->beg))
+ return mrb_false_value();
+ if (!mrb_eql(mrb, r->edges->end, o->edges->end))
+ return mrb_false_value();
+
+ if (r->excl != o->excl)
+ return mrb_false_value();
+ return mrb_true_value();
+}
+
+/* 15.2.14.4.14(x) */
+/*
+ * call-seq:
+ * rng.eql?(obj) -> true or false
+ *
+ * Returns <code>true</code> only if <i>obj</i> is a Range, has equivalent
+ * beginning and end items (by comparing them with #eql?), and has the same
+ * #exclude_end? setting as <i>rng</i>.
+ *
+ * (0..2).eql?(0..2) #=> true
+ * (0..2).eql?(Range.new(0,2)) #=> true
+ * (0..2).eql?(0...2) #=> false
+ *
+ */
+
+static mrb_value
+range_eql(mrb_state *mrb, mrb_value range)
+{
+ mrb_value obj;
+ mrb_get_args(mrb, "o", &obj);
+
+ if (mrb_obj_equal(mrb, range, obj))
+ return mrb_true_value();
+ if (!mrb_obj_is_kind_of(mrb, obj, mrb->range_class))
+ return mrb_false_value();
+ return mrb_exec_recursive_paired(mrb, recursive_eql, range, obj, &obj);
+}
+
+/* 15.2.14.4.15(x) */
+mrb_value
+range_initialize_copy(mrb_state *mrb, mrb_value copy)
+{
+ mrb_value src;
+ mrb_get_args(mrb, "o", &src);
+
+ if (mrb_obj_equal(mrb, copy, src)) return copy;
+ //mrb_check_frozen(copy);
+ if (!mrb_obj_is_instance_of(mrb, src, mrb_obj_class(mrb, copy))) {
+ mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
+ }
+ memcpy(mrb_range_ptr(copy), mrb_range_ptr(src), sizeof(struct RRange));
+
+ return copy;
+}
+
+void
+mrb_init_range(mrb_state *mrb)
+{
+ struct RClass *r;
+ r = mrb->range_class = mrb_define_class(mrb, "Range", mrb->object_class);
+ mrb_include_module(mrb, r, mrb_class_get(mrb, "Enumerable"));
+
+ mrb_define_method(mrb, r, "begin", mrb_range_beg, ARGS_NONE()); /* 15.2.14.4.3 */
+ mrb_define_method(mrb, r, "end", mrb_range_end, ARGS_NONE()); /* 15.2.14.4.5 */
+ mrb_define_method(mrb, r, "==", mrb_range_eq, ARGS_REQ(1)); /* 15.2.14.4.1 */
+ mrb_define_method(mrb, r, "===", mrb_range_include, ARGS_REQ(1)); /* 15.2.14.4.2 */
+ mrb_define_method(mrb, r, "each", mrb_range_each, ARGS_NONE()); /* 15.2.14.4.4 */
+ mrb_define_method(mrb, r, "exclude_end?", mrb_range_excl, ARGS_NONE()); /* 15.2.14.4.6 */
+ mrb_define_method(mrb, r, "first", mrb_range_beg, ARGS_NONE()); /* 15.2.14.4.7 */
+ mrb_define_method(mrb, r, "include?", mrb_range_include, ARGS_REQ(1)); /* 15.2.14.4.8 */
+ mrb_define_method(mrb, r, "initialize", mrb_range_initialize, ARGS_REQ(4)); /* 15.2.14.4.9 */
+ mrb_define_method(mrb, r, "last", mrb_range_end, ARGS_NONE()); /* 15.2.14.4.10 */
+ mrb_define_method(mrb, r, "member?", mrb_range_include, ARGS_REQ(1)); /* 15.2.14.4.11 */
+
+ mrb_define_method(mrb, r, "to_s", range_to_s, ARGS_NONE()); /* 15.2.14.4.12(x) */
+ mrb_define_method(mrb, r, "inspect", range_inspect, ARGS_NONE()); /* 15.2.14.4.13(x) */
+ mrb_define_method(mrb, r, "eql?", range_eql, ARGS_REQ(1)); /* 15.2.14.4.14(x) */
+ mrb_define_method(mrb, r, "initialize_copy", range_initialize_copy, ARGS_REQ(1)); /* 15.2.14.4.15(x) */
+}