/// <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); }