struct.c 25.1 KB
Newer Older
mimaki's avatar
mimaki committed
1 2
/*
** struct.c - Struct class
roco's avatar
roco committed
3
**
mimaki's avatar
mimaki committed
4 5
** See Copyright Notice in mruby.h
*/
mimaki's avatar
mimaki committed
6 7 8

#include <string.h>
#include <stdarg.h>
mattn's avatar
mattn committed
9 10
#include "mruby.h"
#include "mruby/array.h"
mimaki's avatar
mimaki committed
11 12
#include "mruby/string.h"
#include "mruby/class.h"
13
#include "mruby/variable.h"
take_cheeze's avatar
take_cheeze committed
14
#include "mruby/hash.h"
15
#include "mruby/range.h"
mimaki's avatar
mimaki committed
16

17 18
#define RSTRUCT_LEN(st) RARRAY_LEN(st)
#define RSTRUCT_PTR(st) RARRAY_PTR(st)
mimaki's avatar
mimaki committed
19 20 21 22 23 24 25 26 27 28 29 30 31

static struct RClass *
struct_class(mrb_state *mrb)
{
  return mrb_class_get(mrb, "Struct");
}

static inline mrb_value
struct_ivar_get(mrb_state *mrb, mrb_value c, mrb_sym id)
{
  struct RClass* kclass;
  struct RClass* sclass = struct_class(mrb);
  mrb_value ans;
32 33 34 35 36 37 38 39 40

  for (;;) {
    ans = mrb_iv_get(mrb, c, id);
    if (!mrb_nil_p(ans)) return ans;
    kclass = RCLASS_SUPER(c);
    if (kclass == 0 || kclass == sclass)
      return mrb_nil_value();
    c = mrb_obj_value(kclass);
  }
mimaki's avatar
mimaki committed
41 42 43 44 45
}

mrb_value
mrb_struct_iv_get(mrb_state *mrb, mrb_value c, const char *name)
{
46
  return struct_ivar_get(mrb, c, mrb_intern_cstr(mrb, name));
mimaki's avatar
mimaki committed
47 48 49 50 51
}

mrb_value
mrb_struct_s_members(mrb_state *mrb, mrb_value klass)
{
52
  mrb_value members = struct_ivar_get(mrb, klass, mrb_intern_lit(mrb, "__members__"));
mimaki's avatar
mimaki committed
53

54 55 56 57 58 59 60
  if (mrb_nil_p(members)) {
    mrb_raise(mrb, E_TYPE_ERROR, "uninitialized struct");
  }
  if (!mrb_array_p(members)) {
    mrb_raise(mrb, E_TYPE_ERROR, "corrupted struct");
  }
  return members;
mimaki's avatar
mimaki committed
61 62 63 64 65 66
}

mrb_value
mrb_struct_members(mrb_state *mrb, mrb_value s)
{
  mrb_value members = mrb_struct_s_members(mrb, mrb_obj_value(mrb_obj_class(mrb, s)));
mattn's avatar
mattn committed
67
  if (!strcmp(mrb_class_name(mrb, mrb_obj_class(mrb, s)), "Struct")) {
mimaki's avatar
mimaki committed
68
    if (RSTRUCT_LEN(s) != RARRAY_LEN(members)) {
69
      mrb_raisef(mrb, E_TYPE_ERROR,
70 71
                 "struct size differs (%S required %S given)",
                 mrb_fixnum_value(RARRAY_LEN(members)), mrb_fixnum_value(RSTRUCT_LEN(s)));
mimaki's avatar
mimaki committed
72 73 74 75 76 77 78 79
    }
  }
  return members;
}

static mrb_value
mrb_struct_s_members_m(mrb_state *mrb, mrb_value klass)
{
80 81 82 83 84 85 86 87 88 89 90
  mrb_value members, ary;
  mrb_value *p, *pend;

  members = mrb_struct_s_members(mrb, klass);
  ary = mrb_ary_new_capa(mrb, RARRAY_LEN(members));
  p = RARRAY_PTR(members); pend = p + RARRAY_LEN(members);
  while (p < pend) {
    mrb_ary_push(mrb, ary, *p);
    p++;
  }
  return ary;
mimaki's avatar
mimaki committed
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
}

/* 15.2.18.4.6  */
/*
 *  call-seq:
 *     struct.members    -> array
 *
 *  Returns an array of strings representing the names of the instance
 *  variables.
 *
 *     Customer = Struct.new(:name, :address, :zip)
 *     joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
 *     joe.members   #=> [:name, :address, :zip]
 */

static mrb_value
mrb_struct_members_m(mrb_state *mrb, mrb_value obj)
{
109
  return mrb_struct_s_members_m(mrb, mrb_obj_value(mrb_obj_class(mrb, obj)));
mimaki's avatar
mimaki committed
110 111 112 113 114
}

