Manipulating expressions with macros

Hi. I’m having trouble defining a macro which manipulates expressions containing $’s. It’s best illustrated by example:

Is it possible to define a macro @newrule such that

for op ∈ (+, -)
	@newrule $op(a, 0) => a
end

expands to a function call newrule(::Expr)

for op ∈ (+, -)
	newrule(:($op(a, 0) => a))
end

where op is in the local scope? So at run-time this is should be equivalent to:

newrule(:(a + 0 => a))
newrule(:(a - 0 => a))

It seems like this should certainly be possible, since the macro performs a purely syntactical transformation at parse-time—but here’s my trouble:

If we do

using MacroTools: postwalk

macro newrule(expr)
	escaped = postwalk(expr) do node
		if node isa Expr && node.head == :$
			esc(node.args[1])
		else
			node
		end
	end
	escaped
end

then the macro call @newrule $op(a, 0) => a will return and evaluate the expression :( a + 0 => a ), which errors since a is undefined. But we want the macro to return an expression which evaluates to the expression, so that the run-time value is the expression :( a + 0 => a ). If I do

		...
	end
	QuoteNode(escaped)
end

then the macro call results in a run-time expression, but the interpolation is quoted also, so that the run-time value is :(($(Expr(:escape, :op)))(a, 0)).

Thanks for your help!

I did it! The macro I sought was:

macro newrule(expr)
	quote $(Expr(:quote, expr)) end
end

Now we have, as desired:

julia> [@newrule $op(a, 0) => a for op ∈ (+, -)]
2-element Array{Expr,1}:
 :((+)(a, 0) => a)
 :((-)(a, 0) => a)

QuoteNode(expr) was a red herring. I thought this played the role of Expr(:quote, expr), but it does not, and I wasn’t exactly sure what QuoteNode was meant for until I read this issue.

1 Like