Git Product home page Git Product logo

expressionevaluator's People

Contributors

arc-huangjingtong avatar attilaszobonya avatar codingseb avatar dependabot[bot] avatar haering avatar israellot avatar jl0pd avatar kirillosenkov avatar lofcz avatar luandevecchi avatar phenx avatar stg609 avatar stukselbax avatar yazwh0 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

expressionevaluator's Issues

Named arguments support

Hello,
Couldn't find anything about this - but is there a support for named optional arguments in function calls?
Here is something I'm trying to do:

public class Program
{
	public class Ctx
	{
		public double Subtract(double x = 0, double y = 1)
		{
			return x - y;
		}
	}
	
	public static void Main()
	{
		ExpressionEvaluator evaluator = new ExpressionEvaluator();
		evaluator.Context = new Ctx();
		
		string expression = "Subtract(y:2)";
		
		Console.WriteLine(expression);
		Console.WriteLine(evaluator.Evaluate(expression));
		Console.WriteLine(string.Empty);
	}
}

So I'd expect to be able to use named argument just like in C#, is there a support for that? If no, do you have any plans for implementing it?

Thank you.

Cast error when OptionForceIntegerNumbersEvaluationsAsDoubleByDefault is true

Hi everybody,
I noticed that when OptionForceIntegerNumbersEvaluationsAsDoubleByDefault is turned on, the Round function would throw an Invalid cast exception.

The problem in around line 471, where:
return Math.Round(Convert.ToDouble(self.Evaluate(args[0])), (int)self.Evaluate(args[1]), (MidpointRounding)self.Evaluate(args[2]));

tries to cast to int a (now forced) Double value.

I suggest to substitute the cast with a Convert.ToInt32()

Same thing a few rows down, always with Math.Round() call.

Sum of expression lamba on List

Hi

I was trying to make an evaluation, a Sum on a List, but I'm getting an error: CodingSeb.ExpressionEvaluator.ExpressionEvaluatorSyntaxErrorException: 'Function [Sum] unknown in expression : [Sum(x => x.Nombre == "uno" ? (x.Valor ?? 0m) : 0m)]'

What am I doing wrong? or it is not supported?

var Lista = new List<Test>() { new Test { Nombre = "uno", Valor = 1.11m }, new Test { Nombre = "uno", Valor = 2.22m }, new Test { Nombre = "uno", Valor = null }, new Test { Nombre = "dos", Valor = 3.33m } };
string script = "Sum(x => x.Nombre == \"uno\" ? (x.Valor ?? 0m) : 0m)";

ExpressionEvaluator Eval = new ExpressionEvaluator()
{
      StaticTypesForExtensionsMethods = new List<Type> { typeof(Enumerable) },
      Namespaces = new List<string>() { "System", "System.Linq"},
      OptionVariableAssignationActive = false, 
      OptionPropertyOrFieldSetActive = false, 
      CacheTypesResolutions = true
};

Eval.Context = Lista;

var result = Eval.Evaluate(script);

// ***********************************************************************
public class Test
{
     public string Nombre { get; set; }
     public decimal? Valor { get; set; }
}

Ensure Type 'double' though all calculated values

Hey there,

If I try to evaluate (130-120)/(2*250) I get 0 instead of 0.02.
I´ll deep dive into this, but maybe someone already stumble across this.

Update:
After looking at the code, I´ll guess the problem is that after the first cycle of evaluating my expression, the values are int and in the next cycle the division will happen with two int´s. And that´s why I get no double Value.

Is there a Way to ensure all calculated Values are double Values?

Thanks for your response.

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException : Operator '&&' cannot be applied to operands of type 'bool' and 'double'

Hi, when trying to evaluate boolean expressions, it throws a RuntimeBinderException.

ExpressionEvaluator evaluator = new ExpressionEvaluator();
evaluator.Variables = new Dictionary<string, object>()
{
	{ "A", true },
	{ "B", false },
	{ "C", false },
	{ "E", true },
};
var result = evaluator.Evaluate("(A&&B&&C)||(A&&B)||(A&&E&&C)");

