mirror of
https://github.com/c-cube/ocaml-containers.git
synced 2025-12-06 11:15:31 -05:00
214 lines
5.3 KiB
OCaml
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
|