Fresh variables

Once macros generate code, they are parsed with a regular Crystal parser where local variables in the context of the macro invocations are assumed to be defined.

This is better understood with an example:

  1. macro update_x
  2. x = 1
  3. end
  4. x = 0
  5. update_x
  6. x #=> 1

This can sometimes be useful to avoid repetitive code by deliberately reading/writing local variables, but can also overwrite local variables by mistake. To avoid this, fresh variables can be declared with %name:

  1. macro dont_update_x
  2. %x = 1
  3. puts %x
  4. end
  5. x = 0
  6. dont_update_x # outputs 1
  7. x #=> 0

Using %x in the above example, we declare a variable whose name is guaranteed not to conflict with local variables in the current scope.

Additionally, fresh variables with respect to some other AST node can be declared with %var{key1, key2, ..., keyN}. For example:

  1. macro fresh_vars_sample(*names)
  2. # First declare vars
  3. {% for name, index in names %}
  4. print "Declaring: ", "%name{index}", '\n'
  5. %name{index} = {{index}}
  6. {% end %}
  7. # Then print them
  8. {% for name, index in names %}
  9. print "%name{index}: ", %name{index}, '\n'
  10. {% end %}
  11. end
  12. fresh_vars_sample a, b, c
  13. # Sample output:
  14. # Declaring: __temp_255
  15. # Declaring: __temp_256
  16. # Declaring: __temp_257
  17. # __temp_255: 0
  18. # __temp_256: 1
  19. # __temp_257: 2

In the above example, three indexed variables are declared, assigned values, and then printed, displaying their corresponding indices.