I have come at a stand still on chapter 6 of Bjarne Stroustrup's book Practice and Principles using c++. Basically, I have read this chapter twice and tried to understand the examples about grammars and tokens related to making a calculator. I would say that I understand the examples but when it all comes together, I am not able to debug the code and get the faulty program Bjarne gives us working.
We had to solve 5 errors that causes the program to not compile, and 3 logic errors. I solved 3 of the compile errors, but left 2 of them because I'm not sure if the way I fix them actually helps break the program. I will mark these with
//Syntax error "reason"
on the code. After that, there will be 3 logic errors and I would appreciate any help on figuring these out. You don't have to give me the actual answer for the logic ones, but some clues at least would be appreciated, probably even more so than answers. But if you give me the answer, that would also be great.
I keep looking online to see if anyone else has posted questions about the drill, but so far I haven't come across any. All help is appreciated. Thanks.
This chapter has been giving me a head ache and I would really like to get past it!
Added information:
What I did was declare full to be bool and true and make buffer and char. This is located in the get() function.
in the main function, I declared val a double.
After this, the program will tell me that get() function and primary() function don't return on all paths, so I made the default for get() return a token and the default for primary() return ts.value.
After this, the compiler shows no errors but won't run.
The code below has none of these changes, because I believe my changed help break the program.
// The code
#include "../../../std_lib_facilities.h"
//------------------------------------------------------------------------------
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 get()
{
if (full) { // do we already have a Token ready? //Syntax error "full" and "buffer" not declared
// 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();
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'; //Syntax error "val" not declared
else
ts.putback(t);
val = expression(); //Syntax error "val" not declared
}
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;
}
//------------------------------------------------------------------------------
As you know, the purpose of the programme is to compute simple arithmetic expressions. The expressions are made of numbers, arithmetic operators (sum, substraction, multiplication, division, remainder), and parentheses to group compounds and override the usual operator precedences.
The programme is built on top of the Token
and Token_stream
classes, which permit the lexical analysis of the original expression encoded as a stream of characters (the text input by the user). The later class extracts possibly meaningful characters from the underlying stream ( cin
), and depending on their values, build Token
instances, tokens with additional semantic (*):
double
Since the Token
class uses a single character to identify its kind , numbers are associated with the character 8
; note, however, that this is completely arbitrary, any other character not used by another token kind (ie. operators or parentheses here) could be used in its place: char kind
might as well be int kind
, or better yet enum kind
with a well defined enum
to represent that value without incidence on the programme (in this simple setting, char
happens to work well and is easy to implement).
The code performing the actual expression computation is divided in 3 functions, naturally covering the operator precedence:
primary
computes number from constants (ie. numbers as they come out of the stream), and parenthetized expressions, term
computes operations with higher precedence, expression
deals with lower precedence operations Again, in each case, a double
is returned, clearly indicating the type used to represent numbers.
As explained by the other answer, the val
variable must be declared in the scope of its use. You may define that variable either as a double
(the type of the computation result), or a string
(in which case the value will be converted).
The first one is located in the number token construction code: look at the characters used to detect if a number is coming next in the stream.
The second one is in the Token_stream::get
class method: roughly speaking, its declaration and definition don't match.
The third one looks like a copy and paste error in the expression
function: it looks like 2 operations are handled, but do we really compute 2 different output?
(*): Given that >>
skips blanks and other "unneeded" character from the stream, we really have two layers of tokening.
if (t.kind == ';') // ';' for "print now"
cout << "=" << val << '\n'; //Syntax error "val" not declared
else
ts.putback(t);
val = expression(); //Syntax error "val" not declared
The compiler does not know what val
is all about. You should declare it on top and assign it to a string you wanted to display.
String val = '';
...
val = 'message';
The compilation errors are:
1) In the first line after #include
lass Token //Wrong
class Token //Correct
2) In the member function get() is declared outside the class definition so the notation is wrong, this generate the error of full and buffer variables
Token get() //Wrong
Token Token_stream::get() //Correct
3) In the primary member function
if (t.kind != ')') error("')' expected); //Wrong
if (t.kind != ')') error("')' expected"); //Correct
4) In the expression member function
double left = term(; //Wrong
double left = term(); //Correct
5) In the main function the variable val is not declare so you need:
double val=0;
With that corrections the program should be able to compile correctly:
The Logic errors are fun to hunt so the clues are:
If you use Visual C++, try to use breakpoints and debug using F11, is easier to find the logic errors
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.