public DekiScriptOutputBuffer.Range Visit(DekiScriptSwitch expr, DekiScriptExpressionEvaluationState state)
        {
            DekiScriptLiteral value = state.Pop(expr.Value.VisitWith(this, state));

            DekiScriptSwitch.CaseBlock caseBlock = null;

            // have to use for instead of foreach, since a fallthrough default case needs to be able to look ahead
            for (int i = 0; i < expr.Cases.Length; i++)
            {
                DekiScriptSwitch.CaseBlock current = expr.Cases[i];

                // check for default case
                foreach (DekiScriptExpression condition in current.Conditions)
                {
                    if (condition == null)
                    {
                        // check if this is the first default we've found
                        if (caseBlock == null)
                        {
                            caseBlock = current;
                        }

                        // continue in case loop, since default only gets executed if there is no match
                        continue;
                    }

                    // evaluate test
                    DekiScriptExpression test      = DekiScriptExpression.BinaryOp(current.Location, DekiScriptBinary.Op.Equal, value, condition);
                    DekiScriptLiteral    caseMatch = state.Pop(test.VisitWith(this, state));

                    // evaluate body on success
                    if (!caseMatch.IsNilFalseZero)
                    {
                        // found a matching cast statement
                        caseBlock = current;
                        break;
                    }
                }
            }

            // haven't found a match yet, so if we have a default, return it
            if (caseBlock != null)
            {
                int marker = state.Buffer.Marker;
                try {
                    return(caseBlock.Body.VisitWith(this, state));
                } catch (DekiScriptBreakException) {
                    // nothing to do
                }
                return(state.Buffer.Since(marker));
            }
            return(DekiScriptOutputBuffer.Range.Empty);
        }
        private object Optimize(DekiScriptSwitch.CaseBlock expr, DekiScriptExpression value, DekiScriptExpressionEvaluationState state, out bool isDefaultCase)
        {
            List <DekiScriptExpression> conditions = new List <DekiScriptExpression>();

            isDefaultCase = false;
            for (int i = 0; i < expr.Conditions.Length; i++)
            {
                if (expr.Conditions[i] != null)
                {
                    DekiScriptExpression condition = expr.Conditions[i].VisitWith(this, state);

                    // check if condition always succeeds or always fails
                    if ((value is DekiScriptLiteral) && (condition is DekiScriptLiteral))
                    {
                        var test   = DekiScriptExpression.BinaryOp(Location.None, DekiScriptBinary.Op.Equal, value, condition);
                        var result = state.Pop(test.VisitWith(DekiScriptExpressionEvaluation.Instance, state));
                        if (!result.IsNilFalseZero)
                        {
                            // NOTE (steveb): we wrap the outcome into a sequence to ensure proper handling of break/continue statements

                            // condition succeeded, return it
                            return(DekiScriptExpression.Block(expr.Location, new[] { expr.Body.VisitWith(this, state) }));
                        }
                    }
                    else
                    {
                        conditions.Add(condition);
                    }
                }
                else
                {
                    isDefaultCase = true;
                    conditions.Add(null);
                }
            }

            // check if any conditions were true or unknown
            if (conditions.Count == 0)
            {
                return(null);
            }
            DekiScriptExpression body = expr.Body.VisitWith(this, state);

            return(new DekiScriptSwitch.CaseBlock(expr.Location, conditions.ToArray(), body));
        }