For this exercise, we will start from the code in
exercise 4-6, as using
getline
would render additions made in following exercises
irrelevant.
Seeing as switching to getline
is a major change, let us
first identify what can be removed from our program. We obviously no
longer need getch
and ungetch
, but this also
means we can remove buf and the variables associated with
it.
If we look at our program currently, we notice that getch
and ungetch
are almost exclusively used in
getop
, so in theory, if we can adapt getop
to
use getline
and make sure we execute getline
in main
some way, we should not need to make any more
changes. Let us tackle the latter first. We start by introducing the
global variable line to store the current input line. We
also create the global index lp to keep track of which
position on line we are currently on. Then, in main
, we
nest the while
-loop already present inside another
while
-loop that executes getline
until
EOF
is reached. This means we also need to change the
condition of the inner while
-loop to execute
getop
until a newline is reached and move the newline case
in the switch
statement to after the loop so that it gets
executed after the inner while
-loop terminates. Finally,
before getline
is called again, we reset lp to
zero.
#include <stdio.h>
#include <stdlib.h> /* for atof() */
#include <ctype.h>
#include <math.h>
#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
#define MAXLINE 1000 /* maximum input line size */
int getop(char []);
void push(double);
double pop(void);
void print_val(void);
void duplicate(void);
void swap(void);
void clear(void);
void setvar(int, double);
double getval(int);
int getch(void);
void ungetch(int);
int my_getline(char s[], int lim);
int lookfor(char []);
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 */
char line[MAXLINE]; /* current input line */
int lp = 0; /* current position in line */
/* reverse Polish calculator */
main()
{
int type, temp;
double op1, op2;
char s[MAXOP];
while (my_getline(line, MAXLINE) > 0) {
while ((type = getop(s)) != EOF) {
while ((type = getop(s)) != '\n') {
switch (type) {
...
case '\n':
if (sp == 1) {
printf("\t%.8g\n", op1 = pop());
ans = op1;
}
default:
printf("error: unknown command %s\n", s);
break;
}
}
if (sp == 1) {
printf("\t%.8g\n", op1 = pop());
ans = op1;
}
lp = 0;
}
return 0;
}
...
With the groundwork laid down, altering getop
to use
getline
becomes simple. We replace every call of
getch
with ++lp
(and seeing as we usually
assign the return value to another variable, we can assign
line[++lp]
instead), and every call of
ungetch
with --lp
. Also, there is no longer a
need for c, as its value can be represented as
line[lp]
. Seeing as we increment lp at the
start of getop
, we now initialize and reset lp
to -1 every time we read in a new line so that the first character of a
line does not get skipped. Finally, as we also use getch
and ungetch
in lookfor
, we replace its
function calls using the same idea since line and
lp are global.
...
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 line[MAXLINE]; /* current input line */
int lp = 0; /* current position in line */
int lp = -1; /* current position in line */
/* reverse Polish calculator */
main()
{
int type, temp;
double op1, op2;
char s[MAXOP];
while (my_getline(line, MAXLINE) > 0) {
...
if (sp == 1) {
printf("\t%.8g\n", op1 = pop());
ans = op1;
}
lp = 0;
lp = -1;
}
return 0;
}
...
/* getop: get next operator or numeric operand */
int getop(char s[])
{
int i, c;
while ((s[0] = c = getch()) == ' ' || c == '\t')
while ((s[0] = line[++lp]) == ' ' || line[lp] == '\t')
;
s[1] = '\0';
if (!isdigit(c) && c != '.' && c != '+' && c != '-') {
if (islower(c)) {
switch (c) {
if (!isdigit(line[lp]) && line[lp] != '.' && line[lp] != '+' &&
line[lp] != '-') {
if (islower(line[lp])) {
switch (line[lp]) {
case 's':
return (lookfor("in")) ? SIN : c;
return (lookfor("in")) ? SIN : line[lp];
case 'e':
return (lookfor("xp")) ? EXP : c;
return (lookfor("xp")) ? EXP : line[lp];
case 'p':
return (lookfor("ow")) ? POW : c;
return (lookfor("ow")) ? POW : line[lp];
default:
return c; /* not a number */
return line[lp]; /* not a number */
}
} else if (c == '&') {
s[1] = getch();
} else if (line[lp] == '&') {
s[1] = line[++lp];
s[2] = '\0';
return '&';
} else if (isupper(c))
} else if (isupper(line[lp]))
return VAR;
return c;
return line[lp];
}
i = 0;
if (isdigit(c) || c == '+' || c == '-') /* collect integer part */
while (isdigit(s[++i] = c = getch()))
;
if (c == '.') /* collect fraction part */
while (isdigit(s[++i] = c = getch()))
;
/* collect integer part */
if (isdigit(line[lp]) || line[lp] == '+' || line[lp] == '-')
while (isdigit(s[++i] = line[++lp]))
;
if (line[lp] == '.') /* collect fraction part */
while (isdigit(s[++i] = line[++lp]))
;
s[i] = '\0';
if (c != EOF)
ungetch(c);
if (line[lp] != EOF)
--lp;
if ((s[0] == '+' || s[0] == '-') && i == 1)
return s[0]; /* return operator if no digits afterwards */
return NUMBER;
}
...
/* lookfor: return 1 if specified string is found */
int lookfor(char s[])
{
int i, j;
int temp[MAXOP];
for (i = 0; s[i] != '\0' && (temp[i] = getch()) == s[i]; ++i)
for (i = 0; s[i] != '\0' && line[++lp] == s[i]; ++i)
;
if (s[i] == '\0') /* string was found */
return 1;
else { /* unget read characters and return 0 */
for (j = 0; j <= i; ++j)
ungetch(temp[j]);
lp -= i + 1;
return 0;
}
}