781 lines
20 KiB
OCaml
781 lines
20 KiB
OCaml
open Import
|
|
|
|
(*
|
|
RE - A regular expression library
|
|
|
|
Copyright (C) 2001 Jerome Vouillon
|
|
email: Jerome.Vouillon@pps.jussieu.fr
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation, with
|
|
linking exception; either version 2.1 of the License, or (at
|
|
your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*)
|
|
|
|
let hash_combine h accu = (accu * 65599) + h
|
|
|
|
module Ids : sig
|
|
module Id : sig
|
|
type t
|
|
|
|
val equal : t -> t -> bool
|
|
val zero : t
|
|
val hash : t -> int
|
|
val pp : t Fmt.t
|
|
|
|
module Hash_set : sig
|
|
type id := t
|
|
type t
|
|
|
|
val create : unit -> t
|
|
val mem : t -> id -> bool
|
|
val add : t -> id -> unit
|
|
val clear : t -> unit
|
|
end
|
|
end
|
|
|
|
type t
|
|
|
|
val create : unit -> t
|
|
val next : t -> Id.t
|
|
end = struct
|
|
module Id = struct
|
|
type t = int
|
|
|
|
module Hash_set = Hash_set
|
|
|
|
let equal = Int.equal
|
|
let zero = 0
|
|
let hash x = x
|
|
let pp = Fmt.int
|
|
end
|
|
|
|
type t = int ref
|
|
|
|
let create () = ref 0
|
|
|
|
let next t =
|
|
incr t;
|
|
!t
|
|
;;
|
|
end
|
|
|
|
module Id = Ids.Id
|
|
|
|
module Sem = struct
|
|
type t =
|
|
[ `Longest
|
|
| `Shortest
|
|
| `First
|
|
]
|
|
|
|
let to_string = function
|
|
| `Shortest -> "short"
|
|
| `Longest -> "long"
|
|
| `First -> "first"
|
|
;;
|
|
|
|
let to_dyn t = Dyn.enum (to_string t)
|
|
let equal = Poly.equal
|
|
let pp ch k = Format.pp_print_string ch (to_string k)
|
|
end
|
|
|
|
module Rep_kind = struct
|
|
type t =
|
|
[ `Greedy
|
|
| `Non_greedy
|
|
]
|
|
|
|
let to_string = function
|
|
| `Greedy -> "Greedy"
|
|
| `Non_greedy -> "Non_greedy"
|
|
;;
|
|
|
|
let to_dyn t = Dyn.enum (to_string t)
|
|
let pp fmt t = Format.pp_print_string fmt (to_string t)
|
|
end
|
|
|
|
module Mark : sig
|
|
type t = private int
|
|
|
|
val compare : t -> t -> int
|
|
val equal : t -> t -> bool
|
|
val pp : t Fmt.t
|
|
val to_dyn : t -> Dyn.t
|
|
val start : t
|
|
val prev : t -> t
|
|
val next : t -> t
|
|
val next2 : t -> t
|
|
val group_count : t -> int
|
|
val outside_range : t -> start_inclusive:t -> stop_inclusive:t -> bool
|
|
end = struct
|
|
type t = int
|
|
|
|
let equal = Int.equal
|
|
let compare = Int.compare
|
|
let pp = Format.pp_print_int
|
|
let to_dyn = Dyn.int
|
|
let start = 0
|
|
let prev x = pred x
|
|
let next x = succ x
|
|
let next2 x = x + 2
|
|
let group_count x = x / 2
|
|
|
|
let outside_range t ~start_inclusive ~stop_inclusive =
|
|
t < start_inclusive || t > stop_inclusive
|
|
;;
|
|
end
|
|
|
|
module Idx : sig
|
|
type t = private int
|
|
|
|
val pp : t Fmt.t
|
|
val to_dyn : t -> Dyn.t
|
|
val to_int : t -> int
|
|
val unknown : t
|
|
val initial : t
|
|
val used : t -> bool
|
|
val make : int -> t
|
|
val equal : t -> t -> bool
|
|
end = struct
|
|
type t = int
|
|
|
|
let to_dyn = Dyn.int
|
|
let to_int x = x
|
|
let pp = Format.pp_print_int
|
|
let used t = t >= 0
|
|
let make x = x
|
|
let equal = Int.equal
|
|
let unknown = -1
|
|
let initial = 0
|
|
end
|
|
|
|
module Expr = struct
|
|
type t =
|
|
{ id : Id.t
|
|
; def : def
|
|
}
|
|
|
|
and def =
|
|
| Cst of Cset.t
|
|
| Alt of t list
|
|
| Seq of Sem.t * t * t
|
|
| Eps
|
|
| Rep of Rep_kind.t * Sem.t * t
|
|
| Mark of Mark.t
|
|
| Erase of Mark.t * Mark.t
|
|
| Before of Category.t
|
|
| After of Category.t
|
|
| Pmark of Pmark.t
|
|
|
|
let wrap_sem sem sem' v =
|
|
let open Dyn in
|
|
let name = Sem.to_string sem' in
|
|
match sem with
|
|
| Some sem when Sem.equal sem sem' -> v
|
|
| None | Some _ ->
|
|
(match v with
|
|
| List v -> variant name v
|
|
| _ -> variant name [ v ])
|
|
;;
|
|
|
|
let rec seq_as_list sem = function
|
|
| Eps -> []
|
|
| Cst cs -> [ Cst cs ]
|
|
| Seq (sem', x, y) ->
|
|
if Sem.equal sem sem'
|
|
then x.def :: seq_as_list sem y.def
|
|
else raise_notrace Not_found
|
|
| _ -> raise_notrace Not_found
|
|
;;
|
|
|
|
let seq_as_list sem t =
|
|
match seq_as_list sem t with
|
|
| exception Not_found -> None
|
|
| s -> Some s
|
|
;;
|
|
|
|
let rec dyn_of_def sem =
|
|
let open Dyn in
|
|
function
|
|
| Cst cset -> Cset.to_dyn cset
|
|
| Alt alt -> variant "Alt" (List.map ~f:(to_dyn sem) alt)
|
|
| Seq (sem', x, y) ->
|
|
let to_dyn = to_dyn (Some sem') in
|
|
let x =
|
|
match seq_as_list sem' y.def with
|
|
| None -> variant "Seq" [ to_dyn x; to_dyn y ]
|
|
| Some y -> variant "Seq" (to_dyn x :: List.map y ~f:(dyn_of_def sem))
|
|
in
|
|
wrap_sem sem sem' x
|
|
| Eps -> Enum "Eps"
|
|
| Rep (_, sem', t) -> wrap_sem sem sem' (variant "Rep" [ to_dyn (Some sem') t ])
|
|
| Mark m -> variant "Mark" [ Mark.to_dyn m ]
|
|
| Pmark m -> variant "Pmark" [ Pmark.to_dyn m ]
|
|
| Erase (x, y) -> variant "Erase" [ Mark.to_dyn x; Mark.to_dyn y ]
|
|
| Before c -> variant "Before" [ Category.to_dyn c ]
|
|
| After c -> variant "After" [ Category.to_dyn c ]
|
|
|
|
and to_dyn sem { id = _; def } = dyn_of_def sem def
|
|
|
|
let rec pp_with_sem sem ch e =
|
|
let open Fmt in
|
|
match e.def with
|
|
| Cst l -> sexp ch "cst" Cset.pp l
|
|
| Alt l -> sexp ch "alt" (list (pp_with_sem sem)) l
|
|
| Seq (k, e, e') ->
|
|
sexp ch "seq" (triple Sem.pp (pp_with_sem sem) (pp_with_sem sem)) (k, e, e')
|
|
| Eps -> str ch "eps"
|
|
| Rep (_rk, k, e) -> sexp ch "rep" (pair Sem.pp (pp_with_sem (Some k))) (k, e)
|
|
| Mark i -> sexp ch "mark" Mark.pp i
|
|
| Pmark i -> sexp ch "pmark" Pmark.pp i
|
|
| Erase (b, e) -> sexp ch "erase" (pair Mark.pp Mark.pp) (b, e)
|
|
| Before c -> sexp ch "before" Category.pp c
|
|
| After c -> sexp ch "after" Category.pp c
|
|
;;
|
|
|
|
let pp = pp_with_sem None
|
|
let eps_expr = { id = Id.zero; def = Eps }
|
|
let mk ids def = { id = Ids.next ids; def }
|
|
let empty ids = mk ids (Alt [])
|
|
let cst ids s = if Cset.is_empty s then empty ids else mk ids (Cst s)
|
|
let eps ids = mk ids Eps
|
|
let rep ids kind sem x = mk ids (Rep (kind, sem, x))
|
|
let mark ids m = mk ids (Mark m)
|
|
let pmark ids i = mk ids (Pmark i)
|
|
let erase ids m m' = mk ids (Erase (m, m'))
|
|
let before ids c = mk ids (Before c)
|
|
let after ids c = mk ids (After c)
|
|
|
|
let alt ids = function
|
|
| [] -> empty ids
|
|
| [ c ] -> c
|
|
| l -> mk ids (Alt l)
|
|
;;
|
|
|
|
let seq ids (kind : Sem.t) x y =
|
|
match x.def, y.def with
|
|
| Alt [], _ -> x
|
|
| _, Alt [] -> y
|
|
| Eps, _ -> y
|
|
| _, Eps when Sem.equal kind `First -> x
|
|
| _ -> mk ids (Seq (kind, x, y))
|
|
;;
|
|
|
|
let is_eps expr =
|
|
match expr.def with
|
|
| Eps -> true
|
|
| _ -> false
|
|
;;
|
|
|
|
let rec rename ids x =
|
|
match x.def with
|
|
| Cst _ | Eps | Mark _ | Pmark _ | Erase _ | Before _ | After _ -> mk ids x.def
|
|
| Alt l -> mk ids (Alt (List.map ~f:(rename ids) l))
|
|
| Seq (k, y, z) -> mk ids (Seq (k, rename ids y, rename ids z))
|
|
| Rep (g, k, y) -> mk ids (Rep (g, k, rename ids y))
|
|
;;
|
|
end
|
|
|
|
type expr = Expr.t
|
|
|
|
include Expr
|
|
|
|
module Marks = struct
|
|
type t =
|
|
{ marks : (Mark.t * Idx.t) list
|
|
; pmarks : Pmark.Set.t
|
|
}
|
|
|
|
let to_dyn { marks; pmarks } : Dyn.t =
|
|
let open Dyn in
|
|
record
|
|
[ ( "marks"
|
|
, List.map marks ~f:(fun (m, idx) -> pair (Mark.to_dyn m) (Idx.to_dyn idx))
|
|
|> list )
|
|
; "pmarks", Pmark.Set.to_list pmarks |> List.map ~f:Pmark.to_dyn |> list
|
|
]
|
|
;;
|
|
|
|
let equal { marks; pmarks } t =
|
|
List.equal
|
|
~eq:(fun (x, y) (x', y') -> Mark.equal x x' && Idx.equal y y')
|
|
marks
|
|
t.marks
|
|
&& Pmark.Set.equal pmarks t.pmarks
|
|
;;
|
|
|
|
let empty = { marks = []; pmarks = Pmark.Set.empty }
|
|
|
|
let hash_marks_offset =
|
|
let f acc ((a : Mark.t), (i : Idx.t)) =
|
|
hash_combine (a :> int) (hash_combine (i :> int) acc)
|
|
in
|
|
fun l init -> List.fold_left l ~init ~f
|
|
;;
|
|
|
|
let hash m accu = hash_marks_offset m.marks (hash_combine (Hashtbl.hash m.pmarks) accu)
|
|
|
|
let marks_set_idx =
|
|
let rec marks_set_idx idx marks =
|
|
match marks with
|
|
| [] -> []
|
|
| (a, idx') :: rem ->
|
|
if Idx.equal idx' Idx.unknown then (a, idx) :: marks_set_idx idx rem else marks
|
|
in
|
|
fun marks idx -> { marks with marks = marks_set_idx idx marks.marks }
|
|
;;
|
|
|
|
let filter t (b : Mark.t) (e : Mark.t) =
|
|
{ t with
|
|
marks =
|
|
List.filter t.marks ~f:(fun ((i : Mark.t), _) ->
|
|
Mark.outside_range i ~start_inclusive:b ~stop_inclusive:e)
|
|
}
|
|
;;
|
|
|
|
let set_mark t (i : Mark.t) =
|
|
{ t with marks = (i, Idx.unknown) :: List.remove_assq i t.marks }
|
|
;;
|
|
|
|
let set_pmark t i = { t with pmarks = Pmark.Set.add i t.pmarks }
|
|
|
|
let pp fmt { marks; pmarks } =
|
|
Format.pp_open_box fmt 1;
|
|
(match marks with
|
|
| [] -> ()
|
|
| _ :: _ ->
|
|
Format.fprintf
|
|
fmt
|
|
"@[<2>marks@ %a@]"
|
|
(Format.pp_print_list (fun fmt (a, i) ->
|
|
Format.fprintf fmt "%a-%a" Mark.pp a Idx.pp i))
|
|
marks);
|
|
(match Pmark.Set.to_list pmarks with
|
|
| [] -> ()
|
|
| pmarks ->
|
|
Format.fprintf fmt "@[<2>pmarks %a@]" (Format.pp_print_list Pmark.pp) pmarks);
|
|
Format.pp_close_box fmt ()
|
|
;;
|
|
end
|
|
|
|
module Status = struct
|
|
type t =
|
|
| Failed
|
|
| Match of Mark_infos.t * Pmark.Set.t
|
|
| Running
|
|
end
|
|
|
|
module Desc : sig
|
|
type t
|
|
|
|
val pp : t Fmt.t
|
|
|
|
module E : sig
|
|
type nonrec t = private
|
|
| TSeq of Sem.t * t * Expr.t
|
|
| TExp of Marks.t * Expr.t
|
|
| TMatch of Marks.t
|
|
end
|
|
|
|
val to_dyn : t -> Dyn.t
|
|
val fold_right : t -> init:'acc -> f:(E.t -> 'acc -> 'acc) -> 'acc
|
|
val tseq : Sem.t -> t -> Expr.t -> t -> t
|
|
val initial : Expr.t -> t
|
|
val empty : t
|
|
val set_idx : Idx.t -> t -> t
|
|
val hash : t -> int -> int
|
|
val equal : t -> t -> bool
|
|
val status : t -> Status.t
|
|
val first_match : t -> Marks.t option
|
|
val remove_matches : t -> t
|
|
val split_at_match : t -> t * t
|
|
val add_match : t -> Marks.t -> t
|
|
val add_eps : t -> Marks.t -> t
|
|
val add_expr : t -> E.t -> t
|
|
val iter_marks : t -> f:(Marks.t -> unit) -> unit
|
|
val remove_duplicates : Id.Hash_set.t -> t -> Expr.t -> t
|
|
end = struct
|
|
module E = struct
|
|
type t =
|
|
| TSeq of Sem.t * t list * Expr.t
|
|
| TExp of Marks.t * Expr.t
|
|
| TMatch of Marks.t
|
|
|
|
let rec equal_list l1 l2 = List.equal ~eq:equal l1 l2
|
|
|
|
and equal x y =
|
|
match x, y with
|
|
| TSeq (_, l1, e1), TSeq (_, l2, e2) -> Id.equal e1.id e2.id && equal_list l1 l2
|
|
| TExp (marks1, e1), TExp (marks2, e2) ->
|
|
Id.equal e1.id e2.id && Marks.equal marks1 marks2
|
|
| TMatch marks1, TMatch marks2 -> Marks.equal marks1 marks2
|
|
| _, _ -> false
|
|
;;
|
|
|
|
let rec hash (t : t) accu =
|
|
match t with
|
|
| TSeq (_, l, e) ->
|
|
hash_combine 0x172a1bce (hash_combine (Id.hash e.id) (hash_list l accu))
|
|
| TExp (marks, e) ->
|
|
hash_combine 0x2b4c0d77 (hash_combine (Id.hash e.id) (Marks.hash marks accu))
|
|
| TMatch marks -> hash_combine 0x1c205ad5 (Marks.hash marks accu)
|
|
|
|
and hash_list =
|
|
let f acc x = hash x acc in
|
|
fun l init -> List.fold_left l ~init ~f
|
|
;;
|
|
end
|
|
|
|
type t = E.t list
|
|
|
|
let rec to_dyn sem t = Dyn.list (List.map ~f:(dyn_of_e sem) t)
|
|
|
|
and dyn_of_e sem =
|
|
let open Dyn in
|
|
function
|
|
| E.TSeq (sem', x, y) ->
|
|
wrap_sem
|
|
sem
|
|
sem'
|
|
(variant "TSeq" [ to_dyn (Some sem') x; Expr.to_dyn (Some sem') y ])
|
|
| TExp (marks, e) ->
|
|
let e =
|
|
let base = [ Expr.to_dyn sem e ] in
|
|
if Marks.(equal empty marks) then base else Marks.to_dyn marks :: base
|
|
in
|
|
variant "TExp" e
|
|
| TMatch m -> variant "TMarks" [ Marks.to_dyn m ]
|
|
;;
|
|
|
|
let to_dyn t = to_dyn None t
|
|
|
|
open E
|
|
|
|
let equal = E.equal_list
|
|
let hash = E.hash_list
|
|
|
|
let tseq' kind x y =
|
|
match x with
|
|
| [] -> []
|
|
| [ TExp (marks, { def = Eps; _ }) ] -> [ TExp (marks, y) ]
|
|
| _ -> [ TSeq (kind, x, y) ]
|
|
;;
|
|
|
|
let tseq kind x y rem = tseq' kind x y @ rem
|
|
|
|
let rec fold_right t ~init ~f =
|
|
match t with
|
|
| [] -> init
|
|
| x :: xs -> f x (fold_right xs ~init ~f)
|
|
;;
|
|
|
|
let rec iter_marks t ~f =
|
|
List.iter t ~f:(fun (e : E.t) ->
|
|
match e with
|
|
| TSeq (_, l, _) -> iter_marks l ~f
|
|
| TExp (marks, _) | TMatch marks -> f marks)
|
|
;;
|
|
|
|
let rec print_state_rec ch e (y : Expr.t) =
|
|
match e with
|
|
| TMatch marks -> Format.fprintf ch "@[<2>(TMatch@ %a)@]" Marks.pp marks
|
|
| TSeq (sem, l', x) ->
|
|
Format.fprintf ch "@[<2>(TSeq@ %a@ " Sem.pp sem;
|
|
print_state_lst ch l' x;
|
|
Format.fprintf ch "@ %a)@]" Expr.pp x
|
|
| TExp (marks, { def = Eps; _ }) ->
|
|
Format.fprintf ch "@[<2>(TExp@ %a@ (%a)@ (eps))@]" Id.pp y.id Marks.pp marks
|
|
| TExp (marks, x) ->
|
|
Format.fprintf ch "@[<2>(TExp@ %a@ (%a)@ %a)@]" Id.pp x.id Marks.pp marks Expr.pp x
|
|
|
|
and print_state_lst ch l y =
|
|
match l with
|
|
| [] -> Format.fprintf ch "()"
|
|
| e :: rem ->
|
|
print_state_rec ch e y;
|
|
List.iter rem ~f:(fun e ->
|
|
Format.fprintf ch "@ | ";
|
|
print_state_rec ch e y)
|
|
;;
|
|
|
|
let pp ch t = print_state_lst ch [ t ] { id = Id.zero; def = Eps }
|
|
|
|
let rec first_match = function
|
|
| [] -> None
|
|
| TMatch marks :: _ -> Some marks
|
|
| _ :: r -> first_match r
|
|
;;
|
|
|
|
let remove_matches t =
|
|
List.filter t ~f:(function
|
|
| TMatch _ -> false
|
|
| _ -> true)
|
|
;;
|
|
|
|
let split_at_match =
|
|
let rec split_at_match_rec l = function
|
|
| [] -> assert false
|
|
| TMatch _ :: r -> List.rev l, remove_matches r
|
|
| x :: r -> split_at_match_rec (x :: l) r
|
|
in
|
|
fun l -> split_at_match_rec [] l
|
|
;;
|
|
|
|
let status : _ -> Status.t = function
|
|
| [] -> Failed
|
|
| TMatch m :: _ -> Match (Mark_infos.make (m.marks :> (int * int) list), m.pmarks)
|
|
| _ -> Running
|
|
;;
|
|
|
|
let set_idx =
|
|
let rec f idx = function
|
|
| TMatch marks -> TMatch (Marks.marks_set_idx marks idx)
|
|
| TSeq (kind, l, x) -> TSeq (kind, set_idx idx l, x)
|
|
| TExp (marks, x) -> TExp (Marks.marks_set_idx marks idx, x)
|
|
and set_idx idx xs = List.map xs ~f:(f idx) in
|
|
set_idx
|
|
;;
|
|
|
|
let[@ocaml.warning "-32"] pp fmt t =
|
|
Format.fprintf fmt "[%a]" (Format.pp_print_list ~pp_sep:(Fmt.lit "; ") pp) t
|
|
;;
|
|
|
|
let empty = []
|
|
let initial expr = [ TExp (Marks.empty, expr) ]
|
|
let add_match t marks = TMatch marks :: t
|
|
let add_eps t marks = TExp (marks, eps_expr) :: t
|
|
let add_expr t expr = expr :: t
|
|
|
|
let remove_duplicates =
|
|
let rec loop seen l y =
|
|
match l with
|
|
| [] -> []
|
|
| (TMatch _ as x) :: _ ->
|
|
(* Truncate after first match *)
|
|
[ x ]
|
|
| TSeq (kind, l, x) :: r ->
|
|
let l = loop seen l x in
|
|
let r = loop seen r y in
|
|
tseq kind l x r
|
|
| (TExp (_marks, { def = Eps; _ }) as e) :: r ->
|
|
if Id.Hash_set.mem seen y.id
|
|
then loop seen r y
|
|
else (
|
|
Id.Hash_set.add seen y.id;
|
|
e :: loop seen r y)
|
|
| (TExp (_marks, x) as e) :: r ->
|
|
if Id.Hash_set.mem seen x.id
|
|
then loop seen r y
|
|
else (
|
|
Id.Hash_set.add seen x.id;
|
|
e :: loop seen r y)
|
|
in
|
|
fun seen l y ->
|
|
Id.Hash_set.clear seen;
|
|
loop seen l y
|
|
;;
|
|
end
|
|
|
|
module E = Desc.E
|
|
|
|
module State = struct
|
|
type t =
|
|
{ idx : Idx.t
|
|
; category : Category.t
|
|
; desc : Desc.t
|
|
; mutable status : Status.t option
|
|
; hash : int
|
|
}
|
|
(* Thread-safety: We use double-checked locking to access field
|
|
[status] in function [status] below. *)
|
|
|
|
let pp fmt t = Desc.pp fmt t.desc
|
|
let[@inline] idx t = t.idx
|
|
let to_dyn t = Desc.to_dyn t.desc
|
|
|
|
let dummy =
|
|
{ idx = Idx.unknown
|
|
; category = Category.dummy
|
|
; desc = Desc.empty
|
|
; status = None
|
|
; hash = -1
|
|
}
|
|
;;
|
|
|
|
let hash idx cat desc =
|
|
Desc.hash desc (hash_combine idx (hash_combine (Category.to_int cat) 0))
|
|
land 0x3FFFFFFF
|
|
;;
|
|
|
|
let mk idx cat desc =
|
|
{ idx; category = cat; desc; status = None; hash = hash (idx :> int) cat desc }
|
|
;;
|
|
|
|
let create cat e = mk Idx.initial cat (Desc.initial e)
|
|
|
|
let equal { idx; category; desc; status = _; hash } t =
|
|
Int.equal hash t.hash
|
|
&& Idx.equal idx t.idx
|
|
&& Category.equal category t.category
|
|
&& Desc.equal desc t.desc
|
|
;;
|
|
|
|
(* To be called when the mutex has already been acquired *)
|
|
let status_no_mutex s =
|
|
match s.status with
|
|
| Some s -> s
|
|
| None ->
|
|
let st = Desc.status s.desc in
|
|
s.status <- Some st;
|
|
st
|
|
;;
|
|
|
|
let status m s =
|
|
match s.status with
|
|
| Some s -> s
|
|
| None ->
|
|
Mutex.lock m;
|
|
let st = status_no_mutex s in
|
|
Mutex.unlock m;
|
|
st
|
|
;;
|
|
|
|
module Table = Hashtbl.Make (struct
|
|
type nonrec t = t
|
|
|
|
let equal = equal
|
|
let hash t = t.hash
|
|
end)
|
|
end
|
|
|
|
(**** Find a free index ****)
|
|
|
|
module Working_area = struct
|
|
type t =
|
|
{ mutable ids : Bit_vector.t
|
|
; seen : Id.Hash_set.t
|
|
; index_count : int Atomic.t
|
|
}
|
|
|
|
let create () =
|
|
{ ids = Bit_vector.create_zero 1
|
|
; seen = Id.Hash_set.create ()
|
|
; index_count = Atomic.make 0
|
|
}
|
|
;;
|
|
|
|
let index_count w = Atomic.get w.index_count
|
|
|
|
let mark_used_indices tbl =
|
|
Desc.iter_marks ~f:(fun marks ->
|
|
List.iter marks.marks ~f:(fun (_, i) ->
|
|
if Idx.used i then Bit_vector.set tbl (i :> int) true))
|
|
;;
|
|
|
|
let rec find_free tbl idx len =
|
|
if idx = len || not (Bit_vector.get tbl idx) then idx else find_free tbl (idx + 1) len
|
|
;;
|
|
|
|
let free_index t l =
|
|
Bit_vector.reset_zero t.ids;
|
|
mark_used_indices t.ids l;
|
|
let len = Bit_vector.length t.ids in
|
|
let idx = find_free t.ids 0 len in
|
|
if idx = len
|
|
then (
|
|
t.ids <- Bit_vector.create_zero (2 * len);
|
|
(* This function is only called when the mutex is locked. So we
|
|
are sure that this is always coherent with the length of
|
|
[t.ids]. *)
|
|
Atomic.set t.index_count (2 * len));
|
|
Idx.make idx
|
|
;;
|
|
end
|
|
|
|
(**** Computation of the next state ****)
|
|
|
|
type ctx =
|
|
{ c : Cset.c
|
|
; prev_cat : Category.t
|
|
; next_cat : Category.t
|
|
}
|
|
|
|
let rec delta_expr ({ c; _ } as ctx) marks (x : Expr.t) rem =
|
|
(*Format.eprintf "%d@." x.id;*)
|
|
match x.def with
|
|
| Cst s -> if Cset.mem c s then Desc.add_eps rem marks else rem
|
|
| Alt l -> delta_alt ctx marks l rem
|
|
| Seq (kind, y, z) ->
|
|
let y = delta_expr ctx marks y Desc.empty in
|
|
delta_seq ctx kind y z rem
|
|
| Rep (rep_kind, kind, y) -> delta_rep ctx marks x rep_kind kind y rem
|
|
| Eps -> Desc.add_match rem marks
|
|
| Mark i -> Desc.add_match rem (Marks.set_mark marks i)
|
|
| Pmark i -> Desc.add_match rem (Marks.set_pmark marks i)
|
|
| Erase (b, e) -> Desc.add_match rem (Marks.filter marks b e)
|
|
| Before cat ->
|
|
if Category.intersect ctx.next_cat cat then Desc.add_match rem marks else rem
|
|
| After cat ->
|
|
if Category.intersect ctx.prev_cat cat then Desc.add_match rem marks else rem
|
|
|
|
and delta_rep ctx marks x rep_kind kind y rem =
|
|
let y, marks' =
|
|
let y = delta_expr ctx marks y Desc.empty in
|
|
match Desc.first_match y with
|
|
| None -> y, marks
|
|
| Some marks -> Desc.remove_matches y, marks
|
|
in
|
|
match rep_kind with
|
|
| `Greedy -> Desc.tseq kind y x (Desc.add_match rem marks')
|
|
| `Non_greedy -> Desc.add_match (Desc.tseq kind y x rem) marks
|
|
|
|
and delta_alt ctx marks l rem = List.fold_right l ~init:rem ~f:(delta_expr ctx marks)
|
|
|
|
and delta_seq ctx (kind : Sem.t) y z rem =
|
|
match Desc.first_match y with
|
|
| None -> Desc.tseq kind y z rem
|
|
| Some marks ->
|
|
(match kind with
|
|
| `Longest -> Desc.tseq kind (Desc.remove_matches y) z (delta_expr ctx marks z rem)
|
|
| `Shortest -> delta_expr ctx marks z (Desc.tseq kind (Desc.remove_matches y) z rem)
|
|
| `First ->
|
|
let y, y' = Desc.split_at_match y in
|
|
Desc.tseq kind y z (delta_expr ctx marks z (Desc.tseq kind y' z rem)))
|
|
;;
|
|
|
|
let rec delta_e ctx marks (x : E.t) rem =
|
|
match x with
|
|
| TSeq (kind, y, z) ->
|
|
let y = delta_desc ctx marks y Desc.empty in
|
|
delta_seq ctx kind y z rem
|
|
| TExp (marks, e) -> delta_expr ctx marks e rem
|
|
| TMatch _ -> Desc.add_expr rem x
|
|
|
|
and delta_desc ctx marks (l : Desc.t) rem =
|
|
Desc.fold_right l ~init:rem ~f:(fun y acc -> delta_e ctx marks y acc)
|
|
;;
|
|
|
|
let delta (tbl_ref : Working_area.t) next_cat char (st : State.t) =
|
|
let expr =
|
|
let prev_cat = st.category in
|
|
let ctx = { c = char; next_cat; prev_cat } in
|
|
Desc.remove_duplicates
|
|
tbl_ref.seen
|
|
(delta_desc ctx Marks.empty st.desc Desc.empty)
|
|
Expr.eps_expr
|
|
in
|
|
let idx = Working_area.free_index tbl_ref expr in
|
|
let expr = Desc.set_idx idx expr in
|
|
State.mk idx next_cat expr
|
|
;;
|