mbed I/F binding for mruby

Dependents:   mruby_mbed_web mirb_mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers range.c Source File

range.c

00001 /*
00002 ** range.c - Range class
00003 **
00004 ** See Copyright Notice in mruby.h
00005 */
00006 
00007 #include "mruby.h"
00008 #include "mruby/class.h"
00009 #include "mruby/range.h"
00010 #include "mruby/string.h"
00011 #include "mruby/array.h"
00012 
00013 #define RANGE_CLASS (mrb_class_get(mrb, "Range"))
00014 
00015 static void
00016 range_check(mrb_state *mrb, mrb_value a, mrb_value b)
00017 {
00018   mrb_value ans;
00019   enum mrb_vtype ta;
00020   enum mrb_vtype tb;
00021 
00022   ta = mrb_type(a);
00023   tb = mrb_type(b);
00024   if ((ta == MRB_TT_FIXNUM || ta == MRB_TT_FLOAT) &&
00025       (tb == MRB_TT_FIXNUM || tb == MRB_TT_FLOAT)) {
00026     return;
00027   }
00028 
00029   ans =  mrb_funcall(mrb, a, "<=>", 1, b);
00030   if (mrb_nil_p(ans)) {
00031     /* can not be compared */
00032     mrb_raise(mrb, E_ARGUMENT_ERROR, "bad value for range");
00033   }
00034 }
00035 
00036 MRB_API mrb_value
00037 mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl)
00038 {
00039   struct RRange *r;
00040 
00041   range_check(mrb, beg, end);
00042   r = (struct RRange*)mrb_obj_alloc(mrb, MRB_TT_RANGE, RANGE_CLASS);
00043   r->edges = (mrb_range_edges *)mrb_malloc(mrb, sizeof(mrb_range_edges));
00044   r->edges->beg = beg;
00045   r->edges->end = end;
00046   r->excl = excl;
00047   return mrb_range_value(r);
00048 }
00049 
00050 /*
00051  *  call-seq:
00052  *     rng.first    => obj
00053  *     rng.begin    => obj
00054  *
00055  *  Returns the first object in <i>rng</i>.
00056  */
00057 mrb_value
00058 mrb_range_beg(mrb_state *mrb, mrb_value range)
00059 {
00060   struct RRange *r = mrb_range_ptr(range);
00061 
00062   return r->edges->beg;
00063 }
00064 
00065 /*
00066  *  call-seq:
00067  *     rng.end    => obj
00068  *     rng.last   => obj
00069  *
00070  *  Returns the object that defines the end of <i>rng</i>.
00071  *
00072  *     (1..10).end    #=> 10
00073  *     (1...10).end   #=> 10
00074  */
00075 
00076 mrb_value
00077 mrb_range_end(mrb_state *mrb, mrb_value range)
00078 {
00079   struct RRange *r = mrb_range_ptr(range);
00080 
00081   return r->edges->end;
00082 }
00083 
00084 /*
00085  *  call-seq:
00086  *     range.exclude_end?    => true or false
00087  *
00088  *  Returns <code>true</code> if <i>range</i> excludes its end value.
00089  */
00090 mrb_value
00091 mrb_range_excl(mrb_state *mrb, mrb_value range)
00092 {
00093   struct RRange *r = mrb_range_ptr(range);
00094 
00095   return mrb_bool_value(r->excl);
00096 }
00097 
00098 static void
00099 range_init(mrb_state *mrb, mrb_value range, mrb_value beg, mrb_value end, mrb_bool exclude_end)
00100 {
00101   struct RRange *r = mrb_range_ptr(range);
00102 
00103   range_check(mrb, beg, end);
00104   r->excl = exclude_end;
00105   if (!r->edges) {
00106     r->edges = (mrb_range_edges *)mrb_malloc(mrb, sizeof(mrb_range_edges));
00107   }
00108   r->edges->beg = beg;
00109   r->edges->end = end;
00110 }
00111 /*
00112  *  call-seq:
00113  *     Range.new(start, end, exclusive=false)    => range
00114  *
00115  *  Constructs a range using the given <i>start</i> and <i>end</i>. If the third
00116  *  parameter is omitted or is <code>false</code>, the <i>range</i> will include
00117  *  the end object; otherwise, it will be excluded.
00118  */
00119 
00120 mrb_value
00121 mrb_range_initialize(mrb_state *mrb, mrb_value range)
00122 {
00123   mrb_value beg, end;
00124   mrb_bool exclusive;
00125   int n;
00126 
00127   n = mrb_get_args(mrb, "oo|b", &beg, &end, &exclusive);
00128   if (n != 3) {
00129     exclusive = FALSE;
00130   }
00131   /* Ranges are immutable, so that they should be initialized only once. */
00132   range_init(mrb, range, beg, end, exclusive);
00133   return range;
00134 }
00135 /*
00136  *  call-seq:
00137  *     range == obj    => true or false
00138  *
00139  *  Returns <code>true</code> only if
00140  *  1) <i>obj</i> is a Range,
00141  *  2) <i>obj</i> has equivalent beginning and end items (by comparing them with <code>==</code>),
00142  *  3) <i>obj</i> has the same #exclude_end? setting as <i>rng</t>.
00143  *
00144  *    (0..2) == (0..2)            #=> true
00145  *    (0..2) == Range.new(0,2)    #=> true
00146  *    (0..2) == (0...2)           #=> false
00147  *
00148  */
00149 
00150 mrb_value
00151 mrb_range_eq(mrb_state *mrb, mrb_value range)
00152 {
00153   struct RRange *rr;
00154   struct RRange *ro;
00155   mrb_value obj;
00156 
00157   mrb_get_args(mrb, "o", &obj);
00158 
00159   if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value();
00160   if (!mrb_obj_is_instance_of(mrb, obj, mrb_obj_class(mrb, range))) { /* same class? */
00161     return mrb_false_value();
00162   }
00163 
00164   rr = mrb_range_ptr(range);
00165   ro = mrb_range_ptr(obj);
00166   if (!mrb_bool(mrb_funcall(mrb, rr->edges->beg, "==", 1, ro->edges->beg)) ||
00167       !mrb_bool(mrb_funcall(mrb, rr->edges->end, "==", 1, ro->edges->end)) ||
00168       rr->excl != ro->excl) {
00169     return mrb_false_value();
00170   }
00171   return mrb_true_value();
00172 }
00173 
00174 static mrb_bool
00175 r_le(mrb_state *mrb, mrb_value a, mrb_value b)
00176 {
00177   mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */
00178   /* output :a < b => -1, a = b =>  0, a > b => +1 */
00179 
00180   if (mrb_fixnum_p(r)) {
00181     mrb_int c = mrb_fixnum(r);
00182     if (c == 0 || c == -1) return TRUE;
00183   }
00184 
00185   return FALSE;
00186 }
00187 
00188 static mrb_bool
00189 r_gt(mrb_state *mrb, mrb_value a, mrb_value b)
00190 {
00191   mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b);
00192   /* output :a < b => -1, a = b =>  0, a > b => +1 */
00193 
00194   return mrb_fixnum_p(r) && mrb_fixnum(r) == 1;
00195 }
00196 
00197 static mrb_bool
00198 r_ge(mrb_state *mrb, mrb_value a, mrb_value b)
00199 {
00200   mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */
00201   /* output :a < b => -1, a = b =>  0, a > b => +1 */
00202 
00203   if (mrb_fixnum_p(r)) {
00204     mrb_int c = mrb_fixnum(r);
00205     if (c == 0 || c == 1) return TRUE;
00206   }
00207 
00208   return FALSE;
00209 }
00210 
00211 /*
00212  *  call-seq:
00213  *     range === obj       =>  true or false
00214  *     range.member?(val)  =>  true or false
00215  *     range.include?(val) =>  true or false
00216  *
00217  */
00218 mrb_value
00219 mrb_range_include(mrb_state *mrb, mrb_value range)
00220 {
00221   mrb_value val;
00222   struct RRange *r = mrb_range_ptr(range);
00223   mrb_value beg, end;
00224   mrb_bool include_p;
00225 
00226   mrb_get_args(mrb, "o", &val);
00227 
00228   beg = r->edges->beg;
00229   end = r->edges->end;
00230   include_p = r_le(mrb, beg, val) && /* beg <= val */
00231               ((r->excl && r_gt(mrb, end, val)) || /* end >  val */
00232               (r_ge(mrb, end, val))); /* end >= val */
00233 
00234   return mrb_bool_value(include_p);
00235 }
00236 
00237 static mrb_bool
00238 range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc)
00239 {
00240   mrb_int beg, end;
00241   struct RRange *r = mrb_range_ptr(range);
00242 
00243   if (mrb_type(range) != MRB_TT_RANGE) return FALSE;
00244 
00245   beg = mrb_int(mrb, r->edges->beg);
00246   end = mrb_int(mrb, r->edges->end);
00247 
00248   if (beg < 0) {
00249     beg += len;
00250     if (beg < 0) return FALSE;
00251   }
00252 
00253   if (trunc) {
00254     if (beg > len) return FALSE;
00255     if (end > len) end = len;
00256   }
00257 
00258   if (end < 0) end += len;
00259   if (!r->excl && (!trunc || end < len))
00260     end++;                      /* include end point */
00261   len = end - beg;
00262   if (len < 0) len = 0;
00263 
00264   *begp = beg;
00265   *lenp = len;
00266   return TRUE;
00267 }
00268 
00269 MRB_API mrb_bool
00270 mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len)
00271 {
00272   return range_beg_len(mrb, range, begp, lenp, len, TRUE);
00273 }
00274 
00275 /* 15.2.14.4.12(x) */
00276 /*
00277  * call-seq:
00278  *   rng.to_s   -> string
00279  *
00280  * Convert this range object to a printable form.
00281  */
00282 
00283 static mrb_value
00284 range_to_s(mrb_state *mrb, mrb_value range)
00285 {
00286   mrb_value str, str2;
00287   struct RRange *r = mrb_range_ptr(range);
00288 
00289   str  = mrb_obj_as_string(mrb, r->edges->beg);
00290   str2 = mrb_obj_as_string(mrb, r->edges->end);
00291   str  = mrb_str_dup(mrb, str);
00292   mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2);
00293   mrb_str_append(mrb, str, str2);
00294 
00295   return str;
00296 }
00297 
00298 /* 15.2.14.4.13(x) */
00299 /*
00300  * call-seq:
00301  *   rng.inspect  -> string
00302  *
00303  * Convert this range object to a printable form (using
00304  * <code>inspect</code> to convert the start and end
00305  * objects).
00306  */
00307 
00308 static mrb_value
00309 range_inspect(mrb_state *mrb, mrb_value range)
00310 {
00311   mrb_value str, str2;
00312   struct RRange *r = mrb_range_ptr(range);
00313 
00314   str  = mrb_inspect(mrb, r->edges->beg);
00315   str2 = mrb_inspect(mrb, r->edges->end);
00316   str  = mrb_str_dup(mrb, str);
00317   mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2);
00318   mrb_str_append(mrb, str, str2);
00319 
00320   return str;
00321 }
00322 
00323 /* 15.2.14.4.14(x) */
00324 /*
00325  *  call-seq:
00326  *     rng.eql?(obj)    -> true or false
00327  *
00328  *  Returns <code>true</code> only if <i>obj</i> is a Range, has equivalent
00329  *  beginning and end items (by comparing them with #eql?), and has the same
00330  *  #exclude_end? setting as <i>rng</i>.
00331  *
00332  *    (0..2).eql?(0..2)            #=> true
00333  *    (0..2).eql?(Range.new(0,2))  #=> true
00334  *    (0..2).eql?(0...2)           #=> false
00335  *
00336  */
00337 
00338 static mrb_value
00339 range_eql(mrb_state *mrb, mrb_value range)
00340 {
00341   mrb_value obj;
00342   struct RRange *r, *o;
00343 
00344   mrb_get_args(mrb, "o", &obj);
00345 
00346   if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value();
00347   if (!mrb_obj_is_kind_of(mrb, obj, RANGE_CLASS)) {
00348     return mrb_false_value();
00349   }
00350   if (mrb_type(obj) != MRB_TT_RANGE) return mrb_false_value();
00351 
00352   r = mrb_range_ptr(range);
00353   o = mrb_range_ptr(obj);
00354   if (!mrb_eql(mrb, r->edges->beg, o->edges->beg) ||
00355       !mrb_eql(mrb, r->edges->end, o->edges->end) ||
00356       (r->excl != o->excl)) {
00357     return mrb_false_value();
00358   }
00359   return mrb_true_value();
00360 }
00361 
00362 /* 15.2.14.4.15(x) */
00363 static mrb_value
00364 range_initialize_copy(mrb_state *mrb, mrb_value copy)
00365 {
00366   mrb_value src;
00367   struct RRange *r;
00368 
00369   mrb_get_args(mrb, "o", &src);
00370 
00371   if (mrb_obj_equal(mrb, copy, src)) return copy;
00372   if (!mrb_obj_is_instance_of(mrb, src, mrb_obj_class(mrb, copy))) {
00373     mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
00374   }
00375 
00376   r = mrb_range_ptr(src);
00377   range_init(mrb, copy, r->edges->beg, r->edges->end, r->excl);
00378 
00379   return copy;
00380 }
00381 
00382 mrb_value
00383 mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, const mrb_value *argv, mrb_value (*func)(mrb_state*, mrb_value, mrb_int))
00384 {
00385   mrb_int i, j, beg, len;
00386   mrb_value result;
00387   result = mrb_ary_new(mrb);
00388 
00389   for (i = 0; i < argc; ++i) {
00390     if (mrb_fixnum_p(argv[i])) {
00391       mrb_ary_push(mrb, result, func(mrb, obj, mrb_fixnum(argv[i])));
00392     }
00393     else if (range_beg_len(mrb, argv[i], &beg, &len, olen, FALSE)) {
00394       mrb_int const end = olen < beg + len ? olen : beg + len;
00395       for (j = beg; j < end; ++j) {
00396         mrb_ary_push(mrb, result, func(mrb, obj, j));
00397       }
00398 
00399       for (; j < beg + len; ++j) {
00400         mrb_ary_push(mrb, result, mrb_nil_value());
00401       }
00402     }
00403     else {
00404       mrb_raisef(mrb, E_TYPE_ERROR, "invalid values selector: %S", argv[i]);
00405     }
00406   }
00407 
00408   return result;
00409 }
00410 
00411 void
00412 mrb_init_range(mrb_state *mrb)
00413 {
00414   struct RClass *r;
00415 
00416   r = mrb_define_class(mrb, "Range", mrb->object_class);                                /* 15.2.14 */
00417   MRB_SET_INSTANCE_TT(r, MRB_TT_RANGE);
00418 
00419   mrb_define_method(mrb, r, "begin",           mrb_range_beg,         MRB_ARGS_NONE()); /* 15.2.14.4.3  */
00420   mrb_define_method(mrb, r, "end",             mrb_range_end,         MRB_ARGS_NONE()); /* 15.2.14.4.5  */
00421   mrb_define_method(mrb, r, "==",              mrb_range_eq,          MRB_ARGS_REQ(1)); /* 15.2.14.4.1  */
00422   mrb_define_method(mrb, r, "===",             mrb_range_include,     MRB_ARGS_REQ(1)); /* 15.2.14.4.2  */
00423   mrb_define_method(mrb, r, "exclude_end?",    mrb_range_excl,        MRB_ARGS_NONE()); /* 15.2.14.4.6  */
00424   mrb_define_method(mrb, r, "first",           mrb_range_beg,         MRB_ARGS_NONE()); /* 15.2.14.4.7  */
00425   mrb_define_method(mrb, r, "include?",        mrb_range_include,     MRB_ARGS_REQ(1)); /* 15.2.14.4.8  */
00426   mrb_define_method(mrb, r, "initialize",      mrb_range_initialize,  MRB_ARGS_ANY());  /* 15.2.14.4.9  */
00427   mrb_define_method(mrb, r, "last",            mrb_range_end,         MRB_ARGS_NONE()); /* 15.2.14.4.10 */
00428   mrb_define_method(mrb, r, "member?",         mrb_range_include,     MRB_ARGS_REQ(1)); /* 15.2.14.4.11 */
00429 
00430   mrb_define_method(mrb, r, "to_s",            range_to_s,            MRB_ARGS_NONE()); /* 15.2.14.4.12(x) */
00431   mrb_define_method(mrb, r, "inspect",         range_inspect,         MRB_ARGS_NONE()); /* 15.2.14.4.13(x) */
00432   mrb_define_method(mrb, r, "eql?",            range_eql,             MRB_ARGS_REQ(1)); /* 15.2.14.4.14(x) */
00433   mrb_define_method(mrb, r, "initialize_copy", range_initialize_copy, MRB_ARGS_REQ(1)); /* 15.2.14.4.15(x) */
00434 }
00435