Skip to content

Commit

Permalink
Allow different parsings of (a; b,) to compare equal (#184)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
c42f authored Jan 24, 2023
1 parent 2470b28 commit 1988ddb
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 13 deletions.
2 changes: 0 additions & 2 deletions test/expr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,6 @@
@test parse(Expr, "'a'") == 'a'
@test parse(Expr, "'α'") == 'α'
@test parse(Expr, "'\\xce\\xb1'") == 'α'
# FIXME
# @test_throws ParseError parse(Expr, "'abcde'")
end

@testset "do block conversion" begin
Expand Down
19 changes: 18 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,38 @@ using JuliaSyntax: GreenNode, SyntaxNode,
children, child, setchild!, SyntaxHead

include("test_utils.jl")

# Tests for the test_utils go here to allow the utils to be included on their
# own without invoking the tests.
@testset "Test tools" begin
@testset "Reference parser bugs" begin
# `global (x,y)`
@test exprs_roughly_equal(Expr(:global, :x, :y),
Expr(:global, Expr(:tuple, :x, :y)))
@test exprs_roughly_equal(Expr(:local, :x, :y),
Expr(:local, Expr(:tuple, :x, :y)))
# `0x1.8p0f`
@test exprs_roughly_equal(1.5,
Expr(:call, :*, 1.5, :f))
@test exprs_roughly_equal(1.5,
Expr(:call, :*, 1.5, :f0))
# `@f(a=1) do \n end`
@test exprs_roughly_equal(Expr(:do, Expr(:macrocall, Symbol("@f"), LineNumberNode(1), Expr(:kw, :a, 1)),
Expr(:->, Expr(:tuple), Expr(:block, LineNumberNode(1)))),
Expr(:do, Expr(:macrocall, Symbol("@f"), LineNumberNode(1), Expr(:(=), :a, 1)),
Expr(:->, Expr(:tuple), Expr(:block, LineNumberNode(1)))))
# `"""\n a\n \n b"""`
@test exprs_roughly_equal("a\n \nb", " a\n\n b")
@test !exprs_roughly_equal("a\n x\nb", " a\n x\n b")
@test exprs_roughly_equal("a\n x\nb", "a\n x\nb")
# `(a; b,)`
@test exprs_roughly_equal(Expr(:block, :a, LineNumberNode(1), :b),
Expr(:tuple, Expr(:parameters, :b), :a))
@test !exprs_roughly_equal(Expr(:block, :a, LineNumberNode(1), :b),
Expr(:tuple, Expr(:parameters, :c), :a))
@test !exprs_roughly_equal(Expr(:block, :a, LineNumberNode(1), :b),
Expr(:tuple, Expr(:parameters, :b), :c))
@test !exprs_roughly_equal(Expr(:block, :a, LineNumberNode(1), :b, :c),
Expr(:tuple, Expr(:parameters, :b), :a))
end

@testset "Tokenize" begin
Expand Down
37 changes: 27 additions & 10 deletions test/test_utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,22 @@ function kw_to_eq(ex)
return Meta.isexpr(ex, :kw) ? Expr(:(=), ex.args...) : ex
end

function triple_string_roughly_equal(str, fl_str)
function triple_string_roughly_equal(fl_str, str)
# Allow some leeway for a bug in the reference parser with
# triple quoted strings
lines = split(str, '\n')
fl_lines = split(fl_str, '\n')
if length(lines) != length(fl_lines)
return false
end
for (line1, line2) in zip(lines, fl_lines)
if !all(c in " \t" for c in line2) && !endswith(line1, line2)
has_whitespace_only_line =
any(!isempty(fl_line) && all(c in " \t" for c in fl_line)
for fl_line in fl_lines)
if !has_whitespace_only_line
return str == fl_str
end
for (line, fl_line) in zip(lines, fl_lines)
if !all(c in " \t" for c in fl_line) && !endswith(line, fl_line)
return false
end
end
Expand All @@ -107,22 +113,33 @@ function exprs_roughly_equal(fl_ex, ex)
if fl_ex == ex
return true
else
return triple_string_roughly_equal(ex, fl_ex)
return triple_string_roughly_equal(fl_ex, ex)
end
else
return fl_ex == ex
end
end
# Ignore differences in line number nodes within block-like constructs
fl_args = fl_ex.head in (:block, :quote, :toplevel) ?
filter(x->!(x isa LineNumberNode), fl_ex.args) :
fl_ex.args
args = ex.head in (:block, :quote, :toplevel) ?
filter(x->!(x isa LineNumberNode), ex.args) :
ex.args
if (fl_ex.head == :block && ex.head == :tuple &&
length(fl_args) == 2 && length(args) == 2 &&
Meta.isexpr(args[1], :parameters, 1) &&
exprs_roughly_equal(fl_args[2], args[1].args[1]) &&
exprs_roughly_equal(fl_args[1], args[2]))
# Allow `(a; b,)`:
# * Reference parser produces a block
# * New parser produces a frankentuple
return true
end
if fl_ex.head != ex.head
return false
end
h = ex.head
fl_args = fl_ex.args
args = ex.args
if ex.head in (:block, :quote, :toplevel)
fl_args = filter(x->!(x isa LineNumberNode), fl_args)
args = filter(x->!(x isa LineNumberNode), args)
end
if (h == :global || h == :local) && length(args) == 1 && Meta.isexpr(args[1], :tuple)
# Allow invalid syntax like `global (x, y)`
args = args[1].args
Expand Down

0 comments on commit 1988ddb

Please sign in to comment.