You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
103 lines
3.2 KiB
Lua
103 lines
3.2 KiB
Lua
--- Helper module for specifying finite state machines (FSMs) with. Provides a DSL.
|
|
-- @module lqc.fsm
|
|
-- @alias fsm
|
|
local algorithm = require 'lqc.fsm.algorithm'
|
|
local state = require 'lqc.fsm.state'
|
|
local lqc = require 'lqc.quickcheck'
|
|
|
|
--- Adds a stop state to the list of states.
|
|
-- This is a special predefined state that will stop the FSM from generating
|
|
-- more state transitions.
|
|
-- @param state_list List of states in the FSM (not including stop state)
|
|
-- @return the updated state list (variable modified in place).
|
|
local function add_stop_state(state_list)
|
|
table.insert(state_list, state 'stop' {
|
|
precondition = function()
|
|
return true
|
|
end, -- always succeeds
|
|
next_state = function()
|
|
return nil
|
|
end, -- not used
|
|
postcondition = function()
|
|
return true
|
|
end, -- always succeeds
|
|
})
|
|
return state_list
|
|
end
|
|
|
|
--- Checks if an object is callable (function or functable):
|
|
-- @param obj Object to be checked if it is callable
|
|
-- @return true if it is callable; otherwise false
|
|
local function is_callable(obj)
|
|
local type_obj = type(obj)
|
|
return type_obj == 'function' or type_obj == 'table'
|
|
end
|
|
|
|
--- Checks if the FSM table contains a valid specification of a state machine
|
|
-- @param fsm_table Table containing FSM information/description
|
|
-- @return nil; raises an error message if specification is not valid
|
|
local function check_valid_fsm_spec(fsm_table)
|
|
if not is_callable(fsm_table.commands) then
|
|
error 'Need to provide list of commands to FSM!'
|
|
end
|
|
if not is_callable(fsm_table.initial_state) then
|
|
error 'Need to provide initial state function to FSM!'
|
|
end
|
|
|
|
local states = fsm_table.states
|
|
if type(states) ~= 'table' then
|
|
error 'Need to provide a table of possible states of the FSM!'
|
|
end
|
|
|
|
-- States are already checked in state.lua
|
|
end
|
|
|
|
local function default_cleanup()
|
|
end
|
|
local function default_when_fail()
|
|
end
|
|
|
|
--- Constructs a new FSM
|
|
-- @param description text description of the FSM
|
|
-- @param fsm_table table containing FSM info
|
|
-- @return FSM object
|
|
local function new(description, fsm_table)
|
|
local FSM = {}
|
|
|
|
function FSM.check(_)
|
|
return algorithm.check(description, fsm_table)
|
|
end
|
|
|
|
return FSM
|
|
end
|
|
|
|
--- Creates a new FSM and inserts it into the list of properties.
|
|
-- @param descr Text description of the FSM
|
|
-- @param fsm_info_table Table containing information of the FSM
|
|
local function fsm(descr, fsm_info_table)
|
|
local function fsm_func(fsm_table)
|
|
fsm_table.states = add_stop_state(fsm_table.states)
|
|
fsm_table.cleanup = fsm_table.cleanup or default_cleanup
|
|
fsm_table.when_fail = fsm_table.when_fail or default_when_fail
|
|
fsm_table.numtests = fsm_table.numtests or lqc.numtests
|
|
fsm_table.numshrinks = fsm_table.numshrinks or lqc.numshrinks
|
|
|
|
check_valid_fsm_spec(fsm_table)
|
|
local new_fsm = new(descr, fsm_table)
|
|
table.insert(lqc.properties, new_fsm)
|
|
end
|
|
|
|
if fsm_info_table then
|
|
-- Called normally (most likely from Moonscript)
|
|
fsm_func(fsm_info_table)
|
|
return function()
|
|
end
|
|
end
|
|
|
|
-- Called with DSL syntax
|
|
return fsm_func
|
|
end
|
|
|
|
return fsm
|
|
|