/* ________________________________________________________________________________________________ rpn101.txt ~ rev. 2011.08.12 XGB Web and Software Design ~ www.xgbdesign.com Version 1.01 extends the capabilities of Version 1.00 by allowing the user to create and invoke exactly one programmable sequence of commands. To this end the class Program is introduced, and an instance of Program is made a member of the Calculator class. For additional documentation of this source file, visit http://www.xgbdesign.com/c++code/rpn-calculator/version1-01.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 #include // New in v1.01 using namespace std; // Class declarations: ____________________________________________________________________________ // New in v1.01: class Program { public: Program(); void CheckOperation(string&); bool IsRunning(); private: // Fields: bool isRunning; int count; vector instructions; // Methods: void Set(); string NextInstruction(); }; 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 Program program; // Allows user to create and run one program New in v1.01 // Methods: void PushNumericValue(); void PushVariableValue(); void DeployOperator(); void DeployMathFunction(); void DeployStackFunction(); bool EntryIsNumeric(); bool EntryIsOperatorName(); bool EntryIsVariableName(); bool LastEntryIsVariableName(); bool EntryIsStackFunctionName(); }; // Program: Constructor and public methods: _______________________________________________________ Program::Program() { isRunning = false; count = 0; } void Program::CheckOperation(string& entry) { if (entry == "pgm") Set(); if (entry == "run") { isRunning = true; count = 0; } if (isRunning) entry = NextInstruction(); } bool Program::IsRunning() { return isRunning; } // Program: Private methods: ______________________________________________________________________ void Program::Set() { // Flush the vector first: instructions.resize(0); string newEntry = ""; cout << "Enter a sequence of program instructions; terminate by entering \"end\": "; while (cin >> newEntry && newEntry != "end") instructions.push_back(newEntry); cout << " Program created.\n"; } string Program::NextInstruction() { string next = instructions[count++]; if (count == instructions.size()) isRunning = false; return next; } // 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.01\n\n"; } void Calculator::Run() { // I.e., while the program is running, or user enters instructions manually... while (program.IsRunning() || cin >> entry) { // New in v1.01 program.CheckOperation(entry); if (entry == "pgm") // New in v1.01; Traps the instruction "pgm" so that ; // it's not claimed to be a nonexistent math function else 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; }