String Expression Calculator

Parsing of math equations from text expressions is the interesting thing. This task is close to LL parsing but can be solved in different ways. This approach tends to be the simplest one.
From time to time some of your customers might ask you to extend current functions of your system with ability to specify some simple math formulas. This is tricky challenge if you have no relevant experience. Well, omitting all that theoretical things and go further. The code below could help you calculate simple math formulas with braces. Take a look at the screen-shot to get what I mean:

Below you can find that evaluator which can be simply integrated into your code. Have a look at the usage sample below:

StrExpr.StrExpr se = new StrExpr.StrExpr();
txtResult.Text = se.Parse(txtExpression.Text);

And the evaluator source code:

public class StrExpr
{
    private readonly Hashtable _opTagList = new Hashtable();
    private ArrayList _opList;
    private ArrayList _figuresList;

    public StrExpr()
    {
        _opTagList.Add('+', 0);
        _opTagList.Add('-', 0);
        _opTagList.Add('*', 1);
        _opTagList.Add('/', 1);
        _opTagList.Add('(', 10);
        _opTagList.Add(')', -10);
    }

    public string Parse(string text)
    {
        _figuresList = new ArrayList();
        _opList = new ArrayList();
        return this.Evaluate(text.Trim());
    }

    private string Evaluate(string text)
    {
        if (text != null)
        {
            #region Parsing
            StringBuilder expr = new StringBuilder(text);
            char[] ops = new char[] { '+', '-', '*', '/', '(', ')' };
            int opIndex = expr.ToString().IndexOfAny(ops);
            int priority = 0;
            while (opIndex != -1)
            {
                if (opIndex > 0)
                {
                    _figuresList.Add(double.Parse(expr.ToString().Substring(0, opIndex)));
                    expr.Remove(0, opIndex);
                    opIndex = expr.ToString().IndexOfAny(ops);
                    if (opIndex == -1)
                    break;
                }
                char op = expr[0];
                priority += (int)_opTagList[op];
                if (!((op == '(') || (op == ')')))
                {
                    _opList.Add(new DictionaryEntry(op, priority));
                }
                expr.Remove(0, 1);
                opIndex = expr.ToString().IndexOfAny(ops);
                if ((opIndex == -1) && (expr.Length > 0))
                opIndex = expr.Length;
            }
            #endregion
            #region Calculating
            while (_opList.Count > 0)
            {
                #region Find max priority
                DictionaryEntry maxPriorityOp = (DictionaryEntry)_opList[0];
                foreach (DictionaryEntry entry in _opList)
                {
                    if ((int)entry.Value > (int)maxPriorityOp.Value)
                    {
                        maxPriorityOp = entry;
                    }
                }
                #endregion
                int index = _opList.IndexOf(maxPriorityOp);
                double op1 = (double)_figuresList[index];
                double op2 = (double)_figuresList[index + 1];
                object d = null;
                switch ((char)maxPriorityOp.Key)
                {
                    case '+':
                        d = op1 + op2;
                        break;
                    case '-':
                        d = op1 - op2;
                        break;
                    case '*':
                        d = op1 * op2;
                        break;
                    case '/':
                        d = op1 / op2;
                        break;
                }
                _opList.Remove(maxPriorityOp);
                _figuresList.RemoveAt(index + 1);
                _figuresList.RemoveAt(index);
                if (d != null)
                _figuresList.Insert(index, (double)d);
            }
            return _figuresList[0].ToString();
            #endregion
        }
        return string.Empty;
    }
}

 

Tags:

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Related Post

%d bloggers like this: