Modify Calculator to Support Bit Operations
Multi-module programs and MAKE
- Compile and link multiple modules together into a single executable file named “calcit”.
Copy the makefile from my hw4 directory(/courses/cs240/f17/kamaral/GROUP/hw4) to your hw4 subdirectory. Then, create source code files for a MODIFIED version of the calcit program. You will use modules named: main.c, getop.c, stack.c, calc.h, and getch.c.
Your MODIFIED version of the calcit program will operate on integers instead of floating point variables and support BIT OPERATIONS: ^, % and ~. In order to do this, change the variable types and returns of functions such as “pop” so that type “int” is used in calculations. Then, add the operations ^, % and ~ to support these binary operations. For example, if the operation is ^, then it is programmed as:
push(pop()^ pop());
Note that because we no longer allow double type returns, we can deal only with integers and must use the atoi() function instead of atof(). Look for atoi() in the index, also try “man atoi”–this is a C library function, and you are not required to code it. But you need to be careful when you are converting a negative number since the bit representation will be quite different from the positive number.
Use the infix string ~( ((292 %16) +(292/16)*16) ^ 292) as the script test case. Your program need not understand infix arithmetic though — you must convert the test expression given above to reverse polish before feeding it to your program (just create a file named “calc.in” with the proper sequence and use it as stdin).
- Experiment with make and touch.
Prepare another file “makefile2” that is a copy of makefile except remove the command lists for the .o rules. Leave the command list in the rule for calcit. In this case, make will try to build the .o files according to some built-in implicit rules which includes $(CC) and $(CFLAGS).
Use “script typescript2” to create a file that shows you doing the following:
- “make clean –f makefile2” to clear out old *.o files
- “make –f makefile2” to do a full rebuild of calcit.
- modifygetop.c to add in a comment line with your name.
- “make –f makefile2” to see just getop.c recompiled, plus the loadable calcit.
- “touch calc.h” to simulate an edit to calc.h.
- “make –f makefile2” to see two compiles based on dependencies on calc.h, plus the loadable calcit.
- Use the diff -w command to show how the two makefiles are different.
- Use exit to save the typescript2 file.
Solution
calc.h
#define NUMBER ‘0’
void push(int);
int pop(void);
intgetop(char[]);
intgetch(void);
voidungetch(int);
getch.c
/*
* getch.c
*
* getch returns one character from stdin
* or from the buffer buf if it is not empty
* ungetch puts one character in the buffer buf
*/
#include <stdio.h>
#define BUFSIZE 100
charbuf[BUFSIZE]; /* buffer for ungetch */
intbufp = 0; /* next free position in buf */
intgetch(void) /* get a (possibly pushed back) character */
{
return(bufp> 0) ? buf[–bufp] : getchar();
}
voidungetch(int c) /* push character back on input */
{
if (bufp>= BUFSIZE)
printf(“ungetch: too many character \n”);
else
buf[bufp++] = c;
}
getop.c
/*
* getop.c
*
* gets next token: operator or numeric operand
*/
#include <stdio.h>
#include <ctype.h>
#include “calc.h”
intgetop(char s[])
{
int i, c;
while ((s[0] = c = getch()) == ‘ ‘ || c == ‘\t’)
;
s[1] = ‘\0’;
if (!isdigit(c))
return c; /* not a number */
/* collect integer part in string s */
i = 0;
while (isdigit(s[++i] = c = getch()))
;
s[i] = ‘\0’;
if (c != EOF)
ungetch(c);
return NUMBER;
}
main.c
/*
* main.c
*
* reverse polish calculator
*/
#include <stdio.h>
#include “calc.h” /* header file */
#include <math.h>
#include <stdlib.h>
#define MAXOP 100
main()
{
int type;
int op2;
char s[MAXOP];
while ((type = getop(s)) != EOF)
{
switch(type)
{
case NUMBER:
push(atoi(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)
push(pop() / op2);
else
{
printf(“error : zero divisor\n”);
exit(1);
}
break;
case ‘^’:
push(pop() ^ pop());
break;
case ‘%’:
op2 = pop();
if (op2 != 0)
push(pop() % op2);
else
{
printf(“error : modulus by zero\n”);
exit(1);
}
break;
case ‘~’:
push(~pop());
break;
case ‘\n’:
printf(“The answer is %d.\n”, pop());
break;
default:
printf(“error: unknown command %s\n”, s);
exit(1);
}
}
}
stack.c
/*
* stack.c
*
* stack routines
*/
#include <stdio.h>
#include “calc.h”
#define MAXVAL 100 /* maximum depth of val stack */
/* even better, we could make the following two defines “static”
and thus hide these data structures from the rest of the sources */
staticintsp = 0; /* next free stack position */
staticintval[MAXVAL]; /* value stack */
/* push: push f onto value stack */
void push(int f)
{
if (sp< MAXVAL)
val[sp++] = f;
else
printf(“error: stack full, can’t push %d\n”, f);
}
/* pop: pop and return top value from stack */
int pop(void)
{
if (sp> 0)
returnval[–sp];
else
{
printf(“error: stack empty\n”);
return 0;
}
}
makefile
# makefile for hw4
# format of entries;
# < target list >: < dependency list >
# < command list >
# make will look at the last-write dates of each file, and if the target
# file hasn’t been updated since the source files were last modified,
# it will update the target file by executing the shell command. Note that
# the spacing to the < dependency list > and < command list > MUST be done
# with tabs, not spaces.
#
# now here is a Macro as defined in Glass Unix.
cc = gcc
CFLAGS = -m32
calcit: main.ogetop.ostack.ogetch.o
$(cc) $(CFLAGS) -g -o calcitmain.ogetop.ostack.ogetch.o
# note that if calc.h changes, main.c and getop.c must be recompiled
main.o: main.ccalc.h
$(cc) $(CFLAGS) -c -o main.omain.c
getop.o: getop.ccalc.h
$(cc) $(CFLAGS) -c -o getop.ogetop.c
stack.o: stack.c
$(cc) $(CFLAGS) -c -o stack.ostack.c
getch.o: getch.c
$(cc) $(CFLAGS) -c -o getch.ogetch.c
# this tells make what to make if you just type ‘make’; if this isn’t there,
# the first rule in the file will be the default.
# default: calcit
# here is a rule to clean up the directory when calcit is final and you want
# to reduce the clutter in the listing. Note that clean doesn’t need to
# depend on any file modification time, so the < dependency list > position is
# just left empty.
clean:
rm *.o
makefile2
# makefile for hw4
# format of entries;
# < target list >: < dependency list >
# < command list >
# make will look at the last-write dates of each file, and if the target
# file hasn’t been updated since the source files were last modified,
# it will update the target file by executing the shell command. Note that
# the spacing to the < dependency list > and < command list > MUST be done
# with tabs, not spaces.
#
# now here is a Macro as defined in Glass Unix.
cc = gcc
CFLAGS = -m32
calcit: main.ogetop.ostack.ogetch.o
$(cc) $(CFLAGS) -g -o calcitmain.ogetop.ostack.ogetch.o
# this tells make what to make if you just type ‘make’; if this isn’t there,
# the first rule in the file will be the default.
# default: calcit
# here is a rule to clean up the directory when calcit is final and you want
# to reduce the clutter in the listing. Note that clean doesn’t need to
# depend on any file modification time, so the < dependency list > position is
# just left empty.
clean:
rm *.o