struct.c 19.9 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

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

16 17
#define RSTRUCT_LEN(st) mrb_ary_ptr(st)->len
#define RSTRUCT_PTR(st) mrb_ary_ptr(st)->ptr
mimaki's avatar
mimaki committed
18 19 20 21 22 23 24 25 26 27 28 29 30

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;
31 32 33 34 35 36 37 38 39

  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
40 41
}

42
static mrb_value
43
struct_s_members(mrb_state *mrb, struct RClass *klass)
mimaki's avatar
mimaki committed
44
{
45
  mrb_value members = struct_ivar_get(mrb, mrb_obj_value(klass), mrb_intern_lit(mrb, "__members__"));
mimaki's avatar
mimaki committed
46

47 48 49 50 51 52 53
  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
54 55
}

56
static mrb_value
57 58 59 60 61 62 63
struct_members(mrb_state *mrb, mrb_value s)
{
  mrb_value members = struct_s_members(mrb, mrb_obj_class(mrb, s));
  if (!mrb_array_p(s)) {
    mrb_raise(mrb, E_TYPE_ERROR, "corrupted struct");
  }
  if (RSTRUCT_LEN(s) != RARRAY_LEN(members)) {
64 65 66 67 68 69 70 71
    if (RSTRUCT_LEN(s) == 0) {  /* probably uninitialized */
      mrb_ary_resize(mrb, s, RARRAY_LEN(members));
    }
    else {
      mrb_raisef(mrb, E_TYPE_ERROR,
                 "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
  }
  return members;
}

static mrb_value
mrb_struct_s_members_m(mrb_state *mrb, mrb_value klass)
{
79 80
  mrb_value members, ary;

81
  members = struct_s_members(mrb, mrb_class_ptr(klass));
82
  ary = mrb_ary_new_capa(mrb, RARRAY_LEN(members));
83
  mrb_ary_replace(mrb, ary, members);
84
  return ary;
mimaki's avatar
mimaki committed
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
}

/* 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
101
mrb_struct_members(mrb_state *mrb, mrb_value obj)
mimaki's avatar
mimaki committed
102
{
103
  return mrb_struct_s_members_m(mrb, mrb_obj_value(mrb_obj_class(mrb, obj)));
mimaki's avatar
mimaki committed
104 105
}

106
static mrb_value struct_aref_sym(mrb_state *mrb, mrb_value obj, mrb_sym id);
mimaki's avatar
mimaki committed
107 108 109 110

static mrb_value
mrb_struct_ref(mrb_state *mrb, mrb_value obj)
{
111
  return struct_aref_sym(mrb, obj, mrb->c->ci->mid);
mimaki's avatar
mimaki committed
112 113
}

114
static mrb_sym
115
mrb_id_attrset(mrb_state *mrb, mrb_sym id)
mimaki's avatar
mimaki committed
116
{
117 118
  const char *name;
  char *buf;
119
  mrb_int len;
120 121 122
  mrb_sym mid;

  name = mrb_sym2name_len(mrb, id, &len);
123 124
  buf = (char *)mrb_malloc(mrb, (size_t)len+2);
  memcpy(buf, name, (size_t)len);
125 126 127
  buf[len] = '=';
  buf[len+1] = '\0';

128
  mid = mrb_intern(mrb, buf, len+1);
129 130
  mrb_free(mrb, buf);
  return mid;
mimaki's avatar
mimaki committed
131 132
}

133 134
static mrb_value mrb_struct_aset_sym(mrb_state *mrb, mrb_value s, mrb_sym id, mrb_value val);

mimaki's avatar
mimaki committed
135
static mrb_value
136
mrb_struct_set_m(mrb_state *mrb, mrb_value obj)
mimaki's avatar
mimaki committed
137
{
138 139
  mrb_value val;

140
  const char *name;
141
  mrb_int slen;
142
  mrb_sym mid;
143 144

  mrb_get_args(mrb, "o", &val);
145 146

  /* get base id */
147 148
  name = mrb_sym2name_len(mrb, mrb->c->ci->mid, &slen);
  mid = mrb_intern(mrb, name, slen-1); /* omit last "=" */
149

150
  return mrb_struct_aset_sym(mrb, obj, mid, val);
151 152
}

153
static mrb_bool
154
is_local_id(mrb_state *mrb, const char *name)
mimaki's avatar
mimaki committed
155
{
156
  if (!name) return FALSE;
157
  return !ISUPPER(name[0]);
mimaki's avatar
mimaki committed
158 159
}

160
static mrb_bool
161
is_const_id(mrb_state *mrb, const char *name)
mimaki's avatar
mimaki committed
162
{
163
  if (!name) return FALSE;
164
  return ISUPPER(name[0]);
mimaki's avatar
mimaki committed
165 166
}

167 168 169
static void
make_struct_define_accessors(mrb_state *mrb, mrb_value members, struct RClass *c)
{
170
  const mrb_value *ptr_members = RARRAY_PTR(members);
171 172 173 174 175 176 177 178 179
  mrb_int i;
  mrb_int len = RARRAY_LEN(members);
  int ai = mrb_gc_arena_save(mrb);

  for (i=0; i<len; i++) {
    mrb_sym id = mrb_symbol(ptr_members[i]);
    const char *name = mrb_sym2name_len(mrb, id, NULL);

    if (is_local_id(mrb, name) || is_const_id(mrb, name)) {
180
      mrb_define_method_id(mrb, c, id, mrb_struct_ref, MRB_ARGS_NONE());
181 182 183 184 185 186
      mrb_define_method_id(mrb, c, mrb_id_attrset(mrb, id), mrb_struct_set_m, MRB_ARGS_REQ(1));
      mrb_gc_arena_restore(mrb, ai);
    }
  }
}

mimaki's avatar
mimaki committed
187 188 189
static mrb_value
make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass * klass)
{
190
  mrb_value nstr;
191 192
  mrb_sym id;
  struct RClass *c;
mimaki's avatar
mimaki committed
193

194 195 196 197 198 199
  if (mrb_nil_p(name)) {
    c = mrb_class_new(mrb, klass);
  }
  else {
    /* old style: should we warn? */
    name = mrb_str_to_str(mrb, name);
200
    id = mrb_obj_to_sym(mrb, name);
201
    if (!is_const_id(mrb, mrb_sym2name_len(mrb, id, NULL))) {
202
      mrb_name_error(mrb, id, "identifier %S needs to be constant", name);
mimaki's avatar
mimaki committed
203
    }
204
    if (mrb_const_defined_at(mrb, mrb_obj_value(klass), id)) {
205
      mrb_warn(mrb, "redefining constant Struct::%S", name);
206
      /* ?rb_mod_remove_const(klass, mrb_sym2name(mrb, id)); */
mimaki's avatar
mimaki committed
207
    }
208 209
    c = mrb_define_class_under(mrb, klass, RSTRING_PTR(name), klass);
  }
210
  MRB_SET_INSTANCE_TT(c, MRB_TT_ARRAY);
211
  nstr = mrb_obj_value(c);
212
  mrb_iv_set(mrb, nstr, mrb_intern_lit(mrb, "__members__"), members);
213

214 215 216
  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());
