C# Calculator with base methods.

Hi, everyone!

In my latest project I have to wrote calculator for models with another logic than basic arithmetic operations. It doesn’t matter for my latest words, just see results.

For implementing we have to create parser of expressions. All mathemactic experssions we can illustrate as tree with tokens(nodes) and part of token node value(generic class).

Token Types:

  1. Operand.
  2. Operator.
  3. Precedence( ‘)’, ‘(’ ).
using System;

namespace Voodoo.Expressions
{
    /// 
    /// Class implements container for storing intermediate values of parsed line.
    /// Save tokens for expression parser class.
    /// 
    /// 
    public struct Token<T>
    {
        /// 
        /// Field contains available operands in the expression draft line.
        /// 
        public static readonly String Operands = "+-*/()";

        public T Operand { get; set; }
        public char Operator { get; set; }
        public int Precedence { get; set; }

        public override string ToString()
        {
            return Operator != 0 ? Operator.ToString() : Operand.ToString();
        }
    }
}

Node value we will transfer using generic T param.

namespace Voodoo.Expressions
{
    /// 
    /// Class implements container for storing intermediate values in the expression
    /// parser class.
    /// 
    /// The type of the variable.
    /// The type of the number.
    public class Node<T1, T2>
    {
        public Node()
        {
        }

        public Node(T1 variable, T2 number)
        {
            Variable = variable;
            Number = number;
        }

        public T1 Variable { get; set; }

        public T2 Number { get; set; }
    }
}

Now I show abstract class for parsing expression and making expression tree. In our class we can see abstracts method for add, sub, mul, div operations that’s you can simply override with your implementation.

using System;
using System.Collections.Generic;

namespace Voodoo.Expressions
{
    /// <summary>
    /// Base class implements method for parsing draft line of expression.
    /// </summary>
    /// <typeparam name="T">The type of the 1.</typeparam>
    public abstract class ExpressionParserBase<T, T2>
    {
        private readonly Dictionary<String, T2> m_variables = new Dictionary<String, T2>();

        /// <summary>
        /// Initializes a new instance of the <see cref="ExpressionParserBase<T>"/> class.
        /// </summary>
        /// <param name="expression">The expression.</param>
        protected ExpressionParserBase(String expression)
        {
            Expression = expression;
        }

        /// <summary>
        /// Gets or sets the variables.
        /// </summary>
        /// <value>The variables.</value>
        public Dictionary<String, T2> Variables
        {
            get { return m_variables; }
        }

        /// <summary>
        /// Gets or sets the expression.
        /// </summary>
        /// <value>The expression.</value>
        public String Expression { get; private set; }

        /// <summary>
        /// Gets or sets the tokens.
        /// </summary>
        /// <value>The tokens.</value>
        public List<Token<T>> Tokens { get; private set; }

        /// <summary>
        /// Convert string to T.
        /// </summary>
        /// <param name="variable">The variable.</param>
        /// <returns></returns>
        protected abstract T ConvertStringToT(String variable);

        /// <summary>
        /// Adds the specified stack.
        /// </summary>
        /// <param name="stack">The stack.</param>
        /// <param name="v1">The v1.</param>
        /// <param name="v2">The v2.</param>
        /// <returns></returns>
        protected abstract T Add(Stack<T> stack, T v1, T v2);

        /// <summary>
        /// Subs the specified stack.
        /// </summary>
        /// <param name="stack">The stack.</param>
        /// <param name="v1">The v1.</param>
        /// <param name="v2">The v2.</param>
        /// <returns></returns>
        protected abstract T Sub(Stack<T> stack, T v1, T v2);

        /// <summary>
        /// Divs the specified stack.
        /// </summary>
        /// <param name="stack">The stack.</param>
        /// <param name="v1">The v1.</param>
        /// <param name="v2">The v2.</param>
        /// <returns></returns>
        protected abstract T Div(Stack<T> stack, T v1, T v2);

        /// <summary>
        /// Muls the specified stack.
        /// </summary>
        /// <param name="stack">The stack.</param>
        /// <param name="v1">The v1.</param>
        /// <param name="v2">The v2.</param>
        /// <returns></returns>
        protected abstract T Mul(Stack<T> stack, T v1, T v2);

        /// <summary>
        /// Tokenizes the specified line.
        /// </summary>
        /// <param name="line">The line.</param>
        /// <returns></returns>
        private List<Token<T>> Tokenize(IEnumerable<char> line)
        {
            var tokens = new List<Token<T>>();
            String variable = String.Empty;
            foreach (char arg in line)
            {
                var token = new Token<T>();
                if (Token<T>.Operands.Contains(arg.ToString()) &&
                    !String.IsNullOrEmpty(variable))
                {
                    token.Operand = ConvertStringToT(variable);
                    variable = String.Empty;
                    token.Precedence = 0;
                    tokens.Add(token);
                }
                switch (arg)
                {
                    case '+':
                    case '-':
                        token.Operator = arg;
                        token.Precedence = 2;
                        tokens.Add(token);
                        break;
                    case '*':
                    case '/':
                        token.Operator = arg;
                        token.Precedence = 1;
                        tokens.Add(token);
                        break;
                    case '(':
                        token.Operator = arg;
                        token.Precedence = -1;
                        tokens.Add(token);
                        break;
                    case ')':
                        token.Operator = arg;
                        token.Precedence = 3;
                        tokens.Add(token);
                        break;
                    default:
                        variable += arg;
                        break;
                }
            }

            if (!String.IsNullOrEmpty(variable))
            {
                var token = new Token<T> {Operand = ConvertStringToT(variable), Precedence = 0};
                tokens.Add(token);
            }

            return tokens;
        }

