Click here to Skip to main content
15,887,821 members
Articles / Programming Languages / C#

Yet Another Math Parser (YAMP)

Rate me:
Please Sign up or sign in to vote.
4.98/5 (54 votes)
30 Sep 2012CPOL21 min read 121.7K   2.6K   93  
Constructing a fast math parser using Reflection to do numerics like Matlab.
using System;
using System.Collections.Generic;
using System.Linq;
using MathParserNet.Exceptions;

namespace MathParserNet
{
	public delegate T Func<T, U> (U arg1);
	public delegate T Func<T, U, V> (U arg1, V arg2);
	public delegate T Func<T, U, V, W> (U arg1, V arg2, W arg3);
	public delegate T Func<T, U, V, W, X> (U arg1, V arg2, W arg3, X arg4);
	public delegate T Func<T, U, V, W, X, Y> (U arg1, V arg2, W arg3, X arg4, Y arg5);
	
    public class Parser
    {
        private readonly Queue<NumberClass> _outputQueue;
        private readonly Stack<string> _operatorStack;
        private readonly Dictionary<string, NumberClass> _variables;
        private readonly List<FunctionClass> _functions;
        private readonly Dictionary<string, Delegate> _customFunctions;

        public enum RoundingMethods
        {
            Round,
            RoundUp,
            RoundDown,
            Truncate
        }

        public Parser()
        {
            _outputQueue = new Queue<NumberClass>();
            _operatorStack = new Stack<string>();
            _variables = new Dictionary<string, NumberClass>();
            _functions = new List<FunctionClass>();
            _customFunctions = new Dictionary<string, Delegate>();
        }

        public void UnregisterCustomFunction(string functionName)
        {
            try
            {
                _customFunctions.Remove(functionName);
            }
            catch (Exception)
            {
                throw new NoSuchFunctionException();
            }
        }

        public void UnregisterAllCustomFunctions()
        {
            _customFunctions.Clear();
        }

        public void RegisterCustomFunction(string functionName, Func<object, object> method)
        {
            _customFunctions.Add(functionName, method);
        }

        public void RegisterCustomFunction(string functionName, Func<object, object, object> method)
        {
            _customFunctions.Add(functionName, method);
        }

        public void RegisterCustomFunction(string functionName, Func<object, object, object, object> method)
        {
            _customFunctions.Add(functionName, method);
        }

        public void RegisterCustomFunction(string functionName, Func<object, object, object, object, object> method)
        {
            _customFunctions.Add(functionName, method);
        }

        public void RegisterCustomIntegerFunction(string functionName, Func<int, int> method)
        {
            _customFunctions.Add(functionName, method);
        }

        public void RegisterCustomIntegerFunction(string functionName, Func<int, int, int> method)
        {
            _customFunctions.Add(functionName, method);
        }

        public void RegisterCustomIntegerFunction(string functionName, Func<int, int, int, int> method)
        {
            _customFunctions.Add(functionName, method);
        }

        public void RegisterCustomIntegerFunction(string functionName, Func<int, int, int, int, int> method)
        {
            _customFunctions.Add(functionName, method);
        }

        public void RegisterCustomDoubleFunction(string functionName, Func<double, double> method)
        {
            _customFunctions.Add(functionName, method);
        }

        public void RegisterCustomDoubleFunction(string functionName, Func<double, double, double> method)
        {
            _customFunctions.Add(functionName, method);
        }

        public void RegisterCustomDoubleFunction(string functionName, Func<double, double, double, double> method)
        {
            _customFunctions.Add(functionName, method);
        }

        public void RegisterCustomDoubleFunction(string functionName, Func<double, double, double, double, double> method)
        {
            _customFunctions.Add(functionName, method);
        }

        public void RemoveFunction(string functionName)
        {
			bool found = false;
			
			foreach(var _f in _functions)
			{
				if(_f.Name.Equals(functionName))
				{
					found = true;
					break;
				}
			}

            if (found)
            {
                int ndx = 0;

                while (ndx < _functions.Count)
                {
                    if (_functions[ndx].Name.Equals(functionName))
                        break;
                    ndx++;
                }
                _functions.RemoveAt(ndx);
                return;
            }

            throw new NoSuchFunctionException(StringResources.No_such_function_defined + ": " + functionName);
        }

