www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Will macros just be syntactic sugar?

reply Don Clugston <dac nospam.com.au> writes:
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);
:
------------
Apr 25 2007
parent reply Davidl <Davidl 126.com> writes:
numerle's macro is somewhat worth consideration
an improved syntax should be something like:
macro macroname(arg0,arg1,arg2,arg3)
syntax realname(arg0)[arg1] :=3D arg2 +arg3
{
    arg0[arg1] =3D arg2+arg3;
}

and calling style would be realname(a)[i] :=3D j + k; // which would cal=
l  =

macro macroname(a,i,j,k)

 A couple of weeks ago, I posted an implementation of BLAS1-style vecto=
r =
 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 al=
l =
 the tuple operations away.
 So I came up with this alternative, which gives _many_ more optimisati=
on =
 opportunities, and doesn't require you to wrap arrays of built-in type=
s.
 float [] vec1 =3D [43, 5445, 547, 465];
 auto vec2 =3D [1.24, 765, 6756, 23.143];

 mixin(vectorize("vec2+=3Dvec1*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. Yo=
u =
 can determine which of the variables are compile-time constants (and  =
 optimise the generated code based on the value of those constants). Yo=
u =
 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+=3Db*c"));
 :
 -----------
 With macros:

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

 :
 magic(a+=3Db*c);
 :
 ------------
Apr 25 2007
next sibling parent reply Davidl <Davidl 126.com> writes:
macro (Token... tokens)
{
      static if (tokens[0].ID !=3D Token.LParen)
	pragma(error, `( expected`);
      else static if (tokens[1].ID !=3D Token.Identifier)
         pragma(error, `identifer expected`);
      else static if (tokens[2].ID !=3D Token.RParen)
         pragma(error, ) expected`);
      else static if (tokens[3].toChars !=3D `:=3D`)
         pragma(error, `:=3D expected`);
      static if (tokens[4].ID !=3D Token.LBracket)
	pragma(error, `( expected`);
      else static if (tokens[5].ID !=3D Token.Identifier)
         pragma(error, `identifer expected`);
      else static if (tokens[6].ID !=3D Token.RBracket)
         pragma(error, ) expected`);
      else static if (tokens[7].ID !=3D Token.Identifier)
         pragma(error, `identifer expected`);
      else static if (tokens[8].ID !=3D Token.plus)
         pragma(error, `+ operator expected`);
      else static if (tokens[9].ID !=3D Token.Identifier)
         pragma(error, `identifer expected`);
      alias token[1] arg0;
      alias token[5] arg1;
      alias token[7] arg2;
      alias token[9] arg3;
      arg0[arg1] =3D 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] :=3D arg2 +arg3
 {
     arg0[arg1] =3D arg2+arg3;
 }

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

 A couple of weeks ago, I posted an implementation of BLAS1-style vect=
or =
 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 a=
ll =
 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 =3D [43, 5445, 547, 465];
 auto vec2 =3D [1.24, 765, 6756, 23.143];

 mixin(vectorize("vec2+=3Dvec1*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. Y=
ou =
 can determine which of the variables are compile-time constants (and =
=
 optimise the generated code based on the value of those constants). Y=
ou =
 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 o=
f =
 macros will be a subset of the power of mixins. This means that macro=
s =
 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+=3Db*c"));
 :
 -----------
 With macros:

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

 :
 magic(a+=3Db*c);
 :
 ------------
Apr 25 2007
next sibling parent reply Davidl <Davidl 126.com> writes:
macro macroname(Token... tokens)
{
      static if (tokens[0].ID !=3D Token.LParen)
	pragma(error, `( expected`);
      else static if (tokens[1].ID !=3D Token.Identifier)
         pragma(error, `identifer expected`);
      else static if (tokens[2].ID !=3D Token.RParen)
         pragma(error, ) expected`);
      else static if (tokens[3].toChars !=3D `:=3D`)
         pragma(error, `:=3D expected`);
      static if (tokens[4].ID !=3D Token.LBracket)
	pragma(error, `( expected`);
      else static if (tokens[5].ID !=3D Token.Identifier)
         pragma(error, `identifer expected`);
      else static if (tokens[6].ID !=3D Token.RBracket)
         pragma(error, ) expected`);
      else static if (tokens[7].ID !=3D Token.Identifier)
         pragma(error, `identifer expected`);
      else static if (tokens[8].ID !=3D Token.plus)
         pragma(error, `+ operator expected`);
      else static if (tokens[9].ID !=3D Token.Identifier)
         pragma(error, `identifer expected`);
      alias token[1] arg0;
      alias token[5] arg1;
      alias token[7] arg2;
      alias token[9] arg3;
      arg0[arg1] =3D arg2 + arg3;
}

caller could call the macro with :

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

macroname here is used for simplify the parsing.

 macro (Token... tokens)
 {
      static if (tokens[0].ID !=3D Token.LParen)
 	pragma(error, `( expected`);
      else static if (tokens[1].ID !=3D Token.Identifier)
         pragma(error, `identifer expected`);
      else static if (tokens[2].ID !=3D Token.RParen)
         pragma(error, ) expected`);
      else static if (tokens[3].toChars !=3D `:=3D`)
         pragma(error, `:=3D expected`);
      static if (tokens[4].ID !=3D Token.LBracket)
 	pragma(error, `( expected`);
      else static if (tokens[5].ID !=3D Token.Identifier)
         pragma(error, `identifer expected`);
      else static if (tokens[6].ID !=3D Token.RBracket)
         pragma(error, ) expected`);
      else static if (tokens[7].ID !=3D Token.Identifier)
         pragma(error, `identifer expected`);
      else static if (tokens[8].ID !=3D Token.plus)
         pragma(error, `+ operator expected`);
      else static if (tokens[9].ID !=3D Token.Identifier)
         pragma(error, `identifer expected`);
      alias token[1] arg0;
      alias token[5] arg1;
      alias token[7] arg2;
      alias token[9] arg3;
      arg0[arg1] =3D arg2 + arg3;
 }
  seems the above is more valuable.
Apr 25 2007
parent reply Don Clugston <dac nospam.com.au> writes:
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.
Apr 25 2007
parent reply Davidl <Davidl 126.com> writes:
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 !=3D Token.LParen)
     pragma(error, `( expected`);
:
      alias token[1] arg0;
      alias token[5] arg1;
      alias token[7] arg2;
      alias token[9] arg3;
      arg0[arg1] =3D arg2 + arg3;
 }
  caller could call the macro with :
  macroname(arg0)[arg1] :=3D arg2 + arg3;
I don't think that's much nicer than mixin(macroname("arg0[arg1]:=3D arg2+ arg3")); which works in DMD 1.012. The static if/Token properties can be done v=
ia =
 a CTFE library. If structs were allowed in CTFE functions, the code  =
 wouldn't even be too ugly.
Apr 25 2007
parent reply Don Clugston <dac nospam.com.au> writes:
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.
Apr 25 2007
parent reply Davidl <Davidl 126.com> writes:
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.
Apr 25 2007
parent reply Don Clugston <dac nospam.com.au> writes:
Davidl wrote:
 heh, mixin an assert is bad, compile time is even longer :p
?? It's no different to a static assert.
 Though you are kinda sticking with CTFE & mixin , I'm pretty
 sure you would vote my syntax of macro for macro, right? :)
No. It's nowhere near clean enough yet. The code below, works right now. The challenge is to make it nicer. //-------------------------- // This is a macro library, defining a 'vectorize' macro. //-------------------------- module Blade; import ExpressionParser; //common code for any macros which use D syntax public import std.stdio; // CTFE macro: returns the code to be mixed in. // prints all symbols used in the expression char [] describe(char [] expr, char [][3][] symbols) { char [] result = "Expression:" ~ expr ~ "\nSYMBOL \tTYPE\t\tVALUE\n"; for (int i=0; i<symbols.length; ++i) { result ~= symbols[i][0] ~ " \t" ~ symbols[i][1] ~ " \t" ~ symbols[i][2] ~ \n; } return `printf("` ~ result ~ `");`; } // TO BE MIXED IN -- inserts the boilerplate code for parsing & type extraction, // then gets the macro text from the named function. char [] vectorize(char [] expr) { return expressionMacro("describe", expr); } //-------------------------- // And this is how the user sees it. //-------------------------- import Blade; void main() { auto a = [1.24, 5.35, 324, 2454]; double [] big = [3.44, 5565, 1.45, 67.1]; const yx=-2.354e-68Li; alias big c; mixin(vectorize("a +=yx *big -c")); } =============================== OUTPUT =============================== Expression:a +=yx *big -c SYMBOL TYPE VALUE a double[4] a yx ireal -2.354e-68i big double[] big c double[] big Note that the constant has been evaluated, and the aliases resolved. And if you change the last line to: mixin(vectorize("a +=yx *big -d")); you get: test.d(10): Error: undefined identifier d That happens automatically. Note that the line number is correct. That expressionMacro must be really horrible, right? Actually, no. It just makes use of some trivial identifier and operator parsing routines, and applies the .stringof property. ----------- // Given an expression string 'expr', make a string to mixin in, // which will have an array of 3 strings for each symbol in the // expression. [0]=symbol name, [1]=type, [2]=value char[] getAllIdentifiers(char [] expr) { char [] result; char [] rest; char [] a; char [] b; a = parseQualifiedName(expr, rest); result = "[[`" ~ a ~"`,typeof(" ~ a ~ ").stringof, " ~a ~ ".stringof]"; while (rest!="") { b = parseOperator(rest, rest); char [] r; a = parseQualifiedName(rest, r); rest=r; result ~= "," ~"[`" ~ a ~"`,typeof(" ~ a ~ ").stringof, " ~a ~ ".stringof]"; } return result ~ "]"; } // Actually it should process the arguments into a standard, convenient form. For now, just pass them on, together with the type information. char [] expressionMacro(char [] macroname, char [] args) { return "mixin ("~macroname ~"(`" ~ args ~"`," ~ getAllIdentifiers(args)~ "));"; } ----------- IMHO, this is already pretty good, despite the fact that there's a couple of workarounds for compiler bugs in there. I just can't believe how powerful this language is now.
Apr 25 2007
next sibling parent reply Davidl <Davidl 126.com> writes:
Nice work!
The compiler now is damned powerful :)

