private static object CalculateDateTime(ScriptBinaryOperator op, SourceSpan span, DateTime left, DateTime right) { switch (op) { case ScriptBinaryOperator.Substract: return(left - right); case ScriptBinaryOperator.CompareEqual: return(left == right); case ScriptBinaryOperator.CompareNotEqual: return(left != right); case ScriptBinaryOperator.CompareLess: return(left < right); case ScriptBinaryOperator.CompareLessOrEqual: return(left <= right); case ScriptBinaryOperator.CompareGreater: return(left > right); case ScriptBinaryOperator.CompareGreaterOrEqual: return(left >= right); } throw new ScriptRuntimeException(span, $"The operator `{op}` is not supported for DateTime"); }
public BinaryExpressionOrOperator(ScriptBinaryOperator @operator, ScriptToken operatorToken) { Expression = null; Operator = @operator; OperatorToken = operatorToken; CallKind = FunctionCallKind.None; }
public bool TryEvaluate(TemplateContext context, SourceSpan span, ScriptBinaryOperator op, SourceSpan leftSpan, object leftValue, SourceSpan rightSpan, object rightValue, out object result) { result = null; if (leftValue is KalkComplex leftComplex && rightValue is KalkComplex rightComplex) { switch (op) { case ScriptBinaryOperator.Add: result = (KalkComplex)(leftComplex._value + rightComplex._value); return(true); case ScriptBinaryOperator.Substract: result = (KalkComplex)(leftComplex._value - rightComplex._value); return(true); case ScriptBinaryOperator.Divide: result = (KalkComplex)(leftComplex._value / rightComplex._value); return(true); case ScriptBinaryOperator.DivideRound: result = (KalkComplex)(leftComplex._value / rightComplex._value); return(true); case ScriptBinaryOperator.Multiply: result = (KalkComplex)(leftComplex._value * rightComplex._value); return(true); case ScriptBinaryOperator.Power: result = (KalkComplex)(Complex.Pow(leftComplex._value, rightComplex._value)); return(true); } }
private static object CalculateDateTime(ScriptBinaryOperator op, SourceSpan span, DateTime left, DateTime right) { switch (op) { case ScriptBinaryOperator.Substract: return(left - right); case ScriptBinaryOperator.CompareEqual: return(left == right); case ScriptBinaryOperator.CompareNotEqual: return(left != right); case ScriptBinaryOperator.CompareLess: return(left < right); case ScriptBinaryOperator.CompareLessOrEqual: return(left <= right); case ScriptBinaryOperator.CompareGreater: return(left > right); case ScriptBinaryOperator.CompareGreaterOrEqual: return(left >= right); } throw new ScriptRuntimeException(span, string.Format(RS.UnsupportedOperatorForType, op.ToText(), "datetime")); }
private object Calculate(ScriptBinaryOperator op, object leftValue, Type leftType, object rightValue, Type rightType) { var customType = leftValue as IScriptCustomType ?? rightValue as IScriptCustomType; if (customType != null) { return(customType.EvaluateBinaryExpression(this, leftValue, rightValue)); } // The order matters: double, float, long, int if (leftType == typeof(double)) { var rightDouble = (double)ScriptValueConverter.ToObject(Span, rightValue, typeof(double)); return(Calculate(op, (double)leftValue, rightDouble)); } if (rightType == typeof(double)) { var leftDouble = (double)ScriptValueConverter.ToObject(Span, leftValue, typeof(double)); return(Calculate(op, leftDouble, (double)rightValue)); } if (leftType == typeof(float)) { var rightFloat = (float)ScriptValueConverter.ToObject(Span, rightValue, typeof(float)); return(Calculate(op, (float)leftValue, rightFloat)); } if (rightType == typeof(float)) { var leftFloat = (float)ScriptValueConverter.ToObject(Span, leftValue, typeof(float)); return(Calculate(op, leftFloat, (float)rightValue)); } if (leftType == typeof(long)) { var rightLong = (long)ScriptValueConverter.ToObject(Span, rightValue, typeof(long)); return(Calculate(op, (long)leftValue, rightLong)); } if (rightType == typeof(long)) { var leftLong = (long)ScriptValueConverter.ToObject(Span, leftValue, typeof(long)); return(Calculate(op, leftLong, (long)rightValue)); } if (leftType == typeof(int) || (leftType != null && leftType.GetTypeInfo().IsEnum)) { var rightInt = (int)ScriptValueConverter.ToObject(Span, rightValue, typeof(int)); return(Calculate(op, (int)leftValue, rightInt)); } if (rightType == typeof(int) || (rightType != null && rightType.GetTypeInfo().IsEnum)) { var leftInt = (int)ScriptValueConverter.ToObject(Span, leftValue, typeof(int)); return(Calculate(op, leftInt, (int)rightValue)); } throw new ScriptRuntimeException(Span, $"Unsupported types [{leftValue ?? "null"}/{leftType?.ToString() ?? "null"}] {op.ToText()} [{rightValue ?? "null"}/{rightType?.ToString() ?? "null"}] for binary operation"); }
private static object CalculateDateTime(ScriptBinaryOperator op, SourceSpan span, DateTime left, TimeSpan right) { switch (op) { case ScriptBinaryOperator.Add: return(left + right); } throw new ScriptRuntimeException(span, string.Format(RS.OperatorNotImplemented, op.ToText(), "datetime", "timespan")); }
private static object CalculateDateTime(ScriptBinaryOperator op, SourceSpan span, DateTime left, TimeSpan right) { switch (op) { case ScriptBinaryOperator.Add: return(left + right); } throw new ScriptRuntimeException(span, $"The operator `{op}` is not supported for between <DateTime> and <TimeSpan>"); }
public static string ToText(this ScriptBinaryOperator op) { switch (op) { case ScriptBinaryOperator.Add: return("+"); case ScriptBinaryOperator.Substract: return("-"); case ScriptBinaryOperator.Divide: return("/"); case ScriptBinaryOperator.DivideRound: return("//"); case ScriptBinaryOperator.Multiply: return("*"); case ScriptBinaryOperator.Modulus: return("%"); case ScriptBinaryOperator.RangeInclude: return(".."); case ScriptBinaryOperator.RangeExclude: return("..."); case ScriptBinaryOperator.CompareEqual: return("=="); case ScriptBinaryOperator.CompareNotEqual: return("!="); case ScriptBinaryOperator.CompareLessOrEqual: return("<="); case ScriptBinaryOperator.CompareGreaterOrEqual: return(">="); case ScriptBinaryOperator.CompareLess: return("<"); case ScriptBinaryOperator.CompareGreater: return(">"); case ScriptBinaryOperator.And: return("&&"); case ScriptBinaryOperator.Or: return("||"); } return(op.ToString()); }
private object Calculate(ScriptBinaryOperator op, bool left, bool right) { switch (op) { case ScriptBinaryOperator.CompareEqual: return(left == right); case ScriptBinaryOperator.CompareNotEqual: return(left != right); } throw new ScriptRuntimeException(Span, $"The operator [{op.ToText()}] is not valid for bool<->bool"); }
private static object CalculateBool(ScriptBinaryOperator op, SourceSpan span, bool left, bool right) { switch (op) { case ScriptBinaryOperator.CompareEqual: return(left == right); case ScriptBinaryOperator.CompareNotEqual: return(left != right); } throw new ScriptRuntimeException(span, $"The operator `{op.ToText()}` is not valid for bool<->bool"); }
private static int GetOperatorPrecedence(ScriptBinaryOperator op) { switch (op) { case ScriptBinaryOperator.EmptyCoalescing: return(20); case ScriptBinaryOperator.ShiftLeft: case ScriptBinaryOperator.ShiftRight: return(25); case ScriptBinaryOperator.Or: return(30); case ScriptBinaryOperator.And: return(40); case ScriptBinaryOperator.CompareEqual: case ScriptBinaryOperator.CompareNotEqual: return(50); case ScriptBinaryOperator.CompareLess: case ScriptBinaryOperator.CompareLessOrEqual: case ScriptBinaryOperator.CompareGreater: case ScriptBinaryOperator.CompareGreaterOrEqual: return(60); case ScriptBinaryOperator.LiquidContains: case ScriptBinaryOperator.LiquidStartsWith: case ScriptBinaryOperator.LiquidEndsWith: case ScriptBinaryOperator.LiquidHasKey: case ScriptBinaryOperator.LiquidHasValue: return(65); case ScriptBinaryOperator.Add: case ScriptBinaryOperator.Substract: return(70); case ScriptBinaryOperator.Multiply: case ScriptBinaryOperator.Divide: case ScriptBinaryOperator.DivideRound: case ScriptBinaryOperator.Modulus: return(80); case ScriptBinaryOperator.RangeInclude: case ScriptBinaryOperator.RangeExclude: return(90); default: return(0); } }
private static object CalculateBool(ScriptBinaryOperator op, SourceSpan span, bool left, bool right) { switch (op) { case ScriptBinaryOperator.CompareEqual: return(left == right); case ScriptBinaryOperator.CompareNotEqual: return(left != right); } throw new ScriptRuntimeException(span, string.Format(RS.UnsupportedOperatorForType, op.ToText(), "bool")); }
private object CalculateToString(ScriptBinaryOperator op, object left, object right) { switch (op) { case ScriptBinaryOperator.Add: return(ScriptValueConverter.ToString(Span, left) + ScriptValueConverter.ToString(Span, right)); case ScriptBinaryOperator.Multiply: if (right is int) { var temp = left; left = right; right = temp; } if (left is int) { var rightText = ScriptValueConverter.ToString(Span, right); var builder = new StringBuilder(); for (int i = 0; i < (int)left; i++) { builder.Append(rightText); } return(builder.ToString()); } throw new ScriptRuntimeException(Span, $"Operator [{op.ToText()}] is not supported for the expression [{this}]. Only working on string x int or int x string"); // unit test: 112-binary-string-error1.txt case ScriptBinaryOperator.CompareEqual: return(ScriptValueConverter.ToString(Span, left) == ScriptValueConverter.ToString(Span, right)); case ScriptBinaryOperator.CompareNotEqual: return(ScriptValueConverter.ToString(Span, left) != ScriptValueConverter.ToString(Span, right)); case ScriptBinaryOperator.CompareGreater: return(ScriptValueConverter.ToString(Span, left).CompareTo(ScriptValueConverter.ToString(Span, right)) > 0); case ScriptBinaryOperator.CompareLess: return(ScriptValueConverter.ToString(Span, left).CompareTo(ScriptValueConverter.ToString(Span, right)) < 0); case ScriptBinaryOperator.CompareGreaterOrEqual: return(ScriptValueConverter.ToString(Span, left).CompareTo(ScriptValueConverter.ToString(Span, right)) >= 0); case ScriptBinaryOperator.CompareLessOrEqual: return(ScriptValueConverter.ToString(Span, left).CompareTo(ScriptValueConverter.ToString(Span, right)) <= 0); } // unit test: 150-range-expression-error1.out.txt throw new ScriptRuntimeException(Span, $"Operator [{op.ToText()}] is not supported on string objects"); // unit test: 112-binary-string-error2.txt }
private object Calculate(ScriptBinaryOperator op, int left, int right) { switch (op) { case ScriptBinaryOperator.Add: return(left + right); case ScriptBinaryOperator.Substract: return(left - right); case ScriptBinaryOperator.Multiply: return(left * right); case ScriptBinaryOperator.Divide: return((float)left / right); case ScriptBinaryOperator.DivideRound: return(left / right); case ScriptBinaryOperator.Modulus: return(left % right); case ScriptBinaryOperator.CompareEqual: return(left == right); case ScriptBinaryOperator.CompareNotEqual: return(left != right); case ScriptBinaryOperator.CompareGreater: return(left > right); case ScriptBinaryOperator.CompareLess: return(left < right); case ScriptBinaryOperator.CompareGreaterOrEqual: return(left >= right); case ScriptBinaryOperator.CompareLessOrEqual: return(left <= right); case ScriptBinaryOperator.RangeInclude: return(RangeInclude(left, right)); case ScriptBinaryOperator.RangeExclude: return(RangeExclude(left, right)); } throw new ScriptRuntimeException(Span, $"The operator [{op.ToText()}] is not implemented for int<->int"); }
private static object CalculateLong(ScriptBinaryOperator op, SourceSpan span, long left, long right) { switch (op) { case ScriptBinaryOperator.Add: return(left + right); case ScriptBinaryOperator.Substract: return(left - right); case ScriptBinaryOperator.Multiply: return(left * right); case ScriptBinaryOperator.Divide: return((double)left / right); case ScriptBinaryOperator.DivideRound: return(left / right); case ScriptBinaryOperator.Modulus: return(left % right); case ScriptBinaryOperator.CompareEqual: return(left == right); case ScriptBinaryOperator.CompareNotEqual: return(left != right); case ScriptBinaryOperator.CompareGreater: return(left > right); case ScriptBinaryOperator.CompareLess: return(left < right); case ScriptBinaryOperator.CompareGreaterOrEqual: return(left >= right); case ScriptBinaryOperator.CompareLessOrEqual: return(left <= right); case ScriptBinaryOperator.RangeInclude: return(RangeInclude(left, right)); case ScriptBinaryOperator.RangeExclude: return(RangeExclude(left, right)); } throw new ScriptRuntimeException(span, string.Format(RS.UnsupportedOperatorForType, op.ToText(), "long")); }
private static object CalculateLong(ScriptBinaryOperator op, SourceSpan span, long left, long right) { switch (op) { case ScriptBinaryOperator.Add: return(left + right); case ScriptBinaryOperator.Substract: return(left - right); case ScriptBinaryOperator.Multiply: return(left * right); case ScriptBinaryOperator.Divide: return((double)left / right); case ScriptBinaryOperator.DivideRound: return(left / right); case ScriptBinaryOperator.Modulus: return(left % right); case ScriptBinaryOperator.CompareEqual: return(left == right); case ScriptBinaryOperator.CompareNotEqual: return(left != right); case ScriptBinaryOperator.CompareGreater: return(left > right); case ScriptBinaryOperator.CompareLess: return(left < right); case ScriptBinaryOperator.CompareGreaterOrEqual: return(left >= right); case ScriptBinaryOperator.CompareLessOrEqual: return(left <= right); case ScriptBinaryOperator.RangeInclude: return(RangeInclude(left, right)); case ScriptBinaryOperator.RangeExclude: return(RangeExclude(left, right)); } throw new ScriptRuntimeException(span, $"The operator `{op.ToText()}` is not implemented for long<->long"); }
private static bool HasSimilarPrecedenceThanMultiply(ScriptBinaryOperator op) { switch (op) { case ScriptBinaryOperator.Multiply: case ScriptBinaryOperator.Divide: case ScriptBinaryOperator.DivideRound: case ScriptBinaryOperator.Modulus: case ScriptBinaryOperator.ShiftLeft: case ScriptBinaryOperator.ShiftRight: case ScriptBinaryOperator.Power: return(true); } return(false); }
private bool TryLiquidBinaryOperator(out ScriptBinaryOperator binOp) { binOp = 0; if (Current.Type != TokenType.Identifier) { return(false); } var text = GetAsText(Current); switch (text) { case "or": binOp = ScriptBinaryOperator.Or; return(true); case "and": binOp = ScriptBinaryOperator.And; return(true); case "contains": binOp = ScriptBinaryOperator.LiquidContains; return(true); case "startsWith": binOp = ScriptBinaryOperator.LiquidStartsWith; return(true); case "endsWith": binOp = ScriptBinaryOperator.LiquidEndsWith; return(true); case "hasKey": binOp = ScriptBinaryOperator.LiquidHasKey; return(true); case "hasValue": binOp = ScriptBinaryOperator.LiquidHasValue; return(true); } return(false); }
private static object CalculateDecimal(ScriptBinaryOperator op, SourceSpan span, decimal left, decimal right) { switch (op) { case ScriptBinaryOperator.Add: return(left + right); case ScriptBinaryOperator.Substract: return(left - right); case ScriptBinaryOperator.Multiply: return(left * right); case ScriptBinaryOperator.Divide: return(left / right); case ScriptBinaryOperator.DivideRound: return(Math.Round(left / right)); case ScriptBinaryOperator.Modulus: return(left % right); case ScriptBinaryOperator.CompareEqual: return(left == right); case ScriptBinaryOperator.CompareNotEqual: return(left != right); case ScriptBinaryOperator.CompareGreater: return(left > right); case ScriptBinaryOperator.CompareLess: return(left < right); case ScriptBinaryOperator.CompareGreaterOrEqual: return(left >= right); case ScriptBinaryOperator.CompareLessOrEqual: return(left <= right); } throw new ScriptRuntimeException(span, string.Format(RS.UnsupportedOperatorForType, op.ToText(), "decimal")); }
private static object CalculateFloat(ScriptBinaryOperator op, SourceSpan span, float left, float right) { switch (op) { case ScriptBinaryOperator.Add: return(left + right); case ScriptBinaryOperator.Substract: return(left - right); case ScriptBinaryOperator.Multiply: return(left * right); case ScriptBinaryOperator.Divide: return((float)left / right); case ScriptBinaryOperator.DivideRound: return((double)(int)(left / right)); case ScriptBinaryOperator.Modulus: return(left % right); case ScriptBinaryOperator.CompareEqual: return(left == right); case ScriptBinaryOperator.CompareNotEqual: return(left != right); case ScriptBinaryOperator.CompareGreater: return(left > right); case ScriptBinaryOperator.CompareLess: return(left < right); case ScriptBinaryOperator.CompareGreaterOrEqual: return(left >= right); case ScriptBinaryOperator.CompareLessOrEqual: return(left <= right); } throw new ScriptRuntimeException(span, $"The operator `{op.ToText()}` is not implemented for float<->float"); }
private static object CalculateDecimal(ScriptBinaryOperator op, SourceSpan span, decimal left, decimal right) { switch (op) { case ScriptBinaryOperator.Add: return(left + right); case ScriptBinaryOperator.Substract: return(left - right); case ScriptBinaryOperator.Multiply: return(left * right); case ScriptBinaryOperator.Divide: return(left / right); case ScriptBinaryOperator.DivideRound: return(Math.Round(left / right)); case ScriptBinaryOperator.Modulus: return(left % right); case ScriptBinaryOperator.CompareEqual: return(left == right); case ScriptBinaryOperator.CompareNotEqual: return(left != right); case ScriptBinaryOperator.CompareGreater: return(left > right); case ScriptBinaryOperator.CompareLess: return(left < right); case ScriptBinaryOperator.CompareGreaterOrEqual: return(left >= right); case ScriptBinaryOperator.CompareLessOrEqual: return(left <= right); } throw new ScriptRuntimeException(span, $"The operator `{op.ToText()}` is not implemented for decimal<->decimal"); }
public bool TryEvaluate(TemplateContext context, SourceSpan span, ScriptBinaryOperator op, SourceSpan leftSpan, object leftValue, SourceSpan rightSpan, object rightValue, out object result) { result = null; if (leftValue is KalkHalf leftHalf && rightValue is KalkHalf rightHalf) { result = (KalkHalf)(float)ScriptBinaryExpression.Evaluate(context, span, op, leftSpan, (float)leftHalf, rightSpan, (float)rightHalf); return(true); } if (leftValue is KalkHalf leftHalf1) { result = (KalkHalf)(float)ScriptBinaryExpression.Evaluate(context, span, op, leftSpan, (float)leftHalf1, rightSpan, rightValue); return(true); } if (rightValue is KalkHalf rightHalf1) { result = (KalkHalf)(float)ScriptBinaryExpression.Evaluate(context, span, op, leftSpan, leftValue, rightSpan, rightHalf1); return(true); } return(false); }
public static string ToText(this ScriptBinaryOperator op) { switch (op) { case ScriptBinaryOperator.Add: return("+"); case ScriptBinaryOperator.Substract: // The substract operator requires to be separated by space return("-"); case ScriptBinaryOperator.Divide: return("/"); case ScriptBinaryOperator.DivideRound: return("//"); case ScriptBinaryOperator.Multiply: return("*"); case ScriptBinaryOperator.Modulus: return("%"); case ScriptBinaryOperator.RangeInclude: return(".."); case ScriptBinaryOperator.RangeExclude: return("..<"); case ScriptBinaryOperator.CompareEqual: return("=="); case ScriptBinaryOperator.CompareNotEqual: return("!="); case ScriptBinaryOperator.CompareLessOrEqual: return("<="); case ScriptBinaryOperator.CompareGreaterOrEqual: return(">="); case ScriptBinaryOperator.CompareLess: return("<"); case ScriptBinaryOperator.CompareGreater: return(">"); case ScriptBinaryOperator.And: return("&&"); case ScriptBinaryOperator.Or: return("||"); case ScriptBinaryOperator.EmptyCoalescing: return("??"); case ScriptBinaryOperator.ShiftLeft: return("<<"); case ScriptBinaryOperator.ShiftRight: return(">>"); case ScriptBinaryOperator.LiquidContains: return("| string.contains "); case ScriptBinaryOperator.LiquidStartsWith: return("| string.starts_with "); case ScriptBinaryOperator.LiquidEndsWith: return("| string.ends_with "); case ScriptBinaryOperator.LiquidHasKey: return("| object.has_key "); case ScriptBinaryOperator.LiquidHasValue: return("| object.has_value "); default: throw new ArgumentOutOfRangeException(nameof(op)); } }
public static object Evaluate(TemplateContext context, SourceSpan span, ScriptBinaryOperator op, object leftValue, object rightValue) { if (op == ScriptBinaryOperator.EmptyCoalescing) { return(leftValue ?? rightValue); } switch (op) { case ScriptBinaryOperator.ShiftLeft: var leftList = leftValue as IList; if (leftList != null) { var newList = new ScriptArray(leftList) { rightValue }; return(newList); } break; case ScriptBinaryOperator.ShiftRight: var rightList = rightValue as IList; if (rightList != null) { var newList = new ScriptArray(rightList); newList.Insert(0, leftValue); return(newList); } break; case ScriptBinaryOperator.LiquidHasKey: { var leftDict = leftValue as IDictionary <string, object>; if (leftDict != null) { return(ObjectFunctions.HasKey(leftDict, context.ToString(span, rightValue))); } } break; case ScriptBinaryOperator.LiquidHasValue: { var leftDict = leftValue as IDictionary <string, object>; if (leftDict != null) { return(ObjectFunctions.HasValue(leftDict, context.ToString(span, rightValue))); } } break; case ScriptBinaryOperator.CompareEqual: case ScriptBinaryOperator.CompareNotEqual: case ScriptBinaryOperator.CompareGreater: case ScriptBinaryOperator.CompareLess: case ScriptBinaryOperator.CompareGreaterOrEqual: case ScriptBinaryOperator.CompareLessOrEqual: case ScriptBinaryOperator.Add: case ScriptBinaryOperator.Substract: case ScriptBinaryOperator.Multiply: case ScriptBinaryOperator.Divide: case ScriptBinaryOperator.DivideRound: case ScriptBinaryOperator.Modulus: case ScriptBinaryOperator.RangeInclude: case ScriptBinaryOperator.RangeExclude: case ScriptBinaryOperator.LiquidContains: case ScriptBinaryOperator.LiquidStartsWith: case ScriptBinaryOperator.LiquidEndsWith: if (leftValue is string || rightValue is string) { return(CalculateToString(context, span, op, leftValue, rightValue)); } else if (leftValue == EmptyScriptObject.Default || rightValue == EmptyScriptObject.Default) { return(CalculateEmpty(context, span, op, leftValue, rightValue)); } else { return(CalculateOthers(context, span, op, leftValue, rightValue)); } } throw new ScriptRuntimeException(span, $"Operator `{op.ToText()}` is not implemented for `{leftValue}` and `{rightValue}`"); }
public bool TryEvaluate(TemplateContext context, SourceSpan span, ScriptBinaryOperator op, SourceSpan leftSpan, object leftValue, SourceSpan rightSpan, object rightValue, out object result) { var leftExpr = leftValue as KalkExpression; if (leftExpr is null && !KalkValue.IsNumber(leftValue)) { throw new ScriptRuntimeException(leftSpan, "Expecting a number, vector or matrix"); } var rightExpr = rightValue as KalkExpression; if (rightExpr is null && !KalkValue.IsNumber(rightValue)) { throw new ScriptRuntimeException(rightSpan, "Expecting a number, vector or matrix"); } result = null; switch (op) { case ScriptBinaryOperator.CompareEqual: case ScriptBinaryOperator.CompareNotEqual: case ScriptBinaryOperator.CompareLessOrEqual: case ScriptBinaryOperator.CompareGreaterOrEqual: case ScriptBinaryOperator.CompareLess: case ScriptBinaryOperator.CompareGreater: if (leftExpr != null && rightExpr != null) { var leftSimplifier = new KalkExpressionSimplifier(context); var(leftValueMultiplier, newLeftExpr) = leftSimplifier.Canonical(leftExpr); var rightSimplifier = new KalkExpressionSimplifier(context); var(rightValueMultiplier, newRightExpr) = rightSimplifier.Canonical(rightExpr); var exprEquals = Equals(context, newLeftExpr, newRightExpr); if (exprEquals) { var almostEqual = KalkValue.AlmostEqual(leftValueMultiplier, rightValueMultiplier); switch (op) { case ScriptBinaryOperator.CompareEqual: result = almostEqual; break; case ScriptBinaryOperator.CompareNotEqual: result = !almostEqual; break; case ScriptBinaryOperator.CompareLessOrEqual: result = almostEqual || ScriptBinaryExpression.Evaluate(context, span, op, leftSpan, leftValueMultiplier, rightSpan, rightValueMultiplier) is bool v1 && v1; break; case ScriptBinaryOperator.CompareGreaterOrEqual: result = almostEqual || ScriptBinaryExpression.Evaluate(context, span, op, leftSpan, leftValueMultiplier, rightSpan, rightValueMultiplier) is bool v2 && v2; break; case ScriptBinaryOperator.CompareLess: result = ScriptBinaryExpression.Evaluate(context, span, op, leftSpan, leftValueMultiplier, rightSpan, rightValueMultiplier) is bool v3 && v3; break; case ScriptBinaryOperator.CompareGreater: result = ScriptBinaryExpression.Evaluate(context, span, op, leftSpan, leftValueMultiplier, rightSpan, rightValueMultiplier) is bool v4 && v4; break; } } else { result = op == ScriptBinaryOperator.CompareNotEqual; } } else { if (op == ScriptBinaryOperator.CompareEqual || op == ScriptBinaryOperator.CompareNotEqual) { result = op == ScriptBinaryOperator.CompareNotEqual; } else { throw NotMatching(leftSpan, leftExpr, rightSpan, rightExpr); } } return(true); case ScriptBinaryOperator.Multiply: case ScriptBinaryOperator.Divide: case ScriptBinaryOperator.Power: { if (op != ScriptBinaryOperator.Power || rightExpr == null) { var simplifier = new KalkExpressionSimplifier(context); var(valueMul, valueExpr) = simplifier.Canonical(new KalkBinaryExpression(leftValue, op, rightValue)); var valueBinExpr = valueExpr as KalkBinaryExpression; result = KalkValue.AlmostEqual(valueMul, 1.0) && valueBinExpr == null ? valueExpr ?? (object)1.0 : valueExpr == null ? valueMul : (object)new KalkBinaryExpression(valueMul, ScriptBinaryOperator.Multiply, valueExpr) { OriginalExpression = new KalkBinaryExpression(leftValue, op, rightValue) }; return(true); } else { throw new ScriptRuntimeException(rightSpan, "Cannot use a unit as an exponent."); } } case ScriptBinaryOperator.Add: case ScriptBinaryOperator.Substract: if (leftExpr != null && rightExpr != null) { var leftSimplifier = new KalkExpressionSimplifier(context); var(leftValueMultiplier, newLeftExpr) = leftSimplifier.Canonical(leftExpr); var rightSimplifier = new KalkExpressionSimplifier(context); var(rightValueMultiplier, newRightExpr) = rightSimplifier.Canonical(rightExpr); if (!Equals(context, newLeftExpr, newRightExpr)) { throw new ScriptRuntimeException(span, $"Cannot {(op == ScriptBinaryOperator.Add ? "add" : "subtract")} the expression. Units are not matching. The left expression with unit `{newLeftExpr}` is not matching the right expression with unit `{newRightExpr}`."); } result = new KalkBinaryExpression(ScriptBinaryExpression.Evaluate(context, span, op, leftSpan, leftValueMultiplier, rightSpan, rightValueMultiplier), ScriptBinaryOperator.Multiply, newLeftExpr) { OriginalExpression = new KalkBinaryExpression(leftValue, op, rightValue) }; } else { throw NotMatching(leftSpan, leftExpr, rightSpan, rightExpr); } return(true); } return(false); }
private object Calculate(ScriptBinaryOperator op, object leftValue, Type leftType, object rightValue, Type rightType) { var customType = leftValue as IScriptCustomType ?? rightValue as IScriptCustomType; if (customType != null) { return customType.EvaluateBinaryExpression(this, leftValue, rightValue); } // The order matters: double, float, long, int if (leftType == typeof(double)) { var rightDouble = (double)ScriptValueConverter.ToObject(Span, rightValue, typeof(double)); return Calculate(op, (double)leftValue, rightDouble); } if (rightType == typeof(double)) { var leftDouble = (double)ScriptValueConverter.ToObject(Span, leftValue, typeof(double)); return Calculate(op, leftDouble, (double)rightValue); } if (leftType == typeof(float)) { var rightFloat = (float)ScriptValueConverter.ToObject(Span, rightValue, typeof(float)); return Calculate(op, (float)leftValue, rightFloat); } if (rightType == typeof(float)) { var leftFloat = (float)ScriptValueConverter.ToObject(Span, leftValue, typeof(float)); return Calculate(op, leftFloat, (float)rightValue); } if (leftType == typeof(long)) { var rightLong = (long)ScriptValueConverter.ToObject(Span, rightValue, typeof(long)); return Calculate(op, (long)leftValue, rightLong); } if (rightType == typeof(long)) { var leftLong = (long)ScriptValueConverter.ToObject(Span, leftValue, typeof(long)); return Calculate(op, leftLong, (long)rightValue); } if (leftType == typeof (int) || (leftType != null && leftType.GetTypeInfo().IsEnum)) { var rightInt = (int) ScriptValueConverter.ToObject(Span, rightValue, typeof (int)); return Calculate(op, (int) leftValue, rightInt); } if (rightType == typeof (int) || (rightType != null && rightType.GetTypeInfo().IsEnum)) { var leftInt = (int) ScriptValueConverter.ToObject(Span, leftValue, typeof (int)); return Calculate(op, leftInt, (int) rightValue); } throw new ScriptRuntimeException(Span, $"Unsupported types [{leftValue ?? "null"}/{leftType?.ToString() ?? "null"}] {op.ToText()} [{rightValue ?? "null"}/{rightType?.ToString() ?? "null"}] for binary operation"); }
private object Calculate(ScriptBinaryOperator op, float left, float right) { switch (op) { case ScriptBinaryOperator.Add: return left + right; case ScriptBinaryOperator.Substract: return left - right; case ScriptBinaryOperator.Multiply: return left * right; case ScriptBinaryOperator.Divide: return (float)left / right; case ScriptBinaryOperator.DivideRound: return (double)(int)(left / right); case ScriptBinaryOperator.Modulus: return left % right; case ScriptBinaryOperator.CompareEqual: return left == right; case ScriptBinaryOperator.CompareNotEqual: return left != right; case ScriptBinaryOperator.CompareGreater: return left > right; case ScriptBinaryOperator.CompareLess: return left < right; case ScriptBinaryOperator.CompareGreaterOrEqual: return left >= right; case ScriptBinaryOperator.CompareLessOrEqual: return left <= right; } throw new ScriptRuntimeException(Span, $"The operator [{op.ToText()}] is not implemented for float<->float"); }
public static TokenType ToTokenType(this ScriptBinaryOperator op) { switch (op) { case ScriptBinaryOperator.Add: return(TokenType.Plus); case ScriptBinaryOperator.Substract: return(TokenType.Minus); case ScriptBinaryOperator.Divide: return(TokenType.Divide); case ScriptBinaryOperator.DivideRound: return(TokenType.DoubleDivide); case ScriptBinaryOperator.Multiply: return(TokenType.Asterisk); case ScriptBinaryOperator.Modulus: return(TokenType.Percent); case ScriptBinaryOperator.RangeInclude: return(TokenType.DoubleDot); case ScriptBinaryOperator.RangeExclude: return(TokenType.DoubleDotLess); case ScriptBinaryOperator.CompareEqual: return(TokenType.DoubleEqual); case ScriptBinaryOperator.CompareNotEqual: return(TokenType.ExclamationEqual); case ScriptBinaryOperator.CompareLessOrEqual: return(TokenType.LessEqual); case ScriptBinaryOperator.CompareGreaterOrEqual: return(TokenType.GreaterEqual); case ScriptBinaryOperator.CompareLess: return(TokenType.Less); case ScriptBinaryOperator.CompareGreater: return(TokenType.Greater); case ScriptBinaryOperator.And: return(TokenType.DoubleAmp); case ScriptBinaryOperator.Or: return(TokenType.DoubleVerticalBar); case ScriptBinaryOperator.EmptyCoalescing: return(TokenType.DoubleQuestion); case ScriptBinaryOperator.ShiftLeft: return(TokenType.DoubleLessThan); case ScriptBinaryOperator.ShiftRight: return(TokenType.DoubleGreaterThan); case ScriptBinaryOperator.Power: return(TokenType.Caret); case ScriptBinaryOperator.BinaryAnd: return(TokenType.Amp); case ScriptBinaryOperator.BinaryOr: return(TokenType.VerticalBar); default: throw new ArgumentOutOfRangeException(nameof(op)); } }
public static ScriptToken ToToken(this ScriptBinaryOperator op) { return(new ScriptToken(op.ToTokenType())); }
private static int GetOperatorPrecedence(ScriptBinaryOperator op) { switch (op) { case ScriptBinaryOperator.EmptyCoalescing: return 20; case ScriptBinaryOperator.ShiftLeft: case ScriptBinaryOperator.ShiftRight: return 25; case ScriptBinaryOperator.Or: return 30; case ScriptBinaryOperator.And: return 40; case ScriptBinaryOperator.CompareEqual: case ScriptBinaryOperator.CompareNotEqual: return 50; case ScriptBinaryOperator.CompareLess: case ScriptBinaryOperator.CompareLessOrEqual: case ScriptBinaryOperator.CompareGreater: case ScriptBinaryOperator.CompareGreaterOrEqual: return 60; case ScriptBinaryOperator.Add: case ScriptBinaryOperator.Substract: return 70; case ScriptBinaryOperator.Multiply: case ScriptBinaryOperator.Divide: case ScriptBinaryOperator.DivideRound: case ScriptBinaryOperator.Modulus: return 80; case ScriptBinaryOperator.RangeInclude: case ScriptBinaryOperator.RangeExclude: return 90; default: return 0; } }
private object CalculateToString(ScriptBinaryOperator op, object left, object right) { switch (op) { case ScriptBinaryOperator.Add: return ScriptValueConverter.ToString(Span, left) + ScriptValueConverter.ToString(Span, right); case ScriptBinaryOperator.Multiply: if (right is int) { var temp = left; left = right; right = temp; } if (left is int) { var rightText = ScriptValueConverter.ToString(Span, right); var builder = new StringBuilder(); for (int i = 0; i < (int) left; i++) { builder.Append(rightText); } return builder.ToString(); } throw new ScriptRuntimeException(Span, $"Operator [{op.ToText()}] is not supported for the expression [{this}]. Only working on string x int or int x string"); // unit test: 112-binary-string-error1.txt case ScriptBinaryOperator.CompareEqual: return ScriptValueConverter.ToString(Span, left) == ScriptValueConverter.ToString(Span, right); case ScriptBinaryOperator.CompareNotEqual: return ScriptValueConverter.ToString(Span, left) != ScriptValueConverter.ToString(Span, right); case ScriptBinaryOperator.CompareGreater: return ScriptValueConverter.ToString(Span, left).CompareTo(ScriptValueConverter.ToString(Span, right)) > 0; case ScriptBinaryOperator.CompareLess: return ScriptValueConverter.ToString(Span, left).CompareTo(ScriptValueConverter.ToString(Span, right)) < 0; case ScriptBinaryOperator.CompareGreaterOrEqual: return ScriptValueConverter.ToString(Span, left).CompareTo(ScriptValueConverter.ToString(Span, right)) >= 0; case ScriptBinaryOperator.CompareLessOrEqual: return ScriptValueConverter.ToString(Span, left).CompareTo(ScriptValueConverter.ToString(Span, right)) <= 0; } // unit test: 150-range-expression-error1.out.txt throw new ScriptRuntimeException(Span, $"Operator [{op.ToText()}] is not supported on string objects"); // unit test: 112-binary-string-error2.txt }
private static object CalculateOthers(TemplateContext context, SourceSpan span, ScriptBinaryOperator op, object leftValue, object rightValue) { // Both values are null, applies the relevant binary ops if (leftValue == null && rightValue == null) { switch (op) { case ScriptBinaryOperator.CompareEqual: return(true); case ScriptBinaryOperator.CompareNotEqual: return(false); case ScriptBinaryOperator.CompareGreater: case ScriptBinaryOperator.CompareLess: case ScriptBinaryOperator.CompareGreaterOrEqual: case ScriptBinaryOperator.CompareLessOrEqual: return(false); case ScriptBinaryOperator.Add: case ScriptBinaryOperator.Substract: case ScriptBinaryOperator.Multiply: case ScriptBinaryOperator.Divide: case ScriptBinaryOperator.DivideRound: case ScriptBinaryOperator.Modulus: case ScriptBinaryOperator.RangeInclude: case ScriptBinaryOperator.RangeExclude: return(null); case ScriptBinaryOperator.LiquidContains: case ScriptBinaryOperator.LiquidStartsWith: case ScriptBinaryOperator.LiquidEndsWith: return(false); } return(null); } // One value is null if (leftValue == null || rightValue == null) { switch (op) { case ScriptBinaryOperator.CompareEqual: case ScriptBinaryOperator.CompareNotEqual: case ScriptBinaryOperator.CompareGreater: case ScriptBinaryOperator.CompareLess: case ScriptBinaryOperator.CompareGreaterOrEqual: case ScriptBinaryOperator.CompareLessOrEqual: case ScriptBinaryOperator.LiquidContains: case ScriptBinaryOperator.LiquidStartsWith: case ScriptBinaryOperator.LiquidEndsWith: return(false); case ScriptBinaryOperator.Add: case ScriptBinaryOperator.Substract: case ScriptBinaryOperator.Multiply: case ScriptBinaryOperator.Divide: case ScriptBinaryOperator.DivideRound: case ScriptBinaryOperator.Modulus: case ScriptBinaryOperator.RangeInclude: case ScriptBinaryOperator.RangeExclude: return(null); } return(null); } var leftType = leftValue.GetType(); var rightType = rightValue.GetType(); // The order matters: decimal, double, float, long, int if (leftType == typeof(decimal)) { var rightDecimal = (decimal)context.ToObject(span, rightValue, typeof(decimal)); return(CalculateDecimal(op, span, (decimal)leftValue, rightDecimal)); } if (rightType == typeof(decimal)) { var leftDecimal = (decimal)context.ToObject(span, leftValue, typeof(decimal)); return(CalculateDecimal(op, span, leftDecimal, (decimal)rightValue)); } if (leftType == typeof(double)) { var rightDouble = (double)context.ToObject(span, rightValue, typeof(double)); return(CalculateDouble(op, span, (double)leftValue, rightDouble)); } if (rightType == typeof(double)) { var leftDouble = (double)context.ToObject(span, leftValue, typeof(double)); return(CalculateDouble(op, span, leftDouble, (double)rightValue)); } if (leftType == typeof(float)) { var rightFloat = (float)context.ToObject(span, rightValue, typeof(float)); return(CalculateFloat(op, span, (float)leftValue, rightFloat)); } if (rightType == typeof(float)) { var leftFloat = (float)context.ToObject(span, leftValue, typeof(float)); return(CalculateFloat(op, span, leftFloat, (float)rightValue)); } if (leftType == typeof(long)) { var rightLong = (long)context.ToObject(span, rightValue, typeof(long)); return(CalculateLong(op, span, (long)leftValue, rightLong)); } if (rightType == typeof(long)) { var leftLong = (long)context.ToObject(span, leftValue, typeof(long)); return(CalculateLong(op, span, leftLong, (long)rightValue)); } if (leftType == typeof(int) || (leftType != null && leftType.GetTypeInfo().IsEnum)) { var rightInt = (int)context.ToObject(span, rightValue, typeof(int)); return(CalculateInt(op, span, (int)leftValue, rightInt)); } if (rightType == typeof(int) || (rightType != null && rightType.GetTypeInfo().IsEnum)) { var leftInt = (int)context.ToObject(span, leftValue, typeof(int)); return(CalculateInt(op, span, leftInt, (int)rightValue)); } if (leftType == typeof(bool)) { var rightBool = (bool)context.ToObject(span, rightValue, typeof(bool)); return(CalculateBool(op, span, (bool)leftValue, rightBool)); } if (rightType == typeof(bool)) { var leftBool = (bool)context.ToObject(span, leftValue, typeof(bool)); return(CalculateBool(op, span, leftBool, (bool)rightValue)); } if (leftType == typeof(DateTime) && rightType == typeof(DateTime)) { return(CalculateDateTime(op, span, (DateTime)leftValue, (DateTime)rightValue)); } if (leftType == typeof(DateTime) && rightType == typeof(TimeSpan)) { return(CalculateDateTime(op, span, (DateTime)leftValue, (TimeSpan)rightValue)); } //allows to check equality for objects with not only primitive types if (op == ScriptBinaryOperator.CompareEqual) { return(leftValue.Equals(rightValue)); } throw new ScriptRuntimeException(span, $"Unsupported types `{leftValue}/{leftType}` {op.ToText()} `{rightValue}/{rightType}` for binary operation"); }
private static object CalculateToString(TemplateContext context, SourceSpan span, ScriptBinaryOperator op, object left, object right) { switch (op) { case ScriptBinaryOperator.Add: return(context.ToString(span, left) + context.ToString(span, right)); case ScriptBinaryOperator.Multiply: if (right is int) { var temp = left; left = right; right = temp; } if (left is int) { var rightText = context.ToString(span, right); var builder = new StringBuilder(); for (int i = 0; i < (int)left; i++) { builder.Append(rightText); } return(builder.ToString()); } throw new ScriptRuntimeException(span, $"Operator `{op.ToText()}` is not supported for the expression. Only working on string x int or int x string"); // unit test: 112-binary-string-error1.txt case ScriptBinaryOperator.CompareEqual: return(context.ToString(span, left) == context.ToString(span, right)); case ScriptBinaryOperator.CompareNotEqual: return(context.ToString(span, left) != context.ToString(span, right)); case ScriptBinaryOperator.CompareGreater: return(context.ToString(span, left).CompareTo(context.ToString(span, right)) > 0); case ScriptBinaryOperator.CompareLess: return(context.ToString(span, left).CompareTo(context.ToString(span, right)) < 0); case ScriptBinaryOperator.CompareGreaterOrEqual: return(context.ToString(span, left).CompareTo(context.ToString(span, right)) >= 0); case ScriptBinaryOperator.CompareLessOrEqual: return(context.ToString(span, left).CompareTo(context.ToString(span, right)) <= 0); case ScriptBinaryOperator.LiquidContains: return(context.ToString(span, left).Contains(context.ToString(span, right))); case ScriptBinaryOperator.LiquidStartsWith: return(context.ToString(span, left).StartsWith(context.ToString(span, right))); case ScriptBinaryOperator.LiquidEndsWith: return(context.ToString(span, left).EndsWith(context.ToString(span, right))); } // unit test: 150-range-expression-error1.out.txt throw new ScriptRuntimeException(span, $"Operator `{op.ToText()}` is not supported on string objects"); // unit test: 112-binary-string-error2.txt }
private static object CalculateEmpty(TemplateContext context, SourceSpan span, ScriptBinaryOperator op, object leftValue, object rightValue) { var leftIsEmptyObject = leftValue == EmptyScriptObject.Default; var rightIsEmptyObject = rightValue == EmptyScriptObject.Default; // If both are empty, we return false or empty if (leftIsEmptyObject && rightIsEmptyObject) { switch (op) { case ScriptBinaryOperator.CompareEqual: case ScriptBinaryOperator.CompareGreaterOrEqual: case ScriptBinaryOperator.CompareLessOrEqual: return(true); case ScriptBinaryOperator.CompareNotEqual: case ScriptBinaryOperator.CompareGreater: case ScriptBinaryOperator.CompareLess: case ScriptBinaryOperator.LiquidContains: case ScriptBinaryOperator.LiquidStartsWith: case ScriptBinaryOperator.LiquidEndsWith: return(false); } return(EmptyScriptObject.Default); } var against = leftIsEmptyObject ? rightValue : leftValue; var againstEmpty = context.IsEmpty(span, against); switch (op) { case ScriptBinaryOperator.CompareEqual: return(againstEmpty); case ScriptBinaryOperator.CompareNotEqual: return(againstEmpty is bool? !(bool)againstEmpty : againstEmpty); case ScriptBinaryOperator.CompareGreater: case ScriptBinaryOperator.CompareLess: return(false); case ScriptBinaryOperator.CompareGreaterOrEqual: case ScriptBinaryOperator.CompareLessOrEqual: return(againstEmpty); case ScriptBinaryOperator.Add: case ScriptBinaryOperator.Substract: case ScriptBinaryOperator.Multiply: case ScriptBinaryOperator.Divide: case ScriptBinaryOperator.DivideRound: case ScriptBinaryOperator.Modulus: case ScriptBinaryOperator.RangeInclude: case ScriptBinaryOperator.RangeExclude: return(EmptyScriptObject.Default); case ScriptBinaryOperator.LiquidContains: case ScriptBinaryOperator.LiquidStartsWith: case ScriptBinaryOperator.LiquidEndsWith: return(false); } throw new ScriptRuntimeException(span, $"Operator `{op.ToText()}` is not implemented for `{(leftIsEmptyObject ? "empty" : leftValue)}` / `{(rightIsEmptyObject ? "empty" : rightValue)}`"); }