Changeset 173
- Timestamp:
- 01/11/08 02:38:02 (8 months ago)
- Files:
-
- trunk/blade/Blade.d (modified) (2 diffs)
- trunk/blade/BladeSimplify.d (modified) (6 diffs)
- trunk/blade/PostfixX86.d (modified) (2 diffs)
- trunk/blade/SyntaxTree.d (modified) (6 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/blade/Blade.d
r171 r173 33 33 * Except: Addition inside a dot product or vector sum is treated as associative. 34 34 * 35 * BUGS:36 * Need to create asserts for nested expressions as well as for the primary one.37 *38 35 * FUTURE DIRECTIONS (in order of expected implementation): 39 36 * - trace() … … 56 53 * (passes pointers, not arrays). 57 54 * 0.5 - Expression simplification step. Slicing support. 58 * 0.6 - Dot product, nested expressions .55 * 0.6 - Dot product, nested expressions, intrinsics: abs, sqrt, sum. 59 56 */ 60 57 trunk/blade/BladeSimplify.d
r172 r173 15 15 * - Use associativity of *: A*(B*C[]) == (A*B)*C[] (Not strictly true for 16 16 * floating point; results may differ by 1ulp, 17 eg (1.3L*3.1L)*4.7L < 1.3L*(3.1L*4.7L)17 * eg (1.3L*3.1L)*4.7L < 1.3L*(3.1L*4.7L) 18 18 * Note that floating point addition is not associative at all). 19 * - Remove unary minus where possible, eg A-(-B) => A+B, abs(-A) => abs(A). 20 * - Use associativity of * in intrinsics: 21 * sum(A*V) => A*sum(V), abs(A*B) => abs(A)*abs(B) 19 22 * (D) Expression standardisation 20 23 * - Move multiplies to left: Convert A[]*B into B*A[] (assumes * is commutative, 21 24 * not valid for quaternions). 22 25 * - Convert C-A*B into C+(-A)*B whenever possible. 23 * - Remove unary minus where possible, eg convert A-(-B) into A+B.24 26 * 25 27 * Author: … … 51 53 bool isBladeIntrinsic(char [] str) 52 54 { 53 return str=="dot" || str=="sum" || str=="abs" || str=="sqrt"; 55 return str=="dot" || str=="sum" || str=="max" || str=="min" 56 || str=="abs" || str=="sqrt"; 54 57 } 55 58 … … 282 285 } 283 286 ReturnType onVisitFunction(This this_, char [] func, char [][] args) { 284 switch (func) { 285 case "dot": 286 // 2-argument functions 287 char [] left = wrapInParens(doVisit(this_,args[0])); 288 char [] right = wrapInParens(doVisit(this_, args[1])); 289 return func ~ "(" ~ left ~ "," ~ right ~ ")"; 290 case "sum": 291 case "sqrt": 292 case "abs": 293 // 1-argument functions 294 char [] left = wrapInParens(doVisit(this_,args[0])); 295 return func ~ "(" ~ left ~ ")"; 296 default: 297 assert(0, "BLADE ICE: Unsupported function"); 298 return ""; 299 } 287 // Intrinsics have no effect on slicing 288 char [] result = ""; 289 for (int i=0; i<args.length; ++i) { 290 if (i>0) result ~= ","; 291 result ~= wrapInParens(doVisit(this_,args[i])); 292 } 293 return func ~ "(" ~ result ~ ")"; 300 294 } 301 295 ReturnType onVisitPrefix(This this_, char [] op, char [] expr) { … … 465 459 } 466 460 ReturnType onVisitFunction(This this_, char [] func, char [][] args) { 461 ScalarFold left = doVisit(this_,args[0]); 467 462 switch(func) { 468 463 case "dot": 469 464 // dot(a*v1, c*v2) == (a*b)*dot(v1, v2) 470 465 // this is a scalar, but the dot is a nested expression 471 ScalarFold left = doVisit(this_,args[0]);472 466 ScalarFold right = doVisit(this_, args[1]); 473 467 return ScalarFold("", combineMul(combineMul(left.multiplier, right.multiplier), "{" ~ func ~ "(" ~ wrapInParens(left.expr) ~ "," ~ wrapInParens(right.expr) ~ ")}")); 474 468 case "sum": 475 // Functions where f(v) is a scalar. 476 ScalarFold left = doVisit(this_,args[0]); 469 // sum(A*V) = A*sum(V) is a scalar. 477 470 return ScalarFold("", combineMul(left.multiplier, "{" ~ func ~ "(" ~ wrapInParens(left.expr) ~ ")}")); 478 case "abs": 479 // f(a*v)==f(a)*f(v), and f(-v)==f(v) 480 ScalarFold left = doVisit(this_,args[0]); 471 case "abs": 472 // abs(abs(V)*S) = abs(V)*abs(S) 473 if (left.expr.length>5 && left.expr[0..4]=="abs(") { 474 if (left.multiplier=="") return left; 475 return ScalarFold(left.expr, func ~ "(" ~ wrapInParens(left.multiplier) ~ ")"); 476 } 477 // abs(S*V)==abs(S)*abs(V). abs(-V) = abs(V) 481 478 return ScalarFold((left.expr!="" ? func ~ "(" ~ wrapInParens(left.expr) ~ ")" : ""), (left.multiplier.length>0 && left.multiplier!="-")? func ~ "(" ~ wrapInParens(left.multiplier) ~ ")":""); 482 479 case "sqrt": 483 480 // There's a reduction in precision if we use f(a*v)==f(a)*f(v); also 484 481 // a and b could both be negative. 485 ScalarFold left = doVisit(this_,args[0]);486 482 return ScalarFold((left.expr!="" ? func ~ "(" ~ wrapInParens(left.expr) ~ ")" : ""), (left.multiplier.length>0)? func ~ "(" ~ wrapInParens(left.multiplier) ~ ")":""); 483 case "max": 484 case "min": // max(A*B) can't be simplified unless we know that they are not negative. 485 return ScalarFold("", "{" ~ func ~ "(" ~ combineMulWithCompound(left.expr, left.multiplier) ~ ")}"); 486 // return ScalarFold("", "{" ~ func ~ "(@>" ~ left.expr ~ "@" ~ left.multiplier ~ "<@)}"); 487 487 default: 488 488 assert(0, "BLADE: Unsupported function"); … … 583 583 } 584 584 585 // 'right' should become a new compound expression 585 // 'right' should become a new compound expression (unless it is trivial) 586 586 char [] combineMulWithCompound(char [] left, char [] right) 587 587 { … … 613 613 assert(foldScalars("abs((A*abs(B)))", "10")=="(abs(A))*{(abs((abs(B))))}"); 614 614 assert(foldScalars("abs((-A))", "1")=="abs(A)"); 615 } 615 assert(foldScalars("abs((abs(((-A)*B))))", "10")=="(abs(A))*{(abs((abs((-B)))))}"); 616 assert(foldScalars("min((A*(sum((B*A)))))", "10")=="{min(A*{(B*({sum(A)}))})}"); 617 } trunk/blade/PostfixX86.d
r172 r173 13 13 * *+-/ ST(0)*ST(1), ST(0)+ST(1), ST(0)-ST(1), ST(0)/ST(1) and pop stack 14 14 * _ ST(1)-ST(0) and pop stack 15 * = store stack topand pop stack15 * = store ST(0) and pop stack 16 16 * , duplicate stack top (so ,* means ST=ST*ST, ,+ means ST*=2) 17 17 * a abs 18 18 * n unary negation 19 * m ST(0) = max(ST(0), ST(1)) 20 * u ST(1) = min(ST(0), ST(1)) 21 * q sqrt 19 22 * 20 23 * NOT YET IMPLEMENTED: 21 24 * 1 the literal one (used to initialize a product, for example) 22 * sc sin, cos 23 * q sqrt 25 * scel sin, cos, exp, log 26 * 27 * Unassigned lower case letters: bdfghijkoprtvwxyz 24 28 */ 25 29 … … 57 61 } 58 62 ReturnType onVisitFunction(This this_, char [] func, char [][] args) { 63 char [] left = doVisit(this_,args[0]); 59 64 switch(func) { 60 65 case "dot": 61 char [] left = doVisit(this_,args[0]);62 66 char [] right = doVisit(this_, args[1]); 63 67 if (left==right) return "0" ~ left ~ ",*+"; 64 68 return "0" ~ left ~ right ~ "*+"; 65 case "sum": 66 return "0" ~ doVisit(this_, args[0]) ~ "+"; 67 case "abs": return doVisit(this_,args[0]) ~ "a"; 68 case "sqrt": return doVisit(this_,args[0]) ~ "q"; 69 case "sum": return "0" ~ left ~ "+"; 70 // case "max": return left ~ "m"; 71 // case "min": return left ~ "u"; 72 case "abs": return left ~ "a"; 73 case "sqrt": return left ~ "q"; 69 74 default: assert(0, "BLADE ICE: Unsupported"); 70 75 } trunk/blade/SyntaxTree.d
r171 r173 18 18 * 19 19 * BUGS: 20 * The following syntax is not supported :20 * The following syntax is not supported by mixin_getPrecedence(): 21 21 * unary operators & ! 22 22 * && and || operators … … 24 24 * ?: 25 25 * cast 26 * new, delete, anonymous classes 26 * new, delete, anonymous classes, functions and delegates 27 27 * mixin, assert, and import expressions 28 28 * D 2.004 string literals. … … 106 106 107 107 // ==== LEXER ==== 108 109 /** Return true if text is a non-value, non-type D keyword110 */111 bool isNonTypeKeyword(char [] text) {112 char [][] keywords = [113 // "byte", "bool", "cdouble", "cent", "cfloat", "cdouble", "creal", "char",114 // "double", "dchar", "false", "float", "idouble", "ifloat", "int", "ireal", "long",115 // "null", "real", "short", "true", "ubyte", "ucent", "uint", "ulong", "ushort",116 // "wchar", "this", "super", "void"117 "abstract", "alias", "align", "asm", "assert", "auto", "body", "break",118 "case", "cast", "catch", "class", "const", "continue", "dchar", "debug",119 "default", "delegate", "delete", "deprecated", "do", "else", "enum",120 "export", "extern", "final", "finally", "for", "foreach", "foreach_reverse",121 "function", "goto", "if", "import", "in", "inout", "interface", "invariant",122 "is", "lazy", "macro", "mixin", "module", "new", "out", "override",123 "package", "pragma", "private", "protected", "public", "ref", "return",124 "scope", "static", "struct", "switch", "synchronized", "template", "throw",125 "__traits", "try", "typedef", "typeid", "typeof", "union", "unittest",126 "version", "volatile", "while", "with" ];127 128 foreach(char [] s; keywords) {129 if (s==text) return true;130 }131 return false;132 }133 134 108 /** 135 109 * Extract all terminal symbols(identifiers, type names, and literals) … … 145 119 char [] code = ""; 146 120 char [][] symbols = []; 147 char [] symbol = "";148 121 expr ~= " "; // ensure that it ends. 149 122 char quote=0; // character which marks end of string literal, 0 if none 123 int startOfSymbol=0; 150 124 for(int i=0; i<expr.length; ++i) { 151 125 char c = expr[i]; 152 126 // Deal with string literals 153 127 if (quote != 0) { 154 symbol ~= c; 155 if (c == quote && (symbol.length<1 || symbol[$-1]!='\\')) quote = 0; 128 if (c == quote && (i>0 || expr[i-1]!='\\')) quote=0; 156 129 continue; 157 130 } else if (c == '"' || c == '`' || c == '\'') { // Is a string literal beginning? 158 symbol ~= c;159 131 quote = c; 160 132 continue; 161 133 } 162 // Identifiers and numeric literals 163 if ((c >='a' && c <= 'z') || (c>='A' && c<='Z')|| (c>='0' && c<='9')||(c=='_')||(c>0x7F)){ 164 symbol ~= c; 134 // Identifiers and numeric literals are a..zA..Z_ or non-ASCII. 135 // + and - are part of the symbol, if it is a floating-point literal 136 // . is part of a symbol, unless it is a ".." slice 137 if ((c >='a' && c <= 'z') || (c>='A' && c<='Z') 138 || (c>='0' && c<='9') || (c=='_') || (c>0x7F) 139 || ((c=='+' || c=='-') && i>0 && (expr[startOfSymbol]>='0' && expr[startOfSymbol]<='9') && (expr[i-1]=='e' ||expr[i-1]=='p'|| expr[i-1]=='E' || expr[i-1]=='P')) 140 || (c=='.' && expr[i+1]!='.') 141 ) continue; 142 // The next character is NOT part of the symbol. 143 char [] symbol = expr[startOfSymbol..i]; 144 if (symbol=="is" || symbol=="in") { 145 code ~= symbol; // non-type keywords are NOT symbols 146 symbol=""; 147 } 148 if (symbol.length>0) { 149 // Add the new symbol to the symbol table 150 code ~= cast(char)('A' + symbols.length); 151 symbols ~= symbol; 152 } 153 if (c=='.') { // it was opSlice. Skip the next . as well. 154 i++; 155 code ~= "."; 156 } 157 if (c!='/' || i >= expr.length-1) { 158 code ~= c; 165 159 } else { 166 if ((c=='+' || c=='-') && symbol.length>1 && (symbol[0]>='0' && symbol[0]<='9') && (symbol[$-1]=='e' ||symbol[$-1]=='p'|| symbol[$-1]=='E' || symbol[$-1]=='P')) { 167 symbol ~= c; // + and - are part of the symbol, if it is a floating-point literal 168 continue; 169 } 170 if (c=='.' && expr[i+1]!='.') { // . is part of a symbol, unless it is a ".." slice 171 symbol ~=c; 172 continue; 173 } 174 // The next character is NOT part of the symbol. 175 if (isNonTypeKeyword(symbol)) { 176 code ~= symbol; // non-type keywords are NOT symbols 177 symbol=""; 178 } 179 if (symbol.length>0) { 180 // Add the new symbol to the symbol table 181 code ~= cast(char)('A' + symbols.length); 182 symbols ~= symbol; 183 symbol = ""; 184 } 185 if (c=='.') { // it was opSlice. Skip the next . as well. 186 i++; 187 code ~= "."; 188 } 189 if (c!='/' || i >= expr.length-1) { 190 code ~= c; 191 } else { 192 // Ignore comments. 193 if (expr[i..i+2]=="//") { 194 for ( ; i<expr.length && expr[i]!='\n' ; ++i) {} 195 } else if (expr[i..i+2]=="/*") { 196 i+=3; 197 for ( ; i<expr.length && expr[i-1..i+1] != "*/"; ++i) {} 198 if ( i >= expr.length-1 ) break; 199 } else if (expr[i..i+2]=="/+") { 200 i++; 201 for (int nest = 1; nest>0 && i < expr.length-1; ++i) { 202 if (expr[i..i+2] == "+/") { --nest; ++i; } 203 else if (expr[i..i+2] == "/+") { ++nest; ++i; } 204 } 205 --i; 206 if (i >= expr.length-1) break; 207 } else code~=c; 208 } 209 } 160 // Ignore comments. 161 if (expr[i..i+2]=="//") { 162 for ( ; i<expr.length && expr[i]!='\n' ; ++i) {} 163 } else if (expr[i..i+2]=="/*") { 164 i+=3; 165 for ( ; i<expr.length && expr[i-1..i+1] != "*/"; ++i) {} 166 if ( i >= expr.length-1 ) break; 167 } else if (expr[i..i+2]=="/+") { 168 i++; 169 for (int nest = 1; nest>0 && i < expr.length-1; ++i) { 170 if (expr[i..i+2] == "+/") { --nest; ++i; } 171 else if (expr[i..i+2] == "/+") { ++nest; ++i; } 172 } 173 --i; 174 if (i >= expr.length-1) break; 175 } else code~=c; 176 } 177 startOfSymbol = i+1; 210 178 } 211 179 if (code[$-1]==' ') code=code[0..$-1]; // remove the " " we added. … … 306 274 * Insert parentheses around each symbol, to enforce normal D precedence rules. 307 275 * Returns: a string which, when mixed in, generates a parenthesised expression. 308 *309 * Bugs:310 * Doesn't support:311 * unary operators & !312 * && and || operators313 * ?:314 * Comparison and NCEG floating point operators315 * cast316 * new, delete, anonymous classes317 * mixin, assert, and import expressions318 276 */ 319 277 char [] mixin_getPrecedence(char [] expr) … … 415 373 assert(mixin(mixin_getPrecedence("A([B,C])"))=="A(([B,C]))"); 416 374 } 375 /+ // NO LONGER USED 376 /** Return true if text is a non-value, non-type D keyword 377 */ 378 bool isNonTypeKeyword(char [] text) { 379 char [][] keywords = [ 380 "abstract", "alias", "align", "asm", "assert", "auto", "body", "break", 381 "case", "cast", "catch", "class", "const", "continue", "dchar", "debug", 382 "default", "delegate", "delete", "deprecated", "do", "else", "enum", 383 "export", "extern", "final", "finally", "for", "foreach", "foreach_reverse", 384 "function", "goto", "if", "import", "in", "inout", "interface", "invariant", 385 "is", "lazy", "macro", "mixin", "module", "new", "out", "override", 386 "package", "pragma", "private", "protected", "public", "ref", "return", 387 "scope", "static", "struct", "switch", "synchronized", "template", "throw", 388 "__traits", "try", "typedef", "typeid", "typeof", "union", "unittest", 389 "version", "volatile", "while", "with" ]; 390 391 foreach(char [] s; keywords) { 392 if (s==text) return true; 393 } 394 return false; 395 } 396 +/
