/**
 * The expression class
 * serves as a container for
 * the tokens of an infix or
 * postfix expression. Once built
 * its contents are consumed as
 * they are extracted.
 *
 * @author  Gary D. Brown
 * @version 1.1, 10/26/2005
 * @since   03/15/2005
 */

#include <queue>
#include <stdexcept>
#include <cstring>
#include <iostream>
#include <sstream>
#include <string>

class expression {
  public:
    expression() { }
    expression(std::istream&);
    expression(std::string);
    
    bool nextIsNumber();
    bool nextIsOperator();
    bool nextIsRight();
    bool nextIsLeft();
    bool hasNext();
    
    double getNumber() throw (std::range_error);
    char getOperator() throw (std::range_error);
    char getParenthesis() throw (std::range_error);
    std::string nextToString();
    
    void putNumber(double);
    void putOperator(char) throw (std::invalid_argument);
    void putParenthesis(char) throw (std::invalid_argument);
    
    void putExpression(std::ostream&);
    void putExpression(std::string&);
  private:
    bool isOperator(char);
    bool isParenthesis(char);
    struct entry {
      char type;
      union {
        double n;
        char c; } element;
    };
    std::queue<entry> q;
};

expression::expression(std::istream& in) {
  double n;
  char c;
  while (in && (c = in.peek()) != '\n') {
    if (isdigit(c) || c == '.') {
      in >> n;
      putNumber(n); }
    else if (isOperator(c)) {
      in >> c;
      putOperator(c); }
    else if (isParenthesis(c)) {
      in >> c;
      putParenthesis(c); }
    else
      in.ignore(); }
  if (in.peek() == '\n')
    in.ignore();
}
    
expression::expression(std::string s) {
  double n;
  char c;
  std::istringstream in(s);
  while (in && (c = in.peek()) != '\n') {
    if (isdigit(c) || c == '.') {
      in >> n;
      putNumber(n); }
    else if (isOperator(c)) {
      in >> c;
      putOperator(c); }
    else if (isParenthesis(c)) {
      in >> c;
      putParenthesis(c); }
    else
      in.ignore(); }
}
    
bool expression::nextIsNumber() {
  return (q.front()).type == 'n';
}

bool expression::nextIsOperator() {
  return (q.front()).type == 'o';
}

bool expression::nextIsRight() {
  return (q.front()).type == 'r';
}

bool expression::nextIsLeft() {
  return (q.front()).type == 'l';
}

bool expression::hasNext() {
  return !q.empty();
}

double expression::getNumber() throw (std::range_error) {
  entry ent = q.front();
  if (ent.type == 'n') {
    q.pop();
    return ent.element.n; }
  else
    throw std::range_error("Entry is not a number.");
}

char expression::getOperator() throw (std::range_error) {
  entry ent = q.front();
  if (ent.type == 'o') {
    q.pop();
    return ent.element.c; }
  else
    throw std::range_error("Entry is not an operator.");
}

char expression::getParenthesis() throw (std::range_error) {
  entry ent = q.front();
  if (ent.type == 'l' || ent.type == 'r') {
    q.pop();
    return ent.element.c; }
  else
    throw std::range_error("Entry is not a parenthesis.");
}

std::string expression::nextToString() {
  std::string s;
  std::ostringstream out(s);
  if (hasNext()) {
    entry ent = q.front();
    out << "Next is ";
    switch (ent.type) {
      case 'n':
        out << "a number: " << ent.element.n;
        break;
      case 'o':
        out << "an operator: " << ent.element.c;
        break;
      case 'l':
      case 'r':
        out << "a parenthesis: " << ent.element.c; }}
  else
    out << "Expression is empty.";
  return out.str();
}

void expression::putNumber(double n) {
  entry ent;
  ent.type = 'n';
  ent.element.n = n;
  q.push(ent);
}

void expression::putOperator(char o) throw (std::invalid_argument) {
  if (isOperator(o)) {
    entry ent;
    ent.type = 'o';
    ent.element.c = o;
    q.push(ent); }
  else 
    throw std::invalid_argument("Character is not an operator.");
}

void expression::putParenthesis(char p) throw (std::invalid_argument) {
  if (isParenthesis(p)) {
    entry ent;
    ent.type = (p == '(') ? 'l' : 'r';
    ent.element.c = p;
    q.push(ent); }
  else
    throw std::invalid_argument("Character is not a parenthesis.");
}

void expression::putExpression(std::ostream& out) {
  while (hasNext())
    if (nextIsNumber())
      out << getNumber();
    else if (nextIsRight() || nextIsLeft())
      out << getParenthesis();
    else
      out << " " << getOperator() << " ";
  out << std::endl;
}    

void expression::putExpression(std::string& s) {
  std::ostringstream out(s);
  while (hasNext()) {
    if (nextIsNumber())
      out << getNumber();
    else if (nextIsRight() || nextIsLeft())
      out << getParenthesis();
    else
      out << " " << getOperator() << " "; }
  out << std::endl;
  s = out.str();
}    

bool expression::isOperator(char o) {
  return std::strchr("+-*/", o) != NULL;
}
  
bool expression::isParenthesis(char p) {
  return std::strchr("()", p) != NULL;
}