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}."); } }
/// <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); }