/* ** data.c - Data class ** ** See Copyright Notice in mruby.h */ #include #include #include #include #include #include #include #include #include #include #define RDATA_LEN(st) RARRAY_LEN(st) #define RDATA_PTR(st) RARRAY_PTR(st) #define data_p(o) (mrb_type(o) == MRB_TT_STRUCT) static struct RClass * data_class(mrb_state *mrb) { return mrb_class_get_id(mrb, MRB_SYM(Data)); } static void data_corrupted(mrb_state *mrb) { mrb_raise(mrb, E_TYPE_ERROR, "corrupted data"); } static mrb_value data_s_members(mrb_state *mrb, struct RClass *c) { struct RClass* sclass = data_class(mrb); mrb_value mem; for (;;) { mem = mrb_iv_get(mrb, mrb_obj_value(c), MRB_SYM(__members__)); if (!mrb_nil_p(mem)) { if (!mrb_array_p(mem)) { data_corrupted(mrb); } return mem; } c = c->super; if (c == sclass || c == 0) { mrb_raise(mrb, E_TYPE_ERROR, "uninitialized data"); } } } static mrb_value data_members(mrb_state *mrb, mrb_value obj) { if (!data_p(obj) || RDATA_LEN(obj) == 0) { data_corrupted(mrb); } mrb_value members = data_s_members(mrb, mrb_obj_class(mrb, obj)); if (RDATA_LEN(obj) != RARRAY_LEN(members)) { mrb_raisef(mrb, E_TYPE_ERROR, "data size differs (%i required %i given)", RARRAY_LEN(members), RDATA_LEN(obj)); } return members; } static mrb_value mrb_data_s_members(mrb_state *mrb, mrb_value klass) { mrb_value members = data_s_members(mrb, mrb_class_ptr(klass)); return mrb_ary_new_from_values(mrb, RARRAY_LEN(members), RARRAY_PTR(members)); } /* * call-seq: * data.members -> array * * Returns an array of strings representing the names of the instance * variables. * * Customer = Data.define(:name, :address, :zip) * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) * joe.members #=> [:name, :address, :zip] */ static mrb_value mrb_data_members(mrb_state *mrb, mrb_value obj) { return mrb_data_s_members(mrb, mrb_obj_value(mrb_obj_class(mrb, obj))); } static mrb_value mrb_data_ref(mrb_state *mrb, mrb_value obj) { mrb_int i = mrb_integer(mrb_proc_cfunc_env_get(mrb, 0)); mrb_value *ptr = RDATA_PTR(obj); if (!ptr) return mrb_nil_value(); return ptr[i]; } static void make_data_define_accessors(mrb_state *mrb, mrb_value members, struct RClass *c) { const mrb_value *ptr_members = RARRAY_PTR(members); mrb_int len = RARRAY_LEN(members); int ai = mrb_gc_arena_save(mrb); for (mrb_int i=0; ic->ci->nk > 0) { mrb_value tmp = mrb_str_new(mrb, NULL, sizeof(mrb_sym)*n); mrb_sym *knames = (mrb_sym*)RSTRING_PTR(tmp); mrb_value m = mrb_ary_new_capa(mrb, n); vals = RARRAY_PTR(m); for (mrb_int i=0; ibasic.c->super = c->c; */ make_data_define_accessors(mrb, members, c); return data; } /* * call-seq: * DataClass.new(arg, ...) -> obj * * Data::define returns a new Class object, * which can then be used to create specific instances of the new * data structure. The number of actual parameters must be * equal to the number of attributes defined for this class. * Passing too many or too less parameters will raise an * ArgumentError. * * The remaining methods listed in this section (class and instance) * are defined for this generated class. * * # Create a structure named by its constant * Customer = Data.define(:name, :address) #=> Customer * Customer.new("Dave", "123 Main") #=> # */ static mrb_value mrb_data_s_def(mrb_state *mrb, mrb_value klass) { mrb_value rest; mrb_value b, data; mrb_sym id; const mrb_value *argv; mrb_int argc; mrb_get_args(mrb, "*&", &argv, &argc, &b); rest = mrb_ary_new_from_values(mrb, argc, argv); for (mrb_int i=0; i true or false * * Equality---Returns true if other_data is * equal to this one: they must be of the same class as generated by * Data::define, and all values of must be equal * (according to Object#==). * * Customer = Data.define(:name, :address, :zip) * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) * joe2 = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) * jane = Customer.new("Jane Doe", "456 Elm, Anytown NC", 12345) * joe == joe2 #=> true * joe == jane #=> false */ static mrb_value mrb_data_equal(mrb_state *mrb, mrb_value s) { mrb_value s2 = mrb_get_arg1(mrb); mrb_value *ptr, *ptr2; mrb_int len; if (mrb_obj_equal(mrb, s, s2)) { return mrb_true_value(); } if (mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) { return mrb_false_value(); } if (RDATA_LEN(s) != RDATA_LEN(s2)) { return mrb_false_value(); } ptr = RDATA_PTR(s); ptr2 = RDATA_PTR(s2); len = RDATA_LEN(s); for (mrb_int i=0; i true or false * * Two structures are equal if they are the same object, or if all their * fields are equal (using Object#eql?). */ static mrb_value mrb_data_eql(mrb_state *mrb, mrb_value s) { mrb_value s2 = mrb_get_arg1(mrb); mrb_value *ptr, *ptr2; mrb_int len; if (mrb_obj_equal(mrb, s, s2)) { return mrb_true_value(); } if (mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) { return mrb_false_value(); } if (RDATA_LEN(s) != RDATA_LEN(s2)) { return mrb_false_value(); } ptr = RDATA_PTR(s); ptr2 = RDATA_PTR(s2); len = RDATA_LEN(s); for (mrb_int i=0; i hash * * Create a hash from member names and values. */ static mrb_value mrb_data_to_h(mrb_state *mrb, mrb_value self) { mrb_value members, ret; mrb_value *mems, *vals; members = data_members(mrb, self); mems = RARRAY_PTR(members); vals = RDATA_PTR(self); ret = mrb_hash_new_capa(mrb, RARRAY_LEN(members)); mrb_int len = RARRAY_LEN(members); for (mrb_int i=0; i string * data.inspect -> string * * Returns a string representation of Data */ static mrb_value mrb_data_to_s(mrb_state *mrb, mrb_value self) { mrb_value members, ret, cname; mrb_value *mems, *vals; mrb_int mlen; members = data_members(mrb, self); mlen = RARRAY_LEN(members); mems = RARRAY_PTR(members); vals = RDATA_PTR(self); ret = mrb_str_new_lit(mrb, "#0) mrb_str_cat_lit(mrb, ret, ", "); mrb_str_cat(mrb, ret, name, len); mrb_str_cat_lit(mrb, ret, "="); mrb_str_cat_str(mrb, ret, mrb_inspect(mrb, vals[i])); mrb_gc_arena_restore(mrb, ai); } mrb_str_cat_lit(mrb, ret, ">"); return ret; } /* * A Data is a convenient way to bundle a number of * attributes together, using accessor methods, without having to write * an explicit class. * * The Data class is a generator of specific classes, * each one of which is defined to hold a set of variables and their * accessors. In these examples, we'll call the generated class * "CustomerClass," and we'll show an example instance of that * class as "CustomerInst." * * In the descriptions that follow, the parameter symbol refers * to a symbol (such as :name). */ void mrb_mruby_data_gem_init(mrb_state* mrb) { struct RClass *d; d = mrb_define_class(mrb, "Data", mrb->object_class); MRB_SET_INSTANCE_TT(d, MRB_TT_UNDEF); mrb_undef_class_method(mrb, d, "new"); mrb_define_class_method(mrb, d, "define", mrb_data_s_def, MRB_ARGS_ANY()); mrb_define_method(mrb, d, "==", mrb_data_equal, MRB_ARGS_REQ(1)); mrb_define_method(mrb, d, "members", mrb_data_members, MRB_ARGS_NONE()); mrb_define_method(mrb, d, "initialize", mrb_data_initialize, MRB_ARGS_ANY()); mrb_define_method(mrb, d, "initialize_copy", mrb_data_init_copy, MRB_ARGS_ANY()); mrb_define_method(mrb, d, "eql?", mrb_data_eql, MRB_ARGS_REQ(1)); mrb_define_method(mrb, d, "to_h", mrb_data_to_h, MRB_ARGS_NONE()); mrb_define_method(mrb, d, "to_s", mrb_data_to_s, MRB_ARGS_NONE()); mrb_define_method(mrb, d, "inspect", mrb_data_to_s, MRB_ARGS_NONE()); } void mrb_mruby_data_gem_final(mrb_state* mrb) { }