/
06.05.2025 at 05:16 pm
Cuttings

Nim / Exercise - `swap(a, b)` macro

Swap the values of two variables.

Swap two variables using macros:

var a = 1
var b = 2
swap(a, b)
echo a, b  # Output: 2 1

Hint:

  • Use a temp variable inside the quoted code.

  • Use backticks to plug in the identifiers.

  1. When you're using quote do:, you're not printing code as a string - you're generating an AST (code structure).

  2. So you don't want to convert to strings using astToStr. Instead, inject the expression itself directly, as an AST node.

  3. Notice the difference:

    • Outside ofquote do, expr (even with backticks) gets a NimNode.

    • Inside of quote_do, expr_a injects the runtime value of the variable/expression.

    • Two different results below:

        echo typeof(expr)
        echo typeof(`expr`) # Same as above
        quote do:
          echo typeof(`expr`)
      
  4. Why typeof(astToStr(...)) doesn't work:

    • astToStr is a macro-time function - it only works during macro expansion, not at runtime.

    • Inside of quote_do, astToStr() turns the compile-time AST node into a string representation at compile time.

    • When you call typeof(astToStr(...)), it gets evaluated after quote do is compiled.

  5. When you're inside quote do, the backticks surrounding an identifier (e.g. expr_a) mean:

    > "Inject the code represented by the NimNode expr_a into this spot."
    
  6. But with something like echo typeof(a)it might output int, because:

    • By the time this code is actually run, you've already generated and compiled it.

    • At that point, you're not printing the type of the AST - you're printing the runtime type of the variable a (which is int).

  7. Your insight: with macro, code (AST/NimNode) and runtime values (typed values like int, string, etc.) differ.

    • Nim doesn't evaluate expr_a at macro time - it just splices it into the generated code, like a template.

    • After macro expansion, it's just normal Nim code (using that identifier or expression).

    • Only then is it typed (like int) - hence the typeof(...) giving int (because now it's just typeof(a)).

  8. How do you see Nim's generated code during the compile stage, before the runtime stage then? A few ways in Nim to see the AST dump at compile time:

    1. repr(...) - This prints the raw AST of the generated code:

      let body = quote do:
        ...
      echo repr(body)
      
    2. macro.dumpTree / dumpAst

    3. expandMacros

Code (SolutiON)
     import macros

macro swap_variables(expr_a: untyped, expr_b: untyped): untyped = 
  echo typeof(`expr_a`)
  echo typeof(expr_a) # Same as above
  echo "as string: ", astToStr(expr_a) 

  result = quote do: # Simulate the dumpTree below
    let tmp = `expr_a`
    `expr_a` = `expr_b`
    `expr_b` = tmp
    echo typeof(`expr_a`), " ", typeof(astToStr(`expr_a`))
  # echo treeRepr(result)
  # echo repr(result)


var a = 1
var b = 2
echo a, " ", b

# dumpTree:
# let tmp = a; 
# a = b
# b = tmp

swap_variables(a, b)
echo a, " ", b

                    
Source: Nim Macros - Beginner & Intermediate Exercises
Filed under:
#
#
Words: 352 words approx.
Time to read: 1.41 mins (at 250 wpm)
Keywords:
, , , , , , , , ,

Potentially related:
  1. A Basic Nim Workflow To Metaprogramming/Macros
  2. Nim Macros - Beginner & Intermediate Exercises
  3. Nim / Exercise - `timed(expr)` macro
  4. Nim / Exercise - Make a `logvars(a, b, c)` macro
  5. Nim / Exercise - Make a double(x) macro
  6. Search Notes Fast: With Taskwarrior, jq & ripgrep

Latest Comments

© Wan Zafran. See disclaimer.