Jump to page: 1 2
Thread overview
Will macros just be syntactic sugar?
Apr 25, 2007
Don Clugston
Apr 25, 2007
Davidl
Apr 25, 2007
Davidl
Apr 25, 2007
Davidl
Apr 25, 2007
Don Clugston
Apr 25, 2007
Davidl
Apr 25, 2007
Don Clugston
Apr 25, 2007
Davidl
Re: Will macros just be syntactic sugar? [EXAMPLE]
Apr 25, 2007
Don Clugston
Apr 25, 2007
Davidl
Apr 25, 2007
Don Clugston
Apr 25, 2007
Manfred Nowak
Apr 25, 2007
Charlie
Apr 25, 2007
Davidl
Apr 25, 2007
Davidl
April 25, 2007
A couple of weeks ago, I posted an implementation of BLAS1-style vector expressions, that used tuples and expression templates to generate near-optimal asm code in many circumstances. Unfortunately, when I've looked at the code that is generated, there's a *hideous* amount of stack shuffling -- it is too difficult for the compiler to optimise all the tuple operations away.
So I came up with this alternative, which gives _many_ more optimisation opportunities, and doesn't require you to wrap arrays of built-in types.

float [] vec1 = [43, 5445, 547, 465];
auto vec2 = [1.24, 765, 6756, 23.143];

mixin(vectorize("vec2+=vec1*3.3254"));

This actually works. There are some awesome side effects -- eg, any error messages in the library code appear on the line of user code. You can determine which of the variables are compile-time constants (and optimise the generated code based on the value of those constants). You can get the compiler to do _most_ of the heavy lifting (eg, convert floating point strings <--> const real).

As far as I can tell, *all* of the proposed 'macro' functionality can be done already using mixins and CTFE. It looks as though the power of macros will be a subset of the power of mixins. This means that macros can be explored quite thoroughly *before* deciding on the cleanest syntax for them.

Today:
---------
char [] magic(char [] args)
{
   return "stuff-to-mix-in";
}

:
  mixin(magic("a+=b*c"));
:
-----------
With macros:

macro magic(args)
{
  stuff-to-mix-in
}

:
magic(a+=b*c);
:
------------
April 25, 2007
numerle's macro is somewhat worth consideration
an improved syntax should be something like:
macro macroname(arg0,arg1,arg2,arg3)
syntax realname(arg0)[arg1] := arg2 +arg3
{
   arg0[arg1] = arg2+arg3;
}

and calling style would be realname(a)[i] := j + k; // which would call macro macroname(a,i,j,k)

>
> A couple of weeks ago, I posted an implementation of BLAS1-style vector expressions, that used tuples and expression templates to generate near-optimal asm code in many circumstances. Unfortunately, when I've looked at the code that is generated, there's a *hideous* amount of stack shuffling -- it is too difficult for the compiler to optimise all the tuple operations away.
> So I came up with this alternative, which gives _many_ more optimisation opportunities, and doesn't require you to wrap arrays of built-in types.
>
> float [] vec1 = [43, 5445, 547, 465];
> auto vec2 = [1.24, 765, 6756, 23.143];
>
> mixin(vectorize("vec2+=vec1*3.3254"));
>
> This actually works. There are some awesome side effects -- eg, any error messages in the library code appear on the line of user code. You can determine which of the variables are compile-time constants (and optimise the generated code based on the value of those constants). You can get the compiler to do _most_ of the heavy lifting (eg, convert floating point strings <--> const real).
>
> As far as I can tell, *all* of the proposed 'macro' functionality can be done already using mixins and CTFE. It looks as though the power of macros will be a subset of the power of mixins. This means that macros can be explored quite thoroughly *before* deciding on the cleanest syntax for them.
>
> Today:
> ---------
> char [] magic(char [] args)
> {
>     return "stuff-to-mix-in";
> }
>
> :
>    mixin(magic("a+=b*c"));
> :
> -----------
> With macros:
>
> macro magic(args)
> {
>    stuff-to-mix-in
> }
>
> :
> magic(a+=b*c);
> :
> ------------

April 25, 2007
macro (Token... tokens)
{
     static if (tokens[0].ID != Token.LParen)
	pragma(error, `( expected`);
     else static if (tokens[1].ID != Token.Identifier)
        pragma(error, `identifer expected`);
     else static if (tokens[2].ID != Token.RParen)
        pragma(error, ) expected`);
     else static if (tokens[3].toChars != `:=`)
        pragma(error, `:= expected`);
     static if (tokens[4].ID != Token.LBracket)
	pragma(error, `( expected`);
     else static if (tokens[5].ID != Token.Identifier)
        pragma(error, `identifer expected`);
     else static if (tokens[6].ID != Token.RBracket)
        pragma(error, ) expected`);
     else static if (tokens[7].ID != Token.Identifier)
        pragma(error, `identifer expected`);
     else static if (tokens[8].ID != Token.plus)
        pragma(error, `+ operator expected`);
     else static if (tokens[9].ID != Token.Identifier)
        pragma(error, `identifer expected`);
     alias token[1] arg0;
     alias token[5] arg1;
     alias token[7] arg2;
     alias token[9] arg3;
     arg0[arg1] = arg2 + arg3;
}