Though I still prefer less string operation in compile time, any idea of  
improving the
macro syntax I posted? You see, D is going to bring AST macro in future  
releases. Let's
discuss the syntax it will use?
Apr 25 2007
parent reply Don Clugston <dac nospam.com.au> writes:
Davidl wrote:
 Nice work!
 The compiler now is damned powerful :)
 
 Though I still prefer less string operation in compile time,
Yes. I think that's the key improvement to make. any idea of
 improving the
 macro syntax I posted? You see, D is going to bring AST macro in future 
 releases. Let's
 discuss the syntax it will use?
To the user, we should eliminate the quotes, and the mixin(). My example was a bit specific. Let's consider lazy evaluation. void logging(bool bLog, lazy char [] msg); which shouldn't evaluate msg unless bLog is true. With my method, it would be: bool bActive=true; mixin(logging(`bActive, getFromDatabase("db", "downtime", event)`)); Obviously, we want to get rid of the mixin() and the ``. But that's probably the only thing we need to do, outside of library code. Once we get rid of the ``, we have the requirement that all macro arguments must be syntactically valid D code. This makes things like your := impossible. Then we have: macro logging(char [][] args) { return `if (` ~ args[0].stringof ~ `) printf("%s", ` ~ args[1].stringof ~ `);`; } (macros always return a char [], which is mixed in). But that will look pretty awful if you want to do more than a single function call. OTOH, if we just get rid of the ``, we lose the ability to make decisions at compile time. What then??
Apr 25 2007
next sibling parent =?UTF-8?B?SmFyaS1NYXR0aSBNw6RrZWzDpA==?= <jmjmak utu.fi.invalid> writes:
Don Clugston wrote:
 Obviously, we want to get rid of the mixin() and the ``. But that's
 probably the only thing we need to do, outside of library code.
 Once we get rid of the ``, we have the requirement that all macro
 arguments must be syntactically valid D code. This makes things like
 your := impossible.
 But that will look pretty awful if you want to do more than a single
 function call. OTOH, if we just get rid of the ``, we lose the ability
 to make decisions at compile time.
 What then??
