Jump to: navigation, search

The C Programming Language, 2nd Edition, by Kernighan and Ritchie
Exercise 4.05 on page 79

Add access to library functions like sin , exp , and pow . See <math.h> in Appendix B, Section 4.



Solution by Bob Wightman

#include<stdlib.h>
#include<stdio.h>
#include<ctype.h>
#include<math.h>
#include <string.h>

#define MAXOP 100
#define NUMBER       0
#define IDENTIFIER   1
#define TRUE 1
#define FALSE 0

/* 

  The new additions deal with adding functions from math.h to the
  calculator.
  
  In anticipation of the following exercise the code deals with an 
  identifier in the following manner:
    
  If the identifier is recognised as one of the supported mathematical 
  functions then that function from the library is called. If the 
  identifier is not one of the supported functions, even if it is a
  valid function from math.h it is ignored.
      
  The main changes are the introduction of another define value   
  (IDENTIFIER) along with its associated case in the switch statement.  
  Getop has also been changed to deal with reading in alphabetical 
  characters.
       
  This is exercise 4-5 from Kernighan & Ritchie, page 79.
          
*/

int Getop(char s[]);
void push(double val);
double pop(void);
void showTop(void);
void duplicate(void);
void swapItems(void);
void clearStack();
void dealWithName(char s[]);

int main(void)
{
   int type;
   double op2;
   char s[MAXOP];
   int flag = TRUE;
   
   while((type = Getop(s)) != EOF)
   {
      switch(type)
      {
      case NUMBER:
         push(atof(s));
         break;
      case IDENTIFIER:
         dealWithName(s);
         break;
      case '+':
         push(pop() + pop());
         break;
      case '*':
         push(pop() * pop());
         break;
      case '-':
         op2 = pop();
         push(pop()- op2);
         break;
      case '/':
         op2 = pop();
         if(op2)
            push(pop() / op2);
         else
            printf("\nError: division by zero!");
         break;
      case '%':
         op2 = pop();
         if(op2)
            push(fmod(pop(), op2));
         else
            printf("\nError: division by zero!");
         break;
      case '?':
         showTop();
         break;
      case '#':
         duplicate();
         break;
      case '~':
         swapItems();
         break;
      case '!':
         clearStack();
      case '\n':
         printf("\n\t%.8g\n", pop());
         break;
      default:
         printf("\nError: unknown command %s.\n", s);
         break;
      }
   }
   return EXIT_SUCCESS;
}

#define MAXVAL 100

int sp = 0;          /* Next free stack position. */
double val[MAXVAL];  /* value stack. */

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

/*pop: pop and return top value from stack.*/
double pop(void)
{
   if(sp > 0)
      return val[--sp];
   else
   {
      printf("\nError: stack empty\n");
      return 0.0;
   }
}

void showTop(void)
{
   if(sp > 0)
      printf("Top of stack contains: %8g\n", val[sp-1]);
   else
      printf("The stack is empty!\n");
}

/*
Alternatively:
void showTop(void)
{
double item = pop();
printf("Top of stack contains: %8g\n", item);
push(item);
}  
*/


void duplicate(void)
{
   double temp = pop();
   
   push(temp);
   push(temp);
}

void swapItems(void)
{
   double item1 = pop();
   double item2 = pop();
   
   push(item1);
   push(item2);
}

void clearStack(void)
{
   sp = 0;
}

/* deal with a string/name this may be either a maths function or for
future exercises: a variable */
void dealWithName(char s[])
{
   double op2;
   
   if( 0 == strcmp(s, "sin"))
      push(sin(pop()));
   else if( 0 == strcmp(s, "cos"))
      push(cos(pop()));
   else if (0 == strcmp(s, "exp"))
      push(exp(pop()));
   else if(!strcmp(s, "pow"))
   {
      op2 = pop();
      push(pow(pop(), op2));
   }
   else
      printf("%s is not a supported function.\n", s);
}

int getch(void);
void unGetch(int);

/* Getop: get next operator or numeric operand. */
int Getop(char s[])
{
   int i = 0;
   int c;
   int next;
   /*size_t len;*/
   
   /* Skip whitespace */
   while((s[0] = c = getch()) == ' ' || c == '\t')
      ;
   s[1] = '\0';
   
   if(isalpha(c))
   {
      i = 0;
      while(isalpha(s[i++] = c ))
         c = getch();     
      s[i - 1] = '\0';
      if(c != EOF)
         unGetch(c);
      return IDENTIFIER;
   }
   
   /* Not a number but may contain a unary minus. */
   if(!isdigit(c) && c != '.' && c != '-')
      return c;                 
   
   if(c == '-')
   {
      next = getch();
      if(!isdigit(next) && next != '.')
      {
         return c;
      }
      c = next;
   }
   else
      c = getch();
   
   while(isdigit(s[++i] = c))
      c = getch();
   if(c == '.')                 /* Collect fraction part. */
      while(isdigit(s[++i] = c = getch()))
         ;
      s[i] = '\0';
      if(c != EOF)
         unGetch(c);
      return NUMBER;
}

