diff --git a/AUTHORS.adoc b/AUTHORS.adoc index 9ac9544e..c4c632bc 100644 --- a/AUTHORS.adoc +++ b/AUTHORS.adoc @@ -19,4 +19,5 @@ - Malcolm Matalka (`orbitz`) - David Sheets (@dsheets) - Glenn Slotte (glennsl) -- Leonid Rozenberg (@rleonid) +- @LemonBoy +- Leonid Rozenberg (@rleonid) \ No newline at end of file diff --git a/src/core/CCFormat.ml b/src/core/CCFormat.ml index da23915e..431f7850 100644 --- a/src/core/CCFormat.ml +++ b/src/core/CCFormat.ml @@ -39,6 +39,46 @@ let nativeint fmt n = Format.fprintf fmt "%nd" n let string_quoted fmt s = Format.fprintf fmt "\"%s\"" s let flush = Format.pp_print_flush +let newline = Format.pp_force_newline + +let substring out (s,i,len): unit = + string out (String.sub s i len) + +let text out (s:string): unit = + let len = String.length s in + let i = ref 0 in + let search_ c = + try Some (String.index_from s !i c) with Not_found -> None + in + while !i < len do + let j_newline = search_ '\n' in + let j_space = search_ ' ' in + let on_newline j = + substring out (s, !i, j - !i); + newline out (); + i := j + 1 + and on_space j = + substring out (s, !i, j - !i); + Format.pp_print_space out (); + i := j + 1 + in + begin match j_newline, j_space with + | None, None -> + (* done *) + substring out (s, !i, len - !i); + i := len + | Some j, None -> on_newline j + | None, Some j -> on_space j + | Some j1, Some j2 -> + if j1CCFormat.sprintf "%S" s) + "a\nb\nc" (sprintf_no_color "@[%a@]%!" text "a b c") + "a b\nc" (sprintf_no_color "@[%a@]%!" text "a b\nc") + *) + let list ?(sep=return ",@ ") pp fmt l = let rec pp_list l = match l with | x::((_::_) as l) -> diff --git a/src/core/CCFormat.mli b/src/core/CCFormat.mli index 0687d90b..847de339 100644 --- a/src/core/CCFormat.mli +++ b/src/core/CCFormat.mli @@ -23,6 +23,23 @@ val bool : bool printer val float3 : float printer (* 3 digits after . *) val float : float printer +val newline : unit printer +(** Force newline (see {!Format.pp_force_newline}) + @since NEXT_RELEASE *) + +val substring : (string * int * int) printer +(** Print the substring [(s,i,len)], where [i] is the offset + in [s] and [len] the number of bytes in the substring. + @raise Invalid_argument if the triple [(s,i,len)] does not + describe a proper substring. + @since NEXT_RELEASE *) + +val text : string printer +(** Print string, but replacing spaces with breaks and newlines + with {!newline}. + See [pp_print_text] on recent versions of OCaml. + @since NEXT_RELEASE *) + val char : char printer (** @since 0.14 *) val int32 : int32 printer (** @since 0.14 *) val int64 : int64 printer (** @since 0.14 *) diff --git a/src/core/CCList.ml b/src/core/CCList.ml index f4f3b9ea..1a0ab160 100644 --- a/src/core/CCList.ml +++ b/src/core/CCList.ml @@ -339,6 +339,52 @@ let partition_map f l = assert_equal [1;3] l2 *) +let combine l1 l2 = + let rec direct i l1 l2 = match l1, l2 with + | ([], []) -> [] + | _ when i=0 -> safe l1 l2 [] + | (x1::l1', x2::l2') -> (x1, x2) :: direct (i-1) l1' l2' + | (_, _) -> invalid_arg "CCList.combine" + and safe l1 l2 acc = match l1, l2 with + | ([], []) -> List.rev acc + | (x1::l1', x2::l2') -> safe l1' l2' @@ (x1, x2) :: acc + | (_, _) -> invalid_arg "CCList.combine" + in + direct direct_depth_default_ l1 l2 + +(*$T + try ignore (combine [1] []); false with Invalid_argument _ -> true + try ignore (combine (1--1001) (1--1002)); false with Invalid_argument _ -> true + combine [1;2;3] [3;2;1] = List.combine [1;2;3] [3;2;1] + combine (1 -- 100_000) (1 -- 100_000) = List.combine (1 -- 100_000) (1 -- 100_000) +*) + +(*$Q + Q.(let p = small_list int in pair p p)(fun (l1,l2) -> \ + if List.length l1=List.length l2 \ + then CCList.combine l1 l2 = List.combine l1 l2 \ + else Q.assume_fail() ) + *) + +let combine_gen l1 l2 = + let l1 = ref l1 in + let l2 = ref l2 in + fun () -> match !l1, !l2 with + | [], _ + | _, [] -> None + | x1 :: tail1, x2 :: tail2 -> + l1 := tail1; + l2 := tail2; + Some (x1,x2) + +(*$Q + Q.(let p = small_list int in pair p p)(fun (l1,l2) -> \ + let n = min (List.length l1) (List.length l2) in \ + let res1 = combine (take n l1) (take n l2) in \ + let res2 = combine_gen l1 l2 |> of_gen in \ + res1 = res2) + *) + let return x = [x] let (>>=) l f = flat_map f l diff --git a/src/core/CCList.mli b/src/core/CCList.mli index e8a06c5c..46fbeff4 100644 --- a/src/core/CCList.mli +++ b/src/core/CCList.mli @@ -3,6 +3,12 @@ (** {1 complements to list} *) +type 'a sequence = ('a -> unit) -> unit +type 'a gen = unit -> 'a option +type 'a klist = unit -> [`Nil | `Cons of 'a * 'a klist] +type 'a printer = Format.formatter -> 'a -> unit +type 'a random_gen = Random.State.t -> 'a + type 'a t = 'a list val empty : 'a t @@ -72,6 +78,18 @@ val init : int -> (int -> 'a) -> 'a t (** Similar to {!Array.init} @since 0.6 *) +val combine : 'a list -> 'b list -> ('a * 'b) list +(** Similar to {!List.combine} but tail-recursive. + @raise Invalid_argument if the lists have distinct lengths. + @since NEXT_RELEASE *) + +val combine_gen : 'a list -> 'b list -> ('a * 'b) gen +(** Lazy version of {!combine}. + Unlike {!combine}, it does not fail if the lists have different + lengths; + instead, the output has as many pairs as the smallest input list. + @since NEXT_RELEASE *) + val compare : ('a -> 'a -> int) -> 'a t -> 'a t -> int val equal : ('a -> 'a -> bool) -> 'a t -> 'a t -> bool @@ -433,12 +451,6 @@ end (** {2 Conversions} *) -type 'a sequence = ('a -> unit) -> unit -type 'a gen = unit -> 'a option -type 'a klist = unit -> [`Nil | `Cons of 'a * 'a klist] -type 'a printer = Format.formatter -> 'a -> unit -type 'a random_gen = Random.State.t -> 'a - val random : 'a random_gen -> 'a t random_gen val random_non_empty : 'a random_gen -> 'a t random_gen val random_len : int -> 'a random_gen -> 'a t random_gen diff --git a/src/core/CCString.cppo.ml b/src/core/CCString.cppo.ml index 1f88b308..9592a53d 100644 --- a/src/core/CCString.cppo.ml +++ b/src/core/CCString.cppo.ml @@ -391,16 +391,7 @@ end let split_on_char c s: _ list = Split.list_cpy ~by:(String.make 1 c) s -(*$= & ~printer:Q.Print.(list string) - ["a"; "few"; "words"; "from"; "our"; "sponsors"] \ - (split_on_char ' ' "a few words from our sponsors") -*) - -(*$Q - Q.(printable_string) (fun s -> \ - let s = split_on_char ' ' s |> String.concat " " in \ - s = split_on_char ' ' s |> String.concat " ") -*) +let split = Split.list_cpy let compare_versions a b = let of_int s = try Some (int_of_string s) with _ -> None in @@ -719,7 +710,16 @@ let lowercase_ascii = map CCChar.lowercase_ascii #endif - +let equal_caseless s1 s2: bool = + let char_lower c = + if c >= 'A' && c <= 'Z' + then Char.unsafe_chr (Char. code c + 32) + else c + in + String.length s1 = String.length s2 && + for_all2 + (fun c1 c2 -> char_lower c1 = char_lower c2) + s1 s2 let pp buf s = Buffer.add_char buf '"'; diff --git a/src/core/CCString.mli b/src/core/CCString.mli index 137e6b10..ca640222 100644 --- a/src/core/CCString.mli +++ b/src/core/CCString.mli @@ -399,6 +399,22 @@ val uppercase_ascii : string -> string val lowercase_ascii : string -> string (** See {!String}. @since 0.18 *) +val equal_caseless : string -> string -> bool +(** Comparison without respect to {b ascii} lowercase. + @since NEXT_RELEASE *) + +(*$T + equal_caseless "foo" "FoO" + equal_caseless "helLo" "HEllO" +*) + +(*$Q + Q.(pair printable_string printable_string) (fun (s1,s2) -> \ + equal_caseless s1 s2 = (lowercase_ascii s1=lowercase_ascii s2)) + Q.(printable_string) (fun s -> equal_caseless s s) + Q.(printable_string) (fun s -> equal_caseless (uppercase_ascii s) s) +*) + (** {2 Finding} A relatively efficient algorithm for finding sub-strings @@ -499,6 +515,21 @@ val split_on_char : char -> string -> string list (** Split the string along the given char @since NEXT_RELEASE *) +(*$= & ~printer:Q.Print.(list string) + ["a"; "few"; "words"; "from"; "our"; "sponsors"] \ + (split_on_char ' ' "a few words from our sponsors") +*) + +(*$Q + Q.(printable_string) (fun s -> \ + let s = split_on_char ' ' s |> String.concat " " in \ + s = (split_on_char ' ' s |> String.concat " ")) +*) + +val split : by:string -> string -> string list +(** Alias to {!Split.list_cpy} + @since NEXT_RELEASE *) + (** {2 Utils} *) val compare_versions : string -> string -> int