diff --git a/ExpressionEngine/ExpressionGrammar.cs b/ExpressionEngine/ExpressionGrammar.cs index b58a265..99e5fa7 100644 --- a/ExpressionEngine/ExpressionGrammar.cs +++ b/ExpressionEngine/ExpressionGrammar.cs @@ -10,11 +10,14 @@ namespace ExpressionEngine { public class ExpressionGrammar { + private readonly IList _functionDefinitions; private readonly Parser _method; private readonly Parser> _input; - public ExpressionGrammar(IEnumerable functions) + public ExpressionGrammar(IEnumerable functions, IEnumerable functionDefinitions) { + _functionDefinitions = functionDefinitions?.ToList(); + var functionCollection = functions ?? throw new ArgumentNullException(nameof(functions)); #region BasicAuxParsers @@ -124,13 +127,25 @@ from t in simpleString.Or(allowedCharacters).Many() public async ValueTask EvaluateToString(string input) { - var output = await _input.Parse(input); + var output = await PreAnalyzeAndParse(input); return output.GetValue(); } public async ValueTask EvaluateToValueContainer(string input) { + return await PreAnalyzeAndParse(input); + } + + private async ValueTask PreAnalyzeAndParse(string input) + { + if (_functionDefinitions != null) + { + input = _functionDefinitions.Aggregate(input, + (current, functionDefinition) => + current.Replace(functionDefinition.From, functionDefinition.To)); + } + return await _input.Parse(input); } } diff --git a/ExpressionEngine/FlowRunnerDependencyExtension.cs b/ExpressionEngine/FlowRunnerDependencyExtension.cs index 21b0e4b..41fba1b 100644 --- a/ExpressionEngine/FlowRunnerDependencyExtension.cs +++ b/ExpressionEngine/FlowRunnerDependencyExtension.cs @@ -1,4 +1,5 @@ using ExpressionEngine.Functions.Base; +using ExpressionEngine.Functions.CustomException; using ExpressionEngine.Functions.Implementations.CollectionFunctions; using ExpressionEngine.Functions.Implementations.ConversionFunctions; using ExpressionEngine.Functions.Implementations.LogicalComparisonFunctions; @@ -25,6 +26,22 @@ public static void AddExpressionEngine(this IServiceCollection services) services.AddTransient(); } + /// + /// Added FunctionDefinition to service collection. + /// + /// + /// The name of the function, without function parenthesis + /// The full expression which is inserted + public static void AddFunctionDefinition(this IServiceCollection services, string fromFunctionName, string toExpression) + { + if (fromFunctionName.EndsWith("()")) + { + throw new ArgumentError($"{nameof(fromFunctionName)} cannot end in ()"); + } + + services.AddSingleton(new FunctionDefinition{From = fromFunctionName + "()", To = toExpression}); + } + private static void AddStringFunctions(IServiceCollection services) { services.AddTransient(); diff --git a/ExpressionEngine/FunctionDefinition.cs b/ExpressionEngine/FunctionDefinition.cs new file mode 100644 index 0000000..7d92b49 --- /dev/null +++ b/ExpressionEngine/FunctionDefinition.cs @@ -0,0 +1,18 @@ +namespace ExpressionEngine +{ + /// + /// A Function Definition is a map to a collected set of functions. + /// - currentCellValue() -> formvalue(logicalName())[idx()][attributeLogicalName()] + /// + public class FunctionDefinition : IFunctionDefinition + { + /// + /// The from 'function name' which is replaced + /// + public string From { get; set; } + /// + /// The replacement + /// + public string To { get; set; } + } +} \ No newline at end of file diff --git a/ExpressionEngine/Functions/Implementations/StringFunctions/ConcatFunction.cs b/ExpressionEngine/Functions/Implementations/StringFunctions/ConcatFunction.cs index b2db84d..7cf7157 100644 --- a/ExpressionEngine/Functions/Implementations/StringFunctions/ConcatFunction.cs +++ b/ExpressionEngine/Functions/Implementations/StringFunctions/ConcatFunction.cs @@ -34,7 +34,7 @@ public override ValueTask ExecuteFunction(params ValueContainer[ } return new ValueTask(new ValueContainer(parameters.Aggregate("", - (current, value) => current + AuxiliaryMethods.VcIsString(value)))); + (current, value) => current + value))); } } } \ No newline at end of file diff --git a/ExpressionEngine/IFunctionDefinition.cs b/ExpressionEngine/IFunctionDefinition.cs new file mode 100644 index 0000000..d95c7d5 --- /dev/null +++ b/ExpressionEngine/IFunctionDefinition.cs @@ -0,0 +1,18 @@ +namespace ExpressionEngine +{ + /// + /// Interface to function definitions + /// + public interface IFunctionDefinition + { + /// + /// The name of the 'function' which is replaced + /// + string From { get; set; } + + /// + /// What it is replaced with + /// + string To { get; set; } + } +} \ No newline at end of file diff --git a/Test/ExpressionGrammarTest.cs b/Test/ExpressionGrammarTest.cs index bb14d9f..599278a 100644 --- a/Test/ExpressionGrammarTest.cs +++ b/Test/ExpressionGrammarTest.cs @@ -20,7 +20,7 @@ public void SetUp() var functions = new List {_dummyFunction}; - _expressionGrammar = new ExpressionGrammar(functions); + _expressionGrammar = new ExpressionGrammar(functions, null); } [Test] diff --git a/Test/FunctionDefinitionTests.cs b/Test/FunctionDefinitionTests.cs new file mode 100644 index 0000000..07b4989 --- /dev/null +++ b/Test/FunctionDefinitionTests.cs @@ -0,0 +1,47 @@ +using System; +using System.Threading.Tasks; +using ExpressionEngine; +using ExpressionEngine.Functions.CustomException; +using Microsoft.Extensions.DependencyInjection; +using NUnit.Framework; + +namespace Test +{ + public class FunctionDefinitionTests + { + private ServiceCollection _serviceCollection; + + [SetUp] + public void Setup() + { + _serviceCollection = new ServiceCollection(); + _serviceCollection.AddExpressionEngine(); + _serviceCollection.AddFunctionDefinition("addAndConcat", "concat('result of 1+1 is: ', add(1,1))"); + } + + [TestCase] + public async Task TestFunctionDef() + { + var ee = _serviceCollection.BuildServiceProvider().GetRequiredService(); + const string expectedResult = "result of 1+1 is: 2!"; + + var actualResult = await ee.Parse("@concat(addAndConcat(), '!')"); + + Assert.AreEqual(expectedResult, actualResult); + } + + [TestCase] + public void TestFunctionException() + { + const string expectedMessage = "fromFunctionName cannot end in ()"; + + var exception = Assert.Throws(() => + { + _serviceCollection.AddFunctionDefinition("addAndConcat()", "concat('result of 1+1 is: ', add(1,1))"); + }); + + Assert.NotNull(exception); + Assert.AreEqual(expectedMessage,exception.Message); + } + } +} \ No newline at end of file diff --git a/Test/NullCoalescingTest2.cs b/Test/NullCoalescingTest2.cs index 22a7429..fc7339c 100644 --- a/Test/NullCoalescingTest2.cs +++ b/Test/NullCoalescingTest2.cs @@ -19,7 +19,7 @@ public void Setup() var functions = new List {_returnData}; - _expressionGrammar = new ExpressionGrammar(functions); + _expressionGrammar = new ExpressionGrammar(functions, null); } [Test]