Running macros with expression as an argument

I’ve got errors on “local first_parameter”. If I try to mess something without any sense, sometimes I get error at “local arg1” line.

macro howToRunThis(fun::Expr)
  local first_parameter = fun.args[1]
  local name = first_parameter.args[1].args[1]
  local arg1 = first_parameter.args[1].args[2]
  local arg1_name = arg1.args[1]
  local arg1_type = arg1.args[2]
  local other_data=first_parameter.args[2]
  local second_parameter = fun.args[2]
  ######################
  # Some actions
  ######################
end

# How to run this?

# My try
arg1 = :(
  arg1_name,
  arg1_type
)
object_data = :(
  name,
  $arg1
)

first_parameter = :(
  $object_data,
  other_data
)

fun = :(
  $first_parameter
  ,
  second_parameter
)

@howToRunThis(fun)
# don't work

Outside of the macro I can decompose this fun Expression like in this macro, but I can’t do this in macro.

Example Error:

LoadError: MethodError: no method matching @howToRunThis(::LineNumberNode, ::Module, ::Symbol)
Closest candidates are:
  @howToRunThis(::LineNumberNode, ::Module, ::Expr) at ...

Macros operate on code itself, not on values. The code being passed to the macro is just the symbol :fun. The fact that it has a value of an expression in some scope is irrelevant within the body of the macro.

Depending on what you’re actually trying to accomplish, the answer may be as simple as changing your macro into a function. There’s no reason you can’t pass expressions into functions in Julia, although it’s somewhat uncommon. Can you explain more about what your actual goal is?

2 Likes

The goal is to understand meta-programming and macros. And to knew why I get error with this .args statements. From what I know macro is just replacing some names into given data, before compiling the code. A macro is a way of generating a new output expression, given an unevaluated input expression.
And from what I can see, I can’t pass some types of variables into macro.
For example if I do something like that:

p=:([a+c,b-g],[b-g])
@howToRunThis(:([a+c,b-g],[b-g]))
@howToRunThis(p)

In @howToRunThis(:([a+c,b-g],[b-g])) this fails at the last line of this macro.
In @howToRunThis(p) this fails at the first line of this macro.
When I tried to directly pass that data that I posted in first post, I couldn’t do this because it interpreted “:” sign as Symbol and passed it into args array.
I also understand more that I don’t pass exact argument. On the example:

macro addSomething(x,y)
  return :( x+y )
end

x=2
y=7
@addSomething(dadsfadfss,sdaff)

And it works, all I have to do to run this macro is to declare x and y variable.
However if I change this to:

macro addSomething(x,y)
  return x+y
end

x=2
y=7

@addSomething(x,y)

That’s not working, because macros treat variables as a Symbol
So how to pass arguments into macros?
ERROR: LoadError: MethodError: no method matching +(::Symbol, ::Symbol),
so I have to pass arguments directly.

That’s the fundamental point. You don’t, at least not in the way you’re talking about. When you call @addSomething(x, y), the only thing the macro receives are the names :x and :y. Whether or not they happen to correspond to values in the current scope is irrelevant, and the body of the macro does not access those values. If you want to do something with the values of x, and y, then you need a function, not a macro.

1 Like

The way I understand macro is something that transforms Julia code into Julia code. One nice example is what I am using in TextUserInterfaces.jl.

I have signals to be emitted in the structures. Thus, if I want a structure to emit a signal called return_pressed, then I need to write:

obj.on_return_pressed(obj.vargs_on_return_pressed...)

which is long and bad to read. Then, I created a macro that transforms:

@emit return_pressed obj

into the previous code.

My biggest mistake when I was learning macros, was to think that they are like the C macros. They can mimic the behavior, but the macros is Julia are much, much more powerful.

1 Like