Search code examples
macrosjuliametaprogramming

how to to create a mulitline macro in julia?


macro Estruct(name,arg,type,max=100,min=0,descritpion="")
        :(struct $(esc(name))
        $((esc(arg)))
        $(esc(name))($(esc(arg)))=new(
          check($(esc(type)),$(esc(arg)),$(esc(name)),$(esc(max)),$(esc(min)),$(esc(descritpion))))
       end)
end

how can I imeplent this macro like this:

@Estruct begin
    B 
    arg1 
    Float64 
    200.0 
    5.0 
    "this"
end

I don't know how to make a multiline macro. I thought I just have to add begin and end, but I'm always getting: MethodError: no method matching var"@Estruct"(::LineNumberNode, ::Module, ::Expr)


Solution

  • There's no such thing as a multiline macro, that's just a macro that takes a block as an argument. You can see how the macro gets invoked by writing a dummy version of it that just returns its arguments:

    macro Estruct(args...); args; end
    

    Now you can invoke that the way you want to call it and it will return its arguments as a tuple:

    julia> args = @Estruct begin
               B
               arg1
               Float64
               200.0
               5.0
               "this"
           end
    (quote
        #= REPL[12]:2 =#
        B
        #= REPL[12]:3 =#
        arg1
        #= REPL[12]:4 =#
        Float64
        #= REPL[12]:5 =#
        200.0
        #= REPL[12]:6 =#
        5.0
        #= REPL[12]:7 =#
        "this"
    end,)
    
    
    julia> typeof(args)
    Tuple{Expr}
    
    julia> dump(args[1])
    Expr
      head: Symbol block
      args: Array{Any}((12,))
        1: LineNumberNode
          line: Int64 2
          file: Symbol REPL[12]
        2: Symbol B
        3: LineNumberNode
          line: Int64 3
          file: Symbol REPL[12]
        4: Symbol arg1
        5: LineNumberNode
          line: Int64 4
          file: Symbol REPL[12]
        ...
        8: Float64 200.0
        9: LineNumberNode
          line: Int64 6
          file: Symbol REPL[12]
        10: Float64 5.0
        11: LineNumberNode
          line: Int64 7
          file: Symbol REPL[12]
        12: String "this"
    
    julia> args[1].args
    12-element Vector{Any}:
        :(#= REPL[12]:2 =#)
        :B
        :(#= REPL[12]:3 =#)
        :arg1
        :(#= REPL[12]:4 =#)
        :Float64
        :(#= REPL[12]:5 =#)
     200.0
        :(#= REPL[12]:6 =#)
       5.0
        :(#= REPL[12]:7 =#)
        "this"
    

    This tells you that the macro gets called with a single argument which is an Expr with head type :block, i.e. the one argument is a quoted block. To implement your "multiline macro" you need to implement the macro body to accept a quoted block expression and process it, presumably by looking at its .args field which is where all the expressions you're interested in are. You'll probably want to ignore all the LineNumberNode objects in there and just process the other items in the block.