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

--- 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