Capturing where-clause using MacroTools

Hi there! I wonder if anyone can help me with this… I would like to write a macro that takes a function definition and modifies it to do something at the beginning and at the end. So I cooked up this MWE but I got stuck on using the where-clause from what it was captured:

using MacroTools
macro foo(ex)
    @capture(ex, function f_(args__) where T_ body_ end) || error("bad ex")
    @info "captured" f args T body
    newex = :(
        function $(esc(f))($(esc(args...))) where $T
            println("pre")
            $(esc(body))
            println("post")
        end
    )
    # @info "newex" newex
    return newex
end

And it’s give me this error:

julia> @foo function wat(x::T) where {T <: Number} 
           @info "variables" x 
           return x + 1
       end
┌ Info: captured
│   f = :wat
│   args =
│    1-element Array{Any,1}:
│     :(x::T)
│   T = :(T <: Number)
│   body =
│    quote
│        #= REPL[23]:2 =#
│        #= REPL[23]:2 =# @info "variables" x
│        #= REPL[23]:3 =#
│        return x + 1
└    end
ERROR: UndefVarError: T not defined

What am I missing? Thanks

1 Like

I don’t currently have time to figure what what went wrong in your code, but I would recommend just using MacroTools.splitdef instead:

julia> using MacroTools: splitdef

julia> d = splitdef(:(function wat(x::T) where {T <: Number}
                          @info "variables" x
                          return x + 1
                      end))
Dict{Symbol,Any} with 5 entries:
  :name        => :wat
  :args        => Any[:(x::T)]
  :kwargs      => Any[]
  :body        => quote…
  :whereparams => (:(T <: Number),)

julia> d[:name]
:wat

julia> d[:body]
quote
    #= REPL[51]:2 =#
    #= REPL[51]:2 =# @info "variables" x
    #= REPL[51]:3 =#
    return x + 1
end

You might also want to check out MacroTools.combinedef to put such dictionaries back together into an expr.

4 Likes

You need to change the $T in newex to $(esc(T)) as well.

1 Like

splitdef and combinedef is VERY nice!

3 Likes