why could macros not understand a Dict parameter?

Viewed 147
macro test1(name,arg)
    println(arg.args[2])
    typeof(arg.args[2])
end 

@test1 test1 (
  (arg1, (:max=>10))
)

I have the macro above and I'm trying to pass a Dict parameter as the second parameter. But the problem is that I cannot access it. The macro will always interpret the second parameter as an expression, and when I try to access arg.args[2].args it shows:

Vector{Any} (alias for Array{Any, 1})

so I don't know how to pass the Dict as it is.

I just want to get the second argument like:

Dict{Symbol, Int64} with 1 entry:
  :max => 10
1 Answers

This is because macros work on code before the code is compiled. Source code is first parsed to Symbols, literals (integers, floats, strings, etc), or Expr (expressions). At this point, all expressions contain only these three things.** After the macro is done and returns an expression, that expression is compiled into runtime code where more complicated objects like Dicts can exist.

The code below illustrates the difference before and after compiling. Note how 1+5 and Dict() were expressions in the macro body, but is afterward evaluated to an Int64 and a Dict.

                             # splat arbitrary number of Expr
macro peekExpr(expr1, expr2, expr3tuple...)
    println(typeof(expr1), "   ", expr1)
    println(typeof(expr2), "   ", expr2)
    println(typeof(expr3tuple), "   ", expr3tuple)
    :($expr1, $expr2, $expr3tuple)
end

evaluated = @peekExpr 1+5 Dict() Int64 10 max::Int64
#= printout
Expr   1 + 5
Expr   Dict()
Tuple{Symbol,Int64,Expr}   (:Int64, 10, :(max::Int64))
=#

for item in evaluated  println(typeof(item), "   ", item)  end
#= printout
Int64   6
Dict{Any, Any}   Dict{Any, Any}()
Tuple{Symbol,Int64,Expr}   (:Int64, 10, :(max::Int64))
=#

**PS: Bear in mind that Expr can contain other objects if you interpolate runtime objects into them (x = Dict(); :(a in $x).args[end] vs :(a in Dict()).args[end]). It's just that macros do not work at a phase where it can access runtime objects. @peekExpr $x will only see a Expr(:$, :x).

Related