From 6290a57e3e7182ded62cab7d9318c64a67b74fc7 Mon Sep 17 00:00:00 2001 From: "Thyge S. Steffensen" Date: Mon, 28 Feb 2022 12:15:42 +0100 Subject: [PATCH 1/7] fix!: Concat function does not validate input is string According to the documentation the parameters must be string, but everything works in PA --- .../Functions/Implementations/StringFunctions/ConcatFunction.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 0efcfad8e8585d4016f017df0fef19cea24ef9bb Mon Sep 17 00:00:00 2001 From: "Thyge S. Steffensen" Date: Mon, 28 Feb 2022 12:18:59 +0100 Subject: [PATCH 2/7] feat: Function Definitions Function Definition can be added to 'group' functions together to make it easier to write expressions with a lot of redundant function calls Resolves #59 --- ExpressionEngine/ExpressionGrammar.cs | 19 ++++++++- .../FlowRunnerDependencyExtension.cs | 11 +++++ ExpressionEngine/FunctionDefinition.cs | 41 +++++++++++++++++++ ExpressionEngine/IFunctionDefinition.cs | 8 ++++ Test/ExpressionGrammarTest.cs | 2 +- Test/FunctionDefinitionTests.cs | 32 +++++++++++++++ Test/NullCoalescingTest2.cs | 2 +- 7 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 ExpressionEngine/FunctionDefinition.cs create mode 100644 ExpressionEngine/IFunctionDefinition.cs create mode 100644 Test/FunctionDefinitionTests.cs diff --git a/ExpressionEngine/ExpressionGrammar.cs b/ExpressionEngine/ExpressionGrammar.cs index d0fe8a4..9e7009c 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 @@ -122,13 +125,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..7e73ccd 100644 --- a/ExpressionEngine/FlowRunnerDependencyExtension.cs +++ b/ExpressionEngine/FlowRunnerDependencyExtension.cs @@ -25,6 +25,17 @@ public static void AddExpressionEngine(this IServiceCollection services) services.AddTransient(); } + /// + /// Added FunctionDefinition to service collection + /// + /// + /// + /// + public static void AddFunctionDefinition(this IServiceCollection services, string from, string to) + { + services.AddTransient(_ => new FunctionDefinition(from, to)); + } + private static void AddStringFunctions(IServiceCollection services) { services.AddTransient(); diff --git a/ExpressionEngine/FunctionDefinition.cs b/ExpressionEngine/FunctionDefinition.cs new file mode 100644 index 0000000..6f51fea --- /dev/null +++ b/ExpressionEngine/FunctionDefinition.cs @@ -0,0 +1,41 @@ +namespace ExpressionEngine +{ + /// + /// A Function Definition is a map to a collected set of functions. + /// - currentCellValue() -> formvalue(logicalName())[idx()][attributeLogicalName()] + /// + public class FunctionDefinition : IFunctionDefinition + { + private readonly string _from; + private readonly string _to; + + /// + /// Construction a Function Definition + /// + /// + /// + public FunctionDefinition(string from, string to) + { + _from = @from; + _to = to; + } + + /// + /// + /// + /// + public string From() + { + return _from; + } + + /// + /// + /// + /// + public string To() + { + return _to; + } + } +} \ No newline at end of file diff --git a/ExpressionEngine/IFunctionDefinition.cs b/ExpressionEngine/IFunctionDefinition.cs new file mode 100644 index 0000000..262640e --- /dev/null +++ b/ExpressionEngine/IFunctionDefinition.cs @@ -0,0 +1,8 @@ +namespace ExpressionEngine +{ + public interface IFunctionDefinition + { + public string From(); + public string To(); + } +} \ No newline at end of file diff --git a/Test/ExpressionGrammarTest.cs b/Test/ExpressionGrammarTest.cs index 8541b08..366c98f 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..0e70d52 --- /dev/null +++ b/Test/FunctionDefinitionTests.cs @@ -0,0 +1,32 @@ +using System.Threading.Tasks; +using ExpressionEngine; +using Microsoft.Extensions.DependencyInjection; +using NUnit.Framework; + +namespace Test +{ + public class FunctionDefinitionTests + { + private IExpressionEngine _ee; + + [SetUp] + public void Setup() + { + var serviceCollection = new ServiceCollection(); + serviceCollection.AddExpressionEngine(); + serviceCollection.AddFunctionDefinition("addAndConcat()", "concat('result of 1+1 is: ', add(1,1))"); + + _ee = serviceCollection.BuildServiceProvider().GetRequiredService(); + } + + [TestCase] + public async Task TestFunctionDef() + { + const string expectedResult = "result of 1+1 is: 2!"; + + var actualResult = await _ee.Parse("@concat(addAndConcat(), '!')"); + + Assert.AreEqual(expectedResult, actualResult); + } + } +} \ 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] From ed51518ae40274563908e470b9ac5920d09055f7 Mon Sep 17 00:00:00 2001 From: "Thyge S. Steffensen" Date: Mon, 28 Feb 2022 14:04:57 +0100 Subject: [PATCH 3/7] Implemented feedback from @pksorensen --- .../FlowRunnerDependencyExtension.cs | 17 +++++++--- Test/FunctionDefinitionTests.cs | 31 ++++++++++++++----- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/ExpressionEngine/FlowRunnerDependencyExtension.cs b/ExpressionEngine/FlowRunnerDependencyExtension.cs index 7e73ccd..d783015 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; @@ -26,14 +27,20 @@ public static void AddExpressionEngine(this IServiceCollection services) } /// - /// Added FunctionDefinition to service collection + /// Added FunctionDefinition to service collection. /// /// - /// - /// - public static void AddFunctionDefinition(this IServiceCollection services, string from, string to) + /// 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) { - services.AddTransient(_ => new FunctionDefinition(from, to)); + if (fromFunctionName.EndsWith("()")) + { + throw new ArgumentError($"{nameof(fromFunctionName)} cannot end in ()"); + } + + services.AddTransient(_ => + new FunctionDefinition(fromFunctionName + "()", toExpression)); } private static void AddStringFunctions(IServiceCollection services) diff --git a/Test/FunctionDefinitionTests.cs b/Test/FunctionDefinitionTests.cs index 0e70d52..07b4989 100644 --- a/Test/FunctionDefinitionTests.cs +++ b/Test/FunctionDefinitionTests.cs @@ -1,5 +1,7 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using ExpressionEngine; +using ExpressionEngine.Functions.CustomException; using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; @@ -7,26 +9,39 @@ namespace Test { public class FunctionDefinitionTests { - private IExpressionEngine _ee; + private ServiceCollection _serviceCollection; [SetUp] public void Setup() { - var serviceCollection = new ServiceCollection(); - serviceCollection.AddExpressionEngine(); - serviceCollection.AddFunctionDefinition("addAndConcat()", "concat('result of 1+1 is: ', add(1,1))"); - - _ee = serviceCollection.BuildServiceProvider().GetRequiredService(); + _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(), '!')"); + 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 From 64de37ba86e9dc6540a5a88bdee63855912778bd Mon Sep 17 00:00:00 2001 From: "Thyge S. Steffensen" Date: Fri, 4 Mar 2022 13:06:23 +0100 Subject: [PATCH 4/7] Changed Function Definition to be singleton --- ExpressionEngine/FlowRunnerDependencyExtension.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ExpressionEngine/FlowRunnerDependencyExtension.cs b/ExpressionEngine/FlowRunnerDependencyExtension.cs index d783015..8119b63 100644 --- a/ExpressionEngine/FlowRunnerDependencyExtension.cs +++ b/ExpressionEngine/FlowRunnerDependencyExtension.cs @@ -39,8 +39,7 @@ public static void AddFunctionDefinition(this IServiceCollection services, strin throw new ArgumentError($"{nameof(fromFunctionName)} cannot end in ()"); } - services.AddTransient(_ => - new FunctionDefinition(fromFunctionName + "()", toExpression)); + services.AddSingleton(new FunctionDefinition(fromFunctionName + "()", toExpression)); } private static void AddStringFunctions(IServiceCollection services) From 06f3ee12bc91b9d442038a96d9db46c9db821f48 Mon Sep 17 00:00:00 2001 From: "Thyge S. Steffensen" Date: Fri, 4 Mar 2022 13:10:51 +0100 Subject: [PATCH 5/7] Applied changes from comment --- ExpressionEngine/IFunctionDefinition.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/ExpressionEngine/IFunctionDefinition.cs b/ExpressionEngine/IFunctionDefinition.cs index 262640e..d95c7d5 100644 --- a/ExpressionEngine/IFunctionDefinition.cs +++ b/ExpressionEngine/IFunctionDefinition.cs @@ -1,8 +1,18 @@ namespace ExpressionEngine { + /// + /// Interface to function definitions + /// public interface IFunctionDefinition { - public string From(); - public string To(); + /// + /// 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 From 51623309663da8381de2476beca2453a9608e8c1 Mon Sep 17 00:00:00 2001 From: "Thyge S. Steffensen" Date: Fri, 4 Mar 2022 13:34:08 +0100 Subject: [PATCH 6/7] Build error fixed by implementing the interface properly --- ExpressionEngine/FunctionDefinition.cs | 31 ++++---------------------- 1 file changed, 4 insertions(+), 27 deletions(-) diff --git a/ExpressionEngine/FunctionDefinition.cs b/ExpressionEngine/FunctionDefinition.cs index 6f51fea..7d92b49 100644 --- a/ExpressionEngine/FunctionDefinition.cs +++ b/ExpressionEngine/FunctionDefinition.cs @@ -6,36 +6,13 @@ namespace ExpressionEngine /// public class FunctionDefinition : IFunctionDefinition { - private readonly string _from; - private readonly string _to; - /// - /// Construction a Function Definition + /// The from 'function name' which is replaced /// - /// - /// - public FunctionDefinition(string from, string to) - { - _from = @from; - _to = to; - } - + public string From { get; set; } /// - /// + /// The replacement /// - /// - public string From() - { - return _from; - } - - /// - /// - /// - /// - public string To() - { - return _to; - } + public string To { get; set; } } } \ No newline at end of file From 66537e7da4136cd8c2a6d71f75159e126220a4f3 Mon Sep 17 00:00:00 2001 From: "Thyge S. Steffensen" Date: Fri, 4 Mar 2022 13:36:06 +0100 Subject: [PATCH 7/7] Fix more error --- ExpressionEngine/ExpressionGrammar.cs | 2 +- ExpressionEngine/FlowRunnerDependencyExtension.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ExpressionEngine/ExpressionGrammar.cs b/ExpressionEngine/ExpressionGrammar.cs index 9e7009c..28ac554 100644 --- a/ExpressionEngine/ExpressionGrammar.cs +++ b/ExpressionEngine/ExpressionGrammar.cs @@ -141,7 +141,7 @@ private async ValueTask PreAnalyzeAndParse(string input) { input = _functionDefinitions.Aggregate(input, (current, functionDefinition) => - current.Replace(functionDefinition.From(), functionDefinition.To())); + current.Replace(functionDefinition.From, functionDefinition.To)); } return await _input.Parse(input); diff --git a/ExpressionEngine/FlowRunnerDependencyExtension.cs b/ExpressionEngine/FlowRunnerDependencyExtension.cs index 8119b63..41fba1b 100644 --- a/ExpressionEngine/FlowRunnerDependencyExtension.cs +++ b/ExpressionEngine/FlowRunnerDependencyExtension.cs @@ -39,7 +39,7 @@ public static void AddFunctionDefinition(this IServiceCollection services, strin throw new ArgumentError($"{nameof(fromFunctionName)} cannot end in ()"); } - services.AddSingleton(new FunctionDefinition(fromFunctionName + "()", toExpression)); + services.AddSingleton(new FunctionDefinition{From = fromFunctionName + "()", To = toExpression}); } private static void AddStringFunctions(IServiceCollection services)