Showing posts with label chapter 6 drill. Show all posts
Showing posts with label chapter 6 drill. Show all posts

Thursday, 15 September 2016

Chapter 6 // Drill 2, 3, 4, 5 - Principles & Practice Using C++

In all these exercises I am using Visual Studio Community 2015 and the header file "std_lib_facilities.h" which can be found here:


http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h


My version is spelt differently so adjust the code accordingly if copying and pasting.


Chapter 6 // Drill 2

Change the character used as the exit command from q to x.

#include "stdafx.h"
#include "std_lib_facilities_new_version.h"
using namespace std;

double val; // messy way for main to access val in get()

//------------------------------------------------------------------------------

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
Token_stream::full = false;
return buffer;
}


char ch;
cin >> ch;    // note that >> skips whitespace (space, newline, tab, etc.)

switch (ch) 
{
case ';':    // for "print"
case 'x':    // for "quit"

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
cin >> val;              // read a floating-point number
return Token('#', val);   // let '#' 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 '(':    // handle '(' expression ')'
{
double d = expression();
t = ts.get();
if (t.kind != ')') error("')' expected)");
return d;
break;
}

case '#':            // we use '8' to represent a number
{
return t.value;  // return the number's value
break;
}

default:
error("primary expected");
}
}

//------------------------------------------------------------------------------

// deal with *, /
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;
}

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()
try
{
val = 0;
while (cin)
{
Token t = ts.get();

while (t.kind == '=')
t = ts.get();

if (t.kind == 'x')
{
return 0;
}

ts.putback(t);
cout << expression() << '\n';
}
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;
}


//------------------------------------------------------------------------------


It took a few things to get this working properly. It wasn't just as simple as changing line 87:
case 'q':    // for "quit"
to an x instead. 

First I changed the case to handle 'x' but I noticed that when you pressed x and enter, an error would pop up and force you to exit instead of just quitting the program. Clearly something wasn't right. Pressing 'x' followed by ';' worked and quit the while loop in main but we're supposed to press ';' to print something.

I noticed that in main, to use 'x' to quit it calls ts.get() and assigns 'x' to t.kind. Now main evaluates if what you have inputted is a primary, primary() only handles '(' and '#' so primary() doesn't know what to do with it. I just added another case that handles 'x', returns 'x' so that main just quits the loop when it's returned instead of defaulting to the error message found in primary(). 


That said, doing it that way means you do have to enter x twice and I spent almost 2 hours trying to figure out why but I still don't know. Through debugging I noticed that after an initial calculation has been performed you HAVE to provide at least 2 char's for main to work through. get() always goes through 2 char's (apart from the first time you open the program and 0 is already in the input buffer). I've tried putting numbers into the input buffer if an 'x' is entered but it still makes you put an x in again to quit. 

Looking ahead to chapter 7 Bjarne sort of addresses it and using the code he's written I've managed to get this working now (it's all to do with main) but I don't fully understand why. I guess it's just one of those things I won't get until later on.

Chapter 6 // Drill 3

Change the character used as the print command from ';' to '='.

This one is pretty simple, just changing line 68 to case '=':, then line 203 to compare to =. I also removed the "=" in the cout underneath so it made a bit more sense when printing out.


Chapter 6 // Drill 4

Add a greeting line in main():
     "Welcome to our simple calculator.
     Please enter expressions using floating-point numbers."

int main()
try
{
cout << "Welcome to our simple calculator!\n";
cout << "Please enter expressions using floating-point numbers:\n";

val = 0;
while (cin)
{
Token t = ts.get();

while (t.kind == '=')
t = ts.get();

if (t.kind == 'x')
{
return 0;
}

ts.putback(t);
cout << expression() << '\n';
}
return 0;

}


Chapter 6 // Drill 5

Improve that greeting by mentioning which operators are available and how to print and exit.

//------------------------------------------------------------------------------

void printInstructions()
{
cout << "Welcome to our simple calculator!\n";
cout << "You can use + - / *\n";
cout << "Press = to print the answer and x to exit.\n";
cout << "Please enter expressions using floating-point numbers:\n" << endl;
}

//------------------------------------------------------------------------------

int main()
try
{
printInstructions();

val = 0;
while (cin)
{
Token t = ts.get();

while (t.kind == '=')
t = ts.get();

if (t.kind == 'x')
{
return 0;
}

ts.putback(t);
cout << expression() << '\n';
}
return 0;

}


Thursday, 8 September 2016

Chapter 6 // Drill 1 - Principles & Practice Using C++

In all these exercises I am using Visual Studio Community 2015 and the header file "std_lib_facilities.h" which can be found here:


http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h


My version is spelt differently so adjust the code accordingly if copying and pasting.


Chapter 6 // Drill 1

Take the calculator from the file calculator02buggy.cpp. Get it to compile. You need to find and fix a few bugs. Those bugs are not inserted in the text in the book. Find the three logic errors deviously inserted in calculator02buggy.cpp and remove them so that the calculator produces correct results.


#include "stdafx.h"
#include "std_lib_facilities_new_version.h"
using namespace std;

//
// 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 and 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!

*/

double val; // messy way for main to access val in get()

//------------------------------------------------------------------------------

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
Token_stream::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 '/':
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
cin >> val;              // read a floating-point number
return Token('#', 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 '(':    // handle '(' expression ')'
{
double d = expression();
t = ts.get();
if (t.kind != ')') error("')' expected)");
return d;
break;
}

case '#':            // we use '#' to represent a number
{
return t.value;  // return the number's value
break;
}

default:
error("primary expected");
}
}

//------------------------------------------------------------------------------

// deal with *, /
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;
}

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()
try
{
while (cin) 
{
Token t = ts.get();

if (t.kind == 'q') 
break; // 'q' for quit
if (t.kind == ';')        // ';' for "print now"
cout << "=" << val << '\n';
else
ts.putback(t);
val = expression();
}
keep_window_open();
}
catch (exception& e) {
cerr << "error: " << e.what() << '\n';
keep_window_open();
return 1;
}
catch (...) {
cerr << "Oops: unknown exception!\n";
keep_window_open();
return 2;
}

//------------------------------------------------------------------------------

The five errors were pretty easy to catch as visual studio lights them up in the same way as a spelling mistake on word. The only one I had trouble rectifying was the variable val which stores the returned values that have been worked out. Eventually I just gave up trying to get the compiler to look in the right function and just made it a global variable so it can be accessed from anywhere but I'm pretty sure that isn't the answer. 

The logic errors were quite obvious as well, although I'm not sure if there were 4 or 3. He says 3 but I found 4. 

1. Multiplication doesn't work. To fix this I added parenthesis to the block of code in the switch statement. It was also missing the 'break;'

2. The subtracting was actually adding, that was a simple fix in the code to simply change + to -.

3. The number 8 was missing in the case list for the switch statement found in Token_stream::get(). I also changed the 'kind' for a number from '8' to '#' to make things less confusing.

4. I'm not sure about this one but in his code, the function which deals with * and /, it also says %. Now I tried to add some code so it could deal with modulo but it wouldn't work so I brushed it off as a typo seeing as though he said there were only 3 logic errors that would cause the program to give incorrect results.

As far as I am aware, the above code does give correct results.