List<Token> shuntingYard(List<Token> stream)

Transforms the lexer's token stream into RPN using the Shunting-yard algorithm. Returns a list of Token in RPN form. Throws an ArgumentError if the list is empty.

Source

List<Token> shuntingYard(List<Token> stream) {
  if(stream.isEmpty) {
    throw new ArgumentError("The given tokenStream was empty.");
  }

  List<Token> outputStream = new List<Token>();
  List<Token> operatorBuffer = new List<Token>();

  Token prevToken;

  for(Token curToken in stream) {
    // If the current Token is a value or a variable, put them into the output stream.
    if(curToken.type == TokenType.VAL || curToken.type == TokenType.VAR) {
      outputStream.add(curToken);
      prevToken = curToken;
      continue;
    }

    // If the current Token is a function, put it onto the operator stack.
    if(curToken.type.function) {
      operatorBuffer.add(curToken);
      prevToken = curToken;
      continue;
    }

    /*
     *  If the current Token is a function argument separator, pop operators
     *  to output stream until a left brace is encountered.
     */
    if(curToken.type == TokenType.SEPAR) {
      while(!operatorBuffer.isEmpty && operatorBuffer.last.type != TokenType.LBRACE) {
        outputStream.add(operatorBuffer.removeLast());
      }
      // If no left brace is encountered, separator was misplaced or parenthesis mismatch
      if(!operatorBuffer.isEmpty && operatorBuffer.last.type != TokenType.LBRACE) {
        //TODO never reached, check this.
        throw new StateError('Misplaced separator or mismatched parenthesis.');
      }
      prevToken = curToken;
      continue;
    }

    /* if the current Tokens type is MINUS and the previous Token is an operator or type LBRACE
     * or we're at the beginning of the expression (prevToken == null) the current Token is
     * an unary minus, so the tokentype has to be changed.
     */
    if(curToken.type == TokenType.MINUS && (prevToken == null || prevToken.type.operator || prevToken.type == TokenType.LBRACE)) {
      Token newToken = new Token(curToken.text, TokenType.UNMINUS);
      operatorBuffer.add(newToken);
      prevToken = newToken;
      continue;
    }

    /*
     * If the current token is an operator and it's priority is lower than the priority of the last
     * operator in the operator buffer, than put the operators from the operator buffer into the output
     * stream until you find an operator with a priority lower or equal as the current tokens.
     * Then add the current Token to the operator buffer.
     */
    if(curToken.type.operator) {
      while(!operatorBuffer.isEmpty && ((curToken.type.leftAssociative && curToken.type.priority <= operatorBuffer.last.type.priority)
          || (!curToken.type.leftAssociative && curToken.type.priority < operatorBuffer.last.type.priority))) {
        outputStream.add(operatorBuffer.removeLast());
      }
      operatorBuffer.add(curToken);
      prevToken = curToken;
      continue;
    }

    // If the current Token is a left brace, put it on the operator buffer.
    if(curToken.type == TokenType.LBRACE) {
      operatorBuffer.add(curToken);
      prevToken = curToken;
      continue;
    }

    // If the current Token is a right brace, empty the operator buffer until you find a left brace.
    if(curToken.type == TokenType.RBRACE) {
      while(!operatorBuffer.isEmpty && operatorBuffer.last.type != TokenType.LBRACE) {
        outputStream.add(operatorBuffer.removeLast());
      }

      // Expect next token on stack to be left parenthesis and pop it
      if(operatorBuffer.isEmpty || operatorBuffer.removeLast().type != TokenType.LBRACE) {
        throw new StateError('Mismatched parenthesis.');
      }

      // If the token at the top of the stack is a function token, pop it onto the output queue.
      if (!operatorBuffer.isEmpty && operatorBuffer.last.type.function) {
        outputStream.add(operatorBuffer.removeLast());
      }
    }
    prevToken = curToken;
  }

  /*
   * When the algorithm reaches the end of the input stream, we add the
   * tokens in the operatorBuffer to the outputStream. If the operator
   * on top of the stack is a parenthesis, there are mismatched parenthesis.
   */
  while(!operatorBuffer.isEmpty) {
    if (operatorBuffer.last.type == TokenType.LBRACE ||
        operatorBuffer.last.type == TokenType.RBRACE) {
      throw new StateError('Mismatched parenthesis.');
    }
    outputStream.add(operatorBuffer.removeLast());
  }

  return outputStream;
}