674 lines
19 KiB
OCaml
674 lines
19 KiB
OCaml
|
|
(*
|
||
|
|
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
|
||
|
|
*)
|
||
|
|
|
||
|
|
type sem = [ `Longest | `Shortest | `First ]
|
||
|
|
|
||
|
|
type rep_kind = [ `Greedy | `Non_greedy ]
|
||
|
|
|
||
|
|
type mark = int
|
||
|
|
type idx = int
|
||
|
|
|
||
|
|
type expr = { id : int; def : def }
|
||
|
|
|
||
|
|
and def =
|
||
|
|
Cst of Cset.t
|
||
|
|
| Alt of expr list
|
||
|
|
| Seq of sem * expr * expr
|
||
|
|
| Eps
|
||
|
|
| Rep of rep_kind * sem * expr
|
||
|
|
| Mark of int
|
||
|
|
| Erase of int * int
|
||
|
|
| Before of Category.t
|
||
|
|
| After of Category.t
|
||
|
|
| Pmark of Pmark.t
|
||
|
|
|
||
|
|
let hash_combine h accu = accu * 65599 + h
|
||
|
|
|
||
|
|
module Marks = struct
|
||
|
|
type t =
|
||
|
|
{ marks : (int * int) list
|
||
|
|
; pmarks : Pmark.Set.t }
|
||
|
|
|
||
|
|
let empty = { marks = [] ; pmarks = Pmark.Set.empty }
|
||
|
|
|
||
|
|
let rec merge_marks_offset old = function
|
||
|
|
| [] ->
|
||
|
|
old
|
||
|
|
| (i, v) :: rem ->
|
||
|
|
let nw' = merge_marks_offset (List.remove_assq i old) rem in
|
||
|
|
if v = -2 then
|
||
|
|
nw'
|
||
|
|
else
|
||
|
|
(i, v) :: nw'
|
||
|
|
|
||
|
|
let merge old nw =
|
||
|
|
{ marks = merge_marks_offset old.marks nw.marks
|
||
|
|
; pmarks = Pmark.Set.union old.pmarks nw.pmarks }
|
||
|
|
|
||
|
|
let rec hash_marks_offset l accu =
|
||
|
|
match l with
|
||
|
|
[] -> accu
|
||
|
|
| (a, i) :: r -> hash_marks_offset r (hash_combine a (hash_combine i accu))
|
||
|
|
|
||
|
|
let hash m accu =
|
||
|
|
hash_marks_offset m.marks (hash_combine (Hashtbl.hash m.pmarks) accu)
|
||
|
|
|
||
|
|
let rec marks_set_idx idx = function
|
||
|
|
| (a, -1) :: rem ->
|
||
|
|
(a, idx) :: marks_set_idx idx rem
|
||
|
|
| marks ->
|
||
|
|
marks
|
||
|
|
|
||
|
|
let marks_set_idx marks idx =
|
||
|
|
{ marks with marks = marks_set_idx idx marks.marks }
|
||
|
|
|
||
|
|
let pp_marks ch t =
|
||
|
|
match t.marks with
|
||
|
|
| [] ->
|
||
|
|
()
|
||
|
|
| (a, i) :: r ->
|
||
|
|
Format.fprintf ch "%d-%d" a i;
|
||
|
|
List.iter (fun (a, i) -> Format.fprintf ch " %d-%d" a i) r
|
||
|
|
end
|
||
|
|
|
||
|
|
(****)
|
||
|
|
|
||
|
|
let pp_sem ch k =
|
||
|
|
Format.pp_print_string ch
|
||
|
|
(match k with
|
||
|
|
`Shortest -> "short"
|
||
|
|
| `Longest -> "long"
|
||
|
|
| `First -> "first")
|
||
|
|
|
||
|
|
|
||
|
|
let pp_rep_kind fmt = function
|
||
|
|
| `Greedy -> Format.pp_print_string fmt "Greedy"
|
||
|
|
| `Non_greedy -> Format.pp_print_string fmt "Non_greedy"
|
||
|
|
|
||
|
|
let rec pp 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) l
|
||
|
|
| Seq (k, e, e') ->
|
||
|
|
sexp ch "seq" (triple pp_sem pp pp) (k, e, e')
|
||
|
|
| Eps ->
|
||
|
|
str ch "eps"
|
||
|
|
| Rep (_rk, k, e) ->
|
||
|
|
sexp ch "rep" (pair pp_sem pp) (k, e)
|
||
|
|
| Mark i ->
|
||
|
|
sexp ch "mark" int i
|
||
|
|
| Pmark i ->
|
||
|
|
sexp ch "pmark" int (i :> int)
|
||
|
|
| Erase (b, e) ->
|
||
|
|
sexp ch "erase" (pair int int) (b, e)
|
||
|
|
| Before c ->
|
||
|
|
sexp ch "before" Category.pp c
|
||
|
|
| After c ->
|
||
|
|
sexp ch "after" Category.pp c
|
||
|
|
|
||
|
|
|
||
|
|
(****)
|
||
|
|
|
||
|
|
let rec first f = function
|
||
|
|
| [] ->
|
||
|
|
None
|
||
|
|
| x :: r ->
|
||
|
|
match f x with
|
||
|
|
None -> first f r
|
||
|
|
| Some _ as res -> res
|
||
|
|
|
||
|
|
(****)
|
||
|
|
|
||
|
|
type ids = int ref
|
||
|
|
let create_ids () = ref 0
|
||
|
|
|
||
|
|
let eps_expr = { id = 0; def = Eps }
|
||
|
|
|
||
|
|
let mk_expr ids def =
|
||
|
|
incr ids;
|
||
|
|
{ id = !ids; def = def }
|
||
|
|
|
||
|
|
let empty ids = mk_expr ids (Alt [])
|
||
|
|
|
||
|
|
let cst ids s =
|
||
|
|
if Cset.is_empty s
|
||
|
|
then empty ids
|
||
|
|
else mk_expr ids (Cst s)
|
||
|
|
|
||
|
|
let alt ids = function
|
||
|
|
| [] -> empty ids
|
||
|
|
| [c] -> c
|
||
|
|
| l -> mk_expr ids (Alt l)
|
||
|
|
|
||
|
|
let seq ids kind x y =
|
||
|
|
match x.def, y.def with
|
||
|
|
Alt [], _ -> x
|
||
|
|
| _, Alt [] -> y
|
||
|
|
| Eps, _ -> y
|
||
|
|
| _, Eps when kind = `First -> x
|
||
|
|
| _ -> mk_expr ids (Seq (kind, x, y))
|
||
|
|
|
||
|
|
let is_eps expr =
|
||
|
|
match expr.def with
|
||
|
|
| Eps -> true
|
||
|
|
| _ -> false
|
||
|
|
|
||
|
|
let eps ids = mk_expr ids Eps
|
||
|
|
|
||
|
|
let rep ids kind sem x = mk_expr ids (Rep (kind, sem, x))
|
||
|
|
|
||
|
|
let mark ids m = mk_expr ids (Mark m)
|
||
|
|
|
||
|
|
let pmark ids i = mk_expr ids (Pmark i)
|
||
|
|
|
||
|
|
let erase ids m m' = mk_expr ids (Erase (m, m'))
|
||
|
|
|
||
|
|
let before ids c = mk_expr ids (Before c)
|
||
|
|
|
||
|
|
let after ids c = mk_expr ids (After c)
|
||
|
|
|
||
|
|
(****)
|
||
|
|
|
||
|
|
let rec rename ids x =
|
||
|
|
match x.def with
|
||
|
|
Cst _ | Eps | Mark _ | Pmark _ | Erase _ | Before _ | After _ ->
|
||
|
|
mk_expr ids x.def
|
||
|
|
| Alt l ->
|
||
|
|
mk_expr ids (Alt (List.map (rename ids) l))
|
||
|
|
| Seq (k, y, z) ->
|
||
|
|
mk_expr ids (Seq (k, rename ids y, rename ids z))
|
||
|
|
| Rep (g, k, y) ->
|
||
|
|
mk_expr ids (Rep (g, k, rename ids y))
|
||
|
|
|
||
|
|
(****)
|
||
|
|
|
||
|
|
type hash = int
|
||
|
|
type mark_infos = int array
|
||
|
|
type status = Failed | Match of mark_infos * Pmark.Set.t | Running
|
||
|
|
|
||
|
|
module E = struct
|
||
|
|
type t =
|
||
|
|
| TSeq of t list * expr * sem
|
||
|
|
| TExp of Marks.t * expr
|
||
|
|
| TMatch of Marks.t
|
||
|
|
|
||
|
|
let rec equal l1 l2 =
|
||
|
|
match l1, l2 with
|
||
|
|
| [], [] ->
|
||
|
|
true
|
||
|
|
| TSeq (l1', e1, _) :: r1, TSeq (l2', e2, _) :: r2 ->
|
||
|
|
e1.id = e2.id && equal l1' l2' && equal r1 r2
|
||
|
|
| TExp (marks1, e1) :: r1, TExp (marks2, e2) :: r2 ->
|
||
|
|
e1.id = e2.id && marks1 = marks2 && equal r1 r2
|
||
|
|
| TMatch marks1 :: r1, TMatch marks2 :: r2 ->
|
||
|
|
marks1 = marks2 && equal r1 r2
|
||
|
|
| _ ->
|
||
|
|
false
|
||
|
|
|
||
|
|
let rec hash l accu =
|
||
|
|
match l with
|
||
|
|
| [] ->
|
||
|
|
accu
|
||
|
|
| TSeq (l', e, _) :: r ->
|
||
|
|
hash r (hash_combine 0x172a1bce (hash_combine e.id (hash l' accu)))
|
||
|
|
| TExp (marks, e) :: r ->
|
||
|
|
hash r
|
||
|
|
(hash_combine 0x2b4c0d77 (hash_combine e.id (Marks.hash marks accu)))
|
||
|
|
| TMatch marks :: r ->
|
||
|
|
hash r (hash_combine 0x1c205ad5 (Marks.hash marks accu))
|
||
|
|
|
||
|
|
let texp marks x = TExp (marks, x)
|
||
|
|
|
||
|
|
let tseq kind x y rem =
|
||
|
|
match x with
|
||
|
|
[] -> rem
|
||
|
|
| [TExp (marks, {def = Eps ; _})] -> TExp (marks, y) :: rem
|
||
|
|
| _ -> TSeq (x, y, kind) :: rem
|
||
|
|
|
||
|
|
let rec print_state_rec ch e y =
|
||
|
|
match e with
|
||
|
|
| TMatch marks ->
|
||
|
|
Format.fprintf ch "@[<2>(Match@ %a)@]" Marks.pp_marks marks
|
||
|
|
| TSeq (l', x, _kind) ->
|
||
|
|
Format.fprintf ch "@[<2>(Seq@ ";
|
||
|
|
print_state_lst ch l' x;
|
||
|
|
Format.fprintf ch "@ %a)@]" pp x
|
||
|
|
| TExp (marks, {def = Eps; _}) ->
|
||
|
|
Format.fprintf ch "@[<2>(Exp@ %d@ (%a)@ (eps))@]" y.id Marks.pp_marks marks
|
||
|
|
| TExp (marks, x) ->
|
||
|
|
Format.fprintf ch "@[<2>(Exp@ %d@ (%a)@ %a)@]" x.id Marks.pp_marks marks 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
|
||
|
|
(fun e ->
|
||
|
|
Format.fprintf ch "@ | ";
|
||
|
|
print_state_rec ch e y)
|
||
|
|
rem
|
||
|
|
|
||
|
|
let pp ch t = print_state_lst ch [t] { id = 0; def = Eps }
|
||
|
|
end
|
||
|
|
|
||
|
|
module State = struct
|
||
|
|
type t =
|
||
|
|
{ idx: idx
|
||
|
|
; category: Category.t
|
||
|
|
; desc: E.t list
|
||
|
|
; mutable status: status option
|
||
|
|
; hash: hash }
|
||
|
|
|
||
|
|
let dummy =
|
||
|
|
{ idx = -1
|
||
|
|
; category = Category.dummy
|
||
|
|
; desc = []
|
||
|
|
; status = None
|
||
|
|
; hash = -1 }
|
||
|
|
|
||
|
|
let hash idx cat desc =
|
||
|
|
E.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 cat desc}
|
||
|
|
|
||
|
|
let create cat e = mk 0 cat [E.TExp (Marks.empty, e)]
|
||
|
|
|
||
|
|
let equal x y =
|
||
|
|
(x.hash : int) = y.hash && (x.idx : int) = y.idx &&
|
||
|
|
Category.equal x.category y.category && E.equal x.desc y.desc
|
||
|
|
|
||
|
|
let compare x y =
|
||
|
|
let c = compare (x.hash : int) y.hash in
|
||
|
|
if c <> 0 then c else
|
||
|
|
let c = Category.compare x.category y.category in
|
||
|
|
if c <> 0 then c else
|
||
|
|
compare x.desc y.desc
|
||
|
|
|
||
|
|
type t' = t
|
||
|
|
module Table = Hashtbl.Make(
|
||
|
|
struct
|
||
|
|
type t = t'
|
||
|
|
let equal = equal
|
||
|
|
let hash t = t.hash
|
||
|
|
end)
|
||
|
|
end
|
||
|
|
|
||
|
|
(**** Find a free index ****)
|
||
|
|
|
||
|
|
type working_area = bool array ref
|
||
|
|
|
||
|
|
let create_working_area () = ref [| false |]
|
||
|
|
|
||
|
|
let index_count w = Array.length !w
|
||
|
|
|
||
|
|
let reset_table a = Array.fill a 0 (Array.length a) false
|
||
|
|
|
||
|
|
let rec mark_used_indices tbl =
|
||
|
|
List.iter (function
|
||
|
|
| E.TSeq (l, _, _) -> mark_used_indices tbl l
|
||
|
|
| E.TExp (marks, _)
|
||
|
|
| E.TMatch marks ->
|
||
|
|
List.iter (fun (_, i) -> if i >= 0 then tbl.(i) <- true)
|
||
|
|
marks.Marks.marks)
|
||
|
|
|
||
|
|
let rec find_free tbl idx len =
|
||
|
|
if idx = len || not tbl.(idx) then idx else find_free tbl (idx + 1) len
|
||
|
|
|
||
|
|
let free_index tbl_ref l =
|
||
|
|
let tbl = !tbl_ref in
|
||
|
|
reset_table tbl;
|
||
|
|
mark_used_indices tbl l;
|
||
|
|
let len = Array.length tbl in
|
||
|
|
let idx = find_free tbl 0 len in
|
||
|
|
if idx = len then tbl_ref := Array.make (2 * len) false;
|
||
|
|
idx
|
||
|
|
|
||
|
|
(**** Computation of the next state ****)
|
||
|
|
|
||
|
|
let remove_matches = List.filter (function E.TMatch _ -> false | _ -> true)
|
||
|
|
|
||
|
|
let rec split_at_match_rec l' = function
|
||
|
|
| [] -> assert false
|
||
|
|
| E.TMatch _ :: r -> (List.rev l', remove_matches r)
|
||
|
|
| x :: r -> split_at_match_rec (x :: l') r
|
||
|
|
|
||
|
|
let split_at_match l = split_at_match_rec [] l
|
||
|
|
|
||
|
|
let rec remove_duplicates prev l y =
|
||
|
|
match l with
|
||
|
|
[] ->
|
||
|
|
([], prev)
|
||
|
|
| E.TMatch _ as x :: _ -> (* Truncate after first match *)
|
||
|
|
([x], prev)
|
||
|
|
| E.TSeq (l', x, kind) :: r ->
|
||
|
|
let (l'', prev') = remove_duplicates prev l' x in
|
||
|
|
let (r', prev'') = remove_duplicates prev' r y in
|
||
|
|
(E.tseq kind l'' x r', prev'')
|
||
|
|
| E.TExp (_marks, {def = Eps; _}) as e :: r ->
|
||
|
|
if List.memq y.id prev then
|
||
|
|
remove_duplicates prev r y
|
||
|
|
else
|
||
|
|
let (r', prev') = remove_duplicates (y.id :: prev) r y in
|
||
|
|
(e :: r', prev')
|
||
|
|
| E.TExp (_marks, x) as e :: r ->
|
||
|
|
if List.memq x.id prev then
|
||
|
|
remove_duplicates prev r y
|
||
|
|
else
|
||
|
|
let (r', prev') = remove_duplicates (x.id :: prev) r y in
|
||
|
|
(e :: r', prev')
|
||
|
|
|
||
|
|
let rec set_idx idx = function
|
||
|
|
| [] ->
|
||
|
|
[]
|
||
|
|
| E.TMatch marks :: r ->
|
||
|
|
E.TMatch (Marks.marks_set_idx marks idx) :: set_idx idx r
|
||
|
|
| E.TSeq (l', x, kind) :: r ->
|
||
|
|
E.TSeq (set_idx idx l', x, kind) :: set_idx idx r
|
||
|
|
| E.TExp (marks, x) :: r ->
|
||
|
|
E.TExp ((Marks.marks_set_idx marks idx), x) :: set_idx idx r
|
||
|
|
|
||
|
|
let filter_marks b e marks =
|
||
|
|
{marks with Marks.marks = List.filter (fun (i, _) -> i < b || i > e) marks.Marks.marks }
|
||
|
|
|
||
|
|
let rec delta_1 marks c ~next_cat ~prev_cat x rem =
|
||
|
|
(*Format.eprintf "%d@." x.id;*)
|
||
|
|
match x.def with
|
||
|
|
Cst s ->
|
||
|
|
if Cset.mem c s then E.texp marks eps_expr :: rem else rem
|
||
|
|
| Alt l ->
|
||
|
|
delta_2 marks c ~next_cat ~prev_cat l rem
|
||
|
|
| Seq (kind, y, z) ->
|
||
|
|
let y' = delta_1 marks c ~next_cat ~prev_cat y [] in
|
||
|
|
delta_seq c ~next_cat ~prev_cat kind y' z rem
|
||
|
|
| Rep (rep_kind, kind, y) ->
|
||
|
|
let y' = delta_1 marks c ~next_cat ~prev_cat y [] in
|
||
|
|
let (y'', marks') =
|
||
|
|
match
|
||
|
|
first
|
||
|
|
(function E.TMatch marks -> Some marks | _ -> None) y'
|
||
|
|
with
|
||
|
|
None -> (y', marks)
|
||
|
|
| Some marks' -> (remove_matches y', marks')
|
||
|
|
in
|
||
|
|
begin match rep_kind with
|
||
|
|
`Greedy -> E.tseq kind y'' x (E.TMatch marks' :: rem)
|
||
|
|
| `Non_greedy -> E.TMatch marks :: E.tseq kind y'' x rem
|
||
|
|
end
|
||
|
|
| Eps ->
|
||
|
|
E.TMatch marks :: rem
|
||
|
|
| Mark i ->
|
||
|
|
let marks = { marks with Marks.marks = (i, -1) :: List.remove_assq i marks.Marks.marks } in
|
||
|
|
E.TMatch marks :: rem
|
||
|
|
| Pmark i ->
|
||
|
|
let marks = { marks with Marks.pmarks = Pmark.Set.add i marks.Marks.pmarks } in
|
||
|
|
E.TMatch marks :: rem
|
||
|
|
| Erase (b, e) ->
|
||
|
|
E.TMatch (filter_marks b e marks) :: rem
|
||
|
|
| Before cat'' ->
|
||
|
|
if Category.intersect next_cat cat'' then E.TMatch marks :: rem else rem
|
||
|
|
| After cat'' ->
|
||
|
|
if Category.intersect prev_cat cat'' then E.TMatch marks :: rem else rem
|
||
|
|
|
||
|
|
and delta_2 marks c ~next_cat ~prev_cat l rem =
|
||
|
|
match l with
|
||
|
|
[] -> rem
|
||
|
|
| y :: r ->
|
||
|
|
delta_1 marks c ~next_cat ~prev_cat y
|
||
|
|
(delta_2 marks c ~next_cat ~prev_cat r rem)
|
||
|
|
|
||
|
|
and delta_seq c ~next_cat ~prev_cat kind y z rem =
|
||
|
|
match
|
||
|
|
first (function E.TMatch marks -> Some marks | _ -> None) y
|
||
|
|
with
|
||
|
|
None ->
|
||
|
|
E.tseq kind y z rem
|
||
|
|
| Some marks ->
|
||
|
|
match kind with
|
||
|
|
`Longest ->
|
||
|
|
E.tseq kind (remove_matches y) z
|
||
|
|
(delta_1 marks c ~next_cat ~prev_cat z rem)
|
||
|
|
| `Shortest ->
|
||
|
|
delta_1 marks c ~next_cat ~prev_cat z
|
||
|
|
(E.tseq kind (remove_matches y) z rem)
|
||
|
|
| `First ->
|
||
|
|
let (y', y'') = split_at_match y in
|
||
|
|
E.tseq kind y' z
|
||
|
|
(delta_1 marks c ~next_cat ~prev_cat z (E.tseq kind y'' z rem))
|
||
|
|
|
||
|
|
let rec delta_3 c ~next_cat ~prev_cat x rem =
|
||
|
|
match x with
|
||
|
|
E.TSeq (y, z, kind) ->
|
||
|
|
let y' = delta_4 c ~next_cat ~prev_cat y [] in
|
||
|
|
delta_seq c ~next_cat ~prev_cat kind y' z rem
|
||
|
|
| E.TExp (marks, e) ->
|
||
|
|
delta_1 marks c ~next_cat ~prev_cat e rem
|
||
|
|
| E.TMatch _ ->
|
||
|
|
x :: rem
|
||
|
|
|
||
|
|
and delta_4 c ~next_cat ~prev_cat l rem =
|
||
|
|
match l with
|
||
|
|
[] -> rem
|
||
|
|
| y :: r ->
|
||
|
|
delta_3 c ~next_cat ~prev_cat y
|
||
|
|
(delta_4 c ~next_cat ~prev_cat r rem)
|
||
|
|
|
||
|
|
let delta tbl_ref next_cat char st =
|
||
|
|
let prev_cat = st.State.category in
|
||
|
|
let (expr', _) =
|
||
|
|
remove_duplicates []
|
||
|
|
(delta_4 char ~next_cat ~prev_cat st.State.desc [])
|
||
|
|
eps_expr in
|
||
|
|
let idx = free_index tbl_ref expr' in
|
||
|
|
let expr'' = set_idx idx expr' in
|
||
|
|
State.mk idx next_cat expr''
|
||
|
|
|
||
|
|
(****)
|
||
|
|
|
||
|
|
let rec red_tr = function
|
||
|
|
| [] | [_] as l ->
|
||
|
|
l
|
||
|
|
| ((s1, st1) as tr1) :: ((s2, st2) as tr2) :: rem ->
|
||
|
|
if State.equal st1 st2 then
|
||
|
|
red_tr ((Cset.union s1 s2, st1) :: rem)
|
||
|
|
else
|
||
|
|
tr1 :: red_tr (tr2 :: rem)
|
||
|
|
|
||
|
|
let simpl_tr l =
|
||
|
|
List.sort
|
||
|
|
(fun (s1, _) (s2, _) -> compare s1 s2)
|
||
|
|
(red_tr (List.sort (fun (_, st1) (_, st2) -> State.compare st1 st2) l))
|
||
|
|
|
||
|
|
(****)
|
||
|
|
|
||
|
|
let prepend_deriv = List.fold_right (fun (s, x) l -> Cset.prepend s x l)
|
||
|
|
|
||
|
|
let rec restrict s = function
|
||
|
|
| [] -> []
|
||
|
|
| (s', x') :: rem ->
|
||
|
|
let s'' = Cset.inter s s' in
|
||
|
|
if Cset.is_empty s''
|
||
|
|
then restrict s rem
|
||
|
|
else (s'', x') :: restrict s rem
|
||
|
|
|
||
|
|
let rec remove_marks b e rem =
|
||
|
|
if b > e then rem else remove_marks b (e - 1) ((e, -2) :: rem)
|
||
|
|
|
||
|
|
let rec prepend_marks_expr m = function
|
||
|
|
| E.TSeq (l, e', s) -> E.TSeq (prepend_marks_expr_lst m l, e', s)
|
||
|
|
| E.TExp (m', e') -> E.TExp (Marks.merge m m', e')
|
||
|
|
| E.TMatch m' -> E.TMatch (Marks.merge m m')
|
||
|
|
|
||
|
|
and prepend_marks_expr_lst m l =
|
||
|
|
List.map (prepend_marks_expr m) l
|
||
|
|
|
||
|
|
let prepend_marks m =
|
||
|
|
List.map (fun (s, x) -> (s, prepend_marks_expr_lst m x))
|
||
|
|
|
||
|
|
let rec deriv_1 all_chars categories marks cat x rem =
|
||
|
|
match x.def with
|
||
|
|
| Cst s ->
|
||
|
|
Cset.prepend s [E.texp marks eps_expr] rem
|
||
|
|
| Alt l ->
|
||
|
|
deriv_2 all_chars categories marks cat l rem
|
||
|
|
| Seq (kind, y, z) ->
|
||
|
|
let y' = deriv_1 all_chars categories marks cat y [(all_chars, [])] in
|
||
|
|
deriv_seq all_chars categories cat kind y' z rem
|
||
|
|
| Rep (rep_kind, kind, y) ->
|
||
|
|
let y' = deriv_1 all_chars categories marks cat y [(all_chars, [])] in
|
||
|
|
List.fold_right
|
||
|
|
(fun (s, z) rem ->
|
||
|
|
let (z', marks') =
|
||
|
|
match
|
||
|
|
first
|
||
|
|
(function E.TMatch marks -> Some marks | _ -> None)
|
||
|
|
z
|
||
|
|
with
|
||
|
|
None -> (z, marks)
|
||
|
|
| Some marks' -> (remove_matches z, marks')
|
||
|
|
in
|
||
|
|
Cset.prepend s
|
||
|
|
(match rep_kind with
|
||
|
|
`Greedy -> E.tseq kind z' x [E.TMatch marks']
|
||
|
|
| `Non_greedy -> E.TMatch marks :: E.tseq kind z' x [])
|
||
|
|
rem)
|
||
|
|
y' rem
|
||
|
|
| Eps ->
|
||
|
|
Cset.prepend all_chars [E.TMatch marks] rem
|
||
|
|
| Mark i ->
|
||
|
|
Cset.prepend all_chars [E.TMatch {marks with Marks.marks = ((i, -1) :: List.remove_assq i marks.Marks.marks)}] rem
|
||
|
|
| Pmark _ ->
|
||
|
|
Cset.prepend all_chars [E.TMatch marks] rem
|
||
|
|
| Erase (b, e) ->
|
||
|
|
Cset.prepend all_chars
|
||
|
|
[E.TMatch {marks with Marks.marks = (remove_marks b e (filter_marks b e marks).Marks.marks)}] rem
|
||
|
|
| Before cat' ->
|
||
|
|
Cset.prepend (List.assq cat' categories) [E.TMatch marks] rem
|
||
|
|
| After cat' ->
|
||
|
|
if Category.intersect cat cat' then Cset.prepend all_chars [E.TMatch marks] rem else rem
|
||
|
|
|
||
|
|
and deriv_2 all_chars categories marks cat l rem =
|
||
|
|
match l with
|
||
|
|
[] -> rem
|
||
|
|
| y :: r -> deriv_1 all_chars categories marks cat y
|
||
|
|
(deriv_2 all_chars categories marks cat r rem)
|
||
|
|
|
||
|
|
and deriv_seq all_chars categories cat kind y z rem =
|
||
|
|
if
|
||
|
|
List.exists
|
||
|
|
(fun (_s, xl) ->
|
||
|
|
List.exists (function E.TMatch _ -> true | _ -> false) xl)
|
||
|
|
y
|
||
|
|
then
|
||
|
|
let z' = deriv_1 all_chars categories Marks.empty cat z [(all_chars, [])] in
|
||
|
|
List.fold_right
|
||
|
|
(fun (s, y) rem ->
|
||
|
|
match
|
||
|
|
first (function E.TMatch marks -> Some marks | _ -> None)
|
||
|
|
y
|
||
|
|
with
|
||
|
|
None ->
|
||
|
|
Cset.prepend s (E.tseq kind y z []) rem
|
||
|
|
| Some marks ->
|
||
|
|
let z'' = prepend_marks marks z' in
|
||
|
|
match kind with
|
||
|
|
`Longest ->
|
||
|
|
Cset.prepend s (E.tseq kind (remove_matches y) z []) (
|
||
|
|
prepend_deriv (restrict s z'') rem)
|
||
|
|
| `Shortest ->
|
||
|
|
prepend_deriv (restrict s z'') (
|
||
|
|
Cset.prepend s (E.tseq kind (remove_matches y) z []) rem)
|
||
|
|
| `First ->
|
||
|
|
let (y', y'') = split_at_match y in
|
||
|
|
Cset.prepend s (E.tseq kind y' z []) (
|
||
|
|
prepend_deriv (restrict s z'') (
|
||
|
|
Cset.prepend s (E.tseq kind y'' z []) rem)))
|
||
|
|
y rem
|
||
|
|
else
|
||
|
|
List.fold_right
|
||
|
|
(fun (s, xl) rem -> Cset.prepend s (E.tseq kind xl z []) rem) y rem
|
||
|
|
|
||
|
|
let rec deriv_3 all_chars categories cat x rem =
|
||
|
|
match x with
|
||
|
|
E.TSeq (y, z, kind) ->
|
||
|
|
let y' = deriv_4 all_chars categories cat y [(all_chars, [])] in
|
||
|
|
deriv_seq all_chars categories cat kind y' z rem
|
||
|
|
| E.TExp (marks, e) ->
|
||
|
|
deriv_1 all_chars categories marks cat e rem
|
||
|
|
| E.TMatch _ ->
|
||
|
|
Cset.prepend all_chars [x] rem
|
||
|
|
|
||
|
|
and deriv_4 all_chars categories cat l rem =
|
||
|
|
match l with
|
||
|
|
[] -> rem
|
||
|
|
| y :: r -> deriv_3 all_chars categories cat y
|
||
|
|
(deriv_4 all_chars categories cat r rem)
|
||
|
|
|
||
|
|
let deriv tbl_ref all_chars categories st =
|
||
|
|
let der = deriv_4 all_chars categories st.State.category st.State.desc
|
||
|
|
[(all_chars, [])] in
|
||
|
|
simpl_tr (
|
||
|
|
List.fold_right (fun (s, expr) rem ->
|
||
|
|
let (expr', _) = remove_duplicates [] expr eps_expr in
|
||
|
|
(*
|
||
|
|
Format.eprintf "@[<3>@[%a@]: %a / %a@]@." Cset.print s print_state expr print_state expr';
|
||
|
|
*)
|
||
|
|
let idx = free_index tbl_ref expr' in
|
||
|
|
let expr'' = set_idx idx expr' in
|
||
|
|
List.fold_right (fun (cat', s') rem ->
|
||
|
|
let s'' = Cset.inter s s' in
|
||
|
|
if Cset.is_empty s''
|
||
|
|
then rem
|
||
|
|
else (s'', State.mk idx cat' expr'') :: rem)
|
||
|
|
categories rem) der [])
|
||
|
|
|
||
|
|
(****)
|
||
|
|
|
||
|
|
let flatten_match m =
|
||
|
|
let ma = List.fold_left (fun ma (i, _) -> max ma i) (-1) m in
|
||
|
|
let res = Array.make (ma + 1) (-1) in
|
||
|
|
List.iter (fun (i, v) -> res.(i) <- v) m;
|
||
|
|
res
|
||
|
|
|
||
|
|
let status s =
|
||
|
|
match s.State.status with
|
||
|
|
Some st ->
|
||
|
|
st
|
||
|
|
| None ->
|
||
|
|
let st =
|
||
|
|
match s.State.desc with
|
||
|
|
[] -> Failed
|
||
|
|
| E.TMatch m :: _ -> Match (flatten_match m.Marks.marks, m.Marks.pmarks)
|
||
|
|
| _ -> Running
|
||
|
|
in
|
||
|
|
s.State.status <- Some st;
|
||
|
|
st
|