This commit is contained in:
swrup 2025-11-11 02:07:51 +01:00
parent aa2ff7b2f0
commit 2f3113f55d
11742 changed files with 1223940 additions and 0 deletions

View file

@ -0,0 +1,337 @@
(*
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
*)
module Re = Core
exception Parse_error = Parse_buffer.Parse_error
type enclosed =
| Char of char
| Range of char * char
type piece =
| Exactly of char
| Any_of of enclosed list
| Any_but of enclosed list
| One
| Many
| ManyMany
type t = piece list
let of_string ~double_asterisk s : t =
let buf = Parse_buffer.create s in
let eos () = Parse_buffer.eos buf in
let read c = Parse_buffer.accept buf c in
let char () =
ignore (read '\\' : bool);
if eos () then raise Parse_error;
Parse_buffer.get buf
in
let enclosed () : enclosed list =
let rec loop s =
(* This returns the list in reverse order, but order isn't important
anyway *)
if s <> [] && read ']'
then s
else (
let c = char () in
if not (read '-')
then loop (Char c :: s)
else if read ']'
then Char c :: Char '-' :: s
else (
let c' = char () in
loop (Range (c, c') :: s)))
in
loop []
in
let piece acc =
if double_asterisk && Parse_buffer.accept_s buf "/**"
then ManyMany :: (if eos () then Exactly '/' :: acc else acc)
else if read '*'
then (if double_asterisk && read '*' then ManyMany else Many) :: acc
else if read '?'
then One :: acc
else if not (read '[')
then Exactly (char ()) :: acc
else if read '^' || read '!'
then Any_but (enclosed ()) :: acc
else Any_of (enclosed ()) :: acc
in
let rec loop pieces = if eos () then List.rev pieces else loop (piece pieces) in
loop []
;;
let mul l l' = List.flatten (List.map (fun s -> List.map (fun s' -> s ^ s') l') l)
let explode str =
let l = String.length str in
let rec expl inner s i acc beg =
if i >= l
then (
if inner then raise Parse_error;
mul beg [ String.sub str s (i - s) ], i)
else (
match str.[i] with
| '\\' -> expl inner s (i + 2) acc beg
| '{' ->
let t, i' = expl true (i + 1) (i + 1) [] [ "" ] in
expl inner i' i' acc (mul beg (mul [ String.sub str s (i - s) ] t))
| ',' when inner ->
expl inner (i + 1) (i + 1) (mul beg [ String.sub str s (i - s) ] @ acc) [ "" ]
| '}' when inner -> mul beg [ String.sub str s (i - s) ] @ acc, i + 1
| _ -> expl inner s (i + 1) acc beg)
in
List.rev (fst (expl false 0 0 [] [ "" ]))
;;
module State = struct
type t =
{ re_pieces : Re.t list (* last piece at head of list. *)
; remaining : piece list (* last piece at tail of list. *)
; am_at_start_of_pattern : bool (* true at start of pattern *)
; am_at_start_of_component : bool
(* true at start of pattern or immediately
after '/' *)
; pathname : bool
; match_backslashes : bool
; period : bool
}
let create ~period ~pathname ~match_backslashes remaining =
{ re_pieces = []
; am_at_start_of_pattern = true
; am_at_start_of_component = true
; pathname
; match_backslashes
; period
; remaining
}
;;
let explicit_period t =
t.period && (t.am_at_start_of_pattern || (t.am_at_start_of_component && t.pathname))
;;
let explicit_slash t = t.pathname
let slashes t = if t.match_backslashes then [ '/'; '\\' ] else [ '/' ]
let append ?(am_at_start_of_component = false) t piece =
{ t with
re_pieces = piece :: t.re_pieces
; am_at_start_of_pattern = false
; am_at_start_of_component
}
;;
let to_re t = Re.seq (List.rev t.re_pieces)
let next t =
match t.remaining with
| [] -> None
| piece :: remaining -> Some (piece, { t with remaining })
;;
end
let one ~explicit_slash ~slashes ~explicit_period =
Re.compl
(List.concat
[ (if explicit_slash then List.map Re.char slashes else [])
; (if explicit_period then [ Re.char '.' ] else [])
])
;;
let enclosed enclosed =
match enclosed with
| Char c -> Re.char c
| Range (low, high) -> Re.rg low high
;;
let enclosed_set ~explicit_slash ~slashes ~explicit_period kind set =
let set = List.map enclosed set in
let enclosure =
match kind with
| `Any_of -> Re.alt set
| `Any_but -> Re.compl set
in
Re.inter [ enclosure; one ~explicit_slash ~slashes ~explicit_period ]
;;
let exactly state c =
let slashes = State.slashes state in
let am_at_start_of_component = List.mem c slashes in
let chars = if am_at_start_of_component then slashes else [ c ] in
State.append state (Re.alt (List.map Re.char chars)) ~am_at_start_of_component
;;
let many_many state =
let explicit_period = state.State.period && state.State.pathname in
let first_explicit_period = State.explicit_period state in
let slashes = State.slashes state in
let match_component ~explicit_period =
Re.seq
[ one ~explicit_slash:true ~slashes ~explicit_period
; Re.rep (one ~explicit_slash:true ~slashes ~explicit_period:false)
]
in
(* We must match components individually when [period] flag is set,
making sure to not match ["foo/.bar"]. *)
State.append
state
(Re.seq
[ Re.opt (match_component ~explicit_period:first_explicit_period)
; Re.rep
(Re.seq
[ Re.alt (List.map Re.char slashes)
; Re.opt (match_component ~explicit_period)
])
])
;;
let many (state : State.t) =
let explicit_slash = State.explicit_slash state in
let explicit_period = State.explicit_period state in
let slashes = State.slashes state in
(* Whether we must explicitly match period depends on the surrounding
characters, but slashes are easy to explicit match. This conditional
splits out some simple cases. *)
if not explicit_period
then State.append state (Re.rep (one ~explicit_slash ~slashes ~explicit_period))
else if not explicit_slash
then
(* In this state, we explicitly match periods only at the very beginning *)
State.append
state
(Re.opt
(Re.seq
[ one ~explicit_slash:false ~slashes ~explicit_period
; Re.rep (one ~explicit_slash:false ~slashes ~explicit_period:false)
]))
else (
let not_empty =
Re.seq
[ one ~explicit_slash:true ~slashes ~explicit_period:true
; Re.rep (one ~explicit_slash:true ~slashes ~explicit_period:false)
]
in
(* [maybe_empty] is the default translation of Many, except in some special
cases. *)
let maybe_empty = Re.opt not_empty in
let enclosed_set state kind set =
State.append
state
(Re.alt
[ enclosed_set kind set ~explicit_slash:true ~slashes ~explicit_period:true
; Re.seq
[ not_empty
; (* Since [not_empty] matched, subsequent dots are not leading. *)
enclosed_set
kind
set
~explicit_slash:true
~slashes
~explicit_period:false
]
])
in
let rec lookahead state =
match State.next state with
| None -> State.append state maybe_empty
(* glob ** === glob * . *)
| Some (Many, state) -> lookahead state
| Some (Exactly c, state) ->
let state = State.append state (if c = '.' then not_empty else maybe_empty) in
exactly state c
(* glob *? === glob ?* *)
| Some (One, state) -> State.append state not_empty
| Some (Any_of enclosed, state) -> enclosed_set state `Any_of enclosed
| Some (Any_but enclosed, state) -> enclosed_set state `Any_but enclosed
(* * then ** === ** *)
| Some (ManyMany, state) -> many_many state
in
lookahead state)
;;
let piece state piece =
let explicit_slash = State.explicit_slash state in
let explicit_period = State.explicit_period state in
let slashes = State.slashes state in
match piece with
| One -> State.append state (one ~explicit_slash ~slashes ~explicit_period)
| Many -> many state
| Any_of enclosed ->
State.append
state
(enclosed_set `Any_of ~explicit_slash ~slashes ~explicit_period enclosed)
| Any_but enclosed ->
State.append
state
(enclosed_set `Any_but ~explicit_slash ~slashes ~explicit_period enclosed)
| Exactly c -> exactly state c
| ManyMany -> many_many state
;;
let glob ~pathname ~match_backslashes ~period glob =
let rec loop state =
match State.next state with
| None -> State.to_re state
| Some (p, state) -> loop (piece state p)
in
loop (State.create ~pathname ~match_backslashes ~period glob)
;;
let glob
?(anchored = false)
?(pathname = true)
?(match_backslashes = false)
?(period = true)
?(expand_braces = false)
?(double_asterisk = true)
s
=
let to_re s =
let re = glob ~pathname ~match_backslashes ~period (of_string ~double_asterisk s) in
if anchored then Re.whole_string re else re
in
if expand_braces then Re.alt (List.map to_re (explode s)) else to_re s
;;
let glob_result
?anchored
?pathname
?match_backslashes
?period
?expand_braces
?double_asterisk
s
=
match
glob ?anchored ?pathname ?match_backslashes ?period ?expand_braces ?double_asterisk s
with
| re -> Ok re
| exception Parse_error -> Error `Parse_error
;;
let glob' ?anchored period s = glob ?anchored ~period s
let globx ?anchored s = glob ?anchored ~expand_braces:true s
let globx' ?anchored period s = glob ?anchored ~expand_braces:true ~period s