https://rentry.org/PPP2_Calculator_cpp

// Code derived from Stroustrup's PPP2 book
// From Chs. 6 & 7, The Calculator Project - CLI edition
//  -and beginning on p 174

#include "Calculator.h"

#include <cctype>
#include <iostream>
#include <stdexcept>

using std::cerr;
using std::cin;
using std::cout;
using std::endl;
using std::exception;
using std::isalpha;
using std::isdigit;

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

Token::Token(char const ch) : kind{ch}, value{0} {}

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

Token::Token(char const ch, std::string const& n) : kind{ch}, name{n} {}

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

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

  char ch;
  cin >> ch;  // note that >> skips whitespace (space, newline, tab, etc.)

  // clang-format off
  switch (ch) {
    case quit:
    case print:
    case '(':
    case ')':
    case '+':
    case '-':
    case '*':
    case '/':
    case '%':
    case '=': return Token(ch);  // let each character represent itself
    case '.':  // a floating-point literal can start with a dot
    case '0': case '1': case '2': case '3': case '4':
    case '5': case '6': case '7': case '8': case '9': {  // numeric literal
      cin.putback(ch);  // put digit back into the input stream
      double val;
      cin >> val;  // read a floating-point number
      return Token(number, val);
    }
    default:
      if (isalpha(ch)) {  // start with a letter
        string s;
        s += ch;
        while (cin.get(ch) && (isalpha(ch) || isdigit(ch) || ch == '_'))
          s += ch;  // letters digits and underscores

        cin.putback(ch);

        if (s == declkey)
          return Token{let};  // keyword "let"

        if (s == constkey)
          return Token{con};  // keyword "const"

        return Token{name, s};
      }

      error("Bad token");
  }
  // clang-format on

  // shouldn't reach here
  return Token('K');  // there is no valid 'K' token kind
}

//------------------------------------------------------------------------------
void Token_stream::putback(Token const t)
{
  if (full)
    error("putback() into a full buffer");

  buffer = t;     // copy t to buffer
  full   = true;  // buffer is now full
}

//------------------------------------------------------------------------------
void Token_stream::ignore(char const c)  // c represents the kind of a Token
{
  // first look in buffer:

  if (full && c == buffer.kind) {
    full = false;
    return;
  }

  // now search input:

  full = false;

  char ch = 0;

  while (cin >> ch)
    if (ch == c)
      return;
}

//------------------------------------------------------------------------------
Calculator::Calculator() { set_constants(); }

//------------------------------------------------------------------------------
void Calculator::set_constants()
{
  // note: these pre-defiend names are constants:

  define_name("pi", 3.1415926535, false);
  define_name("e", 2.7182818284, false);
}

//------------------------------------------------------------------------------
double Calculator::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 number: return t.value;  // return the number's value
    case name: {
      Token next = ts.get();

      if (next.kind == '=') {  // handle name = expression
        double d = expression();
        set_value(t.name, d);
        return d;
      } else {
        ts.putback(next);          // not an assignment: return the value
        return get_value(t.name);  // return the variable's value
      }
    }
    case '-': return -primary();
    case '+': return primary();
    default: error("primary expected");
  }

  // shouldn't reach here
  return 0.0;
}

//------------------------------------------------------------------------------
double Calculator::term()
{
  double left = primary();

  Token t = ts.get();  // get the next token from token stream

  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>(term());
        if (i2 == 0)
          error("%: divide by zero");

        left = i1 % i2;
        break;
      }
      default:
        ts.putback(t);  // put t back into the token stream
        return left;
    }

    t = ts.get();
  }
}

//------------------------------------------------------------------------------
double Calculator::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
        break;
      case '-':
        left -= term();  // evaluate Term and subtract
        break;
      default:
        ts.putback(t);  // put t back into the token stream
        return left;    // finally: no more + or -: return the answer
    }

    t = ts.get();
  }
}

//------------------------------------------------------------------------------
double Calculator::declaration(Token const k)
{
  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, k.kind == let);

  return d;
}

//------------------------------------------------------------------------------
double Calculator::statement()
{
  Token t = ts.get();

  switch (t.kind) {
    case let:
    case con: return declaration(t.kind);
    default: ts.putback(t); return expression();
  }
}

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

//------------------------------------------------------------------------------
void Calculator::run()
{
  while (cin) {
    try {
      cout << prompt;

      // First we'll 'peek ahead' in the Token_stream to see what's next:

      Token t = ts.get();

      while (t.kind == print)  // discard all (consecutive) "prints"
        t = ts.get();          //

      if (t.kind == quit)
        return;

      ts.putback(t);

      // Having put the Token back, we can now proceed to parse the grammar
      cout << result << statement() << endl;

    } catch (exception const& e) {
      cerr << e.what() << endl;  // write error message
      clean_up_mess();
    }
  }
}

main_calculator.cppCalculator.hcalc_util.hcalc_util.cpp

Edit
Pub: 09 Mar 2023 05:06 UTC
Edit: 03 May 2023 10:41 UTC
Views: 459