protected override ICode VisitTernary(ExprTernary e) {
            var condition = (Expr)this.Visit(e.Condition);
            var ifTrue = (Expr)this.Visit(e.IfTrue);
            var ifFalse = (Expr)this.Visit(e.IfFalse);

            if (condition.IsLiteralBoolean(true)) {
                return ifTrue;
            }
            if (condition.IsLiteralBoolean(false)) {
                return ifFalse;
            }

            if (e.Type.IsBoolean()) {
                if (ifTrue.IsLiteralBoolean(true)) {
                    return e.Ctx.ExprGen.Or(condition, ifFalse);
                }
                if (ifTrue.IsLiteralBoolean(false)) {
                    return e.Ctx.ExprGen.And(e.Ctx.ExprGen.NotAutoSimplify(condition), ifFalse);
                }
            }

            if (condition.ExprType == Expr.NodeType.Unary) {
                var cUn = (ExprUnary)condition;
                if (cUn.Op == UnaryOp.Not) {
                    // Remove 'not' from condition and swap ifTrue and ifFalse,
                    return new ExprTernary(e.Ctx, cUn.Expr, ifFalse, ifTrue);
                }
            }

            if (condition != e.Condition || ifTrue != e.IfTrue || ifFalse != e.IfFalse) {
                return new ExprTernary(e.Ctx, condition, ifTrue, ifFalse);
            } else {
                return e;
            }
        }
 protected override ICode VisitIf(StmtIf s) {
     var then = (Stmt)this.Visit(s.Then);
     var @else = (Stmt)this.Visit(s.Else);
     if (then == null && @else == null) {
         return null;
     }
     // Remove 'if' if condition is just true or false
     if (s.Condition.ExprType == Expr.NodeType.Literal) {
         if (s.Condition.IsLiteralBoolean(true)) {
             return then;
         }
         if (s.Condition.IsLiteralBoolean(false)) {
             return @else;
         }
     }
     // If 'then' and 'else' are identical, then remove 'if'
     if (then.DoesEqual(@else)) {
         return then;
     }
     // If 'if' only has an 'else' case, not a 'then' case, then swap
     if (then == null) {
         return new StmtIf(s.Ctx, s.Ctx.ExprGen.NotAutoSimplify(s.Condition), @else, null);
     }
     // If both 'if' parts only contain an assignment to the same (with phi clustering) target, then turn into ternary assignment
     if (then != null && @else != null && then.StmtType == Stmt.NodeType.Assignment && @else.StmtType == Stmt.NodeType.Assignment) {
         var thenAssign = (StmtAssignment)then;
         var elseAssign = (StmtAssignment)@else;
         if (this.AreClustered(thenAssign.Target, elseAssign.Target)) {
             this.replaceVars.Add(Tuple.Create(elseAssign.Target, thenAssign.Target));
             var ternary = new ExprTernary(s.Ctx, s.Condition, thenAssign.Expr, elseAssign.Expr);
             return new StmtAssignment(s.Ctx, thenAssign.Target, ternary);
         }
     }
     // If 'if' contains only 'if' then combine condition with 'and'
     if (@else == null && then.StmtType == Stmt.NodeType.If) {
         var thenIf = (StmtIf)then;
         if (thenIf.Else == null) {
             return new StmtIf(s.Ctx, s.Ctx.ExprGen.And(s.Condition, thenIf.Condition), thenIf.Then, null);
         }
     }
     if (then != s.Then || @else != s.Else) {
         return new StmtIf(s.Ctx, s.Condition, then, @else);
     } else {
         return s;
     }
 }
 protected override ICode VisitTernary(ExprTernary e) {
     this.js.Append("(");
     this.Visit(e.Condition);
     this.js.Append(" ? ");
     this.Visit(e.IfTrue);
     this.js.Append(" : ");
     this.Visit(e.IfFalse);
     this.js.Append(")");
     return e;
 }
 public static Stmt Ceiling(Ctx ctx) {
     var arg = ctx.MethodParameter(0);
     var e = new ExprTernary(ctx,
         new ExprJsResolvedMethod(ctx, ctx.Boolean, null, "Number.isFinite", arg),
         new ExprJsResolvedMethod(ctx, ctx.Double, null, "Math.ceil", arg),
         arg);
     return new StmtReturn(ctx, e);
 }