        public void AddFunction(string functionName, FunctionArgumentList argList, string expression)
        {
            var fc = new FunctionClass { Arguments = argList, Expression = expression, Name = functionName };
            var numClass = new NumberClass { NumberType = NumberClass.NumberTypes.Expression, Expression = expression };

            EvaluateFunction(numClass, fc);

            _functions.Add(fc);
        }

        public void RemoveAllVariables()
        {
            _variables.Clear();
        }

        public void RemoveAllFunctions()
        {
            _functions.Clear();
        }

        public void Reset()
        {
            RemoveAllFunctions();
            RemoveAllVariables();
            UnregisterAllCustomFunctions();
        }

        public void RemoveVariable(string varName)
        {
            if (_variables.ContainsKey(varName))
            {
                _variables.Remove(varName);
                return;
            }

            throw new Exception(StringResources.Undefined_Variable + ": " + varName);
        }

        private void AddVariable(string varName, NumberClass valueType)
        {
            if (_variables.ContainsKey(varName))
            {
                throw new VariableAlreadyDefinedException(StringResources.Variable_already_defined + ": " + varName);
            }

            if (valueType.NumberType == NumberClass.NumberTypes.Expression)
            {
                SimplificationReturnValue eeVal;

                eeVal = EvaluateExpression(valueType);

                if (eeVal.ReturnType == SimplificationReturnValue.ReturnTypes.Float)
                {
                    AddVariable(varName, eeVal.DoubleValue);
                    return;
                }
                if (eeVal.ReturnType == SimplificationReturnValue.ReturnTypes.Integer)
                {
                    AddVariable(varName, eeVal.IntValue);
                    return;
                }
            }
            _variables.Add(varName, valueType);
        }

        public void AddVariable(string varName, string value)
        {
            var nc = new NumberClass { NumberType = NumberClass.NumberTypes.Expression, Expression = value };

            AddVariable(varName, nc);
        }

        public void AddVariable(string varName, double value)
        {
            var nc = new NumberClass { NumberType = NumberClass.NumberTypes.Float, FloatNumber = value };

            AddVariable(varName, nc);
        }

        public void AddVariable(string varName, int value)
        {
            var nc = new NumberClass { NumberType = NumberClass.NumberTypes.Integer, IntNumber = value };

            AddVariable(varName, nc);
        }

        private void EvaluateFunction(NumberClass expression, FunctionClass fc)
        {
            EvaluateFunction(expression, fc, new List<NumberClass> {
                               new NumberClass {NumberType = NumberClass.NumberTypes.Integer, IntNumber = 0}});
        }

        private SimplificationReturnValue EvaluateFunction(NumberClass expression, FunctionClass fc, IEnumerable<NumberClass> ncList2)
        {
            var parser = new Parser();
			var ncList = new List<NumberClass>(ncList2);

            foreach (var cfr in _customFunctions)
                parser.AddCustomFunction(cfr.Key, cfr.Value);

            foreach (var v in _variables)
            {
                if (v.Value.NumberType == NumberClass.NumberTypes.Float)
                {
                    parser.AddVariable(v.Key, v.Value.FloatNumber);
                }
                if (v.Value.NumberType == NumberClass.NumberTypes.Integer)
                {
                    parser.AddVariable(v.Key, v.Value.IntNumber);
                }
                if (v.Value.NumberType == NumberClass.NumberTypes.Expression)
                {
                    parser.AddVariable(v.Key, v.Value.Expression);
                }
            }

            foreach (var f in _functions)
            {
                parser.AddFunction(f.Name, f.Arguments, f.Expression);
            }

            int ndx = 0;
            foreach (var a in fc.Arguments)
            {
                NumberClass nc = ndx >= ncList.Count ? 
					new NumberClass 
					{ 
						NumberType = NumberClass.NumberTypes.Integer, 
						IntNumber = 0
					} : ncList[ndx];

                if (nc.NumberType == NumberClass.NumberTypes.Float)
                {
                    try
                    {
                        parser.AddVariable(a, nc.FloatNumber);
                    } catch
                    {}
                }
                if (nc.NumberType == NumberClass.NumberTypes.Integer)
                {
                    try
                    {
                        parser.AddVariable(a, nc.IntNumber);
                    }catch
                    {
                    }
                }
                if (nc.NumberType == NumberClass.NumberTypes.Expression)
                    parser.AddVariable(a, nc.Expression);
                ndx++;
            }
            
            return parser.Simplify(expression.Expression);
        }

