string ParseParenthesisToken(ParenthesisToken token)
        {
            switch (token.Value)
            {
            case Parenthesis.Left:
                return("(");

            case Parenthesis.Right:
                return(")");

            default:
                throw new InvalidOperationException($"Unknown {nameof(Parenthesis)} type: {token.Value}.");
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Обрабатывает входную строку и возвращает ее RPN.
        /// </summary>
        /// <param name="expression">Строка представляющая математическое выражение.</param>
        /// <returns>Список полученных токенов в порядке RPN.</returns>
        public List <IToken> Parse(string expression)
        {
            result         = new List <IToken>();
            operationStack = new Stack <IToken>();

            var constant         = new ConstantToken();
            var openParenthesis  = new ParenthesisToken(true);
            var closeParenthesis = new ParenthesisToken(false);

            int    i        = 0;
            IToken previous = new ConstantToken();

            while (i < expression.Length)
            {
                IToken current;
                if (constant.TryParse(expression, i))        //Если следующий токен константа
                {
                    if (previous is FunctionToken)
                    {
                        throw new FormatException("Отсутсвуют скобки у параметра функции");
                    }

                    result.Add(new ConstantToken(constant.Value, constant.Length));
                    current = new ConstantToken(constant.Value, constant.Length);
                    i      += constant.Length;
                }
                else if (TryParseFunction(expression, i, out var functionToken)) //Если следующий токен функция
                {
                    if (previous is FunctionToken)
                    {
                        throw new FormatException("Отсутсвуют скобки у параметра функции");
                    }

                    if (functionToken.FunctionType == FunctionType.Postfix)
                    {
                        result.Add(functionToken);
                    }


                    if (functionToken.FunctionType == FunctionType.Prefix)
                    {
                        operationStack.Push(functionToken);
                    }

                    current = functionToken;
                    i      += functionToken.Length;
                }
                else if (openParenthesis.TryParse(expression, i)) //Если следующий токен открывающая скобка
                {
                    operationStack.Push(new ParenthesisToken(true));
                    current = openParenthesis;
                    i      += openParenthesis.Length;
                }
                else if (closeParenthesis.TryParse(expression, i)) //Если следующий токен закрывающая скобка
                {
                    while (operationStack.Count != 0 && !(operationStack.Peek() is ParenthesisToken))
                    {
                        result.Add(operationStack.Pop());
                    }

                    if (operationStack.Count == 0)
                    {
                        throw new FormatException("Количество закрывающих скобок больше, чем открывающих");
                    }

                    operationStack.Pop();
                    current = closeParenthesis;
                    i      += closeParenthesis.Length;
                }
                else if (TryParseBinaryOperation(expression, i, out var binaryOperation)) //Если следующий токен бинарная операция
                {
                    while ((operationStack.Count != 0 && ((operationStack.Peek() is FunctionToken)) && ((FunctionToken)operationStack.Peek()).FunctionType == FunctionType.Prefix) ||
                           (operationStack.Count != 0 && ((operationStack.Peek() is BinaryOperationToken)) && ((((BinaryOperationToken)operationStack.Peek()).Priority > binaryOperation.Priority) ||
                                                                                                               ((((BinaryOperationToken)operationStack.Peek()).Associativity == Associativity.Left) && ((BinaryOperationToken)operationStack.Peek()).Priority == binaryOperation.Priority))))
                    {
                        result.Add(operationStack.Pop());
                    }

                    //Есть операции которые являются одновременно унарными и бинарными, среди них алгеброических только 2: унарный минус и комлексное сопряженное.
                    //Комплексные числа в рамках этого парсера не рассматриваются, введение унарного минуса как отдельного вида токенов усложнит код, кроме того унарные операции
                    //уже покрыты токенами функций, но добавлять его в качестве функции не логично, потому что у него отсутсвуют характерные скобки вокруг парметра, поэтому я решил
                    //заменить его костылём. Унарный минус интерпретируется как бинарный, но он будет вычитать число из 0. Например 2+(-3) => 2+(0-3).
                    if (binaryOperation.ToString() == "-")
                    {
                        if ((previous is ParenthesisToken && ((ParenthesisToken)previous).isOpen) || i == 0)
                        {
                            result.Add(new ConstantToken(0, 1));
                        }
                    }

                    operationStack.Push(binaryOperation);
                    current = binaryOperation;
                    i      += binaryOperation.Length;
                }
                else
                {
                    throw new FormatException("Отсутствует подходящий токен");
                }
                previous = current;
            }

            while (operationStack.Count != 0)
            {
                result.Add(operationStack.Pop());
            }

            CheckResult(result);

            return(result);
        }