Skip to content

Commit

Permalink
Allow a lambda expression to access the initial expression's paramete…
Browse files Browse the repository at this point in the history
…rs (#201)

* Allow a lambda expression to access the initial expression's parameters.
Fix #200
  • Loading branch information
metoule authored Dec 10, 2021
1 parent 3abf5b1 commit 1792b39
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 15 deletions.
13 changes: 10 additions & 3 deletions src/DynamicExpresso.Core/Parsing/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -3022,12 +3022,19 @@ private class InterpreterExpression : Expression
private readonly IList<Parameter> _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}");
}
Expand Down
40 changes: 28 additions & 12 deletions src/DynamicExpresso.Core/Parsing/ParserSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ internal class ParserSettings
private readonly Dictionary<string, Identifier> _identifiers;
private readonly Dictionary<string, ReferenceType> _knownTypes;
private readonly HashSet<MethodInfo> _extensionMethods;
public ParserSettings(bool caseInsensitive,bool lateBindObject)

public ParserSettings(bool caseInsensitive, bool lateBindObject)
{
CaseInsensitive = caseInsensitive;

Expand All @@ -27,12 +27,32 @@ public ParserSettings(bool caseInsensitive,bool lateBindObject)
_extensionMethods = new HashSet<MethodInfo>();

AssignmentOperators = AssignmentOperators.All;

DefaultNumberType = DefaultNumberType.Default;

LambdaExpressions = false;
}

private ParserSettings(ParserSettings other) : this(other.CaseInsensitive, other.LateBindObject)
{
_knownTypes = new Dictionary<string, ReferenceType>(other._knownTypes);
_identifiers = new Dictionary<string, Identifier>(other._identifiers);
_extensionMethods = new HashSet<MethodInfo>(other._extensionMethods);

AssignmentOperators = other.AssignmentOperators;
DefaultNumberType = other.DefaultNumberType;
LambdaExpressions = other.LambdaExpressions;
}

/// <summary>
/// Creates a deep copy of the current settings, so that the identifiers/types/methods can be changed
/// without impacting the existing settings.
/// </summary>
public ParserSettings Clone()
{
return new ParserSettings(this);
}

public IDictionary<string, ReferenceType> KnownTypes
{
get { return _knownTypes; }
Expand All @@ -51,31 +71,27 @@ public HashSet<MethodInfo> ExtensionMethods
public bool CaseInsensitive
{
get;
private set;
}

public bool LateBindObject
{
get;
private set;
}

public DefaultNumberType DefaultNumberType
public StringComparison KeyComparison
{
get;
set;
}

public StringComparison KeyComparison
public IEqualityComparer<string> KeyComparer
{
get;
private set;
}

public IEqualityComparer<string> KeyComparer
public DefaultNumberType DefaultNumberType
{
get;
private set;
set;
}

public AssignmentOperators AssignmentOperators
Expand Down
31 changes: 31 additions & 0 deletions test/DynamicExpresso.UnitTest/LambdaExpressionTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<IEnumerable<int>>("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<IEnumerable<int>>("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<IEnumerable<int>>("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<IEnumerable<int>>("list.Where(n => n > x)");
Assert.AreEqual(new[] { 2, 3 }, listInt);
}
}

/// <summary>
Expand Down

0 comments on commit 1792b39

Please sign in to comment.