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 string? Description { get; set; } public List Parameters { get; set; } public FncType Function { get; set; } public class Parameter { public string Name { get; set; } public string Type { get; set; } public Parameter(string name, string type) { Name = name; Type = type; } } public CoreExpressionFunction(string group, string name, FncType function, List parameters, string? description = null) { Group = group; Name = name; Parameters = parameters; Function = function; Description = description; } } 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()); }, "string fmt", "..."); RegisterFunction("Other", "Client_LoadDocument", "Loads a byte array from the database Document table, with the ID {docID}.", Fnc_LoadDocument, "Guid 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) => RegisterFunction(group, name, null, function, parameters); public static void RegisterFunction(string group, string name, string? description, FncType function, params string[] parameters) => Functions.Add(new CoreExpressionFunction(group, name, function, parameters.Select(x => { var parts = x.Split(' '); if (parts.Length == 1) return new CoreExpressionFunction.Parameter(parts[0], ""); return new CoreExpressionFunction.Parameter(parts[1], parts[0]); }).ToList(), description)); #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; } } }