how to to create a mulitline macro in julia?

Viewed 89
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)

1 Answers

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.

Related