Sample Lab 8 Design Document

Your design document should also include pictures of data structures.

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.
  3. Make sure that the user defined main. This will need to be a special check just before main is pushed onto the call stack. This check handles the special case of an empty file.
  4. Make sure that when a function is defined, that the user has not previously defined the same function. For example, if the user is defining function B, make sure that B has not been previously defined.

Normal Test Cases

  1. main does not call any function (i.e., its definition is "main" or "main print_stack").
  2. main calls functions but the functions do not in turn call any functions (e.g., test1).
  3. functions call other functions (e.g., test2).
  4. functions call other functions multiple times (e.g., test3).
  5. functions gets called by multiple functions (e.g., A calls B and C also calls B).

Data Structures

Your program will need five data structures:

  1. A "function struct" to keep track of a function's information, including its 1) name, 2) the list of functions it calls (which I will henceforth refer to as its "call list"), 3) the size of the call list, and 4) and the line number in the input file on which this function was defined. The line number will be needed for your error messages. Each call 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 call list.
  2. A linked list to store the function structs. I will henceforth call this list the "function list".
  3. A "stack frame" struct. The stack frame struct should keep track of a currently executing function. It should maintain a pointer to the function's "function struct" and an index that keeps track of which function you are currently executing in its call list. The pointer to the "function struct" will allow you to access important information about the function, such as its name, call_list, and in the case of an error, the line number it was defined on in the input file.
  4. A call stack. When you call a function, you should create a stack frame struct for it and push this stack frame record onto the call stack.
  5. 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.

Program Steps

Here is one way you could incrementally develop your program. It is not the only way, so feel free to do it your own way. However, you should use some type of incremental development and like my solution, you should put off error checking to the very end. You should initially assume that your input is correct.

  1. Echo the input to the output: This will force you to allocate an inputstruct and write a loop using getline to read the lines and echo them. That will provide a basic framework for reading the input.

  2. Create the function struct that holds the information for each function (i.e., function name, array pointer for the functions that it calls, size of the array, etc.).

  3. For each input line create a function struct and add it to a dllist. Don't worry about potential duplicate function declarations at this point.

  4. Write a struct for a stack frame. The stack frame will have a pointer to the function struct of the function it is executing, and an integer index into the function's cal list. The function name pointed to by the index is the function currently being called by that function.

  5. Write a function to print the contents of a stack. Test it using a driver program that pushes integers onto the stack and calls your function to print the stack. Once you have debugged the function, you can copy the function to your program and change the print statement so that it prints stack trace information. The change will involve working with stack frame structs.

  6. Create a stack for the call stack, create a stack frame for main and push it onto the stack. To create a stack frame, you will need to write a function that traverses your function list, finds the function struct for that function, and makes the stack frame point to that function.

  7. Write a loop that "executes" your toy program. Each iteration will look at the top stack frame on the call stack. It will advance the call list index for that stack frame and find the function associated with that index. One of three things will happen:

    1. The call index will be past the end of the function's call_list (the array that holds the list of functions that this function calls). In this case the function is done executing so pop the frame off the call stack.
    2. The call index points to a "print_stack" call. Call your print function to print the stack.
    3. The call index points to the name of a function. Look up that function in your function list, create a stack frame struct for that function, and push the stack frame onto the call stack. This is the function that you are going to start "executing".
    When the call stack is finally empty (i.e., main has called all of its functions), your program can exit.

  8. Add error checking. Notice that my error messages reference the line on which a function was defined. When a function is first defined, you can use the "line" field from the fields library to record its line number for later use by error messages.