Skip to content

Commit cb7cff6

Browse files
authored
Merge branch 'master' into optimize-enum-parse
2 parents 63e4432 + 33f016f commit cb7cff6

File tree

7 files changed

+165
-21
lines changed

7 files changed

+165
-21
lines changed

.github/workflows/win.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ concurrency:
1010

1111
env:
1212
SPEC_SPLIT_DOTS: 160
13-
CI_LLVM_VERSION: "19.1.7"
13+
CI_LLVM_VERSION: "20.1.7"
1414
CI_LLVM_TARGETS: "X86,AArch64"
1515
CI_LLVM_LDFLAGS: "psapi.lib shell32.lib ole32.lib uuid.lib advapi32.lib ws2_32.lib ntdll.lib"
1616

@@ -64,7 +64,7 @@ jobs:
6464
run: .\etc\win-ci\build-iconv.ps1 -BuildTree deps\iconv -Version 1.18
6565
- name: Build libffi
6666
if: steps.cache-libs.outputs.cache-hit != 'true'
67-
run: .\etc\win-ci\build-ffi.ps1 -BuildTree deps\ffi -Version 3.4.7
67+
run: .\etc\win-ci\build-ffi.ps1 -BuildTree deps\ffi -Version 3.5.1
6868
- name: Build zlib
6969
if: steps.cache-libs.outputs.cache-hit != 'true'
7070
run: .\etc\win-ci\build-z.ps1 -BuildTree deps\z -Version 1.3.1
@@ -76,7 +76,7 @@ jobs:
7676
run: .\etc\win-ci\build-yaml.ps1 -BuildTree deps\yaml -Version 0.2.5
7777
- name: Build libxml2
7878
if: steps.cache-libs.outputs.cache-hit != 'true'
79-
run: .\etc\win-ci\build-xml2.ps1 -BuildTree deps\xml2 -Version 2.13.6
79+
run: .\etc\win-ci\build-xml2.ps1 -BuildTree deps\xml2 -Version 2.13.8
8080

8181
- name: Cache OpenSSL
8282
id: cache-openssl
@@ -92,7 +92,7 @@ jobs:
9292
uses: ilammy/setup-nasm@72793074d3c8cdda771dba85f6deafe00623038b # v1.5.2
9393
- name: Build OpenSSL
9494
if: steps.cache-openssl.outputs.cache-hit != 'true'
95-
run: .\etc\win-ci\build-openssl.ps1 -BuildTree deps\openssl -Version 3.4.1
95+
run: .\etc\win-ci\build-openssl.ps1 -BuildTree deps\openssl -Version 3.5.0
9696

9797
x86_64-windows-dlls:
9898
runs-on: windows-2025
@@ -214,7 +214,7 @@ jobs:
214214
uses: ./.github/workflows/win_build_portable.yml
215215
with:
216216
release: true
217-
llvm_version: "19.1.7"
217+
llvm_version: "20.1.7"
218218
llvm_targets: "X86,AArch64"
219219
llvm_ldflags: "psapi.lib shell32.lib ole32.lib uuid.lib advapi32.lib ws2_32.lib ntdll.lib"
220220

spec/compiler/normalize/case_spec.cr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ require "../../spec_helper"
22

33
describe "Normalize: case" do
44
it "normalizes case with call" do
5-
assert_expand "case x; when 1; 'b'; when 2; 'c'; else; 'd'; end", "__temp_1 = x\nif 1 === __temp_1\n 'b'\nelse\n if 2 === __temp_1\n 'c'\n else\n 'd'\n end\nend"
5+
assert_expand "case x; when 1; 'b'; when 2; 'c'; else; 'd'; end", "__temp_1 = x\nif 1 === __temp_1\n 'b'\nelsif 2 === __temp_1\n 'c'\nelse\n 'd'\nend"
66
end
77

88
it "normalizes case with var in cond" do
@@ -62,11 +62,11 @@ describe "Normalize: case" do
6262
end
6363

6464
it "normalizes case without value" do
65-
assert_expand "case when 2; 3; when 4; 5; end", "if 2\n 3\nelse\n if 4\n 5\n end\nend"
65+
assert_expand "case when 2; 3; when 4; 5; end", "if 2\n 3\nelsif 4\n 5\nend"
6666
end
6767

6868
it "normalizes case without value with many expressions in when" do
69-
assert_expand "case when 2, 9; 3; when 4; 5; end", "if 2 || 9\n 3\nelse\n if 4\n 5\n end\nend"
69+
assert_expand "case when 2, 9; 3; when 4; 5; end", "if 2 || 9\n 3\nelsif 4\n 5\nend"
7070
end
7171

7272
it "normalizes case with nil to is_a?" do