217
  /* RSTRUCT(nstr)->basic.c->super = c->c; */
218
  make_struct_define_accessors(mrb, members, c);
219
  return nstr;
mimaki's avatar
mimaki committed
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
}

/* 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;
261
  mrb_int argcnt;
262
  mrb_int i;
mimaki's avatar
mimaki committed
263 264 265
  mrb_value b, st;
  mrb_sym id;
  mrb_value *argv;
266
  mrb_int argc;
mimaki's avatar
mimaki committed
267 268 269

  name = mrb_nil_value();
  rest = mrb_nil_value();
Yukihiro Matsumoto's avatar
Yukihiro Matsumoto committed
270
  mrb_get_args(mrb, "*&", &argv, &argc, &b);
271 272
  if (argc == 0) { /* special case to avoid crash */
    rest = mrb_ary_new(mrb);
273 274
  }
  else {
275 276
    if (argc > 0) name = argv[0];
    if (argc > 1) rest = argv[1];
277
    if (mrb_array_p(rest)) {
278
      if (!mrb_nil_p(name) && mrb_symbol_p(name)) {
279 280 281 282
        /* 1stArgument:symbol -> name=nil rest=argv[0]-[n] */
        mrb_ary_unshift(mrb, rest, name);
        name = mrb_nil_value();
      }
mimaki's avatar
mimaki committed
283
    }
284 285 286
    else {
      pargv = &argv[1];
      argcnt = argc-1;
287
      if (!mrb_nil_p(name) && mrb_symbol_p(name)) {
288 289 290 291 292 293
        /* 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
294
    }
295
    for (i=0; i<RARRAY_LEN(rest); i++) {
296
      id = mrb_obj_to_sym(mrb, RARRAY_PTR(rest)[i]);
297
      mrb_ary_set(mrb, rest, i, mrb_symbol_value(id));
298
    }
299
  }
mimaki's avatar
mimaki committed
300 301
  st = make_struct(mrb, name, rest, struct_class(mrb));
  if (!mrb_nil_p(b)) {
ksss's avatar
ksss committed
302
    mrb_yield_with_class(mrb, b, 1, &st, st, mrb_class_ptr(st));
mimaki's avatar
mimaki committed
303 304 305 306 307
  }

  return st;
}

308
static mrb_int
mimaki's avatar
mimaki committed
309 310
num_members(mrb_state *mrb, struct RClass *klass)
{
311 312
  mrb_value members;

313
  members = struct_ivar_get(mrb, mrb_obj_value(klass), mrb_intern_lit(mrb, "__members__"));
314 315 316 317
  if (!mrb_array_p(members)) {
    mrb_raise(mrb, E_TYPE_ERROR, "broken members");
  }
  return RARRAY_LEN(members);
mimaki's avatar
mimaki committed
318 319 320 321 322 323
}

/* 15.2.18.4.8  */
/*
 */
static mrb_value
324
mrb_struct_initialize_withArg(mrb_state *mrb, mrb_int argc, mrb_value *argv, mrb_value self)
mimaki's avatar
mimaki committed
325 326
{
  struct RClass *klass = mrb_obj_class(mrb, self);
327
  mrb_int i, n;
mimaki's avatar
mimaki committed
328 329 330 331 332

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

334
  for (i = 0; i < argc; i++) {
335
    mrb_ary_set(mrb, self, i, argv[i]);
336
  }
mattn's avatar
mattn committed
337
  for (i = argc; i < n; i++) {
338
    mrb_ary_set(mrb, self, i, mrb_nil_value());
mattn's avatar
mattn committed
339
  }
mimaki's avatar
mimaki committed
340 341 342 343
  return self;
}

static mrb_value
344
mrb_struct_initialize(mrb_state *mrb, mrb_value self)
mimaki's avatar
mimaki committed
345 346
{
  mrb_value *argv;
347
  mrb_int argc;
mimaki's avatar
mimaki committed
348 349 350 351 352 353 354

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

/* 15.2.18.4.9  */
/* :nodoc: */
355
static mrb_value
mimaki's avatar
mimaki committed
356 357
mrb_struct_init_copy(mrb_state *mrb, mrb_value copy)
{
358
  mrb_value s;
359

mimaki's avatar
mimaki committed
360 361
  mrb_get_args(mrb, "o", &s);

362
  if (mrb_obj_equal(mrb, copy, s)) return copy;
363 364 365 366 367 368
  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");
  }
369
  mrb_ary_replace(mrb, copy, s);
370
  return copy;
mimaki's avatar
mimaki committed
371 372 373
}

static mrb_value
374
struct_aref_sym(mrb_state *mrb, mrb_value obj, mrb_sym id)
mimaki's avatar
mimaki committed
375
{
376
  mrb_value members, *ptr;
377
  const mrb_value *ptr_members;
378
  mrb_int i, len;
379

380
  members = struct_members(mrb, obj);
381 382
  ptr_members = RARRAY_PTR(members);
  len = RARRAY_LEN(members);
383
  ptr = RSTRUCT_PTR(obj);
384
  for (i=0; i<len; i++) {
385 386
    mrb_value slot = ptr_members[i];
    if (mrb_symbol_p(slot) && mrb_symbol(slot) == id) {
387
      return ptr[i];
mimaki's avatar
mimaki committed
388
    }
389
  }
390
  mrb_raisef(mrb, E_INDEX_ERROR, "'%S' is not a struct member", mrb_sym2str(mrb, id));
391
  return mrb_nil_value();       /* not reached */
mimaki's avatar
mimaki committed
392 393
}

394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
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
409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
/* 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"
 */
428
static mrb_value
429
mrb_struct_aref(mrb_state *mrb, mrb_value s)
mimaki's avatar
mimaki committed
430
{
431
  mrb_value idx;
mimaki's avatar
mimaki committed
432

433
  mrb_get_args(mrb, "o", &idx);
434 435 436 437
  if (mrb_string_p(idx)) {
    mrb_value sym = mrb_check_intern_str(mrb, idx);

    if (mrb_nil_p(sym)) {
438
      mrb_name_error(mrb, mrb_intern_str(mrb, idx), "no member '%S' in struct", idx);
439 440 441 442
    }
    idx = sym;
  }
  if (mrb_symbol_p(idx)) {
443
    return struct_aref_sym(mrb, s, mrb_symbol(idx));
mimaki's avatar
mimaki committed
444
  }
445
  return struct_aref_int(mrb, s, mrb_int(mrb, idx));
mimaki's avatar
mimaki committed
446 447 448
}

static mrb_value
449
mrb_struct_aset_sym(mrb_state *mrb, mrb_value s, mrb_sym id, mrb_value val)
mimaki's avatar
mimaki committed
450
{
451 452
  mrb_value members, *ptr;
  const mrb_value *ptr_members;
453
  mrb_int i, len;
mimaki's avatar
mimaki committed
454

455
  members = struct_members(mrb, s);
456 457 458 459 460 461 462
  len = RARRAY_LEN(members);
  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
463
    }
464
  }
465
  mrb_name_error(mrb, id, "no member '%S' in struct", mrb_sym2str(mrb, id));
466
  return val;                   /* not reach */
mimaki's avatar
mimaki committed
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
}

/* 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"
 */

491
static mrb_value
mimaki's avatar
mimaki committed
492 493
mrb_struct_aset(mrb_state *mrb, mrb_value s)
{
494
  mrb_int i;
mimaki's avatar
mimaki committed
495 496
  mrb_value idx;
  mrb_value val;
497

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

500 501 502 503
  if (mrb_string_p(idx)) {
    mrb_value sym = mrb_check_intern_str(mrb, idx);

    if (mrb_nil_p(sym)) {
504
      mrb_name_error(mrb, mrb_intern_str(mrb, idx), "no member '%S' in struct", idx);
505 506 507
    }
    idx = sym;
  }
508 509
  if (mrb_symbol_p(idx)) {
    return mrb_struct_aset_sym(mrb, s, mrb_symbol(idx), val);
510
  }
mimaki's avatar
mimaki committed
511

512
  i = mrb_int(mrb, idx);
513 514
  if (i < 0) i = RSTRUCT_LEN(s) + i;
  if (i < 0) {
515
    mrb_raisef(mrb, E_INDEX_ERROR,
516 517
               "offset %S too small for struct(size:%S)",
               mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
518 519
  }
  if (RSTRUCT_LEN(s) <= i) {
520
    mrb_raisef(mrb, E_INDEX_ERROR,
521 522
               "offset %S too large for struct(size:%S)",
               mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
523 524
  }
  return RSTRUCT_PTR(s)[i] = val;
mimaki's avatar
mimaki committed
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
}

/* 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;
549
  mrb_value *ptr, *ptr2;
550
  mrb_int i, len;
mimaki's avatar
mimaki committed
551 552

  mrb_get_args(mrb, "o", &s2);
553
  if (mrb_obj_equal(mrb, s, s2)) {
554
    return mrb_true_value();
555
  }
556
  if (mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) {
557
    return mrb_false_value();
558
  }
559
  if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) {
560
    mrb_bug(mrb, "inconsistent struct"); /* should never happen */
mimaki's avatar
mimaki committed
561
  }
562 563 564 565 566 567
  ptr = RSTRUCT_PTR(s);
  ptr2 = RSTRUCT_PTR(s2);
  len = RSTRUCT_LEN(s);
  for (i=0; i<len; i++) {
    if (!mrb_equal(mrb, ptr[i], ptr2[i])) {
      return mrb_false_value();
568
    }
569
  }
570

571
  return mrb_true_value();
mimaki's avatar
mimaki committed
572 573 574 575 576 577 578 579 580 581 582 583 584 585
}

/* 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;
586
  mrb_value *ptr, *ptr2;
587
  mrb_int i, len;
mimaki's avatar
mimaki committed
588 589

  mrb_get_args(mrb, "o", &s2);
590
  if (mrb_obj_equal(mrb, s, s2)) {
591
    return mrb_true_value();
592
  }
593
  if (mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) {
594
    return mrb_false_value();
595
  }
596
  if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) {
597
    mrb_bug(mrb, "inconsistent struct"); /* should never happen */
mimaki's avatar
mimaki committed
598
  }