seems the above is more valuable.

> numerle's macro is somewhat worth consideration
> an improved syntax should be something like:
> macro macroname(arg0,arg1,arg2,arg3)
> syntax realname(arg0)[arg1] := arg2 +arg3
> {
>     arg0[arg1] = arg2+arg3;
> }
>
> and calling style would be realname(a)[i] := j + k; // which would call macro macroname(a,i,j,k)
>
>>
>> A couple of weeks ago, I posted an implementation of BLAS1-style vector expressions, that used tuples and expression templates to generate near-optimal asm code in many circumstances. Unfortunately, when I've looked at the code that is generated, there's a *hideous* amount of stack shuffling -- it is too difficult for the compiler to optimise all the tuple operations away.
>> So I came up with this alternative, which gives _many_ more optimisation opportunities, and doesn't require you to wrap arrays of built-in types.
>>
>> float [] vec1 = [43, 5445, 547, 465];
>> auto vec2 = [1.24, 765, 6756, 23.143];
>>
>> mixin(vectorize("vec2+=vec1*3.3254"));
>>
>> This actually works. There are some awesome side effects -- eg, any error messages in the library code appear on the line of user code. You can determine which of the variables are compile-time constants (and optimise the generated code based on the value of those constants). You can get the compiler to do _most_ of the heavy lifting (eg, convert floating point strings <--> const real).
>>
>> As far as I can tell, *all* of the proposed 'macro' functionality can be done already using mixins and CTFE. It looks as though the power of macros will be a subset of the power of mixins. This means that macros can be explored quite thoroughly *before* deciding on the cleanest syntax for them.
>>
>> Today:
>> ---------
>> char [] magic(char [] args)
>> {
>>     return "stuff-to-mix-in";
>> }
>>
>> :
>>    mixin(magic("a+=b*c"));
>> :
>> -----------
>> With macros:
>>
>> macro magic(args)
>> {
>>    stuff-to-mix-in
>> }
>>
>> :
>> magic(a+=b*c);
>> :
>> ------------
>

April 25, 2007
macro macroname(Token... tokens)
{
     static if (tokens[0].ID != Token.LParen)
	pragma(error, `( expected`);
     else static if (tokens[1].ID != Token.Identifier)
        pragma(error, `identifer expected`);
     else static if (tokens[2].ID != Token.RParen)
        pragma(error, ) expected`);
     else static if (tokens[3].toChars != `:=`)
        pragma(error, `:= expected`);
     static if (tokens[4].ID != Token.LBracket)
	pragma(error, `( expected`);
     else static if (tokens[5].ID != Token.Identifier)
        pragma(error, `identifer expected`);
     else static if (tokens[6].ID != Token.RBracket)
        pragma(error, ) expected`);
     else static if (tokens[7].ID != Token.Identifier)
        pragma(error, `identifer expected`);
     else static if (tokens[8].ID != Token.plus)
        pragma(error, `+ operator expected`);
     else static if (tokens[9].ID != Token.Identifier)
        pragma(error, `identifer expected`);
     alias token[1] arg0;
     alias token[5] arg1;
     alias token[7] arg2;
     alias token[9] arg3;
     arg0[arg1] = arg2 + arg3;
}

caller could call the macro with :

macroname(arg0)[arg1] := arg2 + arg3;

macroname here is used for simplify the parsing.

> macro (Token... tokens)
> {
>      static if (tokens[0].ID != Token.LParen)
> 	pragma(error, `( expected`);
>      else static if (tokens[1].ID != Token.Identifier)
>         pragma(error, `identifer expected`);
>      else static if (tokens[2].ID != Token.RParen)
>         pragma(error, ) expected`);
>      else static if (tokens[3].toChars != `:=`)
>         pragma(error, `:= expected`);
>      static if (tokens[4].ID != Token.LBracket)
> 	pragma(error, `( expected`);
>      else static if (tokens[5].ID != Token.Identifier)
>         pragma(error, `identifer expected`);
>      else static if (tokens[6].ID != Token.RBracket)
>         pragma(error, ) expected`);
>      else static if (tokens[7].ID != Token.Identifier)
>         pragma(error, `identifer expected`);
>      else static if (tokens[8].ID != Token.plus)
>         pragma(error, `+ operator expected`);
>      else static if (tokens[9].ID != Token.Identifier)
>         pragma(error, `identifer expected`);
>      alias token[1] arg0;
>      alias token[5] arg1;
>      alias token[7] arg2;
>      alias token[9] arg3;
>      arg0[arg1] = arg2 + arg3;
> }
>  seems the above is more valuable.

