Jump to: navigation, search

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

Add commands for handling variables. (It's easy to provide twenty-six variables with single-letter names.) Add a variable for the most recently printed value.



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
/* 4-6 these are new for this exercise*/
#define IDENTIFIER   1
#define ENDSTRING    2
/* 4-6 end of new stuff */
#define TRUE         1
#define FALSE        0
#define MAX_ID_LEN  32
#define MAXVARS     30

/* 
The new additions deal with adding variables to the calculator.

  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.
  
  This is a class 1 solution as it uses structures which are not 
  introduced until Chapter 6. This allows the use of "normal" names for
  variables rather than the suggested single letter though any 
  identifier is limited to 31 characters.
    
  The main changes are:
      
    1. The introduction of two more define values (IDENTIFIER, 
       ENDSTRING) along with associated cases in the switch statement. 
    2. Getop has also been changed to deal with reading in alphabetical 
       characters and coping with the '=' sign.
    3. A structure to hold the variable name and value.
    4. Another case in the switch statement to deal with the '=' sign.
    5. Altering the clearStack function to clear the array of structs as
       well as the stack.
    6. The '<' operator now prints the last accessed variable.
        
  Improvements:
  The code could be made class 0 by the use of "parallel" arrays for the
  names and values rather than a struct but this would be messy and is 
  the situation that structs were made for.
  The use of a binary tree together with dynamically allocated memory
  would allow the arbitrary limit of 30 variables to be avoided. This 
  would still be a class 1 solution. 
         
  This is exercise 4-6 from Kernighan & Ritchie, page 79.               
*/

/* 4-6 this is new for this program */
struct varType{
   char name[MAX_ID_LEN];
   double val;
};
/* 4-6 End of new stuff */

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

/* 4-6 this is new for this program */
/* Changed clearStack(void) to clearStacks(struct varType var[])*/
void clearStacks(struct varType var[]);
void dealWithName(char s[],  struct varType var[]);
void dealWithVar(char s[], struct varType var[]);

int pos = 0;
struct varType last;

/* 4-6 End of new stuff */

int main(void)
{
   int type;
   double op2;
   char s[MAXOP];
   struct varType var[MAXVARS];
   
   /* Use the new function here */
   clearStacks(var);
   
   while((type = Getop(s)) != EOF)
   {
      switch(type)
      {
      case NUMBER:
         push(atof(s));
         break;
      case IDENTIFIER:
         dealWithName(s, var);
         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 '!':
         clearStacks(var);
         break;
      case '\n':
         printf("\n\t%.8g\n", pop());
         break;
         /* 4-6 this is new for this program */
      case ENDSTRING:
         break;
      case '=':
         pop();
         var[pos].val = pop();
         last.val = var[pos].val;
         push(last.val);
         break;
      case '<':
         printf("The last variable used was: %s (value == %g)\n",
                                            last.name, last.val);
         break;
         /* 4-6 End of new stuff */
      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);
}

/* 4-6 this is new for this program */
/* Altered to clear both the main stack and that of the variable
structure */
void clearStacks(struct varType var[])
{
   int i;
   
   /* Clear the main stack by setting the pointer to the bottom. */
   sp = 0;
   
   /* Clear the variables by setting the initial element of each name
   to the terminating character. */
   for( i = 0; i < MAXVARS; ++i)
   {
      var[i].name[0] = '\0';
      var[i].val = 0.0;
   }
}

/* a string/name may be either a maths function or a variable */
void dealWithName(char s[], struct varType var[])
{
   double op2;
   
   if(!strcmp(s, "sin"))
      push(sin(pop()));
   else if(!strcmp(s, "cos"))
      push(cos(pop()));
   else if (!strcmp(s, "exp"))
      push(exp(pop()));
   else if(!strcmp(s, "pow"))
   {
      op2 = pop();
      push(pow(pop(), op2));
   }
   /* Finally if it isn't one of the supported maths functions we have a 
      variable to deal with. */
   else
   {
      dealWithVar(s, var);
   }
}

/* Our identifier is not one of the supported maths function so we have 
   to regard it as an identifier. */
void dealWithVar(char s[], struct varType var[])
{
   int i = 0;
   
   while(var[i].name[0] != '\0' && i < MAXVARS-1)
   {
      if(!strcmp(s, var[i].name))
      {
         strcpy(last.name, s);
                   last.val = var[i].val;
         push(var[i].val);
         pos = i;
         return;
      }
      i++;
   }
   
   /* variable name not found so add it */
   strcpy(var[i].name, s);
   /* And save it to the last variable */
   strcpy(last.name, s);
   push(var[i].val);
   pos = i;
}
/* 4-6 End of new stuff */

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

/* Getop: get next operator or numeric operand. */
int Getop(char s[])
{
   int i = 0;
   int c;
   int next;
   
   /* 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 != '-')
   {
      /* 4-6 Deal with assigning a variable. */
      if('=' == c && '\n' == (next = getch()))
      {
         unGetch('\0');
         return c;
      }
      if('\0' == c)
         return ENDSTRING;

      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

int 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 Jesus Alvarez

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

#define MAXOP    100        /* Max size of operand or operator. */
#define NUMBER   '0'        /* Signal that a number was found. */
#define VARIABLE '1'
#define VARMAX   27

int is_first_input = 0;    /* Prevents the solution from being printed on first
                              input */
double var_array[VARMAX];  /* Contains user defined variables. */

int    getop(char []);
void   push(double);
double pop(void);
double top(void);
int    clear(void);
int    swap(void);
int    elem(void);
int    dup(void);
void   sprnt(void);
void   result(void);
void   set_solution(void);
void   print_help(void);

int main()
{
        int type;
        int i, j;
        int op3;
        double topd;
        double op2;
        char s[MAXOP];
        char tmp[MAXOP];

        for (i = 0; i < VARMAX; i++) {
                var_array[i] = 0;
        }

        print_help();

        while ((type = getop(s)) != EOF) {

                op3 = elem();
                if (op3 == 0) { /* Only one input completed. */
                        is_first_input = 1;
                } else if (op3 > 1) {
                        is_first_input = 0;
                }

                i = j = 0;

                switch (type) {
                case NUMBER:
                        push(atof(s));
                        break;
                case VARIABLE:
                        for (i = 2; s[i] != '\0'; i++){
                                tmp[j++] = s[i];
                                tmp[j] = '\0';
                        }
                        var_array[s[0] - 'A'] = atof(tmp);
                        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: Divide by zero.\n");
                        }
                        break;
                case '%':
                        op3 = (int) pop();
                        push((int) pop() % op3);
                        break;
                case 'c':
                        if (clear()) {
                                printf("Stack Cleared.\n");
                        }
                        break;
                case 'p':
                        if ((topd = top()) != 0) {
                                printf("Top stack element: %g", topd);
                                printf(" of %d elements.\n", elem());
                        }
                        break;
                case 's':
                        if (swap()) {
                                printf("Swap successful.\n");
                        }
                        break;
                case 'd':
                        if (dup()) {
                                printf("Duplication is successful.\n");
                        } else {
                                printf("Error: Stack empty.\n");
                        }
                        break;
                case 'r':
                        sprnt();
                        break;
                case 'o':
                        if (elem() < 2) {
                                printf("Error: pow requires at least two ");
                                printf("items on the stack.\n");
                                break;
                        }
                        op2 = pop();
                        push(pow(op2, pop()));
                        break;
                case 'i':
                        set_solution();
                        push(sin(pop()));
                        result();
                        break;
                case 'y':
                        set_solution();
                        push(cos(pop()));
                        break;
                case 't':
                        set_solution();
                        push(tan(pop()));
                        break;
                case 'x':
                        set_solution();
                        push(exp(pop()));
                        break;
                case 'q':
                        set_solution();
                        push(sqrt(pop()));
                        break;
                case 'f':
                        set_solution();
                        push(floor(pop()));
                        break;
                case 'l':
                        set_solution();
                        push(ceil(pop()));
                        break;
                case 'v':
                        for (i = 0; i < VARMAX; i++) {
                        if (i < VARMAX-1) {
                                printf("%c: %10.10G\n", 'A' + i, var_array[i]);
                        } else {
                                printf("%c: %10.10G\n", '=', var_array[VARMAX]);
                        }
                        }
                        break;
                case 'h':
                        print_help();
                        break;
                case '\n':
                        result();
                        break;
                default:
                        if ((type >= 'A' && type <= 'Z') || type == '=') {
                                if (type != '=') {
                                        push(var_array[type - 'A']);
                                } else {
                                        push(var_array[VARMAX]);
                                }
                        } else {
                                printf("Error: Unknown command \'%s\'\n", s);
                        }
                        break;

                }
        }
        return 0;
}

#define MAXVAL  100     /* Maximum depth of val stack. */
int sp = 0;             /* Next free stack position. */
double val[MAXVAL];     /* Value stack. */

void push(double f)
{
        if (sp < MAXVAL) {
                val[sp++] = f;
        } else {
                printf("Error: Stack full, cannot push %g\n", f);
        }
}

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

double top(void)
{
        if (sp > 0) {
                return val[sp-1];
        } else {
                printf("Error: Stack empty.\n");
                return 0.0;
        }
}

int clear(void)
{
        if (sp > 0) {
                while(val[--sp] != '\0');
                sp = 0;
                return 1;
        } else {
                printf("Error: Stack empty.\n");
                return 0;
        }
}

int swap(void)
{
        double sbuf;
        if (sp > 0) {
                sbuf = val[sp-2];
                val[sp-2] = val[sp-1];
                val[sp-1] = sbuf;
                return 1;
        } else {
                printf("Error: Stack empty.\n");
                return 0;
        }
}

int elem(void)
{
        return sp;
}

int dup (void)
{
        if (sp > 0) {
                sp++;
                val[sp] = val[sp-1];
                return 1;
        } else {
                return 0;
        }
}

void sprnt(void)
{
        int count = 0;
        while (count < sp) {
                printf("%d:%10.12g\n", count+1, val[count]);
                count++;
        }
}

void result(void)
{
        if (sp == 1 && is_first_input != 1) {
                printf("Solution: %10.20g\n", val[0]);
                var_array[VARMAX] = val[0];
                is_first_input = 0;
                clear();
        }
}

/*
 * Opens result() for execution.
 * Primarily used with the math functions because they can be used with only
 * one stack item. For ex, if "1 i" is entered as the first input, this
 * function would allow for a result to be shown and the stack cleared.
 */
void set_solution(void)
{
        if (elem() >= 1) {
                is_first_input = 0;
        }
}

int getch(void);
void ungetch(int);

int getop(char s[])
{
        int i = 0, c;
        while ((s[0] = c = getch()) == ' ' || c == '\t');

        s[1] = '\0';

        if (isalpha(c) && c >= 'A' && c <= 'Z') {
                /* Collect the variable. */
                for ( ; s[i] != ' ' && s[i] != '\n'; s[++i] = getch());
                s[i] = '\0';

                if (i > 1) { /* A properly formed variable definition. */
                        return VARIABLE;
                } else {
                        return c;
                }
        } else if (!isdigit(c) && c != '.' && c != '-') {
                return c; /* Not a number. */
        }

        if (c == '-') {
                if ((c = getch()) == ' ') {
                        /* If the next char is space, then c is a operator. */
                        return c;
                } else if (isdigit(c)) {
                        s[++i] = c;
                }

        }

        if (isdigit(c)) { /* Collect integer part. */
                while (isdigit(s[++i] = 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];      /* Buffer for ungetch. */
int bufp = 0;           /* Next free position in buf. */

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;
        }
}

void print_help(void)
{
        printf("The Polish Calculator\n");
        printf("-----------------------------------------------\n");
        printf("-> Enter equations in the form: \"1 1 + 2 5 + *\"\n");
        printf("-> Use \"A=1 B=2 C=3\" to store variables.\n");
        printf("-> Use \"A B C * *\" to use stored variables.\n");
        printf("-----------------------------------------------\n");
        printf(">>> Command Help:\n");
        printf(">>>     c:      Clear memory.\n");
        printf(">>>     p:      Print last character.\n");
        printf(">>>     s:      Swap last two characters.\n");
        printf(">>>     d:      Duplicate the last input.\n");
        printf(">>>     r:      Print the entire stack.\n");
        printf(">>>     v:      Print variable list.\n");
        printf(">>>     o:      pow(x,y), x^y, x > 0.\n");
        printf(">>>     i:      sin(x), sine of x.\n");
        printf(">>>     y:      cos(x), cosine of x.\n");
        printf(">>>     t:      tan(x), tangent of x.\n");
        printf(">>>     x:      exp(x), e^x, exponential function.\n");
        printf(">>>     q:      sqrt(x), x >= 0, square of x.\n");
        printf(">>>     f:      floor(x), largest integer not greater than x.\n");
        printf(">>>     l:      ceil(x), smallest integer not less than x.\n");
        printf(">>>     =:      Access the last successful solution.\n");
        printf(">>>     h:      Print this help text.\n");
}

Solution by menonsahab

/* I'm beginning to feel that the questions in K&R are getting more and more vague. If you see the above answers
you'll see that what Jesus understood from the question is totally different from what Bob did. I am personally of the
opinion that what Jesus has implemented isn't really what the question meant. Bob's answer is great. I've tried to 
implement a category 0 solution. It does what Bob's code does except:
1) It only handles single letter variable names a,b,c,...,x,y,z
2) It does not handle '='
*/

#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
#define VARIABLE 2

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 *);

char last;
double variableValue[26] = {0};

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, len;
	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;
	len = strlen(s);
	if(len == 1 && isalpha(s[0]))
	{
		last = s[0];
		return VARIABLE;
	}		
	for(i = 0; i < len; 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 VARIABLE:
				push(variableValue[last - 97]);
				break;
			case '_': // This prints the most recent variable
				push(0); // So that when \n is encountered next, the stack isn't empty
				printf("%c\n", last);
				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

This could be cleaned up a bit more by modulating the code with more functions, but the logic is all there. I see menonsahab had the same idea as me for how to more elegantly handle 26 single letter variables while still maintaining a category 0 solution. Unlike their solution though, this one does handle '=' characters.

#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
#define VAR '_'


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");
	}
}

char variable = ' '; //initially as space such that malformed use of '=' won't cause segfaults


int asignment = 0; //hacky way to not push a variable to stack before it's been defined

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

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

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

	if (isalpha(c))	{
		while (isalpha(s[++i] = c = getch()));
		s[i] = '\0';

		if (i == 1)	{	//s is a variable
			variable = s[0];
			if ((c=getch()) == '=')
				asignment = 1;
			ungetch(c);
			return VAR;
		}

		if (strcmp(s, "sin")) //we could had done all the math.h functions but, effort
			return 1;
		if (strcmp(s, "cos"))
			return 2;
		if (strcmp(s, "tan"))
			return 3;
		if (strcmp(s, "exp"))
			return 4;

		printf("error: unsupported function or malformed input\n");
		return '\n';
	}

	if (c == '-') 	{
		s[i] = c;
		if ((c = getch()) == ' ')	{
			return '-';
		}
		else
			s[++i] = c;
	}
	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;
}

