/* ________________________________________________________________________________________________________
rpn100.cpp A Tiny RPN Calculator (;-}= v1.00
Author: Gordon M. Brown Revised 2009.07.30
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 is an HTML webpage. To access this source code, simply select and copy the
text on this page, and paste it into your favorite text editor or IDE. Please DO NOT use the
underlying HTML source code, as it contains certain HTML character codes that will not compile
in C++.
Valid XHTML 1.0 Strict Valid CSS 2.1
________________________________________________________________________________________________________
*/
#include <iostream>
#include <cmath>
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;
}