/*
 * calculator.c
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define SEARCH_FOR_FIRST_OPERAND_START   0
#define SEARCH_FOR_FIRST_OPERAND_END     1
#define SEARCH_FOR_OPERATION_SIGN        2
#define SEARCH_FOR_SECOND_OPERAND_START  3
#define SEARCH_FOR_SECOND_OPERAND_END    4
#define SEARCH_FOR_END_OF_STRING         5


// solution using a finite-state machine

int calculator(char * s, float * result)
{
    int state;
    float first_operand, second_operand;
    char operation;
    char *operand_start, *operand_end;

    char tmp[30];
    int i;

    while (*s != 0) {
        switch (state) {

        case SEARCH_FOR_FIRST_OPERAND_START:
            if (*s == ' ') {
                // it is a space, skip it!
                ++s;
            }
            else if (isdigit(*s) || *s == '.' ) {
                // start of first operand
                operand_start = s;
                state = SEARCH_FOR_FIRST_OPERAND_END;
                ++s;
            }
            else {
                // not a digit, a point or a space, return syntax error
                return 1;
            }
            break;

        case SEARCH_FOR_FIRST_OPERAND_END:
            if (isdigit(*s) || *s == '.' ) {
                // it is still the first operand, continue
                ++s;
            }
            else {
                i = 0;
                // not a digit or a point, the first operand ends here
                operand_end = s;
                // now copy from operand_start to operand_end into a temporary string
                // and convert it into float
                while (operand_start != operand_end) {
                    tmp[i] = *operand_start;
                    ++operand_start;
                    ++i;
                }
                // add end-of-string character
                tmp[i] = 0;
                // now convert it to float
                first_operand = atof(tmp);
                state = SEARCH_FOR_OPERATION_SIGN;
            }
            break;

        case SEARCH_FOR_OPERATION_SIGN:
            if (*s == ' ') {
                // space, skip it!
                ++s;
            }
            else if (*s == '+' || *s == '-' || *s == '*' || *s == '/' ) {
                // a valid symbol has been found, get it!
                operation = *s;
                ++s;
                state = SEARCH_FOR_SECOND_OPERAND_START;
            }
            else {
                // not a valid symbol, return syntax error
                return 1;
            }
            break;

        case SEARCH_FOR_SECOND_OPERAND_START:
            if (*s == ' ') {
                // it is a space, skip it!
                ++s;
            }
            else if (isdigit(*s) || *s == '.' ) {
                // start of first operand
                operand_start = s;
                state = SEARCH_FOR_SECOND_OPERAND_END;
                ++s;
            }
            else {
                // not a digit, a point or a space, return syntax error
                return 1;
            }
            break;

        case SEARCH_FOR_SECOND_OPERAND_END:
            if (isdigit(*s) || *s == '.' ) {
                // it is still the first operand, continue
                ++s;
            }
            else {
                operand_end = s;
                state = SEARCH_FOR_END_OF_STRING;
            }
            break;

        case SEARCH_FOR_END_OF_STRING:
            if (*s == ' ') {
                // it is a space, ok skip it
                ++s;
            }
            else {
                // another symbol? syntax error!
                return 1;
            }

        }
    }

    if (state != SEARCH_FOR_END_OF_STRING && state != SEARCH_FOR_SECOND_OPERAND_END)
        return 1;

    if (state == SEARCH_FOR_SECOND_OPERAND_END)
        operand_end = s;


    i = 0;
    // now we need to get the second operand,
    // so copy from operand_start to operand_end into a temporary string
    // and convert it into float
    while (operand_start != operand_end) {
        tmp[i] = *operand_start;
        ++operand_start;
        ++i;
    }
    // add end of string character
    tmp[i] = 0;
    // now convert it to float
    second_operand = atof(tmp);

    // do the operation
    switch (operation) {
    case '+': *result = first_operand + second_operand; break;
    case '-': *result = first_operand - second_operand; break;
    case '*': *result = first_operand * second_operand; break;
    case '/': *result = first_operand / second_operand; break;
    }

    return 0;

}


void main()
{
    float result;
    char s[80];

    printf("Insert expression:");
    fgets(s, 80, stdin);
    s[strlen(s)-1] = 0; // remove tailing CR

    printf("Computing '%s' = ", s);
    if (calculator(s, &result) == 1)
        printf("SYNTAX ERROR\n");
    else
        printf("%f\n", result);
}

