private static IEnumerable<Expression> ParseCommaSeparatedList(IReadOnlyList<Token> tokens, Dictionary<string, Expression> variableMap) { var currentElement = new List<Token>(); var tokenGroups = new List<List<Token>>(); for (var i = 0; i < tokens.Count; i++) { var token = tokens[i]; if (token.Type == TokenType.Comma) { tokenGroups.Add(currentElement.Select(e => e).ToList()); // move a copy of the list, not a reference currentElement.Clear(); } else if (token.Type == TokenType.OpenBrace) { var indexOfMatchingBrace = IndexOfMatchingBrace(tokens, i); var tokenSubList = tokens.Skip(i + 1).Take(indexOfMatchingBrace - i - 1).ToList(); var innerList = ParseCommaSeparatedList(tokenSubList, variableMap).ToList(); var list = new ExpressionListToken(new ExpressionList(innerList)); currentElement.Add(list); i = indexOfMatchingBrace; } else if (token.Type == TokenType.CloseBrace) { throw new SyntaxErrorException("Mismatched braces"); } else { currentElement.Add(token); } } tokenGroups.Add(currentElement.Select(e => e).ToList()); // move a copy of the list currentElement.Clear(); return tokenGroups.Select(tokenGroup => ParsePostfix(CreatePostfixTokenList(tokenGroup, variableMap), variableMap)); }
private static IEnumerable<Token> CreatePostfixTokenList(List<Token> tokens, Dictionary<string, Expression> variableMap) { tokens = InsertImpliedMultiplication(tokens); // https://en.wikipedia.org/wiki/Shunting-yard_algorithm var outputQueue = new List<Token>(); var stack = new Stack<Token>(); for (var index = 0; index < tokens.Count; index++) { var currentToken = tokens[index]; // If the token is a number, then add it to the output queue. if (currentToken.Type.IsOperand()) { outputQueue.Add(currentToken); } // If the token is a function token, then push it onto the stack. else if (currentToken.Type == TokenType.Function) { if (FunctionRepository.Get(currentToken.Value).FixType == FixType.PreFix) { outputQueue.Add(currentToken); } else { stack.Push(currentToken); } } // If the token is a function argument separator (e.g., a comma): else if (currentToken.Type == TokenType.Comma) { // Until the token at the top of the stack is a left parenthesis, // pop operators off the stack onto the output queue. while (stack.Peek().Type != TokenType.OpenParenthesis) { outputQueue.Add(stack.Pop()); } // If no left parentheses are encountered, either the separator // was misplaced or parentheses were mismatched. } // If the token is an operator, o1, then: else if (currentToken.Type.IsOperator()) { // while there is an operator token o2, or a function token fun, // at the top of the operator stack: while (stack.Any() && (stack.Peek().Type.IsOperator() || stack.Peek().Type == TokenType.Function)) { var somethingChanged = false; // if it is a function token then pop fun off the operator // stack, onto the output queue; if (stack.Peek().Type == TokenType.Function) { outputQueue.Add(stack.Pop()); somethingChanged = true; } // if on the other hand it is an operator token, and either // o1 is left-associative and its precedence is less than or equal to that of o2, or // o1 is right associative, and has precedence less than that of o2, // then pop o2 off the operator stack, onto the output queue; else { var topType = stack.Peek().Type; if (topType.IsOperator()) { var o1Associativity = currentToken.Type.Associativity(); var o1Precedence = currentToken.Type.Precedence(); var o2Precedence = topType.Precedence(); if ((o1Associativity == OperatorAssociativity.Left && o1Precedence <= o2Precedence) || (o1Associativity == OperatorAssociativity.Right && o1Precedence < o2Precedence)) { outputQueue.Add(stack.Pop()); somethingChanged = true; } } } if (!somethingChanged) break; } // at the end of iteration push o1 onto the operator stack. stack.Push(currentToken); } // If the token is a left parenthesis (i.e. "("), then push it onto the stack. else if (currentToken.Type == TokenType.OpenParenthesis) { stack.Push(currentToken); } // If the token is a right parenthesis (i.e. ")"): else if (currentToken.Type == TokenType.CloseParenthesis) { // Until the token at the top of the stack is a left parenthesis, // pop operators off the stack onto the output queue. while (stack.Any() && stack.Peek().Type != TokenType.OpenParenthesis) { outputQueue.Add(stack.Pop()); } // Pop the left parenthesis from the stack, but not onto the output queue. stack.Pop(); // If the token at the top of the stack is a function token, pop it onto the output queue. if (stack.Any() && stack.Peek().Type == TokenType.Function) { outputQueue.Add(stack.Pop()); } // If the stack runs out without finding a left parenthesis, // then there are mismatched parentheses. } else if (currentToken.Type == TokenType.OpenBrace) { var indexOfCloseBrace = tokens.FindLastIndex(t => t.Type == TokenType.CloseBrace); var tokenSubstring = tokens.Skip(index + 1).Take(indexOfCloseBrace - index - 1).ToList(); if (tokenSubstring.Count > 0) { var operands = ParseCommaSeparatedList(tokenSubstring, variableMap).ToList(); var list = new ExpressionListToken(new ExpressionList(operands)); outputQueue.Add(list); } else { // Special case for empty lists outputQueue.Add(new ExpressionListToken(new ExpressionList(new List<Expression>()))); } index = indexOfCloseBrace; } else if (currentToken.Type == TokenType.CloseBrace) { throw new SyntaxErrorException("Mismatched braces"); } else { throw new SyntaxErrorException("Unexpected token", currentToken.Value); } } // When there are no more tokens to read: // While there are still operator tokens in the stack: while (stack.Any()) { // If the operator token on the top of the stack is a parenthesis, // then there are mismatched parentheses. if (stack.Peek().Type == TokenType.OpenParenthesis || stack.Peek().Type == TokenType.CloseParenthesis) { throw new SyntaxErrorException("Mismatched parentheses"); } // Pop the operator onto the output queue. outputQueue.Add(stack.Pop()); } return outputQueue; }