Skip to content

Commit 48e5f1e

Browse files
committed
Implement handling of optional and rest in rb_get_kwargs.
1 parent bcf794a commit 48e5f1e

File tree

1 file changed

+81
-8
lines changed

1 file changed

+81
-8
lines changed

src/main/c/cext/ruby.c

Lines changed: 81 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1662,6 +1662,10 @@ VALUE rb_hash_set_ifnone(VALUE hash, VALUE if_none) {
16621662
return RUBY_CEXT_INVOKE("rb_hash_set_ifnone", hash, if_none);
16631663
}
16641664

1665+
VALUE rb_hash_keys(VALUE hash) {
1666+
return RUBY_INVOKE(hash, "keys");
1667+
}
1668+
16651669
VALUE rb_hash_key_str(VALUE hash) {
16661670
rb_tr_error("rb_hash_key_str not yet implemented");
16671671
}
@@ -4678,24 +4682,93 @@ VALUE rb_current_receiver(void) {
46784682
rb_tr_error("rb_current_receiver not implemented");
46794683
}
46804684

4681-
int rb_get_kwargs(VALUE keyword_hash, const ID *table, int required, int optional, VALUE *values) {
4682-
if (optional == -1) {
4683-
rb_tr_error("rb_get_kwargs with rest not implemented");
4685+
static VALUE rb_keyword_error_new(const char *error, VALUE keys) {
4686+
long i = 0, len = RARRAY_LEN(keys);
4687+
VALUE error_message = rb_sprintf("%s keyword%.*s", error, len > 1, "s");
4688+
4689+
if (len > 0) {
4690+
rb_str_append(error_message, rb_str_new_cstr(": "));
4691+
while (1) {
4692+
const VALUE k = RARRAY_AREF(keys, i);
4693+
Check_Type(k, T_SYMBOL); /* wrong hash is given to rb_get_kwargs */
4694+
rb_str_append(error_message, rb_sym2str(k));
4695+
if (++i >= len) break;
4696+
rb_str_append(error_message, rb_str_new_cstr(", "));
4697+
}
46844698
}
46854699

4686-
if (optional > 0) {
4687-
rb_tr_error("rb_get_kwargs with optional not implemented");
4700+
return rb_exc_new_str(rb_eArgError, error_message);
4701+
}
4702+
4703+
NORETURN(static void rb_keyword_error(const char *error, VALUE keys)) {
4704+
rb_exc_raise(rb_keyword_error_new(error, keys));
4705+
}
4706+
4707+
NORETURN(static void unknown_keyword_error(VALUE hash, const ID *table, int keywords)) {
4708+
int i;
4709+
for (i = 0; i < keywords; i++) {
4710+
VALUE key = table[i];
4711+
rb_hash_delete(hash, key);
46884712
}
4713+
rb_keyword_error("unknown", rb_hash_keys(hash));
4714+
}
4715+
4716+
static VALUE rb_tr_extract_keyword(VALUE keyword_hash, ID key, VALUE *values) {
4717+
VALUE val = rb_hash_lookup2(keyword_hash, key, Qundef);
4718+
if (values) {
4719+
rb_hash_delete(keyword_hash, key);
4720+
}
4721+
return val;
4722+
}
4723+
4724+
int rb_get_kwargs(VALUE keyword_hash, const ID *table, int required, int optional, VALUE *values) {
4725+
int rest = 0;
4726+
int extracted = 0;
4727+
VALUE missing = Qnil;
46894728

46904729
if (optional < 0) {
4691-
rb_tr_error("rb_get_kwargs with rest and optional not implemented");
4730+
rest = 1;
4731+
optional = -1-optional;
46924732
}
46934733

46944734
for (int n = 0; n < required; n++) {
4695-
values[n] = rb_hash_fetch(keyword_hash, table[n]);
4735+
VALUE val = rb_tr_extract_keyword(keyword_hash, table[n], values);
4736+
if (values) {
4737+
values[n] = val;
4738+
}
4739+
if (val == Qundef) {
4740+
if (NIL_P(missing)) {
4741+
missing = rb_ary_new();
4742+
}
4743+
rb_ary_push(missing, table[n]);
4744+
rb_keyword_error("missing", missing);
4745+
}
4746+
extracted++;
4747+
}
4748+
4749+
if (optional && !NIL_P(keyword_hash)) {
4750+
for (int m = required; m < required + optional; m++) {
4751+
VALUE val = rb_tr_extract_keyword(keyword_hash, table[m], values);
4752+
if (values) {
4753+
values[m] = val;
4754+
}
4755+
if (val != Qundef) {
4756+
extracted++;
4757+
}
4758+
}
4759+
}
4760+
4761+
if (!rest && !NIL_P(keyword_hash)) {
4762+
if (RHASH_SIZE(keyword_hash) > (unsigned int)(values ? 0 : extracted)) {
4763+
unknown_keyword_error(keyword_hash, table, required + optional);
4764+
}
4765+
}
4766+
4767+
for (int i = extracted; i < required + optional; i++) {
4768+
values[i] = Qundef;
46964769
}
46974770

4698-
return required;
4771+
return extracted;
46994772
}
47004773

47014774
VALUE rb_extract_keywords(VALUE *orighash) {

0 commit comments

Comments
 (0)