Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Strings parameters fail count check when EvaluateOptions.IterateParameters is used #83

Open
AzGhort opened this issue Oct 18, 2023 · 1 comment
Labels

Comments

@AzGhort
Copy link

AzGhort commented Oct 18, 2023

Hi,
when implenting custom string functions for NCalc, we discovered a bug which prevents using FunctionArgs.Parameters[N].Evaluate() along with EvaluateOptions.IterateParamers when evaluating parameters in custom function evaluators.

Evaluated string parametes are treated as IEnumerable, and therefore fail the same count check in Expression, see:

throw new EvaluationException("When IterateParameters option is used, IEnumerable parameters must have the same number of items");

Minimal code to reproduce:

    [Fact]
    public void EndsWith_Test()
    {
        var a = new Expression("EndsWith([x], [y])", EvaluateOptions.IterateParameters);
        a.Parameters["x"] = new[] { "'abc'", "'def'" };
        a.Parameters["y"] = new[] { "'c'", "'c'" };
        a.EvaluateFunction += EndsWith;

        var result = a.Evaluate();
    }

    private static void EndsWith(string name, FunctionArgs args)
    {
        if (name == "EndsWith")
        {
            var firstParam = args.Parameters[0].Evaluate();
            var secondParam = args.Parameters[1].Evaluate();

            if (firstParam is not string firstArg || secondParam is not string secondArg)
            {
                throw new FormatException();
            }

            args.Result = firstArg.StartsWith(secondArg, StringComparison.InvariantCulture);
        }
    }

Version: NCalcSync 3.8.0

@gumbarros gumbarros added the bug label Apr 22, 2024
@AlPecin
Copy link

AlPecin commented May 6, 2024

Hi,
I'm looking for your problem, I think the exception is the result of a recursion to the Iterate loop in the Expression.cs. I suggest the following changes:

Add the marked line to this method in FuntionArgs.cs

public object[] EvaluateParameters()
{
    var values = new object[_parameters.Length];
    for (var i = 0; i < values.Length; i++)
    {
        parameters[i].Options &= ~EvaluateOptions.IterateParameters;  //<- add this line
        values[i] = _parameters[i].Evaluate();
    }

    return values;
}

``
and change your code to this:

        private static void EndsWith(string name, FunctionArgs args)
        {
            if (name == "EndsWith")
            {
                var parameters = args.EvaluateParameters();

                var firstParam = parameters[0];
                var secondParam = parameters[1];

                if (firstParam is not string firstArg || secondParam is not string secondArg)
                {
                    throw new FormatException();
                }

                args.Result = firstArg.EndsWith(secondArg, StringComparison.InvariantCulture);
            }
        }

Without change in ncalc you can do this

    private static void EndsWith(string name, FunctionArgs args)
    {
        if (name == "EndsWith")
        {
            foreach (var parameterExpression in args.Parameters)
                parameterExpression.Options &= ~EvaluateOptions.IterateParameters;

            var firstParam = args.Parameters[0].Evaluate();
            var secondParam = args.Parameters[1].Evaluate();

            if (firstParam is not string firstArg || secondParam is not string secondArg)
            {
                throw new FormatException();
            }

            args.Result = firstArg.EndsWith(secondArg, StringComparison.InvariantCulture);
        }
    }

``

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants