/**********************************************************************
* A simple console calculator program demonstrating use of functions.
* Commands are:
* <number> Enter <number> into the display. (Number mustn't start
* with a + or -, but if it does you can use:)
* =<number> Enter <number> into the display.
* <op><number> Compute the operation: + - * / ^
* q[uit] Exit
*
* There is a "display," and after each command is executed, the display
* is printed out. Operation commands operate on the display as left
* operand and the input number as the right operand, and return the
* result to the display. All operations are floating point. EOF on
* input is treated as a quit.
**********************************************************************/
#include <iostream>
#include <string>
#include <cctype>
#include <cmath>
using namespace std;
/*
* Print the value and prompt for a command.
*/
void prompt(double disp)
{
// Display the current value (which we know is zero).
cout << " " << disp << endl << " >> ";
}
// This function tries to clear out after an error. It clears the
// standard input stream, then reads and discards the (rest of) the
// input line.
void clear_input_error()
{
cin.clear();
string discard;
getline(cin, discard);
}
/*
* Read a command and a value. Return the command as normal, and the input
* value through the parameter. Returns = if just the number was entered, or q
* at end-of-file.
*/
string read_cmd(double &value)
{
// Read a command. If the read fails, we treat it as q (return
// false). Note that the read skips leading white space, so the
// comd is the first non-blank character.
string command;
cin >> command;
if(cin.eof() || command[0] == 'q') return "q";
// Now, the command could actually be a number entered into
// the display. Or it could just be a one-letter command,
// and the number comes later. Or it could be a one-letter
// command followed by a number. Let's find out. We'll
// make command exactly the command, arg the number.
if(command[0] == '.' || isdigit(command[0])) {
// Seems to be a number. Convert it, and set the
// command to = which assigns the argument to the
// display.
value = stod(command);
return "=";
} else if(command.length() > 1 &&
(command[1] == '.' || isdigit(command[1]))) {
// Now, the character letter didn't look numberish,
// but the second does. We think it's a one-letter
// command followed by a number. We extract the
// number part into value, and return the command part.
value = stod(command.substr(1));
return command.substr(0,1);
} else {
// Well, maybe we just put the command, and the number comes
// later. Keep the command, and read the number. If the
// conversion fails, complain and try again.
while(!(cin >> value)) {
// Clear the input and try again.
clear_input_error();
cout << "You need to enter a number. Try again."
<< endl << "> ";
}
}
return command;
}
/*
* Apply the given operation to the two numbers, and return the result,
* The op is one of the command characters, =+-* /^. Notice the
* dirty trick of returning from the cases, so I don't need break.
* Don't count on this happening often, but it does occasionally.
*/
double apply_op(char op, double first, double second)
{
switch(op)
{
case '=': // Enter.
return second;
case '+': // Add.
return first + second;
case '-': // Subtract.
return first - second;
case '*': // Multiply.
return first * second;
case '/': // Divide.
return first / second;
case '^': // Raise to power. Pow() is std library function.
return pow(first,second);
}
}
int main()
{
double disp = 0.0; // Display value.
// So, do you know why this is not inside the loop?
// Loop until exited by a quit command.
while(true)
{
string comd; // Input command.
double arg; // Input money argument.
// Prompt and read. Exit on quit.
prompt(disp);
comd = read_cmd(arg);
if(comd[0] == 'q') break;
// Obey the command. If the command is not legal (not
// found in the list of legal ones), whine. Otherwise,
// apply the operation to the display and argument.
if(string("=+-*/^").find(comd[0]) == string::npos) {
cout << "Bad command, " << comd << endl;
clear_input_error();
} else {
disp = apply_op(comd[0], disp, arg);
}
}
}