3

I have an assignment in my Data Structures class in which I have to program a calculator that solves arithmetic expressions with the 4 basic operations and parenthesis, the input is done via the stdin buffer and the same with the output.

It was easy at the beginning, the teacher provided us the algorithms (how to transform the expression from the infix to the postfix and how to evaluate it) and the only goal was for us to implement our own stack and use it, but the calculator itself does not work quite well, and I think it is because of my parser.

This is the algorithm, and my code, used to parse the numbers, operators and parenthesis while putting them into an array to store the expression in a way that it is easier to evaluate later.

// saida is an array of pairs of integers, the first value of the pair is the value of the info (the number itself or the ASCII value of the operator)
// The second value is an indicator of whether it is a number or a operator
for (i = 0; i < exp_size; i++) {
    c = expression[i];

    // If the current char is a digit, store it into a helper string and keep going until a non-digit is found
    // Then atoi() is used to transform this string into an int and then store it.
    if (c >= '0' && c <= '9') {
        j = 1, k = i+1;
        tempInt[0] = c;
        while(expression[k] >= '0' && expression[k] <= '9') {
            tempInt[j++] = expression[k];
            k++;
        }
        tempInt[j] = '\0';
        saida[saidaIndex][0] = atoi(tempInt);
        saida[saidaIndex++][1] = 0;
        i = k-1;
    }

    // If the character is an operator, the algorithm is followed.
    else if (c == '+' || c == '-' || c == '*' || c == '/') {
        while(pilha->size > 0 && isOpBigger( stack_top(pilha), c )) {
            saida[saidaIndex][0] = stack_pop(pilha);
            saida[saidaIndex++][1] = 1;
        }
        stack_push(c, pilha);
    }
    else if (c == '(') stack_push(c, pilha);
    else if (c == ')') {
        j = stack_pop(pilha);
        while(j != '(') {
            saida[saidaIndex][0] = j;
            saida[saidaIndex++][1] = 1;
            j = stack_pop(pilha);
        }
    }
}

The problem is, in this code I can't tell if a minus sign indicates a subtraction operator or a negative number (I know that a minus operator is a sum with a negative number, but it didn't helped me solving this problem), I thought of the following, none with success:

  • Every time there is a minus sign, store a plus sign instead and let the next number be itself * -1. Wouldn't work in case the next number is already a negative one or if the minus sign is before an expression with parenthesis like 1 + 2 - (3 * 4).
  • If there is a space after the minus sign, it is an operator, if not, it is a negative number. Wouldn't work because there are not those rules on the input.

I have no experience whatsoever in interpreters and I really don't know how to proceed. The code works perfectly with valid expressions without negative numbers, but not with weird ones like () + 3 - (), but that's another problem.

Thanks for any help.

Mikael
  • 969
  • 12
  • 24
  • Instead of `for (i = 0; i < exp_size; i++)` (which forces an increment by only `1` each iteration) try `char *p = expression; while (*p) { test/increment as needed }` which allows you to read forward and consume characters in *expression* as required. e.g. `if (*p == '-') while (isblank (*p)) p++;` (or other tests to that effect, e.g. for `(`, `0-9`, etc.) that let you read forward in your *expression* to determine what you are dealing with? – David C. Rankin Aug 28 '16 at 23:53
  • I've never thought about iterating through a string this way, going to try it, thank you! – Mikael Aug 29 '16 at 00:26
  • When parsing any string this adds a great deal of flexibility. You know you will have a *nul-byte* at the end of every string, so simply walking down the string with `while (*p)` and reading/checking as needed allows you to do all sorts of things, like `*(p + x)` to read ahead (or behind). You can even use multiple pointers for bracketing *sub-expressions* within the string, e.g `char *sp, *ep;` for *start* and *end* pointers, which allows you to set `if (*p == '(') sp = p;` and continuing until `if (*p = ')') ep = p;` and then handling what is between `sp` and `ep`. – David C. Rankin Aug 29 '16 at 01:49
  • This really opens up new horizons for me, thank you again, will have that in mind! – Mikael Aug 29 '16 at 15:46

1 Answers1

3

That is the problem called "unary minus" and can in your case (no variables) get solved by substitution.

The operator - is an unary minus if it is

  • preceded by a left parenthesis
  • preceded by another operator
  • the first character of the input

Now instead of storing a - you store a different character like, say m and assign it a higher precedence than the other operators (or the same as the exponentiation operator if you have one).

Another tip: don't use spaces to indicate anything, an arithmetic expression must work without any spaces or it is not correct.

deamentiaemundi
  • 5,502
  • 2
  • 12
  • 20
  • Thanks, I'm going to try that. Should I implement it with a plus sign too? – Mikael Aug 29 '16 at 00:02
  • No, normaly not needed. – deamentiaemundi Aug 29 '16 at 00:07
  • @MikaelMello, as you have the unary minus operator, you have also the unary plus operator. It's unnecessary, but it can come in the input as `+3` (at the begining of an expression) or duplicated, as in `++3`. Check that you can have inputs like `+++-+++-+-+-++3+-+-+-+5` and they are valid. Unary operators should be given more precedence than the binary arithmetic ones, to allow expressions like `5 * - 3` or `-5 * +3` (to be interpreted as `5 * (-3)` and `(-5) * (+3)`) or at least more than the adding operators `+` and `-` (in that case, de second `-5 * +3` would be incorrect) – Luis Colorado Aug 30 '16 at 07:27
  • You are right, I had added an unary plus either way, along with support for multiple unary operators, like your second and third examples. I use a "boolean" variable to indicate if a unary operator is active and it works perfectly, but I chose to indicate `++3` as an invalid expression because it didn't seem right. So if those examples are valid expressions, I am going to go back on the way I first implemented, being it that `-+3` and `+-3` results in `-3`. Thanks for shedding some light into the situation. – Mikael Aug 30 '16 at 13:23
  • @MikaelMello nice, and now implement the factorial: `10!` ;-) – deamentiaemundi Aug 30 '16 at 13:26
  • @deamentiaemundi will do! ps: Just realized I should've been tagging those who I'm replying to. – Mikael Aug 30 '16 at 14:06