Search code examples
c#stringexpressionncalc

Get the parameters in an expression using NCalc


I have an expression which I want to parse to get a list of all used parameters.

For example: "X + 5 / (Y - 1)" should give me the following result: X, Y

I already use NCalc in my project; so is it possible to use NCalc to get the parameters used in an expression?

According to this one discussion entry (https://ncalc.codeplex.com/discussions/361959) it is, but I don't quite understand the answer.


Solution

  • From the discussion/answer here: http://ncalc.codeplex.com/discussions/360990

    A implementation that I've tested and works (for your provided sample expression) is to implement a LogicalExpressionVisitor and have it record the parameters as they are found:

    class ParameterExtractionVisitor : LogicalExpressionVisitor
    {
        public HashSet<string> Parameters = new HashSet<string>();
    
        public override void Visit(NCalc.Domain.Identifier function)
        {
            //Parameter - add to list
            Parameters.Add(function.Name);
        }
    
        public override void Visit(NCalc.Domain.UnaryExpression expression)
        {
            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);
        }
    
        public override void Visit(Function function)
        {
            foreach (var expression in function.Expressions)
            {
                expression.Accept(this);
            }
        }
    
        public override void Visit(LogicalExpression expression)
        {
    
        }
    
        public override void Visit(ValueExpression expression)
        {
    
        }
    }
    

    Then you would use it as:

    var expression = NCalc.Expression.Compile("2 * [x] ^ 2 + 5 * [y]", false);
    
    ParameterExtractionVisitor visitor = new ParameterExtractionVisitor();
    expression.Accept(visitor);
    
    var extractedParameters = visitor.Parameters;
    
    foreach (var param in extractedParameters)
        Console.WriteLine(param);
    

    This outputs "x" and "y" for me.

    Note the use of HashSet in the ParameterExtractionVisitor. This is because if your expression contains the same variable more than once (for example: "[x] + [x]") it will be added twice. If you want to store an entry each time the same variable is used, replace the HashSet with a List.


    That all said, I have very little experience with NCalc, so my implementation of the overridden methods of LogicalExpressionVisitor are guesses. When I overrode the void Visit(ValueExpression expression) method with expression.Accept(this), it resulted in a StackOverflowException. So I simply left the implementation blank and it seemed to work. So I would suggest that you take my answer here with a very large grain of salt. Your mileage may vary and I can't say if this works for all types of expressions.