So I've been following along with the creation of this console calc in Stroustrup's Principles and Practice using C++ book. I'm on chapter 7. Self-instruction. No class to follow along with.
I'm testing strange input in attempts to break the program and catch new errors.
You see this little pyramid of tokens in which I successively evaluate greater and greater numbers of terms in simple addition problems.
http://oi44.tinypic.com/2h2fs7s.jpg
At the 8th term, seemingly arbitrarily, it seems to break down. I opened it again and tested with nine terms, and it broke down too.
But then (in the midst of typing this nonetheless, you know how potential solutions just dawn on you sometimes) I made the terms all '1', and it did just fine, regardless of the number of terms I used.
I played with this theme and tried '9'. It worked. With '8' it did not. So the problem narrows down. I discover that it's not the specific number of terms I'm using that's the problem, but something having to do with the number '8' itself.
http://oi39.tinypic.com/w2mtme.jpg
So I remember last chapter that the constructors for our Token class take two parameters:
I: a char for its kind.
II: a double for its value.
We (cryptically, the author warns me) settle upon using '8' as the "kind" char when our token is a number. I thought this was weird and that there were better choices, but I'm a pissant student, not Bjarne "Be Yarn" Stroustrup so what the hell do I know? Plus I've noticed that he enjoys putting me through the mill and making me do things wrong first, even if I notice.
In short: what causes this problem? My source code? Some inherent property of memory/parsing that I don't understand yet? Both? Neither? Is it arbitrary?
Thanks in advance for your patience and help.
Here's the src
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
|
#include "../std_lib_facilities.h"
//a header that my textbook is making me use until it teaches me about libraries
//------------------------------------------------------------------------------
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 '/':
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 '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 '(': // handle '(' expression ')'
{
double d = expression();
t = ts.get();
if (t.kind != ')') error("')' expected");
return d;
}
case '8': // we use '8' to represent a number
return t.value; // return the number's value
default:
error("primary expected");
}
}
//------------------------------------------------------------------------------
// deal with *, /, and %
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;
break;
}
}
}
//------------------------------------------------------------------------------
// 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
break;
}
}
}
//------------------------------------------------------------------------------
int main()
try
{
double val = 0;
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;
}
//------------------------------------------------------------------------------
|