zzzprojects / Eval-Expression.NET

C# Eval Expression | Evaluate, Compile, and Execute C# code and expression at runtime.
https://eval-expression.net/
Other
459 stars 86 forks source link

Left hand side of subtraction misinterpreted as cast #155

Closed void80 closed 3 months ago

void80 commented 9 months ago

Have a look at the following test cases

    [TestMethod]
    [DataRow("#1", "a1", 1)]
    [DataRow("#2", "(a1)", 1)]
    [DataRow("#3", "(a1)+(a1)", 2)]
    [DataRow("#4", "(a1)+a1", 2)]
    [DataRow("#5", "(a1)-(a1)", 0)]
    [DataRow("#6", "(a1)-a1", 0)]
    public void TestSimpleFalseCastExpressions(string id, string expression, int expected)
    {
        var context = new EvalContext();

        var types = new Dictionary<string, Type>
        {
            { "a1", typeof(double) },
        };

        var values = new Dictionary<object, double>
        {
            { "a1", 1.0 },
        };

        var compiled = context.Compile(expression, types);
        var result = compiled.Invoke(values);

        result.Should().Be(expected);
    }

The cases #5 and #6 fail with the following exception:

Test method EvalExpressionsTests.TestSimpleFalseCastExpressions threw exception: 
Z.Expressions.Compiler.Shared.EvalException: Oops! The type was not found.. The error occurred for expression "(" at position 0 near "(a1)-(a1)".
    at .(ExpressionScope , SyntaxNode , Expression , Boolean )
   at .(ExpressionScope , SyntaxNode , Expression , Boolean )
   at .(ExpressionScope , SyntaxNode , Expression , Boolean )
   at Z.Expressions.CodeCompiler.CSharp.ExpressionParser.ParseSyntax(ExpressionScope scope, SyntaxNode node, Type resultType)
   at .[](EvalContext , String , IDictionary`2 , Type , EvalCompilerParameterKind , ExpressionScope , String , Boolean , Boolean )
   at .[](EvalContext , String , IDictionary`2 , Type , EvalCompilerParameterKind , Boolean , Boolean , Boolean )
   at Z.Expressions.EvalContext.Compile(String code, IDictionary`2 parameterTypes)
   at EvalExpressionsTests.TestSimpleFalseCastExpressions(String id, String expression, Int32 expected) in EvalExpressionsTests.cs:line 141
   at InvokeStub_EvalExpressionsTests.TestSimpleFalseCastExpressions(Object, Span`1)
   at System.Reflection.MethodBaseInvoker.InvokeWithFewArgs(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

My suspicion is that (a1) is (wrongly) interpreted as cast when followed by a - sign, because the - is interpreted as unary negation operator.

I would expect the parser to be able to distinguish between a real cast and an expression in parens.

For example "(double) -1" should be identified as a cast, "(a1)-1" should be identified as a subtraction.

JonathanMagnan commented 9 months ago

Hello @void80 ,

Thank you for reporting.

Your suspicion is probably 100% right. I remember we fixed a similar issue recently but slightly different #154 (he wanted to cast it on his side).

We will look at it.

Best Regards,

Jon

strohhaecker commented 8 months ago

We've figured out that a downgrade to 6.0.1 works around the problem, in case this helps you. Best regards, Sebastian

JonathanMagnan commented 5 months ago

Hello @strohhaecker , @void80

A new version, v6.1.7, has been released today.

All known cases we found have been fixed in this issue.

Could you test the latest version and let us know if everything is working? Or report any missing expression that still have an error.

Best Regards,

Jon

strohhaecker commented 5 months ago

Hi Jon, I'll try to verify this fixes some problems we had, though we have changed a lot around the expression evaluation since that time. Thanks for keeping this up! Sebastian

void80 commented 4 months ago

Hi Jon,

we verified that all of our use cases work with the version 6.1.7.

However, I just wanted to check some cases we are not using. There seems to be one problem remaining (which we are not using).

    [TestMethod]
    [DataRow("#1", "a1", 1)]
    [DataRow("#2", "(a1)", 1)]
    [DataRow("#3", "(a1)+(a1)", 2)]
    [DataRow("#4", "(a1)+a1", 2)]
    [DataRow("#5", "(a1)-(a1)", 0)]
    [DataRow("#6", "(a1)-a1", 0)]
    [DataRow("#7", "(int)-1", -1)]
    [DataRow("#8", "(int)+1", +1)]
    [DataRow("#9", "+1", +1)]
    public void TestSimpleFalseCastExpressions(string id, string expression, int expected)
    {
        var context = new EvalContext();

        var types = new Dictionary<string, Type>
        {
            { "a1", typeof(double) },
        };

        var values = new Dictionary<object, double>
        {
            { "a1", 1.0 },
        };

        var compiled = context.Compile(expression, types);
        var result = compiled.Invoke(values);

        result.Should().Be(expected);
    }

Use case #8 is not working. That is, a cast followed by a unary + operator. Strangely enough, the unary operator itself seems to work (case #9).

Regards Sven

JonathanMagnan commented 4 months ago

Thank you @void80 for reporting.

Indeed, we only added the logic for the minus sign, never for the plus sign. We will see if it is worth doing a similar logic (we are more afraid of the side impact here).

Best Regards,

Jon

JonathanMagnan commented 3 months ago

Hello @void80 ,

The v6.1.8 has been released.

We added an identical logic to the plus sign that we did with the minus sign.

Let me know if this time everything is working as expected.

Best Regards,

Jon