From b0c03f63e5b3865684d8d37e8cac666ba7e0e3e0 Mon Sep 17 00:00:00 2001 From: nobu Date: Sat, 20 Sep 2014 01:48:43 +0000 Subject: [PATCH] parse.y: quoted ID key * parse.y (assoc): allow quoted ID as a key of a hash literal. [ruby-core:34453] [Feature #4276] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@47649 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 5 ++++ ext/ripper/eventids2.c | 3 ++ parse.y | 47 ++++++++++++++++++++++++------ test/ripper/test_parser_events.rb | 4 +++ test/ripper/test_scanner_events.rb | 5 ++++ test/ruby/test_hash.rb | 11 +++++++ 6 files changed, 66 insertions(+), 9 deletions(-) diff --git a/ChangeLog b/ChangeLog index e4e348b8d9..c991fc801e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Sat Sep 20 10:48:41 2014 Nobuyoshi Nakada + + * parse.y (assoc): allow quoted ID as a key of a hash literal. + [ruby-core:34453] [Feature #4276] + Sat Sep 20 10:23:00 2014 Nobuyoshi Nakada * compile.c (iseq_set_arguments): store local variable IDs in diff --git a/ext/ripper/eventids2.c b/ext/ripper/eventids2.c index 423f9d7e29..ced29e93c4 100644 --- a/ext/ripper/eventids2.c +++ b/ext/ripper/eventids2.c @@ -46,6 +46,7 @@ static ID ripper_id_rational; static ID ripper_id_regexp_beg; static ID ripper_id_regexp_end; static ID ripper_id_label; +static ID ripper_id_label_end; static ID ripper_id_tlambda; static ID ripper_id_tlambeg; @@ -103,6 +104,7 @@ ripper_init_eventids2(void) ripper_id_regexp_beg = rb_intern_const("on_regexp_beg"); ripper_id_regexp_end = rb_intern_const("on_regexp_end"); ripper_id_label = rb_intern_const("on_label"); + ripper_id_label_end = rb_intern_const("on_label_end"); ripper_id_tlambda = rb_intern_const("on_tlambda"); ripper_id_tlambeg = rb_intern_const("on_tlambeg"); @@ -259,6 +261,7 @@ static const struct token_assoc { {tWORDS_BEG, &ripper_id_words_beg}, {tXSTRING_BEG, &ripper_id_backtick}, {tLABEL, &ripper_id_label}, + {tLABEL_END, &ripper_id_label_end}, {tLAMBDA, &ripper_id_tlambda}, {tLAMBEG, &ripper_id_tlambeg}, diff --git a/parse.y b/parse.y index 68d6397c69..2f2e48de8d 100644 --- a/parse.y +++ b/parse.y @@ -823,7 +823,7 @@ static void token_info_pop(struct parser_params*, const char *token); %token tAMPER "&" %token tLAMBDA "->" %token tSYMBEG tSTRING_BEG tXSTRING_BEG tREGEXP_BEG tWORDS_BEG tQWORDS_BEG tSYMBOLS_BEG tQSYMBOLS_BEG -%token tSTRING_DBEG tSTRING_DEND tSTRING_DVAR tSTRING_END tLAMBEG +%token tSTRING_DBEG tSTRING_DEND tSTRING_DVAR tSTRING_END tLAMBEG tLABEL_END /* * precedence table @@ -2305,14 +2305,24 @@ arg : lhs '=' arg $$ = dispatch1(defined, $4); %*/ } - | arg '?' arg opt_nl ':' arg + | arg '?' + { + $$ = cond_stack; + cond_stack = 0; + COND_PUSH(1); + } + arg opt_nl ':' + { + cond_stack = $3; + } + arg { /*%%%*/ value_expr($1); - $$ = NEW_IF(cond($1), $3, $6); + $$ = NEW_IF(cond($1), $4, $8); fixpos($$, $1); /*% - $$ = dispatch3(ifop, $1, $3, $6); + $$ = dispatch3(ifop, $1, $4, $8); %*/ } | primary @@ -4217,6 +4227,9 @@ string_content : tSTRING_CONTENT { $$ = lex_strterm; lex_strterm = 0; + } + { + $$ = lex_state; lex_state = EXPR_BEG; } { @@ -4228,12 +4241,13 @@ string_content : tSTRING_CONTENT cond_stack = $1; cmdarg_stack = $2; lex_strterm = $3; - brace_nest = $4; + lex_state = $4; + brace_nest = $5; /*%%%*/ - if ($5) $5->flags &= ~NODE_FL_NEWLINE; - $$ = new_evstr($5); + if ($6) $6->flags &= ~NODE_FL_NEWLINE; + $$ = new_evstr($6); /*% - $$ = dispatch1(string_embexpr, $5); + $$ = dispatch1(string_embexpr, $6); %*/ } ; @@ -4947,6 +4961,14 @@ assoc : arg_value tASSOC arg_value $$ = dispatch2(assoc_new, $1, $2); %*/ } + | tSTRING_BEG string_contents tLABEL_END arg_value + { + /*%%%*/ + $$ = list_append(NEW_LIST(dsym_node($2)), $4); + /*% + $$ = dispatch2(assoc_new, dispatch1(dyna_symbol, $2), $4); + %*/ + } | tDSTAR arg_value { /*%%%*/ @@ -7653,7 +7675,14 @@ parser_yylex(struct parser_params *parser) } else { token = parse_string(lex_strterm); - if (token == tSTRING_END || token == tREGEXP_END) { + if (token == tSTRING_END && (peek_n('\'', -1) || peek_n('"', -1))) { + if (((IS_lex_state(EXPR_BEG | EXPR_ENDFN) && !COND_P()) || IS_ARG()) && + IS_LABEL_SUFFIX(0)) { + nextc(); + token = tLABEL_END; + } + } + if (token == tSTRING_END || token == tREGEXP_END || token == tLABEL_END) { rb_gc_force_recycle((VALUE)lex_strterm); lex_strterm = 0; lex_state = EXPR_END; diff --git a/test/ripper/test_parser_events.rb b/test/ripper/test_parser_events.rb index 537d3fc406..1942157b89 100644 --- a/test/ripper/test_parser_events.rb +++ b/test/ripper/test_parser_events.rb @@ -548,6 +548,10 @@ class TestRipper::ParserEvents < Test::Unit::TestCase thru_dyna_symbol = false parse(':"#{foo}"', :on_dyna_symbol) {thru_dyna_symbol = true} assert_equal true, thru_dyna_symbol + + thru_dyna_symbol = false + parse('{"#{foo}": 1}', :on_dyna_symbol) {thru_dyna_symbol = true} + assert_equal true, thru_dyna_symbol end def test_else diff --git a/test/ripper/test_scanner_events.rb b/test/ripper/test_scanner_events.rb index ac34ac57df..91903e3dcd 100644 --- a/test/ripper/test_scanner_events.rb +++ b/test/ripper/test_scanner_events.rb @@ -878,6 +878,11 @@ class TestRipper::ScannerEvents < Test::Unit::TestCase scan('label', '{foo: 1}') end + def test_label_end + assert_equal %w(":), + scan('label_end', '{"foo-bar": 1}') + end + def test_tlambda assert_equal %w(->), scan('tlambda', '->{}') diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb index dd93a63340..4431552dd9 100644 --- a/test/ruby/test_hash.rb +++ b/test/ruby/test_hash.rb @@ -1269,6 +1269,17 @@ class TestHash < Test::Unit::TestCase assert_equal(bug9381, hash[wrapper.new(5)]) end + def test_label_syntax + return unless @cls == Hash + + feature4935 = '[ruby-core:37553] [Feature #4935]' + x = 'world' + hash = assert_nothing_raised(SyntaxError) do + break eval(%q({foo: 1, "foo-bar": 2, "hello-#{x}": 3, 'hello-#{x}': 4})) + end + assert_equal({:foo => 1, :'foo-bar' => 2, :'hello-world' => 3, :'hello-#{x}' => 4}, hash) + end + class TestSubHash < TestHash class SubHash < Hash def reject(*)