diff --git a/ext/ripper/eventids2.c b/ext/ripper/eventids2.c index 8b0d9c3..7ef8cc2 100644 --- a/ext/ripper/eventids2.c +++ b/ext/ripper/eventids2.c @@ -38,6 +38,7 @@ static ID ripper_id_tstring_content; static ID ripper_id_tstring_end; static ID ripper_id_words_beg; static ID ripper_id_qwords_beg; +static ID ripper_id_qsymbols_beg; static ID ripper_id_words_sep; static ID ripper_id_regexp_beg; static ID ripper_id_regexp_end; @@ -91,6 +92,7 @@ ripper_init_eventids2(VALUE self) ripper_id_tstring_end = rb_intern_const("on_tstring_end"); ripper_id_words_beg = rb_intern_const("on_words_beg"); ripper_id_qwords_beg = rb_intern_const("on_qwords_beg"); + ripper_id_qsymbols_beg = rb_intern_const("on_qsymbols_beg"); ripper_id_words_sep = rb_intern_const("on_words_sep"); ripper_id_regexp_beg = rb_intern_const("on_regexp_beg"); ripper_id_regexp_end = rb_intern_const("on_regexp_end"); @@ -230,6 +232,7 @@ static const struct token_assoc { {tOROP, &ripper_id_op}, {tPOW, &ripper_id_op}, {tQWORDS_BEG, &ripper_id_qwords_beg}, + {tQSYMBOLS_BEG, &ripper_id_qsymbols_beg}, {tREGEXP_BEG, &ripper_id_regexp_beg}, {tREGEXP_END, &ripper_id_regexp_end}, {tRPAREN, &ripper_id_rparen}, diff --git a/parse.y b/parse.y index 015ad51..ff6c597 100644 --- a/parse.y +++ b/parse.y @@ -681,7 +681,7 @@ static void token_info_pop(struct parser_params*, const char *token); %type singleton strings string string1 xstring regexp %type string_contents xstring_contents regexp_contents string_content -%type words qwords word_list qword_list word +%type words qwords qsymbols word_list qword_list sym_list word %type literal numeric dsym cpath %type top_compstmt top_stmts top_stmt %type bodystmt compstmt stmts stmt expr arg primary command command_call method_call @@ -732,7 +732,7 @@ static void token_info_pop(struct parser_params*, const char *token); %token tSTAR /* * */ %token tAMPER /* & */ %token tLAMBDA /* -> */ -%token tSYMBEG tSTRING_BEG tXSTRING_BEG tREGEXP_BEG tWORDS_BEG tQWORDS_BEG +%token tSYMBEG tSTRING_BEG tXSTRING_BEG tREGEXP_BEG tWORDS_BEG tQWORDS_BEG tQSYMBOLS_BEG %token tSTRING_DBEG tSTRING_DVAR tSTRING_END tLAMBEG /* @@ -2604,6 +2604,7 @@ primary : literal | regexp | words | qwords + | qsymbols | var_ref | backref | tFID @@ -4029,6 +4030,25 @@ qwords : tQWORDS_BEG ' ' tSTRING_END } ; +qsymbols : tQSYMBOLS_BEG ' ' tSTRING_END + { + /*%%%*/ + $$ = NEW_ZARRAY(); + /*% + $$ = dispatch0(qsymbols_new); + $$ = dispatch1(array, $$); + %*/ + } + | tQSYMBOLS_BEG sym_list tSTRING_END + { + /*%%%*/ + $$ = $2; + /*% + $$ = dispatch1(array, $2); + %*/ + } + ; + qword_list : /* none */ { /*%%%*/ @@ -4047,6 +4067,28 @@ qword_list : /* none */ } ; +sym_list : /* none */ + { + /*%%%*/ + $$ = 0; + /*% + $$ = dispatch0(qsymbols_new); + %*/ + } + | sym_list tSTRING_CONTENT ' ' + { + /*%%%*/ + VALUE lit; + lit = $2->nd_lit; + $2->nd_lit = ID2SYM(rb_intern_str(lit)); + nd_set_type($2, NODE_LIT); + $$ = list_append($1, $2); + /*% + $$ = dispatch2(qsymbols_add, $1, $2); + %*/ + } + ; + string_contents : /* none */ { /*%%%*/ @@ -7561,6 +7603,12 @@ parser_yylex(struct parser_params *parser) pushback(c); return tQWORDS_BEG; + case 'S': + lex_strterm = NEW_STRTERM(str_sword, term, paren); + do {c = nextc();} while (ISSPACE(c)); + pushback(c); + return tQSYMBOLS_BEG; + case 'x': lex_strterm = NEW_STRTERM(str_xquote, term, paren); return tXSTRING_BEG; diff --git a/test/ripper/test_parser_events.rb b/test/ripper/test_parser_events.rb index 14799d3..d456d9e 100644 --- a/test/ripper/test_parser_events.rb +++ b/test/ripper/test_parser_events.rb @@ -745,12 +745,24 @@ class TestRipper::ParserEvents < Test::Unit::TestCase assert_equal true, thru_qwords_add end + def test_qsymbols_add + thru_qsymbols_add = false + parse('%S[a]', :on_qsymbols_add) {thru_qsymbols_add = true} + assert_equal true, thru_qsymbols_add + end + def test_qwords_new thru_qwords_new = false parse('%w[]', :on_qwords_new) {thru_qwords_new = true} assert_equal true, thru_qwords_new end + def test_qsymbols_new + thru_qsymbols_new = false + parse('%S[]', :on_qsymbols_new) {thru_qsymbols_new = true} + assert_equal true, thru_qsymbols_new + end + def test_redo thru_redo = false parse('redo', :on_redo) {thru_redo = true} diff --git a/test/ripper/test_scanner_events.rb b/test/ripper/test_scanner_events.rb index 7d5bb3c..0cdbc63 100644 --- a/test/ripper/test_scanner_events.rb +++ b/test/ripper/test_scanner_events.rb @@ -607,6 +607,17 @@ class TestRipper::ScannerEvents < Test::Unit::TestCase scan('qwords_beg', '%w( w w w )') end + def test_qsymbols_beg + assert_equal [], + scan('qsymbols_beg', '') + assert_equal ['%S('], + scan('qsymbols_beg', '%S()') + assert_equal ['%S('], + scan('qsymbols_beg', '%S(w w w)') + assert_equal ['%S( '], + scan('qsymbols_beg', '%S( w w w )') + end + # FIXME: Close paren must not present (`words_end' scanner event?). def test_words_sep assert_equal [], diff --git a/test/ruby/test_array.rb b/test/ruby/test_array.rb index 6452476..e97cb11 100644 --- a/test/ruby/test_array.rb +++ b/test/ruby/test_array.rb @@ -12,6 +12,11 @@ class TestArray < Test::Unit::TestCase $VERBOSE = @verbose end + def test_percent_S + assert_equal([:foo, :bar], %S[foo bar]) + assert_equal([:"\"foo"], %S["foo]) + end + def test_0_literal assert_equal([1, 2, 3, 4], [1, 2] + [3, 4]) assert_equal([1, 2, 1, 2], [1, 2] * 2)