Stacktrace

  Message: 
    Microsoft.CSharp.RuntimeBinder.RuntimeBinderException : Operator '&&' cannot be applied to operands of type 'bool' and 'double'
  Stack Trace: 
    CallSite.Target(Closure , CallSite , Object , Object )
    UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
    CallSite.Target(Closure , CallSite , Object , Object )
    <.cctor>b__270_29(Object left, Object right)
    CallSite.Target(Closure , CallSite , Func`3 , Object , Object )
    UpdateDelegates.UpdateAndExecute3[T0,T1,T2,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2)
    CallSite.Target(Closure , CallSite , Func`3 , Object , Object )
    <>c__DisplayClass250_0.<ProcessStack>b__3(IDictionary`2 operatorEvalutationsDict)
    List`1.ForEach(Action`1 action)
    ExpressionEvaluator.ProcessStack(Stack`1 stack)
    ExpressionEvaluator.Evaluate(String expression)
    ExpressionEvaluator.EvaluateParenthis(String expression, Stack`1 stack, Int32& i)
    <>c__DisplayClass238_1.<Evaluate>b__0(ParsingMethodDelegate parsingMethod)
    Enumerable.Any[TSource](IEnumerable`1 source, Func`2 predicate)
    ExpressionEvaluator.Evaluate(String expression)
    ExpressionTest.TestEvaluator() line 176

Failed to evaluate variables

I used the following code.

ExpressionEvaluator evaluator = new ExpressionEvaluator();
            evaluator.Variables = new Dictionary<string, object>();
            evaluator.Variables["a"] = 1;
            evaluator.Variables["b"] = 2;
            evaluator.Variables["c"] = "a+b";
            evaluator.Variables["d"] = "c+3";
            Console.WriteLine(evaluator.Evaluate("a"));
            Console.WriteLine(evaluator.Evaluate("b"));
            Console.WriteLine(evaluator.Evaluate("c"));
            Console.WriteLine(evaluator.Evaluate("d"));

The output was

1
2
a+b
c+3

Shouldn't the output be the following?

1
2
3
6

Problems with null and inexistent properties, Exception not handled

Something is broken handling nulls and inexistent variables

if Name has a valid string, everything is ok, but if Name is null an exception should be triggered but it doesn't
and if instead of Name you call some other property (inexistent) an exception should be triggered too but it doesn't

the problem happens when the expression is compossed with some elements

Eval.Context = new { Person = new Person { Name = null, Value = 1.11m } };
try
{
     var result2 = Eval.Evaluate("\"test one \" + Person.Name.Trim()");
     var result3 = Eval.Evaluate("\"test two\" + Person.AnotherName.Trim()"); 
}
catch(Exception e)
{
    // no exception is triggered
     Console.WriteLine(e.Message);
}


//****************************************************
public class Person
{
    public string Name { get; set; }
    public decimal? Value { get; set; }
}

[bug] ExpressionEvaluator peeks empty stack

Hi, this is a snippet from latest version of EE, starting from line 2942.
If stack count is eq to 0 you are calling stack.Peek(), which results in an unthrown error. The comment on this line else if (evaluationStackCount == 1) is your original code, commented by me.

stack.Clear();
for (int i = 0; i < list.Count; i++)
{
   stack.Push(list[i]);
}

if (stack.Count > 1)
{
    foreach (var item in stack)
    {
        if (item is BubbleExceptionContainer bubbleExceptionContainer)
        {
            throw bubbleExceptionContainer.Exception; //Throw the first occuring error
        }
    }
    throw new ExpressionEvaluatorSyntaxErrorException("Syntax error. Check that no operator is missing");
}
else if (evaluationStackCount == 1) //  && stack.Peek() is BubbleExceptionContainer bubbleExceptionContainer <-- peek on empty stack
{
    //We reached the top level of the evaluation. So we want to throw the resulting exception.
    throw new Exception(); // <-- here you throw bubbleExceptionContainer in original code
}

return stack.Pop();

Best possible resolution here would be to properly throw why the error occured.

Occurs on zero tokens eval:

new EE().Evaluate("")

[feature] Support for out parameters

Hello, I need to call IDictionary.TryGetValue method in my expression, as I understand out parameters are not supported now.
I've tried to execute following code in TryWindow application:

var dictionary = new Dictionary<string, object>();
dictionary.Add("x", 5);
object number = null;
if (dictionary.TryGetValue("x", out number))
	return number;
return 0;

and have got following message:

Variable [out] unknown in expression : [out number]

Mb I did something wrong?
Are there any plans to support out parameters feature?

[bug] chained unary operators are not pushed correctly to stack

Consider these examples:

int a = 0;
a = -~a;
return a; // returns 1 in c#, EE crashes
int a = 0;
a = +-+-+-+-+a;
return a; // returns 0 in c#, EE crashes
int a = 0;
a = a >> +-+-+-+2 << +-+-+-+-2 >> +-+-+-+-+2 << +-+-+-+-+2;
return a; // returns 0 in c#, EE crashes

[question] Enumerable.Range(0,10).Select() not working

Hi,

I am trying to execute following kind of expression

evaluator.Evaluate("Enumerable.Range(0, 10).Select(x => x * x)");

but I get following error for it:

CodingSeb.ExpressionEvaluator.ExpressionEvaluatorSyntaxErrorException: '[System.Linq.Enumerable+d__113] object has no Method named "Select".'

Interesting part is that this works in LINQPad (.NET Core 3.1.8) but not within Visual Studio (.NET Framework 4.7.2).

If I run following code in VS:

var result_a = Enumerable.Range(0, 10);
var result_b = evaluator.Evaluate("Enumerable.Range(0, 10)");

I can see that both variables have value
System.Linq.Enumerable.<RangeIterator>d__113}
but different types:

result_a: System.Collections.Generic.IEnumerable<int> {System.Linq.Enumerable.<RangeIterator>d__113}
result_b: object {System.Linq.Enumerable.<RangeIterator>d__113}

I don't get what is going on here and why it works in LINQPad but not in VS. Any hints?

`OptionForceIntegerNumbersEvaluationsAsDoubleByDefault` leads to not recognising three parameter version of Math.Round

The code

var evaluator = new ExpressionEvaluator()
{
    OptionForceIntegerNumbersEvaluationsAsDoubleByDefault = true
};

string expression = "Math.Round(1,0,MidpointRounding.AwayFromZero)";

Console.WriteLine(expression);
Console.WriteLine(evaluator.Evaluate(expression));

gives the error

[System.Math] object has no Method named "Round".

instead of the expected result "1".

This came about in 1.4.31.0 and worked in 1.4.30.0 (but 1.4.31.0 is a good fix for #110, so thanks for that!)

Ternary expression when condition True has a Bug

Expression: 1==1?true:false run will show exception.
Code in function "EvaluateTernaryConditionalOperator" under s2.Equals(":"):
stack.Push(condition ? Evaluate(restOfExpression.Substring(1, j-1)) : Evaluate(restOfExpression.Substring(j + 1)));
replace with
stack.Push(condition ? Evaluate(restOfExpression.Substring(0, j)) : Evaluate(restOfExpression.Substring(j + 1)));
It will be work.

Using namespace syntax

For example to use WebUtility.UrlEncode method from inside System.Net namespace without adding the namespace to the namespace list I would like to execute something like:
"System.Net.WebUtility.UrlEncode(...)"
Is it possible somehow? because it returns an error that "System" variable is not recognized. This would allow to execute a lot of methods without needing to add the namespace each time.

ConditionalAnd with variables

First, I want to say it's a fascinating and high-quality module, congratulations and thank you for putting it up here!

I have a problem evaluating expressions with variables when using ConditionalAnd and null values:

Dictionary<string, object> dictValues = new Dictionary<string, object>();
string nullString = null;
dictValues[ "TestNullString" ] = nullString;

//pass dictValues as expEval.Variables into expression evaluation instance and then...
string expression = "!string.IsNullOrEmpty(TestNullString) && TestNullString.StartsWith(\"ABC\")";
var result = expEval.Evaluate(expression);

This throws a NullReferenceException with the second half, where "plain C#" would skip evaluating this part. The issue seems related to some that were discussed here recently, especially #51 and #53, but the fixes don't address it. The exception details are:

 at ExpressionEvaluator.DetermineInstanceOrStatic(Type& objType, Object& obj, ValueTypeNestingTrace& valueTypeNestingTrace) in EvaluatorTest\ExpressionEvaluator.cs

[suggestion] Implement async / await

Currently this is not included in todo / roadmap document here - https://github.com/codingseb/ExpressionEvaluator/wiki/ExpressionEvaluator-Todo-List

Implementing async / await (with priority on await) would greatly increase flexibility of EE. C# is becoming more and more asynchronous with each new version and by using async patterns we can free current thread while external work is being processed (querying a database, creating/saving a stream, waiting for an external library to do some work...). This is critical for web applications where threadpool is very limited and we need to free our threads as often as possible.

This suggestion hence proposes that async / await keywords would be recognized when parsing scripts.

Backend:

public class Main {
     public staic void Main() {
          ExpressionEvaluator eval = new();
          eval .StaticTypesForExtensionsMethods.Add(typeof(MyClassExt));
          eval.Variables = new Dictionary<string, object> {
               { "SomeAsyncMethod", new Func<int>(async () => await someAwaitableJobReturningInt())} }
          }

          eval.ScriptEvaluate(script);
     }
}

Script:

myResult = await SomeAsyncMethod();

Note that we would also need to support this on extension methods.

For the sake of supporting various syntaxes keywords should be remappable.

myResult = waitfor SomeAsyncMethod(); /* waitfor = await */

[question] Declare methods in scripts

Hi, I'm not quite sure if I missed this in the docs, but is something like this possible?

[void] myMethod([int] a) {

}

myMethod(1);

running the above from new EE().ScriptEvaluate(), myMethod is defined inside of script, not on input / context, [] means optional.

Exception when changing the value of a struct variable

Hi! I didn't do an excessive research about that bug, but I received an exception when I tried to change the value of a field of a struct.

Message:
Operator '==' cannot be applied to operands of type '<my_struct_type>' and 'NULL'

StackTrace:
at CallSite.Target(Closure , CallSite , Object , Object ) at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1) at CallSite.Target(Closure , CallSite , Object , Object ) at CodingSeb.ExpressionEvaluator.ExpressionEvaluator.EvaluateVarOrFunc(String expr, String restOfExpression, Stack'1 stack, Int32& i) in [...]\ExpressionEvaluator.cs:line 1833 at CodingSeb.ExpressionEvaluator.ExpressionEvaluator.Evaluate(String expression) in [...]\ExpressionEvaluator.cs:line 1385

The expression was simply:
struct.field = anotherVariable

It was somehow related to the dictionary read in line 1833, where you tried to read the variable value into a dynamic variable. I was able to fix the issue by changing the type of 'cusVarValueToPush' to 'object' and only casting it to 'dynamic' in line 1872 and 1876.

I created a pull request with the fix. #31

[suggestion] Allow bracketless version of if/while/for/foreach

Instead of mandatory () around an expression in listed keywords an option would exist to parse them without brackets.

Now:

if (expr) {

}

With suggestion option toggled on:

if expr {

}

Since EE already supports lightweighted syntactic constructs (myVar = 0; instead of [type] myVar = 0;), this would a nice addition for Python like scripting.

Parameter type mismatch is causing missing methods

In 1.4.20 I started having an issue with member methods not being found, specifically on DateTime and DateTimeOffset. Some digging and it looks like the parameter filter are causing some of the basic methods like AddDays(double d) to be excluded when the given parameter is in fact an int.

var evaluator = new ExpressionEvaluator();
evaluator.Evaluate("DateTime.Now.AddDays(1)");

This will yield an exception, CodingSeb.ExpressionEvaluator.ExpressionEvaluatorSyntaxErrorException: [System.DateTime] object has no Method named "AddDays".]

Specifying the variable as a double fixes the issue, evaluator.Evaluate("DateTime.Now.AddDays(1d)"); but this isn't always ideal or obvious as int is implicitly convertable to double and passing an int to AddDays is valid C#.

I see in the commits that this section of code is being worked on - is this something I should try to fix and send in a pull request? Or hold off till next release, or let the maintainers handle?

[suggestion] return instead of throw

Now when invalid token / expression is encountered during evaluation throw statement is used to notify user something went wrong. In scenarios where EE is "proxied" behind some interface (for example a simple web application where I can create and evaluate my scripts) this is suboptimal behavior.

  • throw is expensive and shouldn't be used in scenarios where can be triggered often
  • it makes hard for users of EE to customize onerror behavior, texts of errors and recover from errors (forces try-catch)
  • there is no well defined list of recognized errors, users have to look them up in source code

This suggestion hence proposes that EE.ScriptEvaluate would return ExpressionEvaluatorResult insted of object. ExpressionEvaluatorResult is technically a tuple (could be reduced to a tuple) but for the sake of supporting pre-tuple versions of c# it would be nice to keep this as a class.

public class ExpressionEvaluatorResult {
  public object Result {get; set;} // <-- this is what ScriptEvaluate returns now
  public EvaluationResults State {get; set;} // <-- enum indicating result of evaluation
  public EvaluationResultErrorInfo ErrorInfo {get; set;} // <-- when "ok" this is null
}

Inner members definitions:

public enum EvaluationResults {
   Ok, // <-- first entry indicates the evaluation was successful
   MissingLParen, // <-- following entries all indicate an error
   MissingRParen
   ...
}

public class EvaluationResultErrorInfo {
   public int Line {get; set;} // <-- line in script where error occured
   public int Character {get; set;} // <-- character index in that line
   public string Message {get; set;} // <-- text we now pass to throw
}

Usage would then be:

ExpressionEvaluatorResult result = new ExpressionEvaluator("return 1 + 1;");
if (result.State == EvaluationResults.Ok) {
  // script evaluated successfully
  int ret = (int)result.Result;
}

Please note that all naming used is open for consideration and improvement, it should be as intuitive as possible and this is just from the top of my head.

Manage default values for methods parameters

When a method implement default value for a parameter like :

public void MyMethod(int x, int y = 100)
{
    // ...
}

For now, calling this method with EE force us to define the parameter (here y). In C# we can omit it.
EE should allow to omit parameters with default values when we call this kind of method.

Implementing a syntaxicTree for version 2.0 of ExpressionEvaluator

I create here an issue to follow and document (first for myself but for who is interested) the evolution of the work to version 2.0

The goal is to deeply refactor ExpressionEvaluator to internally build a syntaxic tree with different kinds of tokens.
The evaluation will be executed in 2 phases.

  1. Parse the expression string and build the syntaxic tree.
  2. Execute the syntaxic tree.

This will allow a lot of stuffs that are not possible with the current "on the fly" way of evaluating things.

It should allow to :

  • Cache expressions to speed up evaluations when executed multiple times.
  • Managing better the evaluation flow.
    • To correct #56 (Conditional and and Conditional always execute their right part) without breaking operators precedence
    • To correct #65 (Problem to select the good method when multiple method with same name defined that take lambda as arguments)
  • Validate more things before executing and prevent partial executions
  • Make better self explained exceptions.

Risks and drawdowns

  • I'm not sure yet that all existing functionalities can work with this new way of doing things.
  • Will take time
  • A lot of tests exists to ensure that what is working now will still working after but there are not exaustive. So there is a high risk of breaking things. More tests are needed.
  • As the structure will change, the way that some of the existing functionalities works will change. It means that the corresponding documentation will need to be rewrite.

I will also decide how to split things between version 1 and 2.

  • Fork, Branch or continue in the same repo and same release flow
  • How to manage documentation differences.
  • Split code in multiple files, keep it in one for easy copy or make a precompile merge or something like this.

`OptionForceIntegerNumbersEvaluationsAsDoubleByDefault` leads to exception when dividing

The code

var evaluator = new ExpressionEvaluator()
{
    OptionForceIntegerNumbersEvaluationsAsDoubleByDefault = true 
};

string expression = "3/Math.Round(Avg(1,2),MidpointRounding.AwayFromZero)";

Console.WriteLine(expression);
Console.WriteLine(evaluator.Evaluate(expression));

gives the error

Unhandled exception. Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Operator '/' cannot be applied to operands of type 'double' and 'decimal'

It worked in 1.4.20 and first stopped working in 1.4.21. Math.Round is picking the decimal overload when I'd expect it to pick the double overload. It works without OptionForceIntegerNumbersEvaluationsAsDoubleByDefault.

NetStandard compatibility

Hello, thanks for this great lib, is the support for NetStandard / dotnet core something we can wait for ?

Why are operators limited to "two" characters ?

Method name "EvaluateTwoCharsOperators" implies it.

If one were to add "And" and "Or" operators as aliases for && and ||, the current implementation wouldn't work because "And" has three characters.

Making this change seems to work, but not sure it's the best way:

BEFORE

    private bool EvaluateTwoCharsOperators(string expr, Stack<object> stack, ref int i)
    {
        if (i < expr.Length - 1)
        {
            string op = expr.Substring(i, 2);
            if (operatorsDictionary.ContainsKey(op))
            {
                stack.Push(operatorsDictionary[op]);
                i++;
                return true;
            }
        }

        return false;
    }

AFTER

    private bool EvaluateTwoCharsOperators(string expr, Stack<object> stack, ref int i)
    {
        if (i < expr.Length - 1)
        {
            string op = expr.Substring(i, 2);
            var containsKey = operatorsDictionary.ContainsKey(op);
            if (!containsKey && expr.Length >= i + 3)
            {
                op = expr.Substring(i, 3);
                containsKey = operatorsDictionary.ContainsKey(op);
            }
            if (containsKey)
            {
                stack.Push(operatorsDictionary[op]);
                i += op.Length - 1;
                return true;
            }
        }

        return false;
    }

Manage nested class and enum

For now types that are defined in a parent class are not supported in EE.

So something like below do not work :

string path = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);

This because the enum SpecialFolder is defined in the class Environment.

ExpressionEvaluator method should use IDictionary or IReadOnlyDictionary input type

Small request.

The "ExpressionEvaluator()" method that takes a Dictionary of variables, I'd like to suggest that it instead take IDictionary or, better yet, IReadOnlyDictionary. The reason is because the host code may already have a set of "variables" in a dictionary but the "value" item of each entry is not the variable value, but a different object that contains the value, in addition to other things. Passing that directly to your code wouldn't work because your code expects the Value of the dictionary entry to be an actual intrinsic value such as an int or a double. So today, I have to create a separate dictionary just for the evaluator which is wasteful. If the ExpressionEvaluator code took on an interface instead, I could implement that interface in my object that used the existing dictionary internal to our code.

In general, parameters to methods should be the least simplest form that the code requires. So if your code isn't going to add items to the dictionary, than IReadOnlyDictionary<string, object> is all you need to require.

Added a righthandOperator 'Not' does not work

I tried to create a new righthand operator named "Not" which does exactly the same as ExpressionOperator.LogicalNegation
But it will fail because the function 'EvaluateVarOrFunc' throws an error like
"Variable [Not] unknown in expression : []");

I think the problem is that 'Not' is a string and because of that it will be tried to parse it to a function in steat of an operator.
Maybe a solution could be to add on line 1716:
&& !operatorsDictionary.ContainsKey(varFuncMatch.Groups["name"].Value)

Evaluating null produce reference error

Evaluating a simple expression like

null?.Trim()

should return null

but instead is returning an exception "reference not set to an instance of an object"

Extended Methods

this a complete library... one of the best I've seen. the only feature that is missing is support for extended methods.

for example. I declare this class

public static class MyAuxiliarMethods
{
    public static string MyExtendedMethod(this string str)
    {
        return str + " extended";
    }
}

then I should add this type somewhere so all the exteded methods became registered.
or maybe just adding the corresponding namespace...

then I could evaluate an expression like this:

AnStringVariable.MyExtendedMethod()

EvaluateVariable delegate intended operation?

Say I have an object, OBJ, which has a property Property.
I also setup an EvaluateVariable() delegate.
In my variable dictionary, I have the entry ("OBJ", OBJ)

I ask the engine to evaluate:
OBJ.Property

In regards to OBJ, the EvaluateVariable() delegate is only called if the OBJ entry is not included in the dictionary.

However, in regards to Property, EvaluateVariable() is called every time.

  • If EvaluateVariable() simply returns on this call (and doesn't set e.Value), the engine will use the value returned by OBJ.Property
  • If EvaluateVariable() does set e.Value, the engine will use that value

Is this the intended behavior? It seems inconsistent. I expected EvaluateVariable() would only get called for Property, if the Property was not part of OBJ proper.

VS2019, NuGet version 1.3.7

missing variable classOrTypeName

Hi!

I'm trying to see if your libraries fits my needs.

When i import the .cs file to my project, i get an error in the file on lines 282 and 283 that "classOrTypeName" does not exist in the current context.

Am i missing something?

Escaping variable names

Hi Seb!
First of all I'd like to say this library is quite a marvel compared to the alternatives we have in the dotnet world!.
Keep up the good work!!

This is not an issue per say, but i'm wondering if there is a way to escape the variables names in expressions.

The goal here would be to allow hyphen-case variables to be properly identified by the parser
for example i'd like to be able to evaluate expressions like this :

var exp = "net-pv / Total::net-pv"

where net-pv and Total::net-pv are both variables.

If the functionality does not exist , a quick win might be to use string literals to help distinguish variable names in the expression

var exp = $"{net-pv}/{Total::net-pv}"

[question] funding

There seems to be no way to support this project financially, do you think it would be possible to either mark this project as open for github sponsors or add PayPal / whatever payment gate link? I (and possibly others) would like to chip in to support this.

Null value causes Property to fail with object has no public Property or Member named [...]

Having a Dynamic Object with a null value for a Property results in the following error:

LogParsing.ExpressionEvaluatorSyntaxErrorException: '[System.Dynamic.ExpandoObject] object has no public Property or Member named "NullValue".'

            dynamic MyDynamic = new System.Dynamic.ExpandoObject();
            MyDynamic.NullValue = null;

            ExpressionEvaluator evaluator = new ExpressionEvaluator();
            evaluator.Variables = new Dictionary<string, object>()
            {
                  { "md", MyDynamic },
            };
            string expression = "md.NullValue ?? \"A\"";
            var exp = evaluator.Evaluate(expression);

Verified on the latest version of the code. Any ideas?

Sum of decimal fail

the library is resolving sum like an integer... not diferenciating if a property is a decimal... probably it's failing for another datatypes like float, double, etc.

var Persons = new List<Person>() { new Person() { Code = "QT00010", Name = "Pedrito", Number = 11.11m },
            new Person() { Code = "QT00011", Name = "Pablito", Number = 12.11m }};

ExpressionEvaluator evaluator = new ExpressionEvaluator();
evaluator.Context = new { Persons };
object val1 = evaluator.Evaluate("Persons.Sum(x=>x.Number)");
Console.WriteLine(val1);

public class Person{
        public string Code { get; set; }
        public string Name { get; set; }
        public decimal Number { get; set; }
}

this should print a decimal 23.22, but it's printing an int 23

EvaluateInstanceCreationWithNewKeyword doesn't support arrays

"".Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
throws error in EvaluateInstanceCreationWithNewKeyword method "No '(' found after..."
same for
"".Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries)

any quick workaround?

[bug] Exception is assigned to variable

Hi,

I found the bug, that if I assign the function result to a variable an exception thrown by that function will be assigned to that variable.

In the following example I would expect the Evaluate function to throw the exception and the result variable does not exist, but in the current version the exception is thrown and additionally assigned to the variable.

[Test]
public void Evaluate_WithException_ThrowsExceptionAndDoesNotAssignIt()
{
    TestConnectExpressionEvaluator evaluator = new TestConnectExpressionEvaluator();
    
    evaluator.Variables.Add("dummy", new DummyForException());
    Assert.Throws<ExpressionEvaluatorSyntaxErrorException>(() => evaluator.Evaluate("result = dummy.bang()"));
    
    Assert.That(evaluator.Variables.ContainsKey("result"), Is.False);
}

public class DummyForException
{
  public void bang()
  {
      throw new Exception();
  }
}

[suggestion] allow remapping of "new"

This is a followup suggestion for #73. Now to instantiate, only standard c# syntax can be used (on right hand of expression):

myList = new List<int>();

Keyword new should be remappable (either as regex or string array) to keep consistency with recently added options to modify syntax of other constructs.

// my exotic syntax
myList = -> List<int>();

License file

Great piece of code! Would love to use it in a project I'm working on, but would need a free license like MIT included. Could you add that license file?

Thanks! :)

Support for a global Context

Receiving a list of variables is helpfull, but it would be great if instead of this

var eval = new ExpressionEvaluator(variables);

the library could receive an object which would be the context. (similar to what roslyn does)

var eval = new ExpressionEvaluator(Person1);

then all the properties, variables and methods would be directly accessible

eval.Evaluate(" Name + \" \" + LastName ");
eval.Evaluate(" aPersonMethod() + 10");

where Person1 is an instance of the class

public class Person{
   public string Name {get;set;}
   public string LastName {get;set;}
   public int aPersonMethod(){
      return 10;
   }
}

Evaluate methods names as delegates : (calls to Array.ConvertAll() failing)

When I try the following code:

var evaluator = new ExpressionEvaluator
{
    Variables = new Dictionary<string, object>()
    {
        {"values", "1,2,3,4,5,6,-1"}
    }
};

var script = "var numbers = Array.ConvertAll(values.Split(','), Int32.Parse); return numbers.Min();";

return evaluator.ScriptEvaluate(script);

...it fails with the error:

[System.Array] object has no Method named "ConvertAll".

Should this work?

[suggestion] anonymous array declaration, relax object syntax (JS/Json syntax)

Currently EE can evaluate anonymous object declarations:

x = new {a = 10, b = 20, c = 30}

property = value is enforced in method InitSimpleObjet (we should rename that to InitSimpleObject). First part of this suggestion is to relax this syntax and allow customization (preferably in the way customization of new works now) such as:

x = new {a: 10, b: 20, c: 30}

second part of this suggestion proposes support of JS-like anonymous array declaration (respecting c# new initialization pattern), which would internally map to List<object>:

x = new [10, 20, 30] // x[2] = 30

@codingseb do you think this would be possible to implement and of use?

Options for Unicode variable name

Hi
I'd like to say your work is quite a nice and useful to me. Thank you.

I want to use "Unicode variable name" like standard c#. (for example Korean, Chinese ...),
so I modified your code as below, and then it works fine for Korean.

// changed line
const string diactiticsKeywordsRegexPattern = @"\p{L}_";

Example

// english
one = 1;
two = 2;
three = one + two;

// korean
하나 = 1;
둘 = 2;
셋 = 하나 + 둘;

I hope, you can make a option, like "OptionsUnicodeVariableNameActive".

Thank you!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.