#define BUFSIZE 100

char buf[BUFSIZE];
int bufp = 0;

/* Getch: get a ( possibly pushed back) character. */
int getch(void)
{
   return (bufp > 0) ? buf[--bufp]: getchar();
}

/* unGetch: push character back on input. */
void unGetch(int c)
{
   if(bufp >= BUFSIZE)
      printf("\nUnGetch: too many characters\n");
   else
      buf[bufp++] = c;
}

Solution by menonsahab

/* The previous solution while using the mathematical functions, does not check
whether there are sufficient number of elements in the stack to perform that
particular operation. I've made the necessary changes. */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>

#define MAXOP 100
#define NUMBER 0
#define MAXVAL 100
#define BUFSIZE 100
#define IDENTIFIER 1

int getop(char *);
void push(double);
double pop(void);
int getch(void);
void ungetch(int);
void viewstack(void);
void showTop(void);
void swap(void);
void duplicate(void);
void clearStack(void);
void mathfunc(char *);

int bufp = 0;
char buf[BUFSIZE];
int sp = 0;
double val[MAXVAL];


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

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

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

void ungetch(int c)
{
	if(bufp >= BUFSIZE)
		printf("ungetch: too many characters\n");
	else
		buf[bufp++] = c;
}

int getop(char *s)
{
	int i, c, d, flag;
	while((s[0] = c = getch()) == ' ' || c == '\t');
	s[1] = '\0';
	if(!isalnum(c) && c != '.' && c != '-')
		return c;
	if(c == '-')
	{
		d = getch();
		if(d == ' ')
			return c;
		else
			ungetch(d);
	}
	i = 0;
	if(isalnum(c) || c == '-')
		while(isalnum(s[++i] = c = getch()));
	if(c == '.')
		while(isalnum(s[++i] = c = getch()));
	s[i] = '\0';
	if(c != EOF)
		ungetch(c);
	flag = 1;
	for(i = 0; s[i] != '\0'; i++)
		if(!isalpha(s[i]))
		{
			flag = 0;
			break;
		}
	if(flag == 1)
		return IDENTIFIER;
	else
		return NUMBER;
}

void viewstack(void)
{
	int i;
	printf("\nstack:\n");
	for(i = sp - 1; i >= 0; i--)
		printf("%lf\n", val[i]);
}

void showTop(void)
{
	sp > 0 ? printf("\t%.8g\n", val[sp-1]) : printf("stack is empty\n");
}

void swap(void)
{
	double temp;
	if(sp < 1)
		printf("error: stack has less than 2 elements, can't swap\n");
	else
	{
		temp = val[sp - 1];
		val[sp - 1] = val[sp - 2];
		val[sp - 2] = temp; 
	}
}

void duplicate(void)
{
	if(sp > MAXVAL - 1)
		printf("error: stack is full, can't duplicate\n");
	else
	{
		double temp = pop();
		push(temp);
		push(temp);
		++sp;
	}
}

void clearStack(void)
{
	sp = 0;
}

void mathfunc(char *s)
{
	if(strcmp(s, "sin") == 0)
	{
		if(sp < 1)
			printf("error: stack is empty, can't use sin function\n");
		else
			push(sin(pop()));
	}
	else if(strcmp(s, "cos") == 0)
	{
		if(sp < 1)
			printf("error: stack is empty, can't use cos function\n");
		else
			push(cos(pop()));		
	}
	else if(strcmp(s, "exp") == 0)
	{
		if(sp < 1)
			printf("error: stack is empty, can't use exp function\n");
		else
			push(exp(pop()));
	}
	else if(strcmp(s, "pow") == 0)
	{
		if(sp < 2)
			printf("error: stack has less than 2 elements, can't use pow function\n");
		else
		{
			double op2;
			op2 = pop();
			push(pow(pop(), op2));
		}
	}
	else
		printf("%s is not a supported function\n", s);
}

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

	while((type = getop(s)) != EOF)
	{
		//viewstack(); // Use this function if you wish to see the stack after every iteration
		switch(type)
		{
			case NUMBER:
				push(atof(s));
				break;
			case IDENTIFIER:
				mathfunc(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)
					push(fmod(pop(), op2));
				else
					printf("error: division by zero\n");
				break;
			case '?': // show top item on stack without popping
				showTop();
				break;
			case '~': // swap top two elements of the stack
				swap();
				break;
			case '#': // duplicate the top element
				duplicate();
				break;
			case '!': // clearStack
				clearStack();
				break;
			case '\n':
				printf("\t%.8g\n", pop());
				break;
			default:
				printf("error: unknown command %s\n", s);
				break;
		}
		//viewstack();
	}
	return 0;
}

