问题
Is there a build-in Julia function for stripping LineNumberNode
in Expr
? especially for macrocalls:
julia> ex = :(@foo 1)
:(#= REPL[5]:1 =# @foo 1)
julia> dump(ex)
Expr
head: Symbol macrocall
args: Array{Any}((3,))
1: Symbol @foo
2: LineNumberNode
line: Int64 1
file: Symbol REPL[5]
3: Int64 1
Tried MacroTools.striplines
, but
julia> ex = :(@foo 1+1)
:(#= REPL[7]:1 =# @foo 1 + 1)
julia> MacroTools.striplines(ex) |> dump
Expr
head: Symbol macrocall
args: Array{Any}((3,))
1: Symbol @foo
2: LineNumberNode
line: Int64 1
file: Symbol REPL[7]
3: Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol +
2: Int64 1
3: Int64 1
My use-case is to compare two different exprs constructed in different files(so different line number info). My current workaround is to explicitly write Expr(:macrocall, Symbol("@foo"), nothing, :(1+1)) which is a little bit verbose.
回答1:
The built-in function is Base.remove_linenums!
:
julia> ex = quote begin
x = 3
y = 2
z = 4
foo(x) = 3
end
end
quote
#= REPL[2]:1 =#
begin
#= REPL[2]:2 =#
x = 3
#= REPL[2]:3 =#
y = 2
#= REPL[2]:4 =#
z = 4
#= REPL[2]:5 =#
foo(x) = begin
#= REPL[2]:5 =#
3
end
end
end
julia> Base.remove_linenums!(ex)
quote
begin
x = 3
y = 2
z = 4
foo(x) = begin
3
end
end
end
Credit to Alex Arslan for reminding me of it.
回答2:
Not built in, but MacroTools.jl has MacroTools.striplines(ex)
which removes the LineNumberNodes
from an expression.
回答3:
Since your goal is to be able to compare Expr
s maybe replace LineNumberNode
s with nothing
. This allows to make comparisons and the Expr
s still work. See the example below:
julia> macro hello(world)
println("hello ",world)
end
@hello (macro with 1 method)
julia> m1 = :(@hello "world")
:(#= REPL[99]:1 =# @hello "world")
julia> m2 = :(@hello "world")
:(#= REPL[100]:1 =# @hello "world")
julia> m1 == m2
false
julia> replace!(arg -> typeof(arg) <: LineNumberNode ? nothing : arg, m1.args);
julia> replace!(arg -> typeof(arg) <: LineNumberNode ? nothing : arg, m2.args);
julia> dump(m1)
Expr
head: Symbol macrocall
args: Array{Any}((3,))
1: Symbol @hello
2: Nothing nothing
3: String "world"
julia> eval(m1)
hello world
julia> m1 == m2
true
Of course if your code is nested you will have to make the replace its elements recursively over the entire Expr
's AST.
回答4:
You can consider defining the following function to achieve what you want by comparing two expressions for equality ignoring line number nodes:
function cmpexpr(ex1::Expr, ex2::Expr)
ex1.head === ex2.head || return false
length(ex1.args) === length(ex2.args) || return false
for (a1, a2) in zip(ex1.args, ex2.args)
typeof(a1) === typeof(a2) || return false
if a1 isa Expr
cmpexpr(a1, a2) || return false
elseif !(a1 isa LineNumberNode)
isequal(a1, a2) || return false
end
end
return true
end
来源:https://stackoverflow.com/questions/53274001/generic-function-for-stripping-linenumbernode-in-exprshould-be-able-to-deal