https://rentry.org/PPP2_p240

// Code derived from Stroustrup's PPP2 book
// § 7.7 Recovering from errors
//  -and beginning on p 240

/*
    Simple calculator

    Revision history:

        Revised by Bjarne Stroustrup May 2007
        Revised by Bjarne Stroustrup August 2006
        Revised by Bjarne Stroustrup August 2004
        Originally written by Bjarne Stroustrup
            (bs@cs.tamu.edu) Spring 2004.

    This program implements a basic expression calculator.
    Input from cin; output to cout.

    The grammar for input is:

    Statement:
        Expression
        Print
        Quit

    Print:
        ;

    Quit:
        q

    Expression:
        Term
        Expression + Term
        Expression - Term
    Term:
        Primary
        Term * Primary
        Term / Primary
        Term % Primary
    Primary:
        Number
        ( Expression )
        - Primary
        + Primary
    Number:
        floating-point-literal


        Input comes from cin through the Token_stream called ts.
*/

#include <iostream>
#include <stdexcept>
#include <string>

using namespace std;

void error(const char* s) { throw runtime_error(s); }

// run-time checked narrowing cast (type conversion)
template <class R, class A>
R narrow_cast(A const& a)
{
  R r = R(a);
  if (A(r) != a)
    error("narrow_cast() info loss");

  return r;
}

//------------------------------------------------------------------------------

const char   number = '8';   // t.kind==number means that t is a number Token
const char   quit   = 'q';   // t.kind==quit means that t is a quit Token
const char   print  = ';';   // t.kind==print means that t is a print Token
const string prompt = "> ";  // used to indicate we're waiting on user input
const string result = "= ";  // used to indicate that what follows is a result

//------------------------------------------------------------------------------
class Token {
 public:
  Token(char ch) : kind{ch} {}

  Token(char ch, double val) : kind{ch}, value{val} {}

  char   kind  = '0';
  double value = 0.0;
};

//------------------------------------------------------------------------------
class Token_stream {
 public:
  Token_stream();

  Token get();
  void  putback(Token t);
  void  ignore(char c);

 private:
  bool  full;
  Token buffer;
};

//------------------------------------------------------------------------------
Token_stream::Token_stream() : full{false}, buffer{0} {}

//------------------------------------------------------------------------------
// read a token from cin
Token Token_stream::get()
{
  if (full) {  // check if we already have a Token ready
    full = false;
    return buffer;
  }

  char ch;
  cin >> ch;

  // clang-format off
  switch (ch) {
    case print:
    case quit:
    case '(':
    case ')':
    case '+':
    case '-':
    case '*':
    case '/':
    case '%':
      return Token{ch};
      break;
    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 input stream
      double val;
      cin >> val;
      return Token{number, val};
      break;
    }
    default:
      error("Bad token");
      return Token{'K'};  // invalid, shouldn't reach here
  }
  // clang-format on
}

//------------------------------------------------------------------------------
// put token back into stream
void Token_stream::putback(Token t)
{
  if (full)
    error("putback() into a full buffer");

  buffer = t;
  full   = true;
}

//------------------------------------------------------------------------------
// c represents the kind of a Token
void Token_stream::ignore(char c)
{
  // 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_stream ts;

double expression();

//------------------------------------------------------------------------------
// deal with numbers, parentheses, unary -, and unary +
double primary()
{
  Token t = ts.get();

  switch (t.kind) {
    case '(': {
      double d = expression();

      t = ts.get();
      if (t.kind != ')')
        error("')' expected");

      return d;
      break;
    }
    case number: return t.value; break;
    case '-': return -primary(); break;
    case '+': return primary(); break;
    default:
      error("primary expected");
      // shouldn't reach here
      return 0.0;
  }
}

//------------------------------------------------------------------------------
// deal with *, /, and %
double term()
{
  double left = primary();

  Token t = ts.get();

  while (true) {
    switch (t.kind) {
      case '*': left *= primary(); break;
      case '/': {
        double d = primary();
        if (d == 0)
          error("divide by zero");

        left /= d;
        break;
      }
      case '%': {
        int i1 = narrow_cast<int>(left);
        int i2 = narrow_cast<int>(primary());
        if (i2 == 0)
          error("%: divide by zero");

        left = i1 % i2;
        break;
      }
      default: ts.putback(t); return left;
    }

    t = ts.get();
  }
}

//------------------------------------------------------------------------------
// deal with + and -
double expression()
{
  double left = term();

  Token t = ts.get();

  while (true) {
    switch (t.kind) {
      case '+': left += term(); break;
      case '-': left -= term(); break;
      default: ts.putback(t); return left;
    }

    t = ts.get();
  }
}

//------------------------------------------------------------------------------
void clean_up_mess() { ts.ignore(print); }

//------------------------------------------------------------------------------
// expression evaluation loop
void calculate()
{
  while (cin)
    try {
      cout << prompt;

      Token t = ts.get();

      while (t.kind == print)
        t = ts.get();

      if (t.kind == quit) {
        cout << "\b\b";
        return;
      }

      ts.putback(t);

      cout << result << expression() << '\n';

    } catch (exception& e) {
      cerr << e.what() << '\n';

      clean_up_mess();
    }
}

//------------------------------------------------------------------------------
int main()
try {
  calculate();

} catch (exception& e) {
  cerr << e.what() << '\n';
  return 1;

} catch (...) {
  cerr << "exception \n";
  return 2;
}

build & run:

g++ -std=c++20 -O2 -Wall -pedantic ./ch_07/main_p240.cpp && ./a.out

PrevUpNext

Edit
Pub: 02 Apr 2023 09:39 UTC
Edit: 02 May 2023 21:59 UTC
Views: 540