Skip to content

Commit

Permalink
+ ruby28.y: endless method definition (#676)
Browse files Browse the repository at this point in the history
  • Loading branch information
palkan committed Apr 29, 2020
1 parent 5a85967 commit eb4a804
Show file tree
Hide file tree
Showing 9 changed files with 243 additions and 36 deletions.
27 changes: 27 additions & 0 deletions doc/AST_FORMAT.md
Expand Up @@ -802,6 +802,33 @@ Format:
~~~~~~~~~~~~~~~~~ expression
~~~

### "Endless" method

Format:

~~~
(def_e :foo (args) (int 42))
"def foo() = 42"
~~~ keyword
~~~ name
^ assignment
~~~~~~~~~~~~~~ expression
~~~


### "Endless" singleton method

Format:

~~~
(defs_e (self) :foo (args) (int 42))
"def self.foo() = 42"
~~~ keyword
~~~ name
^ assignment
~~~~~~~~~~~~~~~~~~~ expression
~~~

### Undefinition

Format:
Expand Down
1 change: 1 addition & 0 deletions lib/parser.rb
Expand Up @@ -46,6 +46,7 @@ module Source
require 'parser/source/map/variable'
require 'parser/source/map/keyword'
require 'parser/source/map/definition'
require 'parser/source/map/endless_definition'
require 'parser/source/map/send'
require 'parser/source/map/index'
require 'parser/source/map/condition'
Expand Down
4 changes: 4 additions & 0 deletions lib/parser/ast/processor.rb
Expand Up @@ -159,6 +159,8 @@ def on_def(node)
])
end

alias on_def_e on_def

def on_defs(node)
definee_node, name, args_node, body_node = *node

Expand All @@ -168,6 +170,8 @@ def on_defs(node)
])
end

alias on_defs_e on_defs

alias on_undef process_regular_node
alias on_alias process_regular_node

Expand Down
45 changes: 37 additions & 8 deletions lib/parser/builders/default.rb
Expand Up @@ -652,19 +652,28 @@ def def_method(def_t, name_t, args,
definition_map(def_t, nil, name_t, end_t))
end

def def_endless_method(def_t, name_t, args,
assignment_t, body)
n(:def_e, [ value(name_t).to_sym, args, body ],
endless_definition_map(def_t, nil, name_t, assignment_t, body))
end

def def_singleton(def_t, definee, dot_t,
name_t, args,
body, end_t)
case definee.type
when :int, :str, :dstr, :sym, :dsym,
:regexp, :array, :hash
return unless validate_definee(definee)

diagnostic :error, :singleton_literal, nil, definee.loc.expression
n(:defs, [ definee, value(name_t).to_sym, args, body ],
definition_map(def_t, dot_t, name_t, end_t))
end

else
n(:defs, [ definee, value(name_t).to_sym, args, body ],
definition_map(def_t, dot_t, name_t, end_t))
end
def def_endless_singleton(def_t, definee, dot_t,
name_t, args,
assignment_t, body)
return unless validate_definee(definee)

n(:defs_e, [ definee, value(name_t).to_sym, args, body ],
endless_definition_map(def_t, dot_t, name_t, assignment_t, body))
end

def undef_method(undef_t, names)
Expand Down Expand Up @@ -1743,6 +1752,14 @@ def definition_map(keyword_t, operator_t, name_t, end_t)
loc(end_t))
end

def endless_definition_map(keyword_t, operator_t, name_t, assignment_t, body_e)
body_l = body_e.loc.expression

Source::Map::EndlessDefinition.new(loc(keyword_t),
loc(operator_t), loc(name_t),
loc(assignment_t), body_l)
end

def send_map(receiver_e, dot_t, selector_t, begin_t=nil, args=[], end_t=nil)
if receiver_e
begin_l = receiver_e.loc.expression
Expand Down Expand Up @@ -1985,6 +2002,18 @@ def diagnostic(type, reason, arguments, location, highlights=[])
@parser.send :yyerror
end
end

def validate_definee(definee)
case definee.type
when :int, :str, :dstr, :sym, :dsym,
:regexp, :array, :hash

diagnostic :error, :singleton_literal, nil, definee.loc.expression
false
else
true
end
end
end

end
2 changes: 1 addition & 1 deletion lib/parser/meta.rb
Expand Up @@ -16,7 +16,7 @@ module Meta
op_asgn and_asgn ensure rescue arg_expr
or_asgn back_ref nth_ref
match_with_lvasgn match_current_line
module class sclass def defs undef alias args
module class sclass def defs def_e defs_e undef alias args
cbase arg optarg restarg blockarg block_pass kwarg kwoptarg
kwrestarg kwnilarg send csend super zsuper yield block
and not or if when case while until while_post
Expand Down
87 changes: 60 additions & 27 deletions lib/parser/ruby28.y
Expand Up @@ -310,6 +310,35 @@ rule
result = [ val[1], val[2] ]
}

def_name: fname
{
@static_env.extend_static
@lexer.cmdarg.push(false)
@lexer.cond.push(false)
@current_arg_stack.push(nil)

result = val[0]
}

defn_head: kDEF def_name
{
@context.push(:def)

result = [ val[0], val[1] ]
}

defs_head: kDEF singleton dot_or_colon
{
@lexer.state = :expr_fname
}
def_name
{
@context.push(:defs)

result = [ val[0], val[1], val[2], val[4] ]
}


command_call: command
| block_command

Expand Down Expand Up @@ -817,6 +846,28 @@ rule
result = @builder.ternary(val[0], val[1],
val[2], val[4], val[5])
}
| defn_head f_paren_args tEQL arg
{
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
}
| defs_head f_paren_args tEQL arg
{
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
}
| primary

