CSCE 121 Chapter 7

From Notes
Jump to navigation Jump to search

« 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

  1. cerr is unbuffered and put directly to the screen