        /// <summary>
        /// Posts the fix.
        /// </summary>
        /// <param name="tokens">The tokens.</param>
        private void PostFix(List<Token<T>> tokens)
        {
            for (int i = 0; i < tokens.Count - 1; i++)
            {
                if (tokens[i].Precedence > tokens[i + 1].Precedence)
                {
                    int j = i;
                    int scopes = 0;
                    while (j < tokens.Count - 1 && (scopes > 0 ||
                                                    tokens[j].Precedence > tokens[j + 1].Precedence))
                    {
                        Token<T> tmp = tokens[j];
                        tokens[j] = tokens[j + 1];
                        tokens[j + 1] = tmp;
                        scopes += ((tokens[j].Operator == '(') ? 1 : 0)
                                  - ((tokens[j].Operator == ')') ? 1 : 0);
                        j++;
                    }
                    i--;
                }
            }
        }

        /// <summary>
        /// Gets the result of execution.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        protected abstract T2 GetResult(T value);

        /// <summary>
        /// Calculate expression.
        /// </summary>
        /// <returns></returns>
        public virtual T2 Execute()
        {
            Tokens = Tokenize(Expression);
            PostFix(Tokens);

            var stack = new Stack<T>();
            foreach (var token in Tokens)
            {
                switch (token.Operator)
                {
                    case '+':
                        Add(stack, stack.Pop(), stack.Pop());
                        break;
                    case '-':
                        Sub(stack, stack.Pop(), stack.Pop());
                        break;
                    case '*':
                        Mul(stack, stack.Pop(), stack.Pop());
                        break;
                    case '/':
                        Div(stack, stack.Pop(), stack.Pop());
                        break;
                    case '(':
                    case ')':
                        break;
                    default:
                        stack.Push(token.Operand);
                        break;
                }
            }
            return GetResult(stack.Pop());
        }
    }

And using base model of expression parser we can implement our arithmetic operations calculator. See example:

using System;
using System.Collections.Generic;

namespace Voodoo.Expressions
{
    /// <summary>
    /// Class implements basic expression evaluator.
    /// </summary>
    public class Calculator : ExpressionParserBase<float, float>
    {
        public Calculator(String expression)
            : base(expression)
        {
        }

        /// <summary>
        /// Adds the specified stack.
        /// </summary>
        /// <param name="stack">The stack.</param>
        /// <param name="v1">The v1.</param>
        /// <param name="v2">The v2.</param>
        /// <returns></returns>
        protected override float Add(Stack<float> stack, float v1, float v2)
        {
            float value = v1 + v2;
            stack.Push(value);
            return value;
        }

        /// <summary>
        /// Subs the specified stack.
        /// </summary>
        /// <param name="stack">The stack.</param>
        /// <param name="v1">The v1.</param>
        /// <param name="v2">The v2.</param>
        /// <returns></returns>
        protected override float Sub(Stack<float> stack, float v1, float v2)
        {
            float value = v2 - v1;
            stack.Push(value);
            return value;
        }

        /// <summary>
        /// Divs the specified stack.
        /// </summary>
        /// <param name="stack">The stack.</param>
        /// <param name="v1">The v1.</param>
        /// <param name="v2">The v2.</param>
        /// <returns></returns>
        protected override float Div(Stack<float> stack, float v1, float v2)
        {
            float value = v2/v1;
            stack.Push(value);
            return value;
        }

        /// <summary>
        /// Muls the specified stack.
        /// </summary>
        /// <param name="stack">The stack.</param>
        /// <param name="v1">The v1.</param>
        /// <param name="v2">The v2.</param>
        /// <returns></returns>
        protected override float Mul(Stack<float> stack, float v1, float v2)
        {
            float value = v2 - v1;
            stack.Push(value);
            return value;
        }

        /// <summary>
        /// Gets the result.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        protected override float GetResult(float value)
        {
            return value;
        }

        /// <summary>
        /// Converts the string to T.
        /// </summary>
        /// <param name="variable">The variable.</param>
        /// <returns></returns>
        protected override float ConvertStringToT(string variable)
        {
            float result = 0;
            if (!float.TryParse(variable, out result))
            {
                throw new ArgumentException("Unknown token: " + variable);
            }
            return result;
        }
    }
}

Thanks for reading!!!

1 note