Skip to content

Commit

Permalink
Improve Expr comparison code for registry testing (#175)
Browse files Browse the repository at this point in the history
Add special cases to explicitly allow a few incompatibilities for cases
where the reference parser has bugs:

* `0x1.8p23f` is a `Float64` literal, with the trailing `f` ignored (also `0x1p1f0`)
* The macrocall in `"@f(a=1) do\nend"` is not the same as the call in `@f(a=1)`
* `global (x,y)` is the same as `global x,y`
* Triple quoted indentation - `"\"\"\"\n  a\n \n  b\"\"\""` parses to "a\n \nb"
  • Loading branch information
c42f authored Dec 20, 2022
1 parent f7ad15f commit 5ee03f2
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 1 deletion.
90 changes: 90 additions & 0 deletions test/test_utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,79 @@ function parse_diff(text, showfunc=dump)
show_expr_text_diff(stdout, showfunc, ex, fl_ex)
end

function kw_to_eq(ex)
return Meta.isexpr(:kw, ex) ? Expr(:(=), ex.args...) : ex
end

function triple_string_roughly_equal(str, fl_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)
return false
end
end
return true
end

# Compare Expr from reference parser expression to JuliaSyntax parser, ignoring
# differences due to bugs in the reference parser.
function exprs_roughly_equal(fl_ex, ex)
if fl_ex isa Float64 && Meta.isexpr(ex, :call, 3) &&
ex.args[1] == :* &&
ex.args[2] == fl_ex &&
(ex.args[3] == :f || ex.args[3] == :f0)
# 0x1p0f
return true
elseif !(fl_ex isa Expr) || !(ex isa Expr)
if fl_ex isa String && ex isa String
if fl_ex == ex
return true
else
return triple_string_roughly_equal(ex, fl_ex)
end
else
return fl_ex == ex
end
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
end
if length(fl_args) != length(args)
return false
end
if h == :do && length(args) >= 1 && Meta.isexpr(fl_args[1], :macrocall)
# Macrocalls with do, as in `@f(a=1) do\nend` use :kw in the
# reference parser for the `a=1`, but we regard this as a bug.
fl_args = copy(fl_args)
fl_args[1] = Expr(:macrocall, map(kw_to_eq, args[1].args)...)
end
for i = 1:length(args)
flarg = fl_args[i]
arg = args[i]
if !exprs_roughly_equal(flarg, arg)
return false
end
end
return true
end

function parsers_agree_on_file(filename; show_diff=false)
text = try
read(filename, String)
Expand All @@ -93,6 +166,8 @@ function parsers_agree_on_file(filename; show_diff=false)
return !JuliaSyntax.any_error(stream) &&
JuliaSyntax.remove_linenums!(ex) ==
JuliaSyntax.remove_linenums!(fl_ex)
# Could alternatively use
# exprs_roughly_equal(fl_ex, ex)
catch exc
@error "Parsing failed" filename exception=current_exceptions()
return false
Expand Down Expand Up @@ -308,3 +383,18 @@ function parse_sexpr(code)
st
end


@testset "Test tools" begin
@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)))
@test exprs_roughly_equal(1.5,
Expr(:call, :*, 1.5, :f))
@test exprs_roughly_equal(1.5,
Expr(:call, :*, 1.5, :f0))
@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)))))
end
2 changes: 1 addition & 1 deletion tools/check_all_packages.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Logging.with_logger(logger) do
@assert Meta.isexpr(e2, :toplevel)
try
e1 = JuliaSyntax.parseall(Expr, code, filename=fpath)
if JuliaSyntax.remove_linenums!(e1) != JuliaSyntax.remove_linenums!(e2)
if !exprs_roughly_equal(e2, e1)
mismatch_count += 1
@error("Parsers succeed but disagree",
fpath,
Expand Down

0 comments on commit 5ee03f2

Please sign in to comment.