mbed I/F binding for mruby

Dependents:   mruby_mbed_web mirb_mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers struct.c Source File

struct.c

00001 /*
00002 ** struct.c - Struct class
00003 **
00004 ** See Copyright Notice in mruby.h
00005 */
00006 
00007 #include <ctype.h>
00008 #include <string.h>
00009 #include "mruby.h"
00010 #include "mruby/array.h"
00011 #include "mruby/string.h"
00012 #include "mruby/class.h"
00013 #include "mruby/variable.h"
00014 #include "mruby/hash.h"
00015 #include "mruby/range.h"
00016 
00017 #define RSTRUCT_LEN(st) mrb_ary_ptr(st)->len
00018 #define RSTRUCT_PTR(st) mrb_ary_ptr(st)->ptr
00019 
00020 static struct RClass *
00021 struct_class(mrb_state *mrb)
00022 {
00023   return mrb_class_get(mrb, "Struct");
00024 }
00025 
00026 static inline mrb_value
00027 struct_ivar_get(mrb_state *mrb, mrb_value c, mrb_sym id)
00028 {
00029   struct RClass* kclass;
00030   struct RClass* sclass = struct_class(mrb);
00031   mrb_value ans;
00032 
00033   for (;;) {
00034     ans = mrb_iv_get(mrb, c, id);
00035     if (!mrb_nil_p(ans)) return ans;
00036     kclass = RCLASS_SUPER(c);
00037     if (kclass == 0 || kclass == sclass)
00038       return mrb_nil_value();
00039     c = mrb_obj_value(kclass);
00040   }
00041 }
00042 
00043 static mrb_value
00044 mrb_struct_s_members(mrb_state *mrb, mrb_value klass)
00045 {
00046   mrb_value members = struct_ivar_get(mrb, klass, mrb_intern_lit(mrb, "__members__"));
00047 
00048   if (mrb_nil_p(members)) {
00049     mrb_raise(mrb, E_TYPE_ERROR, "uninitialized struct");
00050   }
00051   if (!mrb_array_p(members)) {
00052     mrb_raise(mrb, E_TYPE_ERROR, "corrupted struct");
00053   }
00054   return members;
00055 }
00056 
00057 static mrb_value
00058 mrb_struct_members(mrb_state *mrb, mrb_value s)
00059 {
00060   mrb_value members = mrb_struct_s_members(mrb, mrb_obj_value(mrb_obj_class(mrb, s)));
00061   if (!strcmp(mrb_class_name(mrb, mrb_obj_class(mrb, s)), "Struct")) {
00062     if (RSTRUCT_LEN(s) != RARRAY_LEN(members)) {
00063       mrb_raisef(mrb, E_TYPE_ERROR,
00064                  "struct size differs (%S required %S given)",
00065                  mrb_fixnum_value(RARRAY_LEN(members)), mrb_fixnum_value(RSTRUCT_LEN(s)));
00066     }
00067   }
00068   return members;
00069 }
00070 
00071 static mrb_value
00072 mrb_struct_s_members_m(mrb_state *mrb, mrb_value klass)
00073 {
00074   mrb_value members, ary;
00075 
00076   members = mrb_struct_s_members(mrb, klass);
00077   ary = mrb_ary_new_capa(mrb, RARRAY_LEN(members));
00078   mrb_ary_replace(mrb, ary, members);
00079   return ary;
00080 }
00081 
00082 /* 15.2.18.4.6  */
00083 /*
00084  *  call-seq:
00085  *     struct.members    -> array
00086  *
00087  *  Returns an array of strings representing the names of the instance
00088  *  variables.
00089  *
00090  *     Customer = Struct.new(:name, :address, :zip)
00091  *     joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
00092  *     joe.members   #=> [:name, :address, :zip]
00093  */
00094 
00095 static mrb_value
00096 mrb_struct_members_m(mrb_state *mrb, mrb_value obj)
00097 {
00098   return mrb_struct_s_members_m(mrb, mrb_obj_value(mrb_obj_class(mrb, obj)));
00099 }
00100 
00101 static mrb_value
00102 mrb_struct_getmember(mrb_state *mrb, mrb_value obj, mrb_sym id)
00103 {
00104   mrb_value members, slot, *ptr;
00105   const mrb_value *ptr_members;
00106   mrb_int i, len;
00107 
00108   ptr = RSTRUCT_PTR(obj);
00109   members = mrb_struct_members(mrb, obj);
00110   ptr_members = RARRAY_PTR(members);
00111   slot = mrb_symbol_value(id);
00112   len = RARRAY_LEN(members);
00113   for (i=0; i<len; i++) {
00114     if (mrb_obj_equal(mrb, ptr_members[i], slot)) {
00115       return ptr[i];
00116     }
00117   }
00118   mrb_raisef(mrb, E_INDEX_ERROR, "`%S' is not a struct member", mrb_sym2str(mrb, id));
00119   return mrb_nil_value();       /* not reached */
00120 }
00121 
00122 static mrb_value
00123 mrb_struct_ref(mrb_state *mrb, mrb_value obj)
00124 {
00125   return mrb_struct_getmember(mrb, obj, mrb->c->ci->mid);
00126 }
00127 
00128 static mrb_value mrb_struct_ref0(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[0];}
00129 static mrb_value mrb_struct_ref1(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[1];}
00130 static mrb_value mrb_struct_ref2(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[2];}
00131 static mrb_value mrb_struct_ref3(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[3];}
00132 static mrb_value mrb_struct_ref4(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[4];}
00133 static mrb_value mrb_struct_ref5(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[5];}
00134 static mrb_value mrb_struct_ref6(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[6];}
00135 static mrb_value mrb_struct_ref7(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[7];}
00136 static mrb_value mrb_struct_ref8(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[8];}
00137 static mrb_value mrb_struct_ref9(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[9];}
00138 
00139 #define numberof(array) (int)(sizeof(array) / sizeof((array)[0]))
00140 #define N_REF_FUNC numberof(ref_func)
00141 
00142 static const mrb_func_t ref_func[] = {
00143   mrb_struct_ref0,
00144   mrb_struct_ref1,
00145   mrb_struct_ref2,
00146   mrb_struct_ref3,
00147   mrb_struct_ref4,
00148   mrb_struct_ref5,
00149   mrb_struct_ref6,
00150   mrb_struct_ref7,
00151   mrb_struct_ref8,
00152   mrb_struct_ref9,
00153 };
00154 
00155 static mrb_sym
00156 mrb_id_attrset(mrb_state *mrb, mrb_sym id)
00157 {
00158   const char *name;
00159   char *buf;
00160   mrb_int len;
00161   mrb_sym mid;
00162 
00163   name = mrb_sym2name_len(mrb, id, &len);
00164   buf = (char *)mrb_malloc(mrb, (size_t)len+2);
00165   memcpy(buf, name, (size_t)len);
00166   buf[len] = '=';
00167   buf[len+1] = '\0';
00168 
00169   mid = mrb_intern(mrb, buf, len+1);
00170   mrb_free(mrb, buf);
00171   return mid;
00172 }
00173 
00174 static mrb_value
00175 mrb_struct_set(mrb_state *mrb, mrb_value obj, mrb_value val)
00176 {
00177   const char *name;
00178   mrb_int i, len, slen;
00179   mrb_sym mid;
00180   mrb_value members, slot, *ptr;
00181   const mrb_value *ptr_members;
00182 
00183   /* get base id */
00184   name = mrb_sym2name_len(mrb, mrb->c->ci->mid, &slen);
00185   mid = mrb_intern(mrb, name, slen-1); /* omit last "=" */
00186 
00187   members = mrb_struct_members(mrb, obj);
00188   ptr_members = RARRAY_PTR(members);
00189   len = RARRAY_LEN(members);
00190   ptr = RSTRUCT_PTR(obj);
00191   for (i=0; i<len; i++) {
00192     slot = ptr_members[i];
00193     if (mrb_symbol(slot) == mid) {
00194       return ptr[i] = val;
00195     }
00196   }
00197   mrb_raisef(mrb, E_INDEX_ERROR, "`%S' is not a struct member", mrb_sym2str(mrb, mid));
00198   return mrb_nil_value();       /* not reached */
00199 }
00200 
00201 static mrb_value
00202 mrb_struct_set_m(mrb_state *mrb, mrb_value obj)
00203 {
00204   mrb_value val;
00205 
00206   mrb_get_args(mrb, "o", &val);
00207   return mrb_struct_set(mrb, obj, val);
00208 }
00209 
00210 static mrb_bool
00211 is_local_id(mrb_state *mrb, const char *name)
00212 {
00213   if (!name) return FALSE;
00214   return !ISUPPER(name[0]);
00215 }
00216 
00217 static mrb_bool
00218 is_const_id(mrb_state *mrb, const char *name)
00219 {
00220   if (!name) return FALSE;
00221   return ISUPPER(name[0]);
00222 }
00223 
00224 static void
00225 make_struct_define_accessors(mrb_state *mrb, mrb_value members, struct RClass *c)
00226 {
00227   const mrb_value *ptr_members = RARRAY_PTR(members);
00228   mrb_int i;
00229   mrb_int len = RARRAY_LEN(members);
00230   int ai = mrb_gc_arena_save(mrb);
00231 
00232   for (i=0; i<len; i++) {
00233     mrb_sym id = mrb_symbol(ptr_members[i]);
00234     const char *name = mrb_sym2name_len(mrb, id, NULL);
00235 
00236     if (is_local_id(mrb, name) || is_const_id(mrb, name)) {
00237       if (i < N_REF_FUNC) {
00238         mrb_define_method_id(mrb, c, id, ref_func[i], MRB_ARGS_NONE());
00239       }
00240       else {
00241         mrb_define_method_id(mrb, c, id, mrb_struct_ref, MRB_ARGS_NONE());
00242       }
00243       mrb_define_method_id(mrb, c, mrb_id_attrset(mrb, id), mrb_struct_set_m, MRB_ARGS_REQ(1));
00244       mrb_gc_arena_restore(mrb, ai);
00245     }
00246   }
00247 }
00248 
00249 static mrb_value
00250 make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass * klass)
00251 {
00252   mrb_value nstr;
00253   mrb_sym id;
00254   struct RClass *c;
00255 
00256   if (mrb_nil_p(name)) {
00257     c = mrb_class_new(mrb, klass);
00258   }
00259   else {
00260     /* old style: should we warn? */
00261     name = mrb_str_to_str(mrb, name);
00262     id = mrb_obj_to_sym(mrb, name);
00263     if (!is_const_id(mrb, mrb_sym2name_len(mrb, id, NULL))) {
00264       mrb_name_error(mrb, id, "identifier %S needs to be constant", name);
00265     }
00266     if (mrb_const_defined_at(mrb, mrb_obj_value(klass), id)) {
00267       mrb_warn(mrb, "redefining constant Struct::%S", name);
00268       /* ?rb_mod_remove_const(klass, mrb_sym2name(mrb, id)); */
00269     }
00270     c = mrb_define_class_under(mrb, klass, RSTRING_PTR(name), klass);
00271   }
00272   MRB_SET_INSTANCE_TT(c, MRB_TT_ARRAY);
00273   nstr = mrb_obj_value(c);
00274   mrb_iv_set(mrb, nstr, mrb_intern_lit(mrb, "__members__"), members);
00275 
00276   mrb_define_class_method(mrb, c, "new", mrb_instance_new, MRB_ARGS_ANY());
00277   mrb_define_class_method(mrb, c, "[]", mrb_instance_new, MRB_ARGS_ANY());
00278   mrb_define_class_method(mrb, c, "members", mrb_struct_s_members_m, MRB_ARGS_NONE());
00279   /* RSTRUCT(nstr)->basic.c->super = c->c; */
00280   make_struct_define_accessors(mrb, members, c);
00281   return nstr;
00282 }
00283 
00284 /* 15.2.18.3.1  */
00285 /*
00286  *  call-seq:
00287  *     Struct.new( [aString] [, aSym]+> )    -> StructClass
00288  *     StructClass.new(arg, ...)             -> obj
00289  *     StructClass[arg, ...]                 -> obj
00290  *
00291  *  Creates a new class, named by <i>aString</i>, containing accessor
00292  *  methods for the given symbols. If the name <i>aString</i> is
00293  *  omitted, an anonymous structure class will be created. Otherwise,
00294  *  the name of this struct will appear as a constant in class
00295  *  <code>Struct</code>, so it must be unique for all
00296  *  <code>Struct</code>s in the system and should start with a capital
00297  *  letter. Assigning a structure class to a constant effectively gives
00298  *  the class the name of the constant.
00299  *
00300  *  <code>Struct::new</code> returns a new <code>Class</code> object,
00301  *  which can then be used to create specific instances of the new
00302  *  structure. The number of actual parameters must be
00303  *  less than or equal to the number of attributes defined for this
00304  *  class; unset parameters default to <code>nil</code>.  Passing too many
00305  *  parameters will raise an <code>ArgumentError</code>.
00306  *
00307  *  The remaining methods listed in this section (class and instance)
00308  *  are defined for this generated class.
00309  *
00310  *     # Create a structure with a name in Struct
00311  *     Struct.new("Customer", :name, :address)    #=> Struct::Customer
00312  *     Struct::Customer.new("Dave", "123 Main")   #=> #<struct Struct::Customer name="Dave", address="123 Main">
00313  *
00314  *     # Create a structure named by its constant
00315  *     Customer = Struct.new(:name, :address)     #=> Customer
00316  *     Customer.new("Dave", "123 Main")           #=> #<struct Customer name="Dave", address="123 Main">
00317  */
00318 static mrb_value
00319 mrb_struct_s_def(mrb_state *mrb, mrb_value klass)
00320 {
00321   mrb_value name, rest;
00322   mrb_value *pargv;
00323   mrb_int argcnt;
00324   mrb_int i;
00325   mrb_value b, st;
00326   mrb_sym id;
00327   mrb_value *argv;
00328   mrb_int argc;
00329 
00330   name = mrb_nil_value();
00331   rest = mrb_nil_value();
00332   mrb_get_args(mrb, "*&", &argv, &argc, &b);
00333   if (argc == 0) { /* special case to avoid crash */
00334     rest = mrb_ary_new(mrb);
00335   }
00336   else {
00337     if (argc > 0) name = argv[0];
00338     if (argc > 1) rest = argv[1];
00339     if (mrb_array_p(rest)) {
00340       if (!mrb_nil_p(name) && mrb_symbol_p(name)) {
00341         /* 1stArgument:symbol -> name=nil rest=argv[0]-[n] */
00342         mrb_ary_unshift(mrb, rest, name);
00343         name = mrb_nil_value();
00344       }
00345     }
00346     else {
00347       pargv = &argv[1];
00348       argcnt = argc-1;
00349       if (!mrb_nil_p(name) && mrb_symbol_p(name)) {
00350         /* 1stArgument:symbol -> name=nil rest=argv[0]-[n] */
00351         name = mrb_nil_value();
00352         pargv = &argv[0];
00353         argcnt++;
00354       }
00355       rest = mrb_ary_new_from_values(mrb, argcnt, pargv);
00356     }
00357     for (i=0; i<RARRAY_LEN(rest); i++) {
00358       id = mrb_obj_to_sym(mrb, RARRAY_PTR(rest)[i]);
00359       mrb_ary_set(mrb, rest, i, mrb_symbol_value(id));
00360     }
00361   }
00362   st = make_struct(mrb, name, rest, struct_class(mrb));
00363   if (!mrb_nil_p(b)) {
00364     mrb_yield_with_class(mrb, b, 1, &st, st, mrb_class_ptr(klass));
00365   }
00366 
00367   return st;
00368 }
00369 
00370 static mrb_int
00371 num_members(mrb_state *mrb, struct RClass *klass)
00372 {
00373   mrb_value members;
00374 
00375   members = struct_ivar_get(mrb, mrb_obj_value(klass), mrb_intern_lit(mrb, "__members__"));
00376   if (!mrb_array_p(members)) {
00377     mrb_raise(mrb, E_TYPE_ERROR, "broken members");
00378   }
00379   return RARRAY_LEN(members);
00380 }
00381 
00382 /* 15.2.18.4.8  */
00383 /*
00384  */
00385 static mrb_value
00386 mrb_struct_initialize_withArg(mrb_state *mrb, mrb_int argc, mrb_value *argv, mrb_value self)
00387 {
00388   struct RClass *klass = mrb_obj_class(mrb, self);
00389   mrb_int i, n;
00390 
00391   n = num_members(mrb, klass);
00392   if (n < argc) {
00393     mrb_raise(mrb, E_ARGUMENT_ERROR, "struct size differs");
00394   }
00395 
00396   for (i = 0; i < argc; i++) {
00397     mrb_ary_set(mrb, self, i, argv[i]);
00398   }
00399   for (i = argc; i < n; i++) {
00400     mrb_ary_set(mrb, self, i, mrb_nil_value());
00401   }
00402   return self;
00403 }
00404 
00405 static mrb_value
00406 mrb_struct_initialize_m(mrb_state *mrb, /*int argc, mrb_value *argv,*/ mrb_value self)
00407 {
00408   mrb_value *argv;
00409   mrb_int argc;
00410 
00411   mrb_get_args(mrb, "*", &argv, &argc);
00412   return mrb_struct_initialize_withArg(mrb, argc, argv, self);
00413 }
00414 
00415 static mrb_value
00416 inspect_struct(mrb_state *mrb, mrb_value s, mrb_bool recur)
00417 {
00418   const char *cn = mrb_class_name(mrb, mrb_obj_class(mrb, s));
00419   mrb_value members, str = mrb_str_new_lit(mrb, "#<struct ");
00420   mrb_value *ptr;
00421   const mrb_value *ptr_members;
00422   mrb_int i, len;
00423 
00424   if (cn) {
00425     mrb_str_append(mrb, str, mrb_str_new_cstr(mrb, cn));
00426   }
00427   if (recur) {
00428     return mrb_str_cat_lit(mrb, str, ":...>");
00429   }
00430 
00431   members = mrb_struct_members(mrb, s);
00432   ptr_members = RARRAY_PTR(members);
00433   ptr = RSTRUCT_PTR(s);
00434   len = RSTRUCT_LEN(s);
00435   for (i=0; i<len; i++) {
00436     mrb_value slot;
00437     mrb_sym id;
00438     const char *name;
00439     mrb_int namelen;
00440 
00441     if (i > 0) {
00442       mrb_str_cat_lit(mrb, str, ", ");
00443     }
00444     else if (cn) {
00445       mrb_str_cat_lit(mrb, str, " ");
00446     }
00447     slot = ptr_members[i];
00448     id = mrb_symbol(slot);
00449     name = mrb_sym2name_len(mrb, id, &namelen);
00450     if (is_local_id(mrb, name) || is_const_id(mrb, name)) {
00451       mrb_str_append(mrb, str, mrb_str_new(mrb, name, namelen));
00452     }
00453     else {
00454       mrb_str_append(mrb, str, mrb_inspect(mrb, slot));
00455     }
00456     mrb_str_cat_lit(mrb, str, "=");
00457     mrb_str_append(mrb, str, mrb_inspect(mrb, ptr[i]));
00458   }
00459   mrb_str_cat_lit(mrb, str, ">");
00460 
00461   return str;
00462 }
00463 
00464 /*
00465  * call-seq:
00466  *   struct.to_s      -> string
00467  *   struct.inspect   -> string
00468  *
00469  * Describe the contents of this struct in a string.
00470  */
00471 static mrb_value
00472 mrb_struct_inspect(mrb_state *mrb, mrb_value s)
00473 {
00474   return inspect_struct(mrb, s, FALSE);
00475 }
00476 
00477 /* 15.2.18.4.9  */
00478 /* :nodoc: */
00479 static mrb_value
00480 mrb_struct_init_copy(mrb_state *mrb, mrb_value copy)
00481 {
00482   mrb_value s;
00483   mrb_int i, len;
00484 
00485   mrb_get_args(mrb, "o", &s);
00486 
00487   if (mrb_obj_equal(mrb, copy, s)) return copy;
00488   if (!mrb_obj_is_instance_of(mrb, s, mrb_obj_class(mrb, copy))) {
00489     mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
00490   }
00491   if (!mrb_array_p(s)) {
00492     mrb_raise(mrb, E_TYPE_ERROR, "corrupted struct");
00493   }
00494   if (RSTRUCT_LEN(copy) != RSTRUCT_LEN(s)) {
00495     mrb_raise(mrb, E_TYPE_ERROR, "struct size mismatch");
00496   }
00497   len = RSTRUCT_LEN(copy);
00498   for (i = 0; i < len; i++) {
00499     mrb_ary_set(mrb, copy, i, RSTRUCT_PTR(s)[i]);
00500   }
00501   return copy;
00502 }
00503 
00504 static mrb_value
00505 struct_aref_sym(mrb_state *mrb, mrb_value s, mrb_sym id)
00506 {
00507   mrb_value *ptr, members;
00508   const mrb_value *ptr_members;
00509   mrb_int i, len;
00510 
00511   ptr = RSTRUCT_PTR(s);
00512   members = mrb_struct_members(mrb, s);
00513   ptr_members = RARRAY_PTR(members);
00514   len = RARRAY_LEN(members);
00515   for (i=0; i<len; i++) {
00516     if (mrb_symbol(ptr_members[i]) == id) {
00517       return ptr[i];
00518     }
00519   }
00520   mrb_raisef(mrb, E_INDEX_ERROR, "no member '%S' in struct", mrb_sym2str(mrb, id));
00521   return mrb_nil_value();       /* not reached */
00522 }
00523 
00524 static mrb_value
00525 struct_aref_int(mrb_state *mrb, mrb_value s, mrb_int i)
00526 {
00527   if (i < 0) i = RSTRUCT_LEN(s) + i;
00528   if (i < 0)
00529       mrb_raisef(mrb, E_INDEX_ERROR,
00530                  "offset %S too small for struct(size:%S)",
00531                  mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
00532   if (RSTRUCT_LEN(s) <= i)
00533     mrb_raisef(mrb, E_INDEX_ERROR,
00534                "offset %S too large for struct(size:%S)",
00535                mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
00536   return RSTRUCT_PTR(s)[i];
00537 }
00538 
00539 /* 15.2.18.4.2  */
00540 /*
00541  *  call-seq:
00542  *     struct[symbol]    -> anObject
00543  *     struct[fixnum]    -> anObject
00544  *
00545  *  Attribute Reference---Returns the value of the instance variable
00546  *  named by <i>symbol</i>, or indexed (0..length-1) by
00547  *  <i>fixnum</i>. Will raise <code>NameError</code> if the named
00548  *  variable does not exist, or <code>IndexError</code> if the index is
00549  *  out of range.
00550  *
00551  *     Customer = Struct.new(:name, :address, :zip)
00552  *     joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
00553  *
00554  *     joe["name"]   #=> "Joe Smith"
00555  *     joe[:name]    #=> "Joe Smith"
00556  *     joe[0]        #=> "Joe Smith"
00557  */
00558 static mrb_value
00559 mrb_struct_aref(mrb_state *mrb, mrb_value s)
00560 {
00561   mrb_value idx;
00562 
00563   mrb_get_args(mrb, "o", &idx);
00564   if (mrb_string_p(idx)) {
00565     mrb_value sym = mrb_check_intern_str(mrb, idx);
00566 
00567     if (mrb_nil_p(sym)) {
00568       mrb_raisef(mrb, E_INDEX_ERROR, "no member '%S' in struct", idx);
00569     }
00570     idx = sym;
00571   }
00572   if (mrb_symbol_p(idx)) {
00573     return struct_aref_sym(mrb, s, mrb_symbol(idx));
00574   }
00575   return struct_aref_int(mrb, s, mrb_int(mrb, idx));
00576 }
00577 
00578 static mrb_value
00579 mrb_struct_aset_sym(mrb_state *mrb, mrb_value s, mrb_sym id, mrb_value val)
00580 {
00581   mrb_value members, *ptr;
00582   const mrb_value *ptr_members;
00583   mrb_int i, len;
00584 
00585   members = mrb_struct_members(mrb, s);
00586   len = RARRAY_LEN(members);
00587   if (RSTRUCT_LEN(s) != len) {
00588     mrb_raisef(mrb, E_TYPE_ERROR,
00589                "struct size differs (%S required %S given)",
00590                mrb_fixnum_value(len), mrb_fixnum_value(RSTRUCT_LEN(s)));
00591   }
00592   ptr = RSTRUCT_PTR(s);
00593   ptr_members = RARRAY_PTR(members);
00594   for (i=0; i<len; i++) {
00595     if (mrb_symbol(ptr_members[i]) == id) {
00596       ptr[i] = val;
00597       return val;
00598     }
00599   }
00600   mrb_raisef(mrb, E_INDEX_ERROR, "no member '%S' in struct", mrb_sym2str(mrb, id));
00601   return val;                   /* not reach */
00602 }
00603 
00604 /* 15.2.18.4.3  */
00605 /*
00606  *  call-seq:
00607  *     struct[symbol] = obj    -> obj
00608  *     struct[fixnum] = obj    -> obj
00609  *
00610  *  Attribute Assignment---Assigns to the instance variable named by
00611  *  <i>symbol</i> or <i>fixnum</i> the value <i>obj</i> and
00612  *  returns it. Will raise a <code>NameError</code> if the named
00613  *  variable does not exist, or an <code>IndexError</code> if the index
00614  *  is out of range.
00615  *
00616  *     Customer = Struct.new(:name, :address, :zip)
00617  *     joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
00618  *
00619  *     joe["name"] = "Luke"
00620  *     joe[:zip]   = "90210"
00621  *
00622  *     joe.name   #=> "Luke"
00623  *     joe.zip    #=> "90210"
00624  */
00625 
00626 static mrb_value
00627 mrb_struct_aset(mrb_state *mrb, mrb_value s)
00628 {
00629   mrb_int i;
00630   mrb_value idx;
00631   mrb_value val;
00632 
00633   mrb_get_args(mrb, "oo", &idx, &val);
00634 
00635   if (mrb_string_p(idx)) {
00636     mrb_value sym = mrb_check_intern_str(mrb, idx);
00637 
00638     if (mrb_nil_p(sym)) {
00639       mrb_raisef(mrb, E_INDEX_ERROR, "no member '%S' in struct", idx);
00640     }
00641     idx = sym;
00642   }
00643   if (mrb_symbol_p(idx)) {
00644     return mrb_struct_aset_sym(mrb, s, mrb_symbol(idx), val);
00645   }
00646 
00647   i = mrb_int(mrb, idx);
00648   if (i < 0) i = RSTRUCT_LEN(s) + i;
00649   if (i < 0) {
00650     mrb_raisef(mrb, E_INDEX_ERROR,
00651                "offset %S too small for struct(size:%S)",
00652                mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
00653   }
00654   if (RSTRUCT_LEN(s) <= i) {
00655     mrb_raisef(mrb, E_INDEX_ERROR,
00656                "offset %S too large for struct(size:%S)",
00657                mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
00658   }
00659   return RSTRUCT_PTR(s)[i] = val;
00660 }
00661 
00662 /* 15.2.18.4.1  */
00663 /*
00664  *  call-seq:
00665  *     struct == other_struct     -> true or false
00666  *
00667  *  Equality---Returns <code>true</code> if <i>other_struct</i> is
00668  *  equal to this one: they must be of the same class as generated by
00669  *  <code>Struct::new</code>, and the values of all instance variables
00670  *  must be equal (according to <code>Object#==</code>).
00671  *
00672  *     Customer = Struct.new(:name, :address, :zip)
00673  *     joe   = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
00674  *     joejr = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
00675  *     jane  = Customer.new("Jane Doe", "456 Elm, Anytown NC", 12345)
00676  *     joe == joejr   #=> true
00677  *     joe == jane    #=> false
00678  */
00679 
00680 static mrb_value
00681 mrb_struct_equal(mrb_state *mrb, mrb_value s)
00682 {
00683   mrb_value s2;
00684   mrb_value *ptr, *ptr2;
00685   mrb_int i, len;
00686   mrb_bool equal_p;
00687 
00688   mrb_get_args(mrb, "o", &s2);
00689   if (mrb_obj_equal(mrb, s, s2)) {
00690     equal_p = 1;
00691   }
00692   else if (!strcmp(mrb_class_name(mrb, mrb_obj_class(mrb, s)), "Struct") ||
00693            mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) {
00694     equal_p = 0;
00695   }
00696   else if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) {
00697     mrb_bug(mrb, "inconsistent struct"); /* should never happen */
00698     equal_p = 0; /* This substuture is just to suppress warnings. never called. */
00699   }
00700   else {
00701     ptr = RSTRUCT_PTR(s);
00702     ptr2 = RSTRUCT_PTR(s2);
00703     len = RSTRUCT_LEN(s);
00704     equal_p = 1;
00705     for (i=0; i<len; i++) {
00706       if (!mrb_equal(mrb, ptr[i], ptr2[i])) {
00707         equal_p = 0;
00708         break;
00709       }
00710     }
00711   }
00712 
00713   return mrb_bool_value(equal_p);
00714 }
00715 
00716 /* 15.2.18.4.12(x)  */
00717 /*
00718  * code-seq:
00719  *   struct.eql?(other)   -> true or false
00720  *
00721  * Two structures are equal if they are the same object, or if all their
00722  * fields are equal (using <code>eql?</code>).
00723  */
00724 static mrb_value
00725 mrb_struct_eql(mrb_state *mrb, mrb_value s)
00726 {
00727   mrb_value s2;
00728   mrb_value *ptr, *ptr2;
00729   mrb_int i, len;
00730   mrb_bool eql_p;
00731 
00732   mrb_get_args(mrb, "o", &s2);
00733   if (mrb_obj_equal(mrb, s, s2)) {
00734     eql_p = 1;
00735   }
00736   else if (strcmp(mrb_class_name(mrb, mrb_obj_class(mrb, s2)), "Struct") ||
00737            mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) {
00738     eql_p = 0;
00739   }
00740   else if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) {
00741     mrb_bug(mrb, "inconsistent struct"); /* should never happen */
00742     eql_p = 0; /* This substuture is just to suppress warnings. never called. */
00743   }
00744   else {
00745     ptr = RSTRUCT_PTR(s);
00746     ptr2 = RSTRUCT_PTR(s2);
00747     len = RSTRUCT_LEN(s);
00748     eql_p = 1;
00749     for (i=0; i<len; i++) {
00750       if (!mrb_eql(mrb, ptr[i], ptr2[i])) {
00751         eql_p = 0;
00752         break;
00753       }
00754     }
00755   }
00756 
00757   return mrb_bool_value(eql_p);
00758 }
00759 
00760 /*
00761  * call-seq:
00762  *    struct.length   -> Fixnum
00763  *    struct.size     -> Fixnum
00764  *
00765  * Returns number of struct members.
00766  */
00767 static mrb_value
00768 mrb_struct_len(mrb_state *mrb, mrb_value self)
00769 {
00770   return mrb_fixnum_value(RSTRUCT_LEN(self));
00771 }
00772 
00773 /*
00774  * call-seq:
00775  *    struct.to_a    -> array
00776  *    struct.values  -> array
00777  *
00778  * Create an array from struct values.
00779  */
00780 static mrb_value
00781 mrb_struct_to_a(mrb_state *mrb, mrb_value self)
00782 {
00783   return mrb_ary_new_from_values(mrb, RSTRUCT_LEN(self), RSTRUCT_PTR(self));
00784 }
00785 
00786 /*
00787  * call-seq:
00788  *    struct.to_h -> hash
00789  *
00790  * Create a hash from member names and struct values.
00791  */
00792 static mrb_value
00793 mrb_struct_to_h(mrb_state *mrb, mrb_value self)
00794 {
00795   mrb_value members, ret;
00796   mrb_int i;
00797 
00798   members = mrb_struct_s_members(mrb, mrb_obj_value(mrb_class(mrb, self)));
00799   ret = mrb_hash_new_capa(mrb, RARRAY_LEN(members));
00800 
00801   for (i = 0; i < RARRAY_LEN(members); ++i) {
00802     mrb_hash_set(mrb, ret, RARRAY_PTR(members)[i], RSTRUCT_PTR(self)[i]);
00803   }
00804 
00805   return ret;
00806 }
00807 
00808 static mrb_value
00809 mrb_struct_values_at(mrb_state *mrb, mrb_value self)
00810 {
00811   mrb_int argc;
00812   mrb_value *argv;
00813 
00814   mrb_get_args(mrb, "*", &argv, &argc);
00815 
00816   return mrb_get_values_at(mrb, self, RSTRUCT_LEN(self), argc, argv, struct_aref_int);
00817 }
00818 
00819 /*
00820  *  A <code>Struct</code> is a convenient way to bundle a number of
00821  *  attributes together, using accessor methods, without having to write
00822  *  an explicit class.
00823  *
00824  *  The <code>Struct</code> class is a generator of specific classes,
00825  *  each one of which is defined to hold a set of variables and their
00826  *  accessors. In these examples, we'll call the generated class
00827  *  ``<i>Customer</i>Class,'' and we'll show an example instance of that
00828  *  class as ``<i>Customer</i>Inst.''
00829  *
00830  *  In the descriptions that follow, the parameter <i>symbol</i> refers
00831  *  to a symbol, which is either a quoted string or a
00832  *  <code>Symbol</code> (such as <code>:name</code>).
00833  */
00834 void
00835 mrb_mruby_struct_gem_init(mrb_state* mrb)
00836 {
00837   struct RClass *st;
00838   st = mrb_define_class(mrb, "Struct",  mrb->object_class);
00839 
00840   mrb_define_class_method(mrb, st, "new",             mrb_struct_s_def,       MRB_ARGS_ANY());  /* 15.2.18.3.1  */
00841 
00842   mrb_define_method(mrb, st,       "==",              mrb_struct_equal,       MRB_ARGS_REQ(1)); /* 15.2.18.4.1  */
00843   mrb_define_method(mrb, st,       "[]",              mrb_struct_aref,        MRB_ARGS_REQ(1)); /* 15.2.18.4.2  */
00844   mrb_define_method(mrb, st,       "[]=",             mrb_struct_aset,        MRB_ARGS_REQ(2)); /* 15.2.18.4.3  */
00845   mrb_define_method(mrb, st,       "members",         mrb_struct_members_m,   MRB_ARGS_NONE()); /* 15.2.18.4.6  */
00846   mrb_define_method(mrb, st,       "initialize",      mrb_struct_initialize_m,MRB_ARGS_ANY());  /* 15.2.18.4.8  */
00847   mrb_define_method(mrb, st,       "initialize_copy", mrb_struct_init_copy,   MRB_ARGS_REQ(1)); /* 15.2.18.4.9  */
00848   mrb_define_method(mrb, st,       "inspect",         mrb_struct_inspect,     MRB_ARGS_NONE()); /* 15.2.18.4.10(x)  */
00849   mrb_define_alias(mrb, st,        "to_s", "inspect");                                      /* 15.2.18.4.11(x)  */
00850   mrb_define_method(mrb, st,       "eql?",            mrb_struct_eql,         MRB_ARGS_REQ(1)); /* 15.2.18.4.12(x)  */
00851 
00852   mrb_define_method(mrb, st,        "size",           mrb_struct_len,         MRB_ARGS_NONE());
00853   mrb_define_method(mrb, st,        "length",         mrb_struct_len,         MRB_ARGS_NONE());
00854   mrb_define_method(mrb, st,        "to_a",           mrb_struct_to_a,        MRB_ARGS_NONE());
00855   mrb_define_method(mrb, st,        "values",         mrb_struct_to_a,        MRB_ARGS_NONE());
00856   mrb_define_method(mrb, st,        "to_h",           mrb_struct_to_h,        MRB_ARGS_NONE());
00857   mrb_define_method(mrb, st,        "values_at",      mrb_struct_values_at,   MRB_ARGS_NONE());
00858 }
00859 
00860 void
00861 mrb_mruby_struct_gem_final(mrb_state* mrb)
00862 {
00863 }
00864