C# has a handy nameof expression which allows you to get the string representation of a variable, type or member so you don’t need to use string literals to refer to them. Like this:
// This is C#, not Crystal
readonly record struct Hiker(string Firstname, string Lastname, int Age);
var hiker = new Hiker("Arthur", "Dent", 42);
var x = nameof(hiker); // => "hiker"
var y = nameof(Hiker); // => "Hiker"
var z = nameof(Hiker.Age); // => "Age"
require "json"
macro nameof(name)
{{name.stringify}}
end
record AStruct, something : Array(String) do
include JSON::Serializable
end
def a_method
"hello"
end
hello = a_method
p nameof(a_method) # => "a_method"
p nameof(hello) # => "hello"
p nameof(AStruct) # => "AStruct"
The only problem with your macro is that properties off of Objects would return full path (e.g. nameof(hiker.Age) => “hiker.Age” in Crystal). I dont think its possible without doing some regex to remove everything before the last period (so you could handle something like nameof(hiker.family.father.age)).
require "json"
macro nameof(name)
{% if name.is_a?(Var) || name.is_a?(Path) %}
{{ name.stringify }}
{% else %}
{{ name.name.stringify }}
{% end %}
end
record AStruct, something : Array(String) do
include JSON::Serializable
end
def a_method
"hello"
end
hello = a_method
p nameof(a_method) # => "a_method"
p nameof(hello) # => "hello"
p nameof(AStruct) # => "AStruct"
p nameof(a_method.to_s) # => "to_s"
You can prefer the nodes name over stringifying the whole thing, which should handle the case you mentioned @mjblack
That works but I was going for a more simplified approach since it looks like C# doesnt care if the name is a valid variable or not. Please dont judge me harshly for my simplified version.
macro nameof(name)
{{name.id.stringify.split(".").last}}
end
Thank you, seems to work very well! The only change I needed to do was to split the string and get the last element (when there are multiple “levels” of references):