Detecting All Parameters

Topics: New Features
Jun 26, 2012 at 3:01 PM

In a situation where I use NCalc, the expression itself is defined by the user, and it's up to the code to detect the presence of parameters and set them.  The event handler is nice, but it requires that all of my data be available in an event handler, and that's not a good approach for this situation.  I could write special code that deals with this, but really like polymorphism.  I could not find a method documented for finding all parameters from a single expression tree.  I solved the problem with the following:

In LogicalExpression, I created an abstract member called Terms:

        public abstract IEnumerable<LogicalExpression> Terms
        {
            get;
        }

In any class which inherrits from LogicalExpression, I override Terms.  For UnaryExpression, it returns new LogicalExpression[] { Expression }, for Binary it returns new LogicalExpression[] { LeftExpression, RightExpression }, etc.  For Parameters, Values, etc, it returns new LogicalExpression[] { }.  With this in place I can walk the expression tree from Expression.ParsedExpression and detect all parameters that exist, knowing that any future implementor of LogicalExpression will also implement Terms.

If there is another way to do what I have done, please let me know, but if not, I would appreciate if this logic could be added to the mainline development.  I used IEnumerable on the abstract, but if you would prefer use an array, that works.  If you would prefer call it Expressions as it is for Function instead of Terms, that works too.  Still, this functionality of easily identifying all parameters before evaluating an expression is helpful.

Thank you,

dmp

Feb 6, 2014 at 10:54 AM
This is a long time after the question but I have been looking at how to do this and thought it might be useful in the future.

You can create a visitor to get this information by implementing NCalc.Domain.LogicalExpressionVisitor. You can get the names of the parameters by overriding the Visit function for the Identifier type e.g.:
    List<string> parameters = new List<string>();

    public override void Visit(NCalc.Domain.Identifier function)
    {
        //Parameter - add to list
        parameters.Add(function.Name);
    }

In order to get the visitor to navigate its way through the expression tree you also need to provide implementations for all compound/node expression types (those that can contain other expressions. The implementations just need to make sure the visitor visits each node e.g.:
    public override void Visit(NCalc.Domain.UnaryExpression expression)
    {
        expression.Accept(this);
    }

    public override void Visit(NCalc.Domain.BinaryExpression expression)
    {
       //Visit left and right
        expression.LeftExpression.Accept(this);
        expression.RightExpression.Accept(this);
    }

    public override void Visit(NCalc.Domain.TernaryExpression expression)
    {
        //Visit left, right and middle
        expression.LeftExpression.Accept(this);
        expression.RightExpression.Accept(this);
        expression.MiddleExpression.Accept(this);
    }
All of the other implemented methods can be left blank as they are for leaf expression types.

In your code create an instance of your visitor and use the 'accept' method on the root of the expression tree to extract all of the parameter names:
        var expr = NCalc.Expression.Compile("1+2+3/4+[parameter1]+[parameter2]", false);

        ParameterExtractionVisitor visitor = new ParameterExtractionVisitor();
        expr.Accept(visitor);
        List<string> extractedParameters = visitor.Parameters;

If all goes to plan you will have a list containing the names of the parameters ["parameter1", "parameter2"] in this case.

This is probably too late for the original question but hopefully it will help someone else trying to do the same thing!
Mar 9, 2014 at 7:25 AM
Sometimes fetching parameters from other sources one by one is expensive so It would be nice if this feature to be included in future releases.