using Expressive; using Expressive.Expressions; using Expressive.Functions; using InABox.Clients; using System.Diagnostics.CodeAnalysis; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; namespace InABox.Core { public interface IExpressionModel { } public interface IExpressionModel : IExpressionModel { } public abstract class CoreExpressionFunction : FunctionBase { public abstract string Group { get; } public abstract string Description { get; } public abstract string[] Parameters { get; } } internal class FormatFunction : CoreExpressionFunction { #region IFunction Members public override object Evaluate(IExpression[] parameters, Context context) { ValidateParameterCount(parameters,-1, 2); string fmt = parameters.First()?.Evaluate(Variables).ToString() ?? ""; object[] objects = parameters.Skip(1).Select(x => x.Evaluate(Variables)).ToArray(); return String.Format(fmt, objects); } public override string Group => "String"; public override string Name => "Format"; public override string Description => "Formats a list of objects using the specified format string"; public override string[] Parameters => new[] { "string format", "object[] parameters" }; #endregion } internal class Client_LoadDocumentFunction : CoreExpressionFunction { public override object? Evaluate(IExpression[] parameters, Context context) { ValidateParameterCount(parameters, 1, 1); var id = parameters[0].Evaluate(Variables); if (id is null) return null; if (!(id is Guid docID)) return null; return new Client() .Query( new Filter(x => x.ID).IsEqualTo(docID), Columns.None().Add(x => x.Data)) .Rows.FirstOrDefault() ?.Get(x => x.Data); } public override string Group => "Other"; public override string Name => "Client_LoadDocument"; public override string Description => "Retrieves a database document with the specified id"; public override string[] Parameters => new[] { "Guid id" }; } public class CoreExpression { private Expression Expression; public bool IsValid { get { try { return Expression.ReferencedVariables != null; } catch { return false; } } } public IReadOnlyCollection ReferencedVariables => Expression.ReferencedVariables; protected virtual Type? ReturnType { get; } public CoreExpression(string expressionString) { Expression = new Expression(expressionString, _context); if (!IsValid) { var expr = "\"" + expressionString + "\""; var tags = new Regex(@"\[(.*?)\]").Matches(expressionString); foreach (var tag in tags) expr = expr.Replace($"{tag}", $"\"+{tag}+\""); expr = expr.Replace("+\"\"", ""); Expression = new Expression(expr); } } 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(); private static Context _context = new Context(ExpressiveOptions.None); static void RegisterFunction() where T : CoreExpressionFunction, new () { var function = new T(); Functions.Add(function); _context.RegisterFunction(function); } static CoreExpression() { RegisterFunction(); RegisterFunction(); } #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; } } }