summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2014-09-20 01:48:43 +0000
committernobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2014-09-20 01:48:43 +0000
commitb0c03f63e5b3865684d8d37e8cac666ba7e0e3e0 (patch)
tree96a42bddc5ce6514cb9eda0322e87c8c5d7d41c4
parent7190c04417a52c29d1dcf6b2f04bb55b30cbfd9f (diff)
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
-rw-r--r--ChangeLog5
-rw-r--r--ext/ripper/eventids2.c3
-rw-r--r--parse.y47
-rw-r--r--test/ripper/test_parser_events.rb4
-rw-r--r--test/ripper/test_scanner_events.rb5
-rw-r--r--test/ruby/test_hash.rb11
6 files changed, 66 insertions, 9 deletions
diff --git a/ChangeLog b/ChangeLog
index e4e348b..c991fc8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+Sat Sep 20 10:48:41 2014 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * 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 <nobu@ruby-lang.org>
* compile.c (iseq_set_arguments): store local variable IDs in
diff --git a/ext/ripper/eventids2.c b/ext/ripper/eventids2.c
index 423f9d7..ced29e9 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 68d6397..2f2e48d 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 '?'
+ {
+ $<val>$ = cond_stack;
+ cond_stack = 0;
+ COND_PUSH(1);
+ }
+ arg opt_nl ':'
+ {
+ cond_stack = $<val>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
{
$<node>$ = lex_strterm;
lex_strterm = 0;
+ }
+ {
+ $<num>$ = lex_state;
lex_state = EXPR_BEG;
}
{
@@ -4228,12 +4241,13 @@ string_content : tSTRING_CONTENT
cond_stack = $<val>1;
cmdarg_stack = $<val>2;
lex_strterm = $<node>3;
- brace_nest = $<num>4;
+ lex_state = $<num>4;
+ brace_nest = $<num>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 537d3fc..1942157 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 ac34ac5..91903e3 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 dd93a63..4431552 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(*)