Skip to content

Commit

Permalink
JS: improve compression of conditional expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
tdewolff committed Apr 5, 2022
1 parent 3ae425a commit cc66302
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 22 deletions.
3 changes: 2 additions & 1 deletion js/js_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,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)`},
{`a&&b?!1:!0`, `!a||!b`},
{`a&&b?!0:!1`, `!!(a&&b)`},
{`a?true:5`, `!!a||5`},
{`a?5:false`, `!!a&&5`},
Expand Down Expand Up @@ -603,6 +603,7 @@ func TestJS(t *testing.T) {
//{`!(!a&&!b)`, `a||b`},
//{`!(!a&&b)&&c`, `(a||!b)&&c`},
{`!(a&&b)&&c`, `!(a&&b)&&c`},
{`a===false||b===true?false:true`, `a!==!1&&b!==!0`},

// other
{`async function g(){await x+y}`, `async function g(){await x+y}`},
Expand Down
61 changes: 40 additions & 21 deletions js/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -569,22 +569,27 @@ func isBooleanExpr(expr js.IExpr) bool {
return false
}

func invertBooleanOp(op js.TokenType) js.TokenType {
if op == js.EqEqToken {
return js.NotEqToken
} else if op == js.NotEqToken {
return js.EqEqToken
} else if op == js.EqEqEqToken {
return js.NotEqEqToken
} else if op == js.NotEqEqToken {
return js.EqEqEqToken
}
return js.ErrorToken
}

func optimizeBooleanExpr(expr js.IExpr, invert bool, prec js.OpPrec) js.IExpr {
if invert {
// unary !(boolean) has already been handled
if binaryExpr, ok := expr.(*js.BinaryExpr); ok && binaryOpPrecMap[binaryExpr.Op] == js.OpEquals {
if binaryExpr.Op == js.EqEqToken {
binaryExpr.Op = js.NotEqToken
} else if binaryExpr.Op == js.NotEqToken {
binaryExpr.Op = js.EqEqToken
} else if binaryExpr.Op == js.EqEqEqToken {
binaryExpr.Op = js.NotEqEqToken
} else if binaryExpr.Op == js.NotEqEqToken {
binaryExpr.Op = js.EqEqEqToken
}
binaryExpr.Op = invertBooleanOp(binaryExpr.Op)
return expr
} else {
return &js.UnaryExpr{js.NotToken, groupExpr(expr, js.OpUnary)}
return optimizeUnaryExpr(&js.UnaryExpr{js.NotToken, groupExpr(expr, js.OpUnary)}, prec)
}
} else if isBooleanExpr(expr) {
return groupExpr(expr, prec)
Expand All @@ -594,26 +599,40 @@ func optimizeBooleanExpr(expr js.IExpr, invert bool, prec js.OpPrec) js.IExpr {
}

func optimizeUnaryExpr(expr *js.UnaryExpr, prec js.OpPrec) js.IExpr {
// rewrite !(a||b) to !a&&!b and similar conversions
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
}
unaryX, notX := binary.X.(*js.UnaryExpr)
if notX {
notX = unaryX.Op == js.NotToken
precInside := binaryOpPrecMap[op]

// 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
}
unaryY, notY := binary.Y.(*js.UnaryExpr)
if notY {
notY = unaryY.Op == js.NotToken
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
}
precInside := binaryOpPrecMap[op]
if (prec <= precInside || precInside == js.OpCoalesce && prec == js.OpBitOr) && !notX && !notY {
if !isNotX && !isNotY && (isEqX || isEqY || (prec <= precInside || precInside == js.OpCoalesce && prec == js.OpBitOr)) {
binary.Op = op
binary.X = &js.UnaryExpr{js.NotToken, binary.X}
binary.Y = &js.UnaryExpr{js.NotToken, binary.Y}
if isEqX {
binary.X.(*js.BinaryExpr).Op = invertBooleanOp(binary.X.(*js.BinaryExpr).Op)
} else {
binary.X = &js.UnaryExpr{js.NotToken, binary.X}
}
if isEqY {

binary.Y.(*js.BinaryExpr).Op = invertBooleanOp(binary.Y.(*js.BinaryExpr).Op)
} else {
binary.Y = &js.UnaryExpr{js.NotToken, binary.Y}
}
return binary
}
}
Expand Down

0 comments on commit cc66302

Please sign in to comment.