This commit is contained in:
parent
aa2ff7b2f0
commit
2f3113f55d
11742 changed files with 1223940 additions and 0 deletions
7
unikernel/duniverse/stringext/CHANGES.md
Normal file
7
unikernel/duniverse/stringext/CHANGES.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# 1.6.0
|
||||
|
||||
* Port to dune and opam 2.0 metadata format
|
||||
|
||||
# 1.5.0 and earlier
|
||||
|
||||
No changelogs recorded.
|
||||
21
unikernel/duniverse/stringext/LICENSE.md
Normal file
21
unikernel/duniverse/stringext/LICENSE.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017 Rudi Grinberg
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
10
unikernel/duniverse/stringext/Makefile
Normal file
10
unikernel/duniverse/stringext/Makefile
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
.PHONY: all clean test
|
||||
|
||||
all:
|
||||
dune build
|
||||
|
||||
test:
|
||||
dune runtest
|
||||
|
||||
clean:
|
||||
rm -rf _build *.install
|
||||
78
unikernel/duniverse/stringext/README.md
Normal file
78
unikernel/duniverse/stringext/README.md
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
## Stringext -- Extra string functions fo OCaml
|
||||
|
||||
Extra string functions for OCaml. Mainly splitting. All functions are in the
|
||||
`Stringext` module. Here's a snippet of most useful functions out of the mli:
|
||||
|
||||
## Api Documentation
|
||||
|
||||
```ocaml
|
||||
(** string_after [s] [n] returns the substring of [s] that is after
|
||||
character [n] *)
|
||||
val string_after : string -> int -> string
|
||||
|
||||
(** equivalent to [Str.quote] *)
|
||||
val quote : string -> string
|
||||
|
||||
(** split [?max] [s] [~on] splits [s] on every [on] occurence upto
|
||||
[max] number of items if [max] is specified. [max] is assumed to
|
||||
be a small number if specified. To not cause stack overflows *)
|
||||
val split : ?max:int -> string -> on:char -> string list
|
||||
|
||||
(** full_split [s] [~on] will split [s] on every occurence
|
||||
of [on] but will add the separators between the tokens. Maintains
|
||||
the invariant:
|
||||
|
||||
String.concat (full_split s ~on) =s *)
|
||||
val full_split : string -> on:char -> string list
|
||||
|
||||
(** Trims spaces on the left of the string. In case no trimming is needed
|
||||
the same string is returned without copying *)
|
||||
val trim_left : string -> string
|
||||
|
||||
(** split_strim_left [s] [~on] [~trim] splits [s] on every character
|
||||
in [on]. Characters in [trim] are trimmed from the left of every
|
||||
result element *)
|
||||
val split_trim_left : string -> on:string -> trim:string -> string list
|
||||
|
||||
val of_char : char -> string
|
||||
|
||||
val of_list : char list -> string
|
||||
|
||||
val to_list : string -> char list
|
||||
|
||||
val to_array : string -> char array
|
||||
|
||||
val of_array : char array -> string
|
||||
|
||||
val find_from : ?start:int -> string -> pattern:string -> int option
|
||||
|
||||
val replace_all : string -> pattern:string -> with_:string -> string
|
||||
|
||||
val replace_all_assoc : string -> (string * string) list -> string
|
||||
|
||||
val cut : string -> on:string -> (string * string) option
|
||||
(** [String.cut on s] is either the pair [Some (l,r)] of the two
|
||||
(possibly empty) substrings of [s] that are delimited by the first
|
||||
match of the non empty onarator string [on] or [None] if [on]
|
||||
can't be matched in [s]. Matching starts from the beginning of [s].
|
||||
|
||||
The invariant [l ^ on ^ r = s] holds.
|
||||
|
||||
@raise Invalid_argument if [on] is the empty string. *)
|
||||
|
||||
val rcut : string -> on:string -> (string * string) option
|
||||
(** [String.rcut on s] is like {!cut} but the matching is done backwards
|
||||
starting from the end of [s].
|
||||
|
||||
@raise Invalid_argument if [on] is the empty string. *)
|
||||
|
||||
val chop_prefix : string -> prefix:string -> string option
|
||||
|
||||
val drop : string -> int -> string
|
||||
|
||||
val take : string -> int -> string
|
||||
|
||||
(** [trim_left_sub s ~pos ~len ~chars] Trim all characters inside [chars]
|
||||
from [s] starting from [pos] and up to [len] *)
|
||||
val trim_left_sub : string -> pos:int -> len:int -> chars:string -> string
|
||||
```
|
||||
3
unikernel/duniverse/stringext/dune-project
Normal file
3
unikernel/duniverse/stringext/dune-project
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
(lang dune 1.0)
|
||||
(name stringext)
|
||||
(version 1.6.0)
|
||||
4
unikernel/duniverse/stringext/lib/dune
Normal file
4
unikernel/duniverse/stringext/lib/dune
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
(library
|
||||
(name stringext)
|
||||
(public_name stringext)
|
||||
(wrapped false))
|
||||
337
unikernel/duniverse/stringext/lib/stringext.ml
Normal file
337
unikernel/duniverse/stringext/lib/stringext.ml
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
open String
|
||||
|
||||
let string_after s n = String.sub s n (String.length s - n)
|
||||
|
||||
let quote s =
|
||||
let len = String.length s in
|
||||
let buf = Buffer.create (2 * len) in
|
||||
for i = 0 to len - 1 do
|
||||
match s.[i] with
|
||||
'[' | ']' | '*' | '.' | '\\' | '?' | '+' | '^' | '$' as c ->
|
||||
Buffer.add_char buf '\\';
|
||||
Buffer.add_char buf c
|
||||
| c -> Buffer.add_char buf c
|
||||
done;
|
||||
Buffer.contents buf
|
||||
|
||||
|
||||
(* Not tail recursive for "performance", please choose low values for
|
||||
[max]. The idea is that max is always small because it's hard
|
||||
code *)
|
||||
let split_char_bounded str ~on ~max =
|
||||
if str = "" then []
|
||||
else if max = 1 then [str]
|
||||
else
|
||||
let rec loop offset tokens =
|
||||
if tokens = max - 1
|
||||
then [sub str offset (length str - offset)]
|
||||
else
|
||||
try
|
||||
let index = index_from str offset on in
|
||||
if index = offset then
|
||||
""::(loop (offset + 1) (tokens + 1))
|
||||
else
|
||||
let token = String.sub str offset (index - offset) in
|
||||
token::(loop (index + 1) (tokens + 1))
|
||||
with Not_found -> [sub str offset (length str - offset)]
|
||||
in loop 0 0
|
||||
|
||||
let split_char_unbounded str ~on =
|
||||
if str = "" then []
|
||||
else
|
||||
let rec loop acc offset =
|
||||
try begin
|
||||
let index = rindex_from str offset on in
|
||||
if index = offset then
|
||||
loop (""::acc) (index - 1)
|
||||
else
|
||||
let token = sub str (index + 1) (offset - index) in
|
||||
loop (token::acc) (index - 1)
|
||||
end
|
||||
with Not_found -> (sub str 0 (offset + 1))::acc
|
||||
in loop [] (length str - 1)
|
||||
|
||||
let of_char = String.make 1
|
||||
|
||||
let full_split str ~on =
|
||||
if str = "" then []
|
||||
else
|
||||
let sep = of_char on in
|
||||
let rec loop acc offset =
|
||||
try begin
|
||||
let index = rindex_from str offset on in
|
||||
if index = offset then
|
||||
loop (sep::acc) (index - 1)
|
||||
else
|
||||
let token = sub str (index + 1) (offset - index) in
|
||||
loop (sep::token::acc) (index - 1)
|
||||
end
|
||||
with Not_found ->
|
||||
if offset >= 0
|
||||
then (sub str 0 (offset + 1))::acc
|
||||
else acc
|
||||
in loop [] (length str - 1)
|
||||
|
||||
(* copying core's convention for String.split but with an optional max
|
||||
argument *)
|
||||
let split ?max s ~on =
|
||||
match max with
|
||||
| None -> split_char_unbounded s ~on
|
||||
| Some max -> (* assert (max < 100); *)
|
||||
split_char_bounded s ~on ~max
|
||||
|
||||
let rindex_from_on s ~offset ~on =
|
||||
let rec loop i =
|
||||
if i < 0 then raise Not_found
|
||||
else if String.contains on s.[i] then i
|
||||
else loop (i - 1)
|
||||
in loop offset
|
||||
|
||||
let trim_left_sub s ~pos ~len ~chars =
|
||||
let start_pos =
|
||||
let final = pos + len in
|
||||
let rec loop last_char i =
|
||||
if i = final then last_char
|
||||
else if String.contains chars s.[i] then loop (i + 1) (i + 1)
|
||||
else last_char
|
||||
in loop pos pos
|
||||
in
|
||||
let new_len = len - (start_pos - pos) in
|
||||
String.sub s start_pos new_len
|
||||
|
||||
let split_trim_left str ~on ~trim =
|
||||
if str = "" then []
|
||||
else
|
||||
let rec loop acc offset =
|
||||
try begin
|
||||
let index = rindex_from_on str ~offset ~on in
|
||||
if index = offset then
|
||||
loop (""::acc) (index - 1)
|
||||
else
|
||||
let token = trim_left_sub str ~pos:(index + 1)
|
||||
~len:(offset - index) ~chars:trim in
|
||||
loop (token::acc) (index - 1)
|
||||
end
|
||||
with Not_found ->
|
||||
(trim_left_sub str ~pos:0 ~len:(offset + 1) ~chars:trim)::acc
|
||||
in loop [] (length str - 1)
|
||||
|
||||
exception Found_int of int
|
||||
|
||||
let first_char_ne s c =
|
||||
String.length s > 0 && s.[0] <> c
|
||||
|
||||
let trim_left s =
|
||||
if first_char_ne s ' ' then s
|
||||
else
|
||||
let len = String.length s in
|
||||
try
|
||||
for i=0 to len - 1 do
|
||||
if s.[i] <> ' ' then raise (Found_int i)
|
||||
done;
|
||||
""
|
||||
with Found_int non_space ->
|
||||
sub s non_space (len - non_space)
|
||||
|
||||
let substr_eq ?(start=0) s ~pattern =
|
||||
try
|
||||
for i = 0 to String.length pattern - 1 do
|
||||
if s.[i + start] <> pattern.[i] then raise Exit
|
||||
done;
|
||||
true
|
||||
with _ -> false
|
||||
|
||||
let find_from ?(start=0) str ~pattern =
|
||||
try
|
||||
for i = start to (String.length str) - (String.length pattern) do
|
||||
if substr_eq ~start:i str ~pattern then
|
||||
raise (Found_int i)
|
||||
done;
|
||||
None
|
||||
with
|
||||
| Found_int i -> Some i
|
||||
| _ -> None
|
||||
|
||||
let find_min l ~f =
|
||||
let rec loop x fx = function
|
||||
| [] -> Some (x, fx)
|
||||
| x'::xs ->
|
||||
let fx' = f x' in
|
||||
if fx' < fx then loop x' fx' xs
|
||||
else loop x fx xs
|
||||
in
|
||||
match l with
|
||||
| [] -> None
|
||||
| x::xs -> loop x (f x) xs
|
||||
|
||||
let replace_all str ~pattern ~with_ =
|
||||
let (slen, plen) = String.(length str, length pattern) in
|
||||
let buf = Buffer.create slen in
|
||||
let rec loop i =
|
||||
match find_from ~start:i str ~pattern with
|
||||
| None ->
|
||||
Buffer.add_substring buf str i (slen - i);
|
||||
Buffer.contents buf
|
||||
| Some j ->
|
||||
Buffer.add_substring buf str i (j - i);
|
||||
Buffer.add_string buf with_;
|
||||
loop (j + plen)
|
||||
in loop 0
|
||||
|
||||
exception Found_replace of int * string * string
|
||||
|
||||
let replace_all_assoc str tbl =
|
||||
let slen = String.length str in
|
||||
let buf = Buffer.create slen in
|
||||
let rec loop i =
|
||||
if i >= slen then Buffer.contents buf
|
||||
else
|
||||
let r =
|
||||
try
|
||||
let found = ref false in
|
||||
let e =
|
||||
find_min tbl ~f:(fun (pattern, with_) ->
|
||||
match find_from ~start:i str ~pattern with
|
||||
| None -> max_int
|
||||
| Some j when j = i -> raise (Found_replace (j, pattern, with_))
|
||||
| Some j -> found := true; j)
|
||||
in
|
||||
match e with
|
||||
| None -> None
|
||||
| Some ((pattern, with_), j) when !found -> Some (j, pattern, with_)
|
||||
| Some _ -> None
|
||||
with Found_replace (j, pattern, with_) -> Some (j, pattern, with_)
|
||||
in
|
||||
match r with
|
||||
| None ->
|
||||
Buffer.add_substring buf str i (slen - i);
|
||||
Buffer.contents buf
|
||||
| Some (j, pattern, with_) ->
|
||||
Buffer.add_substring buf str i (j - i);
|
||||
Buffer.add_string buf with_;
|
||||
loop (j + String.length pattern)
|
||||
in loop 0
|
||||
|
||||
let iteri f l =
|
||||
let rec loop i = function
|
||||
| [] -> ()
|
||||
| x::xs -> (f i x); loop (succ i) xs
|
||||
in loop 0 l
|
||||
|
||||
let of_list xs =
|
||||
let l = List.length xs in
|
||||
let s = Bytes.create l in
|
||||
iteri (fun i c -> Bytes.set s i c) xs;
|
||||
Bytes.unsafe_to_string s
|
||||
|
||||
let to_list s =
|
||||
let rec loop acc i =
|
||||
if i = -1 then acc
|
||||
else
|
||||
loop (s.[i] :: acc) (pred i)
|
||||
in loop [] (String.length s - 1)
|
||||
|
||||
let of_array a =
|
||||
let len = Array.length a in
|
||||
let bytes = Bytes.create len in
|
||||
for i = 0 to len - 1 do
|
||||
Bytes.set bytes i a.(i)
|
||||
done;
|
||||
Bytes.unsafe_to_string bytes
|
||||
|
||||
let to_array s = Array.init (String.length s) (String.get s)
|
||||
|
||||
(* ripped off from one of dbuenzli's libs *)
|
||||
let cut s ~on =
|
||||
let sep_max = length on - 1 in
|
||||
if sep_max < 0 then invalid_arg "Stringext.cut: empty separator" else
|
||||
let s_max = length s - 1 in
|
||||
if s_max < 0 then None else
|
||||
let k = ref 0 in
|
||||
let i = ref 0 in
|
||||
(* We run from the start of [s] to end with [i] trying to match the
|
||||
first character of [on] in [s]. If this matches, we verify that
|
||||
the whole [on] is matched using [k]. If it doesn't match we
|
||||
continue to look for [on] with [i]. If it matches we exit the
|
||||
loop and extract a substring from the start of [s] to the
|
||||
position before the [on] we found and another from the position
|
||||
after the [on] we found to end of string. If [i] is such that no
|
||||
separator can be found we exit the loop and return the no match
|
||||
case. *)
|
||||
try
|
||||
while (!i + sep_max <= s_max) do
|
||||
(* Check remaining [on] chars match, access to unsafe s (!i + !k) is
|
||||
guaranteed by loop invariant. *)
|
||||
if unsafe_get s !i <> unsafe_get on 0 then incr i else begin
|
||||
k := 1;
|
||||
while (!k <= sep_max && unsafe_get s (!i + !k) = unsafe_get on !k)
|
||||
do incr k done;
|
||||
if !k <= sep_max then (* no match *) incr i else raise Exit
|
||||
end
|
||||
done;
|
||||
None (* no match in the whole string. *)
|
||||
with
|
||||
| Exit -> (* i is at the beginning of the separator *)
|
||||
let left_end = !i - 1 in
|
||||
let right_start = !i + sep_max + 1 in
|
||||
Some (sub s 0 (left_end + 1),
|
||||
sub s right_start (s_max - right_start + 1))
|
||||
|
||||
let rcut s ~on =
|
||||
let sep_max = length on - 1 in
|
||||
if sep_max < 0 then invalid_arg "Stringext.rcut: empty separator" else
|
||||
let s_max = length s - 1 in
|
||||
if s_max < 0 then None else
|
||||
let k = ref 0 in
|
||||
let i = ref s_max in
|
||||
(* We run from the end of [s] to the beginning with [i] trying to
|
||||
match the last character of [on] in [s]. If this matches, we
|
||||
verify that the whole [on] is matched using [k] (we do that
|
||||
backwards). If it doesn't match we continue to look for [on]
|
||||
with [i]. If it matches we exit the loop and extract a
|
||||
substring from the start of [s] to the position before the
|
||||
[on] we found and another from the position after the [on] we
|
||||
found to end of string. If [i] is such that no separator can
|
||||
be found we exit the loop and return the no match case. *)
|
||||
try
|
||||
while (!i >= sep_max) do
|
||||
if unsafe_get s !i <> unsafe_get on sep_max then decr i else begin
|
||||
(* Check remaining [on] chars match, access to unsafe_get
|
||||
s (sep_start + !k) is guaranteed by loop invariant. *)
|
||||
let sep_start = !i - sep_max in
|
||||
k := sep_max - 1;
|
||||
while (!k >= 0 && unsafe_get s (sep_start + !k) = unsafe_get on !k)
|
||||
do decr k done;
|
||||
if !k >= 0 then (* no match *) decr i else raise Exit
|
||||
end
|
||||
done;
|
||||
None (* no match in the whole string. *)
|
||||
with
|
||||
| Exit -> (* i is at the end of the separator *)
|
||||
let left_end = !i - sep_max - 1 in
|
||||
let right_start = !i + 1 in
|
||||
Some (sub s 0 (left_end + 1),
|
||||
sub s right_start (s_max - right_start + 1))
|
||||
|
||||
let chop_prefix s ~prefix =
|
||||
let prefix_l = String.length prefix in
|
||||
let string_l = String.length s in
|
||||
if prefix_l > string_l then None
|
||||
else
|
||||
try
|
||||
for i = 0 to prefix_l - 1 do
|
||||
if s.[i] <> prefix.[i] then raise Exit;
|
||||
done;
|
||||
Some (String.sub s prefix_l (string_l - prefix_l))
|
||||
with _ -> None
|
||||
|
||||
let drop s n =
|
||||
let l = String.length s in
|
||||
if n >= l
|
||||
then ""
|
||||
else String.sub s n (l - n)
|
||||
|
||||
let take s n =
|
||||
if n >= String.length s
|
||||
then s
|
||||
else String.sub s 0 n
|
||||
72
unikernel/duniverse/stringext/lib/stringext.mli
Normal file
72
unikernel/duniverse/stringext/lib/stringext.mli
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
(** Misc. string functions not found in the built in OCaml string
|
||||
module *)
|
||||
|
||||
(** string_after [s] [n] returns the substring of [s] that is after
|
||||
character [n] *)
|
||||
val string_after : string -> int -> string
|
||||
|
||||
(** equivalent to [Str.quote] *)
|
||||
val quote : string -> string
|
||||
|
||||
(** split [?max] [s] [~on] splits [s] on every [on] occurence upto
|
||||
[max] number of items if [max] is specified. [max] is assumed to
|
||||
be a small number if specified. To not cause stack overflows *)
|
||||
val split : ?max:int -> string -> on:char -> string list
|
||||
|
||||
(** full_split [s] [~on] will split [s] on every occurence
|
||||
of [on] but will add the separators between the tokens. Maintains
|
||||
the invariant:
|
||||
|
||||
String.concat (full_split s ~on) =s *)
|
||||
val full_split : string -> on:char -> string list
|
||||
|
||||
(** Trims spaces on the left of the string. In case no trimming is needed
|
||||
the same string is returned without copying *)
|
||||
val trim_left : string -> string
|
||||
|
||||
(** split_strim_left [s] [~on] [~trim] splits [s] on every character
|
||||
in [on]. Characters in [trim] are trimmed from the left of every
|
||||
result element *)
|
||||
val split_trim_left : string -> on:string -> trim:string -> string list
|
||||
|
||||
val of_char : char -> string
|
||||
|
||||
val of_list : char list -> string
|
||||
|
||||
val to_list : string -> char list
|
||||
|
||||
val to_array : string -> char array
|
||||
|
||||
val of_array : char array -> string
|
||||
|
||||
val find_from : ?start:int -> string -> pattern:string -> int option
|
||||
|
||||
val replace_all : string -> pattern:string -> with_:string -> string
|
||||
|
||||
val replace_all_assoc : string -> (string * string) list -> string
|
||||
|
||||
val cut : string -> on:string -> (string * string) option
|
||||
(** [String.cut on s] is either the pair [Some (l,r)] of the two
|
||||
(possibly empty) substrings of [s] that are delimited by the first
|
||||
match of the non empty onarator string [on] or [None] if [on]
|
||||
can't be matched in [s]. Matching starts from the beginning of [s].
|
||||
|
||||
The invariant [l ^ on ^ r = s] holds.
|
||||
|
||||
@raise Invalid_argument if [on] is the empty string. *)
|
||||
|
||||
val rcut : string -> on:string -> (string * string) option
|
||||
(** [String.rcut on s] is like {!cut} but the matching is done backwards
|
||||
starting from the end of [s].
|
||||
|
||||
@raise Invalid_argument if [on] is the empty string. *)
|
||||
|
||||
val chop_prefix : string -> prefix:string -> string option
|
||||
|
||||
val drop : string -> int -> string
|
||||
|
||||
val take : string -> int -> string
|
||||
|
||||
(** [trim_left_sub s ~pos ~len ~chars] Trim all characters inside [chars]
|
||||
from [s] starting from [pos] and up to [len] *)
|
||||
val trim_left_sub : string -> pos:int -> len:int -> chars:string -> string
|
||||
17
unikernel/duniverse/stringext/lib_test/dune
Normal file
17
unikernel/duniverse/stringext/lib_test/dune
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
(executables
|
||||
(names test_stringext test_stringext_qcheck)
|
||||
(libraries stringext oUnit qcheck))
|
||||
|
||||
(alias
|
||||
(name runtest)
|
||||
(deps
|
||||
(:< test_stringext.exe))
|
||||
(action
|
||||
(run %{<})))
|
||||
|
||||
(alias
|
||||
(name runtest)
|
||||
(deps
|
||||
(:< test_stringext_qcheck.exe))
|
||||
(action
|
||||
(run %{<})))
|
||||
240
unikernel/duniverse/stringext/lib_test/test_stringext.ml
Normal file
240
unikernel/duniverse/stringext/lib_test/test_stringext.ml
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
open OUnit2
|
||||
|
||||
let (|>) x f = f x
|
||||
|
||||
let printer elems =
|
||||
"[" ^ (elems
|
||||
|> List.map (fun x -> "\"" ^ x ^ "\"")
|
||||
|> String.concat ";")
|
||||
^ "]"
|
||||
|
||||
let test_split_1 _ =
|
||||
let strings = Stringext.split "test:one:two" ~on:':' in
|
||||
assert_equal ~printer ["test";"one";"two"] strings
|
||||
|
||||
let test_split_bounded_1 _ =
|
||||
let strings = Stringext.split "testing:foo:bar" ~on:':' ~max:2 in
|
||||
assert_equal ~printer ["testing";"foo:bar"] strings
|
||||
|
||||
let test_split_none _ =
|
||||
let s = "foo:bar" in
|
||||
assert_equal ~printer [s] (Stringext.split s ~on:'=')
|
||||
|
||||
let split_trim_left1 _ =
|
||||
let strings = Stringext.split_trim_left
|
||||
" testing, stuff; \t again" ~on:",;" ~trim:" \t" in
|
||||
assert_equal ~printer ["testing";"stuff";"again"] strings
|
||||
|
||||
let split_trim_left2 _ =
|
||||
let strings = Stringext.split_trim_left
|
||||
" testing,stuff;\t again" ~on:",;" ~trim:" \t" in
|
||||
assert_equal ~printer ["testing";"stuff";"again"] strings
|
||||
|
||||
let split_trim_left3 _ =
|
||||
assert_equal ~printer
|
||||
["a ";"b ";"c"]
|
||||
(Stringext.split_trim_left ~on:"," ~trim:" " "a ,b ,c")
|
||||
|
||||
let split_trim_left4 _ =
|
||||
assert_equal ~printer
|
||||
["vpof "; "hbjeu "; ""; "c"]
|
||||
(Stringext.split_trim_left ~on:"," ~trim:" " "vpof ,hbjeu , ,c")
|
||||
|
||||
let printer s = "'" ^ (String.concat ";" s) ^ "'"
|
||||
|
||||
let full_split1 _ =
|
||||
let strings = Stringext.full_split
|
||||
"//var/test//ocaml/" ~on:'/' in
|
||||
assert_equal ~printer
|
||||
["/";"/";"var";"/";"test";"/";"/";"ocaml";"/"] strings
|
||||
|
||||
let full_split2 _ =
|
||||
let strings = Stringext.full_split "//foobar.com/quux" ~on:'/' in
|
||||
assert_equal ~printer
|
||||
["/";"/";"foobar.com";"/";"quux"] strings
|
||||
|
||||
let full_split3 _ =
|
||||
let strings = Stringext.full_split "foobar.com/quux" ~on:'/' in
|
||||
assert_equal ~printer
|
||||
["foobar.com";"/";"quux"] strings
|
||||
|
||||
let full_split4 _ =
|
||||
let strings = Stringext.full_split "a/path/fragment" ~on:'/' in
|
||||
assert_equal ~printer
|
||||
["a";"/";"path";"/";"fragment"] strings
|
||||
|
||||
let (to_list, of_list) = Stringext.(to_list, of_list)
|
||||
|
||||
let to_list1 _ = assert_equal ['o';'c';'a';'m';'l'] (to_list "ocaml")
|
||||
|
||||
let to_list2 _ = assert_equal [] (to_list "")
|
||||
|
||||
let of_list1 _ = assert_equal "" (of_list [])
|
||||
|
||||
let of_list2 _ = assert_equal "ocaml" (of_list ['o';'c';'a';'m';'l'])
|
||||
|
||||
let s = "testing one two three"
|
||||
|
||||
let opt_int = function
|
||||
| None -> "none"
|
||||
| Some x -> string_of_int x
|
||||
|
||||
let find_from1 _ =
|
||||
let r = Stringext.find_from s ~pattern:"ocaml" in
|
||||
assert_equal None r
|
||||
|
||||
let find_from2 _ =
|
||||
let r = Stringext.find_from s ~pattern:"testing" in
|
||||
assert_equal (Some 0) r
|
||||
|
||||
let find_from3 _ =
|
||||
let r = Stringext.find_from s ~pattern:"one" in
|
||||
assert_equal (Some 8) r
|
||||
|
||||
let find_from4 _ =
|
||||
let r = Stringext.find_from s ~pattern:"threee" in
|
||||
assert_equal None r
|
||||
|
||||
let find_from5 _ =
|
||||
let r = Stringext.find_from s ~pattern:" " in
|
||||
assert_equal (Some 7) r
|
||||
|
||||
let find_from6 _ =
|
||||
let pattern = "three" in
|
||||
let r = Stringext.find_from s ~pattern in
|
||||
assert_equal ~printer:opt_int
|
||||
(Some (String.length s - String.length pattern)) r
|
||||
|
||||
let replace_all1 _ =
|
||||
let s = "the quick brown fox brown." in
|
||||
let s' = Stringext.replace_all s ~pattern:"brown" ~with_:"blue" in
|
||||
assert_equal ~printer:(fun x -> x) "the quick blue fox blue." s'
|
||||
|
||||
let replace_all2 _ =
|
||||
let s = "one two three" in
|
||||
let s' = Stringext.replace_all s ~pattern:" " ~with_:"_" in
|
||||
assert_equal ~printer:(fun x -> x) "one_two_three" s'
|
||||
|
||||
let replace_all_assoc1 _ =
|
||||
let s = "hello from ocaml" in
|
||||
let tbl = [("hello", "goodbye"); ("ocaml", "haskell")] in
|
||||
let s' = Stringext.replace_all_assoc s tbl in
|
||||
assert_equal ~printer:(fun x -> x) "goodbye from haskell" s'
|
||||
|
||||
let replace_all_assoc2 _ =
|
||||
let s = "one two three" in
|
||||
let t = [("one", "four"); ("two", "five"); ("three", "six"); (" ", "_")] in
|
||||
let s' = Stringext.replace_all_assoc s t in
|
||||
assert_equal ~printer:(fun x -> x) "four_five_six" s'
|
||||
|
||||
let replace_all_assoc3 _ =
|
||||
let s = "one two three" in
|
||||
let t = [(" ", "_")] in
|
||||
let s' = Stringext.replace_all_assoc s t in
|
||||
assert_equal ~printer:(fun x -> x) "one_two_three" s'
|
||||
|
||||
let replace_all_assoc4 _ =
|
||||
let s = "onetwo" in
|
||||
let t = [("one", "xxxx"); ("two", "yyy")] in
|
||||
let s' = Stringext.replace_all_assoc s t in
|
||||
assert_equal ~printer:(fun x -> x) "xxxxyyy" s'
|
||||
|
||||
let of_array1 _ =
|
||||
let s = [| 'a'; 'b'; 'c' |] in
|
||||
assert_equal "abc" (Stringext.of_array s)
|
||||
|
||||
let trim_left_sub1 _ =
|
||||
let s = "testing" in
|
||||
assert_equal ~printer:(fun x -> x)
|
||||
s (Stringext.trim_left_sub s ~pos:0 ~len:(String.length s) ~chars:" ")
|
||||
|
||||
let trim_left_sub2 _ =
|
||||
let s = " , testing" in
|
||||
assert_equal ~printer:(fun x -> x)
|
||||
"testing"
|
||||
(Stringext.trim_left_sub s ~pos:0 ~len:(String.length s) ~chars:" ,")
|
||||
|
||||
let trim_left_sub3 _ =
|
||||
let s = " , testing" in
|
||||
assert_equal ~printer:(fun x -> x)
|
||||
"test" (Stringext.trim_left_sub s ~pos:0 ~len:(7) ~chars:" ,")
|
||||
|
||||
let trim_left_sub4 _ =
|
||||
let s = "a a" in
|
||||
assert_equal ~printer:(fun x -> x)
|
||||
s (Stringext.trim_left_sub s ~pos:0 ~len:3 ~chars:" ")
|
||||
|
||||
let trim_left_sub5 _ =
|
||||
assert_equal ~printer:(fun x -> x)
|
||||
"a" (Stringext.trim_left_sub "a" ~pos:0 ~len:1 ~chars:" ")
|
||||
|
||||
let trim_left1 _ =
|
||||
assert_equal ~printer:(fun x -> x) "" (Stringext.trim_left " ")
|
||||
|
||||
let trim_left2 _ =
|
||||
assert_equal ~printer:(fun x -> x) "" (Stringext.trim_left "")
|
||||
|
||||
let test_fixtures =
|
||||
"test various string functions" >:::
|
||||
[ "test split char 1" >:: test_split_1
|
||||
; "test split bounded 1" >:: test_split_bounded_1
|
||||
; "test split none" >:: test_split_none
|
||||
; "split trim left1" >:: split_trim_left1
|
||||
; "split trim left2" >:: split_trim_left2
|
||||
; "split trim left3" >:: split_trim_left3
|
||||
; "split trim left4" >:: split_trim_left4
|
||||
; "trim left sub1" >:: trim_left_sub1
|
||||
; "trim left sub2" >:: trim_left_sub2
|
||||
; "trim left sub3" >:: trim_left_sub3
|
||||
; "trim left sub4" >:: trim_left_sub4
|
||||
; "trim left sub5" >:: trim_left_sub5
|
||||
; "trim left1" >:: trim_left1
|
||||
; "trim left2" >:: trim_left2
|
||||
; "full split1" >:: full_split1
|
||||
; "full split2" >:: full_split2
|
||||
; "full split3" >:: full_split3
|
||||
; "full split4" >:: full_split4
|
||||
; "to_list1" >:: to_list1
|
||||
; "to_list2" >:: to_list2
|
||||
; "of_list1" >:: of_list1
|
||||
; "of_list2" >:: of_list2
|
||||
; "find_from1" >:: find_from1
|
||||
; "find_from2" >:: find_from2
|
||||
; "find_from3" >:: find_from3
|
||||
; "find_from4" >:: find_from4
|
||||
; "find_from5" >:: find_from5
|
||||
; "find_from6" >:: find_from6
|
||||
; "replace_all1" >:: replace_all1
|
||||
; "replace_all2" >:: replace_all2
|
||||
; "replace_all_assoc1" >:: replace_all_assoc1
|
||||
; "replace_all_assoc2" >:: replace_all_assoc2
|
||||
; "replace_all_assoc3" >:: replace_all_assoc3
|
||||
; "replace_all_assoc4" >:: replace_all_assoc4
|
||||
; "chop_prefix" >:: (fun _ ->
|
||||
let ae = assert_equal ~printer:(function
|
||||
| Some x -> "Some " ^ x
|
||||
| None -> "None") in
|
||||
ae (Some "bar") (Stringext.chop_prefix "foobar" ~prefix:"foo");
|
||||
ae None (Stringext.chop_prefix "foobar" ~prefix:"bar");
|
||||
ae (Some "foobar") (Stringext.chop_prefix "foobar" ~prefix:"");
|
||||
ae (Some "") (Stringext.chop_prefix "foobar" ~prefix:"foobar")
|
||||
)
|
||||
; "take" >:: (fun _ ->
|
||||
let ae = assert_equal ~printer:(fun x -> x) in
|
||||
ae "foo" (Stringext.take "foobar" 3);
|
||||
ae "bar" (Stringext.take "bar" 5);
|
||||
ae "" (Stringext.take "" 0);
|
||||
ae "" (Stringext.take "xxx" 0)
|
||||
)
|
||||
; "drop" >:: (fun _ ->
|
||||
let ae = assert_equal ~printer:(fun x -> x) in
|
||||
ae "foobar" (Stringext.drop "foobar" 0);
|
||||
ae "bar" (Stringext.drop "foobar" 3);
|
||||
ae "" (Stringext.drop "" 5);
|
||||
ae "" (Stringext.drop "foobar" 99)
|
||||
)
|
||||
; "of_array" >:: of_array1 ]
|
||||
|
||||
let _ = run_test_tt_main test_fixtures
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
open QCheck
|
||||
|
||||
let (|>) x f = f x
|
||||
|
||||
let quoted_str = Printf.sprintf "%S"
|
||||
|
||||
let run lst =
|
||||
OUnit2.run_test_tt_main (QCheck_runner.to_ounit2_test lst)
|
||||
|
||||
let _ = run (
|
||||
let f1 s = Stringext.split_trim_left ~on:"," ~trim:" " s in
|
||||
let f2 s = Stringext.split ~on:',' s |> List.map Stringext.trim_left in
|
||||
let pp str =
|
||||
(quoted_str str) ^ " -> " ^
|
||||
(Print.list quoted_str (f1 str)) ^ " != " ^
|
||||
(Print.list quoted_str (f2 str))
|
||||
in
|
||||
Test.make ~name:"stringext.split_trim_left == split |> trim_left" ~small:String.length
|
||||
((pair (oneofl [",";" ,";", ";" , "]) (list_of_size (Gen.int_bound 3) printable_string)) |>
|
||||
map (fun (sep,sub) -> String.concat sep sub) |>
|
||||
set_shrink Shrink.string |>
|
||||
set_print pp)
|
||||
(fun s -> f1 s = f2 s)
|
||||
)
|
||||
25
unikernel/duniverse/stringext/stringext.opam
Normal file
25
unikernel/duniverse/stringext/stringext.opam
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
version: "1.6.0"
|
||||
opam-version: "2.0"
|
||||
maintainer: "rudi.grinberg@gmail.com"
|
||||
authors: "Rudi Grinberg"
|
||||
license: "MIT"
|
||||
homepage: "https://github.com/rgrinberg/stringext"
|
||||
bug-reports: "https://github.com/rgrinberg/stringext/issues"
|
||||
depends: [
|
||||
"ocaml" {>= "4.02.3"}
|
||||
"dune" {build & >= "1.0"}
|
||||
"ounit" {with-test}
|
||||
"qtest" {with-test & >= "2.2"}
|
||||
"base-bytes"
|
||||
]
|
||||
build: [
|
||||
["dune" "subst"] {pinned}
|
||||
["dune" "build" "-p" name "-j" jobs]
|
||||
["dune" "runtest" "-p" name "-j" jobs] {with-test}
|
||||
]
|
||||
dev-repo: "git+https://github.com/rgrinberg/stringext.git"
|
||||
synopsis: "Extra string functions for OCaml"
|
||||
description: """
|
||||
Extra string functions for OCaml. Mainly splitting. All functions are in the
|
||||
Stringext module.
|
||||
"""
|
||||
Loading…
Add table
Add a link
Reference in a new issue