        protected void AddCustomFunction(string s, Delegate d)
        {
            _customFunctions.Add(s, d);
        }

        private SimplificationReturnValue EvaluateExpression(NumberClass expression)
        {
            var parser = new Parser();

            foreach (var cfh in _customFunctions)
                parser.AddCustomFunction(cfh.Key,cfh.Value);

            foreach (var v in _variables)
            {
                if (v.Value.NumberType == NumberClass.NumberTypes.Float)
                {
                    parser.AddVariable(v.Key, v.Value.FloatNumber);
                }
                if (v.Value.NumberType == NumberClass.NumberTypes.Integer)
                {
                    parser.AddVariable(v.Key, v.Value.IntNumber);
                }
                if (v.Value.NumberType == NumberClass.NumberTypes.Expression)
                {
                    parser.AddVariable(v.Key, v.Value.Expression);
                }
            }

            foreach (var f in _functions)
            {
                parser.AddFunction(f.Name, f.Arguments, f.Expression);
            }
            return parser.Simplify(expression.Expression);
        }

        public object SimplifyObject(string equation)
        {
            var retval = Simplify(equation);

            if (retval.ReturnType == SimplificationReturnValue.ReturnTypes.Integer)
                return retval.IntValue;

            return retval.DoubleValue;
        }

        public int SimplifyInt(string equation, RoundingMethods roundMethod)
        {
            SimplificationReturnValue retval = Simplify(equation);

            if (retval.ReturnType == SimplificationReturnValue.ReturnTypes.Integer)
                return retval.IntValue;

            if (roundMethod == RoundingMethods.RoundDown)
                return (int)Math.Floor(retval.DoubleValue);
            if (roundMethod == RoundingMethods.RoundUp)
                return (int)Math.Ceiling(retval.DoubleValue);
            if (roundMethod == RoundingMethods.Round)
                return (int)Math.Round(retval.DoubleValue);

            return (int)Math.Truncate(retval.DoubleValue);

        }

        public int SimplifyInt(string equation)
        {
            return SimplifyInt(equation, RoundingMethods.Round);
        }

        public double SimplifyDouble(string equation)
        {
            SimplificationReturnValue retval = Simplify(equation);

            if (retval.ReturnType == SimplificationReturnValue.ReturnTypes.Float)
                return retval.DoubleValue;

            return double.Parse(retval.IntValue.ToString());
        }

