Generic function for stripping `LineNumberNode` in `Expr`(should be able to deal with :macrocalls)?

橙三吉。 提交于 2020-06-12 04:07:51

问题


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 Exprs maybe replace LineNumberNodes with nothing. This allows to make comparisons and the Exprs 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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!