mrb_value
mrb_struct_getmember(mrb_state *mrb, mrb_value obj, mrb_sym id)
{
115
  mrb_value members, slot, *ptr, *ptr_members;
116
  mrb_int i, len;
117 118 119 120 121 122 123 124 125

  ptr = RSTRUCT_PTR(obj);
  members = mrb_struct_members(mrb, obj);
  ptr_members = RARRAY_PTR(members);
  slot = mrb_symbol_value(id);
  len = RARRAY_LEN(members);
  for (i=0; i<len; i++) {
    if (mrb_obj_equal(mrb, ptr_members[i], slot)) {
      return ptr[i];
mimaki's avatar
mimaki committed
126
    }
127
  }
128
  mrb_raisef(mrb, E_INDEX_ERROR, "`%S' is not a struct member", mrb_sym2str(mrb, id));
129
  return mrb_nil_value();       /* not reached */
mimaki's avatar
mimaki committed
130 131 132 133 134
}

static mrb_value
mrb_struct_ref(mrb_state *mrb, mrb_value obj)
{
135
  return mrb_struct_getmember(mrb, obj, mrb->c->ci->mid);
mimaki's avatar
mimaki committed
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
}

static mrb_value mrb_struct_ref0(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[0];}
static mrb_value mrb_struct_ref1(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[1];}
static mrb_value mrb_struct_ref2(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[2];}
static mrb_value mrb_struct_ref3(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[3];}
static mrb_value mrb_struct_ref4(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[4];}
static mrb_value mrb_struct_ref5(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[5];}
static mrb_value mrb_struct_ref6(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[6];}
static mrb_value mrb_struct_ref7(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[7];}
static mrb_value mrb_struct_ref8(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[8];}
static mrb_value mrb_struct_ref9(mrb_state* mrb, mrb_value obj) {return RSTRUCT_PTR(obj)[9];}

#define numberof(array) (int)(sizeof(array) / sizeof((array)[0]))
#define N_REF_FUNC numberof(ref_func)

static mrb_value (*const ref_func[])(mrb_state*, mrb_value) = {
153 154 155 156 157 158 159 160 161 162
  mrb_struct_ref0,
  mrb_struct_ref1,
  mrb_struct_ref2,
  mrb_struct_ref3,
  mrb_struct_ref4,
  mrb_struct_ref5,
  mrb_struct_ref6,
  mrb_struct_ref7,
  mrb_struct_ref8,
  mrb_struct_ref9,
mimaki's avatar
mimaki committed
163 164 165
};

mrb_sym
166
mrb_id_attrset(mrb_state *mrb, mrb_sym id)
mimaki's avatar
mimaki committed
167
{
168 169
  const char *name;
  char *buf;
170
  mrb_int len;
171 172 173
  mrb_sym mid;

  name = mrb_sym2name_len(mrb, id, &len);
174 175
  buf = (char *)mrb_malloc(mrb, (size_t)len+2);
  memcpy(buf, name, (size_t)len);
176 177 178
  buf[len] = '=';
  buf[len+1] = '\0';

179
  mid = mrb_intern(mrb, buf, len+1);
180 181
  mrb_free(mrb, buf);
  return mid;
mimaki's avatar
mimaki committed
182 183 184 185 186
}

static mrb_value
mrb_struct_set(mrb_state *mrb, mrb_value obj, mrb_value val)
{
187
  const char *name;
188
  size_t i, len;
189
  mrb_int slen;
190 191 192 193
  mrb_sym mid;
  mrb_value members, slot, *ptr, *ptr_members;

  /* get base id */
194 195
  name = mrb_sym2name_len(mrb, mrb->c->ci->mid, &slen);
  mid = mrb_intern(mrb, name, slen-1); /* omit last "=" */
196 197 198 199 200 201 202

  members = mrb_struct_members(mrb, obj);
  ptr_members = RARRAY_PTR(members);
  len = RARRAY_LEN(members);
  ptr = RSTRUCT_PTR(obj);
  for (i=0; i<len; i++) {
    slot = ptr_members[i];
203
    if (mrb_symbol(slot) == mid) {
204
      return ptr[i] = val;
mimaki's avatar
mimaki committed
205
    }
206
  }
207 208
  mrb_raisef(mrb, E_INDEX_ERROR, "`%S' is not a struct member", mrb_sym2str(mrb, mid));
  return mrb_nil_value();       /* not reached */
mimaki's avatar
mimaki committed
209 210
}

211 212 213 214 215 216 217 218 219
static mrb_value
mrb_struct_set_m(mrb_state *mrb, mrb_value obj)
{
  mrb_value val;

  mrb_get_args(mrb, "o", &val);
  return mrb_struct_set(mrb, obj, val);
}

220 221
#define is_notop_id(id) (id) /* ((id)>tLAST_TOKEN) */
#define is_local_id(id) (is_notop_id(id)) /* &&((id)&ID_SCOPE_MASK)==ID_LOCAL) */
mimaki's avatar
mimaki committed
222 223 224
int
mrb_is_local_id(mrb_sym id)
{
225
  return is_local_id(id);
mimaki's avatar
mimaki committed
226 227
}

