Skip to content

Commit

Permalink
Allow constants and instance / class variables as receivers for sette…
Browse files Browse the repository at this point in the history
…r proc pointers (#10741)

Co-authored-by: Johannes Müller <[email protected]>
HertzDevil and straight-shoota authored Jul 23, 2021

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 3b6d6bb commit f2168b1
Showing 3 changed files with 42 additions and 55 deletions.
15 changes: 9 additions & 6 deletions spec/compiler/formatter/formatter_spec.cr
Original file line number Diff line number Diff line change
@@ -750,13 +750,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"
@@ -765,9 +771,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
14 changes: 8 additions & 6 deletions spec/compiler/parser/parser_spec.cr
Original file line number Diff line number Diff line change
@@ -1275,18 +1275,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"))

68 changes: 25 additions & 43 deletions src/compiler/crystal/syntax/parser.cr
Original file line number Diff line number Diff line change
@@ -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
@@ -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
@@ -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
@@ -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
@@ -5977,6 +5948,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

0 comments on commit f2168b1

Please sign in to comment.