Solution by Luke Panayi

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#include <string.h>
#define MAXOP 100
#define NUMBER '0'
#define MAXVAL 100
#define BUFSIZE 100


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

int sp = 0;
double val[MAXVAL];

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

}

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

void peek(void)	{
	if (sp > 0)	{
		printf("%g\n", val[sp]);
	}
	else 	{
		printf("error: stack empty\n");
	}
}

void swap(void)	{
	if (sp > 1)	{ 
		double tmp;
		tmp = val[sp];
		val[sp] = val[sp-1];
		val[sp-1] = tmp;
	}
	else 	{
		printf("error: not enough elements in stack\n");
	}
}

void clear(void)	{
	sp = 0; //should be enough to simply set the pointer to 0, old values will be overwritten.
}

void duplicate(void)	{
	if (sp < MAXVAL -1)	{
		push(val[sp]);
	}
	else 	{
		printf("error: stack full\n");
	}
}

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

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

	if (!isdigit(c) && !isalpha(c) && c != '.' && c != '-')
		return c;
	i = 0;

	if (c == '-') 	{
		s[i] = c;
		if (!isdigit(c = getch()))	{
			return '-';
		}
		else
			s[++i] = c;
	}
	if (isalpha(c))	{
		j = 0;
		char t[4];

		while (isalpha(t[++j] = c = getch()));
		t[j] = '\0';

		if (strcmp(t, "sin")) //we could had done all the math.h functions but effort
			return 's';
		if (strcmp(t, "cos"))
			return 'c';
		if (strcmp(t, "tan"))
			return 't';
		if (strcmp(t, "e"))
			return 'e';
		printf("error: unsupported function or malformed input\n");
		return '\n';
	}
	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;
}

char buf[BUFSIZE];
int bufp = 0;

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

void ungetch(int c)	{
	if (bufp >= BUFSIZE)
		printf("ungetch: too many characters\n");
	else
		buf[bufp++] = c;
}

int main()	{
	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 '/':
				if (op2 != 0.0)	{
					op2 = pop();
					push(pop() / op2);
				}
				else
					printf("error: zero divisor\n");
				break;
			case '%':
				op2 = pop();
				if (op2 != 0.0)	{
					push(fmod(pop(), op2));
				}
				else
					printf("error: zero divisor\n");
				break;
			case '^':
				op2 = pop();
				push(pow(pop(), op2));
				break;
			case 's':
				push(sin(pop()));
				break;
			case 'c':
				push(cos(pop()));
				break;
			case 't':
				push(tan(pop()));
				break;
			case 'e':
				push(exp(pop()));
				break;
			case '\n':
				printf("\t%.8g\n", pop());
				break;
		}
	}
	return 0;
}

Solution by anonymous

I opted to go the simple route of adding commands to access library functions by mapping them to special characters. They are all added to the select statement in main. Also, since the code is rather simple, I didn't write separate functions for them.

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

/*
    Exercise 4-5. Add access to library functions like sin, exp, and pow. See <math.h> in Appendix B, Section 4.
*/

#define MAXOP 100   // max size of operand or operator
#define NUMBER '0'  // signal that a number was found
#define MAXVAL 100  // maximum depth of val stack
#define BUFSIZE 100 

enum boolean {FALSE, TRUE};

int sp = 0;         // next free stack position
double val[MAXVAL]; // value stack
char buf[BUFSIZE];  // buffer for ungetch
int bufp = 0;       // next free position in buf

int getop(char s[]);
void push(double f);
double pop(void);
int getch(void);
void ungetch(int c);
void printTop(void);
void duplicateTop(void);
void swapTopTwo(void);

