forked from kyleconroy/lua-state-machine
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstatemachine.lua
More file actions
105 lines (83 loc) · 2.29 KB
/
statemachine.lua
File metadata and controls
105 lines (83 loc) · 2.29 KB
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
local machine = {}
machine.__index = machine
local function call_handler(handler, params)
if handler then
return handler(unpack(params))
end
end
local function create_transition(name)
return function(self, ...)
local can, to = self:can(name)
if can then
local from = self.current
local params = { self, name, from, to, ... }
if call_handler(self["onbefore" .. name], params) == false
or call_handler(self["onleave" .. from], params) == false then
return false
end
self.current = to
call_handler(self["onenter" .. to] or self["on" .. to], params)
call_handler(self["onafter" .. name] or self["on" .. name], params)
call_handler(self["onstatechange"], params)
return true
end
return false
end
end
local function add_to_map(map, event)
if type(event.from) == 'string' then
map[event.from] = event.to
else
for _, from in ipairs(event.from) do
map[from] = event.to
end
end
end
function machine.create(options)
assert(options.events)
local fsm = {}
setmetatable(fsm, machine)
fsm.options = options
fsm.current = options.initial or 'none'
fsm.events = {}
for _, event in ipairs(options.events or {}) do
local name = event.name
fsm[name] = fsm[name] or create_transition(name)
fsm.events[name] = fsm.events[name] or { map = {} }
add_to_map(fsm.events[name].map, event)
end
for name, callback in pairs(options.callbacks or {}) do
fsm[name] = callback
end
return fsm
end
function machine:is(state)
return self.current == state
end
function machine:can(e)
local event = self.events[e]
local to = event and event.map[self.current]
return to ~= nil, to
end
function machine:cannot(e)
return not self:can(e)
end
function machine:todot(filename)
local dotfile = io.open(filename,'w')
dotfile:write('digraph {\n')
local transition = function(event,from,to)
dotfile:write(string.format('%s -> %s [label=%s];\n',from,to,event))
end
for _, event in pairs(self.options.events) do
if type(event.from) == 'table' then
for _, from in ipairs(event.from) do
transition(event.name,from,event.to)
end
else
transition(event.name,event.from,event.to)
end
end
dotfile:write('}\n')
dotfile:close()
end
return machine