Modifying Julia code using macros to substitute variables

I’m trying to create a Julia macro to substitute specific variables in a Julia program with new ones. I have a working example, but I’m facing a challenge when I need to reassign variables within nested functions.

Here’s a simplified version of my Input code:

module m1

using sub_vars

function f1()
  @declare_newvar v1 v_1 1
  v2 = 2

  function f2()
    v3 = v1 + v2
    @declare_newvar v1 v^1 4
    v4 = v1 + v3
    v1 = v1 + v4
  end

  f2()
end

f1()
end

where

module sub_vars

?

macro declare_newvar(orig_var :: Symbol, new_var :: Symbol, val :: Any)
  ?
end 

end 

The @declare_newvar macro should replace v1 with v_1 and v^1 in the code as specified.

I would like to get the desired output:

module m1

using sub_vars

function f1()
  v_1 = 1
  v2 = 2

  function f2()
    v3 = v_1 + v2
    v^1 = 4
    v4 = v^1 + v3
    v^1 = v^1 + v4
  end

  f2()
end

f1()
end

Approaches to tackle this issue:

  1. Transform the code into an Expr variable and employ a relevant package to identify variable scopes. Subsequently, navigate through the Abstract Syntax Tree (AST) and modify the Expr variable accordingly.
  2. Create and maintain a Symbol Table within the sub_vars module. Utilize macros to capture essential information during passes and employ this data to facilitate variable substitution.

Are there any alternative methods to address this problem?

Can you give us more context? Why not just use function arguments to substitute in values for variables?

1 Like

Functions are not suitable for altering variable names within an Expr. Therefore, I opted for a macro to achieve this functionality.

It’s definitely possible to traverse an expression and replace a symbol with another, though you would definitely need a different macro call on the entire f1 function expression that treats @declare_newvar as tags that change the symbols’ substitutions. But it’s not clear why you would do this because the expanded code is not visible in the source code, so it’s harder to tell variables apart this way than if you replaced the text in the first place. Probably safest to find-replace in an editor one by one so you don’t accidentally replace the wrong v1 text.

Also, v^1 is not a valid variable name, it does ^(v, 1). Unicode however does support , you just type v\^1<TAB> in the REPL or whatever editor supports this tab completion.

1 Like

I prefer using this approach because the variable names I need to use are lengthy and intricate, which can make the code less visually appealing. Shortening the variable names might sacrifice clarity and meaning. By adopting this approach, I discover it more user-friendly for code composition, and I retain the ability to retrieve the original code as required later on.

I see, so you had made a MWE. Regardless of how long the variable names, it’s definitely a bad idea to have the same text represent different variables depending on where @declare_newvar tags are instead of something built in the language like local scopes. You are right to stick to shorter variable names that are easier to read and write, but you can put whatever longer intricate descriptions in comments next to the variable e.g. v_1 = 3 # voltage at breaker in basement level 1 and v¹ = 4 # voltage at breaker at ground level, it’s just as clear. Too much metaprogramming can obscure what an algorithm does, so macros should only be used for more drastic algorithmic changes, not mere variable renaming.

1 Like