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

[parser,ast] Allow function captures as block parameters for instantiation #91

Merged
merged 1 commit into from
Dec 23, 2017
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
17 changes: 12 additions & 5 deletions spec/syntax/parser_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -529,19 +529,19 @@ describe "Parser" do
it_does_not_parse %q(a.b! = 1)
# Assigned Anonymous Functions called should be coerced to a Call
it_parses %q(
foo = fn
->() { }
foo = fn
->() { }
end
foo()
), SimpleAssign.new(v("foo"), AnonymousFunction.new([Block.new])), Call.new(nil, "foo")
it_parses %q(
foo = fn
->() { }
foo = fn
->() { }
end
bar = foo
bar()
), SimpleAssign.new(v("foo"), AnonymousFunction.new([Block.new])), SimpleAssign.new(v("bar"), v("foo")), Call.new(nil, "bar")

# Assignments can not be made to literal values.
it_does_not_parse %q(2 = 4), /cannot assign to literal value/i
it_does_not_parse %q(2.56 = 4), /cannot assign to literal value/i
Expand Down Expand Up @@ -1126,6 +1126,13 @@ describe "Parser" do
it_parses %q(
%Thing{ } { |a,b| }
), Instantiation.new(c("Thing"), block: Block.new([p("a"), p("b")]))
# The block parameter can also be specified as a capture
it_parses %q(%Thing{&block}), Instantiation.new(c("Thing"), block: FunctionCapture.new(Call.new(nil, "block")))
it_parses %q(%Thing{
&fn
->(){}
end
}), Instantiation.new(c("Thing"), block: FunctionCapture.new(AnonymousFunction.new([Block.new])))

# Also as in a Call, trailing commas and the like are invalid
it_does_not_parse %q(%Thing{ 1, })
Expand Down
2 changes: 1 addition & 1 deletion src/myst/syntax/ast.cr
Original file line number Diff line number Diff line change
Expand Up @@ -781,7 +781,7 @@ module Myst
class Instantiation < Node
property type : Node
property args : Array(Node)
property! block : Block?
property! block : (Block | FunctionCapture)?

def initialize(@type, @args=[] of Node, @block=nil)
end
Expand Down
22 changes: 18 additions & 4 deletions src/myst/syntax/parser.cr
Original file line number Diff line number Diff line change
Expand Up @@ -724,8 +724,8 @@ module Myst
def parse_var_or_call(receiver=nil)
start = expect(Token::Type::IDENT, Token::Type::CONST)
name = start.value
skip_space

skip_space
if receiver.nil? && current_token.type != Token::Type::LPAREN
if name.starts_with?('_')
return Underscore.new(name).at(start.location)
Expand Down Expand Up @@ -932,9 +932,15 @@ module Myst
else
loop do
skip_space_and_newlines
inst.args << parse_expression
case current_token.type
when Token::Type::AMPERSAND
inst.block = parse_function_capture
else
inst.args << parse_expression
end
skip_space_and_newlines


# If there is no comma, this is the last argument, and a closing
# parenthesis should be expected.
unless accept(Token::Type::COMMA)
Expand All @@ -946,7 +952,15 @@ module Myst
end

skip_space
if inst.block = parse_optional_block
if inline_block = parse_optional_block
# If a block argument was already specified by a function capture,
# inline blocks are not allowed. Checking this after parsing the
# optional block allows for a more helpful error message.
if inst.block?
raise ParseError.new(current_location, "A block argument has already been given by a captured function. Defining an extra inline block is not allowed.")
end

inst.block = inline_block
return inst.at_end(inst.block)
end

Expand Down