summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog15
-rw-r--r--NEWS3
-rw-r--r--ext/ripper/eventids2.c6
-rw-r--r--parse.y98
-rw-r--r--test/ripper/test_parser_events.rb24
-rw-r--r--test/ripper/test_scanner_events.rb22
-rw-r--r--test/ruby/test_array.rb11
7 files changed, 177 insertions, 2 deletions
diff --git a/ChangeLog b/ChangeLog
index e782653a3d..bb78f62b95 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+Wed Jul 25 03:05:06 2012 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * parse.y: added symbols and qsymbols productions for %i and %I
+ support. %i{ .. } returns a list of symbols without interpolation,
+ %I{ .. } returns a list of symbols with interpolation. Thanks to
+ Josh Susser for inspiration of this feature. [Feature #4985]
+
+ * ext/ripper/eventids2.c: added ripper events for %i and %I.
+
+ * test/ripper/test_parser_events.rb: ripper tests
+
+ * test/ripper/test_scanner_events.rb: ditto
+
+ * test/ruby/test_array.rb: test for %i and %I behavior
+
Tue Jul 24 23:34:43 2012 Hiroshi Shirosaki <h.shirosaki@gmail.com>
* include/ruby/win32.h (rb_w32_pow): add new function.
diff --git a/NEWS b/NEWS
index 30e44679d7..85add229de 100644
--- a/NEWS
+++ b/NEWS
@@ -169,6 +169,9 @@ with all sufficient information, see the ChangeLog file.
long.
=== Language changes
+
+ * Added %i and %I for symbol list creation (similar to %w and %W).
+
=== Compatibility issues (excluding feature bug fixes)
* Signal.trap
diff --git a/ext/ripper/eventids2.c b/ext/ripper/eventids2.c
index faeabd4b65..841a18c583 100644
--- a/ext/ripper/eventids2.c
+++ b/ext/ripper/eventids2.c
@@ -38,6 +38,8 @@ 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_symbols_beg;
static ID ripper_id_words_sep;
static ID ripper_id_regexp_beg;
static ID ripper_id_regexp_end;
@@ -91,6 +93,8 @@ ripper_init_eventids2(void)
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_symbols_beg = rb_intern_const("on_symbols_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");
@@ -228,6 +232,8 @@ 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},
+ {tSYMBOLS_BEG, &ripper_id_symbols_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 049e356276..cc0bae59b8 100644
--- a/parse.y
+++ b/parse.y
@@ -715,7 +715,7 @@ static void token_info_pop(struct parser_params*, const char *token);
%type <node> singleton strings string string1 xstring regexp
%type <node> string_contents xstring_contents regexp_contents string_content
-%type <node> words qwords word_list qword_list word
+%type <node> words symbols symbol_list qwords qsymbols word_list qword_list qsym_list word
%type <node> literal numeric dsym cpath
%type <node> top_compstmt top_stmts top_stmt
%type <node> bodystmt compstmt stmts stmt_or_begin stmt expr arg primary command command_call method_call
@@ -769,7 +769,7 @@ static void token_info_pop(struct parser_params*, const char *token);
%token tDSTAR /* ** */
%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 tSYMBOLS_BEG tQSYMBOLS_BEG
%token tSTRING_DBEG tSTRING_DEND tSTRING_DVAR tSTRING_END tLAMBEG
/*
@@ -2672,6 +2672,8 @@ primary : literal
| regexp
| words
| qwords
+ | symbols
+ | qsymbols
| var_ref
| backref
| tFID
@@ -4105,6 +4107,45 @@ word : string_content
}
;
+symbols : tSYMBOLS_BEG ' ' tSTRING_END
+ {
+ /*%%%*/
+ $$ = NEW_ZARRAY();
+ /*%
+ $$ = dispatch0(symbols_new);
+ $$ = dispatch1(array, $$);
+ %*/
+ }
+ | tSYMBOLS_BEG symbol_list tSTRING_END
+ {
+ /*%%%*/
+ $$ = $2;
+ /*%
+ $$ = dispatch1(array, $2);
+ %*/
+ }
+ ;
+
+symbol_list : /* none */
+ {
+ /*%%%*/
+ $$ = 0;
+ /*%
+ $$ = dispatch0(symbols_new);
+ %*/
+ }
+ | symbol_list word ' '
+ {
+ /*%%%*/
+ $2 = evstr2dstr($2);
+ nd_set_type($2, NODE_DSYM);
+ $$ = list_append($1, $2);
+ /*%
+ $$ = dispatch2(symbols_add, $1, $2);
+ %*/
+ }
+ ;
+
qwords : tQWORDS_BEG ' ' tSTRING_END
{
/*%%%*/
@@ -4124,6 +4165,25 @@ qwords : tQWORDS_BEG ' ' tSTRING_END
}
;
+qsymbols : tQSYMBOLS_BEG ' ' tSTRING_END
+ {
+ /*%%%*/
+ $$ = NEW_ZARRAY();
+ /*%
+ $$ = dispatch0(qsymbols_new);
+ $$ = dispatch1(array, $$);
+ %*/
+ }
+ | tQSYMBOLS_BEG qsym_list tSTRING_END
+ {
+ /*%%%*/
+ $$ = $2;
+ /*%
+ $$ = dispatch1(array, $2);
+ %*/
+ }
+ ;
+
qword_list : /* none */
{
/*%%%*/
@@ -4142,6 +4202,28 @@ qword_list : /* none */
}
;
+qsym_list : /* none */
+ {
+ /*%%%*/
+ $$ = 0;
+ /*%
+ $$ = dispatch0(qsymbols_new);
+ %*/
+ }
+ | qsym_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 */
{
/*%%%*/
@@ -7796,6 +7878,18 @@ parser_yylex(struct parser_params *parser)
pushback(c);
return tQWORDS_BEG;
+ case 'I':
+ lex_strterm = NEW_STRTERM(str_dword, term, paren);
+ do {c = nextc();} while (ISSPACE(c));
+ pushback(c);
+ return tSYMBOLS_BEG;
+
+ case 'i':
+ 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 3b4785804d..278b47623a 100644
--- a/test/ripper/test_parser_events.rb
+++ b/test/ripper/test_parser_events.rb
@@ -755,12 +755,36 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
assert_equal true, thru_qwords_add
end
+ def test_qsymbols_add
+ thru_qsymbols_add = false
+ parse('%i[a]', :on_qsymbols_add) {thru_qsymbols_add = true}
+ assert_equal true, thru_qsymbols_add
+ end
+
+ def test_symbols_add
+ thru_symbols_add = false
+ parse('%I[a]', :on_symbols_add) {thru_symbols_add = true}
+ assert_equal true, thru_symbols_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('%i[]', :on_qsymbols_new) {thru_qsymbols_new = true}
+ assert_equal true, thru_qsymbols_new
+ end
+
+ def test_symbols_new
+ thru_symbols_new = false
+ parse('%I[]', :on_symbols_new) {thru_symbols_new = true}
+ assert_equal true, thru_symbols_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 5b58dd8289..bcbbf0192c 100644
--- a/test/ripper/test_scanner_events.rb
+++ b/test/ripper/test_scanner_events.rb
@@ -608,6 +608,28 @@ 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 ['%i('],
+ scan('qsymbols_beg', '%i()')
+ assert_equal ['%i('],
+ scan('qsymbols_beg', '%i(w w w)')
+ assert_equal ['%i( '],
+ scan('qsymbols_beg', '%i( w w w )')
+ end
+
+ def test_symbols_beg
+ assert_equal [],
+ scan('symbols_beg', '')
+ assert_equal ['%I('],
+ scan('symbols_beg', '%I()')
+ assert_equal ['%I('],
+ scan('symbols_beg', '%I(w w w)')
+ assert_equal ['%I( '],
+ scan('symbols_beg', '%I( 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 fff55e13ff..4a9a58b723 100644
--- a/test/ruby/test_array.rb
+++ b/test/ruby/test_array.rb
@@ -12,6 +12,17 @@ class TestArray < Test::Unit::TestCase
$VERBOSE = @verbose
end
+ def test_percent_i
+ assert_equal([:foo, :bar], %i[foo bar])
+ assert_equal([:"\"foo"], %i["foo])
+ end
+
+ def test_percent_I
+ x = 10
+ assert_equal([:foo, :b10], %I[foo b#{x}])
+ assert_equal([:"\"foo10"], %I["foo#{x}])
+ end
+
def test_0_literal
assert_equal([1, 2, 3, 4], [1, 2] + [3, 4])
assert_equal([1, 2, 1, 2], [1, 2] * 2)