What are AST Macros?
David Bennett
davidbennett at bravevision.com
Fri Apr 13 11:54:12 UTC 2018
On Friday, 6 April 2018 at 20:33:10 UTC, Chris Katko wrote:
> Sorry if this is "re-opening" an old thread, but did anything
> come from this and DIP50? It seems like a really interesting
> concept and this thread was one of the first results for a
> Google search.
>
> Thanks.
Thanks for reminding me about this thread, I thought I would see
how close i could get to having this work now that I know D's
edges better:
On Sunday, 11 July 2010 at 13:29:36 UTC, Michel Fortin wrote:
>
> That said, I don't feel like I'm cheating when using string
> mixins. I find them a quite good substitute to AST macros. And
> perhaps string mixins are simpler too: you don't have to learn
> a new macro syntax, you just manipulate strings. Though I'm
> still waiting for the day we can use string mixins in
> expressions to do things like this:
>
> int num = 1;
> string result = substitute!"Number: $num";
> assert(result == "Number: 1");
So what i came up with was:
--- substituter.d
module substituter;
string substituteForMixin(string input){
input ~= ` `;
string output = ``;
size_t l= input.length-1;
for(size_t i=0; i<l; i++){
if(input[i]=='#' && input[i+1]=='{'){
output ~= (i==0) ? `to!string(` : `"~to!string(`;
i+=2;
while(input[i] != '}'){
output ~= input[i++];
}
output ~= (i==l-1) ? `)` : `)~"`;
}else{
if(i==0) output ~= `"`;
output ~= input[i];
if(i==l-1) output ~= `"`;
}
}
return output;
}
mixin template Substituter(){
string substitute(alias string ident)(){
import std.conv : to;
mixin(`return `~substituteForMixin(ident)~`;`);
};
}
@safe unittest{
mixin Substituter;
int number = 42;
assert(
substitute!("number is #{number}")
== "number is 42"
);
assert(
substitute!("#{number+1} is the result of number+1")
== "43 is the result of number+1"
);
assert(
substitute!("you get #{number*number} when you square
number")
== "you get 1764 when you square number"
);
}
---
I think thats as clean as I can make it currently. Also I'm using
#{} syntax to allow basic expressions and converting to!string on
everything.
Not sure if this syntax is documented but you can use enum like:
enum wrapmixin(alias string s) = {mixin(s)};
enum wrapmixin(alias string s){mixin(s)};
And the mixin() is run at the time the enum is called. The only
problem with this though is the lambda/function is defined in the
scope enum was defined not where the enum was called. So I think
its just sugar for:
template wrapmixin(alias string s){alias wrapmixin = {mixin(s);}}
template wrapmixin(alias string s){auto wrapmixin(){mixin(s);}}
To work around this I used a mixin template to work around the
scope issue.
Now to bring us back to 2018, I think that this enum version of
the code should inject the function/lambda into the scope of it's
caller. That would allow us to do a lot of cool things.
Also the other idea I had was to have mixin functions that only
take compiletime args (they are UFCS-able though, unlike
templates) and mixin themselves when called like:
---
mixin add1Xtimes(alias int a, alias int t){
uint i=t;
do{a++;}while(--i);
return a;
}
@safe unittest{
uint n=0;
n.add1Xtimes!(4).add1Xtimes!(6);
assert(n==10);
}
---
And that becomes:
---
@safe unittest{
uint n=0;
uint __m0_i=4;
do{n++;}while(--__m0_i);
uint __m1_i=6;
do{n++;}while(--__m1_i);
assert(n==10);
}
---
Notice that if the return is never used it's stripped out.
I haven't really given much thought to the implementation of this
though.
More information about the Digitalmars-d
mailing list