CSCE 121 Chapter 7
« previous | Monday, September 13, 2010 | next »
Completing a program
Defining Token
struct Token { // user-defined type called Token
// data members
char kind;
double value;
// function members ("Constructors")
Token(char ch) : kind(ch), value(0) { }
Token(char ch), double val) : kind(ch), value(val) { }
};
Token t;
t.kind = '#';
t.value = 2.3;
Token u = t; // u becomes a copy of t
cout << u.value; // will print 2.3
- a struct is the simplest form of a class
- class is C++'s term for a user-defined type
- Constructors have same name as class; defines how object is initialized
Defining Token_stream
- reads chars, producing Tokens on demand
- Tokens can be stored inside for later use
- uses "buffer" to hold tokens we put back into it.
class Token_stream {
bool full; // is there a Token in the buffer?
Token buffer; // keep a Token put back using putback()
public:
// User interface
Token get(); // get Token
void putback(Token); // store Token in buffer
Token_stream() : full(false), buffer(0) { } // constructor
};
void Token_stream::putback(Token t) {
if (full) error("putback() into a full buffer");
buffer = t;
full = true;
}
Token Token_stream::get() {
if (full) { full = false; return buffer; }
char ch;
cin >> ch;
switch (ch) {
case '(': case ')': case ';': case 'q': case '+': case '-': case '*': case '/':
return Token(ch); // let each character repeat 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 input stream
double val;
cin >> val; // attempt to read value as a double
return Token('#', val);
}
default:
error("Bad token");
}
}
Improvements to Calculator
Wednesday, September 15, 2010 (Substitute: Dr. Walter Daugherity)
Hint: use exit(int n); to return n to the operating system. This can be used instead of return #; in main()
- Style (clarity of code)
- Comments
- Naming
- Use of functions (refactoring)
- ...
- Functionality
- Better prompt
- Recovery on error
- Negative numbers
- % (Remainder/modulus)
- Pre-defined symbolic values
- Variables
- ...
Prompting
Don't be too wordy:
Expression: 2+3; 5*7; 2+9; Result: 5 Expression: Result: 35 Expression: Result: 11 Expression:
Use something universal and simple:
> 2+3; = 5 >
Cleaning up code
Leave main() for initialization and error catching only. Move as much code out of main() as possible.
Fix: Create a calculate() function:
void calculate() {
while (cin) try {
cout << PROMPT;
Token t = ts.get();
while (t.kind == PRINT) t = ts.get();
if (t.kind == QUIT) return;
} catch (exception& e) {
cerr << e.what << endl;
clean_up_mess(); // <<< The tricky part!
}
}
No "magic" (undefined) constants.
Fix:
// Token "kind" values:
const char NUMBER = '#'; // a floating-point number
const char QUIT = 'q'; // an exit command
const char PRINT = ';'; // a print command
// User interaction strings:
const string PROMPT = "> ";
const string RESULT = "= ";
If a constant is used more than once, it should probably be symbolic
Graceful Error Recovery
Friday, September 17, 2010 (Substitute: Dr. Bjarne Stroustrup)
Currently: any user error terminates the program
Fix:
- use try{ ... } catch (...) { ... }
- use cerr to print error messages[1]
void calculate() {
while (cin) try {
cout << PROMPT;
Token t = ts.get();
while (t.kind == PRINT) t = ts.get();
if (t.kind == QUIT) return;
} catch (exception& e) {
cerr << e.what << endl;
clean_up_mess(); // <<< The tricky part!
}
}
First attempt at clean_up_mess()
void clean_up_mess() { // Skip everything up to ';'
while (true) {
Token t = ts.get(); // throws error if bad token
if (t.kind == PRINT) return
}
}
We don't want error-handling while we're handling errors; look at characters instead of tokens.
Second attempt at clean_up_mess()
void Token_stream::ignore(char c) {
if (full && c == buffer.kind) {
full = false;
return;
}
full = false;
char ch = 0;
while (cin >> ch)
if (ch == c) return;
}
// ...
void clean_up_mess(){
ts.ignore(PRINT);
}
Feature Requests
Not yet implemented:
- Negative numbers
- remainder/modulo
- Pre-defined symbolic values (like π)
- Variables
Start small, then "grow" the programs
Footnotes
- ↑ cerr is unbuffered and put directly to the screen