From 2a872907a11bdd58f412dcc6ae17bcaeff3ba021 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 19 Oct 2016 18:17:40 +0200 Subject: [PATCH 01/14] add `CCArray.{sorted,sort_indices,sort_ranking}` (closes #81) --- src/core/CCArray.ml | 139 +++++++++++++++++++++++++++++++++++++++++++ src/core/CCArray.mli | 23 +++++++ 2 files changed, 162 insertions(+) diff --git a/src/core/CCArray.ml b/src/core/CCArray.ml index cc7b622f..f83b4360 100644 --- a/src/core/CCArray.ml +++ b/src/core/CCArray.ml @@ -51,6 +51,29 @@ module type S = sig val reverse_in_place : 'a t -> unit (** Reverse the array in place *) + val sorted : ('a -> 'a -> int) -> 'a t -> 'a array + (** [sorted cmp a] makes a copy of [a] and sorts it with [cmp]. + @since NEXT_RELEASE *) + + val sort_indices : ('a -> 'a -> int) -> 'a t -> int array + (** [sort_indices cmp a] returns a new array [b], with the same length as [a], + such that [b.(i)] is the index of the [i]-th element in [sort cmp a]. + In other words, [map (fun i -> a.(i)) (sort_indices a) = sorted cmp a]. + [a] is not modified. + @since NEXT_RELEASE *) + + val sort_ranking : ('a -> 'a -> int) -> 'a t -> int array + (** [sort_ranking cmp a] returns a new array [b], with the same length as [a], + such that [b.(i)] is the position in [sorted cmp a] of the [i]-th + element of [a]. + [a] is not modified. + + In other words, [map (fun i -> (sorted cmp a).(i)) (sort_ranking cmp a) = a]. + + Without duplicates, we also have + [lookup_exn (sorted a) a.(i) = (sorted_ranking a).(i)] + @since NEXT_RELEASE *) + val find : ('a -> 'b option) -> 'a t -> 'b option (** [find f a] returns [Some y] if there is an element [x] such that [f x = Some y], else it returns [None] *) @@ -260,6 +283,18 @@ let _shuffle _rand_int a i j = let b = Array.copy a in shuffle_with st a; a <> b *) +let _sort_indices cmp a i j = + let len = j-i in + let b = Array.init len (fun k->k) in + Array.sort (fun k1 k2 -> cmp a.(k1+i) a.(k2+i)) b; + b + +let _sorted cmp a i j = + let len = j-i in + let b = Array.sub a i len in + Array.sort cmp b; + b + let _choose a i j st = if i>=j then raise Not_found; a.(i+Random.State.int st (j-i)) @@ -366,6 +401,48 @@ let reverse_in_place a = a = [| 6;5;4;3;2;1 |] *) +let sorted cmp a = _sorted cmp a 0 (Array.length a) + +(*$= & ~cmp:(=) ~printer:Q.Print.(array int) + [||] (sorted Pervasives.compare [||]) + [|0;1;2;3;4|] (sorted Pervasives.compare [|3;2;1;4;0|]) + *) + +(*$Q + Q.(array int) (fun a -> \ + let b = Array.copy a in \ + Array.sort Pervasives.compare b; b = sorted Pervasives.compare a) +*) + +let sort_indices cmp a = _sort_indices cmp a 0 (Array.length a) + +(*$= & ~cmp:(=) ~printer:Q.Print.(array int) + [||] (sort_indices Pervasives.compare [||]) + [|4;2;1;0;3|] (sort_indices Pervasives.compare [|"d";"c";"b";"e";"a"|]) +*) + +(*$Q + Q.(array printable_string) (fun a -> \ + let b = sort_indices String.compare a in \ + sorted String.compare a = Array.map (Array.get a) b) +*) + +let sort_ranking cmp a = + let cmp_int : int -> int -> int = Pervasives.compare in + sort_indices cmp_int (sort_indices cmp a) + +(*$= & ~cmp:(=) ~printer:Q.Print.(array int) + [||] (sort_ranking Pervasives.compare [||]) + [|3;2;1;4;0|] (sort_ranking Pervasives.compare [|"d";"c";"b";"e";"a"|]) +*) + +(*$Q + Q.(array printable_string) (fun a -> \ + let b = sort_ranking String.compare a in \ + let a_sorted = sorted String.compare a in \ + a = Array.map (Array.get a_sorted) b) +*) + let rev a = let b = Array.copy a in reverse_in_place b; @@ -689,6 +766,68 @@ module Sub = struct Sub.reverse_in_place s; a = [| 1; 2; 5; 4; 3; 6 |] *) + let sorted cmp a = _sorted cmp a.arr a.i a.j + + (*$= & ~cmp:(=) ~printer:Q.Print.(array int) + [||] \ + (let a = 1--6 in let s = Sub.make a 2 ~len:0 in \ + Sub.sorted Pervasives.compare s) + [|2;3;4|] \ + (let a = [|6;5;4;3;2;1|] in let s = Sub.make a 2 ~len:3 in \ + Sub.sorted Pervasives.compare s) + *) + + (*$Q + Q.(array int) (fun a -> \ + Array.length a > 10 ==> ( Array.length a > 10 && \ + let s = Sub.make a 5 ~len:5 in \ + let b = Array.sub a 5 5 in \ + Array.sort Pervasives.compare b; b = Sub.sorted Pervasives.compare s)) + *) + + let sort_ranking cmp a = + let idx = _sort_indices cmp a.arr a.i a.j in + let cmp_int : int -> int -> int = Pervasives.compare in + sort_indices cmp_int idx + + (*$= & ~cmp:(=) ~printer:Q.Print.(array int) + [||] \ + (let a = 1--6 in let s = Sub.make a 2 ~len:0 in \ + Sub.sort_ranking Pervasives.compare s) + [|2;1;3;0|] \ + (let a = [|"d";"c";"b";"e";"a"|] in let s = Sub.make a 1 ~len:4 in \ + Sub.sort_ranking Pervasives.compare s) + *) + + (*$Q + Q.(array printable_string) (fun a -> \ + Array.length a > 10 ==> ( Array.length a > 10 && \ + let s = Sub.make a 5 ~len:5 in \ + let b = Sub.sort_indices String.compare s in \ + Sub.sorted String.compare s = Array.map (Sub.get s) b)) + *) + + let sort_indices cmp a = _sort_indices cmp a.arr a.i a.j + + (*$= & ~cmp:(=) ~printer:Q.Print.(array int) + [||] \ + (let a = 1--6 in let s = Sub.make a 2 ~len:0 in \ + Sub.sort_indices Pervasives.compare s) + [|3;1;0;2|] \ + (let a = [|"d";"c";"b";"e";"a"|] in let s = Sub.make a 1 ~len:4 in \ + Sub.sort_indices Pervasives.compare s) + *) + + (*$Q + Q.(array printable_string) (fun a -> \ + Array.length a > 10 ==> ( Array.length a > 10 && \ + let s = Sub.make a 5 ~len:5 in \ + let b = Sub.sort_ranking String.compare s in \ + let a_sorted = Sub.sorted String.compare s in \ + Sub.copy s = Array.map (Array.get a_sorted) b)) + *) + + let find f a = _find (fun _ -> f) a.arr a.i a.j let findi f a = _find (fun i -> f (i-a.i)) a.arr a.i a.j diff --git a/src/core/CCArray.mli b/src/core/CCArray.mli index 8206d2cb..c22045f4 100644 --- a/src/core/CCArray.mli +++ b/src/core/CCArray.mli @@ -53,6 +53,29 @@ module type S = sig val reverse_in_place : 'a t -> unit (** Reverse the array in place *) + val sorted : ('a -> 'a -> int) -> 'a t -> 'a array + (** [sorted cmp a] makes a copy of [a] and sorts it with [cmp]. + @since NEXT_RELEASE *) + + val sort_indices : ('a -> 'a -> int) -> 'a t -> int array + (** [sort_indices cmp a] returns a new array [b], with the same length as [a], + such that [b.(i)] is the index of the [i]-th element in [sort cmp a]. + In other words, [map (fun i -> a.(i)) (sort_indices a) = sorted cmp a]. + [a] is not modified. + @since NEXT_RELEASE *) + + val sort_ranking : ('a -> 'a -> int) -> 'a t -> int array + (** [sort_ranking cmp a] returns a new array [b], with the same length as [a], + such that [b.(i)] is the position in [sorted cmp a] of the [i]-th + element of [a]. + [a] is not modified. + + In other words, [map (fun i -> (sorted cmp a).(i)) (sort_ranking cmp a) = a]. + + Without duplicates, we also have + [lookup_exn a.(i) (sorted a) = (sorted_ranking a).(i)] + @since NEXT_RELEASE *) + val find : ('a -> 'b option) -> 'a t -> 'b option (** [find f a] returns [Some y] if there is an element [x] such that [f x = Some y], else it returns [None] *) From 7ba8f58571b201b14402e191d819b321c5bedc86 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 19 Oct 2016 23:59:23 +0200 Subject: [PATCH 02/14] fix typo --- src/core/CCArray.ml | 4 ++-- src/core/CCArray.mli | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/CCArray.ml b/src/core/CCArray.ml index f83b4360..9058c53e 100644 --- a/src/core/CCArray.ml +++ b/src/core/CCArray.ml @@ -57,7 +57,7 @@ module type S = sig val sort_indices : ('a -> 'a -> int) -> 'a t -> int array (** [sort_indices cmp a] returns a new array [b], with the same length as [a], - such that [b.(i)] is the index of the [i]-th element in [sort cmp a]. + such that [b.(i)] is the index of the [i]-th element of [a] in [sort cmp a]. In other words, [map (fun i -> a.(i)) (sort_indices a) = sorted cmp a]. [a] is not modified. @since NEXT_RELEASE *) @@ -71,7 +71,7 @@ module type S = sig In other words, [map (fun i -> (sorted cmp a).(i)) (sort_ranking cmp a) = a]. Without duplicates, we also have - [lookup_exn (sorted a) a.(i) = (sorted_ranking a).(i)] + [lookup_exn a.(i) (sorted a) = (sorted_ranking a).(i)] @since NEXT_RELEASE *) val find : ('a -> 'b option) -> 'a t -> 'b option diff --git a/src/core/CCArray.mli b/src/core/CCArray.mli index c22045f4..53e36181 100644 --- a/src/core/CCArray.mli +++ b/src/core/CCArray.mli @@ -59,7 +59,7 @@ module type S = sig val sort_indices : ('a -> 'a -> int) -> 'a t -> int array (** [sort_indices cmp a] returns a new array [b], with the same length as [a], - such that [b.(i)] is the index of the [i]-th element in [sort cmp a]. + such that [b.(i)] is the index of the [i]-th element of [a] in [sort cmp a]. In other words, [map (fun i -> a.(i)) (sort_indices a) = sorted cmp a]. [a] is not modified. @since NEXT_RELEASE *) From 73bb61a2de3ca80e85ba32e9ff61dc7db9ea89d2 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 25 Oct 2016 12:09:05 +0200 Subject: [PATCH 03/14] disable parallel build to support cygwin --- _oasis | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/_oasis b/_oasis index 2ea5ba5d..82ecf65e 100644 --- a/_oasis +++ b/_oasis @@ -10,7 +10,8 @@ OCamlVersion: >= 4.00.1 BuildTools: ocamlbuild AlphaFeatures: compiled_setup_ml, ocamlbuild_more_args -XOCamlbuildExtraArgs: "-j 0" +# cygwin fails with anything else +XOCamlbuildExtraArgs: "-j 1" Synopsis: A modular standard library focused on data structures. Description: From b7db149e27eb62c5d5ac584bb3777662d9af388f Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 1 Nov 2016 11:27:07 +0100 Subject: [PATCH 04/14] add `CCArray.Sub.to_list` --- src/core/CCArray.ml | 3 +++ src/core/CCArray.mli | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/src/core/CCArray.ml b/src/core/CCArray.ml index 9058c53e..142bda9d 100644 --- a/src/core/CCArray.ml +++ b/src/core/CCArray.ml @@ -709,6 +709,9 @@ module Sub = struct else _fold (f acc a.arr.(i)) (i+1) j in _fold acc a.i a.j + let to_list a = + fold (fun l x -> x::l) [] a |> List.rev + let foldi f acc a = _foldi f acc a.arr a.i a.j let fold_while f acc a = diff --git a/src/core/CCArray.mli b/src/core/CCArray.mli index 53e36181..44f7cc7b 100644 --- a/src/core/CCArray.mli +++ b/src/core/CCArray.mli @@ -247,6 +247,10 @@ module Sub : sig (** Convert into a triple [(arr, i, len)] where [len] is the length of the subarray of [arr] starting at offset [i] *) + val to_list : 'a t -> 'a list + (** Convert directly to a list + @since NEXT_RELEASE *) + val full : 'a array -> 'a t (** Slice that covers the full array *) From 61ff75dca05f6a89b4ba0840876af27dffa4bb9d Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 1 Nov 2016 11:27:15 +0100 Subject: [PATCH 05/14] bugfix + tests for `CCArray.Sub.sub` --- src/core/CCArray.ml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/core/CCArray.ml b/src/core/CCArray.ml index 142bda9d..618f2390 100644 --- a/src/core/CCArray.ml +++ b/src/core/CCArray.ml @@ -695,7 +695,15 @@ module Sub = struct let copy a = Array.sub a.arr a.i (length a) - let sub a i len = make a.arr ~len:(a.i + i) len + let sub a i len = make a.arr (a.i + i) ~len + (*$= + [ 3;4 ] \ + (let a = Sub.make (0--10) 2 5 in Sub.sub a 1 2 |> Sub.to_list) + [ ] \ + (let a = Sub.make (0--10) 2 5 in Sub.sub a 1 0 |> Sub.to_list) + [ 5 ] \ + (let a = Sub.make (0--10) 1 9 in Sub.sub a 4 1 |> Sub.to_list) + *) let equal eq a b = length a = length b && _equal eq a.arr a.i a.j b.arr b.i b.j @@ -710,7 +718,8 @@ module Sub = struct in _fold acc a.i a.j let to_list a = - fold (fun l x -> x::l) [] a |> List.rev + let l = fold (fun l x -> x::l) [] a in + List.rev l let foldi f acc a = _foldi f acc a.arr a.i a.j From 18b32f83134dc289562e2999b05dcdb4ebca464c Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 1 Nov 2016 12:17:01 +0100 Subject: [PATCH 06/14] bugfixes in `CCArray.Sub` --- src/core/CCArray.ml | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/core/CCArray.ml b/src/core/CCArray.ml index 618f2390..427ccf7b 100644 --- a/src/core/CCArray.ml +++ b/src/core/CCArray.ml @@ -845,17 +845,28 @@ module Sub = struct let findi f a = _find (fun i -> f (i-a.i)) a.arr a.i a.j let find_idx p a = - _find (fun i x -> if p x then Some (i,x) else None) a.arr a.i a.j + _find (fun i x -> if p x then Some (i-a.i,x) else None) a.arr a.i a.j + + (*$= + (Some (1,"c")) (Sub.find_idx ((=) "c") (Sub.make [| "a"; "b"; "c" |] 1 2)) + *) let lookup_exn ?(cmp=Pervasives.compare) k a = - _lookup_exn ~cmp k a.arr a.i (a.j-1) + _lookup_exn ~cmp k a.arr a.i (a.j-1) - a.i let lookup ?(cmp=Pervasives.compare) k a = - try Some (_lookup_exn ~cmp k a.arr a.i (a.j-1)) + try Some (_lookup_exn ~cmp k a.arr a.i (a.j-1) - a.i) with Not_found -> None + (*$= + (Some 1) (Sub.lookup "c" (Sub.make [| "a"; "b"; "c" |] 1 2)) + *) + let bsearch ?(cmp=Pervasives.compare) k a = - bsearch_ ~cmp k a.arr a.i (a.j - 1) + match bsearch_ ~cmp k a.arr a.i (a.j - 1) with + | `At m -> `At (m - a.i) + | `Just_after m -> `Just_after (m - a.i) + | res -> res let for_all p a = _for_all p a.arr a.i a.j @@ -889,7 +900,8 @@ module Sub = struct let pp ?(sep=", ") pp_item buf a = _pp ~sep pp_item buf a.arr a.i a.j - let pp_i ?(sep=", ") pp_item buf a = _pp_i ~sep pp_item buf a.arr a.i a.j + let pp_i ?(sep=", ") pp_item buf a = + _pp_i ~sep (fun out k x -> pp_item out (k-a.i) x) buf a.arr a.i a.j let print ?(sep=", ") pp_item fmt a = _print ~sep pp_item fmt a.arr a.i a.j From 3e7cbc142002b74a572479db6110e3caab70c983 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 2 Nov 2016 08:22:03 +0100 Subject: [PATCH 07/14] add alias `Containers.IO` --- src/core/containers.ml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/containers.ml b/src/core/containers.ml index b8271589..829c1f11 100644 --- a/src/core/containers.ml +++ b/src/core/containers.ml @@ -81,6 +81,9 @@ module Vector = CCVector module Int64 = CCInt64 (** @since 0.13 *) +module IO = CCIO +(** @since NEXT_RELEASE *) + module Char = struct include Char include (CCChar : module type of CCChar with type t := t) From 89c63a5357674d053d58084d2a24ebed6d8cfda1 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 2 Nov 2016 11:47:57 +0100 Subject: [PATCH 08/14] update oasis setup --- Makefile | 6 +++--- configure | 8 ++++++-- setup.ml | 6 ++++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 00dd73da..b00c76d8 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ # OASIS_START -# DO NOT EDIT (digest: 9a60866e2fa295c5e33a3fe33b8f3a32) +# DO NOT EDIT (digest: 4c293511860bb966e727ba6f0ecc8197) SETUP = ./setup.exe @@ -37,8 +37,8 @@ setup.data: $(SETUP) configure: $(SETUP) $(SETUP) -configure $(CONFIGUREFLAGS) -setup.exe: setup.ml - ocamlfind ocamlopt -o $@ -linkpkg -package oasis.dynrun $< || ocamlfind ocamlc -o $@ -linkpkg -package oasis.dynrun $< || true +setup.exe: setup.ml _oasis + ocamlfind ocamlopt -o $@ -linkpkg -package oasis.dynrun setup.ml || ocamlfind ocamlc -o $@ -linkpkg -package oasis.dynrun setup.ml || true $(RM) setup.cmi setup.cmo setup.cmx setup.o .PHONY: build doc test all install uninstall reinstall clean distclean configure diff --git a/configure b/configure index d2a26d17..7231d47f 100755 --- a/configure +++ b/configure @@ -1,7 +1,7 @@ #!/bin/sh # OASIS_START -# DO NOT EDIT (digest: 6f7b8221311e800a7093dc3b793f67ca) +# DO NOT EDIT (digest: 7577949ceda6f9dbd4983aea8db9275b) set -e FST=true @@ -23,5 +23,9 @@ for i in "$@"; do esac done -make configure CONFIGUREFLAGS="$*" +if [ ! -e setup.exe ] || [ _oasis -nt setup.exe ] || [ setup.ml -nt setup.exe ] || [ configure -nt setup.exe ]; then + ocamlfind ocamlopt -o setup.exe -linkpkg -package oasis.dynrun setup.ml || ocamlfind ocamlc -o setup.exe -linkpkg -package oasis.dynrun setup.ml || exit 1 + rm -f setup.cmi setup.cmo setup.cmx setup.o +fi +./setup.exe -configure "$@" # OASIS_STOP diff --git a/setup.ml b/setup.ml index e4c486de..28f4aab2 100644 --- a/setup.ml +++ b/setup.ml @@ -1,11 +1,11 @@ (* setup.ml generated for the first time by OASIS v0.4.4 *) (* OASIS_START *) -(* DO NOT EDIT (digest: 172e37fc4b327922311f6cf9389bc560) *) +(* DO NOT EDIT (digest: 1bc19e72587da58c1e1f99f847b509aa) *) (******************************************************************************) (* OASIS: architecture for building OCaml libraries and applications *) (* *) -(* Copyright (C) 2011-2013, Sylvain Le Gall *) +(* Copyright (C) 2011-2016, Sylvain Le Gall *) (* Copyright (C) 2008-2011, OCamlCore SARL *) (* *) (* This library is free software; you can redistribute it and/or modify it *) @@ -26,5 +26,7 @@ open OASISDynRun +let setup_t = BaseCompat.Compat_0_4.adapt_setup_t setup_t +open BaseCompat.Compat_0_4 (* OASIS_STOP *) let () = setup ();; From 269d4a7ba9f8ed4d1134dcc6e6924e3b1ca48db7 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 3 Nov 2016 09:48:50 +0100 Subject: [PATCH 09/14] handle '\r` in CCSexpM (fixes #83) --- src/sexp/CCSexpM.ml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/sexp/CCSexpM.ml b/src/sexp/CCSexpM.ml index d714924f..3ecaf953 100644 --- a/src/sexp/CCSexpM.ml +++ b/src/sexp/CCSexpM.ml @@ -41,7 +41,7 @@ let _must_escape s = for i = 0 to String.length s - 1 do let c = String.unsafe_get s i in match c with - | ' ' | ';' | ')' | '(' | '"' | '\\' | '\n' | '\t' -> raise Exit + | ' ' | ';' | ')' | '(' | '"' | '\\' | '\n' | '\t' | '\r' -> raise Exit | _ when Char.code c > 127 -> raise Exit (* non-ascii *) | _ -> () done; @@ -176,11 +176,11 @@ module MakeDecode(M : MONAD) = struct let rec expr k t = if t.i = t.len then _refill t (expr k) _error_eof else match _get t with - | ' ' | '\t' | '\n' -> expr k t + | ' ' | '\t' | '\r' | '\n' -> expr k t | c -> expr_starting_with c k t and expr_starting_with c k t = match c with - | ' ' | '\t' | '\n' -> assert false + | ' ' | '\t' | '\r' | '\n' -> assert false | ';' -> skip_comment (fun _ () -> expr k t) t | '(' -> expr_list [] k t | ')' -> _error t "unexpected ')'" @@ -194,7 +194,7 @@ module MakeDecode(M : MONAD) = struct and expr_list acc k t = if t.i = t.len then _refill t (expr_list acc k) _error_eof else match _get t with - | ' ' | '\t' | '\n' -> expr_list acc k t + | ' ' | '\t' | '\r' | '\n' -> expr_list acc k t | ')' -> k None (`List (List.rev acc)) | c -> expr_starting_with c @@ -216,7 +216,7 @@ module MakeDecode(M : MONAD) = struct else match _get t with | '\\' -> _error t "unexpected '\\' in non-quoted string" | '"' -> _error t "unexpected '\"' in the middle of an atom" - | (' ' | '\n' | '\t' | '(' | ')') as c -> + | (' ' | '\r' | '\n' | '\t' | '(' | ')') as c -> _return_atom (Some c) k t | c -> Buffer.add_char t.atom c; @@ -277,7 +277,7 @@ module MakeDecode(M : MONAD) = struct if t.i = t.len then _refill t (expr_or_end k) (fun _ -> M.return `End) else match _get t with - | ' ' | '\t' | '\n' -> expr_or_end k t + | ' ' | '\t' | '\r' | '\n' -> expr_or_end k t | c -> expr_starting_with c k t (* entry point *) @@ -308,6 +308,8 @@ let parse_string s : t or_error = (*$T CCError.to_opt (parse_string "(abc d/e/f \"hello \\\" () world\" )") <> None CCError.to_opt (parse_string "(abc ( d e ffff ) \"hello/world\")") <> None + (parse_string "(abc\r\n ( d e \r\tffff ))") \ + = `Ok (`List [`Atom "abc"; `List [`Atom "d"; `Atom "e"; `Atom "ffff"]]) *) (*$inject From 4ff174ce18d0229bee84ce2a60d207f79a46964e Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 3 Nov 2016 15:19:50 +0100 Subject: [PATCH 10/14] (breaking) make default start/stop arguments empty in printers (#82) --- src/core/CCFormat.ml | 8 ++++---- src/core/CCList.ml | 6 +++--- src/core/CCMap.ml | 4 ++-- src/core/CCPrint.ml | 8 ++++---- src/core/CCSet.ml | 4 ++-- src/core/CCVector.ml | 4 ++-- src/data/CCImmutArray.ml | 2 +- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/core/CCFormat.ml b/src/core/CCFormat.ml index c7b82288..a2e567ac 100644 --- a/src/core/CCFormat.ml +++ b/src/core/CCFormat.ml @@ -25,7 +25,7 @@ let int64 fmt n = Format.fprintf fmt "%Ld" n let nativeint fmt n = Format.fprintf fmt "%nd" n let string_quoted fmt s = Format.fprintf fmt "\"%s\"" s -let list ?(start="[") ?(stop="]") ?(sep=", ") pp fmt l = +let list ?(start="") ?(stop="") ?(sep=", ") pp fmt l = let rec pp_list l = match l with | x::((_::_) as l) -> pp fmt x; @@ -39,7 +39,7 @@ let list ?(start="[") ?(stop="]") ?(sep=", ") pp fmt l = pp_list l; Format.pp_print_string fmt stop -let array ?(start="[") ?(stop="]") ?(sep=", ") pp fmt a = +let array ?(start="") ?(stop="") ?(sep=", ") pp fmt a = Format.pp_print_string fmt start; for i = 0 to Array.length a - 1 do if i > 0 then ( @@ -50,7 +50,7 @@ let array ?(start="[") ?(stop="]") ?(sep=", ") pp fmt a = done; Format.pp_print_string fmt stop -let arrayi ?(start="[") ?(stop="]") ?(sep=", ") pp fmt a = +let arrayi ?(start="") ?(stop="") ?(sep=", ") pp fmt a = Format.pp_print_string fmt start; for i = 0 to Array.length a - 1 do if i > 0 then ( @@ -61,7 +61,7 @@ let arrayi ?(start="[") ?(stop="]") ?(sep=", ") pp fmt a = done; Format.pp_print_string fmt stop -let seq ?(start="[") ?(stop="]") ?(sep=", ") pp fmt seq = +let seq ?(start="") ?(stop="") ?(sep=", ") pp fmt seq = Format.pp_print_string fmt start; let first = ref true in seq (fun x -> diff --git a/src/core/CCList.ml b/src/core/CCList.ml index 17a29a04..f09ea9fc 100644 --- a/src/core/CCList.ml +++ b/src/core/CCList.ml @@ -1221,7 +1221,7 @@ end (** {2 IO} *) -let pp ?(start="[") ?(stop="]") ?(sep=", ") pp_item buf l = +let pp ?(start="") ?(stop="") ?(sep=", ") pp_item buf l = let rec print l = match l with | x::((_::_) as l) -> pp_item buf x; @@ -1232,10 +1232,10 @@ let pp ?(start="[") ?(stop="]") ?(sep=", ") pp_item buf l = in Buffer.add_string buf start; print l; Buffer.add_string buf stop (*$T - CCPrint.to_string (pp CCPrint.int) [1;2;3] = "[1, 2, 3]" + CCPrint.to_string (pp ~start:"[" ~stop:"]" CCPrint.int) [1;2;3] = "[1, 2, 3]" *) -let print ?(start="[") ?(stop="]") ?(sep=", ") pp_item fmt l = +let print ?(start="") ?(stop="") ?(sep=", ") pp_item fmt l = let rec print fmt l = match l with | x::((_::_) as l) -> pp_item fmt x; diff --git a/src/core/CCMap.ml b/src/core/CCMap.ml index 6dad0ad1..ee7c5f66 100644 --- a/src/core/CCMap.ml +++ b/src/core/CCMap.ml @@ -113,7 +113,7 @@ module Make(O : Map.OrderedType) = struct let to_list m = fold (fun k v acc -> (k,v)::acc) m [] - let pp ?(start="{") ?(stop="}") ?(arrow="->") ?(sep=", ") pp_k pp_v buf m = + let pp ?(start="") ?(stop="") ?(arrow="->") ?(sep=", ") pp_k pp_v buf m = let first = ref true in Buffer.add_string buf start; iter @@ -125,7 +125,7 @@ module Make(O : Map.OrderedType) = struct m; Buffer.add_string buf stop - let print ?(start="[") ?(stop="]") ?(arrow="->") ?(sep=", ") pp_k pp_v fmt m = + let print ?(start="") ?(stop="") ?(arrow="->") ?(sep=", ") pp_k pp_v fmt m = Format.pp_print_string fmt start; let first = ref true in iter diff --git a/src/core/CCPrint.ml b/src/core/CCPrint.ml index c0f1a4c7..3d1fd7b3 100644 --- a/src/core/CCPrint.ml +++ b/src/core/CCPrint.ml @@ -25,7 +25,7 @@ let float3 buf f = Printf.bprintf buf "%.3f" f let float buf f = Buffer.add_string buf (string_of_float f) let char buf c = Buffer.add_char buf c -let list ?(start="[") ?(stop="]") ?(sep=", ") pp buf l = +let list ?(start="") ?(stop="") ?(sep=", ") pp buf l = let rec pp_list l = match l with | x::((_::_) as l) -> pp buf x; @@ -38,7 +38,7 @@ let list ?(start="[") ?(stop="]") ?(sep=", ") pp buf l = pp_list l; Buffer.add_string buf stop -let array ?(start="[") ?(stop="]") ?(sep=", ") pp buf a = +let array ?(start="") ?(stop="") ?(sep=", ") pp buf a = Buffer.add_string buf start; for i = 0 to Array.length a - 1 do (if i > 0 then Buffer.add_string buf sep); @@ -46,7 +46,7 @@ let array ?(start="[") ?(stop="]") ?(sep=", ") pp buf a = done; Buffer.add_string buf stop -let arrayi ?(start="[") ?(stop="]") ?(sep=", ") pp buf a = +let arrayi ?(start="") ?(stop="") ?(sep=", ") pp buf a = Buffer.add_string buf start; for i = 0 to Array.length a - 1 do (if i > 0 then Buffer.add_string buf sep); @@ -54,7 +54,7 @@ let arrayi ?(start="[") ?(stop="]") ?(sep=", ") pp buf a = done; Buffer.add_string buf stop -let seq ?(start="[") ?(stop="]") ?(sep=", ") pp buf seq = +let seq ?(start="") ?(stop="") ?(sep=", ") pp buf seq = Buffer.add_string buf start; let first = ref true in seq (fun x -> diff --git a/src/core/CCSet.ml b/src/core/CCSet.ml index f11d1981..59815a2d 100644 --- a/src/core/CCSet.ml +++ b/src/core/CCSet.ml @@ -51,7 +51,7 @@ module Make(O : Map.OrderedType) = struct let to_list = elements - let pp ?(start="{") ?(stop="}") ?(sep=", ") pp_x buf m = + let pp ?(start="") ?(stop="") ?(sep=", ") pp_x buf m = let first = ref true in Buffer.add_string buf start; iter @@ -61,7 +61,7 @@ module Make(O : Map.OrderedType) = struct m; Buffer.add_string buf stop - let print ?(start="[") ?(stop="]") ?(sep=", ") pp_x fmt m = + let print ?(start="") ?(stop="") ?(sep=", ") pp_x fmt m = Format.pp_print_string fmt start; let first = ref true in iter diff --git a/src/core/CCVector.ml b/src/core/CCVector.ml index 240774fe..1c5362d0 100644 --- a/src/core/CCVector.ml +++ b/src/core/CCVector.ml @@ -737,7 +737,7 @@ let to_klist v = else `Cons (v.vec.(i), aux (i+1)) in aux 0 -let pp ?(start="[") ?(stop="]") ?(sep=", ") pp_item buf v = +let pp ?(start="") ?(stop="") ?(sep=", ") pp_item buf v = Buffer.add_string buf start; iteri (fun i x -> @@ -746,7 +746,7 @@ let pp ?(start="[") ?(stop="]") ?(sep=", ") pp_item buf v = ) v; Buffer.add_string buf stop -let print ?(start="[") ?(stop="]") ?(sep=", ") pp_item fmt v = +let print ?(start="") ?(stop="") ?(sep=", ") pp_item fmt v = Format.pp_print_string fmt start; iteri (fun i x -> diff --git a/src/data/CCImmutArray.ml b/src/data/CCImmutArray.ml index a775a586..e7e335c6 100644 --- a/src/data/CCImmutArray.ml +++ b/src/data/CCImmutArray.ml @@ -116,7 +116,7 @@ let to_gen a = type 'a printer = Format.formatter -> 'a -> unit -let print ?(start="[|") ?(stop="|]") ?(sep=";") pp_item out a = +let print ?(start="") ?(stop="") ?(sep=", ") pp_item out a = Format.pp_print_string out start; for k = 0 to Array.length a - 1 do if k > 0 then ( From 0d9d17d5db7f86ea0a922deebb50e09f98fdf1b5 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 3 Nov 2016 15:36:25 +0100 Subject: [PATCH 11/14] add `CCFormat.Dump` for easy debugging (see #82) --- src/core/CCFormat.ml | 28 ++++++++++++++++++++++++++++ src/core/CCFormat.mli | 29 +++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/src/core/CCFormat.ml b/src/core/CCFormat.ml index a2e567ac..8e356930 100644 --- a/src/core/CCFormat.ml +++ b/src/core/CCFormat.ml @@ -301,3 +301,31 @@ let ksprintf ~f fmt = Format.kfprintf (fun _ -> Format.pp_print_flush out (); f (Buffer.contents buf)) out fmt + + +module Dump = struct + type 'a t = 'a printer + let unit = unit + let int = int + let string = string_quoted + let bool = bool + let float = float + let char = char + let int32 = int32 + let int64 = int64 + let nativeint = nativeint + let list pp = hovbox (list ~start:"[" ~stop:"]" ~sep:";" pp) + let array pp = hovbox (array ~start:"[|" ~stop:"|]" ~sep:";" pp) + let option pp out x = match x with + | None -> Format.pp_print_string out "None" + | Some x -> Format.fprintf out "Some %a" pp x + let pair p1 p2 = pair p1 p2 + let triple p1 p2 p3 = triple p1 p2 p3 + let quad p1 p2 p3 p4 = quad p1 p2 p3 p4 +end + +(*$= & ~printer:(fun s->s) + "[1;2;3]" (to_string Dump.(list int) [1;2;3]) + "Some 1" (to_string Dump.(option int) (Some 1)) + "[None;Some \"a b\"]" (to_string Dump.(list (option string)) [None; Some "a b"]) +*) diff --git a/src/core/CCFormat.mli b/src/core/CCFormat.mli index 7d2fd22b..dd3f0194 100644 --- a/src/core/CCFormat.mli +++ b/src/core/CCFormat.mli @@ -172,3 +172,32 @@ val ksprintf : val to_file : string -> ('a, t, unit, unit) format4 -> 'a (** Print to the given file *) + +(** {2 Dump} + + Print structures as OCaml values, so that they can be parsed back + by OCaml (typically, in the toplevel, for debugging) + + @since NEXT_RELEASE *) + +module Dump : sig + type 'a t = 'a printer + val unit : unit t + val int : int t + val string : string t + val bool : bool t + val float : float t + val char : char t (** @since 0.14 *) + val int32 : int32 t (** @since 0.14 *) + val int64 : int64 t (** @since 0.14 *) + val nativeint : nativeint t (** @since 0.14 *) + + val list : 'a t -> 'a list t + val array : 'a t -> 'a array t + val option : 'a t -> 'a option t + val pair : 'a t -> 'b t -> ('a * 'b) t + val triple : 'a t -> 'b t -> 'c t -> ('a * 'b * 'c) t + val quad : + 'a t -> 'b t -> 'c t -> 'd t -> + ('a * 'b * 'c * 'd) t +end From 9045fcca0b410a7f010a7b8748d57ecb1b4c52ee Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 3 Nov 2016 15:48:21 +0100 Subject: [PATCH 12/14] add `CCFormat.{with_color_sf,fprintf_dyn_color,sprintf_dyn_color}` more dynamic way of adding colors, switching colors on/off, etc. --- src/core/CCFormat.ml | 20 ++++++++++++++++++++ src/core/CCFormat.mli | 28 ++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/core/CCFormat.ml b/src/core/CCFormat.ml index 8e356930..9b1fddaa 100644 --- a/src/core/CCFormat.ml +++ b/src/core/CCFormat.ml @@ -279,8 +279,28 @@ let sprintf_ c format = fmt format +let with_color_sf s fmt = + let buf = Buffer.create 64 in + let out = Format.formatter_of_buffer buf in + if !color_enabled then set_color_tag_handling out; + Format.pp_open_tag out s; + Format.kfprintf + (fun out -> + Format.pp_close_tag out (); + Format.pp_print_flush out (); + Buffer.contents buf) + out fmt + let sprintf fmt = sprintf_ true fmt let sprintf_no_color fmt = sprintf_ false fmt +let sprintf_dyn_color ~colors fmt = sprintf_ colors fmt + +let fprintf_dyn_color ~colors out fmt = + let old_tags = Format.pp_get_mark_tags out () in + Format.pp_set_mark_tags out colors; (* enable/disable tags *) + Format.kfprintf + (fun out -> Format.pp_set_mark_tags out old_tags) + out fmt (*$T sprintf "yolo %s %d" "a b" 42 = "yolo a b 42" diff --git a/src/core/CCFormat.mli b/src/core/CCFormat.mli index dd3f0194..9db0a194 100644 --- a/src/core/CCFormat.mli +++ b/src/core/CCFormat.mli @@ -137,6 +137,16 @@ val with_colorf : string -> t -> ('a, t, unit, unit) format4 -> 'a {b status: experimental} @since 0.16 *) +val with_color_sf : string -> ('a, t, unit, string) format4 -> 'a +(** [with_color_sf "Blue" out "%s %d" "yolo" 42] will behave like + {!sprintf}, but wrapping the content with the given style + Example: + {[ + CCFormat.with_color_sf "red" "%a" CCFormat.Dump.(list int) [1;2;3] |> print_endline;; + ]} + {b status: experimental} + @since NEXT_RELEASE *) + (** {2 IO} *) val output : t -> 'a printer -> 'a -> unit @@ -153,10 +163,28 @@ val sprintf_no_color : ('a, t, unit, string) format4 -> 'a (** Similar to {!sprintf} but never prints colors @since 0.16 *) +val sprintf_dyn_color : colors:bool -> ('a, t, unit, string) format4 -> 'a +(** Similar to {!sprintf} but enable/disable colors depending on [colors]. + Example: + {[ + (* with colors *) + CCFormat.sprintf_dyn_color ~colors:true "@{%a@}" + CCFormat.Dump.(list int) [1;2;3] |> print_endline;; + + (* without colors *) + CCFormat.sprintf_dyn_color ~colors:false "@{%a@}" + CCFormat.Dump.(list int) [1;2;3] |> print_endline;; + ]} + @since NEXT_RELEASE *) + val fprintf : t -> ('a, t, unit ) format -> 'a (** Alias to {!Format.fprintf} @since 0.14 *) +val fprintf_dyn_color : colors:bool -> t -> ('a, t, unit ) format -> 'a +(** Similar to {!fprintf} but enable/disable colors depending on [colors] + @since NEXT_RELEASE *) + val ksprintf : f:(string -> 'b) -> ('a, Format.formatter, unit, 'b) format4 -> From af4c3fc19545375b469b166d63c5d344b261a7f4 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 3 Nov 2016 15:56:02 +0100 Subject: [PATCH 13/14] change boxing in CCFormat.Dump; add example/doc --- src/core/CCFormat.ml | 4 ++-- src/core/CCFormat.mli | 19 +++++++++++++------ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/core/CCFormat.ml b/src/core/CCFormat.ml index 9b1fddaa..a02f6b23 100644 --- a/src/core/CCFormat.ml +++ b/src/core/CCFormat.ml @@ -334,8 +334,8 @@ module Dump = struct let int32 = int32 let int64 = int64 let nativeint = nativeint - let list pp = hovbox (list ~start:"[" ~stop:"]" ~sep:";" pp) - let array pp = hovbox (array ~start:"[|" ~stop:"|]" ~sep:";" pp) + let list pp = within "[" "]" (hovbox (list ~sep:";" pp)) + let array pp = within "[|" "|]" (hovbox (array ~sep:";" pp)) let option pp out x = match x with | None -> Format.pp_print_string out "None" | Some x -> Format.fprintf out "Some %a" pp x diff --git a/src/core/CCFormat.mli b/src/core/CCFormat.mli index 9db0a194..ec7c6efd 100644 --- a/src/core/CCFormat.mli +++ b/src/core/CCFormat.mli @@ -204,7 +204,15 @@ val to_file : string -> ('a, t, unit, unit) format4 -> 'a (** {2 Dump} Print structures as OCaml values, so that they can be parsed back - by OCaml (typically, in the toplevel, for debugging) + by OCaml (typically, in the toplevel, for debugging). + + Example: + {[ + Format.printf "%a@." CCFormat.Dump.(list int) CCList.(1 -- 200);; + + Format.printf "%a@." CCFormat.Dump.(array (list (pair int bool))) + [| [1, true; 2, false]; []; [42, false] |];; + ]} @since NEXT_RELEASE *) @@ -215,11 +223,10 @@ module Dump : sig val string : string t val bool : bool t val float : float t - val char : char t (** @since 0.14 *) - val int32 : int32 t (** @since 0.14 *) - val int64 : int64 t (** @since 0.14 *) - val nativeint : nativeint t (** @since 0.14 *) - + val char : char t + val int32 : int32 t + val int64 : int64 t + val nativeint : nativeint t val list : 'a t -> 'a list t val array : 'a t -> 'a array t val option : 'a t -> 'a option t From c7b815509df4eeea1c6f8b4ac4d7978bb52c0bd7 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 3 Nov 2016 16:04:12 +0100 Subject: [PATCH 14/14] prepare for 0.21 --- CHANGELOG.adoc | 15 +++++++++++++++ _oasis | 2 +- src/core/CCArray.ml | 6 +++--- src/core/CCArray.mli | 8 ++++---- src/core/CCFormat.mli | 8 ++++---- src/core/containers.ml | 2 +- 6 files changed, 28 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 506ae9df..a70fd23e 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -1,5 +1,20 @@ = Changelog +== 0.21 + +- (breaking) make default `start`/`stop` arguments empty in printers (#82) + +- add `CCFormat.{with_color_sf,fprintf_dyn_color,sprintf_dyn_color}` +- add `CCFormat.Dump` for easy debugging (see #82) +- add `CCArray.Sub.to_list` +- add `CCArray.{sorted,sort_indices,sort_ranking}` (closes #81) + +- handle '\r` in CCSexpM (fixes #83) +- add alias `Containers.IO` +- bugfixes in `CCArray.Sub` +- bugfix + tests for `CCArray.Sub.sub` +- disable parallel build to support cygwin + == 0.20 - bugfix in `CCArray.equal` diff --git a/_oasis b/_oasis index 82ecf65e..4ca0a03c 100644 --- a/_oasis +++ b/_oasis @@ -1,6 +1,6 @@ OASISFormat: 0.4 Name: containers -Version: 0.20 +Version: 0.21 Homepage: https://github.com/c-cube/ocaml-containers Authors: Simon Cruanes License: BSD-2-clause diff --git a/src/core/CCArray.ml b/src/core/CCArray.ml index 427ccf7b..f881489e 100644 --- a/src/core/CCArray.ml +++ b/src/core/CCArray.ml @@ -53,14 +53,14 @@ module type S = sig val sorted : ('a -> 'a -> int) -> 'a t -> 'a array (** [sorted cmp a] makes a copy of [a] and sorts it with [cmp]. - @since NEXT_RELEASE *) + @since 0.21 *) val sort_indices : ('a -> 'a -> int) -> 'a t -> int array (** [sort_indices cmp a] returns a new array [b], with the same length as [a], such that [b.(i)] is the index of the [i]-th element of [a] in [sort cmp a]. In other words, [map (fun i -> a.(i)) (sort_indices a) = sorted cmp a]. [a] is not modified. - @since NEXT_RELEASE *) + @since 0.21 *) val sort_ranking : ('a -> 'a -> int) -> 'a t -> int array (** [sort_ranking cmp a] returns a new array [b], with the same length as [a], @@ -72,7 +72,7 @@ module type S = sig Without duplicates, we also have [lookup_exn a.(i) (sorted a) = (sorted_ranking a).(i)] - @since NEXT_RELEASE *) + @since 0.21 *) val find : ('a -> 'b option) -> 'a t -> 'b option (** [find f a] returns [Some y] if there is an element [x] such diff --git a/src/core/CCArray.mli b/src/core/CCArray.mli index 44f7cc7b..11be5ebd 100644 --- a/src/core/CCArray.mli +++ b/src/core/CCArray.mli @@ -55,14 +55,14 @@ module type S = sig val sorted : ('a -> 'a -> int) -> 'a t -> 'a array (** [sorted cmp a] makes a copy of [a] and sorts it with [cmp]. - @since NEXT_RELEASE *) + @since 0.21 *) val sort_indices : ('a -> 'a -> int) -> 'a t -> int array (** [sort_indices cmp a] returns a new array [b], with the same length as [a], such that [b.(i)] is the index of the [i]-th element of [a] in [sort cmp a]. In other words, [map (fun i -> a.(i)) (sort_indices a) = sorted cmp a]. [a] is not modified. - @since NEXT_RELEASE *) + @since 0.21 *) val sort_ranking : ('a -> 'a -> int) -> 'a t -> int array (** [sort_ranking cmp a] returns a new array [b], with the same length as [a], @@ -74,7 +74,7 @@ module type S = sig Without duplicates, we also have [lookup_exn a.(i) (sorted a) = (sorted_ranking a).(i)] - @since NEXT_RELEASE *) + @since 0.21 *) val find : ('a -> 'b option) -> 'a t -> 'b option (** [find f a] returns [Some y] if there is an element [x] such @@ -249,7 +249,7 @@ module Sub : sig val to_list : 'a t -> 'a list (** Convert directly to a list - @since NEXT_RELEASE *) + @since 0.21 *) val full : 'a array -> 'a t (** Slice that covers the full array *) diff --git a/src/core/CCFormat.mli b/src/core/CCFormat.mli index ec7c6efd..98ddcc46 100644 --- a/src/core/CCFormat.mli +++ b/src/core/CCFormat.mli @@ -145,7 +145,7 @@ val with_color_sf : string -> ('a, t, unit, string) format4 -> 'a CCFormat.with_color_sf "red" "%a" CCFormat.Dump.(list int) [1;2;3] |> print_endline;; ]} {b status: experimental} - @since NEXT_RELEASE *) + @since 0.21 *) (** {2 IO} *) @@ -175,7 +175,7 @@ val sprintf_dyn_color : colors:bool -> ('a, t, unit, string) format4 -> 'a CCFormat.sprintf_dyn_color ~colors:false "@{%a@}" CCFormat.Dump.(list int) [1;2;3] |> print_endline;; ]} - @since NEXT_RELEASE *) + @since 0.21 *) val fprintf : t -> ('a, t, unit ) format -> 'a (** Alias to {!Format.fprintf} @@ -183,7 +183,7 @@ val fprintf : t -> ('a, t, unit ) format -> 'a val fprintf_dyn_color : colors:bool -> t -> ('a, t, unit ) format -> 'a (** Similar to {!fprintf} but enable/disable colors depending on [colors] - @since NEXT_RELEASE *) + @since 0.21 *) val ksprintf : f:(string -> 'b) -> @@ -214,7 +214,7 @@ val to_file : string -> ('a, t, unit, unit) format4 -> 'a [| [1, true; 2, false]; []; [42, false] |];; ]} - @since NEXT_RELEASE *) + @since 0.21 *) module Dump : sig type 'a t = 'a printer diff --git a/src/core/containers.ml b/src/core/containers.ml index 829c1f11..470bae8f 100644 --- a/src/core/containers.ml +++ b/src/core/containers.ml @@ -82,7 +82,7 @@ module Int64 = CCInt64 (** @since 0.13 *) module IO = CCIO -(** @since NEXT_RELEASE *) +(** @since 0.21 *) module Char = struct include Char