Skip to content

Commit 1988ddb

Browse files
authored
Allow different parsings of (a; b,) to compare equal (#184)
The reference parser sees `(a; b,)` as a block, but the trailing comma implies this should be a frakentuple. Allow this unusual syntax as a minor bug in the reference parser.
1 parent 2470b28 commit 1988ddb

File tree

3 files changed

+45
-13
lines changed

3 files changed

+45
-13
lines changed

test/expr.jl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,6 @@
161161
@test parse(Expr, "'a'") == 'a'
162162
@test parse(Expr, "'α'") == 'α'
163163
@test parse(Expr, "'\\xce\\xb1'") == 'α'
164-
# FIXME
165-
# @test_throws ParseError parse(Expr, "'abcde'")
166164
end
167165

168166
@testset "do block conversion" begin

test/runtests.jl

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,38 @@ using JuliaSyntax: GreenNode, SyntaxNode,
88
children, child, setchild!, SyntaxHead
99

1010
include("test_utils.jl")
11+
1112
# Tests for the test_utils go here to allow the utils to be included on their
1213
# own without invoking the tests.
13-
@testset "Test tools" begin
14+
@testset "Reference parser bugs" begin
15+
# `global (x,y)`
1416
@test exprs_roughly_equal(Expr(:global, :x, :y),
1517
Expr(:global, Expr(:tuple, :x, :y)))
1618
@test exprs_roughly_equal(Expr(:local, :x, :y),
1719
Expr(:local, Expr(:tuple, :x, :y)))
20+
# `0x1.8p0f`
1821
@test exprs_roughly_equal(1.5,
1922
Expr(:call, :*, 1.5, :f))
2023
@test exprs_roughly_equal(1.5,
2124
Expr(:call, :*, 1.5, :f0))
25+
# `@f(a=1) do \n end`
2226
@test exprs_roughly_equal(Expr(:do, Expr(:macrocall, Symbol("@f"), LineNumberNode(1), Expr(:kw, :a, 1)),
2327
Expr(:->, Expr(:tuple), Expr(:block, LineNumberNode(1)))),
2428
Expr(:do, Expr(:macrocall, Symbol("@f"), LineNumberNode(1), Expr(:(=), :a, 1)),
2529
Expr(:->, Expr(:tuple), Expr(:block, LineNumberNode(1)))))
30+
# `"""\n a\n \n b"""`
31+
@test exprs_roughly_equal("a\n \nb", " a\n\n b")
32+
@test !exprs_roughly_equal("a\n x\nb", " a\n x\n b")
33+
@test exprs_roughly_equal("a\n x\nb", "a\n x\nb")
34+
# `(a; b,)`
35+
@test exprs_roughly_equal(Expr(:block, :a, LineNumberNode(1), :b),
36+
Expr(:tuple, Expr(:parameters, :b), :a))
37+
@test !exprs_roughly_equal(Expr(:block, :a, LineNumberNode(1), :b),
38+
Expr(:tuple, Expr(:parameters, :c), :a))
39+
@test !exprs_roughly_equal(Expr(:block, :a, LineNumberNode(1), :b),
40+
Expr(:tuple, Expr(:parameters, :b), :c))
41+
@test !exprs_roughly_equal(Expr(:block, :a, LineNumberNode(1), :b, :c),
42+
Expr(:tuple, Expr(:parameters, :b), :a))
2643
end
2744

2845
@testset "Tokenize" begin

test/test_utils.jl

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -77,16 +77,22 @@ function kw_to_eq(ex)
7777
return Meta.isexpr(ex, :kw) ? Expr(:(=), ex.args...) : ex
7878
end
7979

80-
function triple_string_roughly_equal(str, fl_str)
80+
function triple_string_roughly_equal(fl_str, str)
8181
# Allow some leeway for a bug in the reference parser with
8282
# triple quoted strings
8383
lines = split(str, '\n')
8484
fl_lines = split(fl_str, '\n')
8585
if length(lines) != length(fl_lines)
8686
return false
8787
end
88-
for (line1, line2) in zip(lines, fl_lines)
89-
if !all(c in " \t" for c in line2) && !endswith(line1, line2)
88+
has_whitespace_only_line =
89+
any(!isempty(fl_line) && all(c in " \t" for c in fl_line)
90+
for fl_line in fl_lines)
91+
if !has_whitespace_only_line
92+
return str == fl_str
93+
end
94+
for (line, fl_line) in zip(lines, fl_lines)
95+
if !all(c in " \t" for c in fl_line) && !endswith(line, fl_line)
9096
return false
9197
end
9298
end
@@ -107,22 +113,33 @@ function exprs_roughly_equal(fl_ex, ex)
107113
if fl_ex == ex
108114
return true
109115
else
110-
return triple_string_roughly_equal(ex, fl_ex)
116+
return triple_string_roughly_equal(fl_ex, ex)
111117
end
112118
else
113119
return fl_ex == ex
114120
end
115121
end
122+
# Ignore differences in line number nodes within block-like constructs
123+
fl_args = fl_ex.head in (:block, :quote, :toplevel) ?
124+
filter(x->!(x isa LineNumberNode), fl_ex.args) :
125+
fl_ex.args
126+
args = ex.head in (:block, :quote, :toplevel) ?
127+
filter(x->!(x isa LineNumberNode), ex.args) :
128+
ex.args
129+
if (fl_ex.head == :block && ex.head == :tuple &&
130+
length(fl_args) == 2 && length(args) == 2 &&
131+
Meta.isexpr(args[1], :parameters, 1) &&
132+
exprs_roughly_equal(fl_args[2], args[1].args[1]) &&
133+
exprs_roughly_equal(fl_args[1], args[2]))
134+
# Allow `(a; b,)`:
135+
# * Reference parser produces a block
136+
# * New parser produces a frankentuple
137+
return true
138+
end
116139
if fl_ex.head != ex.head
117140
return false
118141
end
119142
h = ex.head
120-
fl_args = fl_ex.args
121-
args = ex.args
122-
if ex.head in (:block, :quote, :toplevel)
123-
fl_args = filter(x->!(x isa LineNumberNode), fl_args)
124-
args = filter(x->!(x isa LineNumberNode), args)
125-
end
126143
if (h == :global || h == :local) && length(args) == 1 && Meta.isexpr(args[1], :tuple)
127144
# Allow invalid syntax like `global (x, y)`
128145
args = args[1].args

0 commit comments

Comments
 (0)