Skip to content

Commit

Permalink
Merge pull request #6367 from straight-shoota/jm/feature/case-without…
Browse files Browse the repository at this point in the history
…-when

Syntax: allow empty `case` without `when`
  • Loading branch information
ysbaddaden authored Jul 12, 2018
2 parents 8d28cbb + e14ec7a commit bec784d
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 10 deletions.
6 changes: 6 additions & 0 deletions spec/compiler/formatter/formatter_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,12 @@ describe Crystal::Formatter do
assert_format "case 1\nwhen 1, # 1\n 2, # 2\n 3 # 3\n 1\nend"
assert_format "a = case 1\n when 1, # 1\n 2, # 2\n 3 # 3\n 1\n end"
assert_format "a = 1\ncase\nwhen 2\nelse\n a /= 3\nend"
assert_format "case 1\nend"
assert_format "case 1\nelse\n 2\nend"
assert_format "case\nend"
assert_format "case 1\nend"
assert_format "case\nend"
assert_format "case\nelse\n 1\nend"

assert_format "select \n when foo \n 2 \n end", "select\nwhen foo\n 2\nend"
assert_format "select \n when foo \n 2 \n when bar \n 3 \n end", "select\nwhen foo\n 2\nwhen bar\n 3\nend"
Expand Down
16 changes: 16 additions & 0 deletions spec/compiler/normalize/case_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,20 @@ describe "Normalize: case" do
it "normalizes case with multiple expressions and non-tuple" do
assert_expand_second "x, y = 1, 2; case {x, y}; when 1; 4; end", "if 1 === {x, y}\n 4\nend"
end

it "normalizes case without when and else" do
assert_expand "case x; end", "x"
end

it "normalizes case without when but else" do
assert_expand "case x; else; y; end", "x\ny"
end

it "normalizes case without cond, when and else" do
assert_expand "case; end", ""
end

it "normalizes case without cond, when but else" do
assert_expand "case; else; y; end", "y"
end
end
11 changes: 8 additions & 3 deletions spec/compiler/parser/parser_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ private def regex(string, options = Regex::Options::None)
end

private def it_parses(string, expected_node, file = __FILE__, line = __LINE__)
it "parses #{string}", file, line do
it "parses #{string.dump}", file, line do
parser = Parser.new(string)
parser.filename = "/foo/bar/baz.cr"
node = parser.parse
Expand Down Expand Up @@ -984,8 +984,14 @@ module Crystal
it_parses "case a\nwhen b\n1 / 2\nelse\n1 / 2\nend", Case.new("a".call, [When.new(["b".call] of ASTNode, Call.new(1.int32, "/", 2.int32))], Call.new(1.int32, "/", 2.int32))
it_parses "case a\nwhen b\n/ /\n\nelse\n/ /\nend", Case.new("a".call, [When.new(["b".call] of ASTNode, RegexLiteral.new(StringLiteral.new(" ")))], RegexLiteral.new(StringLiteral.new(" ")))
assert_syntax_error "case {1, 2}; when {3}; 4; end", "wrong number of tuple elements (given 1, expected 2)", 1, 19
assert_syntax_error "case 1; end", "unexpected token: end (expecting when or else)", 1, 9
it_parses "case 1; end", [Case.new(1.int32, [] of When)]
it_parses "case foo; end", [Case.new("foo".call, [] of When)]
it_parses "case\nend", [Case.new(nil, [] of When)]
it_parses "case;end", [Case.new(nil, [] of When)]
it_parses "case 1\nelse\n2\nend", [Case.new(1.int32, [] of When, 2.int32)]
it_parses "a = 1\ncase 1\nwhen a then 1\nend", [Assign.new("a".var, 1.int32), Case.new(1.int32, [When.new(["a".var] of ASTNode, 1.int32)])] of ASTNode
it_parses "case\nwhen true\n1\nend", [Case.new(nil, [When.new([true.bool] of ASTNode, 1.int32)] of When)]
it_parses "case;when true;1;end", [Case.new(nil, [When.new([true.bool] of ASTNode, 1.int32)] of When)]

it_parses "select\nwhen foo\n2\nend", Select.new([Select::When.new("foo".call, 2.int32)])
it_parses "select\nwhen foo\n2\nwhen bar\n4\nend", Select.new([Select::When.new("foo".call, 2.int32), Select::When.new("bar".call, 4.int32)])
Expand Down Expand Up @@ -1447,7 +1453,6 @@ module Crystal

assert_syntax_error "Set {1, 2, 3} of Int32"
assert_syntax_error "Hash {foo: 1} of Int32 => Int32"
assert_syntax_error "case foo; end"
assert_syntax_error "enum Foo < UInt16; end"
assert_syntax_error "foo(1 2)"
assert_syntax_error %(foo("bar" "baz"))
Expand Down
12 changes: 12 additions & 0 deletions src/compiler/crystal/semantic/literal_expander.cr
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,18 @@ module Crystal
# end
def expand(node : Case)
node_cond = node.cond

if node.whens.empty?
expressions = [] of ASTNode
if node_cond
expressions << node_cond
end
if node_else = node.else
expressions << node_else
end
return Expressions.new(expressions).at(node)
end

if node_cond
if node_cond.is_a?(TupleLiteral)
conds = node_cond.elements
Expand Down
12 changes: 5 additions & 7 deletions src/compiler/crystal/syntax/parser.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2454,7 +2454,11 @@ module Crystal
def parse_case
slash_is_regex!
next_token_skip_space_or_newline
unless @token.keyword?(:when)
while @token.type == :";"
next_token_skip_space
end

unless @token.keyword? && {:when, :else, :end}.includes?(@token.value)
cond = parse_op_assign_no_control
skip_statement_end
end
Expand Down Expand Up @@ -2527,19 +2531,13 @@ module Crystal
skip_space_or_newline
whens << When.new(when_conds, when_body).at(location)
when :else
if whens.size == 0
unexpected_token @token.to_s, "expecting when"
end
next_token_skip_statement_end
a_else = parse_expressions
skip_statement_end
check_ident :end
next_token
break
when :end
if whens.empty?
unexpected_token @token.to_s, "expecting when or else"
end
next_token
break
else
Expand Down

0 comments on commit bec784d

Please sign in to comment.