double variables[26];

void define(char var, double n)	{
	var = tolower(var);
	if (var >= 97 && var <= 122)
		variables[tolower(var)-97] = n;
	else 
		printf("error: variable symbol out of range\n");
}

double variableLookUp(char var)	{
	if (var >= 97 && var <= 122)
		return variables[tolower(var)-97];
	else
		printf("error: variable symbol out of range\n");
	return 0.0;
}

int main()	{
	int type;
	double op2, lastPrinted;
	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 1: //such that variables can be used, using non-printable characters for math functions
				push(sin(pop()));
				break;
			case 2:
				push(cos(pop()));
				break;
			case 3:
				push(tan(pop()));
				break;
			case 4:
				push(exp(pop()));
				break;
			case '=':
				define(variable, pop());
				break;
			case VAR:
				if (asignment == 0)	
					push(variableLookUp(variable));
				else
					asignment = 0;
				break;
				
			case '\n':
				lastPrinted = pop();
				printf("\t%.8g\n", lastPrinted);
				break;
		}
	}
	return 0;
}

Solution by anonymous

I added 26 variables which are lowercase letters. You set them by inputting <letter>=<value>. I reserved uppercase L for the variable that contains the most recently printed value. I interrupted the most recently printed as the final result printed, not when something was printed from another command. The varVals array contains the value of a variable. A basic way of using the variables is as follows:

