CoreExpression.cs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. using Expressive;
  2. using Expressive.Expressions;
  3. using Expressive.Functions;
  4. using InABox.Clients;
  5. using System.Diagnostics.CodeAnalysis;
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Linq;
  9. using System.Text;
  10. using System.Text.RegularExpressions;
  11. using Expressive.Exceptions;
  12. using Expressive.Operators;
  13. namespace InABox.Core
  14. {
  15. public interface IExpressionModel { }
  16. public interface IExpressionModel<TReturn> : IExpressionModel { }
  17. public abstract class CoreExpressionFunction : FunctionBase
  18. {
  19. public abstract string Group { get; }
  20. public abstract string Description { get; }
  21. public abstract string[] Parameters { get; }
  22. }
  23. public interface IExpressionConstant
  24. {
  25. string Name { get; set; }
  26. object? GetValue();
  27. }
  28. public class CoreExpressionFunctor : CoreExpressionFunction
  29. {
  30. public delegate object? EvaluateDelegate(IExpression[] parameters, IDictionary<string, object?> variables, Context context);
  31. public EvaluateDelegate Function { get; set; }
  32. private string _name;
  33. public override string Name => _name;
  34. private string _description;
  35. public override string Description => _description;
  36. private string _group;
  37. public override string Group => _group;
  38. private string[] _parameters;
  39. public override string[] Parameters => _parameters;
  40. private int minParameters;
  41. public CoreExpressionFunctor(EvaluateDelegate function, string name, string description, string group, string[] parameters, int? minParameters = null)
  42. {
  43. Function = function;
  44. _name = name;
  45. _description = description;
  46. _group = group;
  47. _parameters = parameters;
  48. minParameters = minParameters ?? parameters.Length;
  49. }
  50. public override object? Evaluate(IExpression[] parameters, Context context)
  51. {
  52. ValidateParameterCount(parameters, _parameters.Length, minParameters);
  53. return Function(parameters, Variables, context);
  54. }
  55. }
  56. internal class FormatFunction : CoreExpressionFunction
  57. {
  58. #region IFunction Members
  59. public override object Evaluate(IExpression[] parameters, Context context)
  60. {
  61. ValidateParameterCount(parameters, -1, 2);
  62. string fmt = parameters.First()?.Evaluate(Variables).ToString() ?? string.Empty;
  63. object[] objects = parameters.Skip(1).Select(x => x.Evaluate(Variables)).ToArray();
  64. return String.Format(fmt, objects);
  65. }
  66. public override string Group => "String";
  67. public override string Name => "Format";
  68. public override string Description => "Formats a list of objects using the specified format string";
  69. public override string[] Parameters => new[] { "string format", "object[] parameters" };
  70. #endregion
  71. }
  72. internal class Client_LoadDocumentFunction : CoreExpressionFunction
  73. {
  74. public override object? Evaluate(IExpression[] parameters, Context context)
  75. {
  76. ValidateParameterCount(parameters, 1, 1);
  77. var id = parameters[0].Evaluate(Variables);
  78. if (id is null)
  79. return null;
  80. if (!(id is Guid docID))
  81. return null;
  82. return new Client<Document>()
  83. .Query(
  84. new Filter<Document>(x => x.ID).IsEqualTo(docID),
  85. Columns.None<Document>().Add(x => x.Data))
  86. .Rows.FirstOrDefault()
  87. ?.Get<Document, byte[]>(x => x.Data);
  88. }
  89. public override string Group => "Other";
  90. public override string Name => "Client_LoadDocument";
  91. public override string Description => "Retrieves a database document with the specified id";
  92. public override string[] Parameters => new[] { "Guid id" };
  93. }
  94. public class CoreExpression
  95. {
  96. private Expression Expression;
  97. public bool IsValid
  98. {
  99. get
  100. {
  101. try
  102. {
  103. return Expression.ReferencedVariables != null;
  104. }
  105. catch
  106. {
  107. return false;
  108. }
  109. }
  110. }
  111. public string ExpressionString { get; set; }
  112. public IReadOnlyCollection<string> ReferencedVariables => Expression.ReferencedVariables;
  113. protected virtual Type? ReturnType { get; }
  114. public CoreExpression(string expressionString, Type? returnType = null)
  115. {
  116. Expression = new Expression(expressionString, _context);
  117. ExpressionString = expressionString;
  118. ReturnType = returnType;
  119. if (!IsValid)
  120. {
  121. var expr = "\"" + expressionString + "\"";
  122. var tags = new Regex(@"\[(.*?)\]").Matches(expressionString);
  123. foreach (var tag in tags)
  124. expr = expr.Replace($"{tag}", $"\"+{tag}+\"");
  125. expr = expr.Replace("+\"\"", "");
  126. Expression = new Expression(expr);
  127. }
  128. }
  129. public object? Evaluate(Dictionary<string, object?>? variables)
  130. {
  131. var result = Expression.Evaluate(variables);
  132. if(ReturnType != null)
  133. {
  134. return CoreUtils.ChangeType(result, ReturnType);
  135. }
  136. return result;
  137. }
  138. public object? Evaluate(IVariableProvider provider)
  139. {
  140. var result = Expression.Evaluate(provider);
  141. if(ReturnType != null)
  142. {
  143. return CoreUtils.ChangeType(result, ReturnType);
  144. }
  145. return result;
  146. }
  147. [return: MaybeNull]
  148. public T Evaluate<T>(IVariableProvider provider)
  149. {
  150. var result = Evaluate(provider);
  151. if(result is T ret)
  152. {
  153. return ret;
  154. }
  155. return default;
  156. }
  157. public Result<T, Exception> TryEvaluate<T>(IVariableProvider provider)
  158. {
  159. try
  160. {
  161. var result = Evaluate(provider);
  162. if(result is T ret)
  163. {
  164. return Result.Ok(ret);
  165. }
  166. return Result.Ok<T>(default);
  167. }
  168. catch (Exception e)
  169. {
  170. return Result.Error(e);
  171. }
  172. }
  173. public static List<string> GetModelVariables(Type modelType)
  174. {
  175. var props = DatabaseSchema.Properties(modelType).Select(x => x.Name).ToList();
  176. props.Sort();
  177. return props;
  178. }
  179. public static List<string> GetModelVariables<TModel>() where TModel : IExpressionModel
  180. => GetModelVariables(typeof(TModel));
  181. #region Static
  182. public static List<CoreExpressionFunction> Functions = new List<CoreExpressionFunction>();
  183. private static Context _context = new Context(ExpressiveOptions.None);
  184. public static void RegisterFunction<T>() where T : CoreExpressionFunction, new ()
  185. {
  186. var function = new T();
  187. Functions.Add(function);
  188. _context.RegisterFunction(function);
  189. }
  190. public static void RegisterFunction(string name, string description, string group, string[] parameters, CoreExpressionFunctor.EvaluateDelegate function)
  191. {
  192. var f = new CoreExpressionFunctor(function, name, description, group, parameters);
  193. Functions.Add(f);
  194. _context.RegisterFunction(f);
  195. }
  196. static CoreExpression()
  197. {
  198. RegisterFunction<FormatFunction>();
  199. RegisterFunction<Client_LoadDocumentFunction>();
  200. DateFunctions.Register();
  201. ArrayFunctions.Register();
  202. }
  203. #endregion
  204. }
  205. public class CoreExpression<TModel, TReturn> : CoreExpression
  206. where TModel : IExpressionModel<TReturn>
  207. {
  208. protected override Type? ReturnType => typeof(TReturn);
  209. public CoreExpression(string expressionString): base(expressionString) { }
  210. [return: MaybeNull]
  211. public new TReturn Evaluate(Dictionary<string, object?>? variables)
  212. {
  213. var result = base.Evaluate(variables);
  214. if(result is TReturn ret)
  215. {
  216. return ret;
  217. }
  218. return default;
  219. }
  220. [return: MaybeNull]
  221. public new TReturn Evaluate(IVariableProvider provider)
  222. {
  223. var result = base.Evaluate(provider);
  224. if(result is TReturn ret)
  225. {
  226. return ret;
  227. }
  228. return default;
  229. }
  230. public Result<TReturn, Exception> Evaluate(TModel model)
  231. {
  232. var values = new Dictionary<string, object?>();
  233. foreach(var variable in ReferencedVariables)
  234. {
  235. values[variable] = CoreUtils.GetPropertyValue(model, variable);
  236. }
  237. try
  238. {
  239. var result = base.Evaluate(values);
  240. if(result is TReturn ret)
  241. {
  242. return Result.Ok(ret);
  243. }
  244. return Result.Ok<TReturn>(default);
  245. }
  246. catch (Exception e)
  247. {
  248. return Result.Error(e);
  249. }
  250. }
  251. }
  252. public static class CoreExpressionExtensions
  253. {
  254. public static T Evaluate<T>(this IExpression expression, IDictionary<string, object?>? variables)
  255. {
  256. var result = expression.Evaluate(variables);
  257. return CoreUtils.ChangeType<T>(result);
  258. }
  259. }
  260. }