patterncMinor
Efficient data-driven finite state machine
Viewed 0 times
finiteefficientstatemachinedatadriven
Problem
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
I implemented this state machine: using simple table structure. This is the code, which includes a simple driver with test vectors.
```
#include
#include
#include
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 },
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
#include
#include
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 },
Solution
Nice work.
-
On
-
As
-
Delay/remove
-
Style inconsistency. Recommend empty
-
Use an auto formatter. The extra space betrays manual formatting as it is inconsistent with the rest of code. Life is too short for manual formatting.
Suggest adding link to software used to create FSM.
-
On
findWeekdayDate() return, I'd expect a value that is enumerated like fWD_SUCCESS, fWD_FAIL, fWD_SUCCESS_for_foo_reason, fWD_FAIL_for_bar_reason, etc. Rather than magic numbers -1,0,3,.... It could end up being a simple bool.-
As
fsm[] is const struct fsmNode, I see no value in making a field const like const char target. char target is simpler and sufficient. OTOH, if there is some value, then next should also be const as in const struct fsmNode * const next.-
Delay/remove
#includes.#include // Needed?
#include // OK, for NULL
#include // Needed?-
Style inconsistency. Recommend empty
for body to match other indentations. // for ( ; state->target && *curr != state->target; ++state)
//{ /* do nothing */ }
for ( ; state->target && *curr != state->target; ++state) {
/* do nothing */
}-
Use an auto formatter. The extra space betrays manual formatting as it is inconsistent with the rest of code. Life is too short for manual formatting.
// v
for (curr = str ; *curr; ++curr) {Suggest adding link to software used to create FSM.
Code Snippets
#include <string.h> // Needed?
#include <stdlib.h> // OK, for NULL
#include <ctype.h> // Needed?// for ( ; state->target && *curr != state->target; ++state)
//{ /* do nothing */ }
for ( ; state->target && *curr != state->target; ++state) {
/* do nothing */
}// v
for (curr = str ; *curr; ++curr) {Context
StackExchange Code Review Q#150637, answer score: 4
Revisions (0)
No revisions yet.