chapter7 calculator

122 views
Skip to first unread message

yuanwxu

unread,
May 6, 2012, 8:18:39 AM5/6/12
to PPP-public
Hi. I wonder if anyone can point out what's going wrong here. Every
bit of the code copied exactly from the book. I got a bizarre "=nan"
as output(it means Not a Number I guess) when I type any expression.

const char number='8';
const char quit='q';
const char print=';';
const string prompt=">";
const string result="=";
const char name='a';//name token
const char let='L';//declaration token
const string declkey="let";//declaration keyword

class Token
{public:
char kind;
double value;
string name;
Token(char ch)
:kind(ch),value(0){}
Token(char ch,double val)
:kind(ch),value(val){}
Token(char ch,string n)
:kind(ch),name(n){}
};

class Token_stream
{public:
Token_stream();
Token get();
void putback(Token t);
void ignore(char c);//discard characters up to and including c
private:
bool full; //is there a token in the buffer?
Token buffer;
};

Token_stream::Token_stream()
:full(false),buffer(0){}
void Token_stream::putback(Token t)
{
if(full)error("putback() into a full buffer");
buffer=t;
full=true;
}
void Token_stream::ignore(char c)//c represents the kind of Token
{
//first look in buffer
if(full && c==buffer.kind)
{
full=false;
return;
}
full=false;
//now search input
char ch=0;
while(cin>>ch)
if(ch==c)return;
}
Token Token_stream::get() //read characters from cin and compose a
token
{
if(full) //remove Token from buffer
{
full=false;
return buffer;
}

char ch;
cin>>ch;
switch(ch)
{
case print://for print
case quit://for quit
case'(':
case')':
case'+':
case'-':
case'*':
case'/':
case '%':
case'=':
return Token(ch); //let each character represnt 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 digit stream
double val;
cin>>val;
return Token(number,val);
}
default:
if(isalpha(ch))
{
string s;
s+=ch;
while(cin.get(ch) && (isalpha(ch) || isdigit(ch))) s
+=ch;
cin.putback(ch);
if(s==declkey) return Token(let);//declaration keyword
return Token(name,s);
}
error("Bad token");
}
}

class Variable
{
public:
string name;
double value;
Variable(string n, double v): name(n),value(v){}
};

vector<Variable> var_table;
double get_value(string s)//return the value of the Variable named s
{
for(int i=0;i<var_table.size();++i)
if(s==var_table[i].name) return var_table[i].value;
error("get:undefined variable ",s);
}
void set_value(string s,double d)
{
for(int i=0;i<var_table.size();++i)
if(s==var_table[i].name) {var_table[i].value=d;return;}
error("set:undefined variable ",s);
}

bool is_declared(string var)//is var already in var_table?
{
for(int i=0;i<var_table.size();++i)
if(var==var_table[i].name) return true;
return false;
}
double define_name(string var,double val)//add (var,val) to var_table
{
if(is_declared(var)) error(var,"declared twice");
var_table.push_back(Variable(var,val));
return val;
}

Token_stream ts;
double expression();

double primary()
{
Token t=ts.get();
switch(t.kind)
{
case'(':
{
double d=expression();
t=ts.get();
if(t.kind!=')')error("')' expected");
return d;
}
case number:
return t.value;
case '-':
return -primary();
case '+':
return primary();
case name:
return get_value(t.name);
default:
error("primary expected");
}
}
double term()
{
double left=primary();
Token t=ts.get();
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 d=primary();
int i1=int(left);
if(i1!=left) error("left-hand operand of % not int");
int i2=int(d);
if(i2!=d) error("right-hand operand of % not int");
if(i2==0) error("%: divide by zero");
left=i1%i2;
t=ts.get();
break;
}
default:
ts.putback(t);//put back into the Token stream
return left;
}
}
}
double expression()
{
double left=term();
Token t=ts.get();
while(true)
{
switch(t.kind)
{
case'+':
left+=term();
t=ts.get();
break;
case'-':
left-=term();
t=ts.get();
break;
default:
ts.putback(t);
return left;
}
}
}


//handle:name=expression, declare a varaible called "name" with
initial value "expression"
double declaration()
{
Token t=ts.get();
if(t.kind!=name)error("name expected in declaration");
string var_name=t.name;

Token t2=ts.get();
if(t2.kind!='=')error("= missing in declaration of ",var_name);

double d=expression();
define_name(var_name,d);
return d;
}
double statement()
{
Token t=ts.get();
switch(t.kind)
{
case let:
return declaration();
default:
ts.putback(t);
return expression();
}
}

void clean_up_mess()
{
ts.ignore(print);
}
void calculate()
{
while(cin)
try{
cout<<prompt;
Token t=ts.get();
while(t.kind==print) t=ts.get(); //eat';'
if(t.kind==quit) return;
ts.putback(t);
cout<<result<<statement()<<endl;
}
catch (exception& e) {
cerr << e.what() << endl;
clean_up_mess();
}
}

int main()
try
{
calculate();
keep_window_open();
return 0;
}
catch (...) {
cerr << "exception \n";
keep_window_open ("~2");
return 2;
}

tfmccarthy_verizon_mail

unread,
May 6, 2012, 4:39:31 PM5/6/12
to ppp-p...@googlegroups.com
I can't replicate the problem. This compiles and accepts basic expressions,

