Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow constants and instance / class variables as receivers for setter proc pointers #10741

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions spec/compiler/formatter/formatter_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -727,13 +727,19 @@ describe Crystal::Formatter do
assert_format "lib Bar\n enum Foo\n A\n end\nend"
assert_format "lib Bar\n enum Foo\n A = 1\n end\nend"

assert_format "->foo="
assert_format "foo = 1\n->foo.bar"
assert_format "foo = 1\n->foo.bar="
%w(foo foo= foo? foo!).each do |method|
assert_format "->#{method}"
assert_format "foo = 1\n->foo.#{method}"
assert_format "->Foo.#{method}"
assert_format "->@foo.#{method}"
assert_format "->@@foo.#{method}"
end

assert_format "foo = 1\n->foo.bar(Int32)"
assert_format "foo = 1\n->foo.bar(Int32*)"
assert_format "foo = 1\n->foo.bar=(Int32)"
assert_format "foo = 1\n->foo.[](Int32)"
assert_format "foo = 1\n->foo.[]=(Int32)"
assert_format "->{ x }"
assert_format "->{\nx\n}", "->{\n x\n}"
assert_format "->do\nx\nend", "->do\n x\nend"
Expand All @@ -742,9 +748,6 @@ describe Crystal::Formatter do
assert_format "->( x , y ) { x }", "->(x, y) { x }"
assert_format "->( x : Int32 , y ) { x }", "->(x : Int32, y) { x }"

assert_format "->@foo.foo"
assert_format "->@@foo.foo"

{:+, :-, :*, :/, :^, :>>, :<<, :|, :&, :&+, :&-, :&*, :&**}.each do |sym|
assert_format ":#{sym}"
end
Expand Down
14 changes: 8 additions & 6 deletions spec/compiler/parser/parser_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -1272,18 +1272,20 @@ module Crystal
it_parses "x = 1; ->{ x }", [Assign.new("x".var, 1.int32), ProcLiteral.new(Def.new("->", body: "x".var))]
it_parses "f ->{ a do\n end\n }", Call.new(nil, "f", ProcLiteral.new(Def.new("->", body: Call.new(nil, "a", block: Block.new))))

it_parses "->foo", ProcPointer.new(nil, "foo")
it_parses "->Foo.foo", ProcPointer.new("Foo".path, "foo")
%w(foo foo= foo? foo!).each do |method|
it_parses "->#{method}", ProcPointer.new(nil, method)
it_parses "foo = 1; ->foo.#{method}", [Assign.new("foo".var, 1.int32), ProcPointer.new("foo".var, method)]
it_parses "->Foo.#{method}", ProcPointer.new("Foo".path, method)
it_parses "->@foo.#{method}", ProcPointer.new("@foo".instance_var, method)
it_parses "->@@foo.#{method}", ProcPointer.new("@@foo".class_var, method)
end

it_parses "->Foo::Bar::Baz.foo", ProcPointer.new(["Foo", "Bar", "Baz"].path, "foo")
it_parses "->foo(Int32, Float64)", ProcPointer.new(nil, "foo", ["Int32".path, "Float64".path] of ASTNode)
it_parses "foo = 1; ->foo.bar(Int32)", [Assign.new("foo".var, 1.int32), ProcPointer.new("foo".var, "bar", ["Int32".path] of ASTNode)]
it_parses "->foo(Void*)", ProcPointer.new(nil, "foo", ["Void".path.pointer_of] of ASTNode)
it_parses "call ->foo", Call.new(nil, "call", ProcPointer.new(nil, "foo"))
it_parses "[] of ->\n", ArrayLiteral.new(of: ProcNotation.new)
it_parses "->foo=", ProcPointer.new(nil, "foo=")
it_parses "foo = 1; ->foo.foo=", [Assign.new("foo".var, 1.int32), ProcPointer.new("foo".var, "foo=")]
it_parses "->@foo.foo", [ProcPointer.new("@foo".instance_var, "foo")]
it_parses "->@@foo.foo", [ProcPointer.new("@@foo".class_var, "foo")]

it_parses "foo &->bar", Call.new(nil, "foo", block_arg: ProcPointer.new(nil, "bar"))

Expand Down
68 changes: 25 additions & 43 deletions src/compiler/crystal/syntax/parser.cr
Original file line number Diff line number Diff line change
Expand Up @@ -1886,45 +1886,34 @@ module Crystal
case @token.type
when :IDENT
name = @token.value.to_s
next_token
if @token.type == :"="
if consume_def_equals_sign_skip_space
name = "#{name}="
next_token_skip_space
else
skip_space
if @token.type == :"."
second_name = consume_def_or_macro_name
if name != "self" && !@def_vars.last.includes?(name)
raise "undefined variable '#{name}'", location.line_number, location.column_number
end
obj = Var.new(name)
name = second_name
next_token
if @token.type == :"="
name = "#{name}="
next_token_skip_space
else
skip_space
end
elsif @token.type == :"."
if name != "self" && !@def_vars.last.includes?(name)
raise "undefined variable '#{name}'", location.line_number, location.column_number
end
obj = Var.new(name)

name = consume_def_or_macro_name
name = "#{name}=" if consume_def_equals_sign_skip_space
end
when :CONST
obj = parse_generic
check :"."
name = consume_def_or_macro_name
next_token_skip_space
name = "#{name}=" if consume_def_equals_sign_skip_space
when :INSTANCE_VAR
obj = InstanceVar.new(@token.value.to_s)
next_token_skip_space
check :"."
name = consume_def_or_macro_name
next_token_skip_space
name = "#{name}=" if consume_def_equals_sign_skip_space
when :CLASS_VAR
obj = ClassVar.new(@token.value.to_s)
next_token_skip_space
check :"."
name = consume_def_or_macro_name
next_token_skip_space
name = "#{name}=" if consume_def_equals_sign_skip_space
else
unexpected_token
end
Expand Down Expand Up @@ -3002,12 +2991,7 @@ module Crystal
raise "macro can't have a receiver"
when :IDENT
check_valid_def_name
next_token
if @token.type == :"="
name += '='
next_token
end
skip_space
name = "#{name}=" if consume_def_equals_sign_skip_space
else
check_valid_def_op_name
next_token_skip_space
Expand Down Expand Up @@ -3440,14 +3424,7 @@ module Crystal
elsif @token.type == :IDENT
check_valid_def_name
name = @token.value.to_s

next_token
if @token.type == :"="
name = "#{name}="
next_token_skip_space
else
skip_space
end
name = "#{name}=" if consume_def_equals_sign_skip_space
else
check_valid_def_op_name
name = @token.type.to_s
Expand All @@ -3474,13 +3451,7 @@ module Crystal
name = @token.value.to_s

name_location = @token.location
next_token
if @token.type == :"="
name = "#{name}="
next_token_skip_space
else
skip_space
end
name = "#{name}=" if consume_def_equals_sign_skip_space
else
check DefOrMacroCheck2
check_valid_def_op_name
Expand Down Expand Up @@ -5976,6 +5947,17 @@ module Crystal
@token.to_s
end

def consume_def_equals_sign_skip_space
next_token
if @token.type == :"="
next_token_skip_space
true
else
skip_space
false
end
end

def push_def
@def_vars.push(Set(String).new)
end
Expand Down