using Flee.PublicTypes; using System.Collections.Generic; using System; using System.Linq; namespace Convention.RScript { public static class ExpressionMath { public static int Sign(double value, double accuracy = ExpressionExtension.DefaultDoubleAccuracy) { if (value.IsCloseToZero(accuracy)) return 0; return value > 0 ? 1 : -1; } public static int Compare(double value1, double value2, double accuracy = ExpressionExtension.DefaultDoubleAccuracy) { if (value1.IsClose(value2, accuracy)) return 0; return value1 > value2 ? 1 : -1; } public static bool IsBetween(double value, double minValue, double maxValue, double accuracy = ExpressionExtension.DefaultDoubleAccuracy) { return Compare(value, minValue, accuracy) >= 0 && Compare(value, maxValue, accuracy) <= 0; } public static bool IsBetweenExclusive(double value, double minValue, double maxValue, double accuracy = ExpressionExtension.DefaultDoubleAccuracy) { return Compare(value, minValue, accuracy) > 0 && Compare(value, maxValue, accuracy) < 0; } public static int ToInt(double value) { return (int)value; } public static int ToInt(float value) { return (int)value; } public static int ToInt(int value) { return (int)value; } public static double ToDouble(float value) { return (double)value; } public static double ToDouble(int value) { return (double)value; } public static double ToDouble(double value) { return (double)value; } } public static class ExpressionExtension { public const double DefaultDoubleAccuracy = 1e-7; public static bool IsClose(this double value1, double value2, double maximumAbsoluteError = DefaultDoubleAccuracy) { if (double.IsInfinity(value1) || double.IsInfinity(value2)) { return Equals(value1, value2); } if (double.IsNaN(value1) || double.IsNaN(value2)) { return false; } var delta = value1 - value2; return !(delta > maximumAbsoluteError || delta < -maximumAbsoluteError); } public static bool IsCloseToZero(this double value, double maximumAbsoluteError = DefaultDoubleAccuracy) { return !(double.IsInfinity(value) || double.IsNaN(value) || value > maximumAbsoluteError || value < -maximumAbsoluteError); } } } namespace Convention.RScript.Parser { public class ExpressionParser { public readonly ExpressionContext context; public ExpressionParser(ExpressionContext context) { this.context = context; } private readonly Dictionary CompileGenericExpressionTypen = new(); private readonly Dictionary CompileGenericExpression = new(); private readonly Dictionary CompileDynamicExpression = new(); public void ClearCache() { CompileGenericExpression.Clear(); CompileDynamicExpression.Clear(); } public T Evaluate(string expression) { if (CompileGenericExpression.TryGetValue(expression, out var result)) { return (result as IGenericExpression).Evaluate(); } return Compile(expression).Evaluate(); } public object Evaluate(string expression) { if (CompileDynamicExpression.TryGetValue(expression, out var result)) { return result.Evaluate(); } return Compile(expression).Evaluate(); } public IGenericExpression Compile(string expression) { var compile = context.CompileGeneric(expression); CompileGenericExpression[expression] = compile; CompileGenericExpressionTypen[expression] = typeof(T); return compile; } public IDynamicExpression Compile(string expression) { var compile = context.CompileDynamic(expression); CompileDynamicExpression[expression] = compile; return compile; } [Serializable] public struct SerializableParser { public Tuple[] CompileGenericExpression; public string[] CompileDynamicExpression; } public SerializableParser Serialize() { return new() { CompileGenericExpression = (from key in CompileGenericExpression.Keys select Tuple.Create(CompileGenericExpressionTypen[key].Name, key)).ToArray(), CompileDynamicExpression = CompileDynamicExpression.Keys.ToArray() }; } public void Deserialize(SerializableParser data) { foreach (var (type, expr) in data.CompileGenericExpression) { if (type == nameof(String)) { this.Compile(expr); } else if (type == nameof(Single)) { this.Compile(expr); } else if (type == nameof(Double)) { this.Compile(expr); } else if (type == nameof(Int32)) { this.Compile(expr); } else if (type == nameof(Boolean)) { this.Compile(expr); } else if (type == nameof(Object)) { this.Compile(expr); } else { throw new NotSupportedException($"Unsupported expression type: {type}"); } } foreach (var expr in data.CompileDynamicExpression) { this.Compile(expr); } } } }