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

Add ASTNode#single_expression and refactor with using it #5513

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
12 changes: 12 additions & 0 deletions spec/compiler/codegen/exception_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -1273,4 +1273,16 @@ describe "Code gen: exception" do
end
)).to_string.should eq("good")
end

it "types parenthesized expression (#5511)" do
run(%(
require "prelude"
begin
((raise "foo").bar).baz
rescue ex
ex.message
end
)).to_string.should eq("foo")
end
end
4 changes: 4 additions & 0 deletions spec/compiler/normalize/case_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ describe "Normalize: case" do
assert_expand "case x = 1; when 2; 3; end", "x = 1\nif 2 === x\n 3\nend"
end

it "normalizes case with assignment wrapped by paren" do
assert_expand "case (x = 1); when 2; 3; end", "x = 1\nif 2 === x\n 3\nend"
end

it "normalizes case without value" do
assert_expand "case when 2; 3; when 4; 5; end", "if 2\n 3\nelse\n if 4\n 5\n end\nend"
end
Expand Down
5 changes: 5 additions & 0 deletions src/compiler/crystal/semantic/cleanup_transformer.cr
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ module Crystal
end

def transform(node : Expressions)
if exp = node.single_expression?
return exp.transform(self)
end

exps = [] of ASTNode

node.expressions.each_with_index do |exp, i|
Expand All @@ -144,6 +148,7 @@ module Crystal
end

def flatten_collect(exp, exps)
exp = exp.single_expression
if exp.is_a?(Expressions)
exp.expressions.each do |subexp|
return true if flatten_collect(subexp, exps)
Expand Down
14 changes: 3 additions & 11 deletions src/compiler/crystal/semantic/literal_expander.cr
Original file line number Diff line number Diff line change
Expand Up @@ -208,11 +208,7 @@ module Crystal
# temp
# end
def expand(node : And)
left = node.left

if left.is_a?(Expressions) && left.expressions.size == 1
left = left.expressions.first
end
left = node.left.single_expression

new_node = if left.is_a?(Var) || (left.is_a?(IsA) && left.obj.is_a?(Var))
If.new(left, node.right, left.clone)
Expand Down Expand Up @@ -245,11 +241,7 @@ module Crystal
# b
# end
def expand(node : Or)
left = node.left

if left.is_a?(Expressions) && left.expressions.size == 1
left = left.expressions.first
end
left = node.left.single_expression

new_node = if left.is_a?(Var) || (left.is_a?(IsA) && left.obj.is_a?(Var))
If.new(left, left.clone, node.right)
Expand Down Expand Up @@ -390,7 +382,7 @@ module Crystal

assigns = [] of ASTNode
temp_vars = conds.map do |cond|
case cond
case cond = cond.single_expression
when Var, InstanceVar
temp_var = cond
when Assign
Expand Down
22 changes: 6 additions & 16 deletions src/compiler/crystal/semantic/main_visitor.cr
Original file line number Diff line number Diff line change
Expand Up @@ -1742,7 +1742,7 @@ module Crystal
target = exp.target
return target if target.is_a?(Var)
when Expressions
return unless exp = single_expression(exp)
return unless exp = exp.single_expression?
return get_expression_var(exp)
end
nil
Expand Down Expand Up @@ -1850,13 +1850,13 @@ module Crystal
# block is when the condition is a Var (in the else it must be
# nil), IsA (in the else it's not that type), RespondsTo
# (in the else it doesn't respond to that message) or Not.
case cond = single_expression(node.cond) || node.cond
case cond = node.cond.single_expression
when Var, IsA, RespondsTo, Not
filter_vars cond_type_filters, &.not
when Or
# Try to apply boolean logic: `!(a || b)` is `!a && !b`
cond_left = single_expression(cond.left) || cond.left
cond_right = single_expression(cond.right) || cond.right
cond_left = cond.left.single_expression
cond_right = cond.right.single_expression

# We can't deduce anything for sub && or || expressions
or_left_type_filters = nil if cond_left.is_a?(And) || cond_left.is_a?(Or)
Expand Down Expand Up @@ -2026,7 +2026,7 @@ module Crystal
node.body.accept self
end

cond = single_expression(node.cond) || node.cond
cond = node.cond.single_expression

endless_while = cond.true_literal?
merge_while_vars cond, endless_while, before_cond_vars_copy, before_cond_vars, after_cond_vars, @vars, node.break_vars
Expand Down Expand Up @@ -2158,7 +2158,7 @@ module Crystal
when Call
return get_while_cond_assign_target(node.obj)
when Expressions
return unless node = single_expression(node)
return unless node = node.single_expression?
return get_while_cond_assign_target(node)
end

Expand Down Expand Up @@ -2192,16 +2192,6 @@ module Crystal
end
end

def single_expression(node)
result = nil

while node.is_a?(Expressions) && node.expressions.size == 1
result = node = node[0]
end

result
end

def end_visit(node : Break)
if last_block_kind == :ensure
node.raise "can't use break inside ensure"
Expand Down
19 changes: 19 additions & 0 deletions src/compiler/crystal/syntax/ast.cr
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,18 @@ module Crystal
def pretty_print(pp)
pp.text to_s
end

# It yields itself for any node, but `Expressions` yields first node
# if it holds only a node.
def single_expression
single_expression? || self
end

# It yields `nil` always.
# (It is overrided by `Expressions` to implement `#single_expression`.)
def single_expression?
nil
end
end

class Nop < ASTNode
Expand Down Expand Up @@ -146,6 +158,13 @@ module Crystal
@end_location || @expressions.last?.try &.end_location
end

# It yields first node if this holds only one node, or yields `nil`.
def single_expression?
return @expressions.first.single_expression if @expressions.size == 1

nil
end

def accept_children(visitor)
@expressions.each &.accept visitor
end
Expand Down