228
#define is_const_id(id) (is_notop_id(id)) /* &&((id)&ID_SCOPE_MASK)==ID_CONST) */
mimaki's avatar
mimaki committed
229 230 231
int
mrb_is_const_id(mrb_sym id)
{
232
  return is_const_id(id);
mimaki's avatar
mimaki committed
233 234 235 236 237
}

static mrb_value
make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass * klass)
{
238 239
  mrb_value nstr, *ptr_members;
  mrb_sym id;
240
  mrb_int i, len;
241
  struct RClass *c;
242
  int ai;
mimaki's avatar
mimaki committed
243

244 245 246 247 248 249
  if (mrb_nil_p(name)) {
    c = mrb_class_new(mrb, klass);
  }
  else {
    /* old style: should we warn? */
    name = mrb_str_to_str(mrb, name);
250
    id = mrb_obj_to_sym(mrb, name);
251
    if (!mrb_is_const_id(id)) {
252
      mrb_name_error(mrb, id, "identifier %S needs to be constant", name);
mimaki's avatar
mimaki committed
253
    }
254
    if (mrb_const_defined_at(mrb, klass, id)) {
255
      mrb_warn(mrb, "redefining constant Struct::%S", name);
256
      /* ?rb_mod_remove_const(klass, mrb_sym2name(mrb, id)); */
mimaki's avatar
mimaki committed
257
    }
258 259
    c = mrb_define_class_under(mrb, klass, RSTRING_PTR(name), klass);
  }
260
  MRB_SET_INSTANCE_TT(c, MRB_TT_ARRAY);
261
  nstr = mrb_obj_value(c);
262
  mrb_iv_set(mrb, nstr, mrb_intern_lit(mrb, "__members__"), members);
263

264 265 266
  mrb_define_class_method(mrb, c, "new", mrb_instance_new, MRB_ARGS_ANY());
  mrb_define_class_method(mrb, c, "[]", mrb_instance_new, MRB_ARGS_ANY());
  mrb_define_class_method(mrb, c, "members", mrb_struct_s_members_m, MRB_ARGS_NONE());
267
  /* RSTRUCT(nstr)->basic.c->super = c->c; */
268 269
  ptr_members = RARRAY_PTR(members);
  len = RARRAY_LEN(members);
270
  ai = mrb_gc_arena_save(mrb);
271 272 273 274
  for (i=0; i< len; i++) {
    mrb_sym id = mrb_symbol(ptr_members[i]);
    if (mrb_is_local_id(id) || mrb_is_const_id(id)) {
      if (i < N_REF_FUNC) {
275
        mrb_define_method_id(mrb, c, id, ref_func[i], MRB_ARGS_NONE());
mimaki's avatar
mimaki committed
276
      }
277
      else {
278
        mrb_define_method_id(mrb, c, id, mrb_struct_ref, MRB_ARGS_NONE());
279
      }
280
      mrb_define_method_id(mrb, c, mrb_id_attrset(mrb, id), mrb_struct_set_m, MRB_ARGS_REQ(1));
281
      mrb_gc_arena_restore(mrb, ai);
mimaki's avatar
mimaki committed
282
    }
283 284
  }
  return nstr;
mimaki's avatar
mimaki committed
285 286 287 288 289
}

mrb_value
mrb_struct_define(mrb_state *mrb, const char *name, ...)
{
290 291 292 293 294
  va_list ar;
  mrb_value nm, ary;
  char *mem;

  if (!name) nm = mrb_nil_value();
295
  else nm = mrb_str_new_cstr(mrb, name);
296 297 298 299
  ary = mrb_ary_new(mrb);

  va_start(ar, name);
  while ((mem = va_arg(ar, char*)) != 0) {
300
    mrb_sym slot = mrb_intern_cstr(mrb, mem);
301 302 303
    mrb_ary_push(mrb, ary, mrb_symbol_value(slot));
  }
  va_end(ar);
mimaki's avatar
mimaki committed
304

305
  return make_struct(mrb, nm, ary, struct_class(mrb));
mimaki's avatar
mimaki committed
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
}

/* 15.2.18.3.1  */
/*
 *  call-seq:
 *     Struct.new( [aString] [, aSym]+> )    -> StructClass
 *     StructClass.new(arg, ...)             -> obj
 *     StructClass[arg, ...]                 -> obj
 *
 *  Creates a new class, named by <i>aString</i>, containing accessor
 *  methods for the given symbols. If the name <i>aString</i> is
 *  omitted, an anonymous structure class will be created. Otherwise,
 *  the name of this struct will appear as a constant in class
 *  <code>Struct</code>, so it must be unique for all
 *  <code>Struct</code>s in the system and should start with a capital
 *  letter. Assigning a structure class to a constant effectively gives
 *  the class the name of the constant.
 *
 *  <code>Struct::new</code> returns a new <code>Class</code> object,
 *  which can then be used to create specific instances of the new
 *  structure. The number of actual parameters must be
 *  less than or equal to the number of attributes defined for this
 *  class; unset parameters default to <code>nil</code>.  Passing too many
 *  parameters will raise an <code>ArgumentError</code>.
 *
 *  The remaining methods listed in this section (class and instance)
 *  are defined for this generated class.
 *
 *     # Create a structure with a name in Struct
 *     Struct.new("Customer", :name, :address)    #=> Struct::Customer
 *     Struct::Customer.new("Dave", "123 Main")   #=> #<struct Struct::Customer name="Dave", address="123 Main">
 *
 *     # Create a structure named by its constant
 *     Customer = Struct.new(:name, :address)     #=> Customer
 *     Customer.new("Dave", "123 Main")           #=> #<struct Customer name="Dave", address="123 Main">
 */
