/* ________________________________________________________________________________________________________
rpn101.cpp A Tiny RPN Calculator (;-}= v1.01
Author: Gordon M. Brown Revised 2009.07.31
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 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>
#include <vector> // 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<string> 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;
}