CS140 -- Lab 5

This lab is designed to give you practice with:

  1. using stacks and lists,
  2. implementing information hiding using void *'s, and
  3. 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:

  1. void push(void *value, void *stack): push the given value onto stack
  2. 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.
  3. void *top(void *stack): return the value of the top element on the stack but do not pop it.
  4. void *create_stack(): create a record for a stack and return it as a void *.
  5. bool is_empty(void *stack): return true if the stack is empty and false otherwise.

Problem Specifications

  1. Place the function declarations in a file called stack.h and the function implementations in a file called stack.c.
  2. 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.
  3. 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.
  4. 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:

  1. 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.

  2. 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

  1. Place your code in a file named call_stack.c.
  2. 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.
  3. The input should always contain a function named main, which will be your starting function.
  4. 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.
  5. 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:

  1. Ensure there are an appropriate number of command line arguments
  2. 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:

  1. 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.
  2. 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.
  3. 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.