relop: tGT | tLT | tGEQ | tLEQ
Expand Down Expand Up @@ -1224,41 +1275,21 @@ rule
@lexer.cmdarg.pop
@static_env.unextend
}
| kDEF fname
| defn_head f_arglist bodystmt kEND
{
@static_env.extend_static
@lexer.cmdarg.push(false)
@lexer.cond.push(false)
@context.push(:def)
@current_arg_stack.push(nil)
}
f_arglist bodystmt kEND
{
result = @builder.def_method(val[0], val[1],
val[3], val[4], val[5])
result = @builder.def_method(*val[0], val[1],
val[2], val[3])
@lexer.cmdarg.pop
@lexer.cond.pop
@static_env.unextend
@context.pop
@current_arg_stack.pop
}
| kDEF singleton dot_or_colon
{
@lexer.state = :expr_fname
}
fname
{
@static_env.extend_static
@lexer.cmdarg.push(false)
@lexer.cond.push(false)
@context.push(:defs)
@current_arg_stack.push(nil)
}
f_arglist bodystmt kEND
| defs_head f_arglist bodystmt kEND
{
result = @builder.def_singleton(val[0], val[1], val[2],
val[4], val[6], val[7], val[8])
result = @builder.def_singleton(*val[0], val[1],
val[2], val[3])
@lexer.cmdarg.pop
@lexer.cond.pop
Expand Down Expand Up @@ -2515,7 +2546,7 @@ keyword_variable: kNIL
result = nil
}
f_arglist: tLPAREN2 f_args rparen
f_paren_args: tLPAREN2 f_args rparen
{
result = @builder.args(val[0], val[1], val[2])
Expand All @@ -2528,6 +2559,8 @@ keyword_variable: kNIL
@lexer.state = :expr_value
}
f_arglist: f_paren_args
| {
result = @lexer.in_kwarg
@lexer.in_kwarg = true
Expand Down
23 changes: 23 additions & 0 deletions lib/parser/source/map/endless_definition.rb
@@ -0,0 +1,23 @@
# frozen_string_literal: true

module Parser
module Source

class Map::EndlessDefinition < Map
attr_reader :keyword
attr_reader :operator
attr_reader :name
attr_reader :assignment

def initialize(keyword_l, operator_l, name_l, assignment_l, body_l)
@keyword = keyword_l
@operator = operator_l
@name = name_l
@assignment = assignment_l

super(@keyword.join(body_l))
end
end

end
end
12 changes: 12 additions & 0 deletions test/test_lexer.rb
Expand Up @@ -3569,6 +3569,18 @@ def test_ambiguous_integer_re
:tIDENTIFIER, 're', [1, 3])
end

def test_endless_method
setup_lexer(28)

assert_scanned('def foo() = 42',
:kDEF, "def", [0, 3],
:tIDENTIFIER, "foo", [4, 7],
:tLPAREN2, "(", [7, 8],
:tRPAREN, ")", [8, 9],
:tEQL, "=", [10, 11],
:tINTEGER, 42, [12, 14])
end

def lex_numbered_parameter(input)
@lex.max_numparam_stack.push

Expand Down
78 changes: 78 additions & 0 deletions test/test_parser.rb
Expand Up @@ -9444,4 +9444,82 @@ def test_parser_bug_645
%{},
SINCE_1_9)
end

def test_endless_method
assert_parses(
s(:def_e, :foo,
s(:args),
s(:int, 42)),
%q{def foo() = 42},
%q{~~~ keyword
| ~~~ name
| ^ assignment
|~~~~~~~~~~~~~~ expression},
SINCE_2_8)

assert_parses(
s(:def_e, :inc,
s(:args, s(:arg, :x)),
s(:send,
s(:lvar, :x), :+,
s(:int, 1))),
%q{def inc(x) = x + 1},
%q{~~~ keyword
| ~~~ name
| ^ assignment
|~~~~~~~~~~~~~~~~~~ expression},
SINCE_2_8)

assert_parses(
s(:defs_e, s(:send, nil, :obj), :foo,
s(:args),
s(:int, 42)),
%q{def obj.foo() = 42},
%q{~~~ keyword
| ^ operator
| ~~~ name
| ^ assignment
|~~~~~~~~~~~~~~~~~~ expression},
SINCE_2_8)

assert_parses(
s(:defs_e, s(:send, nil, :obj), :inc,
s(:args, s(:arg, :x)),
s(:send,
s(:lvar, :x), :+,
s(:int, 1))),
%q{def obj.inc(x) = x + 1},
%q{~~~ keyword
| ~~~ name
| ^ operator
| ^ assignment
|~~~~~~~~~~~~~~~~~~~~~~ expression},
SINCE_2_8)

assert_parses(
s(:def_e, :foo,
s(:forward_args),
s(:send, nil, :bar,
s(:forwarded_args))),
%q{def foo(...) = bar(...)},
%q{~~~ keyword
| ~~~ name
| ^ assignment
|~~~~~~~~~~~~~~~~~~~~~~~ expression},
SINCE_2_8)
end

def test_endless_method_without_brackets
assert_diagnoses(
[:error, :unexpected_token, { :token => 'tEQL' }],
%Q{def foo = 42},
%q{ ^ location},
SINCE_2_8)

assert_diagnoses(
[:error, :unexpected_token, { :token => 'tEQL' }],
%Q{def obj.foo = 42},
%q{ ^ location},
SINCE_2_8)
end
end

0 comments on commit eb4a804

Please sign in to comment.