// Remove comments to use this program. Other similar programs - basic_calculator_tt_6.cpp, calculator02buggy.cpp, calculator00.cpp
//
// This is example code from Chapter 6.7 "Trying the second version" of
// "Software - Principles and Practice using C++" by Bjarne Stroustrup
//
/*
This file is known as calculator02buggy.cpp
I have inserted 5 errors that should cause this not to compile
I have inserted 3 logic errors that should cause the program to give wrong results
First try to find an remove the bugs without looking in the book.
If that gets tedious, compare the code to that in the book (or posted source code)
Happy hunting!
*/
#include "std_lib_facilities.h"
//------------------------------------------------------------------------------
int fact(int x)
{
int val = 1, i = 0;
while (x > 0){
val *= x;
--x;
}
return val;
}
//------------------------------------------------------------------------------
class Token {
public:
char kind; // what kind of token
double value; // for numbers: a value
Token(char ch) // make a Token from a char
:kind(ch), value(0) { }
Token(char ch, double val) // make a Token from a char and a double
:kind(ch), value(val) { }
};
//------------------------------------------------------------------------------
class Token_stream {
public:
Token_stream(); // make a Token_stream that reads from cin
Token get(); // get a Token (get() is defined elsewhere)
void putback(Token t); // put a Token back
private:
bool full; // is there a Token in the buffer?
Token buffer; // here is where we keep a Token put back using putback()
};
//------------------------------------------------------------------------------
// The constructor just sets full to indicate that the buffer is empty:
Token_stream::Token_stream()
:full(false), buffer(0) // no Token in buffer
{
}
//------------------------------------------------------------------------------
// The putback() member function puts its argument back into the Token_stream's buffer:
void Token_stream::putback(Token t)
{
if (full) error("putback() into a full buffer");
buffer = t; // copy t to buffer
full = true; // buffer is now full
}
//------------------------------------------------------------------------------
Token Token_stream::get()
{
if (full) { // do we already have a Token ready?
// remove token from buffer
full = false;
return buffer;
}
char ch;
cin >> ch; // note that >> skips whitespace (space, newline, tab, etc.)
switch (ch) {
case ';': // for "print"
case 'q': // for "quit"
case '{': case '}': case '(': case ')': case '+': case '-': case '*': case '/': case '%': case '!':
return Token(ch); // let each character represent itself
case '.':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{
cin.putback(ch); // put digit back into the input stream
double val;
cin >> val; // read a floating-point number
return Token('8', val); // let '8' represent "a number"
}
default:
error("Bad token");
}
}
//------------------------------------------------------------------------------
Token_stream ts; // provides get() and putback()
//------------------------------------------------------------------------------
double expression(); // declaration so that primary() can call expression()
//------------------------------------------------------------------------------
// deal with numbers and parentheses
double primary()
{
Token t = ts.get();
switch (t.kind) {
case '{':
{
double d = expression();
t = ts.get();
if (t.kind != '}') error("'}' expected");
return d;
}
case '(': // handle '(' expression ')'
{
double d = expression();
t = ts.get();
if (t.kind != ')') error("')' expected)");
return d;
}
case '8': // we use '8' to represent a number
{
double d = t.value;
t = ts.get();
if (t.kind == '!'){
if (d - (int)d == 0)
return fact((int)d); // return factorial of the number
else
error("integer operand not found for '!' opertion");
}
else{
ts.putback(t);
return d; // return the number's value
}
}
default:
error("primary expected");
}
}
//------------------------------------------------------------------------------
// deal with *, /, and %(ok)
double term()
{
double left = primary();
Token t = ts.get(); // get the next token from token stream
while (true) {
switch (t.kind) {
case '*':
left *= primary();
t = ts.get();
break;
case '/':
{
double d = primary();
if (d == 0) error("divide by zero");
left /= d;
t = ts.get();
break;
}
case '%':
{
double d1 = primary();
if ((left - (int)left > 0) || (d1 - (int)d1 > 0))
error("can't use % operation for doubles");
left = (int)left % (int)d1;
t = ts.get();
break;
}
default:
ts.putback(t); // put t back into the token stream
return left;
}
}
}
//------------------------------------------------------------------------------
// deal with + and -
double expression()
{
double left = term(); // read and evaluate a Term
Token t = ts.get(); // get the next token from token stream
while (true) {
switch (t.kind) {
case '+':
left += term(); // evaluate Term and add
t = ts.get();
break;
case '-':
left -= term(); // evaluate Term and subtract
t = ts.get();
break;
default:
ts.putback(t); // put t back into the token stream
return left; // finally: no more + or -: return the answer
}
}
}
//------------------------------------------------------------------------------
int main()
{
double val = 0;
try
{
// Welcome message
cout << "Welcome to our simple calculator."
<< "\nPlease enter expressions using floating-point numbers.\n\n";
// Instructions to use calculator
cout << " How to use this calculator -\n"
<< "\t1. Operations supported: +, -, *, /, % (for integers only).\n"
<< "\t2. Use expressions like 1+2/3*4-5, followed by ';' to terminate expression.\n"
<< "\t3. Evaluation of multiple expressions is supported.\n"
<< "\t4. You can use square brackets or parenthesis or both in expressions as usual, for example, (1-2)*{(3-4)/2}.\n"
<< "\t5. Use 'q' to close the application.\n\n";
// Simple calculator
while (cin) {
cout << "> ";
Token t = ts.get();
if (t.kind == 'q') break; // 'x' for quit
if (t.kind == ';'){ // ';' for "print now"
cout << "=" << val << '\n';
//continue;
}
else
ts.putback(t);
val = expression();
}
keep_window_open();
return 0;
}
catch (exception& e) {
cerr << "error: " << e.what() << '\n';
keep_window_open();
return 1;
}
catch (...) {
cerr << "Oops: unknown exception!\n";
keep_window_open();
return 2;
}
}
//------------------------------------------------------------------------------