
//
// Przykładowy kod z rozdziału 6.6 "Wypróbowywanie pierwszej wersji" książki
// "Programowanie. Teoria i praktyka z wykorzystaniem C++" Bjarne'a Stroustrupa.
//

#include "std_lib_facilities.h"

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

class Token {
public:
    char kind;        // rodzaj tokenu
    double value;     // Dla liczb: wartość. 
    Token(char ch)    // Tworzy Token ze znaku.
        :kind(ch), value(0) { }    
    Token(char ch, double val)     // Tworzy Token ze znaku i wartości typu double.
        :kind(ch), value(val) { }
};

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

class Token_stream {
public: 
    Token_stream();   // Tworzy Token_stream wczytujący ze strumienia cin.
    Token get();      // Pobiera token.
    void putback(Token t); // Wstawia token z powrotem.
private:
    // szczegóły implementacyjne
};

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

Token_stream ts;      // globalny strumień tokenów

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

double expression();  // Wczytuje i wartościuje wyrażenie.

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

double term();        // Wczytuje i wartościuje składnik.

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

double primary();     // Wczytuje i wartościuje czynnik.

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

int main()
try {
    while (cin)
        cout << expression() << '\n';
    keep_window_open();
}
catch (exception& e) {
    cerr << e.what() << endl;
    keep_window_open ();
    return 1;
}
catch (...) {
    cerr << "Wyjątek \n";
    keep_window_open ();
    return 2;
}

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

double expression()
{
    double left = term(); // Wczytuje i wartościuje składnik.
    Token t = ts.get();   // Pobiera następny token ze strumienia.

    while(true) {    
        switch(t.kind) {
    case '+':
        left += term();   // Wartościuje składnik i wykonuje dodawanie.
        t = ts.get();
        break;
    case '-':
        left -= term();   // Wartościuje składnik i wykonuje odejmowanie.
        t = ts.get();
        break;
    default: 
        ts.putback(t);    // Wstawia token t z powrotem do strumienia tokenów.
        return left;      // Jeśli nie ma więcej znaków + lub –, zwraca odpowiedź.
        }
    }
}

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

double term()
{
    double left = primary();
    Token t = ts.get();   // Pobiera następny token ze strumienia.

    while(true) {
        switch (t.kind) {
        case '*':
            left *= primary();
            t = ts.get();
            break;
        case '/':
            {    
                double d = primary();
                if (d == 0) error("Dzielenie przez zero.");
                left /= d; 
                t = ts.get();
                break;
            }
        default: 
            ts.putback(t); // Wstawia token t z powrotem do strumienia tokenów.
            return left;
        }
    }
}

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