Exercise 4.10 - Calculator using getline

Question

An alternate organization uses getline to read an entire input line; this makes getch and ungetch unnecessary. Revise the calculator to use this approach.

/* Revise the Calculator program to use the getline instead of getch and ungetch */

#include<stdio.h>
#include<stdlib.h> /* for atof() */

#define MAXOP 100
#define NUMBER '0'

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

/* reverse polish notation calculator */

int main(void)
{
	int type;
	double 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);
					break;
			case '\n':
					printf("\t%.9g\n",pop());
					break;
			default:
					printf("error: unknown command %s\n",s);
					break;
		}
	}
	return 0;
}

#define MAXVAL 100 /* maximum depth of the val stack */

int sp = 0;
double val[MAXVAL];

/* push : push f onto value stack */

void push(double f)
{
	if(sp < MAXVAL)
		val[sp++] = f;
	else
		printf("error: stack full,can't push %g\n",f);
}

/* pop: pop and return top values from stack */

double pop(void)
{
	if(sp > 0)
		return val[--sp];
	else
	{
		printf("error: stack empty \n");
		return 0.0;
	}
}


/* using getline instead of getch and ungetch */

#include<ctype.h>
#define MAXLINE 100

int mgetline(char line[],int limit);

int li= 0;  /* input line index */
char line[MAXLINE];  /* one input line */

/* getop: get next operator or numeric operand */

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

	if(line[li] == '\0')
		if(mgetline(line,MAXLINE) == 0)
			return EOF;
		else
			li =0;
	
	while((s[0] = c = line[li++]) == ' ' || c == '\t')
		;
	
	s[1] = '\0';
	
	if(!isdigit(c) && c!= '.')
		return c;
	
	i = 0;
	
	if(isdigit(c))
		while(isdigit(s[++i] = c = line[li++]))
			;
	if( c == '.')
		while(isdigit(s[++i] = c = line[li++]))
			;
	
	s[i] = '\0';

	li--;

	return NUMBER;
}

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

	for(i=0;i<lim-1 && (c=getchar())!=EOF && c!='\n';++i)
		s[i] =c;
	
	if(c=='\n')
		s[i++] =c;

	s[i]='\0';
	
	return i;
}


		

Explanation

This program uses mgetline to get the characters and operands from the input and and proceeds with the RPN calculator logic.

This is the main part of the program.

/* getop: get next operator or numeric operand */

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

        if(line[li] == '\0')
                if(mgetline(line,MAXLINE) == 0)
                        return EOF;
                else
                        li =0;

        while((s[0] = c = line[li++]) == ' ' || c == '\t')
                ;

        s[1] = '\0';

        if(!isdigit(c) && c!= '.')
                return c;

        i = 0;

        if(isdigit(c))
                while(isdigit(s[++i] = c = line[li++]))
                        ;
        if( c == '.')
                while(isdigit(s[++i] = c = line[li++]))
                        ;

        s[i] = '\0';

        li--;

        return NUMBER;
}

From the mgetline function, it takes the input in the line character array, and if if the line is 0 only, then we define that as EOF and return EOF. Then we assign to c the value present at line and look for various conditions like, if line is a space or tab character, we simply skip it. If we encouter c which is not a digit or not a . character, we return c immediately. At the end if it is valid number, we return a NUMBER, which is then pushed onto the stack of the RPN calculator.

An example execution will look like this.

10 10 +
        20
10.1 20.2 +
        30.3

Visualize It

Try It