April 25, 2007
sorry for typos in the error message :o
should be
       static if (tokens[4].ID != Token.LBracket)
 	pragma(error, `[ expected`);
       else static if (tokens[5].ID != Token.Identifier)
          pragma(error, `identifer expected`);
       else static if (tokens[6].ID != Token.RBracket)
          pragma(error, `] expected`);

> macro (Token... tokens)
> {
>       static if (tokens[0].ID != Token.LParen)
> 	pragma(error, `( expected`);
>       else static if (tokens[1].ID != Token.Identifier)
>          pragma(error, `identifer expected`);
>       else static if (tokens[2].ID != Token.RParen)
>          pragma(error, ) expected`);
>       else static if (tokens[3].toChars != `:=`)
>          pragma(error, `:= expected`);
>       static if (tokens[4].ID != Token.LBracket)
> 	pragma(error, `( expected`);
>       else static if (tokens[5].ID != Token.Identifier)
>          pragma(error, `identifer expected`);
>       else static if (tokens[6].ID != Token.RBracket)
>          pragma(error, ) expected`);
>       else static if (tokens[7].ID != Token.Identifier)
>          pragma(error, `identifer expected`);
>       else static if (tokens[8].ID != Token.plus)
>          pragma(error, `+ operator expected`);
>       else static if (tokens[9].ID != Token.Identifier)
>          pragma(error, `identifer expected`);
>       alias token[1] arg0;
>       alias token[5] arg1;
>       alias token[7] arg2;
>       alias token[9] arg3;
>       arg0[arg1] = arg2 + arg3;
> }
>
> seems the above is more valuable.
>
>> numerle's macro is somewhat worth consideration
>> an improved syntax should be something like:
>> macro macroname(arg0,arg1,arg2,arg3)
>> syntax realname(arg0)[arg1] := arg2 +arg3
>> {
>>     arg0[arg1] = arg2+arg3;
>> }
>>
>> and calling style would be realname(a)[i] := j + k; // which would call macro macroname(a,i,j,k)
>>
>>>
>>> A couple of weeks ago, I posted an implementation of BLAS1-style vector expressions, that used tuples and expression templates to generate near-optimal asm code in many circumstances. Unfortunately, when I've looked at the code that is generated, there's a *hideous* amount of stack shuffling -- it is too difficult for the compiler to optimise all the tuple operations away.
>>> So I came up with this alternative, which gives _many_ more optimisation opportunities, and doesn't require you to wrap arrays of built-in types.
>>>
>>> float [] vec1 = [43, 5445, 547, 465];
>>> auto vec2 = [1.24, 765, 6756, 23.143];
>>>
>>> mixin(vectorize("vec2+=vec1*3.3254"));
>>>
>>> This actually works. There are some awesome side effects -- eg, any error messages in the library code appear on the line of user code. You can determine which of the variables are compile-time constants (and optimise the generated code based on the value of those constants). You can get the compiler to do _most_ of the heavy lifting (eg, convert floating point strings <--> const real).
>>>
>>> As far as I can tell, *all* of the proposed 'macro' functionality can be done already using mixins and CTFE. It looks as though the power of macros will be a subset of the power of mixins. This means that macros can be explored quite thoroughly *before* deciding on the cleanest syntax for them.
>>>
>>> Today:
>>> ---------
>>> char [] magic(char [] args)
>>> {
>>>     return "stuff-to-mix-in";
>>> }
>>>
>>> :
>>>    mixin(magic("a+=b*c"));
>>> :
>>> -----------
>>> With macros:
>>>
>>> macro magic(args)
>>> {
>>>    stuff-to-mix-in
>>> }
>>>
>>> :
>>> magic(a+=b*c);
>>> :
>>> ------------
>>
>

April 25, 2007
oh noes, why posted so many times.... coz they are too important? :p
forgive my bad network & slow machine , i think the extra posts are
caused by them

