get rid of shadowing variables (mrbgems)
[mruby.git] / mrbgems / mruby-struct / src / struct.c
blob415a8a1d6972289e85988c5e53c3514ac7292701
1 /*
2 ** struct.c - Struct class
3 **
4 ** See Copyright Notice in mruby.h
5 */
7 #include <ctype.h>
8 #include <string.h>
9 #include "mruby.h"
10 #include "mruby/array.h"
11 #include "mruby/string.h"
12 #include "mruby/class.h"
13 #include "mruby/variable.h"
14 #include "mruby/hash.h"
15 #include "mruby/range.h"
17 #define RSTRUCT_LEN(st) RARRAY_LEN(st)
18 #define RSTRUCT_PTR(st) RARRAY_PTR(st)
20 static struct RClass *
21 struct_class(mrb_state *mrb)
23 return mrb_class_get(mrb, "Struct");
26 static inline mrb_value
27 struct_ivar_get(mrb_state *mrb, mrb_value c, mrb_sym id)
29 struct RClass* kclass;
30 struct RClass* sclass = struct_class(mrb);
31 mrb_value ans;
33 for (;;) {
34 ans = mrb_iv_get(mrb, c, id);
35 if (!mrb_nil_p(ans)) return ans;
36 kclass = RCLASS_SUPER(c);
37 if (kclass == 0 || kclass == sclass)
38 return mrb_nil_value();
39 c = mrb_obj_value(kclass);
43 static mrb_value
44 mrb_struct_s_members(mrb_state *mrb, mrb_value klass)
46 mrb_value members = struct_ivar_get(mrb, klass, mrb_intern_lit(mrb, "__members__"));
48 if (mrb_nil_p(members)) {
49 mrb_raise(mrb, E_TYPE_ERROR, "uninitialized struct");
51 if (!mrb_array_p(members)) {
52 mrb_raise(mrb, E_TYPE_ERROR, "corrupted struct");
54 return members;
57 static mrb_value
58 mrb_struct_members(mrb_state *mrb, mrb_value s)
60 mrb_value members = mrb_struct_s_members(mrb, mrb_obj_value(mrb_obj_class(mrb, s)));
61 if (!strcmp(mrb_class_name(mrb, mrb_obj_class(mrb, s)), "Struct")) {
62 if (RSTRUCT_LEN(s) != RARRAY_LEN(members)) {
63 mrb_raisef(mrb, E_TYPE_ERROR,
64 "struct size differs (%S required %S given)",
65 mrb_fixnum_value(RARRAY_LEN(members)), mrb_fixnum_value(RSTRUCT_LEN(s)));
68 return members;
71 static mrb_value
72 mrb_struct_s_members_m(mrb_state *mrb, mrb_value klass)
74 mrb_value members, ary;
75 mrb_value *p, *pend;
77 members = mrb_struct_s_members(mrb, klass);
78 ary = mrb_ary_new_capa(mrb, RARRAY_LEN(members));
79 p = RARRAY_PTR(members); pend = p + RARRAY_LEN(members);
80 while (p < pend) {
81 mrb_ary_push(mrb, ary, *p);
82 p++;
84 return ary;
87 /* 15.2.18.4.6 */
89 * call-seq:
90 * struct.members -> array
92 * Returns an array of strings representing the names of the instance
93 * variables.
95 * Customer = Struct.new(:name, :address, :zip)
96 * joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
97 * joe.members #=> [:name, :address, :zip]
100 static mrb_value
101 mrb_struct_members_m(mrb_state *mrb, mrb_value obj)
103 return mrb_struct_s_members_m(mrb, mrb_obj_value(mrb_obj_class(mrb, obj)));
106 static mrb_value
107 mrb_struct_getmember(mrb_state *mrb, mrb_value obj, mrb_sym id)
109 mrb_value members, slot, *ptr, *ptr_members;
110 mrb_int i, len;
112 ptr = RSTRUCT_PTR(obj);
113 members = mrb_struct_members(mrb, obj);
114 ptr_members = RARRAY_PTR(members);
115 slot = mrb_symbol_value(id);
116 len = RARRAY_LEN(members);
117 for (i=0; i<len; i++) {
118 if (mrb_obj_equal(mrb, ptr_members[i], slot)) {
119 return ptr[i];
122 mrb_raisef(mrb, E_INDEX_ERROR, "`%S' is not a struct member", mrb_sym2str(mrb, id));
123 return mrb_nil_value(); /* not reached */
126 static mrb_value
127 mrb_struct_ref(mrb_state *mrb, mrb_value obj)
129 return mrb_struct_getmember(mrb, obj, mrb->c->ci->mid);
132 static mrb_value mrb_struct_ref0(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[0];}
133 static mrb_value mrb_struct_ref1(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[1];}
134 static mrb_value mrb_struct_ref2(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[2];}
135 static mrb_value mrb_struct_ref3(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[3];}
136 static mrb_value mrb_struct_ref4(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[4];}
137 static mrb_value mrb_struct_ref5(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[5];}
138 static mrb_value mrb_struct_ref6(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[6];}
139 static mrb_value mrb_struct_ref7(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[7];}
140 static mrb_value mrb_struct_ref8(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[8];}
141 static mrb_value mrb_struct_ref9(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[9];}
143 #define numberof(array) (int)(sizeof(array) / sizeof((array)[0]))
144 #define N_REF_FUNC numberof(ref_func)
146 static const mrb_func_t ref_func[] = {
147 mrb_struct_ref0,
148 mrb_struct_ref1,
149 mrb_struct_ref2,
150 mrb_struct_ref3,
151 mrb_struct_ref4,
152 mrb_struct_ref5,
153 mrb_struct_ref6,
154 mrb_struct_ref7,
155 mrb_struct_ref8,
156 mrb_struct_ref9,
159 static mrb_sym
160 mrb_id_attrset(mrb_state *mrb, mrb_sym id)
162 const char *name;
163 char *buf;
164 mrb_int len;
165 mrb_sym mid;
167 name = mrb_sym2name_len(mrb, id, &len);
168 buf = (char *)mrb_malloc(mrb, (size_t)len+2);
169 memcpy(buf, name, (size_t)len);
170 buf[len] = '=';
171 buf[len+1] = '\0';
173 mid = mrb_intern(mrb, buf, len+1);
174 mrb_free(mrb, buf);
175 return mid;
178 static mrb_value
179 mrb_struct_set(mrb_state *mrb, mrb_value obj, mrb_value val)
181 const char *name;
182 mrb_int i, len, slen;
183 mrb_sym mid;
184 mrb_value members, slot, *ptr, *ptr_members;
186 /* get base id */
187 name = mrb_sym2name_len(mrb, mrb->c->ci->mid, &slen);
188 mid = mrb_intern(mrb, name, slen-1); /* omit last "=" */
190 members = mrb_struct_members(mrb, obj);
191 ptr_members = RARRAY_PTR(members);
192 len = RARRAY_LEN(members);
193 ptr = RSTRUCT_PTR(obj);
194 for (i=0; i<len; i++) {
195 slot = ptr_members[i];
196 if (mrb_symbol(slot) == mid) {
197 return ptr[i] = val;
200 mrb_raisef(mrb, E_INDEX_ERROR, "`%S' is not a struct member", mrb_sym2str(mrb, mid));
201 return mrb_nil_value(); /* not reached */
204 static mrb_value
205 mrb_struct_set_m(mrb_state *mrb, mrb_value obj)
207 mrb_value val;
209 mrb_get_args(mrb, "o", &val);
210 return mrb_struct_set(mrb, obj, val);
213 static mrb_bool
214 is_local_id(mrb_state *mrb, const char *name)
216 if (!name) return FALSE;
217 return !ISUPPER(name[0]);
220 static mrb_bool
221 is_const_id(mrb_state *mrb, const char *name)
223 if (!name) return FALSE;
224 return ISUPPER(name[0]);
227 static void
228 make_struct_define_accessors(mrb_state *mrb, mrb_value members, struct RClass *c)
230 mrb_value *ptr_members = RARRAY_PTR(members);
231 mrb_int i;
232 mrb_int len = RARRAY_LEN(members);
233 int ai = mrb_gc_arena_save(mrb);
235 for (i=0; i<len; i++) {
236 mrb_sym id = mrb_symbol(ptr_members[i]);
237 const char *name = mrb_sym2name_len(mrb, id, NULL);
239 if (is_local_id(mrb, name) || is_const_id(mrb, name)) {
240 if (i < N_REF_FUNC) {
241 mrb_define_method_id(mrb, c, id, ref_func[i], MRB_ARGS_NONE());
243 else {
244 mrb_define_method_id(mrb, c, id, mrb_struct_ref, MRB_ARGS_NONE());
246 mrb_define_method_id(mrb, c, mrb_id_attrset(mrb, id), mrb_struct_set_m, MRB_ARGS_REQ(1));
247 mrb_gc_arena_restore(mrb, ai);
252 static mrb_value
253 make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass * klass)
255 mrb_value nstr;
256 mrb_sym id;
257 struct RClass *c;
259 if (mrb_nil_p(name)) {
260 c = mrb_class_new(mrb, klass);
262 else {
263 /* old style: should we warn? */
264 name = mrb_str_to_str(mrb, name);
265 id = mrb_obj_to_sym(mrb, name);
266 if (!is_const_id(mrb, mrb_sym2name_len(mrb, id, NULL))) {
267 mrb_name_error(mrb, id, "identifier %S needs to be constant", name);
269 if (mrb_const_defined_at(mrb, klass, id)) {
270 mrb_warn(mrb, "redefining constant Struct::%S", name);
271 /* ?rb_mod_remove_const(klass, mrb_sym2name(mrb, id)); */
273 c = mrb_define_class_under(mrb, klass, RSTRING_PTR(name), klass);
275 MRB_SET_INSTANCE_TT(c, MRB_TT_ARRAY);
276 nstr = mrb_obj_value(c);
277 mrb_iv_set(mrb, nstr, mrb_intern_lit(mrb, "__members__"), members);
279 mrb_define_class_method(mrb, c, "new", mrb_instance_new, MRB_ARGS_ANY());
280 mrb_define_class_method(mrb, c, "[]", mrb_instance_new, MRB_ARGS_ANY());
281 mrb_define_class_method(mrb, c, "members", mrb_struct_s_members_m, MRB_ARGS_NONE());
282 /* RSTRUCT(nstr)->basic.c->super = c->c; */
283 make_struct_define_accessors(mrb, members, c);
284 return nstr;
287 /* 15.2.18.3.1 */
289 * call-seq:
290 * Struct.new( [aString] [, aSym]+> ) -> StructClass
291 * StructClass.new(arg, ...) -> obj
292 * StructClass[arg, ...] -> obj
294 * Creates a new class, named by <i>aString</i>, containing accessor
295 * methods for the given symbols. If the name <i>aString</i> is
296 * omitted, an anonymous structure class will be created. Otherwise,
297 * the name of this struct will appear as a constant in class
298 * <code>Struct</code>, so it must be unique for all
299 * <code>Struct</code>s in the system and should start with a capital
300 * letter. Assigning a structure class to a constant effectively gives
301 * the class the name of the constant.
303 * <code>Struct::new</code> returns a new <code>Class</code> object,
304 * which can then be used to create specific instances of the new
305 * structure. The number of actual parameters must be
306 * less than or equal to the number of attributes defined for this
307 * class; unset parameters default to <code>nil</code>. Passing too many
308 * parameters will raise an <code>ArgumentError</code>.
310 * The remaining methods listed in this section (class and instance)
311 * are defined for this generated class.
313 * # Create a structure with a name in Struct
314 * Struct.new("Customer", :name, :address) #=> Struct::Customer
315 * Struct::Customer.new("Dave", "123 Main") #=> #<struct Struct::Customer name="Dave", address="123 Main">
317 * # Create a structure named by its constant
318 * Customer = Struct.new(:name, :address) #=> Customer
319 * Customer.new("Dave", "123 Main") #=> #<struct Customer name="Dave", address="123 Main">
321 static mrb_value
322 mrb_struct_s_def(mrb_state *mrb, mrb_value klass)
324 mrb_value name, rest;
325 mrb_value *pargv;
326 mrb_int argcnt;
327 mrb_int i;
328 mrb_value b, st;
329 mrb_sym id;
330 mrb_value *argv;
331 mrb_int argc;
333 name = mrb_nil_value();
334 rest = mrb_nil_value();
335 mrb_get_args(mrb, "*&", &argv, &argc, &b);
336 if (argc == 0) { /* special case to avoid crash */
337 rest = mrb_ary_new(mrb);
339 else {
340 if (argc > 0) name = argv[0];
341 if (argc > 1) rest = argv[1];
342 if (mrb_array_p(rest)) {
343 if (!mrb_nil_p(name) && mrb_symbol_p(name)) {
344 /* 1stArgument:symbol -> name=nil rest=argv[0]-[n] */
345 mrb_ary_unshift(mrb, rest, name);
346 name = mrb_nil_value();
349 else {
350 pargv = &argv[1];
351 argcnt = argc-1;
352 if (!mrb_nil_p(name) && mrb_symbol_p(name)) {
353 /* 1stArgument:symbol -> name=nil rest=argv[0]-[n] */
354 name = mrb_nil_value();
355 pargv = &argv[0];
356 argcnt++;
358 rest = mrb_ary_new_from_values(mrb, argcnt, pargv);
360 for (i=0; i<RARRAY_LEN(rest); i++) {
361 id = mrb_obj_to_sym(mrb, RARRAY_PTR(rest)[i]);
362 RARRAY_PTR(rest)[i] = mrb_symbol_value(id);
365 st = make_struct(mrb, name, rest, struct_class(mrb));
366 if (!mrb_nil_p(b)) {
367 mrb_yield_with_class(mrb, b, 1, &st, st, mrb_class_ptr(klass));
370 return st;
373 static mrb_int
374 num_members(mrb_state *mrb, struct RClass *klass)
376 mrb_value members;