Documentation
¶
Overview ¶
Package fsm provides a scriptable FSM library.
Example ¶
package main import ( "math" "github.com/d5/go-fsm" ) var script = []byte(` fmt := import("fmt") export { truthy: func(src, dst, v) { return !!v }, falsy: func(src, dst, v) { return !v }, action: func(src, dst, v) { fmt.printf("%s -> %s: %v\n", src, dst, v) }, enter: func(src, dst, v) { fmt.printf("%s ->: %v\n", dst, v) }, leave: func(src, dst, v) { fmt.printf("-> %s: %v\n", src, v) } } `) func main() { machine, err := fsm.New(script). State("S", "enter", "leave"). State("T", "enter", "leave"). State("F", "enter", "leave"). Transition("S", "T", "truthy", "action"). Transition("S", "F", "falsy", "action"). Compile() if err != nil { panic(err) } if _, err := machine.Run("S", 1); err != nil { panic(err) } if _, err := machine.Run("S", math.NaN()); err != nil { panic(err) } if _, err := machine.Run("S", "foobar"); err != nil { panic(err) } if _, err := machine.Run("S", []interface{}{}); err != nil { panic(err) } }
Output: -> S: 1 S -> T: 1 T ->: 1 -> S: NaN S -> F: NaN F ->: NaN -> S: "foobar" S -> T: "foobar" T ->: "foobar" -> S: [] S -> F: [] F ->: []
Example (Decimals) ¶
package main import ( "fmt" "github.com/d5/go-fsm" ) var decimalsScript = []byte(` fmt := import("fmt") export { // test if the first character is a digit is_digit: func(src, dst, v) { return v[0] >= '0' && v[0] <= '9' }, // test if the first character is a period is_dot: func(src, dst, v) { return v[0] == '.' }, // test if there are no more characters left is_eol: func(src, dst, v) { return len(v) == 0 }, // prints out transition info print_tx: func(src, dst, v) { fmt.printf("%s -> %s: %q\n", src, dst, v) }, // cut the first character enter: func(src, dst, v) { return v[1:] }, enter_end: func(src, dst, v) { return "valid number" }, enter_error: func(src, dst, v) { return "invalid number: " + v } }`) func main() { // build and compile state machine machine, err := fsm.New(decimalsScript). State("S", "enter", ""). // start State("N", "enter", ""). // whole numbers State("P", "enter", ""). // decimal point State("F", "enter", ""). // fractional part State("E", "enter_end", ""). // end State("X", "enter_error", ""). // error Transition("S", "E", "is_eol", "print_tx"). Transition("S", "N", "is_digit", "print_tx"). Transition("S", "X", "", "print_tx"). Transition("N", "E", "is_eol", "print_tx"). Transition("N", "N", "is_digit", "print_tx"). Transition("N", "P", "is_dot", "print_tx"). Transition("N", "X", "", "print_tx"). Transition("P", "F", "is_digit", "print_tx"). Transition("P", "X", "", "print_tx"). Transition("F", "E", "is_eol", "print_tx"). Transition("F", "F", "is_digit", "print_tx"). Transition("F", "X", "", "print_tx"). Compile() if err != nil { panic(err) } // test case 1: "123.456" res, err := machine.Run("S", "123.456") if err != nil { panic(err) } fmt.Println(res) // test case 2: "12.34.65" res, err = machine.Run("S", "12.34.56") if err != nil { panic(err) } fmt.Println(res) }
Output: S -> N: "123.456" N -> N: "23.456" N -> N: "3.456" N -> P: ".456" P -> F: "456" F -> F: "56" F -> F: "6" F -> E: "" valid number S -> N: "12.34.56" N -> N: "2.34.56" N -> P: ".34.56" P -> F: "34.56" F -> F: "4.56" F -> X: ".56" invalid number: .56
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Builder ¶
type Builder struct {
// contains filtered or unexported fields
}
Builder represents a state machine builder that constructs and compiles the state machine. Call New to create a new Builder.
func New ¶
New creates a new Builder with a user script.
User script must export functions for all condition and actions of the state machine.
Example ¶
var script = []byte(` fmt := import("fmt") export { truthy: func(src, dst, v) { return !!v }, falsy: func(src, dst, v) { return !v }, enter: func(src, dst, v) { fmt.printf("ENTER %v: %v\n", dst, v) }, leave: func(src, dst, v) { fmt.printf("LEAVE %v: %v\n", src, v) } }`) _ = fsm.New(script). State("S", "enter", "leave"). State("T", "enter", "leave"). State("F", "enter", "leave"). Transition("S", "T", "truthy", ""). Transition("S", "F", "falsy", "")
Output:
func (*Builder) Compile ¶
func (b *Builder) Compile() (*StateMachine, error)
Compile compiles the script and builds the state machine. This function does not validate the states and transitions. Call Validate or ValidateCompile if you want to validate them.
func (*Builder) State ¶
State defines a state with its entry/exit action function names.
Entry and exit action functions are optional, but, if specified, the function in the user script must take 3 arguments:
export { action_name: func(src, dst, v) { return some_value // optional } }
For entry functions, 'src' is the previous state, and, 'dst' is entering state. For exit functions, 'src' is the leaving state, and, 'dst' is the next state. 'v' is the current data value of the state machine. 'v' itself is immutable, but, entry and exit action functions may return a new value to change it. If they don't return anything (or return 'undefined'), the value will not be changed. If it returns a Tengo error object, the state machine will stop and returns the error.
export { action_name: func(src, dst, v) { return error("an error occurred") } }
func (*Builder) Transition ¶
Transition defines (adds) a transition from 'src' to 'dst' states. It also takes the condition and action function names, which are optional. An empty condition function name makes the transition unconditional (which means the transition always evaluates to true). Condition function and action function must take 3 arguments:
export { action_name: func(src, dst, v) { return some_value // truthy or falsy } }
'src' is the current state, and, 'dst' is next state of the transition. 'v' is the current data value of the state machine, and, 'v' is immutable. For condition functions, the truthiness (https://github.com/d5/tengo/blob/master/docs/runtime-types.md#objectisfalsy) of the returned value determines whether the condition is fulfilled or not. For action functions, they may return a new value to change it. If they don't return anything (or return 'undefined'), the value will not be changed. If it returns a Tengo error object, the state machine will stop and returns the error.
export { action_name: func(src, dst, v) { return error("an error occurred") } }
func (*Builder) Validate ¶
Validate validates all states and transitions. It ensures that all states are properly defined and all condition and action functions are exported from the user script.
func (*Builder) ValidateCompile ¶
func (b *Builder) ValidateCompile() (*StateMachine, error)
ValidateCompile is combination of Validate and Compile functions. Call Compile if you don't need to validate the states and transitions.
type StateMachine ¶
type StateMachine struct {
// contains filtered or unexported fields
}
StateMachine represents a compiled state machine. Use Builder to construct and compile StateMachine.
func (*StateMachine) Run ¶
func (m *StateMachine) Run( src string, in interface{}, ) (out *tengo.Variable, err error)
Run executes the state machine from an initial state 'src' and an input data value 'in'. See https://github.com/d5/tengo/blob/master/docs/interoperability.md#type-conversion-table for data value conversions. Run continues to evaluate and move between states, until there are no more transitions available. When it stops, Run returns the final output value 'out' or an error 'err' if a script returned an error while executing.