Let's say a Crystal project is using different shards. And each shard wants to do cleanup at the end of compilation of the overall project. Is it possible using Macros?
Something like this for example:
{% at_end %}
{% system("rm 'tmp files'") %}
{% end %}
If you need to share data between multiple macros, you can use Hash
or Array
, and access them through a Constant
which associated AST node is accessible from macros.
Here is an example demonstrating this (live at https://carc.in/#/r/372k):
STORAGE = [] of _
macro add(node)
{% STORAGE << node %}
end
macro list
{% for elem in STORAGE %}
{% puts "Elem: #{elem}" %}
{% end %}
end
add 1
add 2
add "hello"
add :world
add({a: 1, b: 2})
add 3 + 4
add a_call(arg1, 2)
list
The important parts here is:
STORAGE = [] of _
Here I'm declaring an array of a certain type (which is non relevant in this case, as the array is only accessed through macros, you can't use this type (_
) in normal code). The macro system only needs to know that it is an array.
macro add(node)
{% STORAGE << node %}
end
Then I create a macro that will be able to mutate the array AST node (which is type ArrayLiteral in the macro system).
Note that it allows you to store any kind of AST node, from simple nodes like NumberLiteral
or SymbolLiteral
, to complex nodes like a Call
, a Def
or even a ClassDef
.
macro list
{% for elem in STORAGE %}
{% puts "Elem: #{elem}" %}
{% end %}
end
And finally I create a macro that will simply print (at compile time) the content of the STORAGE
array.
The same can be done with a HashLiteral
:
STORAGE = {} of _ => _
And in the same way as explained above, you can use any kind of AST nodes as the key or the value.
Note that the Constants
you use in the macro system can also be seen by normal code, and it's possible to have name clash (what if the user wants to use a constant STORAGE
too for his own code?).
To minimize this possibility (if this is not wanted), I suggest you to put your constants in a special module like MyShard::MacroStorage::SomeInterestingNodes
or use some complicated naming pattern like M____ACRO_Storage____01234
(<-- this is unlikely to be used by the user ;) )