        public SimplificationReturnValue Simplify(string equation)
        {
            var retval = new SimplificationReturnValue { OriginalEquation = equation };

            if (equation.Trim().StartsWith("-") || equation.Trim().StartsWith("+"))
                equation = "0" + equation;
            equation = equation.Replace("(+", "(0+");
            equation = equation.Replace("(-", "(0-");
            equation = equation.Replace("( +", "( 0+");
            equation = equation.Replace("( -", "( 0-");
            equation = equation.Replace(",-", ",0-");
            equation = equation.Replace(", -", ", 0-");
            equation = equation.Replace(",+", ",0+");
            equation = equation.Replace(", +", ", 0+");

            var tp = new TokenParser();

            foreach (var cf in _customFunctions)
            {
                tp.RegisterCustomFunction(cf.Key);
            }

            tp.InputString = equation;
            
            Token token = tp.GetToken();

            _operatorStack.Clear();
            _outputQueue.Clear();

            while (token != null)
            {
                if (token.TokenName == TokenParser.Tokens.Sqrt)
                {
                    string expression = token.TokenValue.Substring(4, token.TokenValue.Length - 4);

                    SimplificationReturnValue rv = EvaluateExpression(new NumberClass
                                                                          {
                                                                              Expression = expression,
                                                                              NumberType = NumberClass.NumberTypes.Expression
                                                                          });


                    token.TokenName = TokenParser.Tokens.Float;

                    switch (rv.ReturnType)
                    {
                        case SimplificationReturnValue.ReturnTypes.Integer:
                            token.TokenValue = Math.Sqrt(rv.IntValue).ToString();
                            break;
                        case SimplificationReturnValue.ReturnTypes.Float:
                            token.TokenValue = Math.Sqrt(rv.DoubleValue).ToString();
                            break;
                    }
                }
                if (token.TokenName == TokenParser.Tokens.Sin)
                {
                    string expression = token.TokenValue.Substring(3, token.TokenValue.Length - 3);

                    SimplificationReturnValue rv =
                        EvaluateExpression(new NumberClass
                        {
                            Expression = expression,
                            NumberType = NumberClass.NumberTypes.Expression
                        });

                    token.TokenName = TokenParser.Tokens.Float;

                    switch (rv.ReturnType)
                    {
                        case SimplificationReturnValue.ReturnTypes.Integer:
                            token.TokenValue = Math.Sin(rv.IntValue).ToString();
                            break;
                        case SimplificationReturnValue.ReturnTypes.Float:
                            token.TokenValue = Math.Sin(rv.DoubleValue).ToString();
                            break;
                    }
                }
                if (token.TokenName == TokenParser.Tokens.Log)
                {
                    string expression = token.TokenValue.Substring(3, token.TokenValue.Length - 3);

                    SimplificationReturnValue rv =
                        EvaluateExpression(new NumberClass
                        {
                            Expression = expression,
                            NumberType = NumberClass.NumberTypes.Expression
                        });

                    token.TokenName = TokenParser.Tokens.Float;

                    switch (rv.ReturnType)
                    {
                        case SimplificationReturnValue.ReturnTypes.Integer:
                            token.TokenValue = Math.Log(rv.IntValue, 10).ToString();
                            break;
                        case SimplificationReturnValue.ReturnTypes.Float:
                            token.TokenValue = Math.Log(rv.DoubleValue, 10).ToString();
                            break;
                    }
                }
                if (token.TokenName == TokenParser.Tokens.LogN)
                {
                    string expression = token.TokenValue.Substring(4, token.TokenValue.Length - 4);

                    SimplificationReturnValue rv =
                        EvaluateExpression(new NumberClass
                        {
                            Expression = expression,
                            NumberType = NumberClass.NumberTypes.Expression
                        });

                    token.TokenName = TokenParser.Tokens.Float;

                    switch (rv.ReturnType)
                    {
                        case SimplificationReturnValue.ReturnTypes.Integer:
                            token.TokenValue = Math.Log(rv.IntValue).ToString();
                            break;
                        case SimplificationReturnValue.ReturnTypes.Float:
                            token.TokenValue = Math.Log(rv.DoubleValue).ToString();
                            break;
                    }
                }
                if (token.TokenName == TokenParser.Tokens.Tan)
                {
                    string expression = token.TokenValue.Substring(3, token.TokenValue.Length - 3);

                    var rv =
                        EvaluateExpression(new NumberClass
                        {
                            Expression = expression,
                            NumberType = NumberClass.NumberTypes.Expression
                        });

                    token.TokenName = TokenParser.Tokens.Float;

                    switch (rv.ReturnType)
                    {
                        case SimplificationReturnValue.ReturnTypes.Integer:
                            token.TokenValue = Math.Tan(rv.IntValue).ToString();
                            break;
                        case SimplificationReturnValue.ReturnTypes.Float:
                            token.TokenValue = Math.Tan(rv.DoubleValue).ToString();
                            break;
                    }
                }
                if (token.TokenName == TokenParser.Tokens.Abs)
                {
                    string expression = token.TokenValue.Substring(3, token.TokenValue.Length - 3);

                    var rv =
                        EvaluateExpression(new NumberClass
                        {
                            Expression = expression,
                            NumberType = NumberClass.NumberTypes.Expression
                        });

                    token.TokenName = TokenParser.Tokens.Float;

                    switch (rv.ReturnType)
                    {
                        case SimplificationReturnValue.ReturnTypes.Integer:
                            token.TokenValue = Math.Abs(rv.IntValue).ToString();
                            break;
                        case SimplificationReturnValue.ReturnTypes.Float:
                            token.TokenValue = Math.Abs(rv.DoubleValue).ToString();
                            break;
                    }
                }
                if (token.TokenName == TokenParser.Tokens.Cos)
                {
                    string expression = token.TokenValue.Substring(3, token.TokenValue.Length - 3);

                    var rv =
                        EvaluateExpression(new NumberClass
                        {
                            Expression = expression,
                            NumberType = NumberClass.NumberTypes.Expression
                        });

                    token.TokenName = TokenParser.Tokens.Float;

                    switch (rv.ReturnType)
                    {
                        case SimplificationReturnValue.ReturnTypes.Integer:
                            token.TokenValue = Math.Cos(rv.IntValue).ToString();
                            break;
                        case SimplificationReturnValue.ReturnTypes.Float:
                            token.TokenValue = Math.Cos(rv.DoubleValue).ToString();
                            break;
                    }
                }
                if ((int)token.TokenName >= 100)
                {
                    int ndx1 = token.TokenValue.IndexOf("(");
                    string fn = token.TokenValue.Substring(0, ndx1);
                    string origExpression = token.TokenValue.Substring(ndx1);
                    string[] expressions = origExpression.Replace(",", "),(").Split(',');
                    bool found = false;
					
					foreach(var _f in _customFunctions)
					{
						if(_f.Key.Equals(fn))
						{
							found = true;
							break;
						}
					}

                    if (found)
                    {
                        foreach (var ff in _customFunctions)
                        {
                            if (ff.Key.Equals(fn))
                            {
                                var p = new Parser();
                                foreach (var cfr in _customFunctions)
                                    p.AddCustomFunction(cfr.Key, cfr.Value);
                                foreach (var vr in _variables)
                                    p.AddVariable(vr.Key, vr.Value);
                                foreach (var vf in _functions)
                                    p.AddFunction(vf.Name, vf.Arguments, vf.Expression);

                                var ex = new SimplificationReturnValue[expressions.Length]; 
								
								for(var i = 0; i < expressions.Length; i++)
								{
									ex[i] = p.Simplify(expressions[i]);
								}

                                object funcRetval = null;

                                if (ff.Value.Method.ReturnType == typeof(int))
                                {
                                    var intParams = new object[ex.Length];
                                    int ndx = 0;

                                    foreach (var pp in ex)
                                    {
                                        if (pp.ReturnType == SimplificationReturnValue.ReturnTypes.Float)
                                            intParams[ndx] = (int)pp.DoubleValue;
                                        if (pp.ReturnType == SimplificationReturnValue.ReturnTypes.Integer)
                                            intParams[ndx] = pp.IntValue;
                                        ndx++;
                                    }
                                    funcRetval = ff.Value.DynamicInvoke(intParams);
                                }
                                if (ff.Value.Method.ReturnType == typeof(double))
                                {
                                    var floatParams = new object[ex.Length];
                                    int ndx = 0;

                                    foreach (var pp in ex)
                                    {
                                        if (pp.ReturnType == SimplificationReturnValue.ReturnTypes.Float)
                                            floatParams[ndx] = pp.DoubleValue;
                                        if (pp.ReturnType == SimplificationReturnValue.ReturnTypes.Integer)
                                            floatParams[ndx] = pp.IntValue;
                                        ndx++;
                                    }
                                    funcRetval = ff.Value.DynamicInvoke(floatParams);
                                }
                                if (ff.Value.Method.ReturnType==typeof(object))
                                {
									var ex2 = new SimplificationReturnValue[expressions.Length];
									
									for(var i = 0; i < ex2.Length; i++)
										ex2[i] = p.Simplify(expressions[i]);
									
                                    funcRetval = ff.Value.DynamicInvoke(ex2);
                                }

                                //object funcRetval = ff.Value.DynamicInvoke(expressions.Select(p.Simplify).ToArray());

                                if (funcRetval is double)
                                {
                                    token.TokenValue = ((double)funcRetval).ToString();
                                    token.TokenName = TokenParser.Tokens.Float;
                                }
                                if (funcRetval is int)
                                {
                                    token.TokenValue = ((int)funcRetval).ToString();
                                    token.TokenName = TokenParser.Tokens.Integer;
                                }
                                if (funcRetval is SimplificationReturnValue)
                                {
                                    var srv = (SimplificationReturnValue)funcRetval;
                                    if (srv.ReturnType == SimplificationReturnValue.ReturnTypes.Integer)
                                    {
                                        token.TokenValue = srv.IntValue.ToString();
                                        token.TokenName = TokenParser.Tokens.Integer;
                                    }
                                    if (srv.ReturnType == SimplificationReturnValue.ReturnTypes.Float)
                                    {
                                        token.TokenValue = srv.DoubleValue.ToString();
                                        token.TokenName = TokenParser.Tokens.Float;
                                    }
                                }
                                break;
                            }
                        }
                    }

                    if (!found)
                    {
                        throw new NoSuchFunctionException(StringResources.No_such_function_defined + ": " + fn);
                    }
                }

                if (token.TokenName == TokenParser.Tokens.Function)
                {
                    int ndx1 = token.TokenValue.IndexOf("(");
                    string fn = token.TokenValue.Substring(0, ndx1).Remove(0, 4);
                    string origExpression = token.TokenValue.Substring(ndx1);
                    string[] expressions = origExpression.Replace(",", "),(").Split(',');
                    bool found = false;
                    FunctionClass fun = null;
					
					foreach(var _f in _functions)
					{
						if(_f.Name.Equals(fn))
						{
							found = true;
							break;
						}
					}

                    if (found)
                    {
                        foreach (var ff in _functions)
                            if (ff.Name.Equals(fn))
                                fun = ff;
                    }

                    if (!found)
                    {
                        throw new NoSuchFunctionException(StringResources.No_such_function_defined + ": " + fn);
                    }

                    var parser = new Parser();
                    foreach (var cfh in _customFunctions)
                        parser.AddCustomFunction(cfh.Key, cfh.Value);

                    foreach (var v in _variables)
                    {
                        if (v.Value.NumberType == NumberClass.NumberTypes.Float)
                        {
                            parser.AddVariable(v.Key, v.Value.FloatNumber);
                        }
                        if (v.Value.NumberType == NumberClass.NumberTypes.Integer)
                        {
                            parser.AddVariable(v.Key, v.Value.IntNumber);
                        }
                        if (v.Value.NumberType == NumberClass.NumberTypes.Expression)
                        {
                            parser.AddVariable(v.Key, v.Value.Expression);
                        }
                    }

                    foreach (var f in _functions)
                    {
                        parser.AddFunction(f.Name, f.Arguments, f.Expression);
                    }
                    var expressionList = new List<NumberClass>();

                    foreach (var expression in expressions)
                    {
                        SimplificationReturnValue simRetval = parser.Simplify(expression);

                        var numClass = new NumberClass();
                        if (simRetval.ReturnType == SimplificationReturnValue.ReturnTypes.Float)
                        {
                            numClass.FloatNumber = simRetval.DoubleValue;
                            numClass.NumberType = NumberClass.NumberTypes.Float;
                        }
                        if (simRetval.ReturnType == SimplificationReturnValue.ReturnTypes.Integer)
                        {
                            numClass.IntNumber = simRetval.IntValue;
                            numClass.NumberType = NumberClass.NumberTypes.Integer;
                        }
                        expressionList.Add(numClass);
                    }

                    if (fun != null)
                    {
                        var numClass = new NumberClass { NumberType = NumberClass.NumberTypes.Expression, Expression = fun.Expression };

                        SimplificationReturnValue sretval = parser.EvaluateFunction(numClass, fun, expressionList);

                        if (sretval != null && sretval.ReturnType == SimplificationReturnValue.ReturnTypes.Integer)
                        {
                            token.TokenName = TokenParser.Tokens.Integer;
                            token.TokenValue = sretval.IntValue.ToString();
                        }
                        if (sretval != null && sretval.ReturnType == SimplificationReturnValue.ReturnTypes.Float)
                        {
                            token.TokenName = TokenParser.Tokens.Float;
                            token.TokenValue = sretval.DoubleValue.ToString();
                        }
                    }
                }

                if (token.TokenName == TokenParser.Tokens.Variable)
                {
                    if (_variables.ContainsKey(token.TokenValue))
                    {
                        var z = _variables[token.TokenValue];

                        if (z.NumberType == NumberClass.NumberTypes.Float)
                        {
                            token.TokenName = TokenParser.Tokens.Float;
                            token.TokenValue = z.FloatNumber.ToString();
                        }
                        else if (z.NumberType == NumberClass.NumberTypes.Integer)
                        {
                            token.TokenName = TokenParser.Tokens.Integer;
                            token.TokenValue = z.IntNumber.ToString();
                        }
                    }
                    else
                    {
                        throw new NoSuchVariableException(StringResources.Undefined_Variable + ": " + token.TokenValue);
                    }
                }

                if (token.TokenName == TokenParser.Tokens.Whitespace || token.TokenName == TokenParser.Tokens.Newline)
                {
                    token = tp.GetToken();
                    continue;
                }
                if (token.TokenName == TokenParser.Tokens.Integer || token.TokenName == TokenParser.Tokens.Float)
                {
                    var nc = new NumberClass();

                    switch (token.TokenName)
                    {
                        case TokenParser.Tokens.Float:
                            nc.NumberType = NumberClass.NumberTypes.Float;
                            nc.FloatNumber = double.Parse(token.TokenValue);
                            break;
                        case TokenParser.Tokens.Integer:
                            nc.NumberType = NumberClass.NumberTypes.Integer;
                            nc.IntNumber = int.Parse(token.TokenValue);
                            break;
                    }
                    _outputQueue.Enqueue(nc);
                }
                if (IsOperator(token.TokenName))
                {
                    if (_operatorStack.Count > 0)
                    {
                        while (_operatorStack.Count > 0)
                        {
                            var op = _operatorStack.Peek(); //o2    

                            if (op == "(" || op == ")")
                                break;
                            if ((GetPrecedence(token.TokenName) <= GetPrecedence(op) &&
                                 IsLeftAssociative(token.TokenValue)) ||
                                !IsLeftAssociative(token.TokenValue) &&
                                GetPrecedence(token.TokenName) < GetPrecedence(op))
                            {
                                op = _operatorStack.Pop();
                                var nc = new NumberClass { NumberType = NumberClass.NumberTypes.Operator, Operator = op };

                                _outputQueue.Enqueue(nc);
                            }
                            else break;
                        }
                    }
                    _operatorStack.Push(token.TokenValue);
                }
                if (token.TokenName == TokenParser.Tokens.Lparen)
                    _operatorStack.Push(token.TokenValue);
                if (token.TokenName == TokenParser.Tokens.Rparen)
                {
                    if (_operatorStack.Count > 0)
                    {
                        var op = _operatorStack.Pop();

                        while (op != "(")
                        {
                            var nc = new NumberClass { Operator = op, NumberType = NumberClass.NumberTypes.Operator };

                            _outputQueue.Enqueue(nc);

                            if (_operatorStack.Count > 0)
                                op = _operatorStack.Pop();
                            else
                            {
                                throw new MismatchedParenthesisException();
                            }
                        }
                    }
                    else
                    {
                        throw new MismatchedParenthesisException();
                    }
                }
                token = tp.GetToken();
            }

            while (_operatorStack.Count > 0)
            {
                var op = _operatorStack.Pop();

                if (op == "(" || op == ")")
                {
                    throw new MismatchedParenthesisException();
                }

                var nc = new NumberClass { NumberType = NumberClass.NumberTypes.Operator, Operator = op };

                _outputQueue.Enqueue(nc);
            }

            bool floatAnswer = false;
			bool slashAnswer = false;
			
			foreach(var v in _outputQueue)
			{
				if(v.NumberType == NumberClass.NumberTypes.Float)
				{
					floatAnswer = true;
				}
			
				if(v.Operator == "/")
				{
					slashAnswer = true;
				}	
			}

            if (floatAnswer || slashAnswer)
            {
                var dblStack = new Stack<double>();

                foreach (var nc in _outputQueue)
                {
                    if (nc.NumberType == NumberClass.NumberTypes.Integer)
                        dblStack.Push(nc.IntNumber);
					
                    if (nc.NumberType == NumberClass.NumberTypes.Float)
                        dblStack.Push(nc.FloatNumber);
					
                    if (nc.NumberType == NumberClass.NumberTypes.Operator)
                    {
                        double val = DoMath(nc.Operator, dblStack.Pop(), dblStack.Pop());
                        dblStack.Push(val);
                    }
                }

                if (dblStack.Count == 0)
                    throw new CouldNotParseExpressionException();
                
                retval.DoubleValue = dblStack.Pop();
                retval.ReturnType = SimplificationReturnValue.ReturnTypes.Float;
            }
            else
            {
                var intStack = new Stack<int>();

                foreach (var nc in _outputQueue)
                {
                    if (nc.NumberType == NumberClass.NumberTypes.Integer)
                        intStack.Push(nc.IntNumber);
					
                    if (nc.NumberType == NumberClass.NumberTypes.Float)
                        intStack.Push((int)nc.FloatNumber);
					
                    if (nc.NumberType == NumberClass.NumberTypes.Operator)
                    {
                        int val = DoMath(nc.Operator, intStack.Pop(), intStack.Pop());
                        intStack.Push(val);
                    }
                }

                if (intStack.Count == 0)
                    throw new CouldNotParseExpressionException();
                
                retval.IntValue = intStack.Pop();
                retval.ReturnType = SimplificationReturnValue.ReturnTypes.Integer;
            }

            return retval;
        }