D being a multiparadigm language, it's a bit funny how many 'native' possibilities there are to implement metaprogramming. All of them have some good and some bad sides. So should these new constructs also cover some meta object protocol or s-expressions or just this shorter syntax for string mixins? Or should meta objects and s-expressions be implemented on the library level?
Apr 25 2007
prev sibling parent Manfred Nowak <svv1999 hotmail.com> writes:
Don Clugston wrote

 Let's consider lazy evaluation.
Yeah. Are you able to eliminate switch-statements out of some code and generate an equivalent series of if-statements---and vice versa? -manfred
Apr 25 2007
prev sibling parent Charlie <charlie.fats gmail.com> writes:
Don Clugston wrote:
 Davidl wrote:
 heh, mixin an assert is bad, compile time is even longer :p
?? It's no different to a static assert.
 Though you are kinda sticking with CTFE & mixin , I'm pretty
 sure you would vote my syntax of macro for macro, right? :)
No. It's nowhere near clean enough yet. The code below, works right now. The challenge is to make it nicer. //-------------------------- // This is a macro library, defining a 'vectorize' macro. //-------------------------- module Blade; import ExpressionParser; //common code for any macros which use D syntax public import std.stdio; // CTFE macro: returns the code to be mixed in. // prints all symbols used in the expression char [] describe(char [] expr, char [][3][] symbols) { char [] result = "Expression:" ~ expr ~ "\nSYMBOL \tTYPE\t\tVALUE\n"; for (int i=0; i<symbols.length; ++i) { result ~= symbols[i][0] ~ " \t" ~ symbols[i][1] ~ " \t" ~ symbols[i][2] ~ \n; } return `printf("` ~ result ~ `");`; } // TO BE MIXED IN -- inserts the boilerplate code for parsing & type extraction, // then gets the macro text from the named function. char [] vectorize(char [] expr) { return expressionMacro("describe", expr); } //-------------------------- // And this is how the user sees it. //-------------------------- import Blade; void main() { auto a = [1.24, 5.35, 324, 2454]; double [] big = [3.44, 5565, 1.45, 67.1]; const yx=-2.354e-68Li; alias big c; mixin(vectorize("a +=yx *big -c")); } =============================== OUTPUT =============================== Expression:a +=yx *big -c SYMBOL TYPE VALUE a double[4] a yx ireal -2.354e-68i big double[] big c double[] big Note that the constant has been evaluated, and the aliases resolved. And if you change the last line to: mixin(vectorize("a +=yx *big -d")); you get: test.d(10): Error: undefined identifier d That happens automatically. Note that the line number is correct. That expressionMacro must be really horrible, right? Actually, no. It just makes use of some trivial identifier and operator parsing routines, and applies the .stringof property. ----------- // Given an expression string 'expr', make a string to mixin in, // which will have an array of 3 strings for each symbol in the // expression. [0]=symbol name, [1]=type, [2]=value char[] getAllIdentifiers(char [] expr) { char [] result; char [] rest; char [] a; char [] b; a = parseQualifiedName(expr, rest); result = "[[`" ~ a ~"`,typeof(" ~ a ~ ").stringof, " ~a ~ ".stringof]"; while (rest!="") { b = parseOperator(rest, rest); char [] r; a = parseQualifiedName(rest, r); rest=r; result ~= "," ~"[`" ~ a ~"`,typeof(" ~ a ~ ").stringof, " ~a ~ ".stringof]"; } return result ~ "]"; } // Actually it should process the arguments into a standard, convenient form. For now, just pass them on, together with the type information. char [] expressionMacro(char [] macroname, char [] args) { return "mixin ("~macroname ~"(`" ~ args ~"`," ~ getAllIdentifiers(args)~ "));"; } ----------- IMHO, this is already pretty good, despite the fact that there's a couple of workarounds for compiler bugs in there.
 I just can't believe how powerful this language is now.
