Skip to content

Commit

Permalink
Merge pull request #76 from delegateas/tst/metadata-function-reg
Browse files Browse the repository at this point in the history
feat!: Redesigned the way functions are resolved
  • Loading branch information
thygesteffensen authored Mar 18, 2022
2 parents d36b9de + a470181 commit d9d614b
Show file tree
Hide file tree
Showing 66 changed files with 400 additions and 524 deletions.
6 changes: 3 additions & 3 deletions ExpressionEngine/ExpressionGrammar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ public class ExpressionGrammar
private readonly Parser<IRule> _method;
private readonly Parser<Task<ValueContainer>> _input;

public ExpressionGrammar(IEnumerable<IFunction> functions, IEnumerable<IFunctionDefinition> functionDefinitions)
public ExpressionGrammar(IEnumerable<FunctionMetadata> functions, IEnumerable<IFunctionDefinition> functionDefinitions, IServiceProvider serviceProvider)
{
_functionDefinitions = functionDefinitions?.ToList();

var functionCollection = functions ?? throw new ArgumentNullException(nameof(functions));
var functionCollection = functions.ToList() ?? throw new ArgumentNullException(nameof(functions));

#region BasicAuxParsers

Expand Down Expand Up @@ -92,7 +92,7 @@ from args in argument.Token().DelimitedBy(Parse.Char(',')).Optional()
from mandatoryLetter in Parse.Letter
from rest in Parse.LetterOrDigit.Many().Text()
from args in arguments.Contained(lParenthesis, rParenthesis)
select new ExpressionRule(functionCollection, mandatoryLetter + rest,
select new ExpressionRule(functionCollection, serviceProvider, mandatoryLetter + rest,
args.IsEmpty
? null
: args.Get());
Expand Down
161 changes: 105 additions & 56 deletions ExpressionEngine/FlowRunnerDependencyExtension.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using ExpressionEngine.Functions.Base;
using System;
using ExpressionEngine.Functions.Base;
using ExpressionEngine.Functions.CustomException;
using ExpressionEngine.Functions.Implementations.CollectionFunctions;
using ExpressionEngine.Functions.Implementations.ConversionFunctions;
Expand All @@ -9,8 +10,15 @@

namespace ExpressionEngine
{
/// <summary>
/// Selection of extension methods
/// </summary>
public static class FlowRunnerDependencyExtension
{
/// <summary>
/// Add necessary dependencies inorder to use expression engine.
/// </summary>
/// <param name="services"><see cref="IServiceCollection"/> to add dependencies</param>
public static void AddExpressionEngine(this IServiceCollection services)
{
services.AddScoped<IExpressionEngine, ExpressionEngine>();
Expand All @@ -22,8 +30,49 @@ public static void AddExpressionEngine(this IServiceCollection services)
AddLogicalComparisonFunctions(services);
AddMathFunctions(services);

services.AddTransient<IFunction, LengthFunction>();
services.AddTransient<IFunction, GreaterFunction>();
services.RegisterTransientFunctionAlias<LengthFunction>("length");
services.RegisterTransientFunctionAlias<GreaterFunction>("greater");
}

/// <summary>
/// Register function to be used in expression, function implementation must implement <see cref="IFunction"/>.
/// </summary>
/// <param name="services">Services which to add function metadata</param>
/// <param name="functionName">name of function used to invoke it</param>
/// <typeparam name="T">Function implementation</typeparam>
public static void RegisterTransientFunctionAlias<T>(this IServiceCollection services, string functionName)
where T : class, IFunction
{
services.AddTransient<T>();
services.AddSingleton(new FunctionMetadata(typeof(T), functionName));
}

/// <summary>
/// Register function to be used in expression, function implementation must implement <see cref="IFunction"/>.
/// </summary>
/// <param name="services">Services which to add function metadata</param>
/// <param name="functionName">name of function used to invoke it</param>
/// <typeparam name="T">Function implementation</typeparam>
public static void RegisterScopedFunctionAlias<T>(this IServiceCollection services, string functionName)
where T : class, IFunction
{
services.AddScoped<T>();
services.AddSingleton(new FunctionMetadata(typeof(T), functionName));
}

/// <summary>
/// Register function to be used in expression, function implementation must implement <see cref="IFunction"/>.
/// </summary>
/// <param name="services">Services which to add function metadata</param>
/// <param name="functionName">name of function used to invoke it</param>
/// <param name="implementationFactory"></param>
/// <typeparam name="T">Function implementation</typeparam>
public static void RegisterScopedFunctionAlias<T>(this IServiceCollection services, string functionName,
Func<IServiceProvider, T> implementationFactory)
where T : class, IFunction
{
services.AddScoped(implementationFactory);
services.AddSingleton(new FunctionMetadata(typeof(T), functionName));
}

/// <summary>
Expand All @@ -44,75 +93,75 @@ public static void AddFunctionDefinition(this IServiceCollection services, strin

private static void AddStringFunctions(IServiceCollection services)
{
services.AddTransient<IFunction, ConcatFunction>();
services.AddTransient<IFunction, EndsWithFunction>();
services.AddTransient<IFunction, FormatNumberFunction>();
services.AddTransient<IFunction, GuidFunction>();
services.AddTransient<IFunction, IndexOfFunction>();
services.AddTransient<IFunction, LastIndexOfFunction>();
services.AddTransient<IFunction, LengthFunction>();
services.AddTransient<IFunction, ReplaceFunction>();
services.AddTransient<IFunction, SplitFunction>();
services.AddTransient<IFunction, StartsWithFunction>();
services.AddTransient<IFunction, SubstringFunction>();
services.AddTransient<IFunction, ToLowerFunction>();
services.AddTransient<IFunction, ToUpperFunction>();
services.AddTransient<IFunction, TrimFunction>();
services.RegisterTransientFunctionAlias<ConcatFunction>("concat");
services.RegisterTransientFunctionAlias<EndsWithFunction>("endsWith");
services.RegisterTransientFunctionAlias<FormatNumberFunction>("formatNumber");
services.RegisterTransientFunctionAlias<GuidFunction>("guid");
services.RegisterTransientFunctionAlias<IndexOfFunction>("indexOf");
services.RegisterTransientFunctionAlias<LastIndexOfFunction>("lastIndexOf");
services.RegisterTransientFunctionAlias<LengthFunction>("length");
services.RegisterTransientFunctionAlias<ReplaceFunction>("replace");
services.RegisterTransientFunctionAlias<SplitFunction>("split");
services.RegisterTransientFunctionAlias<StartsWithFunction>("startsWith");
services.RegisterTransientFunctionAlias<SubstringFunction>("substring");
services.RegisterTransientFunctionAlias<ToLowerFunction>("toLower");
services.RegisterTransientFunctionAlias<ToUpperFunction>("toUpper");
services.RegisterTransientFunctionAlias<TrimFunction>("trim");
}

private static void AddCollectionFunction(IServiceCollection services)
{
services.AddTransient<IFunction, ContainsFunction>();
services.AddTransient<IFunction, EmptyFunction>();
services.AddTransient<IFunction, FirstFunction>();
services.AddTransient<IFunction, InterSectionFunction>();
services.AddTransient<IFunction, JoinFunction>();
services.AddTransient<IFunction, LastFunction>();
services.AddTransient<IFunction, LengthFunction>();
services.AddTransient<IFunction, SkipFunction>();
services.AddTransient<IFunction, TakeFunction>();
services.AddTransient<IFunction, UnionFunction>();
services.RegisterTransientFunctionAlias<ContainsFunction>("contains");
services.RegisterTransientFunctionAlias<EmptyFunction>("empty");
services.RegisterTransientFunctionAlias<FirstFunction>("first");
services.RegisterTransientFunctionAlias<InterSectionFunction>("intersection");
services.RegisterTransientFunctionAlias<JoinFunction>("join");
services.RegisterTransientFunctionAlias<LastFunction>("last");
services.RegisterTransientFunctionAlias<LengthFunction>("length");
services.RegisterTransientFunctionAlias<SkipFunction>("skip");
services.RegisterTransientFunctionAlias<TakeFunction>("take");
services.RegisterTransientFunctionAlias<UnionFunction>("union");
}

private static void AddConversionFunction(IServiceCollection services)
{
services.AddTransient<IFunction, ArrayFunction>();
services.AddTransient<IFunction, Base64Function>();
services.AddTransient<IFunction, Base64ToBinaryFunction>();
services.AddTransient<IFunction, Base64ToStringFunction>();
services.AddTransient<IFunction, BinaryFunction>();
services.AddTransient<IFunction, BoolFunction>();
services.AddTransient<IFunction, CreateArrayFunction>();
services.AddTransient<IFunction, DataUriFunction>();
services.AddTransient<IFunction, DataUriToBinaryFunction>();
services.AddTransient<IFunction, FloatFunction>();
services.AddTransient<IFunction, IntFunction>();
services.RegisterTransientFunctionAlias<ArrayFunction>("array");
services.RegisterTransientFunctionAlias<Base64Function>("base64");
services.RegisterTransientFunctionAlias<Base64ToBinaryFunction>("base64ToBinary");
services.RegisterTransientFunctionAlias<Base64ToStringFunction>("base64ToString");
services.RegisterTransientFunctionAlias<BinaryFunction>("binary");
services.RegisterTransientFunctionAlias<BoolFunction>("bool");
services.RegisterTransientFunctionAlias<CreateArrayFunction>("createArray");
services.RegisterTransientFunctionAlias<DataUriFunction>("dataUri");
services.RegisterTransientFunctionAlias<DataUriToBinaryFunction>("dataUriToBinary");
services.RegisterTransientFunctionAlias<FloatFunction>("float");
services.RegisterTransientFunctionAlias<IntFunction>("int");
}

private static void AddLogicalComparisonFunctions(IServiceCollection services)
{
services.AddTransient<IFunction, AndFunction>();
services.AddTransient<IFunction, EqualFunction>();
services.AddTransient<IFunction, GreaterFunction>();
services.AddTransient<IFunction, GreaterOrEqualsFunction>();
services.AddTransient<IFunction, IfFunction>();
services.AddTransient<IFunction, LessFunction>();
services.AddTransient<IFunction, LessOrEqualsFunction>();
services.AddTransient<IFunction, NotFunction>();
services.AddTransient<IFunction, OrFunction>();
services.RegisterTransientFunctionAlias<AndFunction>("and");
services.RegisterTransientFunctionAlias<EqualFunction>("equal");
services.RegisterTransientFunctionAlias<GreaterFunction>("greater");
services.RegisterTransientFunctionAlias<GreaterOrEqualsFunction>("greaterOrEquals");
services.RegisterTransientFunctionAlias<IfFunction>("if");
services.RegisterTransientFunctionAlias<LessFunction>("less");
services.RegisterTransientFunctionAlias<LessOrEqualsFunction>("lessOrEquals");
services.RegisterTransientFunctionAlias<NotFunction>("not");
services.RegisterTransientFunctionAlias<OrFunction>("or");
}

private static void AddMathFunctions(IServiceCollection services)
{
services.AddTransient<IFunction, AddFunction>();
services.AddTransient<IFunction, DivFunction>();
services.AddTransient<IFunction, MaxFunction>();
services.AddTransient<IFunction, MinFunction>();
services.AddTransient<IFunction, ModFunction>();
services.AddTransient<IFunction, MulFunction>();
services.AddTransient<IFunction, RandFunction>();
services.AddTransient<IFunction, RangeFunction>();
services.AddTransient<IFunction, SubFunction>();
services.RegisterTransientFunctionAlias<AddFunction>("add");
services.RegisterTransientFunctionAlias<DivFunction>("div");
services.RegisterTransientFunctionAlias<MaxFunction>("max");
services.RegisterTransientFunctionAlias<MinFunction>("min");
services.RegisterTransientFunctionAlias<ModFunction>("mod");
services.RegisterTransientFunctionAlias<MulFunction>("mul");
services.RegisterTransientFunctionAlias<RandFunction>("rand");
services.RegisterTransientFunctionAlias<RangeFunction>("range");
services.RegisterTransientFunctionAlias<SubFunction>("sub");
}
}
}
33 changes: 33 additions & 0 deletions ExpressionEngine/FunctionMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using ExpressionEngine.Functions.Base;

namespace ExpressionEngine
{
/// <summary>
/// FunctionMetadata is used to register functions
/// </summary>
public class FunctionMetadata
{
/// <summary>
///
/// </summary>
/// <param name="function">Function must implement <see cref="IFunction"/></param>
/// <param name="alias"></param>
public FunctionMetadata(Type function, string alias)
{

Function = function;
Alias = alias;
}

/// <summary>
/// Function implementation
/// </summary>
public Type Function { get; set; }

/// <summary>
/// Alias used to invoke function
/// </summary>
public string Alias { get; set; }
}
}
26 changes: 10 additions & 16 deletions ExpressionEngine/Functions/Base/IFunction.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,17 @@
using System;
using System.Threading.Tasks;

namespace ExpressionEngine.Functions.Base
{
/// <summary>
/// Interface for implementing a function
/// </summary>
public interface IFunction
{
string FunctionName { get; }
ValueTask<ValueContainer> ExecuteFunction(ValueContainer[] strings);
/// <summary>
/// Execute the given function implementation
/// </summary>
/// <param name="parameters">Parameters given to the functiopn</param>
/// <returns>ValueContainer with the result</returns>
ValueTask<ValueContainer> ExecuteFunction(params ValueContainer[] parameters);
}

public abstract class Function : IFunction
{
public string FunctionName { get; }

protected Function(string functionName)
{
FunctionName = functionName ?? throw new ArgumentNullException(nameof(functionName));
}

public abstract ValueTask<ValueContainer> ExecuteFunction(params ValueContainer[] parameters);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,9 @@

namespace ExpressionEngine.Functions.Implementations.CollectionFunctions
{
public class ContainsFunction : Function
public class ContainsFunction : IFunction
{
public ContainsFunction() : base("contains")
{
}

public override ValueTask<ValueContainer> ExecuteFunction(params ValueContainer[] parameters)
public ValueTask<ValueContainer> ExecuteFunction(params ValueContainer[] parameters)
{
var collection = parameters[0];
var value = parameters[1];
Expand Down Expand Up @@ -48,4 +44,4 @@ public override ValueTask<ValueContainer> ExecuteFunction(params ValueContainer[
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,9 @@

namespace ExpressionEngine.Functions.Implementations.CollectionFunctions
{
public class EmptyFunction : Function
public class EmptyFunction : IFunction
{
public EmptyFunction() : base("empty")
{
}

public override ValueTask<ValueContainer> ExecuteFunction(params ValueContainer[] parameters)
public ValueTask<ValueContainer> ExecuteFunction(params ValueContainer[] parameters)
{
var value = parameters[0];

Expand All @@ -29,4 +25,4 @@ public override ValueTask<ValueContainer> ExecuteFunction(params ValueContainer[
};
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,9 @@

namespace ExpressionEngine.Functions.Implementations.CollectionFunctions
{
public class FirstFunction : Function
public class FirstFunction : IFunction
{
public FirstFunction() : base("first")
{
}

public override ValueTask<ValueContainer> ExecuteFunction(params ValueContainer[] parameters)
public ValueTask<ValueContainer> ExecuteFunction(params ValueContainer[] parameters)
{
var value = parameters[0];

Expand All @@ -26,4 +22,4 @@ public override ValueTask<ValueContainer> ExecuteFunction(params ValueContainer[
};
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,9 @@

namespace ExpressionEngine.Functions.Implementations.CollectionFunctions
{
public class InterSectionFunction : Function
public class InterSectionFunction : IFunction
{
public InterSectionFunction() : base("intersection")
{
}

public override ValueTask<ValueContainer> ExecuteFunction(params ValueContainer[] parameters)
public ValueTask<ValueContainer> ExecuteFunction(params ValueContainer[] parameters)
{
return parameters[0].Type() switch
{
Expand Down Expand Up @@ -49,4 +45,4 @@ private ValueTask<ValueContainer> IntersectList(IReadOnlyList<ValueContainer> pa
return new ValueTask<ValueContainer>(new ValueContainer(intersection));
}
}
}
}
Loading

0 comments on commit d9d614b

Please sign in to comment.