        private static double DoMath(string op, double val1, double val2)
        {
            if (op == "*")
                return val1 * val2;
            if (op == "/")
                return val2 / val1;
            if (op == "+")
                return val1 + val2;
            if (op == "-")
                return val2 - val1;
            if (op == "%")
                return val2 % val1;
            if (op == "^")
                return Math.Pow(val2, val1);

            return 0f;
        }

        private static int DoMath(string op, int val1, int val2)
        {
            if (op == "*")
                return val1 * val2;
            if (op == "/")
                return val2 / val1;
            if (op == "+")
                return val1 + val2;
            if (op == "-")
                return val2 - val1;
            if (op == "%")
                return val2 % val1;
            if (op == "^")
                return (int)Math.Pow(val2, val1);

            return 0;
        }

        private static bool IsLeftAssociative(string op)
        {
            return op == "*" || op == "+" || op == "-" || op == "/" || op == "%";
        }

        private static int GetPrecedence(TokenParser.Tokens token)
        {
            if (token == TokenParser.Tokens.Add || token == TokenParser.Tokens.Subtract)
                return 1;

            if (token == TokenParser.Tokens.Multiply || token == TokenParser.Tokens.Divide || token == TokenParser.Tokens.Modulus)
                return 2;

            if (token == TokenParser.Tokens.Exponent)
                return 3;

            if (token == TokenParser.Tokens.Lparen || token == TokenParser.Tokens.Rparen)
                return 4;

            return 0;
        }

