range.c


DEFINITIONS

This source file includes following functions.
  1. range_check
  2. range_failed
  3. range_init
  4. rb_range_new
  5. range_initialize
  6. range_exclude_end_p
  7. range_eq
  8. r_eq
  9. r_lt
  10. r_le
  11. r_gt
  12. range_eql
  13. range_hash
  14. str_step
  15. step_i
  16. range_each_func
  17. range_step
  18. each_i
  19. range_each
  20. range_first
  21. range_last
  22. rb_range_beg_len
  23. range_min
  24. range_max
  25. range_to_s
  26. range_inspect
  27. member_i
  28. range_member
  29. range_include
  30. Init_Range


   1  /**********************************************************************
   2  
   3    range.c -
   4  
   5    $Author: matz $
   6    $Date: 2002/09/03 05:20:06 $
   7    created at: Thu Aug 19 17:46:47 JST 1993
   8  
   9    Copyright (C) 1993-2002 Yukihiro Matsumoto
  10  
  11  **********************************************************************/
  12  
  13  #include "ruby.h"
  14  
  15  VALUE rb_cRange;
  16  static ID id_cmp, id_succ, id_beg, id_end, id_excl;
  17  
  18  #define EXCL(r) RTEST(rb_ivar_get((r), id_excl))
  19  #define SET_EXCL(r,v) rb_ivar_set((r), id_excl, (v) ? Qtrue : Qfalse)
  20  
  21  static VALUE
  22  range_check(args)
  23      VALUE *args;
  24  {
  25      if (!FIXNUM_P(args[0]) && !rb_obj_is_kind_of(args[0], rb_cNumeric)) {
  26          rb_funcall(args[0], id_cmp, 1, args[1]);
  27          /* rb_funcall(args[0], id_succ, 0, 0); */
  28      }
  29      return Qnil;
  30  }
  31  
  32  static VALUE
  33  range_failed()
  34  {
  35      rb_raise(rb_eArgError, "bad value for range");
  36      return Qnil;                /* dummy */
  37  }
  38  
  39  static void
  40  range_init(range, beg, end, exclude_end)
  41      VALUE range, beg, end;
  42      int exclude_end;
  43  {
  44      VALUE args[2];
  45  
  46      args[0] = beg;
  47      args[1] = end;
  48      
  49      if (!FIXNUM_P(beg) || !FIXNUM_P(end)) {
  50          rb_rescue(range_check, (VALUE)args, range_failed, 0);
  51      }
  52  
  53      SET_EXCL(range, exclude_end);
  54      rb_ivar_set(range, id_beg, beg);
  55      rb_ivar_set(range, id_end, end);
  56  }
  57  
  58  VALUE
  59  rb_range_new(beg, end, exclude_end)
  60      VALUE beg, end;
  61      int exclude_end;
  62  {
  63      VALUE range = rb_obj_alloc(rb_cRange);
  64  
  65      range_init(range, beg, end, exclude_end);
  66      return range;
  67  }
  68  
  69  static VALUE
  70  range_initialize(argc, argv, range)
  71      int argc;
  72      VALUE *argv;
  73      VALUE range;
  74  {
  75      VALUE beg, end, flags;
  76      
  77      rb_scan_args(argc, argv, "21", &beg, &end, &flags);
  78      /* Ranges are immutable, so that they should be initialized only once. */
  79      if (rb_ivar_defined(range, id_beg)) {
  80          rb_name_error(rb_intern("initialize"), "`initialize' called twice");
  81      }
  82      range_init(range, beg, end, RTEST(flags));
  83      return Qnil;
  84  }
  85  
  86  static VALUE
  87  range_exclude_end_p(range)
  88      VALUE range;
  89  {
  90      return EXCL(range) ? Qtrue : Qfalse;
  91  }
  92  
  93  static VALUE
  94  range_eq(range, obj)
  95      VALUE range, obj;
  96  {
  97      if (range == obj) return Qtrue;
  98      if (!rb_obj_is_instance_of(obj, rb_obj_class(range)))
  99          return Qfalse;
 100  
 101      if (!rb_equal(rb_ivar_get(range, id_beg), rb_ivar_get(obj, id_beg)))
 102          return Qfalse;
 103      if (!rb_equal(rb_ivar_get(range, id_end), rb_ivar_get(obj, id_end)))
 104          return Qfalse;
 105  
 106      if (EXCL(range) != EXCL(obj)) return Qfalse;
 107  
 108      return Qtrue;
 109  }
 110  
 111  static int
 112  r_eq(a, b)
 113      VALUE a, b;
 114  {
 115      if (a == b) return Qtrue;
 116  
 117      if (rb_funcall(a, id_cmp, 1, b) == INT2FIX(0))
 118          return Qtrue;
 119      return Qfalse;
 120  }
 121  
 122  static int
 123  r_lt(a, b)
 124      VALUE a, b;
 125  {
 126      VALUE r = rb_funcall(a, id_cmp, 1, b);
 127  
 128      if (rb_cmpint(r) < 0) return Qtrue;
 129      return Qfalse;
 130  }
 131  
 132  static int
 133  r_le(a, b)
 134      VALUE a, b;
 135  {
 136      VALUE r = rb_funcall(a, id_cmp, 1, b);
 137  
 138      if (rb_cmpint(r) <= 0) return Qtrue;
 139      return Qfalse;
 140  }
 141  
 142  static int
 143  r_gt(a,b)
 144      VALUE a, b;
 145  {
 146      VALUE r = rb_funcall(a, id_cmp, 1, b);
 147  
 148      if (rb_cmpint(r) > 0) return Qtrue;
 149      return Qfalse;
 150  }
 151  
 152  static VALUE
 153  range_eql(range, obj)
 154      VALUE range, obj;
 155  {
 156      if (range == obj) return Qtrue;
 157      if (!rb_obj_is_instance_of(obj, rb_obj_class(range)))
 158          return Qfalse;
 159  
 160      if (!rb_eql(rb_ivar_get(range, id_beg), rb_ivar_get(obj, id_beg)))
 161          return Qfalse;
 162      if (!rb_eql(rb_ivar_get(range, id_end), rb_ivar_get(obj, id_end)))
 163          return Qfalse;
 164  
 165      if (EXCL(range) != EXCL(obj)) return Qfalse;
 166  
 167      return Qtrue;
 168  }
 169  
 170  static VALUE
 171  range_hash(range)
 172      VALUE range;
 173  {
 174      long hash = EXCL(range);
 175      VALUE v;
 176  
 177      v = rb_hash(rb_ivar_get(range, id_beg));
 178      hash ^= v << 1;
 179      v = rb_hash(rb_ivar_get(range, id_end));
 180      hash ^= v << 9;
 181      hash ^= EXCL(range) << 24;
 182  
 183      return LONG2FIX(hash);
 184  }
 185  
 186  static VALUE
 187  str_step(args)
 188      VALUE *args;
 189  {
 190      return rb_str_upto(args[0], args[1], EXCL(args[2]));
 191  }
 192  
 193  static VALUE
 194  step_i(i, iter)
 195      VALUE i;
 196      long *iter;
 197  {
 198      iter[0]--;
 199      if (iter[0] == 0) {
 200          rb_yield(i);
 201          iter[0] = iter[1];
 202      }
 203      return Qnil;
 204  }
 205  
 206  static void
 207  range_each_func(range, func, v, e, arg)
 208      VALUE range;
 209      void (*func) _((VALUE, void*));
 210      VALUE v, e;
 211      void *arg;
 212  {
 213      if (EXCL(range)) {
 214          while (r_lt(v, e)) {
 215              (*func)(v, arg);
 216              v = rb_funcall(v, id_succ, 0, 0);
 217          }
 218      }
 219      else {
 220          while (r_le(v, e)) {
 221              (*func)(v, arg);
 222              v = rb_funcall(v, id_succ, 0, 0);
 223          }
 224      }
 225  }
 226  
 227  static VALUE
 228  range_step(argc, argv, range)
 229      int argc;
 230      VALUE *argv;
 231      VALUE range;
 232  {
 233      VALUE b, e, step;
 234      long unit;
 235  
 236      b = rb_ivar_get(range, id_beg);
 237      e = rb_ivar_get(range, id_end);
 238      if (rb_scan_args(argc, argv, "01", &step) == 0) {
 239          step = INT2FIX(1);
 240      }
 241  
 242      unit = NUM2LONG(step);
 243      if (unit <= 0) {
 244          rb_raise(rb_eArgError, "step can't be <= 0");
 245      }
 246      if (FIXNUM_P(b) && FIXNUM_P(e)) { /* fixnums are special */
 247          long end = FIX2LONG(e);
 248          long i;
 249  
 250          if (!EXCL(range)) end += 1;
 251          for (i=FIX2LONG(b); i<end; i+=unit) {
 252              rb_yield(LONG2NUM(i));
 253          }
 254      }
 255      else if (rb_obj_is_kind_of(b, rb_cNumeric)) {
 256          ID c = rb_intern(EXCL(range) ? "<" : "<=");
 257  
 258          while (RTEST(rb_funcall(b, c, 1, e))) {
 259              rb_yield(b);
 260              b = rb_funcall(b, '+', 1, step);
 261          }
 262      }
 263      else if (TYPE(b) == T_STRING) {
 264          VALUE args[5];
 265          long iter[2];
 266  
 267          args[0] = b; args[1] = e; args[2] = range;
 268          iter[0] = 1; iter[1] = unit;
 269          rb_iterate((VALUE(*)_((VALUE)))str_step, (VALUE)args, step_i, (VALUE)iter);
 270      }
 271      else {
 272          long args[2];
 273  
 274          if (!rb_respond_to(b, id_succ)) {
 275              rb_raise(rb_eTypeError, "cannot iterate from %s",
 276                       rb_class2name(CLASS_OF(b)));
 277          }
 278          
 279          args[0] = 1;
 280          args[1] = unit;
 281          range_each_func(range, step_i, b, e, args);
 282      }
 283      return range;
 284  }
 285  
 286  static void
 287  each_i(v, arg)
 288      VALUE v;
 289      void *arg;
 290  {
 291      rb_yield(v);
 292  }
 293  
 294  static VALUE
 295  range_each(range)
 296      VALUE range;
 297  {
 298      VALUE beg, end;
 299  
 300      beg = rb_ivar_get(range, id_beg);
 301      end = rb_ivar_get(range, id_end);
 302  
 303      if (!rb_respond_to(beg, id_succ)) {
 304          rb_raise(rb_eTypeError, "cannot iterate from %s",
 305                   rb_class2name(CLASS_OF(beg)));
 306      }
 307      if (TYPE(beg) == T_STRING) {
 308          VALUE args[5];
 309          long iter[2];
 310  
 311          args[0] = beg; args[1] = end; args[2] = range;
 312          iter[0] = 1; iter[1] = 1;
 313          rb_iterate((VALUE(*)_((VALUE)))str_step, (VALUE)args, step_i, (VALUE)iter);
 314      }
 315      else {
 316          range_each_func(range, each_i, beg, end, 0);
 317      }
 318      return range;
 319  }
 320  
 321  static VALUE
 322  range_first(range)
 323      VALUE range;
 324  {
 325      return rb_ivar_get(range, id_beg);
 326  }
 327  
 328  static VALUE
 329  range_last(range)
 330      VALUE range;
 331  {
 332      return rb_ivar_get(range, id_end);
 333  }
 334  
 335  VALUE
 336  rb_range_beg_len(range, begp, lenp, len, err)
 337      VALUE range;
 338      long *begp, *lenp;
 339      long len;
 340      int err;
 341  {
 342      long beg, end, b, e;
 343  
 344      if (!rb_obj_is_kind_of(range, rb_cRange)) return Qfalse;
 345  
 346      beg = b = NUM2LONG(rb_ivar_get(range, id_beg));
 347      end = e = NUM2LONG(rb_ivar_get(range, id_end));
 348  
 349      if (beg < 0) {
 350          beg += len;
 351          if (beg < 0) goto out_of_range;
 352      }
 353      if (err == 0 || err == 2) {
 354          if (beg > len) goto out_of_range;
 355          if (end > len || (!EXCL(range) && end == len))
 356              end = len;
 357      }
 358      if (end < 0) {
 359          end += len;
 360          if (end < 0) {
 361              if (beg == 0 && end == -1 && !EXCL(range)) {
 362                  len = 0;
 363                  goto length_set;
 364              }
 365              goto out_of_range;
 366          }
 367      }
 368      len = end - beg;
 369      if (!EXCL(range)) len++;    /* include end point */
 370      if (len < 0) goto out_of_range;
 371  
 372    length_set:
 373      *begp = beg;
 374      *lenp = len;
 375  
 376      return Qtrue;
 377  
 378    out_of_range:
 379      if (err) {
 380          rb_raise(rb_eRangeError, "%ld..%s%ld out of range",
 381                   b, EXCL(range)? "." : "", e);
 382      }
 383      return Qnil;
 384  }
 385  
 386  static VALUE
 387  range_min(range)
 388      VALUE range;
 389  
 390  {
 391      VALUE b = rb_ivar_get(range, id_beg);
 392      VALUE e = rb_ivar_get(range, id_end);
 393  
 394      if (r_le(b, e)) return b;
 395      return e;
 396  }
 397  
 398  static VALUE
 399  range_max(range)
 400      VALUE range;
 401  {
 402      VALUE b = rb_ivar_get(range, id_beg);
 403      VALUE e = rb_ivar_get(range, id_end);
 404  
 405      if (r_gt(b, e)) return b;
 406      return e;
 407  }
 408  
 409  static VALUE
 410  range_to_s(range)
 411      VALUE range;
 412  {
 413      VALUE str, str2;
 414  
 415      str = rb_obj_as_string(rb_ivar_get(range, id_beg));
 416      str2 = rb_obj_as_string(rb_ivar_get(range, id_end));
 417      str = rb_str_dup(str);
 418      rb_str_cat(str, "...", EXCL(range)?3:2);
 419      rb_str_append(str, str2);
 420      OBJ_INFECT(str, str2);
 421  
 422      return str;
 423  }
 424  
 425  static VALUE
 426  range_inspect(range)
 427      VALUE range;
 428  {
 429      VALUE str, str2;
 430  
 431      str = rb_inspect(rb_ivar_get(range, id_beg));
 432      str2 = rb_inspect(rb_ivar_get(range, id_end));
 433      str = rb_str_dup(str);
 434      rb_str_cat(str, "...", EXCL(range)?3:2);
 435      rb_str_append(str, str2);
 436      OBJ_INFECT(str, str2);
 437  
 438      return str;
 439  }
 440  
 441  static void
 442  member_i(v, args)
 443      VALUE v;
 444      VALUE *args;
 445  {
 446      if (rb_equal(v, args[0])) {
 447          args[1] = Qtrue;
 448      }
 449  }
 450  
 451  static VALUE
 452  range_member(range, val)
 453      VALUE range, val;
 454  {
 455      VALUE beg, end;
 456      VALUE args[2];
 457  
 458      beg = rb_ivar_get(range, id_beg);
 459      end = rb_ivar_get(range, id_end);
 460  
 461      if (!rb_respond_to(beg, id_succ)) {
 462          rb_raise(rb_eTypeError, "cannot iterate from %s",
 463                   rb_class2name(CLASS_OF(beg)));
 464      }
 465      args[0] = val;
 466      args[1] = Qfalse;
 467      range_each_func(range, member_i, beg, end, args);
 468      return args[1];
 469  }
 470  
 471  static VALUE
 472  range_include(range, val)
 473      VALUE range, val;
 474  {
 475      VALUE beg, end;
 476  
 477      beg = rb_ivar_get(range, id_beg);
 478      end = rb_ivar_get(range, id_end);
 479      if (r_gt(beg, val)) return Qfalse;
 480      if (EXCL(range)) {
 481          if (r_lt(val, end)) return Qtrue;
 482      }
 483      else {
 484          if (r_le(val, end)) return Qtrue;
 485      }
 486      return Qfalse;
 487  }
 488  
 489  void
 490  Init_Range()
 491  {
 492      rb_cRange = rb_define_class("Range", rb_cObject);
 493      rb_include_module(rb_cRange, rb_mEnumerable);
 494      rb_define_method(rb_cRange, "initialize", range_initialize, -1);
 495      rb_define_method(rb_cRange, "==", range_eq, 1);
 496      rb_define_method(rb_cRange, "===", range_include, 1);
 497      rb_define_method(rb_cRange, "eql?", range_eql, 1);
 498      rb_define_method(rb_cRange, "hash", range_hash, 0);
 499      rb_define_method(rb_cRange, "each", range_each, 0);
 500      rb_define_method(rb_cRange, "step", range_step, -1);
 501      rb_define_method(rb_cRange, "first", range_first, 0);
 502      rb_define_method(rb_cRange, "last", range_last, 0);
 503      rb_define_method(rb_cRange, "begin", range_first, 0);
 504      rb_define_method(rb_cRange, "end", range_last, 0);
 505      rb_define_method(rb_cRange, "min", range_min, 0);
 506      rb_define_method(rb_cRange, "max", range_max, 0);
 507      rb_define_method(rb_cRange, "to_s", range_to_s, 0);
 508      rb_define_method(rb_cRange, "inspect", range_inspect, 0);
 509      rb_define_alias(rb_cRange,  "to_ary", "to_a");
 510  
 511      rb_define_method(rb_cRange, "exclude_end?", range_exclude_end_p, 0);
 512  
 513      rb_define_method(rb_cRange, "member?", range_member, 1);
 514      rb_define_method(rb_cRange, "include?", range_include, 1);
 515  
 516      id_cmp = rb_intern("<=>");
 517      id_succ = rb_intern("succ");
 518      id_beg = rb_intern("begin");
 519      id_end = rb_intern("end");
 520      id_excl = rb_intern("excl");
 521  }