ocaml-containers/automaton.ml
2013-12-30 17:59:39 +01:00

214 lines
5.3 KiB
OCaml

(*
copyright (c) 2013, simon cruanes
all rights reserved.
redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer. redistributions in binary
form must reproduce the above copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other materials provided with
the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*)
(** {1 Automaton} *)
type ('s, -'i, +'o) t = 's -> 'i -> 's * 'o list
(** Transition function of an event automaton *)
type ('s, 'i, 'o) automaton = ('s, 'i, 'o) t
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 fmap_o f a s i =
let rec _fmap f l = match l with
| [] -> []
| x::l' -> f x @ _fmap f l'
in
let s', os = a s i in
let os' = _fmap f os in
s', os'
let filter_i p a s i =
if p i
then a s i
else s, []
let filter_o p a s i =
let s', os = a s i in
s', List.filter p os
let fold f s i =
let s' = f s i in
s', [s']
let product f1 f2 (s1, s2) i =
let s1', os1 = f1 s1 i in
let s2', os2 = f2 s2 i in
(s1', s2'), (os1 @ os2)
module I = struct
type 'a t = 'a -> unit
let create f = f
let send x i = x i
let comap f i x = i (f x)
let filter f i x = if f x then i x
end
module O = struct
type 'a t = {
mutable n : int; (* how many handlers? *)
mutable handlers : ('a -> bool) array;
mutable alive : keepalive; (* keep some signal alive *)
} (** Signal of type 'a *)
and keepalive =
| Keep : 'a t -> keepalive
| NotAlive : keepalive
let nop_handler x = true
let create () =
let s = {
n = 0;
handlers = Array.create 3 nop_handler;
alive = NotAlive;
} in
s
(* remove handler at index i *)
let remove s i =
(if i < s.n - 1 (* erase handler with the last one *)
then s.handlers.(i) <- s.handlers.(s.n - 1));
s.handlers.(s.n - 1) <- nop_handler; (* free handler *)
s.n <- s.n - 1;
()
let send s x =
for i = 0 to s.n - 1 do
while not (try s.handlers.(i) x with _ -> false) do
remove s i (* i-th handler is done, remove it *)
done
done
let on s f =
(* resize handlers if needed *)
(if s.n = Array.length s.handlers
then begin
let handlers = Array.create (s.n + 4) nop_handler in
Array.blit s.handlers 0 handlers 0 s.n;
s.handlers <- handlers
end);
s.handlers.(s.n) <- f;
s.n <- s.n + 1
let once s f =
on s (fun x -> ignore (f x); false)
let propagate a b =
on a (fun x -> send b x; true)
let map f signal =
let signal' = create () in
(* weak ref *)
let r = Weak.create 1 in
Weak.set r 0 (Some signal');
on signal (fun x ->
match Weak.get r 0 with
| None -> false
| Some signal' -> send signal' (f x); true);
signal'.alive <- Keep signal;
signal'
let filter p signal =
let signal' = create () in
(* weak ref *)
let r = Weak.create 1 in
Weak.set r 0 (Some signal');
on signal (fun x ->
match Weak.get r 0 with
| None -> false
| Some signal' -> (if p x then send signal' x); true);
signal'.alive <- Keep signal;
signal'
end
let connect o i =
O.on o (fun x -> I.send i x; true)
module Instance = struct
type ('s, 'i, 'o) t = {
transition : ('s, 'i, 'o) automaton;
mutable i : 'i I.t;
o : 'o O.t;
transitions : ('s * 'i * 's * 'o list) O.t;
mutable state : 's;
}
let transition_function a = a.transition
let i a = a.i
let o a = a.o
let state a = a.state
let transitions a = a.transitions
let send a i = I.send a.i i
let _q = Queue.create ()
let _process q =
while not (Queue.is_empty q) do
let task = Queue.pop q in
task ()
done
let _schedule q task = Queue.push task q
let _do_transition q a i =
let s = a.state in
let s', os = a.transition s i in
(* update state *)
a.state <- s';
(* trigger the transitions asap *)
_schedule q (fun () -> O.send a.transitions (s, i, s', os));
List.iter
(fun o -> _schedule q (fun () -> O.send a.o o))
os
let _receive a i =
let first = Queue.is_empty _q in
_do_transition _q a i;
if first then _process _q
let create ~f init =
let o = O.create () in
let transitions = O.create () in
(* create input and automaton *)
let a = { state = init; i=Obj.magic 0; o; transition=f; transitions; } in
a.i <- _receive a;
a
end