Mimic functionality of a macro starting from an expression

#1

Sorry if this is a common question and I just missed the answer when searching around.

I’m trying to mimic the functionality of a macro by building up an expression similar to what it takes as input, calling the same function the macro calls, and then calling eval on the resulting expression. I can’t quite seem to make this work, so was hoping for some advice on what to do differently.

We have the following macro in DiffEqBiological:

macro min_reaction_network(ex::Expr, p...)
    min_coordinate(:min_reaction_network, MacroTools.striplines(ex), p, :no___noise___scaling)
end

Typical usage would be

network = @min_reaction_network begin
  c1, X --> 2X
  c2, X --> 0
end c1 c2 

My understanding is that in the macro definition ex corresponds to the begin to end block expression, and p to a tuple of the symbols at the end (:c1,:c2,:c3).

I’m trying to create a package to read in a different network file format and output a min_reaction_network. My approach was to try to build up an equivalent expression to what gets passed into min_coordinate, and a similar parameter tuple, and then call eval on what min_coordinate returns. The issue is that I don’t seem to be able to quite figure out how to build this expression correctly.

For say the example above, can anyone show me how to build an equivalent expression to what the macro passes to min_coordinate so that I can call it directly?

Thanks!

0 Likes

#2

I’m not sure I understand exactly what you want to do, but maybe this approach would suit you.

First, modify your macro so that it does not call min_coordinate, but only build its argument tuple:

macro min_reaction_network_2(ex::Expr, p...)
    (:min_reaction_network, MacroTools.striplines(ex), p, :no___noise___scaling)
end

then, expand the macro call (a direct evaluation might work too):

julia> @macroexpand @min_reaction_network_2 begin
         c1, X --> 2X
         c2, X --> 0
       end c1 c2

(:min_reaction_network, quote
    (c1, $(Expr(:-->, :X, :(2X))))
    (c2, $(Expr(:-->, :X, 0)))
end, (:c1, :c2), :no___noise___scaling)

this tuple should be what the macro passes to min_coordinates

0 Likes

#3

Thanks for the help. I’d like to be able to do something like:

myexpr = min_coordinate(...)
eval(myexpr)

which I was hoping would mimic the functionality of the macro.

Using your tuple I still don’t seem to be able to do this:

mcargs = (:min_reaction_network, quote
    (c1, $(Expr(:-->, :X, :(2X))))
    (c2, $(Expr(:-->, :X, 0)))
end, (:c1, :c2), :no___noise___scaling)
myexpr = DiffEqBiological.min_coordinate(mcargs[1],MacroTools.striplines(mcargs[2]),mcargs[3],mcargs[4])

returns an expression, but calling eval(myexpr) gives an invalid syntax error. Is this somehow related to the expression not being evaluated within the DiffEqBiological package?

0 Likes

#4

At a higher level, I am trying to avoid building a giant string that contains the full macro command and just calling Meta.parse and then eval on it. i.e. avoid:

str = "@min_reaction_network begin
  c1, X --> 2X
  c2, X --> 0
end c1 c2 "
network = eval(Meta.parse(str))

That approach works fine, but I was hoping to just go directly from expressions to getting the network out.

If there is a different approach that makes more sense to you please do let me know.

0 Likes