[前][次][番号順一覧][スレッド一覧]

ruby-changes:54453

From: nobu <ko1@a...>
Date: Tue, 1 Jan 2019 00:10:43 +0900 (JST)
Subject: [ruby-changes:54453] nobu:r66667 (trunk): Method reference operator

nobu	2019-01-01 00:00:37 +0900 (Tue, 01 Jan 2019)

  New Revision: 66667

  https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=66667

  Log:
    Method reference operator
    
    Introduce the new operator for method reference, `.:`.
    [Feature #12125] [Feature #13581]
    [EXPERIMENTAL]

  Modified files:
    trunk/NEWS
    trunk/ast.c
    trunk/compile.c
    trunk/defs/id.def
    trunk/ext/objspace/objspace.c
    trunk/ext/ripper/eventids2.c
    trunk/insns.def
    trunk/node.c
    trunk/node.h
    trunk/parse.y
    trunk/test/ripper/test_parser_events.rb
    trunk/test/ripper/test_scanner_events.rb
    trunk/test/ruby/test_ast.rb
    trunk/test/ruby/test_method.rb
    trunk/test/ruby/test_syntax.rb
Index: defs/id.def
===================================================================
--- defs/id.def	(revision 66666)
+++ defs/id.def	(revision 66667)
@@ -106,6 +106,7 @@ token_ops = %[\ https://github.com/ruby/ruby/blob/trunk/defs/id.def#L106
   ANDOP         &&
   OROP          ||
   ANDDOT        &.
+  METHREF       .:
 ]
 
 class KeywordError < RuntimeError
Index: insns.def
===================================================================
--- insns.def	(revision 66666)
+++ insns.def	(revision 66667)
@@ -702,6 +702,16 @@ checktype https://github.com/ruby/ruby/blob/trunk/insns.def#L702
     ret = (TYPE(val) == (int)type) ? Qtrue : Qfalse;
 }
 
+/* get method reference. */
+DEFINE_INSN
+methodref
+(ID id)
+(VALUE val)
+(VALUE ret)
+{
+    ret = rb_obj_method(val, ID2SYM(id));
+}
+
 /**********************************************************/
 /* deal with control flow 1: class/module                 */
 /**********************************************************/
Index: ast.c
===================================================================
--- ast.c	(revision 66666)
+++ ast.c	(revision 66667)
@@ -468,6 +468,9 @@ node_children(rb_ast_t *ast, NODE *node) https://github.com/ruby/ruby/blob/trunk/ast.c#L468
                                     NEW_CHILD(ast, node->nd_args));
       case NODE_VCALL:
         return rb_ary_new_from_args(1, ID2SYM(node->nd_mid));
+      case NODE_METHREF:
+        return rb_ary_new_from_args(2, NEW_CHILD(ast, node->nd_recv),
+                                    ID2SYM(node->nd_mid));
       case NODE_SUPER:
         return rb_ary_new_from_node_args(ast, 1, node->nd_args);
       case NODE_ZSUPER:
Index: compile.c
===================================================================
--- compile.c	(revision 66666)
+++ compile.c	(revision 66667)
@@ -4583,9 +4583,11 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCH https://github.com/ruby/ruby/blob/trunk/compile.c#L4583
       case NODE_OPCALL:
       case NODE_VCALL:
       case NODE_FCALL:
+      case NODE_METHREF:
       case NODE_ATTRASGN:{
 	const int explicit_receiver =
 	    (type == NODE_CALL || type == NODE_OPCALL ||
+             type == NODE_METHREF ||
 	     (type == NODE_ATTRASGN && !private_recv_p(node)));
 
 	if (!lfinish[1] && (node->nd_args || explicit_receiver)) {
@@ -7552,6 +7554,10 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK https://github.com/ruby/ruby/blob/trunk/compile.c#L7554
 	}
 	break;
       }
+      case NODE_METHREF:
+        CHECK(COMPILE_(ret, "receiver", node->nd_recv, popped));
+        ADD_ELEM(ret, &new_insn_body(iseq, line, BIN(methodref), 1, ID2SYM(node->nd_mid))->link);
+        break;
       default:
 	UNKNOWN_NODE("iseq_compile_each", node, COMPILE_NG);
       ng:
Index: parse.y
===================================================================
--- parse.y	(revision 66666)
+++ parse.y	(revision 66667)
@@ -886,6 +886,7 @@ static void token_info_warn(struct parse https://github.com/ruby/ruby/blob/trunk/parse.y#L886
 %token tRSHFT		RUBY_TOKEN(RSHFT)  ">>"
 %token <id> tANDDOT	RUBY_TOKEN(ANDDOT) "&."
 %token <id> tCOLON2	RUBY_TOKEN(COLON2) "::"
+%token <id> tMETHREF	RUBY_TOKEN(METHREF) ".:"
 %token tCOLON3		":: at EXPR_BEG"
 %token <id> tOP_ASGN	/* +=, -=  etc. */
 %token tASSOC		"=>"
@@ -2710,6 +2711,13 @@ primary		: literal https://github.com/ruby/ruby/blob/trunk/parse.y#L2711
 		    /*% %*/
 		    /*% ripper: retry! %*/
 		    }