>1+1;
=2
>1-1;
=0
>1*1;
=1
>1/1;
=1
>1%1;
=0
>(1)+1;
=2
>1+(1);
=2
>

Can you provide the input that generates the problem?

- tim

yuanwxu

unread,
May 8, 2012, 3:59:29 PM5/8/12
to PPP-public
I also used simple expressions
>1+2;
=nan
>let a=1;
=nan

On 5月6日, 下午9时39分, "tfmccarthy_verizon_mail" <tfmccar...@verizon.net>
wrote:

tfmccarthy_verizon_mail

unread,
May 10, 2012, 3:01:29 PM5/10/12
to ppp-p...@googlegroups.com
OK, this provides a few more clues to the problem. The problem output line
is,

=nan

This is generated by the line,

void calculate()
{
while (cin)
try {
//...
cout << result << statement() << endl;
} catch (exception& e)
{
//...
}
}

So the problem is the output from statement. The function returns a double
and the value being returned is "not a number" (nan). If you have your
debugger working, you should step through the code in statement and examine
the return value. You should then step into the functions as they are called
to determine which one is causing the problem.

If you can't debug you can add debug output statements to the code to trace
the execution,

Adding debug statements to output productions,

double statement()
{
Token t = ts.get();
switch (t.kind)
{
case let:
cerr << "statement -> declaration" << endl;
return declaration();
default:
ts.putback(t);
cerr << "statement -> expression" << endl;
return expression();
}
}

double expression()
{
double left = term();
Token t = ts.get();
while (true)
{
switch (t.kind)
{
case '+':
cerr << "expression -> term + term" << endl;
left += term();
t = ts.get();
break;
case '-':
cerr << "expression -> term - term" << endl;
left -= term();
t = ts.get();
break;
default:
cerr << "expression -> term" << endl;
ts.putback(t);
return left;
}
}
}

double term()
{
double left = primary();
Token t = ts.get();
while (true)
{
switch (t.kind)
{
case '*':
{
cerr << "term -> primary * primary" << endl;
left *= primary();
t = ts.get();
break;
}
case '/':
{
cerr << "term -> primary / primary" << endl;
double d = primary();
if (d == 0) error("divide by zero");
left /= d;
t = ts.get();
break;
}
case '%':
{
cerr << "term -> primary % primary" << endl;
double d = primary();
int i1 = int(left);
if (i1 != left) error("left-hand operand of % not int");
int i2 = int(d);
if (i2 != d) error("right-hand operand of % not int");
if (i2 == 0) error("%: divide by zero");
left = i1 % i2;
t = ts.get();
break;
}
default:
cerr << "term -> primary" << endl;
ts.putback(t); //put back into the Token stream
return left;
}
}
}

double primary()
{
Token t = ts.get();
switch (t.kind)
{
case'(':
{
cerr << "primary -> (expression)" << endl;
double d = expression();
t = ts.get();
if (t.kind != ')') error("')' expected");
return d;
}
case number:
cerr << "primary -> number" << endl;
return t.value;
case '-':
cerr << "primary -> -primary" << endl;
return -primary();
case '+':
cerr << "primary -> +primary" << endl;
return primary();
case name:
cerr << "primary -> name" << endl;
return get_value(t.name);
default:
error("primary expected");
}
}

This produces the output,

>1+2;
statement -> expression
primary -> number
term -> primary
expression -> term + term
primary -> number
term -> primary
expression -> term
=3
>

Finally, add one more debug aide to output the tokens as they are returned
from Token_stream::get(),

void dump_token(Token token)
{
cout << "Token(" << token.kind
<< ", " << token.value
<< ", " << token.name
<< ")" << endl;
}

Token Token_stream::get() //read characters from cin and compose a token
{
if (full) { //remove Token from buffer
full = false;
dump_token(buffer);
return buffer;
}

char ch;
cin >> ch;
switch(ch)
{
case print://for print
case quit://for quit
case '(':
case ')':
case '+':
case '-':
case '*':
case '/':
case '%':
case '=':
dump_token(Token(ch));
return Token(ch); //let each character represnt 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 digit stream
double val;
cin >> val;
dump_token(Token(number,val));
return Token(number,val);
}
default:
// tfm: where does this come from?
if (isalpha(ch)) {
string s;
s += ch;
while (cin.get(ch) && (isalpha(ch) || isdigit(ch)))
s += ch;
cin.putback(ch);
if (s == declkey) return Token(let); //declaration keyword
dump_token(Token(name,s));
return Token(name,s);
}
error("Bad token");
}
}

This should give the output,

>1+2;
Token(8, 1, ) // read by calculate
Token(8, 1, ) // by statement
statement -> expression
Token(8, 1, ) // by primary
primary -> number
Token(+, 0, ) // by term
term -> primary
Token(+, 0, ) // by expression
expression -> term + term
Token(8, 2, ) // by primary
primary -> number
Token(;, 0, ) // by term
term -> primary
Token(;, 0, ) // by expression
expression -> term
=3
>Token(;, 0, ) // by calculate
q
Token(q, 0, ) // by calculate

This should point to the culprit. Since my copy of your code works, I would
suspect there is some code difference, so re-check the code. Otherwise, the
output statements should identify the problem.

----- Original Message -----
From: "yuanwxu" <yua...@126.com>
To: "PPP-public" <ppp-p...@googlegroups.com>
Reply all
Reply to author
Forward
0 new messages