summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS.md7
-rw-r--r--parse.y143
-rw-r--r--spec/ruby/language/method_spec.rb13
-rw-r--r--test/ruby/test_syntax.rb7
4 files changed, 132 insertions, 38 deletions
diff --git a/NEWS.md b/NEWS.md
index 5b85ba4eb6..68a1539b9a 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -45,6 +45,13 @@ sufficient information, see the ChangeLog file or Redmine
fib(10) => x
```
+* Endless method definition is added. [EXPERIMENTAL]
+ [[Feature #16746]]
+
+ ```ruby
+ def square(x) = x * x
+ ```
+
## Command line options
## Core classes updates
diff --git a/parse.y b/parse.y
index 1c5ee84d70..794e676cfd 100644
--- a/parse.y
+++ b/parse.y
@@ -59,6 +59,8 @@ struct lex_context {
#include "ruby/util.h"
#include "symbol.h"
+#define AREF(ary, i) RARRAY_AREF(ary, i)
+
#ifndef WARN_PAST_SCOPE
# define WARN_PAST_SCOPE 0
#endif
@@ -906,8 +908,27 @@ static VALUE heredoc_dedent(struct parser_params*,VALUE);
#define ID2VAL(id) (id)
#define TOKEN2VAL(t) ID2VAL(t)
#define KWD2EID(t, v) keyword_##t
+
+static NODE *
+set_defun_body(struct parser_params *p, NODE *n, NODE *args, NODE *body, const YYLTYPE *loc)
+{
+ body = remove_begin(body);
+ reduce_nodes(p, &body);
+ n->nd_defn = NEW_SCOPE(args, body, loc);
+ n->nd_loc = *loc;
+ nd_set_line(n->nd_defn, loc->end_pos.lineno);
+ set_line_body(body, loc->beg_pos.lineno);
+ return n;
+}
#endif /* RIPPER */
+static void
+restore_defun(struct parser_params *p, NODE *name)
+{
+ p->cur_arg = name->nd_vid;
+ p->ctxt.in_def = name->nd_state & 1;
+}
+
#ifndef RIPPER
# define Qnone 0
# define Qnull 0
@@ -1082,7 +1103,7 @@ static int looking_at_eol_p(struct parser_params *p);
%type <node> singleton strings string string1 xstring regexp
%type <node> string_contents xstring_contents regexp_contents string_content
%type <node> words symbols symbol_list qwords qsymbols word_list qword_list qsym_list word
-%type <node> literal numeric simple_numeric ssym dsym symbol cpath
+%type <node> literal numeric simple_numeric ssym dsym symbol cpath def_name defn_head defs_head
%type <node> top_compstmt top_stmts top_stmt begin_block rassign
%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
@@ -1093,7 +1114,7 @@ static int looking_at_eol_p(struct parser_params *p);
%type <node> command_rhs arg_rhs
%type <node> command_asgn mrhs mrhs_arg superclass block_call block_command
%type <node> f_block_optarg f_block_opt
-%type <node> f_arglist f_args f_arg f_arg_item f_optarg f_marg f_marg_list f_margs f_rest_marg
+%type <node> f_arglist f_arglist_opt f_args f_arg f_arg_item f_optarg f_marg f_marg_list f_margs f_rest_marg
%type <node> assoc_list assocs assoc undef_list backref string_dvar for_var
%type <node> block_param opt_block_param block_param_def f_opt
%type <node> f_kwarg f_kw f_block_kwarg f_block_kw
@@ -1635,6 +1656,46 @@ expr : command_call
| arg %prec tLBRACE_ARG
;
+def_name : fname
+ {
+ ID fname = get_id($1);
+ ID cur_arg = p->cur_arg;
+ int in_def = p->ctxt.in_def;
+ numparam_name(p, fname);
+ local_push(p, 0);
+ p->cur_arg = 0;
+ p->ctxt.in_def = 1;
+ $<node>$ = NEW_NODE(NODE_SELF, /*vid*/cur_arg, /*mid*/fname, /*state*/in_def, &@$);
+ /*%%%*/
+ /*%
+ $$ = NEW_RIPPER(fname, get_value($1), $$, &NULL_LOC);
+ %*/
+ }
+ ;
+
+defn_head : k_def def_name
+ {
+ $$ = $2;
+ /*%%%*/
+ $$ = NEW_NODE(NODE_DEFN, 0, $$->nd_mid, $$, &@$);
+ /*% %*/
+ }
+ ;
+
+defs_head : k_def singleton dot_or_colon {SET_LEX_STATE(EXPR_FNAME);} def_name
+ {
+ SET_LEX_STATE(EXPR_ENDFN|EXPR_LABEL); /* force for args */
+ $$ = $5;
+ /*%%%*/
+ $$ = NEW_NODE(NODE_DEFS, $2, $$->nd_mid, $$, &@$);
+ /*%
+ VALUE ary = rb_ary_new_from_args(3, $2, $3, get_value($$));
+ add_mark_object(p, ary);
+ $<node>$->nd_rval = ary;
+ %*/
+ }
+ ;
+
expr_value : expr
{
value_expr($1);
@@ -2392,6 +2453,26 @@ arg : lhs '=' arg_rhs
/*% %*/
/*% ripper: ifop!($1, $3, $6) %*/
}
+ | defn_head f_arglist_opt '=' arg
+ {
+ restore_defun(p, $<node>1->nd_defn);
+ /*%%%*/
+ $$ = set_defun_body(p, $1, $2, $4, &@$);
+ /*% %*/
+ /*% ripper: def!(get_value($1), $2, $4) %*/
+ local_pop(p);
+ }
+ | defs_head f_arglist_opt '=' arg
+ {
+ restore_defun(p, $<node>1->nd_defn);
+ /*%%%*/
+ $$ = set_defun_body(p, $1, $2, $4, &@$);
+ /*%
+ $1 = get_value($1);
+ %*/
+ /*% ripper: defs!(AREF($1, 0), AREF($1, 1), AREF($1, 2), $2, $4) %*/
+ local_pop(p);
+ }
| primary
{
$$ = $1;
@@ -3018,56 +3099,31 @@ primary : literal
local_pop(p);
p->ctxt.in_class = $<ctxt>1.in_class;
}
- | k_def fname
- {
- numparam_name(p, get_id($2));
- local_push(p, 0);
- $<id>$ = p->cur_arg;
- p->cur_arg = 0;
- $<ctxt>1 = p->ctxt;
- p->ctxt.in_def = 1;
- }
+ | defn_head
f_arglist
bodystmt
k_end
{
+ restore_defun(p, $<node>1->nd_defn);
/*%%%*/
- NODE *body = remove_begin($5);
- reduce_nodes(p, &body);
- $$ = NEW_DEFN($2, $4, body, &@$);
- nd_set_line($$->nd_defn, @6.end_pos.lineno);
- set_line_body(body, @1.beg_pos.lineno);
+ $$ = set_defun_body(p, $1, $2, $3, &@$);
/*% %*/
- /*% ripper: def!($2, $4, $5) %*/
+ /*% ripper: def!(get_value($1), $2, $3) %*/
local_pop(p);
- p->ctxt.in_def = $<ctxt>1.in_def;
- p->cur_arg = $<id>3;
- }
- | k_def singleton dot_or_colon {SET_LEX_STATE(EXPR_FNAME);} fname
- {
- numparam_name(p, get_id($5));
- $<ctxt>1 = p->ctxt;
- p->ctxt.in_def = 1;
- SET_LEX_STATE(EXPR_ENDFN|EXPR_LABEL); /* force for args */
- local_push(p, 0);
- $<id>$ = p->cur_arg;
- p->cur_arg = 0;
}
+ | defs_head
f_arglist
bodystmt
k_end
{
+ restore_defun(p, $<node>1->nd_defn);
/*%%%*/
- NODE *body = remove_begin($8);
- reduce_nodes(p, &body);
- $$ = NEW_DEFS($2, $5, $7, body, &@$);
- nd_set_line($$->nd_defn, @9.end_pos.lineno);
- set_line_body(body, @1.beg_pos.lineno);
- /*% %*/
- /*% ripper: defs!($2, $3, $5, $7, $8) %*/
+ $$ = set_defun_body(p, $1, $2, $3, &@$);
+ /*%
+ $1 = get_value($1);
+ %*/
+ /*% ripper: defs!(AREF($1, 0), AREF($1, 1), AREF($1, 2), $2, $3) %*/
local_pop(p);
- p->ctxt.in_def = $<ctxt>1.in_def;
- p->cur_arg = $<id>6;
}
| keyword_break
{
@@ -4840,6 +4896,17 @@ superclass : '<'
}
;
+f_arglist_opt : f_arglist
+ | /* none */
+ {
+ /*%%%*/
+ $$ = new_args_tail(p, Qnone, Qnone, Qnone, &@0);
+ $$ = new_args(p, Qnone, Qnone, Qnone, Qnone, $$, &@0);
+ /*% %*/
+ /*% ripper: Qnil %*/
+ }
+ ;
+
f_arglist : '(' f_args rparen
{
/*%%%*/
diff --git a/spec/ruby/language/method_spec.rb b/spec/ruby/language/method_spec.rb
index 0209ca21c9..bc7b8b6606 100644
--- a/spec/ruby/language/method_spec.rb
+++ b/spec/ruby/language/method_spec.rb
@@ -1766,3 +1766,16 @@ describe "An array-dereference method ([])" do
end
end
end
+
+ruby_version_is '2.8' do
+ describe "An endless method definition" do
+ evaluate <<-ruby do
+ def m(a) = a
+ ruby
+
+ a = b = m 1
+ a.should == 1
+ b.should == 1
+ end
+ end
+end
diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb
index 04e98eca83..825d5e1fc0 100644
--- a/test/ruby/test_syntax.rb
+++ b/test/ruby/test_syntax.rb
@@ -1414,6 +1414,13 @@ eom
assert_equal(line, e.backtrace_locations[0].lineno)
end
+ def test_methoddef_endless
+ assert_valid_syntax('private def foo = 42')
+ assert_valid_syntax('private def inc(x) = x + 1')
+ assert_valid_syntax('private def obj.foo = 42')
+ assert_valid_syntax('private def obj.inc(x) = x + 1')
+ end
+
def test_methoddef_in_cond
assert_valid_syntax('while def foo; tap do end; end; break; end')
assert_valid_syntax('while def foo a = tap do end; end; break; end')