static mrb_value
mrb_struct_s_def(mrb_state *mrb, mrb_value klass)
{
  mrb_value name, rest;
  mrb_value *pargv;
  int argcnt;
348
  mrb_int i;
mimaki's avatar
mimaki committed
349 350 351
  mrb_value b, st;
  mrb_sym id;
  mrb_value *argv;
352
  mrb_int argc;
mimaki's avatar
mimaki committed
353 354 355

  name = mrb_nil_value();
  rest = mrb_nil_value();
Yukihiro Matsumoto's avatar
Yukihiro Matsumoto committed
356
  mrb_get_args(mrb, "*&", &argv, &argc, &b);
357 358
  if (argc == 0) { /* special case to avoid crash */
    rest = mrb_ary_new(mrb);
359 360
  }
  else {
361 362
    if (argc > 0) name = argv[0];
    if (argc > 1) rest = argv[1];
363
    if (mrb_array_p(rest)) {
364
      if (!mrb_nil_p(name) && mrb_symbol_p(name)) {
365 366 367 368
        /* 1stArgument:symbol -> name=nil rest=argv[0]-[n] */
        mrb_ary_unshift(mrb, rest, name);
        name = mrb_nil_value();
      }
mimaki's avatar
mimaki committed
369
    }
370 371 372
    else {
      pargv = &argv[1];
      argcnt = argc-1;
373
      if (!mrb_nil_p(name) && mrb_symbol_p(name)) {
374 375 376 377 378 379
        /* 1stArgument:symbol -> name=nil rest=argv[0]-[n] */
        name = mrb_nil_value();
        pargv = &argv[0];
        argcnt++;
      }
      rest = mrb_ary_new_from_values(mrb, argcnt, pargv);
mimaki's avatar
mimaki committed
380
    }
381
    for (i=0; i<RARRAY_LEN(rest); i++) {
382
      id = mrb_obj_to_sym(mrb, RARRAY_PTR(rest)[i]);
383 384
      RARRAY_PTR(rest)[i] = mrb_symbol_value(id);
    }
385
  }
mimaki's avatar
mimaki committed
386 387
  st = make_struct(mrb, name, rest, struct_class(mrb));
  if (!mrb_nil_p(b)) {
388
    mrb_yield_with_class(mrb, b, 1, &st, st, mrb_class_ptr(klass));
mimaki's avatar
mimaki committed
389 390 391 392 393
  }

  return st;
}

Yukihiro Matsumoto's avatar
Yukihiro Matsumoto committed
394
static int
mimaki's avatar
mimaki committed
395 396
num_members(mrb_state *mrb, struct RClass *klass)
{
397 398
  mrb_value members;

399
  members = struct_ivar_get(mrb, mrb_obj_value(klass), mrb_intern_lit(mrb, "__members__"));
400 401 402 403
  if (!mrb_array_p(members)) {
    mrb_raise(mrb, E_TYPE_ERROR, "broken members");
  }
  return RARRAY_LEN(members);
mimaki's avatar
mimaki committed
404 405 406 407 408 409 410 411 412
}

/* 15.2.18.4.8  */
/*
 */
static mrb_value
mrb_struct_initialize_withArg(mrb_state *mrb, int argc, mrb_value *argv, mrb_value self)
{
  struct RClass *klass = mrb_obj_class(mrb, self);
mattn's avatar
mattn committed
413
  int i, n;
mimaki's avatar
mimaki committed
414 415 416 417 418

  n = num_members(mrb, klass);
  if (n < argc) {
    mrb_raise(mrb, E_ARGUMENT_ERROR, "struct size differs");
  }
419

420
  for (i = 0; i < argc; i++) {
421
    mrb_ary_set(mrb, self, i, argv[i]);
422
  }
mattn's avatar
mattn committed
423
  for (i = argc; i < n; i++) {
424
    mrb_ary_set(mrb, self, i, mrb_nil_value());
mattn's avatar
mattn committed
425
  }
mimaki's avatar
mimaki committed
426 427 428 429 430 431 432
  return self;
}

static mrb_value
mrb_struct_initialize_m(mrb_state *mrb, /*int argc, mrb_value *argv,*/ mrb_value self)
{
  mrb_value *argv;
433
  mrb_int argc;
mimaki's avatar
mimaki committed
434 435 436 437 438 439 440 441

  mrb_get_args(mrb, "*", &argv, &argc);
  return mrb_struct_initialize_withArg(mrb, argc, argv, self);
}