// reverse Polish calculator
// note: convert ((((-1 - 2) * (4 + -5)) / -3) % 5) * (-1 - -10) to -1 2 - 4 -5 + * -3 / 5 % -1 -10 - * for reverse Polish notation. -1 2 - 4 -5 + * -3 / 5 % -1 -10 - * == -9
int main()
{
    int type;
    double op2;
    char s[MAXOP];
    char skipNextNewline = FALSE;

    while ((type = getop(s)) != EOF)
    {
        switch (type)
        {
        case NUMBER:
            push(atof(s)); // convert the string to type double and push it on the stack
            break;
        case '+':
            push(pop() + pop()); // pop last two digits to sum them and push the result on the stack
            break;
        case '*':
            push(pop() * pop()); // pop last two digits to multiply them and push the result on the stack
            break;
        case '-':
            /*
                Because + and * are commutative operators, the order in which the popped operands are combined is irrelevant, but for - and / the left and right operands
                must be distinguished. In push(pop() - pop());, the order in which the two calls of pop are evaluated is not defined. To guarantee the right order, it is
                necessary to pop the first value into a temporary variable. Hence op2 = pop() in - and / but not in + and *
            */
            op2 = pop();
            push(pop() - op2); // pop last two digits to subtract them in the correct order and push the result on the stack
            break;
        case '/':
            op2 = pop();
            if (op2 != 0.0)
                push(pop() / op2); // pop last two digits to divide them in the correct order and push the result on the stack
            else
                printf("error: zero divisor\n");
            break;
        case '%':
            op2 = pop();
            if (op2 != 0.0)
                push(fmod(pop(), op2)); // pop last two digits in the correct order to find the modulus and push the result on the stack
            else
                printf("error: zero divisor\n");
            break;
        case '?':
            printTop();
            skipNextNewline = TRUE;
            break;
        case '#':
            duplicateTop();
            skipNextNewline = TRUE;
            break;
        case '~':
            swapTopTwo();
            skipNextNewline = TRUE;
            break;
        case '!':
            // sets next free stack position to zero (meaning the value stack is empty).
            // all of the original values are still there, but they will no longer be accessible by the current functions and they will be overwritten when new elements are stored
            sp = 0;
            skipNextNewline = TRUE;
            break;
        case '$':
            push(sin(pop()));
            skipNextNewline = TRUE;
            break;
        case '@':
            push(exp(pop()));
            skipNextNewline = TRUE;
            break;
        case '^':
            op2 = pop();
            push(pow(pop(), op2)); // pop last two digits in the correct order to raise a number to the given power
            skipNextNewline = TRUE;
            break;
        case '&':
            push(sqrt(pop()));
            skipNextNewline = TRUE;
            break;
        case ',':
            push(fabs(pop()));
            skipNextNewline = TRUE;
            break;
        case '\n':
            if (skipNextNewline)
                skipNextNewline = FALSE;
            else
                printf("\t%.8g\n", pop()); // get the final result
            break;
        default:
            printf("error: unknown command %s\n", s);
            break;
        }
    }
    return 0;
}

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

// pop: pop and return top value from stack
double pop(void)
{
    if (sp > 0) // if the next free stack position is greater than zero, return the highest level item from stack
        return val[--sp];
    else
    {
        printf("error: stack empty\n");
        return 0.0;
    }
}

// getop: get next operator or numeric operand
int getop(char s[])
{
    int i, c;

    while ((s[0] = c = getch()) == ' ' || c == '\t') // skip white space
        ;
    s[1] = '\0'; // terminate string in case input is not a number (s is expected to be a string throughout program)
    if (!isdigit(c) && c != '.' && c != '-')
        return c; // not a number. Probably an operator, so return it. Minus operator is a special case and is handled right before return NUMBER;
    i = 0;
    if (c == '-' || isdigit(c)) // collect integer(s), if any, after first digit found or after minus symbol found
        while (isdigit(s[++i] = c = getch()))
            ;
    if (c == '.') // collect fraction part if period is found
        while (isdigit(s[++i] = c = getch()))
            ;
    s[i] = '\0'; // terminate string after digits were captured
    if (c != EOF) 
        ungetch(c); // since we read to far, push the last read char back on the getch buffer. This buffer is read first before getting the next char from input
    if (i == 1 && s[0] == '-') // if s[0] == '-' && s[1] == '\0', return minus operator
        return '-';
    return NUMBER;
}

// get a (possibly pushed back) character
// checks to see if there are any chars in buffer. If there are, get those and return it. If not, call getchar() from stdio.h to get next char from input
int getch(void)
{
    return (bufp > 0) ? buf[--bufp] : getchar();
}

// push character back on input
// if bufp is less than BUFSIZE, there is room to store more chars to be read by getch next and it stores c and updates the index for it
void ungetch(int c)
{
    if (bufp >= BUFSIZE)
        printf("ungetch: too many characters\n");
    else
        buf[bufp++] = c;
}

// prints the top element in the value stack
void printTop(void)
{
    if (sp > 0)
        printf("\t%.8g\n", val[sp - 1]);
    else
        printf("error: stack empty\n");
}

void duplicateTop(void)
{
    if (sp < MAXVAL) // only need to see if there is space for one more
        push(val[sp - 1]); // duplicates top item
    else
        printf("error: stack full, can't duplicate top element\n");
}

void swapTopTwo(void)
{   // if sp == 2, there are at least two elements stored
    if (sp > 1)
    {                               // <third> <second> <first>
        double first = pop();       // <third> <second>
        double second = pop();      // <third>
        push(first);                // <third> <first>
        push(second);               // <third> <first> <second>
    }
    else
        printf("error: can't swap top two, not enough elements\n");
}
Personal tools