@@ -1662,6 +1662,10 @@ VALUE rb_hash_set_ifnone(VALUE hash, VALUE if_none) {
1662
1662
return RUBY_CEXT_INVOKE ("rb_hash_set_ifnone" , hash , if_none );
1663
1663
}
1664
1664
1665
+ VALUE rb_hash_keys (VALUE hash ) {
1666
+ return RUBY_INVOKE (hash , "keys" );
1667
+ }
1668
+
1665
1669
VALUE rb_hash_key_str (VALUE hash ) {
1666
1670
rb_tr_error ("rb_hash_key_str not yet implemented" );
1667
1671
}
@@ -4678,24 +4682,93 @@ VALUE rb_current_receiver(void) {
4678
4682
rb_tr_error ("rb_current_receiver not implemented" );
4679
4683
}
4680
4684
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
+ }
4684
4698
}
4685
4699
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 );
4688
4712
}
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 ;
4689
4728
4690
4729
if (optional < 0 ) {
4691
- rb_tr_error ("rb_get_kwargs with rest and optional not implemented" );
4730
+ rest = 1 ;
4731
+ optional = -1 - optional ;
4692
4732
}
4693
4733
4694
4734
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 ;
4696
4769
}
4697
4770
4698
- return required ;
4771
+ return extracted ;
4699
4772
}
4700
4773
4701
4774
VALUE rb_extract_keywords (VALUE * orighash ) {
0 commit comments