dnns.dev | Dennis vd Waardenburg, software engineerarchitectdude

Expression evaluation in C#

This is just a test for the markdawn parser. That's it. I will explain the stuff below soon. It needs improvements. But works.

public class ExpressionEval
{
    /// <summary>
    /// Allowed (and supported) operands
    /// </summary>
    private readonly string _operators = "+-/*^";

    public string Expression { get; private set; }

    public ExpressionEval(string expression)
    {
        Expression = expression;
    }

    public double Outcome() {

        Stack<double> numbers = new Stack<double>();
        Stack<char> operators = new Stack<char>();

        for (int i = 0; i < Expression.Length; i++)
        {
            char _char = Expression[i];

            // possible start of expression
            if (_char == '(') {
                
                operators.Push(_char);
            }
            // end of possible expression
            else if (_char == ')') {
                
                while (operators.Peek() != '(') {
                
                    char op = operators.Pop();
                    double param_ = numbers.Pop();
                    double param = numbers.Pop();
                    double calculated = apply(op, param, param_);

                    numbers.Push(calculated);
                }

                operators.Pop();
            }
            else if (_operators.Contains(_char)) {

                while (operators.Count > 0 && prio(operators.Peek()) >= prio(_char)) {
                
                    char op = operators.Pop();
                    double param_ = numbers.Pop();
                    double param = numbers.Pop();
                    double calculated = apply(op, param, param_);
                    
                    numbers.Push(calculated);
                }

                operators.Push(_char);
            }
            else if (char.IsDigit(_char) || _char == '.') {
                
                StringBuilder number = new StringBuilder();

                while (char.IsDigit(_char) || _char == '.') {

                    number.Append(_char);
                    i++;

                    if (i == Expression.Length) {
                        break;
                    }

                    _char = Expression[i];
                }

                i--;

                numbers.Push(double.Parse(number.ToString()));
            }
        }

        while (operators.Count > 0) {
            
            char op = operators.Pop();
            double param_ = numbers.Pop();
            double param = numbers.Pop();

            double calculated = apply(op, param, param_);
            numbers.Push(calculated);
        }

        return numbers.Pop();
    }

    /// <summary>
    /// Apply operation with o1 and o2.
    /// </summary>
    /// <param name="operation"></param>
    /// <param name="o1"></param>
    /// <param name="o2"></param>
    /// <returns></returns>
    private double apply(char operation, double o1, double o2) => operation switch
    {
        '+' => o1 + o2,
        '-' => o1 - o2,
        '*' => o1 * o2,
        '/' => o1 / o2,
        '^' => Math.Pow(o1, o2),
        _ => 0
    };

    /// <summary>
    /// Get priority for each operandus
    /// </summary>
    /// <param name="operation"></param>
    /// <returns></returns>
    private int prio(char operation) => operation switch
    {
        '+' => 1,
        '-' => 1,
        '*' => 2,
        '/' => 2,
        '^' => 3,
        _ => 0
    };
}

You use it like:

ExpressionEval ee = new ExpressionEval("22^4");
Console.WriteLine($"Outcome: {ee.Outcome()}");