Search code examples
c#parsingmathexpressionevaluator

Problem with decimal convertion in Math Expression Evaluator


I'm implementing a math expression evaluator in C# but I has not able to use decimal numbers, like, if I enter "3.4" the program consider it as "34". The problem is the same if use coma like "3,4".



        static void EvaluateInput(string input)
        {
            string[] parts = input.Split('=');
            if (parts.Length == 2)
            {
                string variableName = parts[0].Trim();
                if (IsValidVariableName(variableName))
                {
                    string expression = parts[1].Trim();

                    double result = SolveExpression(expression);
                    variables[variableName] = SolveExpression(expression);
                    lastResult = result;

                    Console.WriteLine($"{variableName} = {result}");
                }
                else
                {
                    Console.WriteLine("Invalid variable name. Use lowercase letters.");
                }
            }
            else
            {
                double result = SolveExpression(input);
                lastResult = result;
                Console.WriteLine($"  = {result}");
            }
        }

        static bool IsValidVariableName(string variableName)
        {
            return !string.IsNullOrEmpty(variableName) && variableName.Length == 1 && char.IsLower(variableName[0]);
        }

        static double SolveExpression(string expression)
        {
            expression = expression.Replace(',', '.');

            foreach (var constant in constants)
            {
                expression = expression.Replace(constant.Key, constant.Value.ToString(CultureInfo.InvariantCulture));
            }

            expression = Regex.Replace(expression, @"(?<=[^\d\.])-", "-");
            expression = Regex.Replace(expression, @"(?<=\d)-", " -");
            string[] tokens = TokenizeExpression(expression);

            Stack<string> operators = new Stack<string>();
            Stack<double> values = new Stack<double>();

            foreach (string token in tokens)
            {
                if (double.TryParse(token, out double number))
                {
                    values.Push(number);
                }
                else if (IsValidVariableName(token))
                {
                    if (variables.ContainsKey(token))
                    {
                        values.Push(variables[token]);
                    }
                    else
                    {
                        throw new InvalidOperationException($"Undefined variable: {token}");
                    }
                }

                else if (IsOperator(token))
                {
                    while (operators.Count > 0 && Priority(operators.Peek()) >= Priority(token))
                    {
                        if (operators.Peek() == "(")
                            break;

                        if (values.Count >= 2)
                        {
                            double val2 = values.Pop();
                            double val1 = values.Pop();
                            string op = operators.Pop();
                            values.Push(ApplyOperator(op, val1, val2));
                        }
                        else
                        {
                            break;
                        }
                    }
                    operators.Push(token);
                }
                else if (token == "(")
                {
                    operators.Push(token);
                }
                else if (token == ")")
                {
                    while (operators.Peek() != "(")
                    {
                        double val2 = values.Pop();
                        double val1 = values.Pop();
                        string op = operators.Pop();
                        values.Push(ApplyOperator(op, val1, val2));
                    }
                    operators.Pop();
                }
            }

            while (operators.Count > 0)
            {
                if (values.Count >= 2)
                {
                    double val2 = values.Pop();
                    double val1 = values.Pop();
                    string op = operators.Pop();
                    values.Push(ApplyOperator(op, val1, val2));
                }
                else
                {
                    break;
                }
            }

            return values.Count > 0 ? values.Pop() : 0;
        }

        static string[] TokenizeExpression(string expression)
        {
            List<string> tokens = new List<string>();
            string currentToken = "";

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

                if (char.IsDigit(c) || c == '.' || c == ',')
                {
                    currentToken += c;
                }
                else
                {
                    if (!string.IsNullOrEmpty(currentToken))
                    {
                        if (double.TryParse(currentToken.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out double parsedNumber))
                        {
                            tokens.Add(parsedNumber.ToString(CultureInfo.InvariantCulture));
                        }
                        else
                        {
                            tokens.Add(currentToken);
                        }
                        currentToken = "";
                    }

                    if (IsOperator(c.ToString()) || c == '(' || c == ')')
                    {
                        tokens.Add(c.ToString());
                    }
                    else if (char.IsLetter(c))
                    {
                        currentToken = c.ToString();
                    }
                }
            }

            if (!string.IsNullOrEmpty(currentToken))
            {
                if (double.TryParse(currentToken.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out double parsedNumber))
                {
                    tokens.Add(parsedNumber.ToString(CultureInfo.InvariantCulture));
                }
                else
                {
                    tokens.Add(currentToken);
                }
            }

            return tokens.ToArray();
        }

        static bool IsOperator(string token)
        {
            return token == "+" || token == "-" || token == "*" || token == "/" || token == "^";
        }

        static int Priority(string op)
        {
            if (op == "^")
                return 3;
            if (op == "*" || op == "/")
                return 2;
            if (op == "+" || op == "-")
                return 1;
            return 0;
        }

        static double ApplyOperator(string op, double a, double b)
        {
            switch (op)
            {
                case "+": return a + b;
                case "-": return a - b;
                case "*": return a * b;
                case "/": return a / b;
                case "^": return Math.Pow(a, b);
                default: throw new ArgumentException($"Invalid operator: {op}");
            }
        }

I have tried to force the code to use "en-US" globalization but I no success. I also tried difference parses in sense to convert the string to a float number, also not succeed


Solution

  • Instead of doing:

    if (double.TryParse(token, out double number))
    {
      values.Push(number);
    }
    

    try this:

    if (new Regex(@"\d+(\.\d+)?").IsMatch(token))
    {
      values.Push(double.Parse(token, CultureInfo.InvariantCulture));
    }
    

    Other TryParses might also need changing.