Finite state machine: Difference between revisions

Include the state in the prompt
m (syntax highlighting fixup automation)
(Include the state in the prompt)
 
(7 intermediate revisions by 5 users not shown)
Line 91:
Refunding money
Machine ready: (d)eposit, or (q)uit?q
</pre>
 
=={{header|ALGOL 68}}==
<syntaxhighlight lang="algol68">
BEGIN # finite state machine #
 
# mode representing a state in the FSM #
MODE FSMSTATE = STRUCT( INT state # code for the state #
, PROC INT next state # routine to change state #
);
# executes the FSM defined by states, starting from the initial state #
# and terminating when the exit state is reached #
PROC run fsm = ( []FSMSTATE states, INT initial state, exit state )VOID:
BEGIN
INT state := initial state;
WHILE state /= exit state DO
BOOL found := FALSE;
FOR s pos FROM LWB states TO UPB states WHILE NOT found DO
IF found := state OF states[ s pos ] = state THEN
state := next state OF states[ s pos ]
FI
OD;
IF NOT found THEN
# in an invalid state - restart #
print( ( "(resetting)", newline ) );
state := initial state
FI
OD
END # run fsm # ;
 
BEGIN # test FSM #
# possible states #
INT exit = 0, ready = 1, waiting = 2, dispense = 3, refunding = 4;
# prompts the user for a single character code and returns it #
# the user is re-prompted until they enter one of the characters in #
# answers #
PROC get code = ( STRING prompt, answers )CHAR:
BEGIN
CHAR response;
WHILE print( ( prompt, ": " ) );
STRING answer;
read( ( answer, newline ) );
response := IF answer = "" THEN REPR 0 ELSE answer[ LWB answer ] FI;
IF response >= "a" AND response <= "z" THEN
# convert lowercase response to upper #
response := REPR ( ABS response + ( ABS "A" - ABS "a" ) )
FI;
NOT char in string( response, NIL, answers )
DO SKIP OD;
response
END # get code # ;
 
run fsm( ( ( ready
, INT: IF "Q" = get code( "Ready : Enter D to deposit, Q to Quit", "DQ" )
THEN exit
ELSE waiting
FI
)
, ( waiting
, INT: IF "S" = get code( "Waiting : Enter S to Select, R to Refund", "SR" )
THEN dispense
ELSE refunding
FI
)
, ( dispense
, INT: BEGIN get code( "Dispensing: Remove your product and Enter R", "R" );
ready
END
)
, ( refunding
, INT: BEGIN print( ( "Refunding", newline ) ); ready END
)
)
, ready
, exit
)
 
END
 
END
</syntaxhighlight>
{{out}}
<pre>
Ready : Enter D to deposit, Q to Quit: d
Waiting : Enter S to Select, R to Refund: s
Dispensing: Remove your product and Enter R: r
Ready : Enter D to deposit, Q to Quit: d
Waiting : Enter S to Select, R to Refund: r
Refunding
Ready : Enter D to deposit, Q to Quit: q
</pre>
 
Line 1,102 ⟶ 1,193:
}
</syntaxhighlight>
 
=={{header|jq}}==
{{works with|jq}}
 
'''Also works with gojq and fq''' provided the line defining
keys_unsorted is uncommented.
 
In this entry, we adopt an approach which emphasizes
separating code from data: we define a format for
representing state-transition tables as JSON objects,
which can be stored for example in separate files;
and we illustrate one possible FSM engine for
animating such state-transition tables.
 
The format of the JSON object specifying a state-transition table is
as follows, it being assumed that each "state" has a distinct
description as a JSON string:
 
* each top-level key represents a state of the FSM, with "exit" meaning stop;
* the value of each top-level key is another JSON object, which we will refer to as the trigger dictionary;
* each trigger dictionary consists of one or more key-value pairs, in which the "key" is the name of a trigger and the value is the name of a state.
 
Triggers can be of three kinds:
1. external (corresponding to external inputs)
2. automatic (corresponding to determinate internal transitions)
3. indeterminate (corresponding to non-determinism)
 
For external transitions, the keys should be non-empty strings that do not match "^[0-9]+ ".
For automatic transitions, the trigger object should have "" as its only key.
For indeterminate transitions, the trigger object should have keys matching the regex "^[0-9]+ "
 
The FSM engine presented here is intended to allow a person to provide
the "external inputs" as well as to pace automatic transitions and
to simulate the indeterminate transitions. In general, a menu of valid
input choices is presented, and the first match of the response with
these options determines the state transition. In addition, "?" as a
user input is recognized as a request for help.
 
'''fsm.json'''
<syntaxhighlight lang=jq>
{
"ready": {
"deposit": "waiting",
"quit": "exit"
},
"waiting": {
"select": "dispense",
"refund": "refunding"
},
"dispense": {
"confirm": "confirming",
"refund": "refunding"
},
"refunding": {
"1 REFUND MONEY": "ready",
"2 SORRY": "ready"
},
"confirming": {
"": "ready"
}
}
</syntaxhighlight>
'''fsm.jq'''
<syntaxhighlight lang=jq>
# Uncomment the following line if using gojq or fq:
# def keys_unsorted: keys;
 
