1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
|
Finite state automatons (FSAs) are usually implemented via em(state x input)
matrices. For example, when using tt(Flexc++) to recognize letters, digits or
other characters it defines three input categories, and 4 states (the first
state being the INITIAL state determining the next state based on the
character read from the scanner's input stream, the other three being the
states that handle characters from their specific categories).
FSAs can also be implemented using coroutines. When using coroutines each
coroutine handles a specific input category, and determines the category to
use next, given the current input category. Figure ref(FSAFig) shows a simple
FSA: at tt(Start) a digit takes us to tt(Digit), a letter to tt(Letter), at
any other character we remain in tt(Start), and at end of file (EOF) we end
the FSA at state tt(Done). tt(Digit) and tt(Letter) act analogously.
figure(coroutines/fsa)
(Finite State Automaton)
(FSAFig)
This FSA uses four coroutines: tt(coStart, coDigit, coLetter), and tt(coDone),
each returning their own handlers (like a tt(Start) handler returned by
tt(coStart), a tt(Digit) handler by tt(coDigit), etc.). Here is
tt(coStart):
verbinsert(-ns4 //coro demo/fsa/start/costart.cc)
The flow of this coroutine is probably self-explanatory, but note the
tt(co_await) statements at lines 9, 14, and 20: at these lines the
tt(co_awaits) realize the switch from the current coroutine to another. How
that's realized is soon described.
The coroutines tt(coDigit) and tt(coLetter) perform similar actions, but
tt(coDone), called at EOF, simply returns, thus ending the coroutine-based
processing of the input stream. Here's tt(coDone), simply using tt(co_return)
to end its lifetime:
verbinsert(-s4 //coro demo/fsa/done/codone.cc)
Now take a look at this short input file to be processed by the program:
verbinsert(-as4 demo/fsa/input)
when processing this input the program shows its state changes:
verbinsert(-as4 demo/fsa/output)
Since coroutines are normally suspended once activated, the tt(Start) handler
privides a member tt(go) starting the FSA by resuming its coroutine:
verbinsert(-s4 //go demo/fsa/start/go.cc)
The tt(main) function merely activates the tt(Start) coroutine, but the
coroutines might of course also be embedded in something like a tt(class FSA),
and tt(main) might offer an option to process a file argument instead of using
redirection. Here's tt(main):
verbinsert(-s4 //main demo/fsa/main.cc)
|