> numerle's macro is somewhat worth consideration
> an improved syntax should be something like:
> macro macroname(arg0,arg1,arg2,arg3)
> syntax realname(arg0)[arg1] := arg2 +arg3
> {
>     arg0[arg1] = arg2+arg3;
> }
>
> and calling style would be realname(a)[i] := j + k; // which would call macro macroname(a,i,j,k)
>
>>
>> A couple of weeks ago, I posted an implementation of BLAS1-style vector expressions, that used tuples and expression templates to generate near-optimal asm code in many circumstances. Unfortunately, when I've looked at the code that is generated, there's a *hideous* amount of stack shuffling -- it is too difficult for the compiler to optimise all the tuple operations away.
>> So I came up with this alternative, which gives _many_ more optimisation opportunities, and doesn't require you to wrap arrays of built-in types.
>>
>> float [] vec1 = [43, 5445, 547, 465];
>> auto vec2 = [1.24, 765, 6756, 23.143];
>>
>> mixin(vectorize("vec2+=vec1*3.3254"));
>>
>> This actually works. There are some awesome side effects -- eg, any error messages in the library code appear on the line of user code. You can determine which of the variables are compile-time constants (and optimise the generated code based on the value of those constants). You can get the compiler to do _most_ of the heavy lifting (eg, convert floating point strings <--> const real).
>>
>> As far as I can tell, *all* of the proposed 'macro' functionality can be done already using mixins and CTFE. It looks as though the power of macros will be a subset of the power of mixins. This means that macros can be explored quite thoroughly *before* deciding on the cleanest syntax for them.
>>
>> Today:
>> ---------
>> char [] magic(char [] args)
>> {
>>     return "stuff-to-mix-in";
>> }
>>
>> :
>>    mixin(magic("a+=b*c"));
>> :
>> -----------
>> With macros:
>>
>> macro magic(args)
>> {
>>    stuff-to-mix-in
>> }
>>
>> :
>> magic(a+=b*c);
>> :
>> ------------
>

April 25, 2007
Davidl wrote:
> macro macroname(Token... tokens)
> {
>      static if (tokens[0].ID != Token.LParen)
>     pragma(error, `( expected`);
:
>      alias token[1] arg0;
>      alias token[5] arg1;
>      alias token[7] arg2;
>      alias token[9] arg3;
>      arg0[arg1] = arg2 + arg3;
> }
> 
> caller could call the macro with :
> 
> macroname(arg0)[arg1] := arg2 + arg3;

I don't think that's much nicer than
mixin(macroname("arg0[arg1]:= arg2+ arg3"));

which works in DMD 1.012. The static if/Token properties can be done via a CTFE library. If structs were allowed in CTFE functions, the code wouldn't even be too ugly.
April 25, 2007
how to emit a error message by CTFE if arg0 is not an
identifier? and compiling that CTFE library would be pretty
much slower compared to my version.
And the advantage of macro is the frontend parser can help
IDE for auto-completion.

> Davidl wrote:
>> macro macroname(Token... tokens)
>> {
>>      static if (tokens[0].ID != Token.LParen)
>>     pragma(error, `( expected`);
> :
>>      alias token[1] arg0;
>>      alias token[5] arg1;
>>      alias token[7] arg2;
>>      alias token[9] arg3;
>>      arg0[arg1] = arg2 + arg3;
>> }
>>  caller could call the macro with :
>>  macroname(arg0)[arg1] := arg2 + arg3;
>
> I don't think that's much nicer than
> mixin(macroname("arg0[arg1]:= arg2+ arg3"));
>
> which works in DMD 1.012. The static if/Token properties can be done via a CTFE library. If structs were allowed in CTFE functions, the code wouldn't even be too ugly.

April 25, 2007
Davidl wrote:
> how to emit a error message by CTFE if arg0 is not an
> identifier?

assert(isIdentifier(arg[0]), "Identifier expected");

The error message even indicates the line of user's code where the error occurs (doesn't refer to the line in the macro).

and compiling that CTFE library would be pretty
> much slower compared to my version.
Yes, but the speed comes from Token; otherwise, they're pretty much the same.
> And the advantage of macro is the frontend parser can help
> IDE for auto-completion.

Naturally.
April 25, 2007
heh, mixin an assert is bad, compile time is even longer :p
Though you are kinda sticking with CTFE & mixin , I'm pretty
sure you would vote my syntax of macro for macro, right? :)

> Davidl wrote:
>> how to emit a error message by CTFE if arg0 is not an
>> identifier?
>
> assert(isIdentifier(arg[0]), "Identifier expected");
>
> The error message even indicates the line of user's code where the error occurs (doesn't refer to the line in the macro).
>
> and compiling that CTFE library would be pretty
>> much slower compared to my version.
> Yes, but the speed comes from Token; otherwise, they're pretty much the same.
>> And the advantage of macro is the frontend parser can help
>> IDE for auto-completion.
>
> Naturally.

« First   ‹ Prev
1 2