Exemplo n.º 1
0
        /// <inheritdoc />
        protected override EvaluationResult DoEval(Context context, ModuleLiteral env, EvaluationStackFrame frame)
        {
            var leftCandidate = LeftExpression.Eval(context, env, frame);

            if (leftCandidate.IsErrorValue)
            {
                return(EvaluationResult.Error);
            }

            EvaluationResult left  = leftCandidate;
            EvaluationResult right = default(EvaluationResult);

            if (OperatorKind != BinaryOperator.And && OperatorKind != BinaryOperator.Or)
            {
                // Don't eval right expression for And and Or operators due to possible short circuit.
                var rightCandidate = RightExpression.Eval(context, env, frame);

                if (rightCandidate.IsErrorValue)
                {
                    return(EvaluationResult.Error);
                }

                right = rightCandidate;
            }

            try
            {
                checked
                {
                    int leftNumber;
                    int rightNumber;

                    switch (OperatorKind)
                    {
                    case BinaryOperator.Addition:
                        // Different cases:

                        // 1. If left OR right is a string result is a string
                        // 2. If left AND right are numbers - result is a number
                        string leftString  = left.Value as string;
                        string rightString = right.Value as string;

                        // First case: if any of the frame is string
                        if (leftString != null)
                        {
                            if (rightString != null)
                            {
                                return(EvaluationResult.Create(leftString + rightString));
                            }

                            return(EvaluationResult.Create(leftString + ToStringConverter.ObjectToString(context, right)));
                        }

                        if (rightString != null)
                        {
                            return(EvaluationResult.Create(ToStringConverter.ObjectToString(context, left) + rightString));
                        }

                        // Expecting numbers, but can't report type mismatch error, because in this case string or number are allowed.
                        if (TryGetNumbers(left, right, out leftNumber, out rightNumber))
                        {
                            return(EvaluationResult.Create(leftNumber + rightNumber));
                        }

                        context.Errors.ReportUnexpectedValueType(env, LeftExpression, left, typeof(int), typeof(string));
                        return(EvaluationResult.Error);

                    // Math operators
                    case BinaryOperator.Remainder:
                        return(EvaluationResult.Create(Converter.ExpectNumber(left, position: 0) % Converter.ExpectNumber(right, position: 1)));

                    case BinaryOperator.Multiplication:
                        return(EvaluationResult.Create(Converter.ExpectNumber(left, position: 0) * Converter.ExpectNumber(right, position: 1)));

                    case BinaryOperator.Subtraction:
                        return(EvaluationResult.Create(Converter.ExpectNumber(left, position: 0) - Converter.ExpectNumber(right, position: 1)));

                    case BinaryOperator.Exponentiation:
                        return(NumberOperations.Power(context, Converter.ExpectNumber(left), Converter.ExpectNumber(right), LocationForLogging(context, env)));

                    // Equality + Comparison
                    case BinaryOperator.Equal:
                        return(EvaluationResult.Create(left.Equals(right)));

                    case BinaryOperator.NotEqual:
                        return(EvaluationResult.Create(!left.Equals(right)));

                    case BinaryOperator.GreaterThanOrEqual:
                        return(EvaluationResult.Create(Converter.ExpectNumber(left, position: 0) >= Converter.ExpectNumber(right, position: 1)));

                    case BinaryOperator.GreaterThan:
                        return(EvaluationResult.Create(Converter.ExpectNumber(left, position: 0) > Converter.ExpectNumber(right, position: 1)));

                    case BinaryOperator.LessThanOrEqual:
                        return(EvaluationResult.Create(Converter.ExpectNumber(left, position: 0) <= Converter.ExpectNumber(right, position: 1)));

                    case BinaryOperator.LessThan:
                        return(EvaluationResult.Create(Converter.ExpectNumber(left, position: 0) < Converter.ExpectNumber(right, position: 1)));

                    // Conditionals
                    case BinaryOperator.Or:
                        return(EvalOr(context, env, frame, left));

                    case BinaryOperator.And:
                        return(EvalAnd(context, env, frame, left));

                    // Bitwise operations
                    // For all bitwise operations call to ToEnumValueIfNeeded is required to convert
                    // numeric representation to enum value if left hand side is enum
                    // (ExpectNumberOrEnums will make sure that left and right operands have the same type).
                    case BinaryOperator.BitWiseOr:
                        if (ExpectNumbersOrEnums(context, env, left, right, out leftNumber, out rightNumber))
                        {
                            return(Converter.ToEnumValueIfNeeded(left.GetType(), leftNumber | rightNumber));
                        }

                        return(EvaluationResult.Error);

                    case BinaryOperator.BitWiseAnd:
                        if (ExpectNumbersOrEnums(context, env, left, right, out leftNumber, out rightNumber))
                        {
                            return(Converter.ToEnumValueIfNeeded(left.GetType(), leftNumber & rightNumber));
                        }

                        return(EvaluationResult.Error);

                    case BinaryOperator.BitWiseXor:
                        if (ExpectNumbersOrEnums(context, env, left, right, out leftNumber, out rightNumber))
                        {
                            return(Converter.ToEnumValueIfNeeded(left.GetType(), leftNumber ^ rightNumber));
                        }

                        break;

                    case BinaryOperator.LeftShift:
                        return(EvaluationResult.Create(Converter.ExpectNumber(left, position: 0) << Converter.ExpectNumber(right, position: 1)));

                    case BinaryOperator.SignPropagatingRightShift:
                        return(EvaluationResult.Create(NumberOperations.SignPropagatingRightShift(
                                                           Converter.ExpectNumber(left, position: 0),
                                                           Converter.ExpectNumber(right, position: 1)).Value));

                    case BinaryOperator.ZeroFillingRightShift:
                        return(EvaluationResult.Create(NumberOperations.ZeroFillingRightShift(
                                                           Converter.ExpectNumber(left, position: 0),
                                                           Converter.ExpectNumber(right, position: 1)).Value));

                    default:
                        // And and Or operator should have been replaced by the ite expression.
                        // case BinaryOperator.And:
                        // case BinaryOperator.Or:
                        //    return (bool)l && (bool)RightExpression.Eval(context, env, frame);
                        //    return (bool) l || (bool) RightExpression.Eval(context, env, frame);
                        Contract.Assert(false);
                        break;
                    }
                }
            }
            catch (OverflowException)
            {
                context.Logger.ReportArithmeticOverflow(
                    context.LoggingContext,
                    LocationForLogging(context, env),
                    this.ToDisplayString(context));
            }
            catch (ConvertException convertException)
            {
                context.Errors.ReportUnexpectedValueType(
                    env,
                    convertException.ErrorContext.Pos == 0 ? LeftExpression : RightExpression,
                    convertException.Value, convertException.ExpectedTypesToString(context));
            }
            catch (DivideByZeroException)
            {
                context.Errors.ReportDivideByZeroException(env, this, Location);
            }

            return(EvaluationResult.Error);
        }