// @topic W130210 state machine implementing desktop calculator
// @brief state machine implementing desktop calculator
// main_sm_calc.cpp
#include <cstdlib>
#include <stack>
#include <iostream>
#include <string>
#include <sstream>
using std::stack;
using std::cout;
using std::cin;
using std::string;
using std::stringstream;
enum {
STATE_OPERATOR_BINARY_PLUS = '+',
STATE_OPERATOR_BINARY_MINUS = '-',
STATE_OPERATOR_MULTIPLY = '*',
STATE_OPERATOR_DIVIDE = '/',
STATE_OPERATOR_EXPR_TERMINATOR = ';',
STATE_CLEAR = 1000,
STATE_GET_NEXT_TOKEN,
STATE_NUMBER,
STATE_CIN_CLOSED,
STATE_ERROR_BAD_NUMBER,
STATE_EXIT
};
void calculate_and_display_the_result( stack< int >& operators, stack< int >& operands )
{
int op1 = 0;
int op2 = 0;
cout << "\t result = ";
while ( operators.size() ) {
switch ( operators.top() ) {
case STATE_OPERATOR_BINARY_PLUS:
if ( operands.size() < 2 ) {
cout << "ERROR: Binary operator \'" << operators.top() << "\' requires two operands\n";
return;
}
op2 = operands.top(); operands.pop();
op1 = operands.top(); operands.pop();
operands.push( op2 + op1 );
cout << " " << op2 << char( operators.top() ) << op1 << " = ";
break;
case STATE_OPERATOR_BINARY_MINUS:
if ( operands.size() < 2 ) {
cout << "ERROR: Binary operator \'" << operators.top() << "\' requires two operands\n";
return;
}
op2 = operands.top(); operands.pop();
op1 = operands.top(); operands.pop();
operands.push( op2 - op1 );
cout << " " << op2 << char( operators.top() ) << op1 << " = ";
break;
case STATE_OPERATOR_MULTIPLY:
if ( operands.size() < 2 ) {
cout << "ERROR: Binary operator \'" << operators.top() << "\' requires two operands\n";
return;
}
op2 = operands.top(); operands.pop();
op1 = operands.top(); operands.pop();
operands.push( op2 * op1 );
cout << " " << op2 << char( operators.top() ) << op1 << " = ";
break;
case STATE_OPERATOR_DIVIDE:
if ( operands.size() < 2 ) {
cout << "ERROR: Binary operator \'" << operators.top() << "\' requires two operands\n";
return;
}
op2 = operands.top(); operands.pop();
op1 = operands.top(); operands.pop();
if ( op2 == 0 ) {
cout << "ERROR: Division by zero is illegal\n";
return;
}
operands.push( op2 / op1 );
cout << " " << op2 << char( operators.top() ) << op1 << " = ";
break;
default:
cout << "ERROR: Unexpected operator \'"<< char( operators.top() ) << "\'\n";
return;
}
operators.pop();
}
if ( operands.size() != 1 ) {
cout << "ERROR: bad arithmetic expression syntax\n";
return;
}
cout << "\t" << operands.top() << "\n";
}
int main()
{
//int result = 0;
int number = 0;
string input;
stack< int > states;
stack< int > operands;
stack< int > operators;
states.push( STATE_CLEAR );
while ( states.size() > 0 ) {
int state = states.top(); // get next state
if ( state == STATE_EXIT ) {
break;
}
states.pop(); // remove current state from the stack
switch ( state ) {
case STATE_CLEAR:
while ( operands.size() ) operands.pop();
while ( operators.size() ) operators.pop();
states.push( STATE_GET_NEXT_TOKEN );
break;
case STATE_CIN_CLOSED:
cout << "Input stream has closed, exiting...\n";
states.push( STATE_EXIT );
break;
case STATE_GET_NEXT_TOKEN:
if ( cin >> input ) {
// parse and process input token:
if ( input == "q" || input == "Q" || input == "quit" ) states.push( STATE_EXIT );
else if ( isdigit( input[ 0 ] ) ) states.push( STATE_NUMBER );
else if ( input.length() > 1 ) states.push( STATE_NUMBER ); // assume any multi-character token is a number
else if ( ispunct( input[ 0 ] ) ) states.push( input[ 0 ] ); // assume it's an operator
}
else {
states.push( STATE_CIN_CLOSED );
}
break;
case STATE_NUMBER:
{
// convert input to a numeric value
stringstream buffer( input );
if ( buffer >> number ) {
states.push( STATE_GET_NEXT_TOKEN );
operands.push( number );
} else {
states.push( STATE_ERROR_BAD_NUMBER );
}
break;
}
case STATE_OPERATOR_BINARY_PLUS:
case STATE_OPERATOR_BINARY_MINUS:
case STATE_OPERATOR_MULTIPLY:
case STATE_OPERATOR_DIVIDE:
operators.push( state );
states.push( STATE_GET_NEXT_TOKEN );
break;
case STATE_OPERATOR_EXPR_TERMINATOR:
calculate_and_display_the_result( operators, operands );
states.push( STATE_CLEAR );
break;
default:
if ( ( 0 < state ) && ( state <= 255 ) && isprint( state ) ) {
cout << "Unexpected token \'" << char( state ) << "\'\n";
} else {
cout << "Unexpected internal state " << state << "\n";
}
states.push( STATE_CLEAR );
break;
}//switch
}//while
system( "pause" );
return 0;
}