# next($action) determines the next state.
# Global: $fsm (the transition-table)
# $action specifies an action, which can be abbreviated: the first possible match is selected.
# Input: {state}
def next($action):
($fsm[.state] | keys_unsorted) as $keys
| if ($action|length) == 0
then if $keys|index("") then fsm[.state][""]
else null
end
else (first($keys[] | select( startswith($action) )) // null) as $k
| if $k then fsm[.state][$k] else null end
end;
 
def start: {"state": "ready"};
 
# The FSM engine - progress from state to state based on user input
def progress:
def options: fsm[.state]|keys_unsorted;
def prompt:
options
| if length == 1 and .[0]=="" then "Enter anything to proceed."
elif .[0]|test("^[0-9]+ ") then "options: \(.) (simulated non-deterministic transition)"
else "options: \(.)"
end;
 
def help:
options
| if length == 1 and .[0]=="" then "(internal state transition awaiting your input)"
elif .[0]|startswith("1 ") then "(simulated NDFSM awaiting your input in the form of an initial substring): \(.)"
else
"Make a selection by typing an initial substring of the option you wish to select: \(.)"
end;
 
start
| label $out
| "Initial state: \(.state)\nMake your selection (at least one letter) from these options: \(options))",
foreach inputs as $in (.;
.previous=.state
| .error = null
| if $in == "?" then .error = true #
else next($in) as $next
| if $next then .state=$next else .error = "try again or enter ? for help" end
end;
if .error == true then help
elif .error then .error
elif .state == "exit" then break $out
else
"\(.previous) + \($in) => \(.state)",
prompt
end
) ;
progress
</syntaxhighlight>
'''Illustrative Transcript''':
<pre>
$ jq -nRr --argfile fsm fsm.json -f fsm.jq
Initial state: ready
Make your selection (at least one letter) from these options: ["deposit","quit"])
?
Make a selection by typing an initial substring of the option you wish to select: ["deposit","quit"]
d
ready + d => waiting
options: ["refund","select"]
s
waiting + s => dispense
options: ["confirm","refund"]
c
dispense + c => confirming
Enter anything to proceed.
 
confirming + => ready
options: ["deposit","quit"]
d
ready + d => waiting
options: ["refund","select"]
r
waiting + r => refunding
options: ["1 REFUND MONEY","2 SORRY"] (simulated non-deterministic transition)
1
refunding + 1 => ready
options: ["deposit","quit"]
q
</pre>
 
=={{header|Julia}}==
Line 2,278 ⟶ 2,522:
'''[dependencies]'''<br>
methods-enum = "0.2.4"
<syntaxhighlight lang="fsharprust">enum State {
Ready,
Waiting,
Line 2,481 ⟶ 2,725:
{{trans|Kotlin}}
{{libheader|Wren-str}}
<syntaxhighlight lang="ecmascriptwren">import "./str" for Str
import "io" for Stdin, Stdout
 
Line 2,552 ⟶ 2,796:
(D)ispense or (Q)uit : q
OK, quitting
</pre>
 
=={{header|XPL0}}==
<syntaxhighlight lang "XPL0">int State, Trans, Table, Msg;
def \State\ Ready, Waiting, Dispense, Refunding, Exit;
def \Trans\ Deposit, Select, Refund, Collect, Quit; \State:
[Table:=[[Waiting, Ready, Ready, Ready, Exit], \Ready
[Waiting, Dispense, Refunding, Waiting, Waiting], \Waiting
[Dispense, Dispense, Dispense, Ready, Dispense], \Dispense
[Ready, Ready, Ready, Ready, Ready], \Refunding
[Exit, Exit, Exit, Exit, Exit]]; \Exit
State:= Ready;
loop [Msg:= ["Ready, choose (D)eposit or (Q)uit: ",
"Waiting, choose (S)elect or (R)efund: ",
"Dispensing, please (C)ollect product: ",
"Refunding, please collect refund.",
"Shutting down."];
Text(0, Msg(State));
case State of
Exit: quit;
Refunding: Trans:= Refund \implicit transition
other case ChIn(1) of \explicit transitions
^D,^d: Trans:= Deposit;
^S,^s: Trans:= Select;
^R,^r: Trans:= Refund;
^C,^c: Trans:= Collect;
^Q,^q: Trans:= Quit
other []; \illegal entries don't change state
CrLf(0);
State:= Table(State, Trans);
];
CrLf(0);
]</syntaxhighlight>
{{out}}
<pre>
Ready, choose (D)eposit or (Q)uit: D
Waiting, choose (S)elect or (R)efund: S
Dispensing, please (C)ollect product: C
Ready, choose (D)eposit or (Q)uit: D
Waiting, choose (S)elect or (R)efund: R
Refunding, please collect refund.
Ready, choose (D)eposit or (Q)uit: Q
Shutting down.
</pre>
 
3,043

edits