        private static int GetPrecedence(string op)
        {
            if (op.Equals("+") || op.Equals("-"))
                return 1;

            if (op.Equals("*") || op.Equals("/") || op.Equals("%"))
                return 2;

            if (op.Equals("^"))
                return 3;

            if (op.Equals("(") || op.Equals(")"))
                return 4;

            return 0;
        }

        private static bool IsOperator(TokenParser.Tokens token)
        {
            return token == TokenParser.Tokens.Add || token == TokenParser.Tokens.Divide || token == TokenParser.Tokens.Exponent ||
                   token == TokenParser.Tokens.Modulus || token == TokenParser.Tokens.Multiply || token == TokenParser.Tokens.Subtract;
        }

        private class FunctionClass
        {
            public string Expression
            {
                get;
                set;
            }

            public string Name
            {
                get;
                set;
            }

            public FunctionArgumentList Arguments
            {
                get;
                set;
            }
        }

        private class NumberClass
        {
            public enum NumberTypes
            {
                Float,
                Integer,
                Operator,
                Expression
            }

            public NumberTypes NumberType
            {
                get;
                set;
            }

            public int IntNumber
            {
                get;
                set;
            }

            public double FloatNumber
            {
                get;
                set;
            }

            public string Operator
            {
                get;
                set;
            }

            public string Expression
            {
                get;
                set;
            }
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Chief Technology Officer
Germany Germany
Florian lives in Munich, Germany. He started his programming career with Perl. After programming C/C++ for some years he discovered his favorite programming language C#. He did work at Siemens as a programmer until he decided to study Physics.

During his studies he worked as an IT consultant for various companies. After graduating with a PhD in theoretical particle Physics he is working as a senior technical consultant in the field of home automation and IoT.

Florian has been giving lectures in C#, HTML5 with CSS3 and JavaScript, software design, and other topics. He is regularly giving talks at user groups, conferences, and companies. He is actively contributing to open-source projects. Florian is the maintainer of AngleSharp, a completely managed browser engine.

Comments and Discussions