more combinators for Automaton, changed some signatures

This commit is contained in:
Simon Cruanes 2013-12-30 13:52:44 +01:00
parent cf592e2c77
commit e3d5b78c5e
2 changed files with 78 additions and 35 deletions

View file

@ -29,28 +29,26 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
type ('s, -'i, +'o) t = 's -> 'i -> 's * 'o list type ('s, -'i, +'o) t = 's -> 'i -> 's * 'o list
(** Transition function of an event automaton *) (** Transition function of an event automaton *)
module EventQueue = struct type queue = (unit -> unit) Queue.t
type t = (unit -> unit) Queue.t
let create () = Queue.create () let create_queue () = Queue.create ()
let default = create () let default_queue = create_queue ()
let _process q = let _process q =
while not (Queue.is_empty q) do while not (Queue.is_empty q) do
let task = Queue.pop q in let task = Queue.pop q in
task () task ()
done done
let _schedule q task = Queue.push task q let _schedule q task = Queue.push task q
end
(* empty callback *) (* empty callback *)
let __noop s i os = true let __noop s i os = true
type ('s, 'i, 'o) instance = { type ('s, 'i, 'o) instance = {
transition : ('s, 'i, 'o) t; transition : ('s, 'i, 'o) t;
queue : EventQueue.t; queue : queue;
mutable state : 's; mutable state : 's;
mutable connections : 'o connection list; mutable connections : 'o connection list;
mutable n_callback : int; mutable n_callback : int;
@ -58,9 +56,11 @@ type ('s, 'i, 'o) instance = {
} }
(* connection to another automaton *) (* connection to another automaton *)
and 'a connection = Conn : (_, 'a, _) instance -> 'a connection and 'a connection =
| Conn : (_, 'a, _) instance -> 'a connection
| ConnMap : ('a -> 'b) * (_, 'b, _) instance -> 'a connection
let instantiate ?(queue=EventQueue.default) ~f init = { let instantiate ?(queue=default_queue) ~f init = {
transition = f; transition = f;
queue; queue;
state = init; state = init;
@ -69,7 +69,7 @@ let instantiate ?(queue=EventQueue.default) ~f init = {
callback = Array.make 3 __noop; callback = Array.make 3 __noop;
} }
let transition a i = a.transition a.state i let transition a = a.transition
let state a = a.state let state a = a.state
@ -84,9 +84,12 @@ let on_transition a k =
a.callback.(a.n_callback) <- k; a.callback.(a.n_callback) <- k;
a.n_callback <- a.n_callback + 1 a.n_callback <- a.n_callback + 1
let connect ~left ~right = let connect left right =
left.connections <- (Conn right) :: left.connections left.connections <- (Conn right) :: left.connections
let connect_map f left right =
left.connections <- (ConnMap (f, right)) :: left.connections
(* remove i-th callback of [a] *) (* remove i-th callback of [a] *)
let _remove_callback a i = let _remove_callback a i =
if i < a.n_callback if i < a.n_callback
@ -123,11 +126,29 @@ let rec send : type s i o. (s, i, o) instance -> i -> unit
(fun o -> _forward_connections a.connections o) (fun o -> _forward_connections a.connections o)
os; os;
(* if no enclosing call to [send], we need to process events *) (* if no enclosing call to [send], we need to process events *)
if first then EventQueue._process a.queue if first then _process a.queue
and _forward_connections : type a. a connection list -> a -> unit and _forward_connections : type a. a connection list -> a -> unit
= fun l o -> match l with = fun l o -> match l with
| [] -> () | [] -> ()
| (Conn a') :: l' -> | (Conn a') :: l' ->
EventQueue._schedule a'.queue (fun () -> send a' o); _schedule a'.queue (fun () -> send a' o);
_forward_connections l' o _forward_connections l' o
| (ConnMap (f, a')) :: l' ->
_schedule a'.queue (fun () -> send a' (f o));
_forward_connections l' o
(** {2 Helpers} *)
let map_i f a s i = a s (f i)
let map_o f a s i =
let s', os = a s i in
s', List.map f os
let iter k a =
on_transition a (fun s i os -> k s i os; true)
let iter_state k = iter (fun s i (s',os) -> k s')
let iter_input k = iter (fun s i os -> k i)
let iter_output k = iter (fun s i (_,os) -> List.iter k os)

View file

@ -33,28 +33,29 @@ type ('s, 'i, 'o) instance
(** Instance of an automaton, with a concrete state, and connections to other (** Instance of an automaton, with a concrete state, and connections to other
automaton instances. *) automaton instances. *)
module EventQueue : sig type queue
type t (** Stateful value used to store the event (pending transitions) that remain
(** Stateful value used to store the event (pending transitions) that remain * to process, using an in-memory queue and processing pending tasks until
* to process, using an in-memory queue and processing pending tasks until * none remains. A default global queue is provided, see {!default_queue}. *)
* none remains. A default global queue is provided, see {!default}. *)
val default : t val default_queue : queue
(** Default event queue *) (** Default event queue *)
val create : unit -> t val create_queue : unit -> queue
end
val instantiate : ?queue:EventQueue.t -> val instantiate :
f:('s, 'i, 'o) t -> 's -> ('s, 'i, 'o) instance ?queue:queue ->
f:('s, 'i, 'o) t ->
's ->
('s, 'i, 'o) instance
(** [instantiate ~f init] creates an instance of [f] with initial state (** [instantiate ~f init] creates an instance of [f] with initial state
[init]. The [queue] is used to process transitions of this automaton. [init].
@param queue event queue used to process transitions of the automaton @param queue event queue used to process transitions of the automaton
upon calls to {!send}. Default value is {!EventQueue.default}. *) upon calls to {!send}. Default value is {!default_queue}. *)
val transition : ('s, 'i, 'o) instance -> 'i -> ('s * 'o list) val transition : ('s, 'i, 'o) instance -> ('s, 'i, 'o) t
(** Compute the transition function for the given input *) (** Transition function of this instance *)
val state : ('s, _, _) instance -> 's val state : ('s, _, _) instance -> 's
(** Current state of the automaton instance *) (** Current state of the automaton instance *)
@ -65,10 +66,14 @@ val on_transition : ('s, 'i, 'o) instance -> ('s -> 'i -> 's * 'o list -> bool)
The callback [k] returns a boolean to signal whether it wants to continue The callback [k] returns a boolean to signal whether it wants to continue
being called ([true]) or stop being called ([false]). *) being called ([true]) or stop being called ([false]). *)
val connect : left:(_, _, 'a) instance -> right:(_, 'a, _) instance -> unit val connect : (_, _, 'a) instance -> (_, 'a, _) instance -> unit
(** [connect ~left ~right] connects the ouput of [left] to the input of [right]. (** [connect left right] connects the ouput of [left] to the input of [right].
Outputs of [left] will be fed to [right]. *) Outputs of [left] will be fed to [right]. *)
val connect_map : ('a -> 'b) -> (_, _, 'a) instance -> (_, 'b, _) instance -> unit
(** [connect_map f left right] is a generalization of {!connect}, that
applies [f] to outputs of [left] before they are sent to [right] *)
val send : (_, 'i, _) instance -> 'i -> unit val send : (_, 'i, _) instance -> 'i -> unit
(** [send a i] uses [a]'s transition function to update [a] with the input (** [send a i] uses [a]'s transition function to update [a] with the input
event [i]. The output of the transition function (a list of outputs) is event [i]. The output of the transition function (a list of outputs) is
@ -76,3 +81,20 @@ val send : (_, 'i, _) instance -> 'i -> unit
This may not terminate, if the automata keep on creating new outputs that This may not terminate, if the automata keep on creating new outputs that
trigger other outputs forever. *) trigger other outputs forever. *)
(** {2 Helpers} *)
val map_i : ('a -> 'b) -> ('s, 'b, 'o) t -> ('s, 'a, 'o) t
(** map inputs *)
val map_o : ('a -> 'b) -> ('s, 'i, 'a) t -> ('s, 'i, 'b) t
(** map outputs *)
val iter : ('s -> 'i -> ('s * 'o list) -> unit) -> ('s,'i,'o) instance -> unit
(** Iterate on every transition (wrapper around {!on_transition}) *)
val iter_state : ('s -> unit) -> ('s, _, _) instance -> unit
val iter_input : ('i -> unit) -> (_, 'i, _) instance -> unit
val iter_output : ('o -> unit) -> (_, _, 'o) instance -> unit