would be nice if macro can have shared variables, and callback after program readed and macro expanded (macro class_finished or macro program_finished).
this allow things like https://github.com/crystal-lang/crystal/issues/2987 or nice dsl, api generators, based on user usage:
shard
class Api
macro mapping(tuple)
{% $data ||= [] of NamedTupleLiteral %}
{% $data << data %}
end
mapping({a : Int32, b : String})
end
macro program_finished
class Api::Data
JSON.mapping({
{% for data in $data %}
{{data}}
{% end %}
)}
end
end
user program
class UserClass < Api
mapping({c : Float64})
end
A long time ago I wasn't sure about this, having stuff change at compile time. However, Elixir does this exact same thing. Well, it's a bit different, they use attributes which are available at compile time, but one can override them and use their previous values to create new ones (I guess mostly because everything's immutable). And then they have __before_compile_.
In fact, in Crystal you can already have shared state. We are just missing the macro at_exit or a better name. For example, this works:
require "json"
class Foo
MAPPING = {} of Nil => Nil
macro mapping(**values)
{% for key, value in values %}
{% MAPPING[key] = value %}
{% end %}
end
macro close_mapping
JSON.mapping({{MAPPING}})
end
mapping x: Int32, y: Int32
end
class Foo
mapping z: Int32?
end
class Foo
close_mapping
end
p Foo.from_json(%<{"x": 1, "y": 2}>)
p Foo.from_json(%<{"x": 1, "y": 2, "z": 3}>)
We just need to define the name for this macro. It's a new macro hook, that will be executed, I think, right after all the top level declarations and methods are processed, right before instance vars types are checked, etc.
I think before_compile is a good name to go.
There was an issue that since the constant can't change the value it reference to, then for value types we need to boxed them. But even with that limitation I think it's good to add this.
_Also someone can hack around with run command and save state in temp files and read them before compiling_ 馃槰 _... someone will eventually do it ... :-P_
Actually, a name in the past tense would be optimal, so it's on par with included, inherited, extended. Ideas?
Maybe compiled ?
Edit: actually finished works well too!
Most helpful comment
A long time ago I wasn't sure about this, having stuff change at compile time. However, Elixir does this exact same thing. Well, it's a bit different, they use attributes which are available at compile time, but one can override them and use their previous values to create new ones (I guess mostly because everything's immutable). And then they have __before_compile_.
In fact, in Crystal you can already have shared state. We are just missing the
macro at_exitor a better name. For example, this works:play
We just need to define the name for this macro. It's a new macro hook, that will be executed, I think, right after all the top level declarations and methods are processed, right before instance vars types are checked, etc.