In creating this answer to a recent question, I thought I might write a fully worked example that implements a finite state machine in C.
The purpose is to implement the following function:
Given a
const char *
, search for a string conforming to the pattern WWWDD where WWW is one of { "Mon", "Tue", "Wed, "Thu", "Fri", "Sat", "Sun" } and DD is an integer in the range \$[1, 31]\$ (inclusive). If the pattern exists within the string, return the offset; otherwise return -1.
I implemented this state machine: using simple table structure. This is the code, which includes a simple driver with test vectors.
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
static const struct fsmNode {
const char target;
const struct fsmNode *next;
} fsm[] = {
{ 'T' , &fsm[6] }, // 0
{ 'M' , &fsm[9] }, // 1
{ 'S' , &fsm[11] }, // 2
{ 'W' , &fsm[12] }, // 3
{ 'F' , &fsm[14] }, // 4
{ '\0' , NULL }, // 5
// second letter
{ 'h' , &fsm[18] }, // 6
{ 'u' , &fsm[20] }, // 7
{ '\0' , NULL }, // 8
{ 'o' , &fsm[22] }, // 9
{ '\0' , NULL }, // 10
{ 'a' , &fsm[24] }, // 11
{ 'u' , &fsm[22] }, // 12
{ '\0' , NULL }, // 13
{ 'e' , &fsm[26] }, // 14
{ '\0' , NULL }, // 15
{ 'r' , &fsm[27] }, // 16
{ '\0' , NULL }, // 17
// third letter
{ 'u' , &fsm[30] }, // 18
{ '\0' , NULL }, // 19
{ 'e' , &fsm[30] }, // 20
{ '\0' , NULL }, // 21
{ 'n' , &fsm[30] }, // 22
{ '\0' , NULL }, // 23
{ 't' , &fsm[30] }, // 24
{ '\0' , NULL }, // 25
{ 'd' , &fsm[30] }, // 26
{ '\0' , NULL }, // 27
{ 'i' , &fsm[30] }, // 28
{ '\0' , NULL }, // 29
// first digit
{ '0' , &fsm[36] }, // 30
{ '1' , &fsm[35] }, // 31
{ '2' , &fsm[35] }, // 32
{ '3' , &fsm[46] }, // 33
{ '\0' , NULL }, // 34
// second digit
{ '0' , NULL }, // 35
{ '1' , NULL }, // 36
{ '2' , NULL }, // 37
{ '3' , NULL }, // 38
{ '4' , NULL }, // 39
{ '5' , NULL }, // 40
{ '6' , NULL }, // 41
{ '7' , NULL }, // 42
{ '8' , NULL }, // 43
{ '9' , NULL }, // 44
{ '\0' , NULL }, // 45
{ '0' , NULL }, // 46
{ '1' , NULL }, // 47
{ '\0' , NULL }, // 48
};
int findWeekdayDate(const char *str) {
const struct fsmNode *state = fsm;
const char *curr;
for (curr = str ; *curr; ++curr) {
for ( ; state->target && *curr != state->target; ++state)
{ /* do nothing */ }
if (state->next == NULL) {
if (state->target) {
// found a valid string
return curr-str-4;
} else {
state = fsm;
}
} else {
state = state->next;
}
}
return -1;
}
// Test code follows...
static const struct test_s {
const char *string;
int expected;
} test[] = {
{ "abcSun24Def", 3},
{ "abcMun24Def", -1},
{ "abcSun091", 3},
{ "MON31", -1},
{ "Mon31", 0},
{ NULL, -1 } // last item
};
#include <stdio.h>
#define RED "\033[31m"
#define GREEN "\033[32m"
#define WHITE "\033[39m"
int main() {
for (size_t i=0; test[i].string != NULL; ++i) {
int val = findWeekdayDate(test[i].string);
printf("%s: \"%s\", expected %d, got %d\n",
(val == test[i].expected ? GREEN " OK" WHITE: RED "BAD" WHITE),
test[i].string, test[i].expected, val
);
}
}
Comments on efficiency, style, performance, data structure choice and implementation are all of interest.
graphviz
. \$\endgroup\$