Changeset 173

Show
Ignore:
Timestamp:
01/11/08 02:38:02 (8 months ago)
Author:
Don Clugston
Message:

* Refactored SyntaxTree?.
* Scalar folding of min, max.
* Improved comments.

Files:

Legend:

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

    r171 r173  
    3333*    Except: Addition inside a dot product or vector sum is treated as associative. 
    3434* 
    35 * BUGS: 
    36 *  Need to create asserts for nested expressions as well as for the primary one. 
    37 * 
    3835* FUTURE DIRECTIONS (in order of expected implementation): 
    3936* - trace() 
     
    5653*       (passes pointers, not arrays). 
    5754* 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
    5956*/ 
    6057 
  • trunk/blade/BladeSimplify.d

    r172 r173  
    1515*    - Use associativity of *: A*(B*C[]) == (A*B)*C[] (Not strictly true for 
    1616*      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) 
    1818*      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) 
    1922* (D) Expression standardisation  
    2023*    - Move multiplies to left: Convert A[]*B into B*A[] (assumes * is commutative, 
    2124*      not valid for quaternions). 
    2225*    - Convert C-A*B into C+(-A)*B whenever possible. 
    23 *    - Remove unary minus where possible, eg convert A-(-B) into A+B. 
    2426* 
    2527* Author: 
     
    5153bool isBladeIntrinsic(char [] str) 
    5254{ 
    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"; 
    5457} 
    5558 
     
    282285    } 
    283286    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 ~ ")"; 
    300294    } 
    301295    ReturnType onVisitPrefix(This this_, char [] op, char [] expr) { 
     
    465459    } 
    466460    ReturnType onVisitFunction(This this_, char [] func, char [][] args) { 
     461        ScalarFold left = doVisit(this_,args[0]); 
    467462        switch(func) { 
    468463        case "dot": 
    469464            // dot(a*v1, c*v2) == (a*b)*dot(v1, v2) 
    470465            // this is a scalar, but the dot is a nested expression 
    471             ScalarFold left = doVisit(this_,args[0]); 
    472466            ScalarFold right = doVisit(this_, args[1]); 
    473467            return ScalarFold("", combineMul(combineMul(left.multiplier, right.multiplier), "{" ~ func ~ "(" ~ wrapInParens(left.expr) ~ "," ~ wrapInParens(right.expr) ~ ")}")); 
    474468        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. 
    477470            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) 
    481478            return ScalarFold((left.expr!="" ? func ~ "(" ~ wrapInParens(left.expr) ~ ")" : ""), (left.multiplier.length>0 && left.multiplier!="-")? func ~ "(" ~ wrapInParens(left.multiplier) ~ ")":""); 
    482479        case "sqrt": 
    483480            // There's a reduction in precision if we use f(a*v)==f(a)*f(v); also 
    484481            // a and b could both be negative. 
    485             ScalarFold left = doVisit(this_,args[0]); 
    486482            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 ~ "<@)}");  
    487487        default: 
    488488            assert(0, "BLADE: Unsupported function"); 
     
    583583} 
    584584 
    585 // 'right' should become a new compound expression 
     585// 'right' should become a new compound expression (unless it is trivial) 
    586586char [] combineMulWithCompound(char [] left, char [] right) 
    587587{ 
     
    613613    assert(foldScalars("abs((A*abs(B)))", "10")=="(abs(A))*{(abs((abs(B))))}"); 
    614614    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  
    1313*  *+-/         ST(0)*ST(1), ST(0)+ST(1), ST(0)-ST(1), ST(0)/ST(1) and pop stack 
    1414*  _            ST(1)-ST(0) and pop stack 
    15 *  =            store stack top and pop stack 
     15*  =            store ST(0) and pop stack 
    1616*  ,            duplicate stack top (so ,* means ST=ST*ST, ,+ means ST*=2) 
    1717*  a            abs 
    1818*  n            unary negation 
     19*  m            ST(0) = max(ST(0), ST(1)) 
     20*  u            ST(1) = min(ST(0), ST(1)) 
     21*  q            sqrt 
    1922* 
    2023*  NOT YET IMPLEMENTED: 
    2124*  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 
    2428*/ 
    2529 
     
    5761    } 
    5862    ReturnType onVisitFunction(This this_, char [] func, char [][] args) { 
     63        char [] left = doVisit(this_,args[0]); 
    5964        switch(func) { 
    6065        case "dot": 
    61             char [] left = doVisit(this_,args[0]); 
    6266            char [] right = doVisit(this_, args[1]); 
    6367            if (left==right) return "0" ~ left ~ ",*+"; 
    6468            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"; 
    6974        default:     assert(0, "BLADE ICE: Unsupported"); 
    7075        } 
  • trunk/blade/SyntaxTree.d

    r171 r173  
    1818* 
    1919* BUGS: 
    20 *  The following syntax is not supported
     20*  The following syntax is not supported by mixin_getPrecedence()
    2121*       unary operators & ! 
    2222*       && and || operators 
     
    2424*       ?: 
    2525*       cast 
    26 *       new, delete, anonymous classes 
     26*       new, delete, anonymous classes, functions and delegates 
    2727*       mixin, assert, and import expressions 
    2828*       D 2.004 string literals. 
     
    106106 
    107107//  ==== LEXER ==== 
    108  
    109 /** Return true if text is a non-value, non-type D keyword 
    110  */ 
    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  
    134108/**  
    135109 * Extract all terminal symbols(identifiers, type names, and literals) 
     
    145119    char [] code = ""; 
    146120    char [][] symbols = []; 
    147     char [] symbol = ""; 
    148121    expr ~= " "; // ensure that it ends. 
    149122    char quote=0; // character which marks end of string literal, 0 if none 
     123    int startOfSymbol=0; 
    150124    for(int i=0; i<expr.length; ++i) { 
    151125        char c = expr[i]; 
    152126        // Deal with string literals 
    153127        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; 
    156129            continue; 
    157130        } else if (c == '"' || c == '`' || c == '\'') { // Is a string literal beginning? 
    158             symbol ~= c; 
    159131            quote = c; 
    160132            continue; 
    161133        } 
    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; 
    165159        } 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; 
    210178    } 
    211179    if (code[$-1]==' ') code=code[0..$-1]; // remove the " " we added. 
     
    306274 * Insert parentheses around each symbol, to enforce normal D precedence rules. 
    307275 * Returns: a string which, when mixed in, generates a parenthesised expression. 
    308  *     
    309  *   Bugs: 
    310  *    Doesn't support: 
    311  *      unary operators & ! 
    312  *      && and || operators 
    313  *      ?: 
    314  *      Comparison and NCEG floating point operators 
    315  *      cast 
    316  *      new, delete, anonymous classes 
    317  *      mixin, assert, and import expressions 
    318276 */ 
    319277char [] mixin_getPrecedence(char [] expr) 
     
    415373    assert(mixin(mixin_getPrecedence("A([B,C])"))=="A(([B,C]))");     
    416374} 
     375/+   // NO LONGER USED 
     376/** Return true if text is a non-value, non-type D keyword 
     377 */ 
     378bool 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+/