ruby-changes:56102
From: Nobuyoshi <ko1@a...>
Date: Thu, 13 Jun 2019 18:48:20 +0900 (JST)
Subject: [ruby-changes:56102] Nobuyoshi Nakada: f169043d81 (trunk): Add pipeline operator [Feature #15799]
https://git.ruby-lang.org/ruby.git/commit/?id=f169043d81 From f169043d81524b5b529f2c1e9c35437ba5bc3a7a Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada <nobu@r...> Date: Tue, 23 Apr 2019 13:14:27 +0900 Subject: Add pipeline operator [Feature #15799] diff --git a/NEWS b/NEWS index 4ad38c9..a332cf3 100644 --- a/NEWS +++ b/NEWS @@ -48,6 +48,18 @@ sufficient information, see the ChangeLog file or Redmine https://github.com/ruby/ruby/blob/trunk/NEWS#L48 " # This has been warned since 2.4 EOS +* Pipeline operator is added. + + This code equals to the next code. + + foo() + |> bar 1, 2 + |> display + + foo() + .bar(1, 2) + .display + === Core classes updates (outstanding ones only) Enumerable:: diff --git a/defs/id.def b/defs/id.def index 44890cf..66dfdf9 100644 --- a/defs/id.def +++ b/defs/id.def @@ -111,6 +111,7 @@ token_ops = %[\ https://github.com/ruby/ruby/blob/trunk/defs/id.def#L111 OROP || ANDDOT &. METHREF .: + PIPE |> ] class KeywordError < RuntimeError diff --git a/ext/ripper/eventids2.c b/ext/ripper/eventids2.c index 88985ac..66e7c8f 100644 --- a/ext/ripper/eventids2.c +++ b/ext/ripper/eventids2.c @@ -262,6 +262,7 @@ static const struct token_assoc { https://github.com/ruby/ruby/blob/trunk/ext/ripper/eventids2.c#L262 {tDSTAR, O(op)}, {tANDDOT, O(op)}, {tMETHREF, O(op)}, + {tPIPE, O(op)}, {tSTRING_BEG, O(tstring_beg)}, {tSTRING_CONTENT, O(tstring_content)}, {tSTRING_DBEG, O(embexpr_beg)}, diff --git a/parse.y b/parse.y index c78f513..6f282d8 100644 --- a/parse.y +++ b/parse.y @@ -1002,7 +1002,7 @@ static void token_info_warn(struct parser_params *p, const char *token, token_in https://github.com/ruby/ruby/blob/trunk/parse.y#L1002 %type <node> literal numeric simple_numeric ssym dsym symbol cpath %type <node> top_compstmt top_stmts top_stmt begin_block %type <node> bodystmt compstmt stmts stmt_or_begin stmt expr arg primary command command_call method_call -%type <node> expr_value expr_value_do arg_value primary_value fcall rel_expr +%type <node> expr_value expr_value_do arg_value primary_value fcall rel_expr pipeline %type <node> if_tail opt_else case_body case_args cases opt_rescue exc_list exc_var opt_ensure %type <node> args call_args opt_call_args %type <node> paren_args opt_paren_args args_tail opt_args_tail block_args_tail opt_block_args_tail @@ -1060,6 +1060,7 @@ static void token_info_warn(struct parser_params *p, const char *token, token_in https://github.com/ruby/ruby/blob/trunk/parse.y#L1060 %token <id> tANDDOT RUBY_TOKEN(ANDDOT) "&." %token <id> tCOLON2 RUBY_TOKEN(COLON2) "::" %token <id> tMETHREF RUBY_TOKEN(METHREF) ".:" +%token tPIPE RUBY_TOKEN(PIPE) "|>" %token tCOLON3 ":: at EXPR_BEG" %token <id> tOP_ASGN "operator-assignment" /* +=, -= etc. */ %token tASSOC "=>" @@ -1095,6 +1096,7 @@ static void token_info_warn(struct parser_params *p, const char *token, token_in https://github.com/ruby/ruby/blob/trunk/parse.y#L1096 %nonassoc modifier_if modifier_unless modifier_while modifier_until %left keyword_or keyword_and %right keyword_not +%left tPIPE %nonassoc keyword_defined %right '=' tOP_ASGN %left modifier_rescue @@ -2269,12 +2271,29 @@ arg : lhs '=' arg_rhs https://github.com/ruby/ruby/blob/trunk/parse.y#L2271 /*% %*/ /*% ripper: ifop!($1, $3, $6) %*/ } + | pipeline | primary { $$ = $1; } ; +pipeline : arg tPIPE operation2 opt_paren_args + { + /*%%%*/ + $$ = new_command_qcall(p, ID2VAL(idPIPE), $1, $3, $4, Qnull, &@3, &@$); + /*% %*/ + /*% ripper: command_call!($1, ID2VAL(idPIPE), $3, $4) %*/ + } + | arg tPIPE operation2 opt_paren_args brace_block + { + /*%%%*/ + $$ = new_command_qcall(p, ID2VAL(idPIPE), $1, $3, $4, $5, &@3, &@$); + /*% %*/ + /*% ripper: method_add_block!(command_call!($1, ID2VAL(idPIPE), $3, $4), $5) %*/ + } + ; + relop : '>' {$$ = '>';} | '<' {$$ = '<';} | tGEQ {$$ = idGE;} @@ -8924,6 +8943,10 @@ parser_yylex(struct parser_params *p) https://github.com/ruby/ruby/blob/trunk/parse.y#L8943 SET_LEX_STATE(EXPR_BEG); return tOP_ASGN; } + if (c == '>') { + SET_LEX_STATE(EXPR_DOT); + return tPIPE; + } SET_LEX_STATE(IS_AFTER_OPERATOR() ? EXPR_ARG : EXPR_BEG|EXPR_LABEL); pushback(p, c); return '|'; diff --git a/test/ripper/test_scanner_events.rb b/test/ripper/test_scanner_events.rb index 339a66d..323c409 100644 --- a/test/ripper/test_scanner_events.rb +++ b/test/ripper/test_scanner_events.rb @@ -573,6 +573,8 @@ class TestRipper::ScannerEvents < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ripper/test_scanner_events.rb#L573 scan('op', 'obj.:foo') assert_equal [], scan('op', %q[`make all`]) + assert_equal %w[|>], + scan('op', %q[x|>y]) end def test_symbeg diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb index f2fcf67..7bffb87 100644 --- a/test/ruby/test_syntax.rb +++ b/test/ruby/test_syntax.rb @@ -1379,6 +1379,13 @@ eom https://github.com/ruby/ruby/blob/trunk/test/ruby/test_syntax.rb#L1379 assert_syntax_error('@1', /outside block/) end + def test_pipeline_operator + assert_valid_syntax('x |> y') + x = nil + assert_equal("121", eval('x = 12 |> pow(2) |> to_s(11)')) + assert_equal(12, x) + end + private def not_label(x) @result = x; @not_label ||= nil end -- cgit v0.10.2 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/