Exercise 5-10 (Calculator with command-line arguments)
Chapter_5 Exercise_5-9 find | Exercise_5-11-1 |
Exercise 5-10 K&R, p. 118
Exercise 5-10. Write the program expr,
which evaluates a reverse Polish expression from the command line, where each operator
or operand is a separate argument. For example,
expr 2 3 4 + *
evaluates to 2 * (3 + 4).
expr.c download
#include <stdio.h> // for printf()
#include <stdlib.h> // for atof()
#define MAXOP 100 // max size of operand or operator
#define NUMBER '0' // signal that a number has been found
int getop(char *); // get the next operand or operator
void push(double); // push operand on stack
double pop(void); // pop operand from stack
// evaluate a reverse Polish expression from the command line
int main(int argc, char *argv[])
{
if (argc < 2)
{
printf("Usage: ./expr [operand]+ [operand|operator]*\n");
return 1; // exit main() with an error code
}
int type, i1, i2;
double op2; // second operand for -, /, %
while (--argc > 0) // read all command-line arguments except program name
{
type = getop(*++argv); // initially, ++argv goes beyond program name
switch(type)
{
case NUMBER :
push(atof(*argv));
break;
case '+' :
push(pop() + pop());
break;
case '-' :
op2 = pop();
push(pop() - op2);
break;
case '*' :
push(pop() * pop());
break;
case '/' :
op2 = pop();
if (op2 != 0.0)
{push(pop() / op2);}
else // continuing operations would give wrong results
{
printf("Error: zero divisor\n");
return 1; // exit main() with an error code
}
break;
case '%' :
i2 = pop(); // automatic conversions
i1 = pop(); // from double to int
if (i2 != 0.0)
{push(i1 % i2);} // automatic conversion from int to double, argument of push()
else // continuing operations would give wrong results
{
printf("Error: zero divisor\n");
return 1; // exit main() with an error code
}
break;
default : // continuing operations would give wrong results
printf("Unknown command: %s\n", *argv);
return 1; // exit main() with an error code
}
}
printf("\t%.8g\n", pop()); // top of stack
return 0;
}
#define MAXVAL 100 // stack size
double val[MAXVAL]; // value stack
double *sp = val; // stack pointer (*val is top of stack)
void push(double f) // push f on (top of) stack
{
if (sp < val+MAXVAL) {*sp++ = f;}
else {printf("Error: stack full, can't push %g\n", f);}
}
double pop(void) // pop and return top value from stack
{
if (sp > val) {return *--sp;}
else
{
printf("Error: stack empty\n");
return 0.0;
}
}
#include <ctype.h> // for isdigit()
int getop(char *s) // get next operator or numeric operand
{
int c;
while ((c = *s++) == ' ' || c == '\t') // skip beginning whitespace (' ', '\t')
{} // last value read in c is not ' ' or '\t'
if (!isdigit(c) && c != '.' && c != '+' && c != '-')
{ // not a number, probably an operator
while (*s != '\0')
{
if (*s != ' ' && *s != '\t') // "*1" is not a legal argument
{
return 'a'; // illegal command
}
s++;
}
return c; // " * " is a legal argument, operator '*' (multiplication)
}
// here c is digit or '.' or '+' or '-'
if (c == '+' || c == '-') // unary or binary sign
{
if (!isdigit(*s) && *s != '.')
{
while (*s != '\0')
{
if (*s != ' ' && *s != '\t') // "+*" is not a legal argument
{
return 'a'; // illegal command
}
s++;
}
return c; // binary '+' or '-'
}
else // *s is digit or '.'
{
return NUMBER; // let atof() deal with other error cases
}
}
// here c is digit or '.'
return NUMBER; // let atof() deal with other error cases
}
/*
gcc expr.c -o expr
./expr
Usage: ./expr [operand]+ [operand|operator]*
./expr +
Error: stack empty
Error: stack empty
0 // value returned by pop() for an empty stack
./expr 1
1 // pop()
./expr 1 2
2 // pop() returns top of stack
./expr 1 2 +
3
./expr " 1 " " 2 " " + "
3
./expr 1 0 /
Error: zero divisor
./expr 1 0 %
Error: zero divisor
./expr a
Unknown command: a
./expr +*
Unknown command: +*
./expr +1 -2 +
-1
./expr -1.2 +.3 "*"
-0.36
./expr 4 3 /
1.3333333
./expr 4 3 %
1
./expr 4 3 % 2 -
-1
./expr +1*2
1 // atof(+1*2) = 1
./expr +1 2 "*"
2
./expr +1 2 '*'
2
*/
Notes:
See Chapter_4,
Sec. 4.3 for the code.
./expr 1 2 *
interprets `*'
as `all files' in the current folder, so we write
./expr 1 2 "*" or
./expr 1 2 '*' instead.
Chapter_5 Exercise_5-9 find | BACK_TO_TOP | Exercise_5-11-1 |
Comments
Post a Comment