+		| primary_value tMETHREF operation2
+		    {
+		    /*%%%*/
+			$$ = NEW_METHREF($1, $3, &@$);
+		    /*% %*/
+		    /*% ripper: methref!($1, $3) %*/
+		    }
 		;
 
 primary_value	: primary
@@ -8060,12 +8068,30 @@ parser_yylex(struct parser_params *p) https://github.com/ruby/ruby/blob/trunk/parse.y#L8068
 
       case '.':
 	SET_LEX_STATE(EXPR_BEG);
-	if ((c = nextc(p)) == '.') {
+	switch (c = nextc(p)) {
+	  case '.':
 	    if ((c = nextc(p)) == '.') {
 		return tDOT3;
 	    }
 	    pushback(p, c);
 	    return tDOT2;
+	  case ':':
+	    switch (c = nextc(p)) {
+	      default:
+		if (!parser_is_identchar(p)) break;
+		/* fallthru */
+	      case '!': case '%': case '&': case '*': case '+':
+	      case '-': case '/': case '<': case '=': case '>':
+	      case '[': case '^': case '`': case '|': case '~':
+		pushback(p, c);
+		SET_LEX_STATE(EXPR_DOT);
+		return tMETHREF;
+	      case -1:
+		break;
+	    }
+	    pushback(p, c);
+	    c = ':';
+	    break;
 	}
 	pushback(p, c);
 	if (c != -1 && ISDIGIT(c)) {
Index: ext/objspace/objspace.c
===================================================================
--- ext/objspace/objspace.c	(revision 66666)
+++ ext/objspace/objspace.c	(revision 66667)
@@ -471,6 +471,7 @@ count_nodes(int argc, VALUE *argv, VALUE https://github.com/ruby/ruby/blob/trunk/ext/objspace/objspace.c#L471
 		COUNT_NODE(NODE_DSYM);
 		COUNT_NODE(NODE_ATTRASGN);
 		COUNT_NODE(NODE_LAMBDA);
+		COUNT_NODE(NODE_METHREF);
 #undef COUNT_NODE
 	      case NODE_LAST: break;
 	    }
Index: ext/ripper/eventids2.c
===================================================================
--- ext/ripper/eventids2.c	(revision 66666)
+++ ext/ripper/eventids2.c	(revision 66667)
@@ -259,6 +259,7 @@ static const struct token_assoc { https://github.com/ruby/ruby/blob/trunk/ext/ripper/eventids2.c#L259
     {tSTAR,			O(op)},
     {tDSTAR,			O(op)},
     {tANDDOT,			O(op)},
+    {tMETHREF,			O(op)},
     {tSTRING_BEG,		O(tstring_beg)},
     {tSTRING_CONTENT,		O(tstring_content)},
     {tSTRING_DBEG,		O(embexpr_beg)},
Index: NEWS
===================================================================
--- NEWS	(revision 66666)
+++ NEWS	(revision 66667)
@@ -14,6 +14,9 @@ sufficient information, see the ChangeLo https://github.com/ruby/ruby/blob/trunk/NEWS#L14
 
 === Language changes
 
+* Method reference operator, <code>.:</code> is introduced as an
+  experimental feature.  [Feature #12125] [Feature #13581]
+
 === Core classes updates (outstanding ones only)
 
 === Stdlib updates (outstanding ones only)
Index: test/ruby/test_ast.rb
===================================================================
--- test/ruby/test_ast.rb	(revision 66666)
+++ test/ruby/test_ast.rb	(revision 66667)
@@ -249,4 +249,13 @@ class TestAst < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_ast.rb#L249
     assert_equal(:b, mid)
     assert_equal(:SCOPE, defn.type)
   end
+
+  def test_methref
+    node = RubyVM::AbstractSyntaxTree.parse("obj.:foo")
+    _, _, body = *node.children
+    assert_equal(:METHREF, body.type)
+    recv, mid = body.children
+    assert_equal(:VCALL, recv.type)
+    assert_equal(:foo, mid)
+  end
 end
Index: test/ruby/test_method.rb
===================================================================
--- test/ruby/test_method.rb	(revision 66666)
+++ test/ruby/test_method.rb	(revision 66667)
@@ -1095,4 +1095,23 @@ class TestMethod < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_method.rb#L1095
       (f >> 5).call(2)
     }
   end
+
+  def test_method_reference_operator
+    m = 1.:succ
+    assert_equal(1.method(:succ), m)
+    assert_equal(2, m.())
+    m = 1.:+
+    assert_equal(1.method(:+), m)
+    assert_equal(42, m.(41))
+    m = 1.:-@
+    assert_equal(1.method(:-@), m)
+    assert_equal(-1, m.())
+    o = Object.new
+    def o.foo; 42; end
+    m = o.method(:foo)
+    assert_equal(m, o.:foo)
+    def o.method(m); nil; end
+    assert_equal(m, o.:foo)
+    assert_nil(o.method(:foo))
+  end
 end
Index: test/ruby/test_syntax.rb
===================================================================
--- test/ruby/test_syntax.rb	(revision 66666)
+++ test/ruby/test_syntax.rb	(revision 66667)
@@ -908,6 +908,7 @@ eom https://github.com/ruby/ruby/blob/trunk/test/ruby/test_syntax.rb#L908
   def test_fluent_dot
     assert_valid_syntax("a\n.foo")
     assert_valid_syntax("a\n&.foo")
+    assert_valid_syntax("a\n.:foo")
   end
 
   def test_no_warning_logop_literal
Index: test/ripper/test_parser_events.rb
===================================================================
--- test/ripper/test_parser_events.rb	(revision 66666)
+++ test/ripper/test_parser_events.rb	(revision 66667)
@@ -434,6 +434,13 @@ class TestRipper::ParserEvents < Test::U https://github.com/ruby/ruby/blob/trunk/test/ripper/test_parser_events.rb#L434
     assert_equal "[call(ref(self),&.,foo,[])]", tree
   end
 
+  def test_methref
+    thru_methref = false
+    tree = parse("obj.:foo", :on_methref) {thru_methref = true}
+    assert_equal true, thru_methref
+    assert_equal "[methref(vcall(obj),foo)]", tree
+  end
+
   def test_excessed_comma
     thru_excessed_comma = false
     parse("proc{|x,|}", :on_excessed_comma) {thru_excessed_comma = true}
Index: test/ripper/test_scanner_events.rb
===================================================================
--- test/ripper/test_scanner_events.rb	(revision 66666)
+++ test/ripper/test_scanner_events.rb	(revision 66667)
@@ -550,6 +550,8 @@ class TestRipper::ScannerEvents < Test:: https://github.com/ruby/ruby/blob/trunk/test/ripper/test_scanner_events.rb#L550
                  scan('op', ':[]=')
     assert_equal ['&.'],
                  scan('op', 'a&.f')
+    assert_equal %w(.:),
+                 scan('op', 'obj.:foo')
     assert_equal [],
                  scan('op', %q[`make all`])
   end
Index: node.c
===================================================================
--- node.c	(revision 66666)
+++ node.c	(revision 66667)
@@ -934,6 +934,15 @@ dump_node(VALUE buf, VALUE indent, int c https://github.com/ruby/ruby/blob/trunk/node.c#L934
 	F_NODE(nd_args, "arguments");
 	return;
 
+      case NODE_METHREF:
+	ANN("method reference");
+	ANN("format: [nd_recv].:[nd_mid]");
+	ANN("example: foo.:method");
+	F_NODE(nd_recv, "receiver");
+	LAST_NODE;
+	F_ID(nd_mid, "method name");
+	return;
+
       case NODE_LAMBDA:
 	ANN("lambda expression");
 	ANN("format: -> [nd_body]");
Index: node.h
===================================================================
--- node.h	(revision 66666)
+++ node.h	(revision 66667)
@@ -120,6 +120,7 @@ enum node_type { https://github.com/ruby/ruby/blob/trunk/node.h#L120
     NODE_DSYM,
     NODE_ATTRASGN,
     NODE_LAMBDA,
+    NODE_METHREF,
     NODE_LAST
 };
 
@@ -361,6 +362,7 @@ typedef struct RNode { https://github.com/ruby/ruby/blob/trunk/node.h#L362
 #define NEW_PREEXE(b,loc) NEW_SCOPE(b,loc)
 #define NEW_POSTEXE(b,loc) NEW_NODE(NODE_POSTEXE,0,b,0,loc)
 #define NEW_ATTRASGN(r,m,a,loc) NEW_NODE(NODE_ATTRASGN,r,m,a,loc)
+#define NEW_METHREF(r,m,loc) NEW_NODE(NODE_METHREF,r,m,0,loc)
 
 #define NODE_SPECIAL_REQUIRED_KEYWORD ((NODE *)-1)
 #define NODE_REQUIRED_KEYWORD_P(node) ((node)->nd_value == NODE_SPECIAL_REQUIRED_KEYWORD)

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]