Changeset 103

Show
Ignore:
Timestamp:
05/01/07 07:26:17 (1 year ago)
Author:
Don Clugston
Message:

Now checks vector lengths and semantics, and actually generates D code.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/blade/BladeMixin.d

    r102 r103  
    11/** BLADE 0.3 -- vector expressions using mixins 
     2* Using mixins gives extraordinary potential for optimisation of the generated code. 
     3* It is possible to detect aliases, and resolve compile-time constants. 
    24* 
     5* Terminology: 
     6* A "CTFE macro" is a function, executed at compile time, which returns a char [] 
     7* containing source code to be mixed in. 
     8* A "CTFE expression macro" is a CTFE macro with parameters (char [] expression, char [][2][] symbol) 
     9* where all terminal symbols in the expression (symbols or literals) are replaced with placeholders 
     10* #a, #b, ... which index into the Symbol array. Eg, for #c, the type is symbol['c'-'a'][0], 
     11* and the value is symbol['c'-'a'][1]. 
    312* 
    413*/ 
     
    2534} 
    2635 
    27  
    28 char [] mixin_typeTuple(char [][] symbols) 
     36// Given a Symbol[] array literal, make a tuple to be mixed in. 
     37char [] mixin_typeTuple(char [][2][] symbols) 
    2938{ 
    3039    char [] result = ""; 
    3140    for(int i=0; i<symbols.length; ++i) { 
    3241        if (i>0) result ~=","; 
    33         result ~= "typeof(" ~ symbols[i] ~ ")"
     42        result ~= symbols[i][0]
    3443    } 
    3544    result ~=""; 
    3645    return result; 
    3746} 
    38  
    39 char [] mixin_valueTuple(char [][] symbols) 
    40 { 
    41     char [] result = ""; 
    42     for(int i=0; i<symbols.length; ++i) { 
    43         if (i>0) result ~=","; 
    44         result ~= "" ~ symbols[i] ~ ""; 
    45     } 
    46     result ~=""; 
    47     return result; 
    48 } 
    49  
    50 /* 
    51 char [] mixin_typeTuple(char [][2][] symbols) 
    52 { 
    53     char [] result = ""; 
    54     for(int i=0; i<symbols.length; ++i) { 
    55         if (i>0) result ~=","; 
    56         result ~= symbols[i][0]; //"typeof(" ~ symbols[i] ~ ")"; 
    57     } 
    58     result ~=""; 
    59     return result; 
    60 } 
    61 */ 
    6247 
    6348/** Parse a string to construct an expression tree, using normal D syntax. 
     
    118103    parseExpression("(any)+=(((((any)*(yx))*(c))+(((((32.145e31f)*(yx))*(any.length))*(test.extra))*(big)))-(big))", exprTree); 
    119104    assert(exprTree=="#a+=((((#b*#c)*#d)+((((#e*#f)*#g)*#h)*#i))-#j)", exprTree); 
    120  
    121     char [] expr = "#a+=(((#a*#b)*#c)+((((#d*#b)*#e)*#c)*#f))-#f"; 
    122105} 
    123106 
     
    150133        else newvalues ~= newvalues[k]; 
    151134    } 
     135    // Update the expression 
    152136    char [] newexpr=expr[0..1]; 
    153137    for (int i=1; i<expr.length; ++i) { 
     
    181165} 
    182166 
    183 char [] determineVectorLength(Symbol[] symbols) 
    184 { 
    185     char [] result=""; 
    186     char [] first; 
    187     char [] knownlength=""; 
    188     bool bFirst=true; 
    189     for (int i=0; i<symbols.length; ++i) { 
    190         if (isArray(symbols[i][0])) { 
    191             if (!isDynamicArray(symbols[i][0])) 
    192                 knownlength=symbols[i][1] ~".length"; 
    193             if (bFirst) { 
    194                 first = symbols[i][1] ~".length"; 
    195                 bFirst=false; 
    196             } else { 
    197                 if (result!="") result ~= " && "; 
    198                 result ~= "(" ~ symbols[i][1] ~".length == "~ first ~")"; 
    199             } 
    200         } 
    201     } 
    202     if (knownlength=="") return first; else return knownlength; 
    203 //    return result; 
    204 } 
    205  
    206 //-------------------------- 
    207 public import std.stdio; 
    208  
    209 // Normal function (not CTFE or mixin) which will be called at run-time. 
    210 // prints all symbols used in the expression 
    211 void do_describe(char [] expr, Symbol[] symbols, int x) 
    212 { 
    213     char [] result = "Expression:" ~ expr ~ "\n\tTYPE\t\tVALUE\n"; 
    214     for (int i=0; i<symbols.length; ++i) { 
    215         result ~= "#" ~ cast(char)('a'+i) ~ "\t" ~ symbols[i][0] ~ " \t" ~ symbols[i][1] ~ \n; 
    216     } 
    217     printf("%.*s\n Length=%d", result, x); 
    218 } 
    219  
    220  
    221167// Returns a string, which, when mixed in, will produce the value described in s. 
    222  
    223168char [] MixinOf(A)(A[] symbols) 
    224169{ 
     
    242187} 
    243188 
    244 // CTFE macro: 
    245 // prints all symbols used in the expression 
    246 char [] describe(char [] expr, Symbol[] symbols) 
     189 
     190/* FOR MIXIN. Using the symbol table, replaces all of the constants and aliased symbols in 
     191 * the expression with their values, and passes them to the text mixin macro 'macroname'. 
     192 */ 
     193char [] mixin_resolvealiases(char [] macroname, char [] expr, Symbol[] symbols) 
    247194{ 
    248195    removeDuplicatesFromExpression(expr, symbols); 
    249 //    assert(0, expr); 
    250     char [] x = determineVectorLength(symbols); 
    251     return `do_describe(` ~ MixinOf(expr) ~ `, ` ~ MixinOf(symbols) ~ "," ~ x ~ ");"; 
    252  
    253 /* 
    254     char [] result = "Expression:" ~ expr ~ "\n\tTYPE\t\tVALUE\n"; 
    255     for (int i=0; i<symbols.length; ++i) { 
    256         result ~= "#" ~ cast(char)('a'+i) ~ "\t" ~ symbols[i][0] ~ " \t" ~ symbols[i][1] ~ \n; 
    257     } 
    258     return `printf("` ~ result ~ `");`; 
    259 */ 
     196    return "mixin (" ~ macroname ~ "!(" ~mixin_typeTuple(symbols) ~ ")(`" ~ expr ~ "`," ~ MixinOf(symbols) ~ "));"; 
    260197} 
    261198 
     
    267204//  char [] macroname(char [] syntaxtree, char [][2][] symboltable) 
    268205// and it must return a char [] of the text to be mixed in. 
    269 char [] mixin_expressionMacro(char [] macroname, char [] args) 
    270 
     206char [] mixin_expressionMacro(char [] macroname, char [] expression) 
     207
     208    // Find all the terminal symbols in the expression, and get their type and value. 
     209    // Pass this information into another CTFE macro which will resolve the aliases. 
    271210    char [] expr; 
    272     char [][] symbols = parseExpression(args, expr); 
    273 //    return "mixin (" ~ macroname ~ "!(" ~mixin_typeTuple(symbols) ~ ")(`" ~ expr ~ "`," ~ mixin_SymbolTable(symbols) ~ "));"; 
    274     return "mixin (" ~ macroname ~ "(`" ~ expr ~ "`," ~ mixin_SymbolTable(symbols) ~ "));"; 
     211    char [][] symbols = parseExpression(expression, expr); 
     212    return "mixin (mixin_resolvealiases(`" ~ macroname ~ "`,`" ~ expr ~ "`," ~ mixin_SymbolTable(symbols) ~ "));"; 
     213
     214 
     215//-------------------------- 
     216 
     217public import std.stdio; 
     218 
     219 
     220 
     221char [] mixin_bladeknownlength(char [] expr, Symbol[] symbols, int knownlength, char [] len) 
     222
     223    if (knownlength<1) { // just inline the whole thing 
     224      char [] result = ""; 
     225      for (int i=0; i<knownlength; ++i) { 
     226          result ~= mixin_insertIndex(expr, symbols, cast(char)('0'+i)~ "") ~";\n"; 
     227      } 
     228      return result; 
     229  } else return mixin_bladeunknownlength(expr, symbols, len); 
     230
     231 
     232// Normal function (not CTFE or mixin) which will be called at run-time. 
     233// prints all symbols used in the expression 
     234 
     235char [] mixin_bladeunknownlength(char [] expr, Symbol[] symbols, char [] len) 
     236
     237    // use blade_index to reduce probability of a 'shadowing declaration' error. 
     238    char [] result = "for (int blade_index=0; blade_index<" ~ len ~ "; ++blade_index){\n" 
     239        ~ mixin_insertIndex(expr, symbols, "blade_index") ~";\n" 
     240        ~ "}\n"; 
     241    return result; 
     242
     243 
     244 
     245/** Restore the symbols into the expression, removing the #a placeholders. 
     246 * For every element in expr which is an array, replace it with [indx]. 
     247 */ 
     248char [] mixin_insertIndex(char [] expr, Symbol[] symbols, char [] indx) 
     249
     250    char [] newExpr; 
     251    for (int i=0; i<expr.length; ++i) { 
     252        if (expr[i]=='#') { 
     253            int n = expr[i+1]-'a'; 
     254            newExpr ~= symbols[n][1]; 
     255            if (isArray(symbols[n][0])) newExpr ~="[" ~ indx ~ "]"; 
     256            ++i; 
     257        } else newExpr ~= expr[i]; 
     258    } 
     259    return newExpr; 
     260
     261 
     262/// Return a constant expression to be mixed in, which gives the 
     263/// length of the arrays. Return "" if it cannot be determined at 
     264/// at compile time. 
     265char [] determineVectorKnownLength(Symbol[] symbols) 
     266
     267    for (int i=0; i<symbols.length; ++i) { 
     268        if (isArray(symbols[i][0]) && !isDynamicArray(symbols[i][0])) 
     269            return symbols[i][1] ~".length"; // Static arrays have known length 
     270    } 
     271    return ""; // Length can't be determined at compile time 
     272
     273 
     274/// Return an expression to be mixed in, which gives the 
     275/// length of the arrays. If possible, this will be a compile-time 
     276/// constant. 
     277char [] determineVectorLength(Symbol[] symbols) 
     278
     279    char [] len; 
     280    for (int i=0; i<symbols.length; ++i) { 
     281        if (isArray(symbols[i][0])) { 
     282            if (!isDynamicArray(symbols[i][0])) 
     283                return symbols[i][1] ~".length"; 
     284            len = symbols[i][1] ~".length"; 
     285        } 
     286    } 
     287    return len; 
     288
     289 
     290/// Return a char[] which asserts that all vectors have the same length. 
     291/// (Note: should also check if it overlaps with a destination vector). 
     292char [] mixin_checkVectorLengths(Symbol[] symbols) 
     293
     294    char [] staticchecks=""; 
     295    char [] runtimechecks=""; 
     296    char [] first; // first expression for the length. 
     297    char [] knownlength=""; // first length which is known at compile time 
     298    bool bFirst=true; // Is this the first array we've found? 
     299    for (int i=0; i<symbols.length; ++i) { 
     300        if (isArray(symbols[i][0])) { 
     301            bool needRuntimeCheck=true; 
     302            if (bFirst) { 
     303                first = symbols[i][1]; 
     304                needRuntimeCheck=false; 
     305            } 
     306            if (!isDynamicArray(symbols[i][0])) { 
     307                // Static array -- has known length 
     308                if (knownlength!="") { // This is not the first one. 
     309                    staticchecks ~= "static assert(" ~ knownlength ~ ".length ==" ~ symbols[i][1] ~".length,`Vector length mismatch: " ~ symbols[i][1] ~", " ~ knownlength ~ "`);\n"; 
     310                    needRuntimeCheck = false; 
     311                } else { 
     312                    knownlength=symbols[i][1]; 
     313                } 
     314            } 
     315            if (needRuntimeCheck) { 
     316                runtimechecks ~= "assert(" ~ symbols[i][1] ~".length == "~ first ~".length,`Vector length mismatch.`);\n"; 
     317            } 
     318            bFirst=false; 
     319        } 
     320    } 
     321    return staticchecks ~ runtimechecks; 
     322
     323 
     324 
     325/// Perform a compile-time check that the vector expression is semantically valid. 
     326char [] mixin_checkVectorSemantics(char [] expr, Symbol[] symbols) 
     327
     328    // The expression should compile if we add a [0] to the end of every vector. 
     329    // The use of .sizeof is a trick to generate a nice error message. 
     330    // (is() tells you it is semantically invalid, but doesn't tell you why). 
     331    return `static assert(typeof(` ~ mixin_insertIndex(expr, symbols, "0")~").sizeof);"; 
     332
     333 
     334 
     335// CTFE macro: 
     336// Insert compile-time and run-time checks to ensure that the vector expression 
     337// does not contain errors. Then generate the code. 
     338char [] describe(Types...)(char [] expr, Symbol[] symbols) 
     339
     340    char [] len = determineVectorLength(symbols); 
     341    char [] knownlength = determineVectorKnownLength(symbols); 
     342 
     343    char [] result = mixin_checkVectorSemantics(expr, symbols) ~ 
     344        mixin_checkVectorLengths(symbols); 
     345        //`printf("` ~ mixin_insertIndex(expr, symbols, "0") ~ `\n");` 
     346    result ~= "printf(`Expression : " ~ expr ~ "`\\n);\n"; 
     347    if (knownlength!="") 
     348        return result ~= `mixin(mixin_bladeknownlength(` 
     349        ~ MixinOf(expr)~ ","~ MixinOf(symbols) ~ "," ~ knownlength~ ",`"~knownlength~ "`));"; 
     350    return result ~`mixin(mixin_bladeunknownlength(` 
     351        ~ MixinOf(expr) ~ `, ` ~ MixinOf(symbols) ~ ",`" ~ len ~ "`));"; 
    275352} 
    276353 
     
    283360 
    284361double extra = 3.8; 
     362 
    285363void main() 
    286364{ 
    287365 
    288     auto any = [1.24, 5.35, 324, 2454]; 
    289     double [] big = [3.44, 5565, 1.45, 67.1]; 
    290     const yx=-82.354e-68i
     366    double [] any = [1.24, 5.35, 324, 2454]; 
     367    double [4] big = [3.44, 5565, 1.45, 67.1]; 
     368    const yx=-82.354e-68
    291369 
    292370    alias extra c; 
    293  
    294371    // For now, parentheses must be used around each element. Yes, it's disgusting. 
    295     mixin(vectorize("(any)+=(((((any)*(yx))*(c))+(((((32.145e31f)*(yx))*(any.length))*(BladeMixin.extra))*(big)))-(big))")); 
     372    mixin(vectorize("(any)=(((((any)*(yx))*(c))+(((((32.145e31f)*(yx))*(any.length))*(BladeMixin.extra))*(big)))-(big))")); 
    296373    printf(\n); 
    297374}