2 -- $Id: fsm.lua,v 1.18 2005/03/29 21:04:19 cpressey Exp $
3 -- Framework for simple Finite State Machines in Lua.
5 -- BEGIN lib/fsm.lua --
7 local POSIX = require("posix")
8 local FileName = require("filename")
9 local App = require("app")
11 -- Global "class" variable:
14 -- Global "static methods":
16 -- Create a new FSM object instance.
17 -- This object can then have states added to it with
18 -- fsm:register(StateName, Function), and can be run
19 -- with fsm:run(InitialStateName).
22 local state = {} -- a dictionary of state name -> state
23 local sequence = {} -- an array of state number -> state
24 local current_state = nil -- reference to the current state
26 fsm.register = function(fsm, tab)
31 position = table.getn(sequence) + 1
33 table.insert(sequence, state[tab.name])
36 fsm.next = function(fsm)
37 return sequence[current_state.position + 1]
40 fsm.prev = function(fsm)
41 return sequence[current_state.position - 1]
44 fsm.current = function(fsm)
48 local resolve_state = function(x)
49 if type(x) == "string" then
53 error("No state named '" .. x ..
56 elseif type(x) == "number" then
60 error("No state number " .. tostring(x) ..
65 elseif type(x) == "table" then
73 error("State object `" .. tostring(x) ..
74 "' does not exist in FSM")
76 error("Invalid state reference: " .. tostring(x))
79 fsm.run = function(fsm, start_state)
82 current_state = resolve_state(start_state)
83 while current_state do
84 if type(current_state.action) ~= "function" then
85 error("State '" .. current_state.name ..
86 "' does not define an action function")
88 result = current_state.action(fsm)
89 current_state = resolve_state(result)
94 -- Note that we only return the table of functions;
95 -- we do not return the state table. However, a
96 -- reference ("upvalue" in Lua terminology) to the
97 -- state table is still carried along inside the
98 -- fsm function table; in this way it is retained,
99 -- and it is also protected from modification from
100 -- the outside (i.e. it is encapsulated.)
104 -- Create a new FSM object instance automatically from
105 -- the Lua script files in the given directory. Each script
106 -- should end with a return statement that returns a table
107 -- describing the state.
108 FSM.from_dir = function(dir)
109 local fsm = FSM.new()
112 local state_count = 0
114 files = POSIX.dir(dir)
117 for file_no in files do
118 local full_file = dir .. "/" .. files[file_no]
121 if files[file_no] ~= FileName.basename(App.current_script) and
122 not FileName.is_dir(full_file) and
123 string.find(files[file_no], "^[^%.].*%.lua$") then
124 tab = App.run_script(full_file)
127 state_count = state_count + 1
132 if state_count == 0 then
133 error("Directory " .. dir .. " should contain at least one FSM scriptlet")
139 -- Create a new FSM object instance automatically from
140 -- the Lua script files in the same directory as the Lua
141 -- script file that invoked this function.
142 FSM.auto = function(start_state)
143 local path = FileName.dirname(App.current_script)
144 local fsm = FSM.from_dir(path)
149 -- END of lib/fsm.lua --