-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Hyeonjae Park
committed
Jul 9, 2020
1 parent
de1ffd7
commit ec20a75
Showing
6 changed files
with
293 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
# fsm | ||
|
||
## Install | ||
|
||
``` | ||
go get github.com/hyeonjae/fsm | ||
``` | ||
|
||
## Usage | ||
|
||
```go | ||
package main | ||
|
||
import "github.com/hyeonjae/fsm" | ||
|
||
func main() { | ||
s1 := fsm.State{State: "s1"} | ||
s2 := fsm.State{State: "s2"} | ||
s3 := fsm.State{State: "s3"} | ||
|
||
a1 := fsm.Action{Action: "a"} | ||
a2 := fsm.Action{Action: "b"} | ||
|
||
t1 := fsm.Transition{ | ||
Tuple: fsm.StateActionTuple{State: s1, Action: a1}, | ||
State: s2, | ||
} | ||
t2 := fsm.Transition{ | ||
Tuple: fsm.StateActionTuple{State: s1, Action: a2}, | ||
State: s1, | ||
} | ||
t3 := fsm.Transition{ | ||
Tuple: fsm.StateActionTuple{State: s2, Action: a1}, | ||
State: s1, | ||
} | ||
t4 := fsm.Transition{ | ||
Tuple: fsm.StateActionTuple{State: s2, Action: a2}, | ||
State: s3, | ||
} | ||
|
||
builder := fsm.Builder(). | ||
States([]fsm.State{s1, s2, s3}). | ||
Actions([]fsm.Action{a1, a2}). | ||
Start(s1). | ||
Accepts([]fsm.State{s3}). | ||
Transitions([]fsm.Transition{t1, t2, t3, t4}) | ||
|
||
statemachine := fsm.New(builder) | ||
|
||
statemachine.Start() | ||
|
||
statemachine.Transit(a1) | ||
statemachine.Transit(a1) | ||
statemachine.Transit(a1) | ||
statemachine.Transit(a2) | ||
|
||
statemachine.Current() // s3 | ||
statemachine.Accepted() // true | ||
} | ||
``` | ||
|
||
## Test | ||
|
||
```shell | ||
$ go test ./... | ||
ok github.com/hyeonjae/fsm 0.758s | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package fsm | ||
|
||
type StateMachineBuilder interface { | ||
States([]State) StateMachineBuilder | ||
Actions([]Action) StateMachineBuilder | ||
Start(State) StateMachineBuilder | ||
Accepts([]State) StateMachineBuilder | ||
Transitions([]Transition) StateMachineBuilder | ||
|
||
Build() stateMachineComprehension | ||
} | ||
|
||
type stateMachineBuilder struct { | ||
states []State | ||
actions []Action | ||
start State | ||
accepts []State | ||
transitions []Transition | ||
} | ||
|
||
func Builder() *stateMachineBuilder { | ||
return &stateMachineBuilder{} | ||
} | ||
|
||
type stateMachineComprehension struct { | ||
states []State | ||
actions []Action | ||
start State | ||
accepts []State | ||
transitions map[StateActionTuple]State | ||
} | ||
|
||
func (builder *stateMachineBuilder) States(states []State) StateMachineBuilder { | ||
builder.states = states | ||
return builder | ||
} | ||
|
||
func (builder *stateMachineBuilder) Actions(actions []Action) StateMachineBuilder { | ||
builder.actions = actions | ||
return builder | ||
} | ||
|
||
func (builder *stateMachineBuilder) Start(start State) StateMachineBuilder { | ||
builder.start = start | ||
return builder | ||
} | ||
|
||
func (builder *stateMachineBuilder) Accepts(accepts []State) StateMachineBuilder { | ||
builder.accepts = accepts | ||
return builder | ||
} | ||
|
||
func (builder *stateMachineBuilder) Transitions(transitions []Transition) StateMachineBuilder { | ||
builder.transitions = transitions | ||
return builder | ||
} | ||
|
||
func (builder *stateMachineBuilder) Build() stateMachineComprehension { | ||
transitions := map[StateActionTuple]State{} | ||
for _, transition := range builder.transitions { | ||
transitions[transition.Tuple] = transition.State | ||
} | ||
|
||
return stateMachineComprehension{ | ||
states: builder.states, | ||
actions: builder.actions, | ||
start: builder.start, | ||
accepts: builder.accepts, | ||
transitions: transitions, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package fsm | ||
|
||
import "errors" | ||
|
||
type StateMachine interface { | ||
Start() | ||
Transit(Action) error | ||
|
||
Current() State | ||
Accepted() bool | ||
} | ||
|
||
type stateMachine struct { | ||
StateMachine stateMachineComprehension | ||
CurrentState State | ||
} | ||
|
||
func (machine *stateMachine) Start() { | ||
machine.CurrentState = machine.StateMachine.start | ||
} | ||
|
||
func (machine *stateMachine) Transit(a Action) error { | ||
s := machine.CurrentState | ||
next, exists := machine.StateMachine.transitions[StateActionTuple{s, a}] | ||
if !exists { | ||
return errors.New("No transition") | ||
} | ||
|
||
machine.CurrentState = next | ||
return nil | ||
} | ||
|
||
func (machine stateMachine) Current() State { | ||
return machine.CurrentState | ||
} | ||
|
||
func (machine stateMachine) Accepted() bool { | ||
for _, accept := range machine.StateMachine.accepts { | ||
if machine.CurrentState == accept { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
func New(builder StateMachineBuilder) StateMachine { | ||
return &stateMachine{ | ||
StateMachine: builder.Build(), | ||
CurrentState: State{}, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package fsm_test | ||
|
||
import ( | ||
"testing" | ||
|
||
fsm "github.com/hyeonjae/fsm" | ||
) | ||
|
||
func TestFSM(t *testing.T) { | ||
s1 := fsm.State{State: "s1"} | ||
s2 := fsm.State{State: "s2"} | ||
s3 := fsm.State{State: "s3"} | ||
|
||
a1 := fsm.Action{Action: "a"} | ||
a2 := fsm.Action{Action: "b"} | ||
|
||
t1 := fsm.Transition{ | ||
Tuple: fsm.StateActionTuple{State: s1, Action: a1}, | ||
State: s2, | ||
} | ||
t2 := fsm.Transition{ | ||
Tuple: fsm.StateActionTuple{State: s1, Action: a2}, | ||
State: s1, | ||
} | ||
t3 := fsm.Transition{ | ||
Tuple: fsm.StateActionTuple{State: s2, Action: a1}, | ||
State: s1, | ||
} | ||
t4 := fsm.Transition{ | ||
Tuple: fsm.StateActionTuple{State: s2, Action: a2}, | ||
State: s3, | ||
} | ||
|
||
builder := fsm.Builder(). | ||
States([]fsm.State{s1, s2, s3}). | ||
Actions([]fsm.Action{a1, a2}). | ||
Start(s1). | ||
Accepts([]fsm.State{s3}). | ||
Transitions([]fsm.Transition{t1, t2, t3, t4}) | ||
|
||
statemachine := fsm.New(builder) | ||
|
||
statemachine.Start() | ||
if statemachine.Current() != s1 { | ||
t.Error("Wrong result") | ||
} | ||
if statemachine.Accepted() != false { | ||
t.Error("Wrong result") | ||
} | ||
|
||
statemachine.Transit(a1) | ||
if statemachine.Current() != s2 { | ||
t.Error("Wrong result") | ||
} | ||
if statemachine.Accepted() != false { | ||
t.Error("Wrong result") | ||
} | ||
|
||
statemachine.Transit(a1) | ||
if statemachine.Current() != s1 { | ||
t.Error("Wrong result") | ||
} | ||
if statemachine.Accepted() != false { | ||
t.Error("Wrong result") | ||
} | ||
|
||
statemachine.Transit(a1) | ||
if statemachine.Current() != s2 { | ||
t.Error("Wrong result") | ||
} | ||
if statemachine.Accepted() != false { | ||
t.Error("Wrong result") | ||
} | ||
|
||
statemachine.Transit(a2) | ||
if statemachine.Current() != s3 { | ||
t.Error("Wrong result") | ||
} | ||
if statemachine.Accepted() != true { | ||
t.Error("Wrong result") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module github.com/hyeonjae/fsm | ||
|
||
go 1.14 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package fsm | ||
|
||
type State struct { | ||
State string | ||
} | ||
|
||
type Action struct { | ||
Action string | ||
} | ||
|
||
type StateActionTuple struct { | ||
State State | ||
Action Action | ||
} | ||
|
||
type Transition struct { | ||
Tuple StateActionTuple | ||
State State | ||
} |