Expression Calculator
#include <string> using std::string; #include <iostream> #include <memory> using std::make_shared; #include "expression_parts.h" #include "expr_stack.h" /* * Class to read the input as a stream of Expression_Part. */ class Reader { public: // Construct one. If an ostream is given, input is prompted // for on it. Reader(std::istream &i, std::ostream *o = NULL): in(i), out(o), atstart(true) { } // Read in an item. This performs input, and creates and returns an // appropriate object. It returns null to indicate the end of the // the line. If a second pointer (to an ostream) is sent, it is used // to issue prompts at the start of each line. std::shared_ptr<Expression_Part> read() { // If we're at the start of the line, prompt. if(atstart) { if(out) *out << "ecalc> "; atstart = false; } // Clear out leading space, but don't read the non-space which // terminates it, and don't read past the end of the line. The // peek() method tells you the next character without consuming // it. So, the loop reads things so long as they're white // space. while(in.peek() == ' ' || in.peek() == '\t') in.get(); if(in.eof()) return NULL; // See if we have an operand. char tocome = in.peek(); if(isdigit(tocome)) return make_shared<Operand>(readint()); // Okay. Read the character and act on it. These are all // operators, except \n, which sets the start of line flag and // returns NULL. tocome = in.get(); while(tocome) { switch(tocome) { case '+': return make_shared<Oper_Plus>(); case '-': // This is tricky. Could be a negative number, // but we only count it that way if the very // next char is a digit. if(isdigit(in.peek())) return make_shared<Operand>(-readint()); else return make_shared<Oper_Minus>(); case '*': return make_shared<Oper_Times>(); case '/': return make_shared<Oper_Div>(); case '%': return make_shared<Oper_Mod>(); case ')': return make_shared<Right>(); case '(': return make_shared<Left>(); case '\n': atstart = true; return NULL; default: // Bad input. throw bad_expression("Bad input character '" + string(1,tocome) + "'."); } } return NULL; } // Clear the line. void clear() { if(atstart) return; char ch; while(ch = in.get()) if(ch == '\n') break; atstart = true; }; // Check for eof. bool eof() { return in.eof(); } private: std::istream &in; // Reading stream. std::ostream *out; // Prompting stream (if any). bool atstart; // At the start of a line. // Read and return an integer from the stream. int readint() { int n; in >> n; return n; } }; main() { expr_stack s; // Evaluation stack. Reader in(std::cin, &std::cout);// Prompting reader. while(true) { std::shared_ptr<Expression_Part> e; try { // Read, finish of finished. e = in.read(); if(in.eof()) break; if(e != NULL) // Perform the action on what we just read. e->action(s); else { // Line done. Complete the expression. // Apply any remaining operations. s.reduce(); // Print the result. Stack should contain // exactly one item, an operand. if(!s.top()->is_operand()) throw bad_expression("Incomplete expression."); if(s.size() != 1) throw bad_expression("Excess symbols."); std::cout << *s.pop() << std::endl; } } catch(const bad_expression& e) { // Print the message, clean up, and let the loop // start again. std::cout << e.what() << std::endl; s.clear(); in.clear(); } } }