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"); }
public object Evaluate(TemplateContext context, ScriptNode callerContext, ScriptArray parameters, ScriptBlockStatement blockStatement) { // Check parameters if ((hasObjectParams && parameters.Count < parametersInfo.Length - 1) || (!hasObjectParams && parameters.Count != parametersInfo.Length)) { throw new ScriptRuntimeException(callerContext.Span, $"Invalid number of arguments passed [{parameters.Count}] while expecting [{parametersInfo.Length}] for [{callerContext}]"); } // Convert arguments var arguments = new object[parametersInfo.Length]; object[] paramArguments = null; if (hasObjectParams) { paramArguments = new object[parameters.Count - lastParamsIndex]; arguments[lastParamsIndex] = paramArguments; } for (int i = 0; i < parameters.Count; i++) { var destType = hasObjectParams && i >= lastParamsIndex ? typeof(object) : parametersInfo[i].ParameterType; try { var argValue = ScriptValueConverter.ToObject(callerContext.Span, parameters[i], destType); if (hasObjectParams && i >= lastParamsIndex) { paramArguments[i - lastParamsIndex] = argValue; } else { arguments[i] = argValue; } } catch (Exception exception) { throw new ScriptRuntimeException(callerContext.Span, $"Unable to convert parameter #{i} of type [{parameters[i]?.GetType()}] to type [{destType}]", exception); } } // Call method try { var result = method.Invoke(target, arguments); return(result); } catch (Exception exception) { throw new ScriptRuntimeException(callerContext.Span, $"Unexpected exception when calling {callerContext}", exception); } }
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 }
public override void Evaluate(TemplateContext context) { var leftValueOriginal = context.Evaluate(Left); var leftValue = leftValueOriginal; var rightValueOriginal = context.Evaluate(Right); object rightValue = rightValueOriginal; if (Operator == ScriptBinaryOperator.EmptyCoalescing) { context.Result = leftValue ?? rightValue; return; } else if (Operator == ScriptBinaryOperator.And || Operator == ScriptBinaryOperator.Or) { var leftBoolValue = ScriptValueConverter.ToBool(leftValue); var rightBoolValue = ScriptValueConverter.ToBool(rightValue); if (Operator == ScriptBinaryOperator.And) { context.Result = leftBoolValue && rightBoolValue; } else { context.Result = leftBoolValue || rightBoolValue; } return; } else { switch (Operator) { case ScriptBinaryOperator.ShiftLeft: case ScriptBinaryOperator.ShiftRight: if (leftValue is IList || rightValue is IList) { // Special path for IList to allow custom binary expression context.Result = ScriptArray.CustomOperator.EvaluateBinaryExpression(this, leftValue, rightValue); return; } 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: var leftType = leftValue?.GetType(); var rightType = rightValue?.GetType(); if (leftValue is string || rightValue is string) { context.Result = CalculateToString(Operator, leftValue, rightValue); { // TODO: Log an error if CalculateToString return null? //context.LogError(Span, $"Operation [{Operator}] on strings not supported"); } } else { context.Result = Calculate(Operator, leftValue, leftType, rightValue, rightType); } return; } } throw new ScriptRuntimeException(Span, $"Operator [{Operator.ToText()}] is not implemented for the left [{Left}] / right [{Right}]"); }
private TKey TransformToKey(string member) { return((TKey)ScriptValueConverter.ToObject(new SourceSpan(), member, typeof(TKey))); }
public override void Evaluate(TemplateContext context) { switch (Operator) { case ScriptUnaryOperator.Not: { var value = context.Evaluate(Right); context.Result = !ScriptValueConverter.ToBool(value); } break; case ScriptUnaryOperator.Negate: case ScriptUnaryOperator.Plus: { var value = context.Evaluate(Right); bool negate = Operator == ScriptUnaryOperator.Negate; var customType = value as IScriptCustomType; if (customType != null) { context.Result = customType.EvaluateUnaryExpression(this); } else if (value != null) { if (value is int) { context.Result = negate ? -((int)value) : value; } else if (value is double) { context.Result = negate ? -((double)value) : value; } else if (value is float) { context.Result = negate ? -((float)value) : value; } else if (value is long) { context.Result = negate ? -((long)value) : value; } else { throw new ScriptRuntimeException(this.Span, $"Unexpected value [{value} / Type: {value?.GetType()}]. Cannot negate(-)/positive(+) a non-numeric value"); } } } break; case ScriptUnaryOperator.FunctionAlias: context.Result = context.Evaluate(Right, true); break; case ScriptUnaryOperator.FunctionParametersExpand: // Function parameters expand is done at the function level, so here, we simply return the actual list Right?.Evaluate(context); break; default: throw new ScriptRuntimeException(Span, $"Operator [{Operator}] is not supported"); } }