Changeset 103
- Timestamp:
- 05/01/07 07:26:17 (1 year ago)
- Files:
-
- trunk/blade/BladeMixin.d (modified) (8 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/blade/BladeMixin.d
r102 r103 1 1 /** 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. 2 4 * 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]. 3 12 * 4 13 */ … … 25 34 } 26 35 27 28 char [] mixin_typeTuple(char [][ ] symbols)36 // Given a Symbol[] array literal, make a tuple to be mixed in. 37 char [] mixin_typeTuple(char [][2][] symbols) 29 38 { 30 39 char [] result = ""; 31 40 for(int i=0; i<symbols.length; ++i) { 32 41 if (i>0) result ~=","; 33 result ~= "typeof(" ~ symbols[i] ~ ")";42 result ~= symbols[i][0]; 34 43 } 35 44 result ~=""; 36 45 return result; 37 46 } 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 */62 47 63 48 /** Parse a string to construct an expression tree, using normal D syntax. … … 118 103 parseExpression("(any)+=(((((any)*(yx))*(c))+(((((32.145e31f)*(yx))*(any.length))*(test.extra))*(big)))-(big))", exprTree); 119 104 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";122 105 } 123 106 … … 150 133 else newvalues ~= newvalues[k]; 151 134 } 135 // Update the expression 152 136 char [] newexpr=expr[0..1]; 153 137 for (int i=1; i<expr.length; ++i) { … … 181 165 } 182 166 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 expression211 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 221 167 // Returns a string, which, when mixed in, will produce the value described in s. 222 223 168 char [] MixinOf(A)(A[] symbols) 224 169 { … … 242 187 } 243 188 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 */ 193 char [] mixin_resolvealiases(char [] macroname, char [] expr, Symbol[] symbols) 247 194 { 248 195 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) ~ "));"; 260 197 } 261 198 … … 267 204 // char [] macroname(char [] syntaxtree, char [][2][] symboltable) 268 205 // and it must return a char [] of the text to be mixed in. 269 char [] mixin_expressionMacro(char [] macroname, char [] args) 270 { 206 char [] 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. 271 210 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 217 public import std.stdio; 218 219 220 221 char [] 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 235 char [] 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 */ 248 char [] 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. 265 char [] 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. 277 char [] 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). 292 char [] 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. 326 char [] 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. 338 char [] 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 ~ "`));"; 275 352 } 276 353 … … 283 360 284 361 double extra = 3.8; 362 285 363 void main() 286 364 { 287 365 288 autoany = [1.24, 5.35, 324, 2454];289 double [ ] big = [3.44, 5565, 1.45, 67.1];290 const yx=-82.354e-68 i;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; 291 369 292 370 alias extra c; 293 294 371 // 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))")); 296 373 printf(\n); 297 374 }
