Reverse Polish Notation shell interpreter with one variable in C I wrote an RPN shell stack-based interpreter. It supports following operators: +, -, *, /, %, ^. Right now it only works with positive numbers. It features also printing out the top stack element by entering new line character and popping out the result with = operator. I decided to also add one variable available to the user, it is the x variable, which can be used in two states. X (capital X) is used for reading the value of the variable and x (lowercase x) is used for writing the value to the variable.

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;ctype.h&gt;
#include &lt;math.h&gt;

#define CRESET   "\033[0m"
#define CYELLOW  "\033[33m"
#define CRED     "\033[31m"


#define MAXOP 100
#define NUMBER '0'
#define MAXSTACK 100
#define BUFSIZE 100


int getop(char []);
void push(double);
double top(void);
double pop(void);
int getch(void);
void ungetch(int);

int sp = 0;
double stack[MAXSTACK];
char buf[BUFSIZE];
int bufp = 0;
double x = 0;

main () {
    int type;
    double _op;
    char s[MAXOP];

    while ((type = getop(s)) != EOF) {
        switch (type) {
            case NUMBER:
                push(atof(s));
                break;
            case '+':
                push(pop() + pop());
                break;
            case '*':
                push(pop() * pop());
                break;
            case '-':
                _op = pop();
                push(pop() - _op);
                break;
            case '^':
                _op = pop();
                push(pow(pop(), _op));
                break;
            case '/':
                _op = pop();
                if (_op != 0.0)
                    push(pop() / _op);
                else
                    printf(CRED "ERROR: Cannot divide by 0\n" CRESET);
                break;
            case '%':
                _op = pop();
                if (_op != 0.0)
                    push(fmod(pop(), _op));
                else
                    printf(CRED "ERROR: Cannot divide by 0\n" CRESET);
                break;
            case 'x':
                x = pop();
                break;
            case 'X':
                push(x);
                break;
            case '\n':
                printf(CYELLOW "Top: %.8g\n" CRESET, top());
                break;
            case '=':
                printf(CYELLOW "Result: %.8g\n" CRESET, pop());
                break;
            default:
                printf(CRED "ERROR: Unknown command %s\n" CRESET, s);
                break;
        }


    }


}

void push(double f) {
    if (sp &lt; MAXSTACK)
        stack[sp++] = f;
    else
        printf(CRED "ERROR: Stack is full, cannot push %g\n" CRESET, f);
}

double top(void) {
    if (sp &gt; 0)
        return stack[sp - 1];
    else {
        return 0.0;
    }

}

double pop(void) {
    if (sp &gt; 0)
        return stack[--sp];
    else {
        printf(CRED "ERROR: Stack is empty\n" CRESET);
        return 0.0;
    }

}

int getop(char s[]) {
    int i, c;

    while ((s[0] = c = getch()) == ' ' || c == '\t')
        ;

    s[1] = '\0';

    if (!isdigit(c) &amp;&amp; c != '.')
        return c;
    i = 0;
    if (isdigit(c))
        while (isdigit(s[++i] = c = getch()))
            ;
    if (c == '.')
        while (isdigit(s[++i] = c = getch()))
            ;
    s[i] = '\0';
    if (c != EOF)
        ungetch(c);
    return NUMBER;

}


int getch(void) {
    return (bufp &gt; 0) ? buf[--bufp] : getchar();
}

void ungetch(int c) {
    if (bufp &gt;= BUFSIZE)
        printf(CRED "UNGETCH: Too many characters\n" CRESET);

  else
        buf[bufp++] = c;

}


Samples



Input:

&gt; 2 2 + =


Output:

Result: 4
Top: 0


Input:

&gt; 3 x
&gt; X 5 +


Output:

Top: 8


Input (x = 3):

&gt; X 3 ^ 2 +


Output:

Top: 29




Could you review my code? Are there any issues I missed regarding the user input?
Here are some ideas for improving your program.

Use the correct prototype for main

Many compilers will default to an int return type when the function return type is undeclared, but it's not a good idea to omit it, especially with main.  Instead of this:

main () {


write this:

int main() {


Eliminate global variables where practical

The code declares and uses 5 global variables.  Global variables obfuscate the actual dependencies within code and make maintainance and understanding of the code that much more difficult.  It also makes the code harder to reuse.  For all of these reasons, it's generally far preferable to eliminate global variables and to instead pass pointers to them.  That way the linkage is explicit and may be altered more easily if needed.  In this case, I'd suggest gathering everything together into a structure within main's scope and then passing that structure to each of the functions.  For example, it might be defined like this:

typedef struct stack_s {
    int sp;
    int bufp;
    double x;
    double stack[MAXSTACK];
    char buf[BUFSIZE];
} StackMachine;


And then within main it could be defined like this:

StackMachine sm = { .sp = 0, .bufp = 0 };


Then push could be reimplemented like this:

void push(StackMachine *sm, double f) {
    if (sm-&gt;sp &lt; MAXSTACK)
        sm-&gt;stack[sm-&gt;sp++] = f;
    else
        printf(CRED "ERROR: Stack is full, cannot push %g\n" CRESET, f);
}


Be consistent with the user interface

When the user uses an empty line to see the top of the stack, it reports 0 instead of STACK EMPTY when the stack is empty.  This is inconsistent with the other operations.