From dd180af6062a96909381dffbb732e1bd89dc2539 Mon Sep 17 00:00:00 2001 From: metoule Date: Wed, 8 Dec 2021 09:53:13 +0100 Subject: [PATCH] Allow a lambda expression to access the initial expression's parameters. Fix #200 --- src/DynamicExpresso.Core/Parsing/Parser.cs | 13 ++++++-- .../Parsing/ParserSettings.cs | 21 ++++++++++--- .../LambdaExpressionTest.cs | 31 +++++++++++++++++++ 3 files changed, 58 insertions(+), 7 deletions(-) diff --git a/src/DynamicExpresso.Core/Parsing/Parser.cs b/src/DynamicExpresso.Core/Parsing/Parser.cs index 1ae3e6fe..20cd6cec 100644 --- a/src/DynamicExpresso.Core/Parsing/Parser.cs +++ b/src/DynamicExpresso.Core/Parsing/Parser.cs @@ -167,7 +167,7 @@ private Expression ParseLambdaExpression() } var lambdaBodyExp = _expressionText.Substring(startExpr, _token.pos - startExpr); - return new InterpreterExpression(_arguments.Settings, lambdaBodyExp, parameters); + return new InterpreterExpression(_arguments, lambdaBodyExp, parameters); } catch (ParseException) { @@ -3012,12 +3012,19 @@ private class InterpreterExpression : Expression private readonly IList _parameters; private Type _type; - public InterpreterExpression(ParserSettings parentSettings, string expressionText, params Parameter[] parameters) + public InterpreterExpression(ParserArguments parserArguments, string expressionText, params Parameter[] parameters) { - _interpreter = new Interpreter(parentSettings); + _interpreter = new Interpreter(parserArguments.Settings.Clone()); _expressionText = expressionText; _parameters = parameters; + // convert the parent's parameters to variables + // note: this doesn't impact the initial settings, because they're cloned + foreach (var pe in parserArguments.DeclaredParameters) + { + _interpreter.SetVariable(pe.Name, pe.Value, pe.Type); + } + // prior to evaluation, we don't know the generic arguments types _type = typeof(Func<>).Assembly.GetType($"System.Func`{parameters.Length + 1}"); } diff --git a/src/DynamicExpresso.Core/Parsing/ParserSettings.cs b/src/DynamicExpresso.Core/Parsing/ParserSettings.cs index 4a5e1c6b..afef8f78 100644 --- a/src/DynamicExpresso.Core/Parsing/ParserSettings.cs +++ b/src/DynamicExpresso.Core/Parsing/ParserSettings.cs @@ -6,10 +6,10 @@ namespace DynamicExpresso.Parsing { internal class ParserSettings { - private readonly Dictionary _identifiers; - private readonly Dictionary _knownTypes; - private readonly HashSet _extensionMethods; - + private Dictionary _identifiers; + private Dictionary _knownTypes; + private HashSet _extensionMethods; + public ParserSettings(bool caseInsensitive,bool lateBindObject) { CaseInsensitive = caseInsensitive; @@ -33,6 +33,19 @@ public ParserSettings(bool caseInsensitive,bool lateBindObject) LambdaExpressions = false; } + /// + /// Creates a deep copy of the current settings, so that the identifiers/types/methods can be changed + /// without impacting the existing settings. + /// + internal ParserSettings Clone() + { + var clone = (ParserSettings)MemberwiseClone(); + clone._identifiers = new Dictionary(_identifiers); + clone._knownTypes = new Dictionary(_knownTypes); + clone._extensionMethods = new HashSet(_extensionMethods); + return clone; + } + public IDictionary KnownTypes { get { return _knownTypes; } diff --git a/test/DynamicExpresso.UnitTest/LambdaExpressionTest.cs b/test/DynamicExpresso.UnitTest/LambdaExpressionTest.cs index 1ac60f55..ba021abb 100644 --- a/test/DynamicExpresso.UnitTest/LambdaExpressionTest.cs +++ b/test/DynamicExpresso.UnitTest/LambdaExpressionTest.cs @@ -269,6 +269,37 @@ public void Zip() Assert.AreEqual(3, results.Count()); Assert.AreEqual(strList.Zip(intList, (str, i) => str + i), results); } + + [Test] + public void Lambda_with_parameter() + { + var target = new Interpreter(InterpreterOptions.Default | InterpreterOptions.LambdaExpressions); + var listInt = target.Eval>("list.Where(n => n > x)", new Parameter("list", new[] { 1, 2, 3 }), new Parameter("x", 1)); + Assert.AreEqual(new[] { 2, 3 }, listInt); + + // ensure the parameters can be reused with different values + listInt = target.Eval>("list.Where(n => n > x)", new Parameter("list", new[] { 2, 4, 5 }), new Parameter("x", 2)); + Assert.AreEqual(new[] { 4, 5 }, listInt); + } + + [Test] + public void Lambda_with_parameter_2() + { + var target = new Interpreter(InterpreterOptions.Default | InterpreterOptions.LambdaExpressions); + var listInt = target.Eval>("list.Select(n => n - 1).Where(n => n > x).Select(n => n + x)", new Parameter("list", new[] { 1, 2, 3 }), new Parameter("x", 1)); + Assert.AreEqual(new[] { 3 }, listInt); + } + + [Test] + public void Lambda_with_variable() + { + var target = new Interpreter(InterpreterOptions.Default | InterpreterOptions.LambdaExpressions); + target.SetVariable("list", new[] { 1, 2, 3 }); + target.SetVariable("x", 1); + + var listInt = target.Eval>("list.Where(n => n > x)"); + Assert.AreEqual(new[] { 2, 3 }, listInt); + } } ///