
//
// 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) { }
};

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

Token get_token()    // Wczytuje token ze strumienia cin.
{
    char ch;
    cin >> ch;    // Operator >> pomija białe znaki (spacje, znaki nowego wiersza, tabulatory itp.)

    switch (ch) {
    case '(': case ')': case '+': case '-': case '*': case '/': 
        return Token(ch);        // Każdy znak reprezentuje samego siebie.
    case '.':
    case '0': case '1': case '2': case '3': case '4':
    case '5': case '6': case '7': case '8': case '9':
        {    
            cin.putback(ch);         // Wstawia cyfrę z powrotem do strumienia wejściowrgo.
            double val;
            cin >> val;              // Wczytuje liczbę zmiennoprzecinkową.
            return Token('8',val);   // Cyfra 8 reprezentuje liczbę.
        }
    default:
        error("Nieprawidłowy token.");
    }
}

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

double expression();  // Wczytuje i oblicza wartość wyrażenia.

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

double term();        // Wczytuje i oblicza wartość składnika.

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

double primary()     // Wczytuje i oblicza wartość czynnika.
{
    Token t = get_token();
    switch (t.kind) {
    case '(':    // Obsługa '(' wyrażenie ')'.
        {    
            double d = expression();
            t = get_token();
            if (t.kind != ')') error("')' był oczekiwany.");
            return d;
        }
    case '8':            // Do reprezentacji liczb służy znak 8.
        return t.value;  // Zwraca wartość liczby.
    default:
        error("Był oczekiwany 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 oblicza wartość składnika.
    Token t = get_token();     // Pobiera następny token.
    while(true) {    
        switch(t.kind) {
        case '+':
            left += term();    // Oblicza wartość składnika i dodaje.
            t = get_token();
            break;
        case '-':
            left -= term();    // Oblicza wartość składnika i odejmuje.
            t = get_token();
            break;
        default:
            return left;       // Nie ma więcej znaków + i -: zwraca wynik.
        }
    }
}

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

double term()
{
    double left = primary();
    Token t = get_token();     // Pobiera następny token.

    while(true) {
        switch (t.kind) {
        case '*':
            left *= primary();
            t = get_token();
            break;
        case '/':
            {    
                double d = primary();
                if (d == 0) error("Dzielenie przez zero.");
                left /= d; 
                t = get_token();
                break;
            }
        default: 
            return left;
        }
    }
}

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