Skip to content

Commit 482b1cf

Browse files
makenowjustRX14
authored andcommittedJan 4, 2018
Add ASTNode#single_expression and refactor with using it (#5513)
* Add ASTNode#single_expression and refactor with using it Fixed #5482 Fixed #5511 But this commit contains no spec for #5511 because I don't know where to place such a spec. * Add spec for #5511 Thank you @asterite! See: #5513 (comment)
1 parent b293b01 commit 482b1cf

File tree

6 files changed

+49
-27
lines changed

6 files changed

+49
-27
lines changed
 

Diff for: ‎spec/compiler/codegen/exception_spec.cr

+12
Original file line numberDiff line numberDiff line change
@@ -1273,4 +1273,16 @@ describe "Code gen: exception" do
12731273
end
12741274
)).to_string.should eq("good")
12751275
end
1276+
1277+
it "types parenthesized expression (#5511)" do
1278+
run(%(
1279+
require "prelude"
1280+
1281+
begin
1282+
((raise "foo").bar).baz
1283+
rescue ex
1284+
ex.message
1285+
end
1286+
)).to_string.should eq("foo")
1287+
end
12761288
end

Diff for: ‎spec/compiler/normalize/case_spec.cr

+4
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ describe "Normalize: case" do
5353
assert_expand "case x = 1; when 2; 3; end", "x = 1\nif 2 === x\n 3\nend"
5454
end
5555

56+
it "normalizes case with assignment wrapped by paren" do
57+
assert_expand "case (x = 1); when 2; 3; end", "x = 1\nif 2 === x\n 3\nend"
58+
end
59+
5660
it "normalizes case without value" do
5761
assert_expand "case when 2; 3; when 4; 5; end", "if 2\n 3\nelse\n if 4\n 5\n end\nend"
5862
end

Diff for: ‎src/compiler/crystal/semantic/cleanup_transformer.cr

+5
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ module Crystal
128128
end
129129

130130
def transform(node : Expressions)
131+
if exp = node.single_expression?
132+
return exp.transform(self)
133+
end
134+
131135
exps = [] of ASTNode
132136

133137
node.expressions.each_with_index do |exp, i|
@@ -144,6 +148,7 @@ module Crystal
144148
end
145149

146150
def flatten_collect(exp, exps)
151+
exp = exp.single_expression
147152
if exp.is_a?(Expressions)
148153
exp.expressions.each do |subexp|
149154
return true if flatten_collect(subexp, exps)

Diff for: ‎src/compiler/crystal/semantic/literal_expander.cr

+3-11
Original file line numberDiff line numberDiff line change
@@ -208,11 +208,7 @@ module Crystal
208208
# temp
209209
# end
210210
def expand(node : And)
211-
left = node.left
212-
213-
if left.is_a?(Expressions) && left.expressions.size == 1
214-
left = left.expressions.first
215-
end
211+
left = node.left.single_expression
216212

217213
new_node = if left.is_a?(Var) || (left.is_a?(IsA) && left.obj.is_a?(Var))
218214
If.new(left, node.right, left.clone)
@@ -245,11 +241,7 @@ module Crystal
245241
# b
246242
# end
247243
def expand(node : Or)
248-
left = node.left
249-
250-
if left.is_a?(Expressions) && left.expressions.size == 1
251-
left = left.expressions.first
252-
end
244+
left = node.left.single_expression
253245

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

391383
assigns = [] of ASTNode
392384
temp_vars = conds.map do |cond|
393-
case cond
385+
case cond = cond.single_expression
394386
when Var, InstanceVar
395387
temp_var = cond
396388
when Assign

Diff for: ‎src/compiler/crystal/semantic/main_visitor.cr

+6-16
Original file line numberDiff line numberDiff line change
@@ -1742,7 +1742,7 @@ module Crystal
17421742
target = exp.target
17431743
return target if target.is_a?(Var)
17441744
when Expressions
1745-
return unless exp = single_expression(exp)
1745+
return unless exp = exp.single_expression?
17461746
return get_expression_var(exp)
17471747
end
17481748
nil
@@ -1850,13 +1850,13 @@ module Crystal
18501850
# block is when the condition is a Var (in the else it must be
18511851
# nil), IsA (in the else it's not that type), RespondsTo
18521852
# (in the else it doesn't respond to that message) or Not.
1853-
case cond = single_expression(node.cond) || node.cond
1853+
case cond = node.cond.single_expression
18541854
when Var, IsA, RespondsTo, Not
18551855
filter_vars cond_type_filters, &.not
18561856
when Or
18571857
# Try to apply boolean logic: `!(a || b)` is `!a && !b`
1858-
cond_left = single_expression(cond.left) || cond.left
1859-
cond_right = single_expression(cond.right) || cond.right
1858+
cond_left = cond.left.single_expression
1859+
cond_right = cond.right.single_expression
18601860

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

2029-
cond = single_expression(node.cond) || node.cond
2029+
cond = node.cond.single_expression
20302030

20312031
endless_while = cond.true_literal?
20322032
merge_while_vars cond, endless_while, before_cond_vars_copy, before_cond_vars, after_cond_vars, @vars, node.break_vars
@@ -2158,7 +2158,7 @@ module Crystal
21582158
when Call
21592159
return get_while_cond_assign_target(node.obj)
21602160
when Expressions
2161-
return unless node = single_expression(node)
2161+
return unless node = node.single_expression?
21622162
return get_while_cond_assign_target(node)
21632163
end
21642164

@@ -2192,16 +2192,6 @@ module Crystal
21922192
end
21932193
end
21942194

2195-
def single_expression(node)
2196-
result = nil
2197-
2198-
while node.is_a?(Expressions) && node.expressions.size == 1
2199-
result = node = node[0]
2200-
end
2201-
2202-
result
2203-
end
2204-
22052195
def end_visit(node : Break)
22062196
if last_block_kind == :ensure
22072197
node.raise "can't use break inside ensure"

Diff for: ‎src/compiler/crystal/syntax/ast.cr

+19
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,18 @@ module Crystal
8989
def pretty_print(pp)
9090
pp.text to_s
9191
end
92+
93+
# It yields itself for any node, but `Expressions` yields first node
94+
# if it holds only a node.
95+
def single_expression
96+
single_expression? || self
97+
end
98+
99+
# It yields `nil` always.
100+
# (It is overrided by `Expressions` to implement `#single_expression`.)
101+
def single_expression?
102+
nil
103+
end
92104
end
93105

94106
class Nop < ASTNode
@@ -146,6 +158,13 @@ module Crystal
146158
@end_location || @expressions.last?.try &.end_location
147159
end
148160

161+
# It yields first node if this holds only one node, or yields `nil`.
162+
def single_expression?
163+
return @expressions.first.single_expression if @expressions.size == 1
164+
165+
nil
166+
end
167+
149168
def accept_children(visitor)
150169
@expressions.each &.accept visitor
151170
end

0 commit comments

Comments
 (0)
Please sign in to comment.