gcv  
                
                  
                    January 6, 2022,  5:28am
                   
                  1 
               
             
            
              I’m having trouble with macros taking multiple parameters. Specifically, I’m writing a macro which needs to take @__DIR__ and an expression as its arguments. Here’s a simplified example:
macro tm1(dir, action)
   :(begin
        println("tm1 dir: ", $dir)
        $action
     end)
end
The following invocation fails:
@tm1 @__DIR__ begin
   3 + 3
end
The error is:
ERROR: LoadError: LoadError: MethodError: no method matching var"@tm1"(::LineNumberNode, ::Module, ::Expr)
Closest candidates are:
  var"@tm1"(::LineNumberNode, ::Module, ::Any, ::Any) at /path/to/file.jl
But if I invoke the macro with an explicit string instead of @__DIR__, it works:
@tm1 "/tmp" begin
   3 + 3
end
All right, maybe macros don’t like taking another macro as an argument?
macro tm2(dir)
   :(begin
        println("tm2 dir: ", $dir)
     end)
end
@tm2 @__DIR__
Which works fine! So a macro with one  parameter can take another macro as an argument, but with two (or more?) it breaks? WTF?
Julia version 1.6.2.
             
            
              
            
           
          
            
              
                jzr  
              
                  
                    January 6, 2022,  5:31am
                   
                  2 
               
             
            
              @__DIR__ needs to be in parentheses.
             
            
              1 Like 
            
            
           
          
            
              
                rdeits  
              
                  
                    January 6, 2022,  2:14pm
                   
                  3 
               
             
            
              The problem is that when you write:
@a @b x
the parser treats that as calling macro @b with argument x, then calling macro @a with just one argument.
You can see that by using dump to look at the syntax tree:
julia> dump(:(@a @b x))
Expr
  head: Symbol macrocall
  args: Array{Any}((3,))
    1: Symbol @a
    2: LineNumberNode
      line: Int64 1
      file: Symbol REPL[5]
    3: Expr
      head: Symbol macrocall
      args: Array{Any}((3,))
        1: Symbol @b
        2: LineNumberNode
          line: Int64 1
          file: Symbol REPL[5]
        3: Symbol x
or just by constructing the expression and letting julia add some parentheses to disambiguate:
julia> :(@a @b x)
:(#= REPL[6]:1 =# @a #= REPL[6]:1 =# @b(x))
And you can fix this by adding the parentheses that you actually want instead:
julia> @tm1(@__DIR__(), begin
         3 + 3
       end)
tm1 dir: /home/user
6
In this case, it’s also sufficient to just add parens on the call to @__DIR__ to avoid it trying to grab the whole begin block as an argument:
julia> @tm1 @__DIR__() begin
         3 + 3
       end
tm1 dir: /home/user
6
 
            
              2 Likes