i=7
i 2 *
        14
L 10 +
        24
L i +
        31
#include <stdio.h>
#include <stdlib.h> // for atof()
#include <ctype.h>
#include <math.h>
#include <string.h> // for strlen()

/*
    Exercise 4-6. Add commands for handling variables. (It's easy to provide twenty-six variables with single-letter names.) Add a variable for the most recently printed value.
*/

#define MAXOP 100       // max size of operand or operator
#define NUMBER '0'      // signal that a number was found
#define SETVARIABLE 's' // signal that a variable is being assigned
#define GETVARIABLE 'g' // signal that a variable is being retrieved
#define ERRORSIGNAL ' ' // signal that an error occurred in getop
#define MAXVAL 100      // maximum depth of val stack
#define BUFSIZE 100     // buffer size for getch and ungetch
#define NUMVARS 27      // number of variables supported (a-z) plus the variable L for last printed

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
double varVals[NUMVARS] = {0.0}; // array to store the double values for variables. Supports variables a through z (lower case). Initial value is 0

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 SETVARIABLE:
            if (strlen(s) > 2 && s[1] == '=')
            {
                int v = s[0];           // stores variable
                int i = 1;              // start at 1 since while loop needed ++i for a few reasons
                while (s[++i] != '\0')  // this removes the variable name= part of s e.g. if s == "a=123.45" after loop s == "123.45"
                    s[i - 2] = s[i];    // shifts chars two to the left by 2
                s[i - 2] = '\0';        // since '\0' isn't copied, terminate string manually
                varVals[v - 'a'] = atof(s); // convert string to double and store it in array
            }
            else
                printf("error: set variable length too small or incorrectly formatted (%s)\n", s);

            skipNextNewline = TRUE;
            break;
        case GETVARIABLE:
            push(varVals[s[0] - 'a']);  // convert the variable name to stored value
            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 'L':
            push(varVals[NUMVARS - 1]); // adds the last printed value to the stop of the stack
            break;
        case '\n':
            if (skipNextNewline)
                skipNextNewline = FALSE;
            else
            {
                varVals[NUMVARS - 1] = pop(); // updates last printed value
                printf("\t%.8g\n", varVals[NUMVARS - 1]); // 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;
    char setVar = FALSE;
    i = 0;

    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 (c >= 'a' && c <= 'z')
    {
        if ((s[++i] = c = getch()) == '=') // get next char and check if it was an equal symbol. Update s in case of error
            setVar = TRUE;
        else if (c == ' ' || c == '\t' || c == '\n')
        {
            ungetch(c); // return the whitespace since it will be processed later
            return GETVARIABLE;
        }

        if (!(setVar && ((s[++i] = c = getch()) == '-' || isdigit(c))))
            return ERRORSIGNAL; // triggers an error and will display what is in s
    }
    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;
    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 '-';
    if (setVar)
        return SETVARIABLE;
    else if (c >= 'a' && c <= 'z')
        return ERRORSIGNAL; // if last char is a variable, throw error

    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