using Expressive; using Expressive.Expressions; using Expressive.Functions; using InABox.Clients; using JetBrains.Annotations; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; namespace InABox.Core { using FncType = Func, object?>; public class CoreExpressionFunction { public string Group { get; set; } public string Name { get; set; } public List Parameters { get; set; } public FncType Function { get; set; } public CoreExpressionFunction(string group, string name, FncType function, List parameters) { Group = group; Name = name; Parameters = parameters; Function = function; } } public interface IExpressionModel { } public interface IExpressionModel : IExpressionModel { } public class CoreExpression { private Expression Expression; public IReadOnlyCollection ReferencedVariables => Expression.ReferencedVariables; protected virtual Type? ReturnType { get; } public CoreExpression(string expressionString) { Expression = new Expression(expressionString); foreach (var function in Functions) { Expression.RegisterFunction(function.Name, function.Function); } } public object? Evaluate(Dictionary? variables) { var result = Expression.Evaluate(variables); if(ReturnType != null) { return CoreUtils.ChangeType(result, ReturnType); } return result; } public static List GetModelVariables(Type modelType) { return CoreUtils.PropertyList(modelType, x => true).Select(x => x.Name).ToList(); } public static List GetModelVariables() where TModel : IExpressionModel => GetModelVariables(typeof(TModel)); #region Static public static List Functions = new List(); static CoreExpression() { RegisterFunction("String", "Format", (p, v) => { if (!(p[0].Evaluate(v) is string format)) throw new Exception("No format string given for Format()"); return string.Format(format, p.Skip(1).Select(x => x.Evaluate(v)).ToArray()); }, "fmt", "..."); RegisterFunction("Other", "Client_LoadDocument", Fnc_LoadDocument, "docID"); } private static object? Fnc_LoadDocument(IExpression[] p, IDictionary v) { var id = p[0].Evaluate(v); if (id is null) return null; if (!(id is Guid docID)) return null; return new Client() .Query( new Filter(x => x.ID).IsEqualTo(docID), new Columns(x => x.Data)) .Rows.FirstOrDefault() ?.Get(x => x.Data); } public static void RegisterFunction(string group, string name, FncType function, params string[] parameters) => Functions.Add(new CoreExpressionFunction(group, name, function, parameters.ToList())); #endregion } public class CoreExpression : CoreExpression where TModel : IExpressionModel { protected override Type? ReturnType => typeof(TReturn); public CoreExpression(string expressionString): base(expressionString) { } [return: MaybeNull] public new TReturn Evaluate(Dictionary? variables) { var result = base.Evaluate(variables); if(result is TReturn ret) { return ret; } return default; } [return: MaybeNull] public TReturn Evaluate(TModel model) { var values = new Dictionary(); foreach(var variable in ReferencedVariables) { values[variable] = CoreUtils.GetPropertyValue(model, variable); } var result = base.Evaluate(values); if(result is TReturn ret) { return ret; } return default; } } }