CS140 -- Lab 5
This lab is designed to give you practice with:
- using stacks and lists,
- implementing information hiding using void *'s, and
- writing and using software libraries.
The lab is divided into two parts. In the first part you are going to
write a stack library and implement it using linked lists. In the second
part you are going to use the stack library to help you
simulate the execution of a program using
a call stack. A call stack in an actual program represents a stack of
the currently executing functions. The function that is currently
executing is the top function on the stack, the function which called it
is the next to top function, and so on. In your simulation you will be
given a list of the functions called by each function and will simulate
each of the calls.
Stack Library
In this part of the lab you are going to create a stack library that
supports the following operations:
- void push(void *value, void *stack): push the given value onto stack
- void *pop(void *stack): pop the top element off the stack and return
its value. Remember to delete the node holding the top element.
If the stack is empty return 0.
- void *top(void *stack): return the value of the top element on the stack
but do not pop it.
- void *create_stack(): create a record for a stack and return it as a
void *.
- bool is_empty(void *stack): return true if the stack is empty and false
otherwise.
Problem Specifications
- Place the function declarations in a file called stack.h and the
function implementations in a file called stack.c.
- Place the
structs that you use to implement a stack in stack.c, so that their
declaration, and hence the stack's implementation, is hidden from the user.
- Use a linked list to implement the stack. You may use either a singly or
doubly linked list, and you can choose whether or not you want to use a
sentinel node. A sentinel node will not help you for this implementation.
- There is a program in /home/bvz/cs140/lab5 called stack_test.c
that you can use to test your stack library. It takes any number of strings
on the command line and prints them out in reverse order.
Call Stack Simulator
In this part of the program you are going to write an interpreter that
reads a very simple program and executes it. An interpreter is a program
that reads instructions and executes them.
Your interpreter will be given input lines of the form:
fct-name fct-call fct-call ... fct-call
For example:
B C print_stack D
will define a function named B that calls the functions C,
print_stack and D. There are only two types of instructions:
- user defined function call: This instruction should cause your
interpreter to push a record for that function onto the call stack and
start "executing" that function.
When the function finishes executing, its record will be popped
off the call stack and the function that called it will resume its
execution. For example, suppose your input has four functions
defined as follows:
main B
B C D
C D
D
Initially you will place a record for main on the call stack:
main
main calls B, thus placing a record of B on
the stack, and B begins execution:
B
main
B then calls C, which in turn calls D, resulting
in the following stack:
D
C
B
main
D
makes no function calls so it exits immediately, and pops itself off the
stack:
C
B
main
C is also finished with calling functions so it exits and pops
itself off the stack:
B
main
B now calls D and pushes it on the stack:
D
B
main
Hopefully you get the picture of how things work.
- print_stack: This instruction should cause your interpreter to print
the call stack. Your interpreter should first print a header of the
form:
**** stack %d ****
where %d will be replaced with a count of the number of times
you have printed the stack thus far.
Your interpreter will then print the call stack from top to bottom. For
each function on the call stack your interpreter
will print the function's name
in a 10 character wide, left justified field. Your interpreter
will then print
a space separated list of the functions it calls. Finally your
interpreter should place
an asterisk after the function that it is currently calling.
For example, given the input:
main A C D
A B
B C print_stack D
C D
D print_stack
your program should produce the output:
**** stack 0 ****
D print_stack*
C D*
B C* print_stack D
A B*
main A* C D
**** stack 1 ****
B C print_stack* D
A B*
main A* C D
**** stack 2 ****
D print_stack*
B C print_stack D*
A B*
main A* C D
**** stack 3 ****
D print_stack*
C D*
main A C* D
**** stack 4 ****
D print_stack*
main A C D*
Input Assumptions and Program Format
- Place your code in a file named call_stack.c.
- Your program will take one command line argument, which is the name
of the input file. You must use the fields library to read and
process the input file.
- The input should always contain a function named main,
which will be your starting function.
- You can find an executable at /home/bvz/cs140/labs/lab5/call_stack.
If you have questions about how your program should behave or how it
should format its output, check the executable.
- There are a number of test files in /home/bvz/cs140/labs/lab5,
all starting with the prefix "test". Do not assume that these files
represent an exhaustive list of tests.
Error Checking
Your program should perform the following error checks:
- Ensure there are an appropriate number of command line arguments
- Ensure that a function has been defined before calling it. If a
function is not defined, print an error message and quit. Note that
this check should cover the case of the user forgetting to define a
function called main.
Program Design
Your program will need three data structures:
- A linked list to store the functions and their calling lists. Each
calling list can be implemented as a dynamically allocated array
because the fields library tells you how many fields there are on a
line and therefore you can compute the number of functions on a
function's calling list.
- A call stack. When you call a function, you should create a
record for it (called a stack frame) and push this stack frame
record onto the call stack. The stack frame record should keep track
of where you are in the function's calling list. It also needs to
have access to the function's name, calling list, and size of the
calling list.
- A print stack. It may not be immediately obviously why you need a
print stack but I am not permitting your stack library to contain
a function for printing the stack contents. Therefore your print_stack
routine is going to have to pull all the elements off the call_stack
in order to print its contents. You can use a second stack, a print_stack,
to save the call_stack elements and restore them once you're done
printing the call stack.