Skip to content

Commit

Permalink
Merge pull request #91 from myst-lang/feature/capture_as_inst_block
Browse files Browse the repository at this point in the history
[parser,ast] Allow function captures as block parameters for instantiation
  • Loading branch information
faultyserver authored Dec 23, 2017
2 parents 019c737 + 5e41b4e commit faeddd0
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 10 deletions.
17 changes: 12 additions & 5 deletions spec/syntax/parser_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -530,19 +530,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 @@ -1127,6 +1127,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 @@ -778,7 +778,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

0 comments on commit faeddd0

Please sign in to comment.