599 600 601 602 603 604
  ptr = RSTRUCT_PTR(s);
  ptr2 = RSTRUCT_PTR(s2);
  len = RSTRUCT_LEN(s);
  for (i=0; i<len; i++) {
    if (!mrb_eql(mrb, ptr[i], ptr2[i])) {
      return mrb_false_value();
605
    }
606
  }
607

608
  return mrb_true_value();
mimaki's avatar
mimaki committed
609 610
}

611 612 613 614 615 616 617 618 619 620 621 622 623
/*
 * 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));
}

624 625 626 627 628 629 630 631 632 633 634 635 636
/*
 * 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
637 638 639 640 641 642 643 644 645 646 647 648
/*
 * 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;

649
  members = struct_s_members(mrb, mrb_class(mrb, self));
take_cheeze's avatar
take_cheeze committed
650 651 652 653 654 655 656 657 658
  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;
}

659 660 661 662 663 664 665 666
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);

667
  return mrb_get_values_at(mrb, self, RSTRUCT_LEN(self), argc, argv, struct_aref_int);
668 669
}

mimaki's avatar
mimaki committed
670 671 672 673 674 675 676 677
/*
 *  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
678 679
 *  "<i>Customer</i>Class," and we'll show an example instance of that
 *  class as "<i>Customer</i>Inst."
mimaki's avatar
mimaki committed
680 681 682 683 684 685
 *
 *  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
686
mrb_mruby_struct_gem_init(mrb_state* mrb)
mimaki's avatar
mimaki committed
687 688 689 690
{
  struct RClass *st;
  st = mrb_define_class(mrb, "Struct",  mrb->object_class);

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

693 694 695
  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  */
696 697
  mrb_define_method(mrb, st,       "members",         mrb_struct_members,     MRB_ARGS_NONE()); /* 15.2.18.4.6  */
  mrb_define_method(mrb, st,       "initialize",      mrb_struct_initialize,  MRB_ARGS_ANY());  /* 15.2.18.4.8  */
698 699
  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,       "eql?",            mrb_struct_eql,         MRB_ARGS_REQ(1)); /* 15.2.18.4.12(x)  */
700 701 702

  mrb_define_method(mrb, st,        "size",           mrb_struct_len,         MRB_ARGS_NONE());
  mrb_define_method(mrb, st,        "length",         mrb_struct_len,         MRB_ARGS_NONE());
703 704
  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
705
  mrb_define_method(mrb, st,        "to_h",           mrb_struct_to_h,        MRB_ARGS_NONE());
706
  mrb_define_method(mrb, st,        "values_at",      mrb_struct_values_at,   MRB_ARGS_NONE());
mimaki's avatar
mimaki committed
707
}
mattn's avatar
mattn committed
708 709 710 711 712

void
mrb_mruby_struct_gem_final(mrb_state* mrb)
{
}