diff options
author | nobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2012-04-28 21:12:05 +0000 |
---|---|---|
committer | nobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2012-04-28 21:12:05 +0000 |
commit | 3380974143d3fdf1721d9e28d6b2d42036f03bd2 (patch) | |
tree | 86a60ee982dbdc8f2c68900a43327765b648822e | |
parent | 82fa2995b5ca47d3383cce82ba5c75da4f09da5a (diff) | |
download | ruby-3380974143d3fdf1721d9e28d6b2d42036f03bd2.tar.gz |
* parse.y (assoc, parser_yylex): add syntax to splat keyword hash.
[ruby-core:44591][Feature #6353]
* compile.c (compile_array_): generate keyword splat insns.
* vm.c (m_core_hash_merge_kwd): merge keyword hash into intermediate
hash. leftward argument is prior currently.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@35489 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r-- | ChangeLog | 10 | ||||
-rw-r--r-- | compile.c | 19 | ||||
-rw-r--r-- | ext/ripper/eventids2.c | 1 | ||||
-rw-r--r-- | id.c | 1 | ||||
-rw-r--r-- | parse.y | 32 | ||||
-rw-r--r-- | test/ripper/test_parser_events.rb | 6 | ||||
-rw-r--r-- | test/ruby/test_syntax.rb | 15 | ||||
-rw-r--r-- | vm.c | 25 |
8 files changed, 104 insertions, 5 deletions
@@ -1,3 +1,13 @@ +Sun Apr 29 06:12:02 2012 Nobuyoshi Nakada <nobu@ruby-lang.org> + + * parse.y (assoc, parser_yylex): add syntax to splat keyword hash. + [ruby-core:44591][Feature #6353] + + * compile.c (compile_array_): generate keyword splat insns. + + * vm.c (m_core_hash_merge_kwd): merge keyword hash into intermediate + hash. leftward argument is prior currently. + Sat Apr 28 18:39:40 2012 Koichi Sasada <ko1@atdot.net> * vm_core.h (rb_thread_t#yielding): add a field. @@ -2296,21 +2296,28 @@ compile_array_(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE* node_root, while (node) { NODE *start_node = node, *end_node; + NODE *kw = 0; const int max = 0x100; DECL_ANCHOR(anchor); INIT_ANCHOR(anchor); - for (i=0; i<max && node; i++, len++) { + for (i=0; i<max && node; i++, len++, node = node->nd_next) { if (CPDEBUG > 0 && nd_type(node) != NODE_ARRAY) { rb_bug("compile_array: This node is not NODE_ARRAY, but %s", ruby_node_name(nd_type(node))); } + if (type == COMPILE_ARRAY_TYPE_HASH && !node->nd_head) { + opt_p = 0; + kw = node->nd_next; + node = kw->nd_next; + kw = kw->nd_head; + break; + } if (opt_p && nd_type(node->nd_head) != NODE_LIT) { opt_p = 0; } COMPILE_(anchor, "array element", node->nd_head, poped); - node = node->nd_next; } if (opt_p && type != COMPILE_ARRAY_TYPE_ARGS) { @@ -2378,12 +2385,18 @@ compile_array_(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE* node_root, ADD_INSN1(anchor, line, newhash, INT2FIX(i)); APPEND_LIST(ret, anchor); } - else { + else if (i > 0) { ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); ADD_INSN(ret, line, swap); APPEND_LIST(ret, anchor); ADD_SEND(ret, line, ID2SYM(id_core_hash_merge_ptr), INT2FIX(i + 1)); } + if (kw) { + ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); + ADD_INSN(ret, line, swap); + COMPILE(ret, "keyword splat", kw); + ADD_SEND(ret, line, ID2SYM(id_core_hash_merge_kwd), INT2FIX(2)); + } break; case COMPILE_ARRAY_TYPE_ARGS: APPEND_LIST(ret, anchor); diff --git a/ext/ripper/eventids2.c b/ext/ripper/eventids2.c index 18d2d2a28a..faeabd4b65 100644 --- a/ext/ripper/eventids2.c +++ b/ext/ripper/eventids2.c @@ -233,6 +233,7 @@ static const struct token_assoc { {tRPAREN, &ripper_id_rparen}, {tRSHFT, &ripper_id_op}, {tSTAR, &ripper_id_op}, + {tDSTAR, &ripper_id_op}, {tSTRING_BEG, &ripper_id_tstring_beg}, {tSTRING_CONTENT, &ripper_id_tstring_content}, {tSTRING_DBEG, &ripper_id_embexpr_beg}, @@ -34,6 +34,7 @@ Init_id(void) REGISTER_SYMID(id_core_hash_from_ary, "core#hash_from_ary"); REGISTER_SYMID(id_core_hash_merge_ary, "core#hash_merge_ary"); REGISTER_SYMID(id_core_hash_merge_ptr, "core#hash_merge_ptr"); + REGISTER_SYMID(id_core_hash_merge_kwd, "core#hash_merge_kwd"); REGISTER_SYMID(idEach, "each"); REGISTER_SYMID(idLength, "length"); @@ -759,6 +759,7 @@ static void token_info_pop(struct parser_params*, const char *token); %token tLBRACE /* { */ %token tLBRACE_ARG /* { */ %token tSTAR /* * */ +%token tDSTAR /* ** */ %token tAMPER /* & */ %token tLAMBDA /* -> */ %token tSYMBEG tSTRING_BEG tXSTRING_BEG tREGEXP_BEG tWORDS_BEG tQWORDS_BEG @@ -805,6 +806,7 @@ static void token_info_pop(struct parser_params*, const char *token); %nonassoc id_core_hash_from_ary %nonassoc id_core_hash_merge_ary %nonassoc id_core_hash_merge_ptr +%nonassoc id_core_hash_merge_kwd %token tLAST_TOKEN @@ -1918,6 +1920,7 @@ op : '|' { ifndef_ripper($$ = '|'); } | '/' { ifndef_ripper($$ = '/'); } | '%' { ifndef_ripper($$ = '%'); } | tPOW { ifndef_ripper($$ = tPOW); } + | tDSTAR { ifndef_ripper($$ = tDSTAR); } | '!' { ifndef_ripper($$ = '!'); } | '~' { ifndef_ripper($$ = '~'); } | tUPLUS { ifndef_ripper($$ = tUPLUS); } @@ -4699,7 +4702,11 @@ f_kwarg : f_kw } ; -f_kwrest : tPOW tIDENTIFIER +kwrest_mark : tPOW + | tDSTAR + ; + +f_kwrest : kwrest_mark tIDENTIFIER { $$ = $2; } @@ -4923,6 +4930,16 @@ assoc : arg_value tASSOC arg_value $$ = dispatch2(assoc_new, $1, $2); %*/ } + | tDSTAR arg_value + { + /*%%%*/ + $$ = list_append(NEW_LIST(0), $2); + /*% + $$ = dispatch1(assoc_splat, $2); + %*/ + } + ; + ; operation : tIDENTIFIER @@ -6890,7 +6907,17 @@ parser_yylex(struct parser_params *parser) return tOP_ASGN; } pushback(c); - c = tPOW; + if (IS_SPCARG(c)) { + rb_warning0("`**' interpreted as argument prefix"); + c = tDSTAR; + } + else if (IS_BEG()) { + c = tDSTAR; + } + else { + warn_balanced("**", "argument prefix"); + c = tPOW; + } } else { if (c == '=') { @@ -9701,6 +9728,7 @@ static const struct { {'+', "+(binary)"}, {'-', "-(binary)"}, {tPOW, "**"}, + {tDSTAR, "**"}, {tUPLUS, "+@"}, {tUMINUS, "-@"}, {tCMP, "<=>"}, diff --git a/test/ripper/test_parser_events.rb b/test/ripper/test_parser_events.rb index af41d3e6a4..3b4785804d 100644 --- a/test/ripper/test_parser_events.rb +++ b/test/ripper/test_parser_events.rb @@ -146,6 +146,12 @@ class TestRipper::ParserEvents < Test::Unit::TestCase assert_equal true, thru_assoc_new end + def test_assoc_splat + thru_assoc_splat = false + parse('m(**h)', :on_assoc_splat) {thru_assoc_splat = true} + assert_equal true, thru_assoc_splat + end + def test_aref_field assert_equal '[assign(aref_field(vcall(a),[1]),2)]', parse('a[1]=2') end diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb index fd5b14d6c3..55655e2e11 100644 --- a/test/ruby/test_syntax.rb +++ b/test/ruby/test_syntax.rb @@ -90,6 +90,21 @@ class TestSyntax < Test::Unit::TestCase assert_equal({foo: 1, bar: 2}, o.kw(foo: 1, bar: 2), bug5989) end + def test_keyword_splat + assert_valid_syntax("foo(**h)", __FILE__) + o = Object.new + def o.kw(k1: 1, k2: 2) [k1, k2] end + h = {k1: 11, k2: 12} + assert_equal([11, 12], o.kw(**h)) + assert_equal([11, 22], o.kw(k2: 22, **h)) + assert_equal([11, 12], o.kw(**h, **{k2: 22})) + assert_equal([11, 22], o.kw(**{k2: 22}, **h)) + h = {k3: 31} + assert_raise(ArgumentError) {o.kw(**h)} + h = {"k1"=>11, k2: 12} + assert_raise(TypeError) {o.kw(**h)} + end + def test_warn_grouped_expression assert_warn("test:2: warning: (...) interpreted as grouped expression\n") do assert_valid_syntax("foo \\\n(\n true)", "test") {$VERBOSE = true} @@ -2068,6 +2068,30 @@ m_core_hash_merge_ptr(int argc, VALUE *argv, VALUE recv) return hash; } +static int +kwmerge_ii(st_data_t *key, st_data_t *value, st_data_t arg, int existing) +{ + if (existing) return ST_STOP; + *value = arg; + return ST_CONTINUE; +} + +static int +kwmerge_i(VALUE key, VALUE value, VALUE hash) +{ + if (!SYMBOL_P(key)) Check_Type(key, T_SYMBOL); + st_update(RHASH_TBL(hash), key, kwmerge_ii, (st_data_t)value); + return ST_CONTINUE; +} + +static VALUE +m_core_hash_merge_kwd(VALUE recv, VALUE hash, VALUE kw) +{ + kw = rb_convert_type(kw, T_HASH, "Hash", "to_hash"); + rb_hash_foreach(kw, kwmerge_i, hash); + return hash; +} + extern VALUE *rb_gc_stack_start; extern size_t rb_gc_stack_maxsize; #ifdef __ia64 @@ -2134,6 +2158,7 @@ Init_VM(void) rb_define_method_id(klass, id_core_hash_from_ary, m_core_hash_from_ary, 1); rb_define_method_id(klass, id_core_hash_merge_ary, m_core_hash_merge_ary, 2); rb_define_method_id(klass, id_core_hash_merge_ptr, m_core_hash_merge_ptr, -1); + rb_define_method_id(klass, id_core_hash_merge_kwd, m_core_hash_merge_kwd, 2); rb_obj_freeze(fcore); rb_gc_register_mark_object(fcore); rb_mRubyVMFrozenCore = fcore; |