Lab 2: FSM Implementation

In this lab, you will build a table of transitions and function pointers to implement a finite state machine in C. FSM implementations frequently adhere to standard templates. Section 1.7 of the book illustrates a lot of the code that you will need for this lab.


Preliminaries

Set up a bare repository on stu.cs.jmu.edu based on the instructions in the CS 361 Submission Procedures, using the name lab2-cmd.git.


Implementing states and transitions

The FSM that you will implement is a model of states that a process goes through during its life cycle:

State model of a FSM for parsing the command line

Completing the following requirements will satisfy the minimum required functionality:

  • Edit the transition table defined in cmdmodel.c to match the model above. Create an FSM instance in cmdline_init() and initialize its initial state and transition function pointer appropriately. (Other fields should be 0 or NULL.)
  • Create a helper function that fsm->transition points to. This function will perform the transition lookup, returning the next state after the transition. Note that the function parameters must match the transition declaration in statemodel.h. Perform robust error handling for invalid events and FSM states. Specifically, use an assert() to throw an error if the FSM is in an invalid state (anything greater than or equal to NST) or the event is invalid (greater than or equal to NIL.

    When checking for potential errors, we can either use assert() or return early in an if statement. The former is preferred when the error should never occur and it isn't clear how to recover.

  • Complete the implementation of handle_event() in statemodel.c. Again, check for errors (such as ensuring that the passed fsm parameter is never NULL). Change state only if the table indicates that the next state is valid (something other than NST or a negative value).

At this point, you should be passing all public unit tests (but not the private ones). If not, you should correct your implementation up to this point.

  • Now move on to building the effects table, similar to the transition table. Note that the transition table is a 2-d array of state_t values, so we use NST to indicate there is no transition. The effects table is a 2-d array of action_t function pointers. If there is no valid transition, or there is a transition but it has no associated effect, the entry should be NULL. Update the transition helper function to perform the effect table lookup similar to the lookup for transitions. Note that the effect will be returned through the call-by-reference parameter.
  • Update handle_event() so that it performs the effect if one was found by the transition lookup.
  • At this point, you should now fail a public test case that is testing the execution of the code in effects.c (this code wasn't called before). Fix this by implementing the effects code functions:
    start_command()
    This will be called the token (described above) is "ls". This function will need to copy the token into the command field of the fsm_t instance and start to set up the args and nargs fields. At this point, nargs will be 1 (because we've only encountered one argument). For args, you'll need to dynamically allocate an array to hold all the argument strings and store the current token as args[0]. (Notice there is a MAX_ARGUMENTS constant you can use.)
    append()
    In the example above, this will be called when the token is "-l" and again when it is "data". Store the token and increment nargs.
    execute()
    Called on the NEWLINE event (the "NL" token). This prints out the arguments as a string. For the example above, the string would be "{ ls, -l, data, (null) }". You also need to free the args array.

Ensure that your code is passing all unit tests at this point. You'll now implement the features to finish the program, producing the correct output along the way.

  • As in Lab 1, you will need to correct the expected output in some of the test cases. Make sure to do this before proceeding to the implementation.
  • Update the main.c to loop through command-line arguments. Note that the program is simulating a command-line parser, so your main() function will receive the simulated string as a single argument (argv[1]). You will tokenize this this string, splitting at the spaces, and passing these tokens to the FSM. Specifically, you will use lookup() that will give you the appropriate event, store the token in the FSM, then call handle_event().


James Madison University logo


© 2011-2024 Michael S. Kirkpatrick.
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.