I agree, the CTFE & mixins have really put D over the top, and this is just the beginning. If we are talking about improving the current syntax I'd really like to see a symbol for explicitly calling a function at call time, having to guess what the code is doing is a PITA , especially if you aren't the original developer. I also think compile time string manipulation could be improved, or at least documented. Do you have this new code up somewhere ? Thanks, Charlie
Apr 25 2007
prev sibling next sibling parent Davidl <Davidl 126.com> writes:
sorry for typos in the error message :o
should be
        static if (tokens[4].ID !=3D Token.LBracket)
  	pragma(error, `[ expected`);
        else static if (tokens[5].ID !=3D Token.Identifier)
           pragma(error, `identifer expected`);
        else static if (tokens[6].ID !=3D Token.RBracket)
           pragma(error, `] expected`);

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

 and calling style would be realname(a)[i] :=3D 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 o=
f =
 built-in types.

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

 mixin(vectorize("vec2+=3Dvec1*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 lifti=
ng =
 (eg, convert floating point strings <--> const real).

 As far as I can tell, *all* of the proposed 'macro' functionality ca=
n =
 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 macr=
os =
 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+=3Db*c"));
 :
 -----------
 With macros:

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

 :
 magic(a+=3Db*c);
 :
 ------------
Apr 25 2007
prev sibling parent =?UTF-8?B?SmFyaS1NYXR0aSBNw6RrZWzDpA==?= <jmjmak utu.fi.invalid> writes:
Davidl wrote:
 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.
Huh, that looks awful. This kind of code surely needs some pattern matching magic. Should we add it to D 2.0 wishlist too.
Apr 25 2007
prev sibling parent Davidl <Davidl 126.com> writes:
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] :=3D arg2 +arg3
 {
     arg0[arg1] =3D arg2+arg3;
 }

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

 A couple of weeks ago, I posted an implementation of BLAS1-style vect=
or =
 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 a=
ll =
 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 =3D [43, 5445, 547, 465];
 auto vec2 =3D [1.24, 765, 6756, 23.143];

 mixin(vectorize("vec2+=3Dvec1*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. Y=
ou =
 can determine which of the variables are compile-time constants (and =
=
 optimise the generated code based on the value of those constants). Y=
ou =
 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 o=
f =
 macros will be a subset of the power of mixins. This means that macro=
s =
 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+=3Db*c"));
 :
 -----------
 With macros:

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

 :
 magic(a+=3Db*c);
 :
 ------------
Apr 25 2007