/* ________________________________________________________________________________________________ rpn100.txt ~ rev. 2011.08.12 XGB Web and Software Design ~ www.xgbdesign.com First draft of an object-oriented implementation of the program presented in "The C Programming Language" by Kernighan and Ritchie (2nd ed.), pp. 76-79; and "The C Answer Book" by Tondo and Gimpel, pp. 73-92. Aside from organizing the code more intelligibly, other major objectives of using C++ include eliminating all global variables, and exploiting the power of the string class included in the ANSI/ISO C++ standard, so that we can do away with all such functions as getch(), ungetch(), getline(), getop(), etc. For additional documentation of this source file, visit http://www.xgbdesign.com/c++code/rpn-calculator/version1-00.html. PLEASE NOTE: This source code file has been saved with the ".txt" extension to obviate browser difficulties, particularly with Internet Explorer. To compile and run this code, just copy and paste this text into a new document, but save the file with the extension ".cpp" PLEASE NOTE: This code is licensed under the terms of the GNU General Public License, Version 3 (http://www.gnu.org/copyleft/gpl.html#header). You are free to use and modify this source code, but if you do, kindly cite XGB Web and Software Design, whether in a link from your website, or or in your own source code. Thanks! ________________________________________________________________________________________________ */ #include #include using namespace std; // Class declarations: ____________________________________________________________________________ class Stack { public: Stack(); void Clear(); void Push(double); double Pop(); private: // Fields: int freeStackPosition; double elements[100]; }; class Calculator { public: Calculator(); void Run(); private: // Fields: double variables[26]; // Variables A through Z string entry; // Stores current entry string lastEntry; // For temporary storage of a possible variable name Stack stack; // Implements RPN calculation // Methods: void PushNumericValue(); void PushVariableValue(); void DeployOperator(); void DeployMathFunction(); void DeployStackFunction(); bool EntryIsNumeric(); bool EntryIsOperatorName(); bool EntryIsVariableName(); bool LastEntryIsVariableName(); bool EntryIsStackFunctionName(); }; // Stack: Constructor and public methods: _________________________________________________________ Stack::Stack() { freeStackPosition = 0; } void Stack::Clear() { freeStackPosition = 0; } void Stack::Push(double val) { if (freeStackPosition < 100) elements[freeStackPosition++] = val; else cout << " ERROR: Stack full; can't push " << val << ".\n"; } double Stack::Pop() { if (freeStackPosition > 0) return elements[--freeStackPosition]; else { cout << " ERROR: Stack empty.\n"; return 0.0; } } // Calculator: Constructor and public methods: ____________________________________________________ Calculator::Calculator() { for (int i = 0; i < 26; ++i) variables[i] = 0.0; entry = ""; lastEntry = ""; cout.precision(12); cout << "\nA Tiny RPN Calculator (;-}= v1.00\n\n"; } void Calculator::Run() { while (cin >> entry) { if (EntryIsNumeric()) PushNumericValue(); else if (EntryIsVariableName()) PushVariableValue(); else if (EntryIsOperatorName()) DeployOperator(); else if (EntryIsStackFunctionName()) DeployStackFunction(); else DeployMathFunction(); lastEntry = entry; } } // Calculator: Private methods: ___________________________________________________________________ void Calculator::PushNumericValue() { stack.Push(atof(entry.c_str())); } void Calculator::PushVariableValue() { stack.Push(variables[entry[0] - 'A']); } void Calculator::DeployOperator() { double arg1; switch (entry[0]) { case '+': stack.Push(stack.Pop() + stack.Pop()); break; case '*': stack.Push(stack.Pop() * stack.Pop()); break; case '-': arg1 = stack.Pop(); stack.Push(stack.Pop() - arg1); break; case '/': arg1 = stack.Pop(); if (arg1 != 0.0) stack.Push(stack.Pop() / arg1); else cout << " ERROR: Attempted division by zero.\n"; break; case '%': arg1 = stack.Pop(); if (arg1 != 0.0) stack.Push(fmod(stack.Pop(), arg1)); else cout << " ERROR: Attempted division by zero.\n"; break; case '=': stack.Pop(); // Pop previously assigned (or spuriously assigned) variable value first if (LastEntryIsVariableName()) variables[lastEntry[0] - 'A'] = stack.Pop(); else cout << " ERROR: No variable being assigned to.\n"; break; } } void Calculator::DeployStackFunction() { double arg1, arg2; if (entry == "?") { // If querying, or preparing program output,... arg1 = stack.Pop(); // ...output top element in stack stack.Push(arg1); cout << " " << arg1 << endl; } else if (entry == "dp") { arg1 = stack.Pop(); // Duplicate top element stack.Push(arg1); stack.Push(arg1); } else if (entry == "sw") { arg1 = stack.Pop(); // Swap top two elements arg2 = stack.Pop(); stack.Push(arg1); stack.Push(arg2); } else if (entry == "cl") stack.Clear(); // Clear the stack } void Calculator::DeployMathFunction() { double arg1; const double pi = 3.141592653589793; // Push some constants onto the stack (N.B.: The Golden Mean and physical // constants are included simply to demonstrate the calculator's potential. // In future drafts we'll have ways to eliminate hard-coding of such constants): if (entry == "pi") stack.Push(pi); else if (entry == "e") stack.Push(2.718281828459045); else if (entry == "gm") stack.Push(1.618033988749895); // Golden Mean else if (entry == "au") stack.Push(1.495e+11); // Astronomical unit (meters) else if (entry == "c") stack.Push(299792458); // Speed of light in a vacuum (meters/second) else if (entry == "yr") stack.Push(31556926); // Year (seconds) else if (entry == "ly") stack.Push(9.46053e+15); // Light-year (meters) // Trigonometric functions: else if (entry == "sin") stack.Push(sin(stack.Pop())); else if (entry == "cos") stack.Push(cos(stack.Pop())); else if (entry == "tan") stack.Push(tan(stack.Pop())); else if (entry == "asin") stack.Push(asin(stack.Pop())); else if (entry == "acos") stack.Push(acos(stack.Pop())); else if (entry == "atan") stack.Push(atan(stack.Pop())); else if (entry == "deg") // Converts radians to degrees stack.Push(stack.Pop() * 180.0 / pi); else if (entry == "rad") // Converts degrees to radians stack.Push(stack.Pop() * pi / 180.0); // Hyperbolic functions: else if (entry == "sinh") stack.Push(sinh(stack.Pop())); else if (entry == "cosh") stack.Push(cosh(stack.Pop())); else if (entry == "tanh") stack.Push(tanh(stack.Pop())); // Exponential and logarithmic functions: else if (entry == "exp") // Natural exponent ("e to the x") stack.Push(exp(stack.Pop())); else if (entry == "ln") // Natural log (base e) stack.Push(log(stack.Pop())); else if (entry == "log") // Common log (base 10) stack.Push(log10(stack.Pop())); else if (entry == "sqrt") // Square root stack.Push(sqrt(stack.Pop())); else if (entry == "sq") // Square stack.Push(pow(stack.Pop(), 2.0)); else if (entry == "cbrt") // Cube root stack.Push(pow(stack.Pop(), 1.0 / 3.0)); else if (entry == "cb") // Cube stack.Push(pow(stack.Pop(), 3.0)); // Functions for formatting numbers: else if (entry == "pr") { int prec = stack.Pop(); // Resets output precision on the fly if (prec >= 0 && prec <= 16) cout.precision(prec); else cout << " ERROR: " << prec << " not a valid setting for precision (0 <= precision <= 16 is valid).\n"; } else if (entry == "ceil") // Rounds up stack.Push(ceil(stack.Pop())); else if (entry == "floor") // Rounds down stack.Push(floor(stack.Pop())); else if (entry == "round") // Rounds to nearest whole number stack.Push(floor(stack.Pop() + 0.5)); // Other useful math functions: else if (entry == "r") { // Reciprocation arg1 = stack.Pop(); if (arg1 != 0.0) stack.Push(1.0 / arg1); else cout << " ERROR: Attempted division by zero.\n"; } else if (entry == "chs") // Change sign stack.Push(-(stack.Pop())); else if (entry == "abs") // Absolute value stack.Push(fabs(stack.Pop())); else if (entry == "yx") { // The power function ("y to the x") arg1 = stack.Pop(); stack.Push(pow(stack.Pop(), arg1)); } // Otherwise, it's not supported: else cout << " ERROR: \"" << entry << "\" is not a supported function.\n"; } bool Calculator::EntryIsNumeric() { return (isdigit(entry[0])) || (entry[0] == '.' && isdigit(entry[1])) || (entry[0] == '-' && isdigit(entry[1])) || (entry[0] == '-' && entry[1] == '.' && isdigit(entry[2])); } bool Calculator::EntryIsVariableName() { return entry.length() == 1 && entry >= "A" && entry <= "Z"; } bool Calculator::LastEntryIsVariableName() { return lastEntry.length() == 1 && lastEntry >= "A" && lastEntry <= "Z"; } bool Calculator::EntryIsOperatorName() { if (entry.length() == 1) { string operators = "+-*/%="; for (int i = 0; i < 6; ++i) if (entry[0] == operators[i]) return true; } return false; } bool Calculator::EntryIsStackFunctionName() { return entry == "?" || entry == "dp" || entry == "sw" || entry == "cl"; } // The main() function: ___________________________________________________________________________ int main() { Calculator tinyCalculator; tinyCalculator.Run(); return 0; }