Token GetNextToken(StringReader reader) { int peekResult = reader.Peek(); if (peekResult == -1) { throw new InvalidOperationException("Stream is empty"); } char firstSymbol = (char)peekResult; if (ParseRules.IsValidNumberFirstChar(firstSymbol)) { return(GetNumber(reader)); } if (ParseRules.IsValidIdentifierFirstChar(firstSymbol)) { return(GetIdentifier(reader)); } if (ParseRules.IsValidSymbolChar(firstSymbol)) { return(GetSymbol(reader)); } throw new ArgumentException(String.Format("Unexpected token: \"{0}\"", firstSymbol)); }
Token GetIdentifier(StringReader reader) { StringBuilder identifierBuilder = new StringBuilder(); char symbol; while (true) { int peekResult = reader.Peek(); if (peekResult == -1) { break; } symbol = (char)peekResult; if (ParseRules.IsValidIdentifierChar(symbol)) { identifierBuilder.Append(symbol); reader.Read(); continue; } if (ParseRules.IsStopForIdentifierOrLiteralChar(symbol)) { break; } throw new ArgumentException(String.Format("Unexpected character in identifier token: \"{0}\"", symbol)); } return(new IdentifierToken(identifierBuilder.ToString())); }
// expression is parsed using modified Dijkstra's shunting-yard algorithm. TreeNode ParseExpression(ConstructionContext context, params char[] stopSymbols) { Stack <TreeNode> termStack = new Stack <TreeNode>(); Stack <BinaryOperatorOperation> operatorStack = new Stack <BinaryOperatorOperation>(); termStack.Push(ParseTermWithPossibleFactorial(context)); // expression should have at least one term // then there should be exactly one binary operator and one term every iteration while (context.TryPeekNextToken(out Token token)) { if (token is SymbolToken symbolToken) { if (ParseRules.IsBinaryOperatorChar(symbolToken.Symbol)) { context.EatLastToken(); // eat binary operator BinaryOperatorOperation curOperation = BinaryOperatorOperation.OperatorDictionary[symbolToken.Symbol.ToString()]; while (operatorStack.Count > 0) { BinaryOperatorOperation topOperation = operatorStack.Peek(); if ((topOperation.Precedence > curOperation.Precedence) || (topOperation.Precedence == curOperation.Precedence) && topOperation.IsLeftAssociative) { PopOperatorAndPushResult(termStack, operatorStack); } else { break; } } operatorStack.Push(curOperation); termStack.Push(ParseTermWithPossibleFactorial(context)); continue; } else if (stopSymbols.Contains(symbolToken.Symbol)) { while (operatorStack.Count > 0) { PopOperatorAndPushResult(termStack, operatorStack); } return(termStack.Pop()); } } throw new Exception(String.Format("Unexpected token: '{0}', expected binary operator or stop-symbol", token)); } if (stopSymbols.Length > 0) { throw new Exception(String.Format("Unfinished expression: expected stop symbol '{0}'", stopSymbols[0])); } while (operatorStack.Count > 0) { PopOperatorAndPushResult(termStack, operatorStack); } return(termStack.Pop()); }
Token GetNumber(StringReader reader) { StringBuilder literalBuilder = new StringBuilder(); char symbol, lastSymbol = (char)0; bool pointWasPut = false, exponentSignWasPut = false, exponentWasPut = false; while (true) { int peekResult = reader.Peek(); if (peekResult == -1) { break; } symbol = (char)peekResult; if (ParseRules.IsValidNumberChar(symbol, lastSymbol, pointWasPut, exponentSignWasPut, exponentWasPut)) { if (ParseRules.IsDecimalPointChar(symbol)) { pointWasPut = true; } if (ParseRules.IsSignChar(symbol)) { exponentSignWasPut = true; } if (ParseRules.IsExponentChar(symbol)) { exponentWasPut = true; } literalBuilder.Append(symbol); lastSymbol = symbol; reader.Read(); continue; } if (ParseRules.IsStopForIdentifierOrLiteralChar(symbol)) { break; } throw new ArgumentException(String.Format("Unexpected character in number token: \"{0}\"", symbol)); } return(new NumberToken(Double.Parse(literalBuilder.ToString(), System.Globalization.NumberStyles.AllowExponent | System.Globalization.NumberStyles.AllowDecimalPoint, System.Globalization.CultureInfo.InvariantCulture))); }