mrb_value
mrb_struct_initialize(mrb_state *mrb, mrb_value self, mrb_value values)
{
Yukihiro Matsumoto's avatar
Yukihiro Matsumoto committed
442
  return mrb_struct_initialize_withArg(mrb, RARRAY_LEN(values), RARRAY_PTR(values), self);
mimaki's avatar
mimaki committed
443 444 445
}

static mrb_value
446
inspect_struct(mrb_state *mrb, mrb_value s, int recur)
mimaki's avatar
mimaki committed
447
{
448
  const char *cn = mrb_class_name(mrb, mrb_obj_class(mrb, s));
449
  mrb_value members, str = mrb_str_new_lit(mrb, "#<struct ");
450
  mrb_value *ptr, *ptr_members;
451
  mrb_int i, len;
452 453 454 455 456

  if (cn) {
    mrb_str_append(mrb, str, mrb_str_new_cstr(mrb, cn));
  }
  if (recur) {
457
    return mrb_str_cat_lit(mrb, str, ":...>");
458 459 460 461 462 463 464 465 466
  }

  members = mrb_struct_members(mrb, s);
  ptr_members = RARRAY_PTR(members);
  ptr = RSTRUCT_PTR(s);
  len = RSTRUCT_LEN(s);
  for (i=0; i<len; i++) {
    mrb_value slot;
    mrb_sym id;
mimaki's avatar
mimaki committed
467

468
    if (i > 0) {
469
      mrb_str_cat_lit(mrb, str, ", ");
mimaki's avatar
mimaki committed
470
    }
471
    else if (cn) {
472
      mrb_str_cat_lit(mrb, str, " ");
mimaki's avatar
mimaki committed
473
    }
474 475 476 477
    slot = ptr_members[i];
    id = mrb_symbol(slot);
    if (mrb_is_local_id(id) || mrb_is_const_id(id)) {
      const char *name;
478
      mrb_int len;
mimaki's avatar
mimaki committed
479

480 481
      name = mrb_sym2name_len(mrb, id, &len);
      mrb_str_append(mrb, str, mrb_str_new(mrb, name, len));
mimaki's avatar
mimaki committed
482
    }
483 484 485
    else {
      mrb_str_append(mrb, str, mrb_inspect(mrb, slot));
    }
486
    mrb_str_cat_lit(mrb, str, "=");
487 488
    mrb_str_append(mrb, str, mrb_inspect(mrb, ptr[i]));
  }
489
  mrb_str_cat_lit(mrb, str, ">");
mimaki's avatar
mimaki committed
490

491
  return str;
mimaki's avatar
mimaki committed
492 493 494 495 496 497 498 499 500 501 502 503
}

/*
 * call-seq:
 *   struct.to_s      -> string
 *   struct.inspect   -> string
 *
 * Describe the contents of this struct in a string.
 */
static mrb_value
mrb_struct_inspect(mrb_state *mrb, mrb_value s)
{
504
  return inspect_struct(mrb, s, 0);
mimaki's avatar
mimaki committed
505 506 507 508 509 510 511
}

/* 15.2.18.4.9  */
/* :nodoc: */
mrb_value
mrb_struct_init_copy(mrb_state *mrb, mrb_value copy)
{
512
  mrb_value s;
cubicdaiya's avatar
cubicdaiya committed
513
  mrb_int i, len;
514

mimaki's avatar
mimaki committed
515 516
  mrb_get_args(mrb, "o", &s);

517
  if (mrb_obj_equal(mrb, copy, s)) return copy;
518 519 520 521 522 523 524
  if (!mrb_obj_is_instance_of(mrb, s, mrb_obj_class(mrb, copy))) {
    mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
  }
  if (!mrb_array_p(s)) {
    mrb_raise(mrb, E_TYPE_ERROR, "corrupted struct");
  }
  if (RSTRUCT_LEN(copy) != RSTRUCT_LEN(s)) {
525 526
    mrb_raise(mrb, E_TYPE_ERROR, "struct size mismatch");
  }
527
  len = RSTRUCT_LEN(copy);
528
  for (i = 0; i < len; i++) {
529
    mrb_ary_set(mrb, copy, i, RSTRUCT_PTR(s)[i]);
530
  }
531
  return copy;
mimaki's avatar
mimaki committed
532 533 534
}

static mrb_value
535
struct_aref_sym(mrb_state *mrb, mrb_value s, mrb_sym id)
mimaki's avatar
mimaki committed
536
{
537
  mrb_value *ptr, members, *ptr_members;
538
  mrb_int i, len;
539 540 541 542 543 544 545 546

  ptr = RSTRUCT_PTR(s);
  members = mrb_struct_members(mrb, s);
  ptr_members = RARRAY_PTR(members);
  len = RARRAY_LEN(members);
  for (i=0; i<len; i++) {
    if (mrb_symbol(ptr_members[i]) == id) {
      return ptr[i];
mimaki's avatar
mimaki committed
547
    }
548
  }
549
  mrb_raisef(mrb, E_INDEX_ERROR, "no member '%S' in struct", mrb_sym2str(mrb, id));
550
  return mrb_nil_value();       /* not reached */
mimaki's avatar
mimaki committed
551 552
}

