2000-05-01 13:42:38 +04:00
|
|
|
/**********************************************************************
|
1998-01-16 15:13:05 +03:00
|
|
|
|
|
|
|
range.c -
|
|
|
|
|
|
|
|
$Author$
|
|
|
|
$Date$
|
|
|
|
created at: Thu Aug 19 17:46:47 JST 1993
|
|
|
|
|
2001-02-14 08:52:06 +03:00
|
|
|
Copyright (C) 1993-2001 Yukihiro Matsumoto
|
1998-01-16 15:13:05 +03:00
|
|
|
|
2000-05-01 13:42:38 +04:00
|
|
|
**********************************************************************/
|
1998-01-16 15:13:05 +03:00
|
|
|
|
|
|
|
#include "ruby.h"
|
|
|
|
|
1999-01-20 07:59:39 +03:00
|
|
|
VALUE rb_cRange;
|
2000-11-27 12:23:38 +03:00
|
|
|
static ID id_cmp, id_beg, id_end, id_excl;
|
1999-08-13 09:45:20 +04:00
|
|
|
|
2000-11-27 12:23:38 +03:00
|
|
|
#define EXCL(r) RTEST(rb_ivar_get((r), id_excl))
|
|
|
|
#define SET_EXCL(r,v) rb_ivar_set((r), id_excl, (v)?Qtrue:Qfalse)
|
1998-01-16 15:13:05 +03:00
|
|
|
|
1999-01-20 07:59:39 +03:00
|
|
|
static VALUE
|
|
|
|
range_check(args)
|
|
|
|
VALUE *args;
|
|
|
|
{
|
|
|
|
rb_funcall(args[0], id_cmp, 1, args[1]);
|
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
range_failed()
|
|
|
|
{
|
|
|
|
rb_raise(rb_eArgError, "bad value for range");
|
1999-12-06 12:04:03 +03:00
|
|
|
return Qnil; /* dummy */
|
1999-01-20 07:59:39 +03:00
|
|
|
}
|
1998-01-16 15:13:05 +03:00
|
|
|
|
2000-02-29 11:05:32 +03:00
|
|
|
static void
|
|
|
|
range_init(obj, beg, end, exclude_end)
|
|
|
|
VALUE obj, beg, end;
|
1999-08-13 09:45:20 +04:00
|
|
|
int exclude_end;
|
1998-01-16 15:13:05 +03:00
|
|
|
{
|
1999-08-13 09:45:20 +04:00
|
|
|
VALUE args[2];
|
1998-01-16 15:13:05 +03:00
|
|
|
|
1999-08-13 09:45:20 +04:00
|
|
|
args[0] = beg; args[1] = end;
|
1999-01-20 07:59:39 +03:00
|
|
|
if (!FIXNUM_P(beg) || !FIXNUM_P(end)) {
|
2000-08-31 09:29:54 +04:00
|
|
|
rb_rescue2(range_check, (VALUE)args, range_failed, 0,
|
|
|
|
rb_eStandardError, rb_eNameError, 0);
|
1998-01-16 15:13:05 +03:00
|
|
|
}
|
|
|
|
|
2000-11-27 12:23:38 +03:00
|
|
|
SET_EXCL(obj, exclude_end);
|
1999-01-20 07:59:39 +03:00
|
|
|
rb_ivar_set(obj, id_beg, beg);
|
|
|
|
rb_ivar_set(obj, id_end, end);
|
1998-01-16 15:13:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
1999-08-13 09:45:20 +04:00
|
|
|
rb_range_new(beg, end, exclude_end)
|
1999-01-20 07:59:39 +03:00
|
|
|
VALUE beg, end;
|
1999-08-13 09:45:20 +04:00
|
|
|
int exclude_end;
|
|
|
|
{
|
2000-02-29 11:05:32 +03:00
|
|
|
VALUE obj = rb_obj_alloc(rb_cRange);
|
|
|
|
|
|
|
|
range_init(obj, beg, end, exclude_end);
|
|
|
|
return obj;
|
1999-08-13 09:45:20 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
2000-02-29 11:05:32 +03:00
|
|
|
range_initialize(argc, argv, obj)
|
1999-08-13 09:45:20 +04:00
|
|
|
int argc;
|
|
|
|
VALUE *argv;
|
2000-02-29 11:05:32 +03:00
|
|
|
VALUE obj;
|
1999-08-13 09:45:20 +04:00
|
|
|
{
|
2001-02-02 14:38:20 +03:00
|
|
|
VALUE beg, end, flags;
|
1999-08-13 09:45:20 +04:00
|
|
|
|
2001-02-02 14:38:20 +03:00
|
|
|
rb_scan_args(argc, argv, "21", &beg, &end, &flags);
|
2000-02-29 11:05:32 +03:00
|
|
|
/* Ranges are immutable, so that they should be initialized only once. */
|
|
|
|
if (rb_ivar_defined(obj, id_beg)) {
|
|
|
|
rb_raise(rb_eNameError, "`initialize' called twice");
|
|
|
|
}
|
2001-02-02 14:38:20 +03:00
|
|
|
range_init(obj, beg, end, RTEST(flags));
|
2000-02-29 11:05:32 +03:00
|
|
|
return Qnil;
|
1999-08-13 09:45:20 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
range_exclude_end_p(range)
|
|
|
|
VALUE range;
|
1998-01-16 15:13:05 +03:00
|
|
|
{
|
1999-08-13 09:45:20 +04:00
|
|
|
return EXCL(range)?Qtrue:Qfalse;
|
1998-01-16 15:13:05 +03:00
|
|
|
}
|
|
|
|
|
2000-11-27 12:23:38 +03:00
|
|
|
static VALUE
|
|
|
|
range_eq(range, obj)
|
|
|
|
VALUE range, obj;
|
|
|
|
{
|
2000-12-29 05:46:12 +03:00
|
|
|
if (range == obj) return Qtrue;
|
2000-11-27 12:23:38 +03:00
|
|
|
if (!rb_obj_is_kind_of(obj, rb_cRange)) return Qfalse;
|
|
|
|
|
|
|
|
if (!rb_equal(rb_ivar_get(range, id_beg), rb_ivar_get(obj, id_beg)))
|
|
|
|
return Qfalse;
|
|
|
|
if (!rb_equal(rb_ivar_get(range, id_end), rb_ivar_get(obj, id_end)))
|
|
|
|
return Qfalse;
|
|
|
|
|
|
|
|
if (EXCL(range) != EXCL(obj)) return Qfalse;
|
|
|
|
|
|
|
|
return Qtrue;
|
|
|
|
}
|
|
|
|
|
1998-01-16 15:13:05 +03:00
|
|
|
static VALUE
|
1999-08-13 09:45:20 +04:00
|
|
|
range_eqq(range, obj)
|
|
|
|
VALUE range, obj;
|
1998-01-16 15:13:05 +03:00
|
|
|
{
|
1999-01-20 07:59:39 +03:00
|
|
|
VALUE beg, end;
|
1998-01-16 15:13:05 +03:00
|
|
|
|
1999-08-13 09:45:20 +04:00
|
|
|
beg = rb_ivar_get(range, id_beg);
|
|
|
|
end = rb_ivar_get(range, id_end);
|
1998-01-16 15:13:05 +03:00
|
|
|
|
1999-01-20 07:59:39 +03:00
|
|
|
if (FIXNUM_P(beg) && FIXNUM_P(obj) && FIXNUM_P(end)) {
|
2000-08-24 10:21:43 +04:00
|
|
|
if (FIX2LONG(beg) <= FIX2LONG(obj)) {
|
1999-08-13 09:45:20 +04:00
|
|
|
if (EXCL(range)) {
|
2000-08-24 10:21:43 +04:00
|
|
|
if (FIX2LONG(obj) < FIX2LONG(end)) return Qtrue;
|
1999-08-13 09:45:20 +04:00
|
|
|
}
|
|
|
|
else {
|
2000-08-24 10:21:43 +04:00
|
|
|
if (FIX2LONG(obj) <= FIX2LONG(end)) return Qtrue;
|
1999-08-13 09:45:20 +04:00
|
|
|
}
|
1998-01-16 15:13:05 +03:00
|
|
|
}
|
1999-01-20 07:59:39 +03:00
|
|
|
return Qfalse;
|
1998-01-16 15:13:05 +03:00
|
|
|
}
|
1999-08-13 09:45:20 +04:00
|
|
|
else if (RTEST(rb_funcall(beg, rb_intern("<="), 1, obj))) {
|
|
|
|
if (EXCL(range)) {
|
|
|
|
if (RTEST(rb_funcall(end, rb_intern(">"), 1, obj)))
|
|
|
|
return Qtrue;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (RTEST(rb_funcall(end, rb_intern(">="), 1, obj)))
|
|
|
|
return Qtrue;
|
1998-01-16 15:13:05 +03:00
|
|
|
}
|
|
|
|
}
|
1999-08-13 09:45:20 +04:00
|
|
|
return Qfalse;
|
1998-01-16 15:13:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
1999-08-13 09:45:20 +04:00
|
|
|
range_each(range)
|
|
|
|
VALUE range;
|
1998-01-16 15:13:05 +03:00
|
|
|
{
|
|
|
|
VALUE b, e;
|
|
|
|
|
1999-08-13 09:45:20 +04:00
|
|
|
b = rb_ivar_get(range, id_beg);
|
|
|
|
e = rb_ivar_get(range, id_end);
|
1998-01-16 15:13:05 +03:00
|
|
|
|
1999-08-13 09:45:20 +04:00
|
|
|
if (FIXNUM_P(b) && FIXNUM_P(e)) { /* fixnums are special */
|
|
|
|
long end = FIX2LONG(e);
|
|
|
|
long i;
|
1998-01-16 15:13:05 +03:00
|
|
|
|
1999-08-13 09:45:20 +04:00
|
|
|
if (!EXCL(range)) end += 1;
|
|
|
|
for (i=FIX2LONG(b); i<end; i++) {
|
|
|
|
rb_yield(INT2NUM(i));
|
|
|
|
}
|
|
|
|
}
|
1999-09-16 13:40:33 +04:00
|
|
|
else if (TYPE(b) == T_STRING) {
|
|
|
|
rb_str_upto(b, e, EXCL(range));
|
|
|
|
}
|
1999-12-14 09:50:43 +03:00
|
|
|
else if (rb_obj_is_kind_of(b, rb_cNumeric)) {
|
|
|
|
b = rb_Integer(b);
|
|
|
|
e = rb_Integer(e);
|
|
|
|
|
|
|
|
if (!EXCL(range)) e = rb_funcall(e, '+', 1, INT2FIX(1));
|
|
|
|
while (RTEST(rb_funcall(b, '<', 1, e))) {
|
|
|
|
rb_yield(b);
|
|
|
|
b = rb_funcall(b, '+', 1, INT2FIX(1));
|
|
|
|
}
|
|
|
|
}
|
1999-09-16 13:40:33 +04:00
|
|
|
else { /* generic each */
|
1999-08-13 09:45:20 +04:00
|
|
|
VALUE v = b;
|
|
|
|
ID succ = rb_intern("succ");
|
|
|
|
|
1999-09-16 13:40:33 +04:00
|
|
|
if (EXCL(range)) {
|
|
|
|
while (RTEST(rb_funcall(v, '<', 1, e))) {
|
|
|
|
if (rb_equal(v, e)) break;
|
|
|
|
rb_yield(v);
|
|
|
|
v = rb_funcall(v, succ, 0, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ID le = rb_intern("<=");
|
|
|
|
|
|
|
|
while (RTEST(rb_funcall(v, le, 1, e))) {
|
|
|
|
rb_yield(v);
|
|
|
|
if (rb_equal(v, e)) break;
|
|
|
|
v = rb_funcall(v, succ, 0, 0);
|
|
|
|
}
|
1999-08-13 09:45:20 +04:00
|
|
|
}
|
1998-01-16 15:13:05 +03:00
|
|
|
}
|
|
|
|
|
1999-08-13 09:45:20 +04:00
|
|
|
return range;
|
1998-01-16 15:13:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
range_first(obj)
|
|
|
|
VALUE obj;
|
|
|
|
{
|
|
|
|
VALUE b;
|
|
|
|
|
1999-01-20 07:59:39 +03:00
|
|
|
b = rb_ivar_get(obj, id_beg);
|
1998-01-16 15:13:05 +03:00
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
range_last(obj)
|
|
|
|
VALUE obj;
|
|
|
|
{
|
|
|
|
VALUE e;
|
|
|
|
|
1999-01-20 07:59:39 +03:00
|
|
|
e = rb_ivar_get(obj, id_end);
|
1998-01-16 15:13:05 +03:00
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
1999-08-13 09:45:20 +04:00
|
|
|
rb_range_beg_len(range, begp, lenp, len, err)
|
1998-01-16 15:13:05 +03:00
|
|
|
VALUE range;
|
1999-08-13 09:45:20 +04:00
|
|
|
long *begp, *lenp;
|
|
|
|
long len;
|
|
|
|
int err;
|
1998-01-16 15:13:05 +03:00
|
|
|
{
|
1999-08-13 09:45:20 +04:00
|
|
|
long beg, end, b, e;
|
1998-01-16 15:13:05 +03:00
|
|
|
|
1999-01-20 07:59:39 +03:00
|
|
|
if (!rb_obj_is_kind_of(range, rb_cRange)) return Qfalse;
|
1998-01-16 15:13:05 +03:00
|
|
|
|
1999-08-13 09:45:20 +04:00
|
|
|
beg = b = NUM2LONG(rb_ivar_get(range, id_beg));
|
|
|
|
end = e = NUM2LONG(rb_ivar_get(range, id_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 || (!EXCL(range) && end == len))
|
|
|
|
end = len;
|
|
|
|
}
|
|
|
|
if (end < 0) {
|
|
|
|
end += len;
|
|
|
|
if (end < 0) {
|
|
|
|
if (beg == 0 && end == -1 && !EXCL(range)) {
|
|
|
|
len = 0;
|
|
|
|
goto length_set;
|
|
|
|
}
|
|
|
|
goto out_of_range;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
len = end - beg;
|
|
|
|
if (!EXCL(range)) len++; /* include end point */
|
|
|
|
if (len < 0) goto out_of_range;
|
|
|
|
|
|
|
|
length_set:
|
|
|
|
*begp = beg;
|
|
|
|
*lenp = len;
|
|
|
|
|
1999-01-20 07:59:39 +03:00
|
|
|
return Qtrue;
|
1999-08-13 09:45:20 +04:00
|
|
|
|
|
|
|
out_of_range:
|
|
|
|
if (err) {
|
2000-03-07 11:37:59 +03:00
|
|
|
rb_raise(rb_eRangeError, "%d..%s%d out of range",
|
1999-08-13 09:45:20 +04:00
|
|
|
b, EXCL(range)?".":"", e);
|
|
|
|
}
|
|
|
|
return Qnil;
|
1998-01-16 15:13:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
range_to_s(range)
|
|
|
|
VALUE range;
|
|
|
|
{
|
|
|
|
VALUE str, str2;
|
|
|
|
|
1999-01-20 07:59:39 +03:00
|
|
|
str = rb_obj_as_string(rb_ivar_get(range, id_beg));
|
|
|
|
str2 = rb_obj_as_string(rb_ivar_get(range, id_end));
|
2000-04-10 09:48:43 +04:00
|
|
|
str = rb_str_dup(str);
|
1999-08-13 09:45:20 +04:00
|
|
|
rb_str_cat(str, "...", EXCL(range)?3:2);
|
2000-04-10 09:48:43 +04:00
|
|
|
rb_str_append(str, str2);
|
|
|
|
OBJ_INFECT(str, str2);
|
1998-01-16 15:13:05 +03:00
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
range_inspect(range)
|
|
|
|
VALUE range;
|
|
|
|
{
|
|
|
|
VALUE str, str2;
|
|
|
|
|
1999-01-20 07:59:39 +03:00
|
|
|
str = rb_inspect(rb_ivar_get(range, id_beg));
|
|
|
|
str2 = rb_inspect(rb_ivar_get(range, id_end));
|
2000-04-10 09:48:43 +04:00
|
|
|
str = rb_str_dup(str);
|
1999-08-13 09:45:20 +04:00
|
|
|
rb_str_cat(str, "...", EXCL(range)?3:2);
|
2000-04-10 09:48:43 +04:00
|
|
|
rb_str_append(str, str2);
|
|
|
|
OBJ_INFECT(str, str2);
|
1998-01-16 15:13:05 +03:00
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
1999-12-07 12:25:55 +03:00
|
|
|
static VALUE
|
|
|
|
length_i(i, length)
|
|
|
|
VALUE i;
|
|
|
|
int *length;
|
|
|
|
{
|
|
|
|
(*length)++;
|
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_length_by_each(obj)
|
|
|
|
VALUE obj;
|
|
|
|
{
|
|
|
|
int length = 0;
|
|
|
|
|
|
|
|
rb_iterate(rb_each, obj, length_i, (VALUE)&length);
|
|
|
|
return INT2FIX(length);
|
|
|
|
}
|
|
|
|
|
1998-01-16 15:13:05 +03:00
|
|
|
static VALUE
|
1999-08-13 09:45:20 +04:00
|
|
|
range_length(range)
|
|
|
|
VALUE range;
|
1998-01-16 15:13:05 +03:00
|
|
|
{
|
1999-01-20 07:59:39 +03:00
|
|
|
VALUE beg, end;
|
1999-12-14 09:50:43 +03:00
|
|
|
long size;
|
1998-01-16 15:13:05 +03:00
|
|
|
|
1999-08-13 09:45:20 +04:00
|
|
|
beg = rb_ivar_get(range, id_beg);
|
|
|
|
end = rb_ivar_get(range, id_end);
|
1998-01-16 15:13:05 +03:00
|
|
|
|
1999-01-20 07:59:39 +03:00
|
|
|
if (RTEST(rb_funcall(beg, '>', 1, end))) {
|
|
|
|
return INT2FIX(0);
|
|
|
|
}
|
1999-08-13 09:45:20 +04:00
|
|
|
if (FIXNUM_P(beg) && FIXNUM_P(end)) {
|
|
|
|
if (EXCL(range)) {
|
|
|
|
return INT2NUM(NUM2LONG(end) - NUM2LONG(beg));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return INT2NUM(NUM2LONG(end) - NUM2LONG(beg) + 1);
|
|
|
|
}
|
|
|
|
}
|
1999-12-14 09:50:43 +03:00
|
|
|
if (!rb_obj_is_kind_of(beg, rb_cInteger)) {
|
1999-12-07 12:25:55 +03:00
|
|
|
return rb_length_by_each(range);
|
1998-01-16 15:13:05 +03:00
|
|
|
}
|
1999-01-20 07:59:39 +03:00
|
|
|
size = rb_funcall(end, '-', 1, beg);
|
1999-08-13 09:45:20 +04:00
|
|
|
if (!EXCL(range)) {
|
|
|
|
size = rb_funcall(size, '+', 1, INT2FIX(1));
|
|
|
|
}
|
2000-12-27 08:59:03 +03:00
|
|
|
if (TYPE(size) == T_FLOAT) {
|
|
|
|
size = rb_funcall(size, rb_intern("floor"), 0);
|
|
|
|
}
|
1998-01-16 15:13:05 +03:00
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
Init_Range()
|
|
|
|
{
|
1999-01-20 07:59:39 +03:00
|
|
|
rb_cRange = rb_define_class("Range", rb_cObject);
|
|
|
|
rb_include_module(rb_cRange, rb_mEnumerable);
|
2000-02-29 11:05:32 +03:00
|
|
|
rb_define_method(rb_cRange, "initialize", range_initialize, -1);
|
2000-11-27 12:23:38 +03:00
|
|
|
rb_define_method(rb_cRange, "==", range_eq, 1);
|
1999-01-20 07:59:39 +03:00
|
|
|
rb_define_method(rb_cRange, "===", range_eqq, 1);
|
|
|
|
rb_define_method(rb_cRange, "each", range_each, 0);
|
|
|
|
rb_define_method(rb_cRange, "first", range_first, 0);
|
|
|
|
rb_define_method(rb_cRange, "last", range_last, 0);
|
|
|
|
rb_define_method(rb_cRange, "begin", range_first, 0);
|
|
|
|
rb_define_method(rb_cRange, "end", range_last, 0);
|
|
|
|
rb_define_method(rb_cRange, "to_s", range_to_s, 0);
|
|
|
|
rb_define_method(rb_cRange, "inspect", range_inspect, 0);
|
|
|
|
|
1999-08-13 09:45:20 +04:00
|
|
|
rb_define_method(rb_cRange, "exclude_end?", range_exclude_end_p, 0);
|
|
|
|
|
1999-01-20 07:59:39 +03:00
|
|
|
rb_define_method(rb_cRange, "length", range_length, 0);
|
|
|
|
rb_define_method(rb_cRange, "size", range_length, 0);
|
|
|
|
|
|
|
|
id_cmp = rb_intern("<=>");
|
|
|
|
id_beg = rb_intern("begin");
|
|
|
|
id_end = rb_intern("end");
|
2000-11-27 12:23:38 +03:00
|
|
|
id_excl = rb_intern("excl");
|
1998-01-16 15:13:05 +03:00
|
|
|
}
|