Seemingly-arbitrary error in calc prog at 8th term

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;
}

//------------------------------------------------------------------------------
Last edited on
Does adding a case: 8 on line 74 fix the error?

Edit: No idea why I put 74 instead of 68... lol
Last edited on
Ding ding ding ding.

Except I did it on line 68 where it made more sense.

I've been switching back and forth between computers while doing this last project so for one computer instead of writing down the code again I just downloaded the file from his site. He warned me it was buggy, and I thought I had fixed all of them (all the important ones at least), overlooking that very simple lack of a case '8'.

Thank you.

:)
See anything missing on line 68?

[Edit: ninja'd!]
Last edited on
Yes, sir/ma'am. I've already fixed it.
Last edited on
Topic archived. No new replies allowed.