diff --git a/js/js_test.go b/js/js_test.go index 5e92b65eb2..a641ab6f47 100644 --- a/js/js_test.go +++ b/js/js_test.go @@ -259,7 +259,7 @@ func TestJS(t *testing.T) { {`if(a,b)b`, `a,b&&b`}, {`if(a,b)b;else d`, `a,b||d`}, {`if(a=b)a;else b`, `(a=b)||b`}, - {`if(!a&&!b){return true}else if(!a||!b){return false}return c&&d`, `return!a&&!b||!(!a||!b)&&c&&d`}, + {`if(!a&&!b){return true}else if(!a||!b){return false}return c&&d`, `return!a&&!b||!!a&&!!b&&c&&d`}, {`if(!a){if(b){throw c}else{return c}}else{return a}`, `if(a)return a;if(b)throw c;return c`}, {`if(!a){return y}else if(b){if(c){return x}}return z`, `return a?b&&c?x:z:y`}, @@ -545,7 +545,7 @@ func TestJS(t *testing.T) { {`a?!0:!1`, `!!a`}, {`a?0:1`, `a?0:1`}, {`!!a?0:1`, `!!a?0:1`}, - //{`a&&b?!1:!0`, `!a||!b`}, // TODO + {`a&&b?!1:!0`, `!a||!b`}, {`a&&b?!0:!1`, `!!(a&&b)`}, {`a?true:5`, `!!a||5`}, {`a?5:false`, `!!a&&5`}, @@ -600,13 +600,15 @@ func TestJS(t *testing.T) { {`a??=b`, `a??=b`}, {`a==false`, `a==!1`}, {`a===false`, `a===!1`}, - //{`!(a||b)`, `!a&&!b`}, // TODO - //{`!(a&&b)`, `!a||!b`}, // TODO - //{`!(!a||!b)`, `a&&b`}, // we don't know of a or b are booleans - //{`!(!a&&!b)`, `a||b`}, - //{`!(!a&&b)&&c`, `(a||!b)&&c`}, + {`!(a||b)`, `!a&&!b`}, + {`!(a&&b)`, `!a||!b`}, {`!(a&&b)&&c`, `!(a&&b)&&c`}, - //{`a===false||b===true?false:true`, `a!==!1&&b!==!0`}, // TODO + {`c&&!(a&&b===5)`, `c&&!(a&&b===5)`}, + {`c&&!(!a&&b!==5)`, `c&&!(!a&&b!==5)`}, + {`c&&!(a==3&&b!==5)`, `c&&(a!=3||b===5)`}, + {`!(a>=0&&a<=1||a>=2&&a<=3)`, `!(a>=0&&a<=1||a>=2&&a<=3)`}, + {`a===false||b===true?false:true`, `a!==!1&&b!==!0`}, + //{`!(!(a>=0||a<=1)&&!(a>=2||a<=3))`, `!!(a>=0||a<=1||a>=2||a<=3)`}, // TODO // other {`async function g(){await x+y}`, `async function g(){await x+y}`}, diff --git a/js/util.go b/js/util.go index 2febaa6b52..c252aeb8e6 100644 --- a/js/util.go +++ b/js/util.go @@ -591,44 +591,84 @@ func optimizeBooleanExpr(expr js.IExpr, invert bool, prec js.OpPrec) js.IExpr { } func optimizeUnaryExpr(expr *js.UnaryExpr, prec js.OpPrec) js.IExpr { - //if group, ok := expr.X.(*js.GroupExpr); ok && expr.Op == js.NotToken { - // if binary, ok := group.X.(*js.BinaryExpr); ok && (binary.Op == js.AndToken || binary.Op == js.OrToken) { - // op := js.AndToken - // if binary.Op == js.AndToken { - // op = js.OrToken - // } - // precInside := binaryOpPrecMap[op] + return expr + if group, ok := expr.X.(*js.GroupExpr); ok && expr.Op == js.NotToken { + if binary, ok := group.X.(*js.BinaryExpr); ok && (binary.Op == js.AndToken || binary.Op == js.OrToken) { + op := js.AndToken + if binary.Op == js.AndToken { + op = js.OrToken + } + precInside := binaryOpPrecMap[op] + needsGroup := precInside < prec && (precInside != js.OpCoalesce || prec != js.OpBitOr) + + // rewrite !(a||b) to !a&&!b + // rewrite !(a==0||b==0) to a!=0&&b!=0 + score := 3 // savings if rewritten (group parentheses and not-token) + if needsGroup { + score -= 2 + } + score -= 2 // add two not-tokens for left and right - // // rewrite !(a||b) to !a&&!b - // // rewrite !(a==0||b==0) to a!=0&&b!=0 - // var isNotX, isNotY, isEqX, isEqY bool - // if unaryExpr, ok := binary.X.(*js.UnaryExpr); ok { - // isNotX = unaryExpr.Op == js.NotToken - // } else if binaryExpr, ok := binary.X.(*js.BinaryExpr); ok { - // isEqX = binaryOpPrecMap[binaryExpr.Op] == js.OpEquals - // } - // if unaryExpr, ok := binary.Y.(*js.UnaryExpr); ok { - // isNotY = unaryExpr.Op == js.NotToken - // } else if binaryExpr, ok := binary.Y.(*js.BinaryExpr); ok { - // isEqY = binaryOpPrecMap[binaryExpr.Op] == js.OpEquals - // } - // if !isNotX && !isNotY && (isEqX || isEqY || (prec <= precInside || precInside == js.OpCoalesce && prec == js.OpBitOr)) { - // binary.Op = op - // if isEqX { - // binary.X.(*js.BinaryExpr).Op = invertBooleanOp(binary.X.(*js.BinaryExpr).Op) - // } else { - // binary.X = &js.UnaryExpr{js.NotToken, binary.X} - // } - // if isEqY { + // == and === can become != and !== + var isEqX, isEqY bool + if binaryExpr, ok := binary.X.(*js.BinaryExpr); ok && binaryOpPrecMap[binaryExpr.Op] == js.OpEquals { + score += 1 + isEqX = true + } + if binaryExpr, ok := binary.Y.(*js.BinaryExpr); ok && binaryOpPrecMap[binaryExpr.Op] == js.OpEquals { + score += 1 + isEqY = true + } - // binary.Y.(*js.BinaryExpr).Op = invertBooleanOp(binary.Y.(*js.BinaryExpr).Op) - // } else { - // binary.Y = &js.UnaryExpr{js.NotToken, binary.Y} - // } - // return binary - // } - // } - //} + // left and right may need group or can remove group + var needsGroupX, needsGroupY bool + if op == js.OrToken { + // remove group + if exprPrec(binary.X) == js.OpOr { + score += 2 + } + if exprPrec(binary.Y) == js.OpAnd { + score += 2 + } + } else { + // add group + if !isEqX && exprPrec(binary.X) < js.OpUnary { + score -= 2 + needsGroupX = true + } + if !isEqY && exprPrec(binary.Y) < js.OpUnary { + score -= 2 + needsGroupY = true + } + } + + if 0 < score { + binary.Op = op + if isEqX { + binary.X.(*js.BinaryExpr).Op = invertBooleanOp(binary.X.(*js.BinaryExpr).Op) + } + if isEqY { + binary.Y.(*js.BinaryExpr).Op = invertBooleanOp(binary.Y.(*js.BinaryExpr).Op) + } + if needsGroupX { + binary.X = &js.GroupExpr{binary.X} + } + if needsGroupY { + binary.Y = &js.GroupExpr{binary.Y} + } + if !isEqX { + binary.X = &js.UnaryExpr{js.NotToken, binary.X} + } + if !isEqY { + binary.Y = &js.UnaryExpr{js.NotToken, binary.Y} + } + if needsGroup { + return &js.GroupExpr{binary} + } + return binary + } + } + } return expr }