spec/compiler/parser/parser_spec.cr

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1271,6 +1271,19 @@ module Crystal
12711271
assert_syntax_error "{% unless 1; 2; elsif 3; 4; end %}"
12721272
assert_syntax_error "{% unless 1 %} 2 {% elsif 3 %} 3 {% end %}"
12731273

1274+
it_parses "{% if 1; 2; end; %}", MacroExpression.new(If.new(1.int32, 2.int32), output: false)
1275+
it_parses "{% if 1; 2; end; 3 %}", MacroExpression.new(Expressions.new([If.new(1.int32, 2.int32), 3.int32]), output: false)
1276+
it_parses "{%\nif 1; 2; end; 3\n%}", MacroExpression.new(Expressions.new([If.new(1.int32, 2.int32), 3.int32]), output: false)
1277+
it_parses "{% 2 if 1; 3 %}", MacroExpression.new(Expressions.new([If.new(1.int32, 2.int32), 3.int32]), output: false)
1278+
it_parses "{%\n2 if 1; 3\n%}", MacroExpression.new(Expressions.new([If.new(1.int32, 2.int32), 3.int32]), output: false)
1279+
it_parses "{% if 1; 2; elsif 3; 4; else; 5; end; 6 %}", MacroExpression.new(Expressions.new([If.new(1.int32, 2.int32, If.new(3.int32, 4.int32, 5.int32)), 6.int32]), output: false)
1280+
1281+
it_parses "{% unless 1; 2; end; %}", MacroExpression.new(Unless.new(1.int32, 2.int32), output: false)
1282+
it_parses "{% unless 1; 2; end; 3 %}", MacroExpression.new(Expressions.new([Unless.new(1.int32, 2.int32), 3.int32]), output: false)
1283+
it_parses "{%\nunless 1; 2; end; 3\n%}", MacroExpression.new(Expressions.new([Unless.new(1.int32, 2.int32), 3.int32]), output: false)
1284+
it_parses "{% 2 unless 1; 3 %}", MacroExpression.new(Expressions.new([Unless.new(1.int32, 2.int32), 3.int32]), output: false)
1285+
it_parses "{%\n2 unless 1; 3\n%}", MacroExpression.new(Expressions.new([Unless.new(1.int32, 2.int32), 3.int32]), output: false)
1286+
12741287
it_parses "{{ 1 // 2 }}", MacroExpression.new(Expressions.from([Call.new(1.int32, "//", 2.int32)] of ASTNode))
12751288
it_parses "{{ //.options }}", MacroExpression.new(Expressions.from([Call.new(RegexLiteral.new(StringLiteral.new("")), "options")] of ASTNode))
12761289

spec/compiler/parser/to_s_spec.cr

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ describe "ASTNode#to_s" do
6363
expect_to_s %({{ foo }})
6464
expect_to_s %({% if foo %}\n foo_then\n{% end %})
6565
expect_to_s %({% if foo %}\n foo_then\n{% else %}\n foo_else\n{% end %})
66+
expect_to_s %({% unless foo %}\n foo_then\n{% end %})
67+
expect_to_s %({% unless foo %}\n foo_then\n{% else %}\n foo_else\n{% end %})
6668
expect_to_s %({% for foo in bar %}\n {{ foo }}\n{% end %})
6769
expect_to_s %(macro foo\n {% for foo in bar %}\n {{ foo }}\n {% end %}\nend)
6870
expect_to_s %[1.as(Int32)]
@@ -229,6 +231,59 @@ describe "ASTNode#to_s" do
229231
expect_to_s %(begin\n (@x = x).is_a?(Foo)\nend)
230232
expect_to_s %(begin\n (1)\n 2\nend)
231233
expect_to_s %(if 1\n begin\n 2\n end\nelse\n begin\n 3\n end\nend)
234+
235+
expect_to_s <<-CRYSTAL
236+
if 1
237+
2
238+
elsif 3
239+
4
240+
elsif 5
241+
elsif 6
242+
else
243+
7
244+
end
245+
CRYSTAL
246+
247+
expect_to_s <<-CRYSTAL, <<-CRYSTAL
248+
if 1
249+
2
250+
else
251+
if 3
252+
end
253+
end
254+
CRYSTAL
255+
if 1
256+
2
257+
elsif 3
258+
end
259+
CRYSTAL
260+
261+
expect_to_s <<-CRYSTAL
262+
if 1
263+
2
264+
else
265+
unless 3
266+
end
267+
end
268+
CRYSTAL
269+
270+
expect_to_s <<-CRYSTAL
271+
if 1
272+
2
273+
else
274+
3 ? 4 : 5
275+
end
276+
CRYSTAL
277+
278+
expect_to_s <<-CRYSTAL
279+
unless 1
280+
2
281+
else
282+
if 3
283+
end
284+
end
285+
CRYSTAL
286+
232287
expect_to_s %(foo do\n begin\n bar\n end\nend)
233288
expect_to_s %q("\e\0\""), %q("\e\u0000\"")
234289
expect_to_s %q("#{1}\0"), %q("#{1}\u0000")
@@ -291,6 +346,25 @@ describe "ASTNode#to_s" do
291346
expect_to_s "{%\n a = 1 %}"
292347
expect_to_s "{% a = 1\n%}"
293348

