Call a macro within a macro


#1

I am trying to define a macro that calls another macro.

To take an example, suppose a package defines a macro @formula as follows:

type Formula
    lhs::Union{Symbol, Expr, Void}
    rhs::Union{Symbol, Expr, Integer}
end
macro formula(ex)
    if (ex.head === :call && ex.args[1] === :(~))
        lhs = Base.Meta.quot(ex.args[2])
        rhs = Base.Meta.quot(ex.args[3])
       return Expr(:call, :Formula, lhs, rhs)
    else
        error("expected ~, got $(ex.args[1])")
    end
end
@formula(y~x)
#> Formula(:y, :x)

My goal is to define a macro @formula2 that simply calls @formula

macro formula2(expr)
   Expr(:macrocall, Symbol("@formula"), expr)
end
@formula2(y~x)
#> ERROR: expected ~, got $(Expr(:globalref, Main, :~))

This simple definition does not work: @formula2(y~x) does not give the same output as @formula(y~x). How could I define @formula2 to obtain exactly the same output as @formula?


#2

Perhaps:

macro formula2(expr)
    :(@formula($(esc(expr))))
end

#3
macro formula2(expr)
    :(@formula $(esc(expr)))
end

#4

Thanks yuyichao! Could you explain a little bit what is happening? Is there some documentation about it?


#5

All user input must be escaped once and exactly once.
https://docs.julialang.org/en/latest/manual/metaprogramming.html#Hygiene-1


#6

Hi @yuyichao , @kristoffer.carlsson

I just encounter a similar problem.

To make it simple:

I have a inner macro:

macro m1(ex)  :(println($ex + 1)) end

and an outer macro:

macro m2(ex)  :(@m1 $(esc(ex))) end

But

julia> for i in 1:3
       @m2 i
       end
ERROR: UndefVarError: i not defined
Stacktrace:
 [1] macro expansion at ./REPL[10]:2 [inlined]
 [2] top-level scope at ./<missing>:0

How can the inner macro access i?

Thanks.


#7

Use esc in the first macro, too, eg

macro m1(ex)
    :(println($(esc(ex)) + 1))
end

macro m2(ex)
    :(@m1 $(esc(ex)))
end

#8

Wow…

Thanks!