Before we implement anything, let us first think of how we can add
support for variables. We can use uppercase letters for variable names
(as we already use some lowercase letters for commands), and perhaps we
could use '='
as the assignment operator. Then, whenever a
variable name is entered, we can have its numerical value
push
ed to the stack. However, we have run into a problem:
if a variable name is converted to its numerical value as soon as it is
entered, how do we assign values to it in the first place? For simple
definitions like A 1 = (A = 1 in infix notation), there may
be workarounds, but our code would become really messy when we start to
account for situations like A B C + = (A = B + C in infix
notation.) Therefore, the easiest way to deal with this is to introduce
the dereferencing operator '&'
: A now refers to
the name of a variable, and &A represents the numerical
value of 'A'
.
We start by defining the floating-point array var, which
holds the values of all the variables. We will set it up in a way so
that var[1]
holds the value of the variable
'A'
, var[2]
holds the value of
'B'
, and so on. We need to create two functions to handle
with variables: setvar
, which takes in a variable name and
assigns a value to it, and getval
, which returns the value
of a variable. For setvar
, all we need to do is assign the
value argument to var[c - 'A']
, where c is the
name of the variable. For getval
, we simply return
var[c - 'A']
. For both functions, we make sure that
c
is a valid variable name (i.e. an uppercase letter.)
...
/* setvar: set the value of a variable */
void setvar(int c, double f)
{
if (isupper(c))
var[c - 'A'] = f;
else
printf("error: invalid variable name\n");
}
/* getval: return value of a variable */
double getval(int c)
{
if (isupper(c))
return var[c - 'A'];
else {
printf("error: invalid variable name\n");
return 0;
}
}
...
Now we need to add commands to utilize these functions. First off,
whenever getop
reads in an uppercase letter, we return a
VAR
token. To add support for the '&'
operator, whenever it is entered, we also read in the next character
(which should be the name of a variable) into s[1]
, so
that it can be used later.
...
#define MAXOP 100 /* max size of operand or operator */
#define NUMBER '0' /* signal that a number was found */
#define VAR 'A' /* signal that a variable was found */
#define SIN 'I' /* signal that a sin function was found */
#define EXP 'E' /* signal that a exp function was found */
#define POW 'O' /* signal that a pow function was found */
#define MAXVAL 100 /* maximum depth of val stack */
#define BUFSIZE 100
...
/* getop: get next operator or numeric operand */
int getop(char s[])
{
int i, c;
while ((s[0] = c = getch()) == ' ' || c == '\t')
;
s[1] = '\0';
if (!isdigit(c) && c != '.' && c != '+' && c != '-') {
if (islower(c)) {
switch (c) {
case 's':
return (lookfor("in")) ? SIN : c;
case 'e':
return (lookfor("xp")) ? EXP : c;
case 'p':
return (lookfor("ow")) ? POW : c;
default:
return c; /* not a number */
}
} else if (c == '&') {
s[1] = getch();
s[2] = '\0';
} else if (isupper(c))
return VAR;
return c;
}
...
Inside main
, we can now add cases for our new tokens.
Firstly, when the loop encounters a VAR
token, we assign
s[0]
(which holds the name of the variable) to
temp. temp will be used when the '='
operator is entered, where the top value of stack is assigned to
variable temp. Additionally, we can also give the
declaration statement itself the value by push
ing it so
that statements like A 1 = 1 +
((A = 1) + 1 in infix
notation) are also valid. Finally, when we come across
'&'
, we push
the value of the variable
s[1]
.
...
/* reverse Polish calculator */
main()
{
int type, temp;
double op1, op2;
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 '-':
op2 = pop();
push(pop() - op2);
break;
case '/':
op2 = pop();
if (op2 != 0.0)
push(pop() / op2);
else
printf("error: zero divisor\n");
break;
case '%':
op2 = pop();
if (op2 != 0.0) {
for (op1 = pop(); op1 >= op2; op1 -= op2)
;
push(op1);
} else
printf("error: zero divisor\n");
break;
case SIN:
push(sin(pop()));
break;
case EXP:
push(exp(pop()));
break;
case POW:
op2 = pop();
push(pow(pop(), op2));
break;
case VAR:
temp = s[0];
break;
case '&':
push(getval(s[1]));
break;
case '=':
setvar(temp, pop());
push(getval(temp));
break;
...
Finally, we need to create a variable for the most recently printed
value. Seeing as this variable is special, we can store it inside its
own variable ans and have it be push
ed when
'a'
is entered. Whenever a value is printed (which can
occur when print_val
is called or a newline is entered),
we need to assign said value to ans.
...
int sp = 0; /* next free stack position */
int ans = 0; /* most recently printed value */
double val[MAXVAL]; /* value stack */
double var[26]; /* variable values */
char buf[BUFSIZE]; /* buffer for ungetch */
int bufp = 0; /* next free position in buf */
/* reverse Polish calculator */
main()
{
int type, temp;
double op1, op2;
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 '-':
op2 = pop();
push(pop() - op2);
break;
case '/':
op2 = pop();
if (op2 != 0.0)
push(pop() / op2);
else
printf("error: zero divisor\n");
break;
case '%':
op2 = pop();
if (op2 != 0.0) {
for (op1 = pop(); op1 >= op2; op1 -= op2)
;
push(op1);
} else
printf("error: zero divisor\n");
break;
case SIN:
push(sin(pop()));
break;
case EXP:
push(exp(pop()));
break;
case POW:
op2 = pop();
push(pow(pop(), op2));
break;
case VAR:
temp = s[0];
break;
case '&':
push(getval(s[1]));
break;
case '=':
setvar(temp, pop());
push(getval(temp));
break;
case 'a':
push(ans);
break;
case 'p':
print_val();
break;
case 'd':
duplicate();
break;
case 's':
swap();
break;
case 'c':
clear();
break;
case '\n':
if (sp == 1) {
printf("\t%.8g\n", op1 = pop());
ans = op1;
}
break;
default:
printf("error: unknown command %s\n", s);
break;
}
}
return 0;
}
...
/* print_val: print top element of value stack */
void print_val(void)
{
if (sp > 0) {
printf("\t%.8g\n", val[sp - 1]);
ans = val[sp - 1];
} else
printf("error: stack empty\n");
}