553 554 555 556 557 558 559 560 561 562 563 564 565 566 567
static mrb_value
struct_aref_int(mrb_state *mrb, mrb_value s, mrb_int i)
{
  if (i < 0) i = RSTRUCT_LEN(s) + i;
  if (i < 0)
      mrb_raisef(mrb, E_INDEX_ERROR,
                 "offset %S too small for struct(size:%S)",
                 mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
  if (RSTRUCT_LEN(s) <= i)
    mrb_raisef(mrb, E_INDEX_ERROR,
               "offset %S too large for struct(size:%S)",
               mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
  return RSTRUCT_PTR(s)[i];
}

mimaki's avatar
mimaki committed
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587
/* 15.2.18.4.2  */
/*
 *  call-seq:
 *     struct[symbol]    -> anObject
 *     struct[fixnum]    -> anObject
 *
 *  Attribute Reference---Returns the value of the instance variable
 *  named by <i>symbol</i>, or indexed (0..length-1) by
 *  <i>fixnum</i>. Will raise <code>NameError</code> if the named
 *  variable does not exist, or <code>IndexError</code> if the index is
 *  out of range.
 *
 *     Customer = Struct.new(:name, :address, :zip)
 *     joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
 *
 *     joe["name"]   #=> "Joe Smith"
 *     joe[:name]    #=> "Joe Smith"
 *     joe[0]        #=> "Joe Smith"
 */
mrb_value
588
mrb_struct_aref(mrb_state *mrb, mrb_value s)
mimaki's avatar
mimaki committed
589
{
590
  mrb_value idx;
mimaki's avatar
mimaki committed
591

592
  mrb_get_args(mrb, "o", &idx);
593 594 595 596 597 598 599 600 601
  if (mrb_string_p(idx)) {
    mrb_value sym = mrb_check_intern_str(mrb, idx);

    if (mrb_nil_p(sym)) {
      mrb_raisef(mrb, E_INDEX_ERROR, "no member '%S' in struct", idx);
    }
    idx = sym;
  }
  if (mrb_symbol_p(idx)) {
602
    return struct_aref_sym(mrb, s, mrb_symbol(idx));
mimaki's avatar
mimaki committed
603
  }
604
  return struct_aref_int(mrb, s, mrb_int(mrb, idx));
mimaki's avatar
mimaki committed
605 606 607
}

static mrb_value
608
mrb_struct_aset_sym(mrb_state *mrb, mrb_value s, mrb_sym id, mrb_value val)
mimaki's avatar
mimaki committed
609
{
610
  mrb_value members, *ptr, *ptr_members;
611
  mrb_int i, len;
mimaki's avatar
mimaki committed
612

613 614 615
  members = mrb_struct_members(mrb, s);
  len = RARRAY_LEN(members);
  if (RSTRUCT_LEN(s) != len) {
616
    mrb_raisef(mrb, E_TYPE_ERROR,
617 618
               "struct size differs (%S required %S given)",
               mrb_fixnum_value(len), mrb_fixnum_value(RSTRUCT_LEN(s)));
619 620 621 622 623 624 625
  }
  ptr = RSTRUCT_PTR(s);
  ptr_members = RARRAY_PTR(members);
  for (i=0; i<len; i++) {
    if (mrb_symbol(ptr_members[i]) == id) {
      ptr[i] = val;
      return val;
mimaki's avatar
mimaki committed
626
    }
627
  }
628
  mrb_raisef(mrb, E_INDEX_ERROR, "no member '%S' in struct", mrb_sym2str(mrb, id));
629
  return val;                   /* not reach */
mimaki's avatar
mimaki committed
630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656
}

/* 15.2.18.4.3  */
/*
 *  call-seq:
 *     struct[symbol] = obj    -> obj
 *     struct[fixnum] = obj    -> obj
 *
 *  Attribute Assignment---Assigns to the instance variable named by
 *  <i>symbol</i> or <i>fixnum</i> the value <i>obj</i> and
 *  returns it. Will raise a <code>NameError</code> if the named
 *  variable does not exist, or an <code>IndexError</code> if the index
 *  is out of range.
 *
 *     Customer = Struct.new(:name, :address, :zip)
 *     joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
 *
 *     joe["name"] = "Luke"
 *     joe[:zip]   = "90210"
 *
 *     joe.name   #=> "Luke"
 *     joe.zip    #=> "90210"
 */

mrb_value
mrb_struct_aset(mrb_state *mrb, mrb_value s)
{
657
  mrb_int i;
mimaki's avatar
mimaki committed
658 659
  mrb_value idx;
  mrb_value val;
660

mimaki's avatar
mimaki committed
661 662
  mrb_get_args(mrb, "oo", &idx, &val);

663 664 665 666 667 668 669 670
  if (mrb_string_p(idx)) {
    mrb_value sym = mrb_check_intern_str(mrb, idx);

    if (mrb_nil_p(sym)) {
      mrb_raisef(mrb, E_INDEX_ERROR, "no member '%S' in struct", idx);
    }
    idx = sym;
  }
671 672
  if (mrb_symbol_p(idx)) {
    return mrb_struct_aset_sym(mrb, s, mrb_symbol(idx), val);
673
  }
mimaki's avatar
mimaki committed
674

675
  i = mrb_int(mrb, idx);
676 677
  if (i < 0) i = RSTRUCT_LEN(s) + i;
  if (i < 0) {
678
    mrb_raisef(mrb, E_INDEX_ERROR,
679 680
               "offset %S too small for struct(size:%S)",
               mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
681 682
  }
  if (RSTRUCT_LEN(s) <= i) {
683
    mrb_raisef(mrb, E_INDEX_ERROR,
684 685
               "offset %S too large for struct(size:%S)",
               mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
686 687
  }
  return RSTRUCT_PTR(s)[i] = val;
mimaki's avatar
mimaki committed
688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711
}

/* 15.2.18.4.1  */
/*
 *  call-seq:
 *     struct == other_struct     -> true or false
 *
 *  Equality---Returns <code>true</code> if <i>other_struct</i> is
 *  equal to this one: they must be of the same class as generated by
 *  <code>Struct::new</code>, and the values of all instance variables
 *  must be equal (according to <code>Object#==</code>).
 *
 *     Customer = Struct.new(:name, :address, :zip)
 *     joe   = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
 *     joejr = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345)
 *     jane  = Customer.new("Jane Doe", "456 Elm, Anytown NC", 12345)
 *     joe == joejr   #=> true
 *     joe == jane    #=> false
 */

static mrb_value
mrb_struct_equal(mrb_state *mrb, mrb_value s)
{
  mrb_value s2;
712
  mrb_value *ptr, *ptr2;
713
  mrb_int i, len;
714
  mrb_bool equal_p;
mimaki's avatar
mimaki committed
715 716

  mrb_get_args(mrb, "o", &s2);
717 718 719 720 721 722 723 724
  if (mrb_obj_equal(mrb, s, s2)) {
    equal_p = 1;
  }
  else if (!strcmp(mrb_class_name(mrb, mrb_obj_class(mrb, s)), "Struct") ||
           mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) {
    equal_p = 0;
  }
  else if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) {
725
    mrb_bug(mrb, "inconsistent struct"); /* should never happen */
726
    equal_p = 0; /* This substuture is just to suppress warnings. never called. */
mimaki's avatar
mimaki committed
727
  }
728 729 730 731 732 733 734 735 736 737 738
  else {
    ptr = RSTRUCT_PTR(s);
    ptr2 = RSTRUCT_PTR(s2);
    len = RSTRUCT_LEN(s);
    equal_p = 1;
    for (i=0; i<len; i++) {
      if (!mrb_equal(mrb, ptr[i], ptr2[i])) {
        equal_p = 0;
        break;
      }
    }
739
  }
740

741
  return mrb_bool_value(equal_p);
mimaki's avatar
mimaki committed
742 743 744 745 746 747 748 749 750 751 752 753 754 755
}

/* 15.2.18.4.12(x)  */
/*
 * code-seq:
 *   struct.eql?(other)   -> true or false
 *
 * Two structures are equal if they are the same object, or if all their
 * fields are equal (using <code>eql?</code>).
 */
static mrb_value
mrb_struct_eql(mrb_state *mrb, mrb_value s)
{
  mrb_value s2;
756
  mrb_value *ptr, *ptr2;
757
  mrb_int i, len;
758
  mrb_bool eql_p;
mimaki's avatar
mimaki committed
759 760

  mrb_get_args(mrb, "o", &s2);
761 762 763 764 765 766 767 768
  if (mrb_obj_equal(mrb, s, s2)) {
    eql_p = 1;
  }
  else if (strcmp(mrb_class_name(mrb, mrb_obj_class(mrb, s2)), "Struct") ||
           mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) {
    eql_p = 0;
  }
  else if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) {
769
    mrb_bug(mrb, "inconsistent struct"); /* should never happen */
770
    eql_p = 0; /* This substuture is just to suppress warnings. never called. */
mimaki's avatar
mimaki committed
771
  }
772 773 774 775 776 777 778 779 780 781 782
  else {
    ptr = RSTRUCT_PTR(s);
    ptr2 = RSTRUCT_PTR(s2);
    len = RSTRUCT_LEN(s);
    eql_p = 1;
    for (i=0; i<len; i++) {
      if (!mrb_eql(mrb, ptr[i], ptr2[i])) {
        eql_p = 0;
        break;
      }
    }
783
  }
784

785
  return mrb_bool_value(eql_p);
mimaki's avatar
mimaki committed
786 787
}

788 789 790 791 792 793 794 795 796 797 798 799 800
/*
 * call-seq:
 *    struct.length   -> Fixnum
 *    struct.size     -> Fixnum
 *
 * Returns number of struct members.
 */
static mrb_value
mrb_struct_len(mrb_state *mrb, mrb_value self)
{
  return mrb_fixnum_value(RSTRUCT_LEN(self));
}

801 802 803 804 805 806 807 808 809 810 811 812 813
/*
 * call-seq:
 *    struct.to_a    -> array
 *    struct.values  -> array
 *
 * Create an array from struct values.
 */
static mrb_value
mrb_struct_to_a(mrb_state *mrb, mrb_value self)
{
  return mrb_ary_new_from_values(mrb, RSTRUCT_LEN(self), RSTRUCT_PTR(self));
}

take_cheeze's avatar
take_cheeze committed
814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835
/*
 * call-seq:
 *    struct.to_h -> hash
 *
 * Create a hash from member names and struct values.
 */
static mrb_value
mrb_struct_to_h(mrb_state *mrb, mrb_value self)
{
  mrb_value members, ret;
  mrb_int i;

  members = mrb_struct_s_members(mrb, mrb_obj_value(mrb_class(mrb, self)));
  ret = mrb_hash_new_capa(mrb, RARRAY_LEN(members));

  for (i = 0; i < RARRAY_LEN(members); ++i) {
    mrb_hash_set(mrb, ret, RARRAY_PTR(members)[i], RSTRUCT_PTR(self)[i]);
  }

  return ret;
}

836 837 838 839 840 841 842 843
static mrb_value
mrb_struct_values_at(mrb_state *mrb, mrb_value self)
{
  mrb_int argc;
  mrb_value *argv;

  mrb_get_args(mrb, "*", &argv, &argc);

844
  return mrb_get_values_at(mrb, self, RSTRUCT_LEN(self), argc, argv, struct_aref_int);
845 846
}

mimaki's avatar
mimaki committed
847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862
/*
 *  A <code>Struct</code> is a convenient way to bundle a number of
 *  attributes together, using accessor methods, without having to write
 *  an explicit class.
 *
 *  The <code>Struct</code> 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
 *  ``<i>Customer</i>Class,'' and we'll show an example instance of that
 *  class as ``<i>Customer</i>Inst.''
 *
 *  In the descriptions that follow, the parameter <i>symbol</i> refers
 *  to a symbol, which is either a quoted string or a
 *  <code>Symbol</code> (such as <code>:name</code>).
 */
void
mattn's avatar
mattn committed
863
mrb_mruby_struct_gem_init(mrb_state* mrb)
mimaki's avatar
mimaki committed
864 865 866 867
{
  struct RClass *st;
  st = mrb_define_class(mrb, "Struct",  mrb->object_class);

868
  mrb_define_class_method(mrb, st, "new",             mrb_struct_s_def,       MRB_ARGS_ANY());  /* 15.2.18.3.1  */
mimaki's avatar
mimaki committed
869

870 871 872 873 874 875 876
  mrb_define_method(mrb, st,       "==",              mrb_struct_equal,       MRB_ARGS_REQ(1)); /* 15.2.18.4.1  */
  mrb_define_method(mrb, st,       "[]",              mrb_struct_aref,        MRB_ARGS_REQ(1)); /* 15.2.18.4.2  */
  mrb_define_method(mrb, st,       "[]=",             mrb_struct_aset,        MRB_ARGS_REQ(2)); /* 15.2.18.4.3  */
  mrb_define_method(mrb, st,       "members",         mrb_struct_members_m,   MRB_ARGS_NONE()); /* 15.2.18.4.6  */
  mrb_define_method(mrb, st,       "initialize",      mrb_struct_initialize_m,MRB_ARGS_ANY());  /* 15.2.18.4.8  */
  mrb_define_method(mrb, st,       "initialize_copy", mrb_struct_init_copy,   MRB_ARGS_REQ(1)); /* 15.2.18.4.9  */
  mrb_define_method(mrb, st,       "inspect",         mrb_struct_inspect,     MRB_ARGS_NONE()); /* 15.2.18.4.10(x)  */
mimaki's avatar
mimaki committed
877
  mrb_define_alias(mrb, st,        "to_s", "inspect");                                      /* 15.2.18.4.11(x)  */
878
  mrb_define_method(mrb, st,       "eql?",            mrb_struct_eql,         MRB_ARGS_REQ(1)); /* 15.2.18.4.12(x)  */
879 880 881

  mrb_define_method(mrb, st,        "size",           mrb_struct_len,         MRB_ARGS_NONE());
  mrb_define_method(mrb, st,        "length",         mrb_struct_len,         MRB_ARGS_NONE());
882 883
  mrb_define_method(mrb, st,        "to_a",           mrb_struct_to_a,        MRB_ARGS_NONE());
  mrb_define_method(mrb, st,        "values",         mrb_struct_to_a,        MRB_ARGS_NONE());
take_cheeze's avatar
take_cheeze committed
884
  mrb_define_method(mrb, st,        "to_h",           mrb_struct_to_h,        MRB_ARGS_NONE());
885
  mrb_define_method(mrb, st,        "values_at",      mrb_struct_values_at,   MRB_ARGS_NONE());
mimaki's avatar
mimaki committed
886
}
mattn's avatar
mattn committed
887 888 889 890 891

void
mrb_mruby_struct_gem_final(mrb_state* mrb)
{
}