···11+(* From dune-glob library
22+33+The MIT License
44+55+Copyright (c) 2016 Jane Street Group, LLC <opensource@janestreet.com>
66+77+Permission is hereby granted, free of charge, to any person obtaining a copy
88+of this software and associated documentation files (the "Software"), to deal
99+in the Software without restriction, including without limitation the rights
1010+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1111+copies of the Software, and to permit persons to whom the Software is
1212+furnished to do so, subject to the following conditions:
1313+1414+The above copyright notice and this permission notice shall be included in all
1515+copies or substantial portions of the Software.
1616+1717+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1818+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1919+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2020+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2121+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2222+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2323+SOFTWARE. *)
2424+2525+type t = Re of { re : Re.re; repr : string } | Literal of string
2626+2727+let test t s =
2828+ match t with
2929+ | Literal t -> String.equal t s
3030+ | Re { re; repr = _ } -> Re.execp re s
3131+3232+let empty = Re { re = Re.compile Re.empty; repr = "\000" }
3333+let universal = Re { re = Re.compile (Re.rep Re.any); repr = "**" }
3434+3535+let of_string_result repr =
3636+ Lexer.parse_string repr
3737+ |> Result.map (function
3838+ | Lexer.Literal s -> Literal s
3939+ | Re re -> Re { re = Re.compile re; repr })
4040+4141+let of_string repr =
4242+ match of_string_result repr with
4343+ | Error (_, msg) -> invalid_arg (Printf.sprintf "invalid glob: :%s" msg)
4444+ | Ok t -> t
4545+4646+let to_string t = match t with Re { repr; re = _ } -> repr | Literal s -> s
4747+let hash t = String.hash (to_string t)
+21
src/lib/glob/glob.mli
···11+(** Simple glob support library. *)
22+33+type t
44+55+val empty : t
66+(** A glob that matches nothing *)
77+88+val universal : t
99+(** A glob that matches anything (including the strings starting with a ".") *)
1010+1111+val test : t -> string -> bool
1212+(** Tests if string matches the glob. *)
1313+1414+val to_string : t -> string
1515+(** Returns textual representation of a glob. *)
1616+1717+val of_string : string -> t
1818+(** Converts string to glob. Throws [Invalid_argument] exception if string is
1919+ not a valid glob. *)
2020+2121+val hash : t -> int
+91
src/lib/glob/lexer.mll
···11+{
22+open Re
33+44+let string_of_list chars =
55+ let s = Bytes.make (List.length chars) '0' in
66+ List.iteri (fun i c -> Bytes.set s i c) chars;
77+ Bytes.to_string s
88+99+type t =
1010+ | Literal of string
1111+ | Re of Re.t
1212+1313+let no_slash = diff any (char '/')
1414+let no_slash_no_dot = diff any (set "./")
1515+1616+type stack =
1717+ | Bottom
1818+ | Lbrace of stack
1919+ | Char of char * stack
2020+ | Re of Re.t * stack
2121+ | Comma of stack
2222+2323+let make_group st =
2424+ let rec loop current_re full_res st =
2525+ match st with
2626+ | Bottom -> failwith "'}' without opening '{'"
2727+ | Re (re, st) -> loop (re :: current_re) full_res st
2828+ | Char (c, st) -> loop (char c :: current_re) full_res st
2929+ | Comma st -> loop [] (seq current_re :: full_res) st
3030+ | Lbrace st -> Re (alt (seq current_re :: full_res), st)
3131+ in
3232+ loop [] [] st
3333+3434+let finalize st =
3535+ let rec loop acc st =
3636+ match st with
3737+ | Bottom -> seq (start :: acc)
3838+ | Re (re, st) -> loop (re :: acc) st
3939+ | Char (c, st) -> loop (char c :: acc) st
4040+ | Comma st -> loop (char ',' :: acc) st
4141+ | Lbrace _ -> failwith "unclosed '{'"
4242+ in
4343+ let rec try_str (acc : char list) st =
4444+ match st with
4545+ | Bottom -> Literal (string_of_list acc)
4646+ | Comma st -> try_str (',' :: acc) st
4747+ | Char (c, st) -> try_str (c :: acc) st
4848+ | st ->
4949+ let re =
5050+ let re = [stop] in
5151+ match acc with
5252+ | [] -> re
5353+ | _ :: _ -> str (string_of_list acc) :: re
5454+ in
5555+ Re (loop re st)
5656+ in
5757+ try_str [] st
5858+}
5959+6060+rule initial = parse
6161+ (* | "**" { glob (Re (rep any, Bottom)) lexbuf } *)
6262+ | "*" { glob (Re (rep any, Bottom)) lexbuf }
6363+ | "" { glob Bottom lexbuf }
6464+6565+and glob st = parse
6666+ | eof
6767+ | '\\' eof { finalize st }
6868+ | '\\' (_ as c) { glob (Char (c , st)) lexbuf }
6969+ | "**" { glob (Re (seq [no_slash_no_dot; rep no_slash] , st)) lexbuf }
7070+ | '*' { glob (Re (rep no_slash , st)) lexbuf }
7171+ | '?' { glob (Re (no_slash , st)) lexbuf }
7272+ | '{' { glob (Lbrace st ) lexbuf }
7373+ | ',' { glob (Comma st ) lexbuf }
7474+ | '}' { glob (make_group st) lexbuf }
7575+ | '[' { char_set st lexbuf }
7676+ | ']' { failwith "']' without opening '['" }
7777+ | _ as c { glob (Char (c , st)) lexbuf }
7878+7979+and char_set st = parse
8080+ | '!' ([^ ']']* as s) "]" { glob (Re (diff any (set s) , st)) lexbuf }
8181+ | ([^ ']']* as s) "]" { glob (Re (set s , st)) lexbuf }
8282+ | "" { failwith "unclosed character set" }
8383+8484+{
8585+ let parse_string s =
8686+ let lb = Lexing.from_string s in
8787+ match initial lb with
8888+ | re -> Result.Ok re
8989+ | exception Failure msg ->
9090+ Error (Lexing.lexeme_start lb, msg)
9191+}
+19
src/lib/import.ml
···5959end
60606161let yojson_pp = Yojson.Safe.pretty_print ~std:true
6262+6363+module String = struct
6464+ include String
6565+6666+ let cut_prefix ~prefix s =
6767+ match Astring.String.cut ~sep:prefix s with
6868+ | Some ("", rest) -> Some rest
6969+ | _ -> None
7070+7171+ let cut_suffix ~suffix s =
7272+ match Astring.String.cut ~sep:suffix s with
7373+ | Some (start, "") -> Some start
7474+ | _ -> None
7575+end
7676+7777+module Glob = struct
7878+ let test ~pattern s = List.filter Glob.(test (of_string pattern)) s
7979+ let glob_dir ~pattern dir = test ~pattern (Eio.Path.read_dir dir)
8080+end