349+
expect_to_s <<-'CRYSTAL'
350+
{%
351+
if 1
352+
2
353+
end
354+
3
355+
%}
356+
CRYSTAL
357+
358+
expect_to_s <<-'CRYSTAL'
359+
{%
360+
if 1
361+
2
362+
end
363+
3
364+
4
365+
%}
366+
CRYSTAL
367+
294368
expect_to_s <<-'CR', <<-'CR'
295369
macro finished
296370
{% verbatim do %}

src/compiler/crystal/syntax/ast.cr

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,30 @@ module Crystal
150150
obj
151151
end
152152

153+
# Concatenates two AST nodes into a single `Expressions` node, removing
154+
# `Nop`s and merging keyword-less expressions into a single node.
155+
#
156+
# *x* and *y* may be modified in-place if they are already `Expressions`
157+
# nodes.
158+
def self.concat!(x : ASTNode, y : ASTNode) : ASTNode
159+
return x if y.is_a?(Nop)
160+
return y if x.is_a?(Nop)
161+
162+
if x.is_a?(Expressions) && x.keyword.none?
163+
if y.is_a?(Expressions) && y.keyword.none?
164+
x.expressions.concat(y.expressions)
165+
else
166+
x.expressions << y
167+
end
168+
x.at_end(y.end_location)
169+
elsif y.is_a?(Expressions) && y.keyword.none?
170+
y.expressions.unshift(x)
171+
y.at(x.location)
172+
else
173+
Expressions.new([x, y] of ASTNode).at(x.location).at_end(y.end_location)
174+
end
175+
end
176+
153177
def initialize(@expressions = [] of ASTNode)
154178
end
155179

src/compiler/crystal/syntax/parser.cr

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3477,10 +3477,13 @@ module Crystal
34773477
else
34783478
node = parse_if_after_condition cond, location, true
34793479
end
3480+
skip_statement_end
3481+
exps = parse_expressions
34803482
@in_macro_expression = false
3481-
skip_space_or_newline
34823483
check :OP_PERCENT_RCURLY
3483-
return MacroExpression.new(node, output: false).at_end(token_end_location)
3484+
3485+
exps = Expressions.concat!(node, exps)
3486+
return MacroExpression.new(exps, output: false).at_end(token_end_location)
34843487
end
34853488

34863489
check :OP_PERCENT_RCURLY

src/compiler/crystal/syntax/to_s.cr

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -334,16 +334,35 @@ module Crystal
334334
return false
335335
end
336336

337-
visit_if_or_unless "if", node
338-
end
337+
while true
338+
@str << "if "
339+
node.cond.accept self
340+
newline
341+
accept_with_indent(node.then)
342+
append_indent
339343

340-
def visit(node : Unless)
341-
visit_if_or_unless "unless", node
344+
# combine `else if` into `elsif` (does not apply to `unless` or `? :`)
345+
if (else_node = node.else).is_a?(If) && !else_node.ternary?
346+
@str << "els"
347+
node = else_node
348+
else
349+
break
350+
end
351+
end
352+
353+
unless else_node.nop?
354+
@str << "else"
355+
newline
356+
accept_with_indent(node.else)
357+
append_indent
358+
end
359+
360+
@str << "end"
361+
false
342362
end
343363

344-
def visit_if_or_unless(prefix, node)
345-
@str << prefix
346-
@str << ' '
364+
def visit(node : Unless)
365+
@str << "unless "
347366
node.cond.accept self
348367
newline
349368
accept_with_indent(node.then)
@@ -885,18 +904,29 @@ module Crystal
885904
end
886905

887906
def visit(node : MacroIf)
888-
@str << "{% if "
907+
if node.is_unless?
908+
@str << "{% unless "
909+
then_node = node.else
910+
else_node = node.then
911+
else
912+
@str << "{% if "
913+
then_node = node.then
914+
else_node = node.else
915+
end
889916
node.cond.accept self
890917
@str << " %}"
918+
891919
inside_macro do
892-
node.then.accept self
920+
then_node.accept self
893921
end
894-
unless node.else.nop?
922+
923+
unless else_node.nop?
895924
@str << "{% else %}"
896925
inside_macro do
897-
node.else.accept self
926+
else_node.accept self
898927
end
899928
end
929+
900930
@str << "{% end %}"
901931
false
902932
end

0 commit comments

Comments
 (0)