From 7ecddb67b2f82df92e424d36b6642cb887148cbc Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Fri, 14 May 2021 17:50:53 +0900 Subject: [PATCH] + ruby31.y: allow "command" syntax in endless method definition (#801) Fixes https://github.com/whitequark/parser/issues/799. --- lib/parser/ruby31.y | 68 +++++++++++++++++++ test/test_parser.rb | 160 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 228 insertions(+) diff --git a/lib/parser/ruby31.y b/lib/parser/ruby31.y index 5a61ba48a..3d1455888 100644 --- a/lib/parser/ruby31.y +++ b/lib/parser/ruby31.y @@ -261,6 +261,74 @@ rule val[0], val[1], val[2]), val[3], val[4]) } + | defn_head f_opt_paren_args tEQL command + { + _def_t, name_t = val[0] + endless_method_name(name_t) + + result = @builder.def_endless_method(*val[0], + val[1], val[2], val[3]) + + @lexer.cmdarg.pop + @lexer.cond.pop + @static_env.unextend + @context.pop + @current_arg_stack.pop + } + | defn_head f_opt_paren_args tEQL command kRESCUE_MOD arg + { + _def_t, name_t = val[0] + endless_method_name(name_t) + + rescue_body = @builder.rescue_body(val[4], + nil, nil, nil, + nil, val[5]) + + method_body = @builder.begin_body(val[3], [ rescue_body ]) + + result = @builder.def_endless_method(*val[0], + val[1], val[2], method_body) + + @lexer.cmdarg.pop + @lexer.cond.pop + @static_env.unextend + @context.pop + @current_arg_stack.pop + } + | defs_head f_opt_paren_args tEQL command + { + _def_t, _recv, _dot_t, name_t = val[0] + endless_method_name(name_t) + + result = @builder.def_endless_singleton(*val[0], + val[1], val[2], val[3]) + + @lexer.cmdarg.pop + @lexer.cond.pop + @static_env.unextend + @context.pop + @current_arg_stack.pop + } + | defs_head f_opt_paren_args tEQL command kRESCUE_MOD arg + { + _def_t, _recv, _dot_t, name_t = val[0] + endless_method_name(name_t) + + rescue_body = @builder.rescue_body(val[4], + nil, nil, nil, + nil, val[5]) + + method_body = @builder.begin_body(val[3], [ rescue_body ]) + + result = @builder.def_endless_singleton(*val[0], + val[1], val[2], method_body) + + @lexer.cmdarg.pop + @lexer.cond.pop + @static_env.unextend + @context.pop + @current_arg_stack.pop + } | backref tOP_ASGN command_rhs { @builder.op_assign(val[0], val[1], val[2]) diff --git a/test/test_parser.rb b/test/test_parser.rb index 849332abe..4ab976c37 100644 --- a/test/test_parser.rb +++ b/test/test_parser.rb @@ -9832,6 +9832,166 @@ def test_endless_method_with_rescue_mod SINCE_3_0) end + def test_endless_method_command_syntax + assert_parses( + s(:def, :foo, + s(:args), + s(:send, nil, :puts, + s(:str, "Hello"))), + %q{def foo = puts "Hello"}, + %q{~~~ keyword + | ~~~ name + | ^ assignment + |~~~~~~~~~~~~~~~~~~~~~~ expression}, + SINCE_3_1) + + assert_parses( + s(:def, :foo, + s(:args), + s(:send, nil, :puts, + s(:str, "Hello"))), + %q{def foo() = puts "Hello"}, + %q{~~~ keyword + | ~~~ name + | ^ assignment + |~~~~~~~~~~~~~~~~~~~~~~~~ expression}, + SINCE_3_1) + + assert_parses( + s(:def, :foo, + s(:args, + s(:arg, :x)), + s(:send, nil, :puts, + s(:lvar, :x))), + %q{def foo(x) = puts x}, + %q{~~~ keyword + | ~~~ name + | ^ assignment + |~~~~~~~~~~~~~~~~~~~ expression}, + SINCE_3_1) + + assert_parses( + s(:defs, + s(:send, nil, :obj), :foo, + s(:args), + s(:send, nil, :puts, + s(:str, "Hello"))), + %q{def obj.foo = puts "Hello"}, + %q{~~~ keyword + | ^ operator + | ~~~ name + | ^ assignment + |~~~~~~~~~~~~~~~~~~~~~~~~~~ expression}, + SINCE_3_1) + + assert_parses( + s(:defs, + s(:send, nil, :obj), :foo, + s(:args), + s(:send, nil, :puts, + s(:str, "Hello"))), + %q{def obj.foo() = puts "Hello"}, + %q{~~~ keyword + | ^ operator + | ~~~ name + | ^ assignment + |~~~~~~~~~~~~~~~~~~~~~~~~~~~~ expression}, + SINCE_3_1) + + assert_parses( + s(:def, :rescued, + s(:args, + s(:arg, :x)), + s(:rescue, + s(:send, nil, :raise, + s(:str, "to be caught")), + s(:resbody, nil, nil, + s(:dstr, + s(:str, "instance "), + s(:begin, + s(:lvar, :x)))), nil)), + %q{def rescued(x) = raise "to be caught" rescue "instance #{x}"}, + %q{~~~ keyword + | ~~~~~~~ name + | ^ assignment + |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ expression}, + SINCE_3_1) + + assert_parses( + s(:defs, + s(:self), :rescued, + s(:args, + s(:arg, :x)), + s(:rescue, + s(:send, nil, :raise, + s(:str, "to be caught")), + s(:resbody, nil, nil, + s(:dstr, + s(:str, "class "), + s(:begin, + s(:lvar, :x)))), nil)), + %q{def self.rescued(x) = raise "to be caught" rescue "class #{x}"}, + %q{~~~ keyword + | ^ operator + | ~~~~~~~ name + | ^ assignment + |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ expression}, + SINCE_3_1) + + assert_parses( + s(:defs, + s(:send, nil, :obj), :foo, + s(:args, + s(:arg, :x)), + s(:send, nil, :puts, + s(:lvar, :x))), + %q{def obj.foo(x) = puts x}, + %q{~~~ keyword + | ^ operator + | ~~~ name + | ^ assignment + |~~~~~~~~~~~~~~~~~~~~~~~ expression}, + SINCE_3_1) + end + + def test_private_endless_method_command_syntax + assert_diagnoses( + [:error, :unexpected_token, { :token => 'tSTRING' }], + %q{private def foo = puts "Hello"}, + %q{ ^^^^^^^ location}, + SINCE_3_1) + + assert_diagnoses( + [:error, :unexpected_token, { :token => 'tSTRING' }], + %q{private def foo() = puts "Hello"}, + %q{ ^^^^^^^ location}, + SINCE_3_1) + + assert_diagnoses( + [:error, :unexpected_token, { :token => 'tIDENTIFIER' }], + %q{private def foo(x) = puts x}, + %q{ ^ location}, + SINCE_3_1) + + assert_diagnoses( + [:error, :unexpected_token, { :token => 'tSTRING' }], + %q{private def obj.foo = puts "Hello"}, + %q{ ^^^^^^^ location}, + SINCE_3_1) + + assert_diagnoses( + [:error, :unexpected_token, { :token => 'tSTRING' }], + %q{private def obj.foo() = puts "Hello"}, + %q{ ^^^^^^^ location}, + SINCE_3_1) + + assert_diagnoses( + [:error, :unexpected_token, { :token => 'tIDENTIFIER' }], + %q{private def obj.foo(x) = puts x}, + %q{ ^ location}, + SINCE_3_1) + end + def test_rasgn_line_continuation assert_diagnoses( [:error, :unexpected_token, { :token => 'tASSOC' }],