Merge branch 'master' into stable for 0.10

This commit is contained in:
Simon Cruanes 2015-04-10 16:43:16 +02:00
commit e268f2d10c
48 changed files with 2692 additions and 146 deletions

View file

@ -9,7 +9,6 @@ S src/threads/
S src/misc
S src/string
S src/bigarray
S src/pervasives
S benchs
S examples
S tests
@ -24,7 +23,6 @@ B _build/src/threads/
B _build/src/misc
B _build/src/string
B _build/src/bigarray
B _build/src/pervasives
B _build/benchs
B _build/examples
B _build/tests

View file

@ -1,11 +1,13 @@
#use "topfind";;
#thread
#require "bigarray";;
#require "unix";;
#directory "_build/src/core";;
#directory "_build/src/misc";;
#directory "_build/src/pervasives/";;
#directory "_build/src/string";;
#directory "_build/src/io";;
#directory "_build/src/unix";;
#directory "_build/src/iter";;
#directory "_build/src/data";;
#directory "_build/src/sexp";;
@ -16,6 +18,7 @@
#load "containers_iter.cma";;
#load "containers_data.cma";;
#load "containers_io.cma";;
#load "containers_unix.cma";;
#load "containers_sexp.cma";;
#load "containers_string.cma";;
#load "containers_pervasives.cma";;

View file

@ -10,3 +10,4 @@
- Bernardo da Costa
- Vincent Bernardoff (vbmithr)
- Emmanuel Surleau (emm)
- Guillaume Bury (guigui)

View file

@ -1,5 +1,20 @@
# Changelog
## 0.10
- add `containers_misc.Puf.iter`
- add `CCString.{lines,unlines,concat_gen}`
- `CCUnix` (with a small subprocess API)
- add `CCList.{sorted_merge_uniq, uniq_succ}`
- breaking: fix documentation of `CCList.sorted_merge` (different semantics)
- `CCPersistentArray` (credit to @gbury and Jean-Christophe Filliâtre)
- `CCIntMap` (big-endian patricia trees) in containers.data
- bugfix in `CCFQueue.add_seq_front`
- add `CCFQueue.{rev, --}`
- add `App_parse` in `containers_string`, experimental applicative parser combinators
- remove `containers.pervasives`, add the module `Containers` to core
- bugfix in `CCFormat.to_file`
## 0.9
- add `Float`, `Ref`, `Set`, `Format` to `CCPervasives`
@ -178,7 +193,7 @@
- renamed threads/future to threads/CCFuture
- big upgrade of `RAL` (random access lists)
- `CCList.Ref` to help use references on lists
- `CCKList`: group,uniq,sort,sort_uniq,repeat and cycle, infix ops, applicative,product
- `CCKList`: `group,uniq,sort,sort_uniq,repeat` and `cycle`, infix ops, applicative,product
- `CCTrie.above/below`: ranges of items
- more functions in `CCPair`
- `CCCat`: funny (though useless) definitions inspired from Haskell
@ -192,7 +207,7 @@
- conversions for `CCString`
- `CCHashtbl`: open-addressing table (Robin-Hood hashing)
- registered printers for `CCError`.guard,wrap1,etc.
- monadic operator in `CCList`: map_m_par
- monadic operator in `CCList`: `map_m_par`
- simple interface to `PrintBox` now more powerful
- constructors for 1 or 2 elements fqueues
- bugfixes in BTree (insertion should work now)
@ -206,7 +221,7 @@
- `CCopt.pure`
- updated `CCPersistentHashtbl` with new functions; updated doc, simplified code
- move `CCString` into core/, since it deals with a basic type; also add some features to `CCString` (Sub and Split modules to deal with slices and splitting by a string)
- `CCArray.blit`, .Sub.to_slice; some bugfixes
- `CCArray.blit`, `.Sub.to_slice`; some bugfixes
- applicative and lifting operators for `CCError`
- `CCError.map2`
- more combinators in `CCError`
@ -219,9 +234,9 @@
- `CCOpt.sequence_l`
- mplus instance for `CCOpt`
- monad instance for `CCFun`
- updated description in _oasis
- updated description in `_oasis`
- `CCTrie`, a compressed functorial persistent trie structure
- fix `CCPrint.unit`, add `CCPrint.silent`
- fix type mismatch
note: git log --no-merges previous_version..HEAD --pretty=%s
note: `git log --no-merges previous_version..HEAD --pretty=%s`

View file

@ -1,7 +1,7 @@
## Make a release
1. `make test-all`
1. `make test`
2. update version in `_oasis`
3. `make update_next_tag` (to update `@since` comments; be careful not to change symlinks)
4. update `CHANGELOG.md` (see its end to find the right git command)

View file

@ -60,6 +60,8 @@ QTESTABLE=$(filter-out $(DONTTEST), \
$(wildcard src/string/*.mli) \
$(wildcard src/io/*.ml) \
$(wildcard src/io/*.mli) \
$(wildcard src/unix/*.ml) \
$(wildcard src/unix/*.mli) \
$(wildcard src/sexp/*.ml) \
$(wildcard src/sexp/*.mli) \
$(wildcard src/advanced/*.ml) \

View file

@ -10,7 +10,10 @@ What is _containers_?
are totally independent and are prefixed with `CC` (for "containers-core"
or "companion-cube" because I'm megalomaniac). This part should be
usable and should work. For instance, `CCList` contains functions and
lists including safe versions of `map` and `append`.
lists including safe versions of `map` and `append`. It also
provides a drop-in replacement to the standard library, in the module
`Containers` (intended to be opened, replaces some stdlib modules
with extended ones)
- Several small additional libraries that complement it:
* `containers.data` with additional data structures that don't have an
equivalent in the standard library;
@ -21,9 +24,6 @@ What is _containers_?
KMP search algorithm, and a few naive utils). Again, modules are independent
and sometimes parametric on the string and char types (so they should
be able to deal with your favorite unicode library).
- A drop-in replacement to the standard library, `containers.pervasives`,
that defined a `CCPervasives` module intented to be opened to extend some
modules of the stdlib.
- A sub-library with complicated abstractions, `containers.advanced` (with
a LINQ-like query module, batch operations using GADTs, and others).
- A library using [Lwt](https://github.com/ocsigen/lwt/), `containers.lwt`.
@ -45,7 +45,7 @@ See [this file](https://github.com/c-cube/ocaml-containers/blob/master/CHANGELOG
## Finding help
- the [github wiki](https://github.com/c-cube/ocaml-containers/wiki)
- the IRC channel (`##ocaml-containers` on Freenode)
- on IRC, ask `companion_cube` on `#ocaml`
## Use
@ -109,6 +109,10 @@ Documentation [here](http://cedeela.fr/~simon/software/containers).
- `CCIO`, basic utilities for IO
### Containers.unix
- `CCUnix`, utils for `Unix`
### Containers.sexp
A small S-expression library.

49
_oasis
View file

@ -1,6 +1,6 @@
OASISFormat: 0.4
Name: containers
Version: 0.9
Version: 0.10
Homepage: https://github.com/c-cube/ocaml-containers
Authors: Simon Cruanes
License: BSD-2-clause
@ -25,6 +25,10 @@ Flag "misc"
Description: Build the misc library, with experimental modules still susceptible to change
Default: true
Flag "unix"
Description: Build the containers.unix library (depends on Unix)
Default: false
Flag "lwt"
Description: Build modules which depend on Lwt
Default: false
@ -49,7 +53,8 @@ Library "containers"
Path: src/core
Modules: CCVector, CCPrint, CCError, CCHeap, CCList, CCOpt, CCPair,
CCFun, CCHash, CCInt, CCBool, CCFloat, CCArray, CCRef, CCSet,
CCOrd, CCRandom, CCString, CCHashtbl, CCMap, CCFormat
CCOrd, CCRandom, CCString, CCHashtbl, CCMap, CCFormat,
Containers
BuildDepends: bytes
Library "containers_io"
@ -59,6 +64,13 @@ Library "containers_io"
FindlibParent: containers
FindlibName: io
Library "containers_unix"
Path: src/unix
Modules: CCUnix
BuildDepends: bytes, unix
FindlibParent: containers
FindlibName: unix
Library "containers_sexp"
Path: src/sexp
Modules: CCSexp, CCSexpStream, CCSexpM
@ -70,7 +82,7 @@ Library "containers_data"
Path: src/data
Modules: CCMultiMap, CCMultiSet, CCTrie, CCFlatHashtbl, CCCache,
CCPersistentHashtbl, CCDeque, CCFQueue, CCBV, CCMixtbl,
CCMixmap, CCRingBuffer
CCMixmap, CCRingBuffer, CCIntMap, CCPersistentArray
BuildDepends: bytes
FindlibParent: containers
FindlibName: data
@ -84,7 +96,8 @@ Library "containers_iter"
Library "containers_string"
Path: src/string
Pack: true
Modules: KMP, Levenshtein
Modules: KMP, Levenshtein, App_parse
BuildDepends: bytes
FindlibName: string
FindlibParent: containers
@ -105,18 +118,12 @@ Library "containers_bigarray"
FindlibParent: containers
BuildDepends: containers, bigarray, bytes
Library "containers_pervasives"
Path: src/pervasives
Modules: CCPervasives
BuildDepends: containers
FindlibName: pervasives
FindlibParent: containers
Library "containers_misc"
Path: src/misc
Pack: true
Modules: AbsSet, Automaton, Bij, CSM, Hashset, LazyGraph, PHashtbl,
PrintBox, RAL, RoseTree, SmallSet, UnionFind, Univ
PrintBox, RAL, RoseTree, SmallSet, UnionFind, Univ, Puf,
Backtrack
BuildDepends: containers, containers.data
FindlibName: misc
FindlibParent: containers
@ -145,15 +152,15 @@ Document containers
Title: Containers docs
Type: ocamlbuild (0.3)
BuildTools+: ocamldoc
Build$: flag(docs) && flag(advanced) && flag(bigarray) && flag(lwt) && flag(misc)
Build$: flag(docs) && flag(advanced) && flag(bigarray) && flag(lwt) && flag(misc) && flag(unix)
Install: true
XOCamlbuildPath: .
XOCamlbuildExtraArgs:
"-docflags '-colorize-code -short-functors -charset utf-8'"
XOCamlbuildLibraries:
containers, containers.misc, containers.iter, containers.data,
containers.string, containers.pervasives, containers.bigarray,
containers.advanced, containers.io, containers.sexp,
containers.string, containers.bigarray,
containers.advanced, containers.io, containers.unix, containers.sexp,
containers.lwt
Executable run_benchs
@ -166,12 +173,12 @@ Executable run_benchs
containers.data, containers.string, containers.iter,
sequence, gen, benchmark
Executable bench_hash
Executable run_bench_hash
Path: benchs/
Install: false
CompiledObject: best
Build$: flag(bench) && flag(misc)
MainIs: bench_hash.ml
MainIs: run_bench_hash.ml
BuildDepends: containers, containers.misc
Executable run_test_future
@ -194,11 +201,11 @@ Executable run_qtest
Install: false
CompiledObject: best
MainIs: run_qtest.ml
Build$: flag(tests) && flag(bigarray)
Build$: flag(tests) && flag(misc) && flag(bigarray) && flag(unix) && flag(advanced)
BuildDepends: containers, containers.misc, containers.string, containers.iter,
containers.io, containers.advanced, containers.sexp,
containers.bigarray,
sequence, gen, oUnit, QTest2Lib
containers.bigarray, containers.unix,
sequence, gen, unix, oUnit, QTest2Lib
Executable run_qtest_lwt
Path: qtest/lwt/
@ -222,7 +229,7 @@ Executable run_tests
Test all
Command: make test-all
TestTools: run_tests, run_qtest
Run$: flag(tests) && flag(misc)
Run$: flag(tests) && flag(misc) && flag(unix) && flag(advanced) && flag(bigarray)
Test lwt
Command: echo "test lwt"; ./run_qtest_lwt.native

28
_tags
View file

@ -1,5 +1,5 @@
# OASIS_START
# DO NOT EDIT (digest: 4bc9d475d595a814a666d126274b25b1)
# DO NOT EDIT (digest: 2d4ff427096956a049556073cd9b4191)
# Ignore VCS directories, you can use the same kind of rule outside
# OASIS_START/STOP if you want to exclude directories that contains
# useless stuff for the build process
@ -20,6 +20,10 @@ true: annot, bin_annot
# Library containers_io
"src/io/containers_io.cmxs": use_containers_io
<src/io/*.ml{,i,y}>: package(bytes)
# Library containers_unix
"src/unix/containers_unix.cmxs": use_containers_unix
<src/unix/*.ml{,i,y}>: package(bytes)
<src/unix/*.ml{,i,y}>: package(unix)
# Library containers_sexp
"src/sexp/containers_sexp.cmxs": use_containers_sexp
<src/sexp/*.ml{,i,y}>: package(bytes)
@ -32,6 +36,8 @@ true: annot, bin_annot
"src/string/containers_string.cmxs": use_containers_string
"src/string/KMP.cmx": for-pack(Containers_string)
"src/string/levenshtein.cmx": for-pack(Containers_string)
"src/string/app_parse.cmx": for-pack(Containers_string)
<src/string/*.ml{,i,y}>: package(bytes)
# Library containers_advanced
"src/advanced/containers_advanced.cmxs": use_containers_advanced
"src/advanced/CCLinq.cmx": for-pack(Containers_advanced)
@ -46,10 +52,6 @@ true: annot, bin_annot
<src/bigarray/*.ml{,i,y}>: package(bigarray)
<src/bigarray/*.ml{,i,y}>: package(bytes)
<src/bigarray/*.ml{,i,y}>: use_containers
# Library containers_pervasives
"src/pervasives/containers_pervasives.cmxs": use_containers_pervasives
<src/pervasives/*.ml{,i,y}>: package(bytes)
<src/pervasives/*.ml{,i,y}>: use_containers
# Library containers_misc
"src/misc/containers_misc.cmxs": use_containers_misc
"src/misc/absSet.cmx": for-pack(Containers_misc)
@ -65,6 +67,8 @@ true: annot, bin_annot
"src/misc/smallSet.cmx": for-pack(Containers_misc)
"src/misc/unionFind.cmx": for-pack(Containers_misc)
"src/misc/univ.cmx": for-pack(Containers_misc)
"src/misc/puf.cmx": for-pack(Containers_misc)
"src/misc/backtrack.cmx": for-pack(Containers_misc)
<src/misc/*.ml{,i,y}>: package(bytes)
<src/misc/*.ml{,i,y}>: use_containers
<src/misc/*.ml{,i,y}>: use_containers_data
@ -101,11 +105,11 @@ true: annot, bin_annot
<benchs/*.ml{,i,y}>: use_containers_advanced
<benchs/*.ml{,i,y}>: use_containers_iter
<benchs/*.ml{,i,y}>: use_containers_string
# Executable bench_hash
<benchs/bench_hash.{native,byte}>: package(bytes)
<benchs/bench_hash.{native,byte}>: use_containers
<benchs/bench_hash.{native,byte}>: use_containers_data
<benchs/bench_hash.{native,byte}>: use_containers_misc
# Executable run_bench_hash
<benchs/run_bench_hash.{native,byte}>: package(bytes)
<benchs/run_bench_hash.{native,byte}>: use_containers
<benchs/run_bench_hash.{native,byte}>: use_containers_data
<benchs/run_bench_hash.{native,byte}>: use_containers_misc
<benchs/*.ml{,i,y}>: package(bytes)
<benchs/*.ml{,i,y}>: use_containers
<benchs/*.ml{,i,y}>: use_containers_data
@ -130,6 +134,7 @@ true: annot, bin_annot
<qtest/run_qtest.{native,byte}>: package(gen)
<qtest/run_qtest.{native,byte}>: package(oUnit)
<qtest/run_qtest.{native,byte}>: package(sequence)
<qtest/run_qtest.{native,byte}>: package(unix)
<qtest/run_qtest.{native,byte}>: use_containers
<qtest/run_qtest.{native,byte}>: use_containers_advanced
<qtest/run_qtest.{native,byte}>: use_containers_bigarray
@ -139,12 +144,14 @@ true: annot, bin_annot
<qtest/run_qtest.{native,byte}>: use_containers_misc
<qtest/run_qtest.{native,byte}>: use_containers_sexp
<qtest/run_qtest.{native,byte}>: use_containers_string
<qtest/run_qtest.{native,byte}>: use_containers_unix
<qtest/*.ml{,i,y}>: package(QTest2Lib)
<qtest/*.ml{,i,y}>: package(bigarray)
<qtest/*.ml{,i,y}>: package(bytes)
<qtest/*.ml{,i,y}>: package(gen)
<qtest/*.ml{,i,y}>: package(oUnit)
<qtest/*.ml{,i,y}>: package(sequence)
<qtest/*.ml{,i,y}>: package(unix)
<qtest/*.ml{,i,y}>: use_containers
<qtest/*.ml{,i,y}>: use_containers_advanced
<qtest/*.ml{,i,y}>: use_containers_bigarray
@ -154,6 +161,7 @@ true: annot, bin_annot
<qtest/*.ml{,i,y}>: use_containers_misc
<qtest/*.ml{,i,y}>: use_containers_sexp
<qtest/*.ml{,i,y}>: use_containers_string
<qtest/*.ml{,i,y}>: use_containers_unix
# Executable run_qtest_lwt
<qtest/lwt/run_qtest_lwt.{native,byte}>: package(QTest2Lib)
<qtest/lwt/run_qtest_lwt.{native,byte}>: package(bytes)

View file

@ -4,6 +4,7 @@ module B = Benchmark
let (@>) = B.Tree.(@>)
let (@>>) = B.Tree.(@>>)
let (@>>>) = B.Tree.(@>>>)
let (|>) = CCFun.(|>)
let app_int f n = string_of_int n @> lazy (f n)
let app_ints f l = B.Tree.concat (List.map (app_int f) l)
@ -234,6 +235,13 @@ module Tbl = struct
done;
!h
let intmap_add n =
let h = ref CCIntMap.empty in
for i = n downto 0 do
h := CCIntMap.add i i !h;
done;
!h
let icchashtbl_add n =
let h = ICCHashtbl.create 50 in
for i = n downto 0 do
@ -248,6 +256,7 @@ module Tbl = struct
"ihashtbl_add", (fun n -> ignore (ihashtbl_add n)), n;
"ipersistenthashtbl_add", (fun n -> ignore (ipersistenthashtbl_add n)), n;
"imap_add", (fun n -> ignore (imap_add n)), n;
"intmap_add", (fun n -> ignore (intmap_add n)), n;
"ccflathashtbl_add", (fun n -> ignore (icchashtbl_add n)), n;
]
@ -301,6 +310,16 @@ module Tbl = struct
done;
!h
let intmap_replace n =
let h = ref CCIntMap.empty in
for i = 0 to n do
h := CCIntMap.add i i !h;
done;
for i = n downto 0 do
h := CCIntMap.add i i !h;
done;
!h
let icchashtbl_replace n =
let h = ICCHashtbl.create 50 in
for i = 0 to n do
@ -318,11 +337,10 @@ module Tbl = struct
"ihashtbl_replace", (fun n -> ignore (ihashtbl_replace n)), n;
"ipersistenthashtbl_replace", (fun n -> ignore (ipersistenthashtbl_replace n)), n;
"imap_replace", (fun n -> ignore (imap_replace n)), n;
"intmap_replace", (fun n -> ignore (intmap_replace n)), n;
"ccflathashtbl_replace", (fun n -> ignore (icchashtbl_replace n)), n;
]
let my_len = 250
let phashtbl_find h =
fun n ->
for i = 0 to n-1 do
@ -353,12 +371,24 @@ module Tbl = struct
ignore (Array.get a i);
done
let persistent_array_find a =
fun n ->
for i = 0 to n-1 do
ignore (CCPersistentArray.get a i);
done
let imap_find m =
fun n ->
for i = 0 to n-1 do
ignore (IMap.find i m);
done
let intmap_find m =
fun n ->
for i = 0 to n-1 do
ignore (CCIntMap.find i m);
done
let icchashtbl_find m =
fun n ->
for i = 0 to n-1 do
@ -370,8 +400,10 @@ module Tbl = struct
let h' = hashtbl_add n in
let h'' = ihashtbl_add n in
let h''''' = ipersistenthashtbl_add n in
let a = Array.init n (fun i -> string_of_int i) in
let a = Array.init n string_of_int in
let pa = CCPersistentArray.init n string_of_int in
let m = imap_add n in
let m' = intmap_add n in
let h'''''' = icchashtbl_add n in
B.throughputN 3 [
"phashtbl_find", (fun () -> phashtbl_find h n), ();
@ -379,7 +411,9 @@ module Tbl = struct
"ihashtbl_find", (fun () -> ihashtbl_find h'' n), ();
"ipersistenthashtbl_find", (fun () -> ipersistenthashtbl_find h''''' n), ();
"array_find", (fun () -> array_find a n), ();
"persistent_array_find", (fun () -> persistent_array_find pa n), ();
"imap_find", (fun () -> imap_find m n), ();
"intmap_find", (fun () -> intmap_find m' n), ();
"cchashtbl_find", (fun () -> icchashtbl_find h'''''' n), ();
]

View file

@ -1,5 +1,5 @@
# OASIS_START
# DO NOT EDIT (digest: 98c09c3ae4c860914660bcfa48ec375f)
# DO NOT EDIT (digest: 463813d3e54d45bc5b6a9d7d4eb17cd0)
src/core/CCVector
src/core/CCPrint
src/core/CCError
@ -21,6 +21,7 @@ src/core/CCString
src/core/CCHashtbl
src/core/CCMap
src/core/CCFormat
src/core/Containers
src/misc/AbsSet
src/misc/Automaton
src/misc/Bij
@ -34,6 +35,8 @@ src/misc/RoseTree
src/misc/SmallSet
src/misc/UnionFind
src/misc/Univ
src/misc/Puf
src/misc/Backtrack
src/iter/CCKTree
src/iter/CCKList
src/data/CCMultiMap
@ -48,15 +51,18 @@ src/data/CCBV
src/data/CCMixtbl
src/data/CCMixmap
src/data/CCRingBuffer
src/data/CCIntMap
src/data/CCPersistentArray
src/string/KMP
src/string/Levenshtein
src/pervasives/CCPervasives
src/string/App_parse
src/bigarray/CCBigstring
src/advanced/CCLinq
src/advanced/CCBatch
src/advanced/CCCat
src/advanced/CCMonadIO
src/io/CCIO
src/unix/CCUnix
src/sexp/CCSexp
src/sexp/CCSexpStream
src/sexp/CCSexpM

View file

@ -44,11 +44,11 @@ CCRef
CCSet
CCString
CCVector
Containers
}
{4 Pervasives (aliases to Core Modules)}
Contains aliases to most modules from {i containers core}, and mixins
The module {!Containers} contains aliases to most other modules defined
in {i containers core}, and mixins
such as:
{[ module List = struct
@ -57,8 +57,6 @@ such as:
end
]}
{!modules: CCPervasives}
{4 Containers.data}
Various data structures.
@ -68,10 +66,12 @@ CCBV
CCCache
CCFQueue
CCFlatHashtbl
CCIntMap
CCMixmap
CCMixtbl
CCMultiMap
CCMultiSet
CCPersistentArray
CCPersistentHashtbl
CCRingBuffer
CCTrie
@ -83,6 +83,12 @@ Helpers to perform simple IO (mostly on files) and iterate on channels.
{!modules: CCIO}
{4 Containers.unix}
Helpers that depend on {!Unix}, e.g. to spawn sub-processes.
{!modules: CCUnix}
{4 Containers.sexp}
A small S-expression library. The interface is relatively unstable, but

View file

@ -1,5 +1,5 @@
(* OASIS_START *)
(* DO NOT EDIT (digest: fb8dea068c03b0d63bc05634c5db1689) *)
(* DO NOT EDIT (digest: c0298c035a279ad3c641dc2bb1ecc03b) *)
module OASISGettext = struct
(* # 22 "src/oasis/OASISGettext.ml" *)
@ -611,13 +611,13 @@ let package_default =
[
("containers", ["src/core"], []);
("containers_io", ["src/io"], []);
("containers_unix", ["src/unix"], []);
("containers_sexp", ["src/sexp"], []);
("containers_data", ["src/data"], []);
("containers_iter", ["src/iter"], []);
("containers_string", ["src/string"], []);
("containers_advanced", ["src/advanced"], []);
("containers_bigarray", ["src/bigarray"], []);
("containers_pervasives", ["src/pervasives"], []);
("containers_misc", ["src/misc"], []);
("containers_thread", ["src/threads"], []);
("containers_lwt", ["src/lwt"], [])
@ -629,7 +629,6 @@ let package_default =
("tests/threads", ["src/core"; "src/threads"]);
("tests", ["src/core"; "src/data"; "src/misc"; "src/string"]);
("src/threads", ["src/core"]);
("src/pervasives", ["src/core"]);
("src/misc", ["src/core"; "src/data"]);
("src/lwt", ["src/core"; "src/misc"]);
("src/bigarray", ["src/core"]);
@ -644,7 +643,8 @@ let package_default =
"src/iter";
"src/misc";
"src/sexp";
"src/string"
"src/string";
"src/unix"
]);
("examples", ["src/core"; "src/misc"; "src/sexp"]);
("benchs",

1
opam
View file

@ -12,6 +12,7 @@ build: [
"--%{lwt:enable}%-lwt"
"--%{base-bigarray:enable}%-bigarray"
"--%{sequence:enable}%-advanced"
"--%{base-unix:enable}%-unix"
"--enable-docs"
"--enable-misc"]
[make "build"]

126
setup.ml
View file

@ -1,7 +1,7 @@
(* setup.ml generated for the first time by OASIS v0.4.4 *)
(* OASIS_START *)
(* DO NOT EDIT (digest: d2414bb4ed47c14d1e696e080da28357) *)
(* DO NOT EDIT (digest: bc1fcdeddb836af6942617417a65ae05) *)
(*
Regenerated by OASIS v0.4.5
Visit http://oasis.forge.ocamlcore.org for more information and
@ -6965,7 +6965,7 @@ let setup_t =
alpha_features = ["ocamlbuild_more_args"];
beta_features = [];
name = "containers";
version = "0.9";
version = "0.10";
license =
OASISLicense.DEP5License
(OASISLicense.DEP5Unit
@ -7041,6 +7041,18 @@ let setup_t =
"Build the misc library, with experimental modules still susceptible to change";
flag_default = [(OASISExpr.EBool true, true)]
});
Flag
({
cs_name = "unix";
cs_data = PropList.Data.create ();
cs_plugin_data = []
},
{
flag_description =
Some
"Build the containers.unix library (depends on Unix)";
flag_default = [(OASISExpr.EBool true, false)]
});
Flag
({
cs_name = "lwt";
@ -7141,7 +7153,8 @@ let setup_t =
"CCString";
"CCHashtbl";
"CCMap";
"CCFormat"
"CCFormat";
"Containers"
];
lib_pack = false;
lib_internal_modules = [];
@ -7179,6 +7192,40 @@ let setup_t =
lib_findlib_name = Some "io";
lib_findlib_containers = []
});
Library
({
cs_name = "containers_unix";
cs_data = PropList.Data.create ();
cs_plugin_data = []
},
{
bs_build = [(OASISExpr.EBool true, true)];
bs_install = [(OASISExpr.EBool true, true)];
bs_path = "src/unix";
bs_compiled_object = Best;
bs_build_depends =
[
FindlibPackage ("bytes", None);
FindlibPackage ("unix", None)
];
bs_build_tools = [ExternalTool "ocamlbuild"];
bs_c_sources = [];
bs_data_files = [];
bs_ccopt = [(OASISExpr.EBool true, [])];
bs_cclib = [(OASISExpr.EBool true, [])];
bs_dlllib = [(OASISExpr.EBool true, [])];
bs_dllpath = [(OASISExpr.EBool true, [])];
bs_byteopt = [(OASISExpr.EBool true, [])];
bs_nativeopt = [(OASISExpr.EBool true, [])]
},
{
lib_modules = ["CCUnix"];
lib_pack = false;
lib_internal_modules = [];
lib_findlib_parent = Some "containers";
lib_findlib_name = Some "unix";
lib_findlib_containers = []
});
Library
({
cs_name = "containers_sexp";
@ -7245,7 +7292,9 @@ let setup_t =
"CCBV";
"CCMixtbl";
"CCMixmap";
"CCRingBuffer"
"CCRingBuffer";
"CCIntMap";
"CCPersistentArray"
];
lib_pack = false;
lib_internal_modules = [];
@ -7294,7 +7343,7 @@ let setup_t =
bs_install = [(OASISExpr.EBool true, true)];
bs_path = "src/string";
bs_compiled_object = Best;
bs_build_depends = [];
bs_build_depends = [FindlibPackage ("bytes", None)];
bs_build_tools = [ExternalTool "ocamlbuild"];
bs_c_sources = [];
bs_data_files = [];
@ -7306,7 +7355,7 @@ let setup_t =
bs_nativeopt = [(OASISExpr.EBool true, [])]
},
{
lib_modules = ["KMP"; "Levenshtein"];
lib_modules = ["KMP"; "Levenshtein"; "App_parse"];
lib_pack = true;
lib_internal_modules = [];
lib_findlib_parent = Some "containers";
@ -7391,36 +7440,6 @@ let setup_t =
lib_findlib_name = Some "bigarray";
lib_findlib_containers = []
});
Library
({
cs_name = "containers_pervasives";
cs_data = PropList.Data.create ();
cs_plugin_data = []
},
{
bs_build = [(OASISExpr.EBool true, true)];
bs_install = [(OASISExpr.EBool true, true)];
bs_path = "src/pervasives";
bs_compiled_object = Best;
bs_build_depends = [InternalLibrary "containers"];
bs_build_tools = [ExternalTool "ocamlbuild"];
bs_c_sources = [];
bs_data_files = [];
bs_ccopt = [(OASISExpr.EBool true, [])];
bs_cclib = [(OASISExpr.EBool true, [])];
bs_dlllib = [(OASISExpr.EBool true, [])];
bs_dllpath = [(OASISExpr.EBool true, [])];
bs_byteopt = [(OASISExpr.EBool true, [])];
bs_nativeopt = [(OASISExpr.EBool true, [])]
},
{
lib_modules = ["CCPervasives"];
lib_pack = false;
lib_internal_modules = [];
lib_findlib_parent = Some "containers";
lib_findlib_name = Some "pervasives";
lib_findlib_containers = []
});
Library
({
cs_name = "containers_misc";
@ -7462,7 +7481,9 @@ let setup_t =
"RoseTree";
"SmallSet";
"UnionFind";
"Univ"
"Univ";
"Puf";
"Backtrack"
];
lib_pack = true;
lib_internal_modules = [];
@ -7592,7 +7613,9 @@ let setup_t =
(OASISExpr.EFlag "bigarray",
OASISExpr.EAnd
(OASISExpr.EFlag "lwt",
OASISExpr.EFlag "misc"))))),
OASISExpr.EAnd
(OASISExpr.EFlag "misc",
OASISExpr.EFlag "unix")))))),
true)
];
doc_install = [(OASISExpr.EBool true, true)];
@ -7648,7 +7671,7 @@ let setup_t =
{exec_custom = false; exec_main_is = "run_benchs.ml"});
Executable
({
cs_name = "bench_hash";
cs_name = "run_bench_hash";
cs_data = PropList.Data.create ();
cs_plugin_data = []
},
@ -7679,7 +7702,7 @@ let setup_t =
bs_byteopt = [(OASISExpr.EBool true, [])];
bs_nativeopt = [(OASISExpr.EBool true, [])]
},
{exec_custom = false; exec_main_is = "bench_hash.ml"});
{exec_custom = false; exec_main_is = "run_bench_hash.ml"});
Executable
({
cs_name = "run_test_future";
@ -7772,7 +7795,13 @@ let setup_t =
(OASISExpr.EBool true, false);
(OASISExpr.EAnd
(OASISExpr.EFlag "tests",
OASISExpr.EFlag "bigarray"),
OASISExpr.EAnd
(OASISExpr.EFlag "misc",
OASISExpr.EAnd
(OASISExpr.EFlag "bigarray",
OASISExpr.EAnd
(OASISExpr.EFlag "unix",
OASISExpr.EFlag "advanced")))),
true)
];
bs_install = [(OASISExpr.EBool true, false)];
@ -7788,8 +7817,10 @@ let setup_t =
InternalLibrary "containers_advanced";
InternalLibrary "containers_sexp";
InternalLibrary "containers_bigarray";
InternalLibrary "containers_unix";
FindlibPackage ("sequence", None);
FindlibPackage ("gen", None);
FindlibPackage ("unix", None);
FindlibPackage ("oUnit", None);
FindlibPackage ("QTest2Lib", None)
];
@ -7908,7 +7939,13 @@ let setup_t =
(OASISExpr.EFlag "tests",
OASISExpr.EAnd
(OASISExpr.EFlag "tests",
OASISExpr.EFlag "misc")),
OASISExpr.EAnd
(OASISExpr.EFlag "misc",
OASISExpr.EAnd
(OASISExpr.EFlag "unix",
OASISExpr.EAnd
(OASISExpr.EFlag "advanced",
OASISExpr.EFlag "bigarray"))))),
true)
];
test_tools =
@ -8063,7 +8100,8 @@ let setup_t =
};
oasis_fn = Some "_oasis";
oasis_version = "0.4.5";
oasis_digest = Some "\180\018\197c\134\002\173(\245'\138\144\0262\197z";
oasis_digest =
Some "Q\133\224\006'\239^\194\020\007 \247\168\220\142\188";
oasis_exec = None;
oasis_setup_args = [];
setup_update = false
@ -8071,6 +8109,6 @@ let setup_t =
let setup () = BaseSetup.setup setup_t;;
# 8075 "setup.ml"
# 8113 "setup.ml"
(* OASIS_STOP *)
let () = setup ();;

View file

@ -113,8 +113,12 @@ val wrap3 : ('a -> 'b -> 'c -> 'd) -> 'a -> 'b -> 'c -> ('d, exn) t
(** {2 Applicative} *)
val pure : 'a -> ('a, 'err) t
(** Synonym of {!return} *)
val (<*>) : ('a -> 'b, 'err) t -> ('a, 'err) t -> ('b, 'err) t
(** [a <*> b] evaluates [a] and [b], and, in case of success, returns
[`Ok (a b)]. Otherwise, it fails, and the error of [a] is chosen
over the error of [b] if both fail *)
(** {2 Collections} *)

View file

@ -231,6 +231,12 @@ let sorted_merge ?(cmp=Pervasives.compare) l1 l2 =
(*$T
List.sort Pervasives.compare ([(( * )2); ((+)1)] <*> [10;100]) \
= [11; 20; 101; 200]
sorted_merge [1;1;2] [1;2;3] = [1;1;1;2;2;3]
*)
(*$Q
Q.(pair (list int) (list int)) (fun (l1,l2) -> \
List.length (sorted_merge l1 l2) = List.length l1 + List.length l2)
*)
let sort_uniq (type elt) ?(cmp=Pervasives.compare) l =
@ -247,6 +253,56 @@ let sort_uniq (type elt) ?(cmp=Pervasives.compare) l =
sort_uniq [10;10;10;10;1;10] = [1;10]
*)
let uniq_succ ?(eq=(=)) l =
let rec f acc l = match l with
| [] -> List.rev acc
| [x] -> List.rev (x::acc)
| x :: ((y :: _) as tail) when eq x y -> f acc tail
| x :: tail -> f (x::acc) tail
in
f [] l
(*$T
uniq_succ [1;1;2;3;1;6;6;4;6;1] = [1;2;3;1;6;4;6;1]
*)
let sorted_merge_uniq ?(cmp=Pervasives.compare) l1 l2 =
let push ~cmp acc x = match acc with
| [] -> [x]
| y :: _ when cmp x y > 0 -> x :: acc
| _ -> acc (* duplicate, do not yield *)
in
let rec recurse ~cmp acc l1 l2 = match l1,l2 with
| [], l
| l, [] ->
let acc = List.fold_left (push ~cmp) acc l in
List.rev acc
| x1::l1', x2::l2' ->
let c = cmp x1 x2 in
if c < 0 then recurse ~cmp (push ~cmp acc x1) l1' l2
else if c > 0 then recurse ~cmp (push ~cmp acc x2) l1 l2'
else recurse ~cmp acc l1 l2' (* drop one of the [x] *)
in
recurse ~cmp [] l1 l2
(*$T
sorted_merge_uniq [1; 1; 2; 3; 5; 8] [1; 2; 3; 4; 6; 8; 9; 9] = [1;2;3;4;5;6;8;9]
*)
(*$Q
Q.(list int) (fun l -> \
let l = List.sort Pervasives.compare l in \
sorted_merge_uniq l [] = uniq_succ l)
Q.(list int) (fun l -> \
let l = List.sort Pervasives.compare l in \
sorted_merge_uniq [] l = uniq_succ l)
Q.(pair (list int) (list int)) (fun (l1, l2) -> \
let l1 = List.sort Pervasives.compare l1 \
and l2 = List.sort Pervasives.compare l2 in \
let l3 = sorted_merge_uniq l1 l2 in \
uniq_succ l3 = l3)
*)
let take n l =
let rec direct i n l = match l with
| [] -> []

View file

@ -118,11 +118,23 @@ val filter_map : ('a -> 'b option) -> 'a t -> 'b t
(** Map and remove elements at the same time *)
val sorted_merge : ?cmp:('a -> 'a -> int) -> 'a list -> 'a list -> 'a list
(** merges elements from both sorted list, removing duplicates *)
(** merges elements from both sorted list *)
val sort_uniq : ?cmp:('a -> 'a -> int) -> 'a list -> 'a list
(** Sort the list and remove duplicate elements *)
val sorted_merge_uniq : ?cmp:('a -> 'a -> int) -> 'a list -> 'a list -> 'a list
(** [sorted_merge_uniq l1 l2] merges the sorted lists [l1] and [l2] and
removes duplicates
@since 0.10 *)
val uniq_succ : ?eq:('a -> 'a -> bool) -> 'a list -> 'a list
(** [uniq_succ l] removes duplicate elements that occur one next to the other.
Examples:
[uniq_succ [1;2;1] = [1;2;1]]
[uniq_succ [1;1;2] = [1;2]]
@since 0.10 *)
(** {2 Indices} *)
module Idx : sig

View file

@ -263,6 +263,24 @@ let of_array a =
let to_array s =
Array.init (String.length s) (fun i -> s.[i])
let lines_gen s = Split.gen_cpy ~by:"\n" s
let lines s = Split.list_cpy ~by:"\n" s
let concat_gen ~sep g =
let b = Buffer.create 256 in
let rec aux ~first () = match g () with
| None -> Buffer.contents b
| Some s ->
if not first then Buffer.add_string b sep;
Buffer.add_string b s;
aux ~first:false ()
in aux ~first:true ()
let unlines l = String.concat "\n" l
let unlines_gen g = concat_gen ~sep:"\n" g
let pp buf s =
Buffer.add_char buf '"';
Buffer.add_string buf s;

View file

@ -113,6 +113,30 @@ val suffix : suf:string -> string -> bool
not (suffix ~suf:"abcd" "cd")
*)
val lines : string -> string list
(** [lines s] returns a list of the lines of [s] (splits along '\n')
@since 0.10 *)
val lines_gen : string -> string gen
(** [lines_gen s] returns a generator of the lines of [s] (splits along '\n')
@since 0.10 *)
val concat_gen : sep:string -> string gen -> string
(** [concat_gen ~sep g] concatenates all strings of [g], separated with [sep].
@since 0.10 *)
val unlines : string list -> string
(** [unlines l] concatenates all strings of [l], separated with '\n'
@since 0.10 *)
val unlines_gen : string gen -> string
(** [unlines_gen g] concatenates all strings of [g], separated with '\n'
@since 0.10 *)
(*$Q
Q.printable_string (fun s -> unlines (lines s) = s)
*)
include S with type t := string
(** {2 Splitting} *)

View file

@ -1,6 +1,6 @@
# OASIS_START
# DO NOT EDIT (digest: 71114627b2165c5eaff8d7c614d71974)
version = "0.9"
# DO NOT EDIT (digest: 09a66d8274446aebd1544537d064203d)
version = "0.10"
description = "A modular standard library focused on data structures."
requires = "bytes"
archive(byte) = "containers.cma"
@ -8,8 +8,19 @@ archive(byte, plugin) = "containers.cma"
archive(native) = "containers.cmxa"
archive(native, plugin) = "containers.cmxs"
exists_if = "containers.cma"
package "unix" (
version = "0.10"
description = "A modular standard library focused on data structures."
requires = "bytes unix"
archive(byte) = "containers_unix.cma"
archive(byte, plugin) = "containers_unix.cma"
archive(native) = "containers_unix.cmxa"
archive(native, plugin) = "containers_unix.cmxs"
exists_if = "containers_unix.cma"
)
package "thread" (
version = "0.9"
version = "0.10"
description = "A modular standard library focused on data structures."
requires = "containers threads"
archive(byte) = "containers_thread.cma"
@ -20,8 +31,9 @@ package "thread" (
)
package "string" (
version = "0.9"
version = "0.10"
description = "A modular standard library focused on data structures."
requires = "bytes"
archive(byte) = "containers_string.cma"
archive(byte, plugin) = "containers_string.cma"
archive(native) = "containers_string.cmxa"
@ -30,7 +42,7 @@ package "string" (
)
package "sexp" (
version = "0.9"
version = "0.10"
description = "A modular standard library focused on data structures."
requires = "bytes"
archive(byte) = "containers_sexp.cma"
@ -40,19 +52,8 @@ package "sexp" (
exists_if = "containers_sexp.cma"
)
package "pervasives" (
version = "0.9"
description = "A modular standard library focused on data structures."
requires = "containers"
archive(byte) = "containers_pervasives.cma"
archive(byte, plugin) = "containers_pervasives.cma"
archive(native) = "containers_pervasives.cmxa"
archive(native, plugin) = "containers_pervasives.cmxs"
exists_if = "containers_pervasives.cma"
)
package "misc" (
version = "0.9"
version = "0.10"
description = "A modular standard library focused on data structures."
requires = "containers containers.data"
archive(byte) = "containers_misc.cma"
@ -63,7 +64,7 @@ package "misc" (
)
package "lwt" (
version = "0.9"
version = "0.10"
description = "A modular standard library focused on data structures."
requires = "containers lwt containers.misc"
archive(byte) = "containers_lwt.cma"
@ -74,7 +75,7 @@ package "lwt" (
)
package "iter" (
version = "0.9"
version = "0.10"
description = "A modular standard library focused on data structures."
archive(byte) = "containers_iter.cma"
archive(byte, plugin) = "containers_iter.cma"
@ -84,7 +85,7 @@ package "iter" (
)
package "io" (
version = "0.9"
version = "0.10"
description = "A modular standard library focused on data structures."
requires = "bytes"
archive(byte) = "containers_io.cma"
@ -95,7 +96,7 @@ package "io" (
)
package "data" (
version = "0.9"
version = "0.10"
description = "A modular standard library focused on data structures."
requires = "bytes"
archive(byte) = "containers_data.cma"
@ -106,7 +107,7 @@ package "data" (
)
package "bigarray" (
version = "0.9"
version = "0.10"
description = "A modular standard library focused on data structures."
requires = "containers bigarray bytes"
archive(byte) = "containers_bigarray.cma"
@ -117,7 +118,7 @@ package "bigarray" (
)
package "advanced" (
version = "0.9"
version = "0.10"
description = "A modular standard library focused on data structures."
requires = "containers sequence"
archive(byte) = "containers_advanced.cma"

View file

@ -40,6 +40,11 @@ Changed [Opt] to [Option] to better reflect that this module is about the
['a option] type, with [module Option = CCOpt].
@since 0.5
Renamed from [CCPervasives] in [containers.pervasives], to [Containers]
in the core library [containers]
@since 0.10
*)
module Array = struct

View file

@ -1,5 +1,5 @@
# OASIS_START
# DO NOT EDIT (digest: 5c58c781604360016ba544a7c9d0c597)
# DO NOT EDIT (digest: b1fae2373cf2a628a9465ba233f7c127)
CCVector
CCPrint
CCError
@ -21,4 +21,5 @@ CCString
CCHashtbl
CCMap
CCFormat
Containers
# OASIS_STOP

View file

@ -1,5 +1,5 @@
# OASIS_START
# DO NOT EDIT (digest: 5c58c781604360016ba544a7c9d0c597)
# DO NOT EDIT (digest: b1fae2373cf2a628a9465ba233f7c127)
CCVector
CCPrint
CCError
@ -21,4 +21,5 @@ CCString
CCHashtbl
CCMap
CCFormat
Containers
# OASIS_STOP

View file

@ -74,6 +74,11 @@ let rec cons : 'a. 'a -> 'a t -> 'a t
| Deep (n,Three (y,z,z'), lazy q', tail) ->
_deep (n+1) (Two (x,y)) (lazy (cons (z,z') q')) tail
(*$Q
(Q.pair Q.int (Q.list Q.int)) (fun (x,l) -> \
cons x (of_list l) |> to_list = x::l)
*)
let rec snoc : 'a. 'a t -> 'a -> 'a t
= fun q x -> match q with
| Shallow Zero -> _single x
@ -87,6 +92,11 @@ let rec snoc : 'a. 'a t -> 'a -> 'a t
| Deep (n,hd, lazy q', Three (y,z,z')) ->
_deep (n+1) hd (lazy (snoc q' (y,z))) (Two(z',x))
(*$Q
(Q.pair Q.int (Q.list Q.int)) (fun (x,l) -> \
snoc (of_list l) x |> to_list = l @ [x])
*)
let rec take_front_exn : 'a. 'a t -> ('a *'a t)
= fun q -> match q with
| Shallow Zero -> raise Empty
@ -105,6 +115,12 @@ let rec take_front_exn : 'a. 'a t -> ('a *'a t)
| Deep (n,Three (x,y,z), middle, tail) ->
x, _deep (n-1) (Two(y,z)) middle tail
(*$Q
(Q.pair Q.int (Q.list Q.int)) (fun (x,l) -> \
let x', q = cons x (of_list l) |> take_front_exn in \
x'=x && to_list q = l)
*)
let take_front q =
try Some (take_front_exn q)
with Empty -> None
@ -117,6 +133,11 @@ let take_front_l n q =
aux (x::acc) q' (n-1)
in aux [] q n
(*$T
let l, q = take_front_l 5 (1 -- 10) in \
l = [1;2;3;4;5] && to_list q = [6;7;8;9;10]
*)
let take_front_while p q =
let rec aux acc q =
if is_empty q then List.rev acc, q
@ -125,6 +146,10 @@ let take_front_while p q =
if p x then aux (x::acc) q' else List.rev acc, q
in aux [] q
(*$T
take_front_while (fun x-> x<5) (1 -- 10) |> fst = [1;2;3;4]
*)
let rec take_back_exn : 'a. 'a t -> 'a t * 'a
= fun q -> match q with
| Shallow Zero -> invalid_arg "FQueue.take_back_exn"
@ -141,6 +166,12 @@ let rec take_back_exn : 'a. 'a t -> 'a t * 'a
| Deep (n, hd, middle, Two(x,y)) -> _deep (n-1) hd middle (One x), y
| Deep (n, hd, middle, Three(x,y,z)) -> _deep (n-1) hd middle (Two (x,y)), z
(*$Q
(Q.pair Q.int (Q.list Q.int)) (fun (x,l) -> \
let q,x' = snoc (of_list l) x |> take_back_exn in \
x'=x && to_list q = l)
*)
let take_back q =
try Some (take_back_exn q)
with Empty -> None
@ -186,6 +217,11 @@ let size : 'a. 'a t -> int
| Shallow d -> _size_digit d
| Deep (n, _, _, _) -> n
(*$Q
(Q.list Q.int) (fun l -> \
size (of_list l) = List.length l)
*)
let _nth_digit i d = match i, d with
| _, Zero -> raise Not_found
| 0, One x -> x
@ -228,18 +264,41 @@ let nth i q =
try Some (nth_exn i q)
with Failure _ -> None
(*$Q
(Q.list Q.int) (fun l -> \
let len = List.length l in let idx = CCList.(0 -- (len - 1)) in \
let q = of_list l in \
l = [] || List.for_all (fun i -> nth i q = Some (List.nth l i)) idx)
*)
let init q =
try fst (take_back_exn q)
with Empty -> q
(*$Q
(Q.list Q.int) (fun l -> \
l = [] || (of_list l |> init |> to_list = List.rev (List.tl (List.rev l))))
*)
let tail q =
try snd (take_front_exn q)
with Empty -> q
(*$Q
(Q.list Q.int) (fun l -> \
l = [] || (of_list l |> tail |> to_list = List.tl l))
*)
let add_seq_front seq q =
let q = ref q in
seq (fun x -> q := cons x !q);
!q
let l = ref [] in
(* reversed seq *)
seq (fun x -> l := x :: !l);
List.fold_left (fun q x -> cons x q) q !l
(*$Q
Q.(pair (list int) (list int)) (fun (l1, l2) -> \
add_seq_front (Sequence.of_list l1) (of_list l2) |> to_list = l1 @ l2)
*)
let add_seq_back q seq =
let q = ref q in
@ -260,12 +319,22 @@ let rec to_seq : 'a. 'a t -> 'a sequence
to_seq q' (fun (x,y) -> k x; k y);
_digit_to_seq tail k
(*$Q
(Q.list Q.int) (fun l -> \
of_list l |> to_seq |> Sequence.to_list = l)
*)
let append q1 q2 =
match q1, q2 with
| Shallow Zero, _ -> q2
| _, Shallow Zero -> q1
| _ -> add_seq_back q1 (to_seq q2)
(*$Q
(Q.pair (Q.list Q.int)(Q.list Q.int)) (fun (l1,l2) -> \
append (of_list l1) (of_list l2) |> to_list = l1 @ l2)
*)
let _map_digit f d = match d with
| Zero -> Zero
| One x -> One (f x)
@ -279,6 +348,11 @@ let rec map : 'a 'b. ('a -> 'b) -> 'a t -> 'b t
let q'' = map (fun (x,y) -> f x, f y) q' in
_deep size (_map_digit f hd) (Lazy.from_val q'') (_map_digit f tl)
(*$Q
(Q.list Q.int) (fun l -> \
of_list l |> map string_of_int |> to_list = List.map string_of_int l)
*)
let (>|=) q f = map f q
let _fold_digit f acc d = match d with
@ -295,6 +369,11 @@ let rec fold : 'a 'b. ('b -> 'a -> 'b) -> 'b -> 'a t -> 'b
let acc = fold (fun acc (x,y) -> f (f acc x) y) acc q' in
_fold_digit f acc tl
(*$Q
(Q.list Q.int) (fun l -> \
of_list l |> fold (fun acc x->x::acc) [] = List.rev l)
*)
let iter f q = to_seq q f
let of_list l = List.fold_left snoc empty l
@ -304,16 +383,23 @@ let to_list q =
to_seq q (fun x -> l := x :: !l);
List.rev !l
let of_seq seq =
let l = ref [] in
seq (fun x -> l := x :: !l);
List.fold_left (fun q x -> cons x q) empty !l
let of_seq seq = add_seq_front seq empty
(*$Q
(Q.list Q.int) (fun l -> \
Sequence.of_list l |> of_seq |> to_list = l)
*)
let rev q =
let q' = ref empty in
iter (fun x -> q' := cons x !q') q;
!q'
(*$Q
(Q.list Q.int) (fun l -> \
of_list l |> rev |> to_list = List.rev l)
*)
let _nil () = `Nil
let _single x cont () = `Cons (x, cont)
let _double x y cont () = `Cons (x, _single y cont)
@ -358,3 +444,24 @@ let rec _equal_klist eq l1 l2 = match l1(), l2() with
eq x1 x2 && _equal_klist eq l1' l2'
let equal eq q1 q2 = _equal_klist eq (to_klist q1) (to_klist q2)
(*$T
let q1 = 1 -- 10 and q2 = append (1 -- 5) (6 -- 10) in \
equal (=) q1 q2
*)
let (--) a b =
let rec up_to q a b = if a = b
then snoc q a
else up_to (snoc q a) (a+1) b
and down_to q a b = if a = b then snoc q a
else down_to (snoc q a) (a-1) b
in
if a <= b then up_to empty a b else down_to empty a b
(*$T
1 -- 5 |> to_list = [1;2;3;4;5]
5 -- 1 |> to_list = [5;4;3;2;1]
0 -- 0 |> to_list = [0]
*)

View file

@ -110,10 +110,15 @@ val append : 'a t -> 'a t -> 'a t
after elements of the first one.
Linear in the size of the second queue. *)
val rev : 'a t -> 'a t
(** Reverse the queue, O(n) complexity
@since 0.10 *)
val map : ('a -> 'b) -> 'a t -> 'b t
(** Map values *)
val (>|=) : 'a t -> ('a -> 'b) -> 'b t
(** Synonym to {!map} *)
val size : 'a t -> int
(** Number of elements in the queue (constant time) *)
@ -130,6 +135,7 @@ val of_list : 'a list -> 'a t
val to_list : 'a t -> 'a list
val add_seq_front : 'a sequence -> 'a t -> 'a t
val add_seq_back : 'a t -> 'a sequence -> 'a t
val to_seq : 'a t -> 'a sequence
@ -138,3 +144,7 @@ val of_seq : 'a sequence -> 'a t
val to_klist : 'a t -> 'a klist
val of_klist : 'a klist -> 'a t
val (--) : int -> int -> int t
(** [a -- b] is the integer range from [a] to [b], both included.
@since 0.10 *)

276
src/data/CCIntMap.ml Normal file
View file

@ -0,0 +1,276 @@
(*
copyright (c) 2013-2015, simon cruanes
all rights reserved.
redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer. redistributions in binary
form must reproduce the above copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other materials provided with
the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*)
(** {1 Map specialized for Int keys} *)
(* "Fast Mergeable Integer Maps", Okasaki & Gill.
We use big-endian trees. *)
type 'a t =
| E (* empty *)
| L of int * 'a (* leaf *)
| N of int (* common prefix *) * int (* bit switch *) * 'a t * 'a t
let empty = E
let bit_is_0_ x ~bit = x land bit = 0
let mask_ x ~mask = (x lor (mask -1)) land (lnot mask)
(* low endian: let mask_ x ~mask = x land (mask - 1) *)
let is_prefix_ ~prefix y ~bit = prefix = mask_ y ~mask:bit
(* loop down until x=lowest_bit_ x *)
let rec highest_bit_naive x m =
if m = 0 then 0
else if x land m = 0 then highest_bit_naive x (m lsr 1)
else m
let highest_bit =
(* the highest representable 2^n *)
let max_log = 1 lsl (Sys.word_size - 2) in
fun x ->
if x > 1 lsl 20
then (* small shortcut: remove least significant 20 bits *)
let x' = x land (lnot ((1 lsl 20) -1)) in
highest_bit_naive x' max_log
else highest_bit_naive x max_log
(*$Q
Q.int (fun i -> \
let b = highest_bit i in \
i < 0 || (b <= i && (i-b) < b))
*)
(* helper:
let b_of_i i =
let rec f acc i =
if i=0 then acc else let q, r = i/2, i mod 2
in
f (r::acc) q in f [] i;;
*)
(* low endian: let branching_bit_ a _ b _ = lowest_bit_ (a lxor b) *)
let branching_bit_ a b =
highest_bit (a lxor b)
let rec find_exn k t = match t with
| E -> raise Not_found
| L (k', v) when k = k' -> v
| L _ -> raise Not_found
| N (prefix, m, l, r) ->
if is_prefix_ ~prefix k ~bit:m
then if bit_is_0_ k ~bit:m
then find_exn k l
else find_exn k r
else raise Not_found
(* FIXME: valid if k < 0?
if k <= prefix (* search tree *)
then find_exn k l
else find_exn k r
*)
let find k t =
try Some (find_exn k t)
with Not_found -> None
let mem k t =
try ignore (find_exn k t); true
with Not_found -> false
let mk_node_ prefix switch l r = match l, r with
| E, o | o, E -> o
| _ -> N (prefix, switch, l, r)
(* join trees t1 and t2 with prefix p1 and p2 respectively
(p1 and p2 do not overlap) *)
let join_ t1 p1 t2 p2 =
let switch = branching_bit_ p1 p2 in
let prefix = mask_ p1 ~mask:switch in
if bit_is_0_ p1 ~bit:switch
then mk_node_ prefix switch t1 t2
else (assert (bit_is_0_ p2 ~bit:switch); mk_node_ prefix switch t2 t1)
let singleton k v = L (k, v)
(* c: conflict function *)
let rec insert_ c k v t = match t with
| E -> L (k, v)
| L (k', v') ->
if k=k'
then L (k, c ~old:v' v)
else join_ t k' (L (k, v)) k
| N (prefix, switch, l, r) ->
if is_prefix_ ~prefix k ~bit:switch
then if bit_is_0_ k ~bit:switch
then N(prefix, switch, insert_ c k v l, r)
else N(prefix, switch, l, insert_ c k v r)
else join_ (L(k,v)) k t prefix
let add k v t = insert_ (fun ~old:_ v -> v) k v t
(*$Q & ~count:20
Q.(list (pair int int)) (fun l -> \
let l = CCList.Set.uniq l in let m = of_list l in \
List.for_all (fun (k,v) -> find_exn k m = v) l)
*)
let rec remove k t = match t with
| E -> E
| L (k', _) -> if k=k' then E else t
| N (prefix, switch, l, r) ->
if is_prefix_ ~prefix k ~bit:switch
then if bit_is_0_ k ~bit:switch
then mk_node_ prefix switch (remove k l) r
else mk_node_ prefix switch l (remove k r)
else t (* not present *)
let update k f t =
try
let v = find_exn k t in
begin match f (Some v) with
| None -> remove k t
| Some v' -> add k v' t
end
with Not_found ->
match f None with
| None -> t
| Some v -> add k v t
let doubleton k1 v1 k2 v2 = add k1 v1 (singleton k2 v2)
let rec iter f t = match t with
| E -> ()
| L (k, v) -> f k v
| N (_, _, l, r) -> iter f l; iter f r
let rec fold f t acc = match t with
| E -> acc
| L (k, v) -> f k v acc
| N (_, _, l, r) ->
let acc = fold f l acc in
fold f r acc
let cardinal t = fold (fun _ _ n -> n+1) t 0
let rec choose_exn = function
| E -> raise Not_found
| L (k, v) -> k, v
| N (_, _, l, _) -> choose_exn l
let choose t =
try Some (choose_exn t)
with Not_found -> None
let rec union f a b = match a, b with
| E, o | o, E -> o
| L (k, v), o
| o, L (k, v) ->
(* insert k, v into o *)
insert_ (fun ~old v -> f k old v) k v o
| N (p1, m1, l1, r1), N (p2, m2, l2, r2) ->
if p1 = p2 && m1 = m2
then mk_node_ p1 m1 (union f l1 l2) (union f r1 r2)
else if m1 < m2 && is_prefix_ ~prefix:p2 p1 ~bit:m1
then if bit_is_0_ p2 ~bit:m1
then N (p1, m1, union f l1 b, r1)
else N (p1, m1, l1, union f r1 b)
else if m1 > m2 && is_prefix_ ~prefix:p1 p2 ~bit:m2
then if bit_is_0_ p1 ~bit:m2
then N (p2, m2, union f l2 a, r2)
else N (p2, m2, l2, union f r2 a)
else join_ a p1 b p2
let rec inter f a b = match a, b with
| E, _ | _, E -> E
| L (k, v), o
| o, L (k, v) ->
begin try
let v' = find_exn k o in
L (k, f k v v')
with Not_found -> E
end
| N (p1, m1, l1, r1), N (p2, m2, l2, r2) ->
if p1 = p2 && m1 = m2
then mk_node_ p1 m1 (inter f l1 l2) (inter f r1 r2)
else if m1 < m2 && is_prefix_ ~prefix:p2 p1 ~bit:m1
then if bit_is_0_ p2 ~bit:m1
then inter f l1 b
else inter f r1 b
else if m1 > m2 && is_prefix_ ~prefix:p1 p2 ~bit:m2
then if bit_is_0_ p1 ~bit:m2
then inter f l2 a
else inter f r2 a
else E
(* TODO: write tests *)
(** {2 Whole-collection operations} *)
type 'a sequence = ('a -> unit) -> unit
type 'a gen = unit -> 'a option
let add_list t l = List.fold_left (fun t (k,v) -> add k v t) t l
let of_list l = add_list empty l
let to_list t = fold (fun k v l -> (k,v) :: l) t []
(*$Q
Q.(list (pair int int)) (fun l -> \
let l = List.map (fun (k,v) -> abs k,v) l in \
let rec is_sorted = function [] | [_] -> true \
| x::y::tail -> x <= y && is_sorted (y::tail) in \
of_list l |> to_list |> List.rev_map fst |> is_sorted)
*)
(*$Q
Q.(list (pair int int)) (fun l -> \
of_list l |> cardinal = List.length l)
*)
let add_seq t seq =
let t = ref t in
seq (fun (k,v) -> t := add k v !t);
!t
let of_seq seq = add_seq empty seq
let to_seq t yield = iter (fun k v -> yield (k,v)) t
let keys t yield = iter (fun k _ -> yield k) t
let values t yield = iter (fun _ v -> yield v) t
type 'a tree = unit -> [`Nil | `Node of 'a * 'a tree list]
let rec as_tree t () = match t with
| E -> `Nil
| L (k, v) -> `Node (`Leaf (k, v), [])
| N (prefix, switch, l, r) ->
`Node (`Node (prefix, switch), [as_tree l; as_tree r])

96
src/data/CCIntMap.mli Normal file
View file

@ -0,0 +1,96 @@
(*
copyright (c) 2013-2015, simon cruanes
all rights reserved.
redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer. redistributions in binary
form must reproduce the above copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other materials provided with
the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*)
(** {1 Map specialized for Int keys}
{b status: unstable}
@since 0.10 *)
type 'a t
val empty : 'a t
val singleton : int -> 'a -> 'a t
val doubleton : int -> 'a -> int -> 'a -> 'a t
val mem : int -> _ t -> bool
val find : int -> 'a t -> 'a option
val find_exn : int -> 'a t -> 'a
(** Same as {!find} but unsafe
@raise Not_found if key not present *)
val add : int -> 'a -> 'a t -> 'a t
val remove : int -> 'a t -> 'a t
val update : int -> ('a option -> 'a option) -> 'a t -> 'a t
val cardinal : _ t -> int
val iter : (int -> 'a -> unit) -> 'a t -> unit
val fold : (int -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val choose : 'a t -> (int * 'a) option
val choose_exn : 'a t -> int * 'a
val union : (int -> 'a -> 'a -> 'a) -> 'a t -> 'a t -> 'a t
val inter : (int -> 'a -> 'a -> 'a) -> 'a t -> 'a t -> 'a t
(** {2 Whole-collection operations} *)
type 'a sequence = ('a -> unit) -> unit
type 'a gen = unit -> 'a option
val add_list : 'a t -> (int * 'a) list -> 'a t
val of_list : (int * 'a) list -> 'a t
val to_list : 'a t -> (int * 'a) list
val add_seq : 'a t -> (int * 'a) sequence -> 'a t
val of_seq : (int * 'a) sequence -> 'a t
val to_seq : 'a t -> (int * 'a) sequence
val keys : _ t -> int sequence
val values : 'a t -> 'a sequence
(** Helpers *)
val highest_bit : int -> int
type 'a tree = unit -> [`Nil | `Node of 'a * 'a tree list]
val as_tree : 'a t -> [`Node of int * int | `Leaf of int * 'a ] tree

View file

@ -0,0 +1,92 @@
(*
copyright (c) 2013-2015, Guillaume Bury
all rights reserved.
redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer. redistributions in binary
form must reproduce the above copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other materials provided with
the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*)
(* Persistent arrays *)
type 'a t = 'a data ref
and 'a data =
| Array of 'a array
| Diff of int * 'a * 'a t
let make n a = ref (Array (Array.make n a))
let init n f = ref (Array (Array.init n f))
let rec _reroot t k = match !t with
| Array a -> k a
| Diff (i, v, t') ->
_reroot t' (fun a ->
let v' = a.(i) in
a.(i) <- v;
t := Array a;
t' := Diff(i, v', t);
k a
)
let reroot t = match !t with
| Array a -> a
| _ -> _reroot t (fun x -> x)
let copy t = ref (Array(Array.copy (reroot t)))
let get t i = match !t with
| Array a -> a.(i)
| _ -> (reroot t).(i)
let set t i v =
let a = reroot t in
let old = a.(i) in
a.(i) <- v;
let t' = ref (Array a) in
t := Diff (i, old, t');
t'
let length t = Array.length (reroot t)
let map f t = ref (Array (Array.map f (reroot t)))
let mapi f t = ref (Array (Array.mapi f (reroot t)))
let iter f t = Array.iter f (reroot t)
let iteri f t = Array.iteri f (reroot t)
let fold_left f acc t = Array.fold_left f acc (reroot t)
let fold_right f t acc = Array.fold_right f (reroot t) acc
let to_array t = Array.copy (reroot t)
let of_array a = init (Array.length a) (fun i -> a.(i))
let to_list t = Array.to_list (reroot t)
let of_list l = ref (Array (Array.of_list l))
type 'a sequence = ('a -> unit) -> unit
let to_seq a yield = iter yield a
let of_seq seq =
let l = ref [] in
seq (fun x -> l := x :: !l);
of_list (List.rev !l)

View file

@ -0,0 +1,105 @@
(*
copyright (c) 2013-2015, Guillaume Bury
all rights reserved.
redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer. redistributions in binary
form must reproduce the above copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other materials provided with
the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*)
(** {1 Persistent Arrays}
From the paper by Jean-Christophe Filliâtre,
"A persistent Union-Find data structure", see
{{: https://www.lri.fr/~filliatr/ftp/publis/puf-wml07.ps} the ps version}
@since 0.10 *)
type 'a t
(** The type of persistent arrays *)
val make : int -> 'a -> 'a t
(** [make n x] returns a persistent array of length n, with [x]. All the
elements of this new array are initially physically equal to x
(in the sense of the == predicate). Consequently, if x is mutable, it is
shared among all elements of the array, and modifying x through one of the
array entries will modify all other entries at the same time.
@raise Invalid_argument if [n < 0] or [n > Sys.max_array_length].
If the value of x is a floating-point number, then the maximum size is
only [Sys.max_array_length / 2].*)
val init : int -> (int -> 'a) -> 'a t
(** [make n f] returns a persistent array of length n, with element
[i] initialized to the result of [f i].
@raise Invalid_argument if [n < 0] or [n > Sys.max_array_length].
If the value of x is a floating-point number, then the maximum size is
only [Sys.max_array_length / 2].*)
val get : 'a t -> int -> 'a
(** [get a i] Returns the element with index [i] from the array [a].
@raise Invalid_argument "index out of bounds" if [n] is outside the
range [0] to [Array.length a - 1].*)
val set : 'a t -> int -> 'a -> 'a t
(** [set a i v] sets the element index [i] from the array [a] to [v].
@raise Invalid_argument "index out of bounds" if [n] is outside the
range [0] to [Array.length a - 1].*)
val length : 'a t -> int
(** Returns the length of the persistent array. *)
val copy : 'a t -> 'a t
(** [copy a] returns a fresh copy of [a]. Both copies are independent. *)
val map : ('a -> 'b) -> 'a t -> 'b t
val mapi : (int -> 'a -> 'b) -> 'a t -> 'b t
(** Applies the given function to all elements of the array, and returns
a persistent array initialized by the results of f. In the case of [mapi],
the function is also given the index of the element.
It is equivalent to [fun f t -> init (fun i -> f (get t i))]. *)
val iter : ('a -> unit) -> 'a t -> unit
val iteri : (int -> 'a -> unit) -> 'a t -> unit
(** [iter f t] applies function [f] to all elements of the persistent array,
in order from element [0] to element [length t - 1]. *)
val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'a
val fold_right : ('a -> 'b -> 'b) -> 'a t -> 'b -> 'b
(** Fold on the elements of the array. *)
val to_array : 'a t -> 'a array
(** [to_array t] returns a mutable copy of [t]. *)
val of_array : 'a array -> 'a t
(** [from_array a] returns an immutable copy of [a]. *)
val to_list : 'a t -> 'a list
(** [to_list t] returns the list of elements in [t]. *)
val of_list : 'a list -> 'a t
(** [of_list l] returns a fresh persistent array containing the elements of [l]. *)
type 'a sequence = ('a -> unit) -> unit
val to_seq : 'a t -> 'a sequence
val of_seq : 'a sequence -> 'a t

View file

@ -1,5 +1,5 @@
# OASIS_START
# DO NOT EDIT (digest: 868cf65b04ece1e5b4b46f9a48586507)
# DO NOT EDIT (digest: b83e1a21d44ea00373b0dde5cda9eedd)
CCMultiMap
CCMultiSet
CCTrie
@ -12,4 +12,6 @@ CCBV
CCMixtbl
CCMixmap
CCRingBuffer
CCIntMap
CCPersistentArray
# OASIS_STOP

View file

@ -1,5 +1,5 @@
# OASIS_START
# DO NOT EDIT (digest: 868cf65b04ece1e5b4b46f9a48586507)
# DO NOT EDIT (digest: b83e1a21d44ea00373b0dde5cda9eedd)
CCMultiMap
CCMultiSet
CCTrie
@ -12,4 +12,6 @@ CCBV
CCMixtbl
CCMixmap
CCRingBuffer
CCIntMap
CCPersistentArray
# OASIS_STOP

View file

@ -74,20 +74,20 @@ type ('a, +'perm) t constraint 'perm = [< `r | `w]
type ('a, 'perm) pipe = ('a, 'perm) t
val keep : _ t -> unit Lwt.t -> unit
val keep : (_,_) t -> unit Lwt.t -> unit
(** [keep p fut] adds a pointer from [p] to [fut] so that [fut] is not
garbage-collected before [p] *)
val is_closed : _ t -> bool
val is_closed : (_,_) t -> bool
val close : _ t -> unit Lwt.t
val close : (_,_) t -> unit Lwt.t
(** [close p] closes [p], which will not accept input anymore.
This sends [`End] to all readers connected to [p] *)
val close_async : _ t -> unit
val close_async : (_,_) t -> unit
(** Same as {!close} but closes in the background *)
val wait : _ t -> unit Lwt.t
val wait : (_,_) t -> unit Lwt.t
(** Evaluates once the pipe closes *)
val create : ?max_size:int -> unit -> ('a, 'perm) t
@ -101,7 +101,7 @@ val connect : ?ownership:[`None | `InOwnsOut | `OutOwnsIn] ->
@param own determines which pipes owns which (the owner, when it
closes, also closes the ownee) *)
val link_close : _ t -> after:_ t -> unit
val link_close : (_,_) t -> after:(_,_) t -> unit
(** [link_close p ~after] will close [p] when [after] closes.
if [after] is closed already, closes [p] immediately *)

193
src/misc/backtrack.ml Normal file
View file

@ -0,0 +1,193 @@
module type MONAD = sig
type 'a t
val return : 'a -> 'a t
val (>>=) : 'a t -> ('a -> 'b t) -> 'b t
end
module NonLogical = struct
type 'a t = unit -> 'a
let return x () = x
let (>>=) x f () = let y = x() in f y ()
end
type ('a, 'b) list_view =
| Nil of exn
| Cons of 'a * 'b
(** The monad is parametrised in the types of state, environment and
writer. *)
module type Param = sig
(** Read only *)
type e
(** Write only *)
type w
(** [w] must be a monoid *)
val wunit : w
val wprod : w -> w -> w
(** Read-write *)
type s
(** Update-only. Essentially a writer on [u->u]. *)
type u
(** [u] must be pointed. *)
val uunit : u
end
module Logical (P:Param) = struct
type state = {
e: P.e;
w: P.w;
s: P.s;
u: P.u;
}
type _ t =
| Ignore : _ t -> unit t
| Return : 'a -> 'a t
| Bind : 'a t * ('a -> 'b t) -> 'b t
| Map : 'a t * ('a -> 'b) -> 'b t
| Get : P.s t
| Set : P.s -> unit t
| Modify : (P.s -> P.s) -> unit t
| Put : P.w -> unit t
| Current : P.e t
| Local : P.e * 'a t -> 'a t (* local bind *)
| Update : (P.u -> P.u) -> unit t
| Zero : exn -> 'a t
| WithState : state * 'a t -> 'a t (* use other state *)
| Plus : 'a t * (exn -> 'a t ) -> 'a t
| Split : 'a t -> ('a, exn -> 'a t) list_view t
| Once : 'a t -> 'a t (* keep at most one element *)
| Break : (exn -> exn option) * 'a t -> 'a t
let return x = Return x
let (>>=) x f = Bind (x, f)
let map f x = match x with
| Return x -> return (f x)
| Map (y, g) -> Map (y, fun x -> f (g x))
| _ -> Map (x, f)
let rec ignore : type a. a t -> unit t = function
| Return _ -> Return ()
| Map (x, _) -> ignore x
| x -> Ignore x
let set x = Set x
let get = Get
let modify f = Modify f
let put x = Put x
let current = Current
let local x y = Local (x, y)
let update f = Update f
let zero e = Zero e
let with_state st x = WithState (st, x)
let rec plus a f = match a with
| Zero e -> f e
| Plus (a1, f1) ->
plus a1 (fun e -> plus (f1 e) f)
| _ -> Plus (a, f)
let split x = Split x
let rec once : type a. a t -> a t = function
| Zero e -> Zero e
| Return x -> Return x
| Map (x, f) -> map f (once x)
| x -> Once x
let break f x = Break (f, x)
type 'a reified =
| RNil of exn
| RCons of 'a * (exn -> 'a reified)
let repr r () = match r with
| RNil e -> Nil e
| RCons (x, f) -> Cons (x, f)
let cons x cont = Cons (x, cont)
let nil e = Nil e
let rcons x cont = RCons (x, cont)
let rnil e = RNil e
(* TODO: maybe (('a * state), exn -> state -> 'a t) list_view is better
for bind and local? *)
type 'a splitted = (('a * state), exn -> 'a t) list_view
let rec run_rec
: type a. state -> a t -> a splitted
= fun st t -> match t with
| Return x -> cons (x, st) zero
| Ignore x ->
begin match run_rec st x with
| Nil e -> Nil e
| Cons ((_, st), cont) -> cons ((), st) (fun e -> Ignore (cont e))
end
| Bind (x,f) ->
begin match run_rec st x with
| Nil e -> Nil e
| Cons ((x, st_x), cont) ->
let y = f x in
run_rec st_x (plus y (fun e -> with_state st (cont e >>= f)))
end
| Map (x,f) ->
begin match run_rec st x with
| Nil e -> Nil e
| Cons ((x, st), cont) ->
cons (f x, st) (fun e -> map f (cont e))
end
| Get -> cons (st.s, st) zero
| Set s -> cons ((), {st with s}) zero
| Modify f ->
let st = {st with s = f st.s} in
cons ((), st) zero
| Put w -> cons ((), {st with w}) zero
| Current -> cons (st.e, st) zero
| Local (e,x) ->
(* bind [st.e = e] in [x], then restore old [e] in each result *)
let old_e = st.e in
let st' = {st with e} in
begin match run_rec st' x with
| Nil e -> Nil e
| Cons ((x, st''), cont) ->
cons (x, {st'' with e=old_e}) (fun e -> assert false) (* TODO: restore old_e*)
end
| Update f ->
let st = {st with u=f st.u} in
cons ((), st) zero
| WithState (st', x) -> run_rec st' x (* ignore [st] *)
| Zero e -> Nil e (* failure *)
| Plus (x,cont) ->
begin match run_rec st x with
| Nil e -> run_rec st (cont e)
| Cons ((x, st), cont') ->
cons (x, st) (fun e -> plus (cont' e) cont)
end
| Split x ->
begin match run_rec st x with
| Nil e -> cons (Nil e, st) zero
| Cons ((x, st'), cont) -> cons (cons x cont, st') zero
end
| Once x ->
begin match run_rec st x with
| Nil e -> Nil e
| Cons ((x, st), _) -> cons (x, st) zero
end
| Break (f,x) -> assert false (* TODO: ? *)
let run t e s =
let state = {e; s; u=P.uunit; w=P.wunit} in
let rec run_list
: type a. state -> a t -> (a * state) reified
= fun state t -> match run_rec state t with
| Nil e -> rnil e
| Cons ((x, st), cont) ->
rcons (x, st) (fun e -> run_list state (cont e))
in
run_list state t
end

88
src/misc/backtrack.mli Normal file
View file

@ -0,0 +1,88 @@
(** {1 Experiment with Backtracking Monad}
Playing stuff, don't use (yet?).
{b status: experimental}
@since 0.10
*)
module type MONAD = sig
type 'a t
val return : 'a -> 'a t
val (>>=) : 'a t -> ('a -> 'b t) -> 'b t
end
(** Taken from Coq "logic_monad.mli" *)
module NonLogical : sig
type 'a t = unit -> 'a
include MONAD with type 'a t := 'a t
end
(** {6 Logical layer} *)
(** The logical monad is a backtracking monad on top of which is
layered a state monad (which is used to implement all of read/write,
read only, and write only effects). The state monad being layered on
top of the backtracking monad makes it so that the state is
backtracked on failure.
Backtracking differs from regular exception in that, writing (+)
for exception catching and (>>=) for bind, we require the
following extra distributivity laws:
x+(y+z) = (x+y)+z
zero+x = x
x+zero = x
(x+y)>>=k = (x>>=k)+(y>>=k) *)
(** A view type for the logical monad, which is a form of list, hence
we can decompose it with as a list. *)
type ('a, 'b) list_view =
| Nil of exn
| Cons of 'a * 'b
(** The monad is parametrised in the types of state, environment and
writer. *)
module type Param = sig
(** Read only *)
type e
(** Write only *)
type w
(** [w] must be a monoid *)
val wunit : w
val wprod : w -> w -> w
(** Read-write *)
type s
(** Update-only. Essentially a writer on [u->u]. *)
type u
(** [u] must be pointed. *)
val uunit : u
end
module Logical (P:Param) : sig
include MONAD
val map : ('a -> 'b) -> 'a t -> 'b t
val ignore : 'a t -> unit t
val set : P.s -> unit t
val get : P.s t
val modify : (P.s -> P.s) -> unit t
val put : P.w -> unit t
val current : P.e t
val local : P.e -> 'a t -> 'a t
val update : (P.u -> P.u) -> unit t
val zero : exn -> 'a t
val plus : 'a t -> (exn -> 'a t) -> 'a t
val split : 'a t -> (('a,(exn->'a t)) list_view) t
val once : 'a t -> 'a t
val break : (exn -> exn option) -> 'a t -> 'a t
(* val lift : 'a NonLogical.t -> 'a t *)
type 'a reified
type state = {
e: P.e;
w: P.w;
s: P.s;
u: P.u;
}
val repr : 'a reified -> ('a, exn -> 'a reified) list_view NonLogical.t
val run : 'a t -> P.e -> P.s -> ('a * state) reified
end

View file

@ -1,5 +1,5 @@
# OASIS_START
# DO NOT EDIT (digest: 64ac3a98881a419a2ed1f076194542f9)
# DO NOT EDIT (digest: a0730df368ed19a3b181d80ccf7985b6)
AbsSet
Automaton
Bij
@ -13,4 +13,6 @@ RoseTree
SmallSet
UnionFind
Univ
Puf
Backtrack
# OASIS_STOP

View file

@ -58,6 +58,8 @@ module PArray = struct
a
end
let iteri f t = Array.iteri f (reroot t)
let get t i =
match !t with
| Array a -> a.(i)
@ -204,6 +206,9 @@ module type S = sig
(** [iter_equiv_class uf a f] calls [f] on every element of [uf] that
is congruent to [a], including [a] itself. *)
val iter : _ t -> (elt -> unit) -> unit
(** Iterate on all root values *)
val inconsistent : _ t -> (elt * elt * elt * elt) option
(** Check whether the UF is inconsistent. It returns [Some (a, b, a', b')]
in case of inconsistency, where a = b, a = a' and b = b' by congruence,
@ -222,7 +227,8 @@ module type S = sig
val explain_distinct : 'e t -> elt -> elt -> elt * elt
(** [explain_distinct uf a b] gives the original pair [a', b'] that
made [a] and [b] distinct by calling [distinct a' b'] *)
made [a] and [b] distinct by calling [distinct a' b']. The
terms must be distinct, otherwise Failure is raised. *)
end
module IH = Hashtbl.Make(struct type t = int let equal i j = i = j let hash i = i end)
@ -446,6 +452,14 @@ module Make(X : ID) : S with type elt = X.t = struct
in
traverse ia
let iter uf f =
PArray.iteri
(fun i i' ->
if i = i' then match PArray.get uf.data i with
| None -> ()
| Some d -> f d.elt
) uf.parent
let inconsistent uf = uf.inconsistent
(** Closest common ancestor of the two elements in the proof forest *)

View file

@ -113,6 +113,10 @@ module type S = sig
(** [iter_equiv_class uf a f] calls [f] on every element of [uf] that
is congruent to [a], including [a] itself. *)
val iter : _ t -> (elt -> unit) -> unit
(** Iterate on all root values
@since NExT_RELEASE *)
val inconsistent : _ t -> (elt * elt * elt * elt) option
(** Check whether the UF is inconsistent. It returns [Some (a, b, a', b')]
in case of inconsistency, where a = b, a = a' and b = b' by congruence,

835
src/string/app_parse.ml Normal file
View file

@ -0,0 +1,835 @@
(*
copyright (c) 2013-2015, simon cruanes
all rights reserved.
redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer. redistributions in binary
form must reproduce the above copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other materials provided with
the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*)
(** {1 Applicative Parser Combinators} *)
type ('a,'b) result = [`Error of 'b | `Ok of 'a]
type multiplicity =
| Star (* 0 or more *)
| Plus (* 1 or more *)
| Question (* 0 or 1 *)
let str fmt = Printf.sprintf fmt
module CharSet = Set.Make(Char)
module CharMap = Map.Make(Char)
let print_char = function
| '\t' -> "\\t"
| '\n' -> "\\n"
| '\r' -> "\\r"
| '"' -> "\\\""
| c -> str "%c" c
let print_char_set set =
let buf = Buffer.create 32 in
Buffer.add_char buf '"';
CharSet.iter (fun c -> Buffer.add_string buf (print_char c)) set;
Buffer.add_char buf '"';
Buffer.contents buf
let domain_of_char_map m =
CharMap.fold (fun c _ set -> CharSet.add c set) m CharSet.empty
let print_char_map map =
let l = CharMap.fold
(fun c _ acc -> print_char c :: acc) map [] in
String.concat ", " l
let ppmap ?(sep=", ") pp_k pp_v fmt m =
let first = ref true in
CharMap.iter
(fun k v ->
if !first then first := false else Format.pp_print_string fmt sep;
pp_k fmt k;
Format.pp_print_string fmt "";
pp_v fmt v;
Format.pp_print_cut fmt ()
) m;
()
let set_of_string s =
let set = ref CharSet.empty in
String.iter
(fun c ->
if CharSet.mem c !set
then invalid_arg (str "any_of: duplicate char %c" c);
set := CharSet.add c !set
) s;
!set
(* add [c -> p] to the map, for every [c] in [set] *)
let map_add_set init set p =
CharSet.fold
(fun c map -> CharMap.add c p map)
set init
(* function composition *)
let compose f g x = f (g x)
let str_of_l l =
let b = Bytes.make (List.length l) ' ' in
List.iteri (fun i c -> Bytes.set b i c) l;
Bytes.unsafe_to_string b
type 'a t = {
mutable st : 'a parse_or_compiled;
}
(* syntactic version *)
and _ parse =
| Many : 'a t * unit t * multiplicity -> 'a list parse
| Skip : 'a t * multiplicity -> unit parse (* same as Many, but ignores *)
| Lazy : 'a t lazy_t -> 'a parse
(* compiled version *)
and _ compiled =
| C_Return : 'a -> 'a compiled
| C_Map : ('a -> 'b) * 'a t -> 'b compiled
| C_Filter: ('a -> bool) * 'a t -> 'a compiled
| C_App : ('a -> 'b) t * 'a t -> 'b compiled
| C_AppLeft : 'a t * 'b t -> 'a compiled
| C_AppRight : 'a t * 'b t -> 'b compiled
| C_Fail : string -> 'a compiled
| C_Int : int compiled
| C_Float : float compiled
| C_Junk : unit compiled (* ignore next char *)
| C_AnyOf : CharSet.t -> char compiled
| C_SwitchC : 'a t CharMap.t * 'a t option -> 'a compiled
| C_Eof : unit compiled
and 'a parse_or_compiled =
| Parse of 'a parse
| Compiled of 'a compiled
(** {2 Helpers} *)
(* build a new parser *)
let make p = {st=Parse p}
let make_c c = {st=Compiled c}
let make_pc st = {st}
let ppmult fmt = function
| Star -> Format.pp_print_string fmt "*"
| Plus -> Format.pp_print_string fmt "+"
| Question -> Format.pp_print_string fmt "?"
let print fmt p =
let depth = ref 0 in
(* print up to a given limit into lazy values *)
let rec print_aux
: type a. Format.formatter -> a t -> unit
= fun fmt p ->
let ppstr = Format.pp_print_string
and ppf fmt x = Format.fprintf fmt x in
let ppc fmt c = ppf fmt "'%s'" (print_char c) in
match p.st with
| Compiled (C_Return _) -> ppstr fmt "<ret>"
| Compiled (C_Map (_, x)) -> ppf fmt "@[(map@ %a)@]" print_aux x
| Compiled (C_Filter (_, x)) -> ppf fmt "@[(filter@ %a)@]" print_aux x
| Compiled (C_App (f, x)) -> ppf fmt "@[<2>@[%a@]@ <*>@ @[%a@]@]" print_aux f print_aux x
| Compiled (C_AppLeft (a, b)) -> ppf fmt "@[%a@ <<@ %a@]" print_aux a print_aux b
| Compiled (C_AppRight (a, b)) -> ppf fmt "@[%a@ >>@ %a@]" print_aux a print_aux b
| Compiled (C_Fail _) -> ppf fmt "<fail>"
| Compiled C_Int -> ppstr fmt "<int>"
| Compiled C_Float -> ppstr fmt "<float>"
| Compiled C_Junk -> ppstr fmt "<junk>"
| Compiled (C_AnyOf set) -> ppf fmt "@[(any@ %s)@]" (print_char_set set)
| Parse (Many (p, sep, mult)) ->
ppf fmt "@[<2>(@[%a@]@ sep:@[%a@])%a@]" print_aux p print_aux sep ppmult mult
| Parse (Skip (p, mult)) ->
ppf fmt "@[<2>(skip @[%a@]%a)@]" print_aux p ppmult mult
| Compiled (C_SwitchC (map, None)) ->
ppf fmt "@[<hv2>(switch@ @[%a@])@]" (ppmap ppc print_aux) map
| Compiled (C_SwitchC (map, Some o)) ->
ppf fmt "@[<hv2>(switch@ @[%a@]@ or:%a)@]" (ppmap ppc print_aux) map print_aux o
| Parse (Lazy _) when !depth > 3 -> ppf fmt "<lazy>"
| Parse (Lazy (lazy p)) ->
incr depth;
print_aux fmt p;
decr depth
| Compiled C_Eof -> ppstr fmt "<eof>"
in
print_aux fmt p
let int_first_char = lazy (set_of_string "-0123456789")
let float_first_char = lazy (set_of_string ".-0123456789")
(* a set of characters that are valid as first characters of a parser *)
type possible_first_chars =
| Set of CharSet.t
| AllChars
| NoChar
| NoCharOrSet of CharSet.t (* either no char, or something starting with set *)
| IsFail of string
let ret_set set = match CharSet.cardinal set with
| 0 -> NoChar
| 256 -> AllChars
| _ -> Set set
let ret_no_char_or set = match CharSet.cardinal set with
| 0 -> NoChar
| 256 -> AllChars
| _ -> NoCharOrSet set
(* pfc of parsing a or b *)
let union_pfc a b = match a, b with
| Set a, Set b -> ret_set (CharSet.union a b)
| NoCharOrSet s, Set s'
| Set s', NoCharOrSet s -> ret_no_char_or (CharSet.union s s')
| NoChar, Set s
| Set s, NoChar -> ret_no_char_or s
| NoCharOrSet s, NoCharOrSet s' -> ret_no_char_or (CharSet.union s s')
| IsFail e, _ | _, IsFail e -> IsFail e
| AllChars, _ | _, AllChars -> AllChars
| NoChar, o | o, NoChar -> o
(* pfc of parsing a then b *)
let then_pfc a b = match a, b with
| Set a, Set b -> ret_set (CharSet.union a b)
| NoCharOrSet s, NoCharOrSet s' -> ret_no_char_or (CharSet.union s s')
| NoCharOrSet s, Set s' -> ret_set (CharSet.union s s')
| NoCharOrSet s, NoChar -> ret_no_char_or s
| Set s, _ -> ret_set s
| IsFail e, _ | _, IsFail e -> IsFail e
| AllChars, _ | _, AllChars -> AllChars
| NoChar, o -> o
let (<|||>) a b = match a with
| NoChar -> Lazy.force b
| NoCharOrSet _ -> then_pfc a (Lazy.force b)
| _ -> a
(* set of possibilities for the first char of a parser *)
let rec pfc : type a. a t -> possible_first_chars = fun t -> pfc_pc t.st
and pfc_pc
: type a. a parse_or_compiled -> possible_first_chars
= function
| Parse p -> pfc_p p
| Compiled c -> pfc_c c
and pfc_p
: type a. a parse -> possible_first_chars
= function
| Many (p, _, (Question | Star)) -> union_pfc (pfc p) NoChar
| Many (p, _, Plus) -> pfc p
| Skip (p, (Question | Star)) -> union_pfc (pfc p) NoChar
| Skip (p, Plus) -> pfc p
| Lazy (lazy p) -> pfc p
and pfc_c
: type a. a compiled -> possible_first_chars
= function
| C_Return _ -> NoChar
| C_Map (_, x) -> pfc x
| C_Filter (_, x) -> pfc x
| C_App (f, x) -> pfc f <|||> lazy (pfc x)
| C_AppLeft (a, b) -> pfc a <|||> lazy (pfc b)
| C_AppRight (a, b) -> pfc a <|||> lazy (pfc b)
| C_Fail e -> IsFail e
| C_Int -> Set (Lazy.force int_first_char)
| C_Float -> Set (Lazy.force float_first_char)
| C_Junk -> AllChars
| C_AnyOf set -> ret_set set
| C_SwitchC (map, None) -> ret_set (domain_of_char_map map)
| C_SwitchC (map, Some o) ->
let s = domain_of_char_map map in
union_pfc (ret_set s) (pfc o)
| C_Eof -> NoChar
let possible_first_chars = pfc
(** {2 Combinators} *)
let return x = make_c (C_Return x)
let pure = return
let success = pure ()
let fail msg = make_c (C_Fail msg)
let junk = make_c C_Junk
let failf fmt = Printf.ksprintf (fun msg -> fail msg) fmt
let map f x = match x.st with
| Compiled (C_Map (g, y)) -> make_c (C_Map (compose f g, y))
| Compiled (C_Return x) -> pure (f x)
| _ -> make_c (C_Map (f, x))
let app f x = match f.st with
| Compiled (C_Return f) -> map f x
| _ -> make_c (C_App (f, x))
let fun_and f f' x = f x && f' x
let filter f x = match x.st with
| Compiled (C_Return y) -> if f y then return y else fail "filter failed"
| Compiled (C_Filter (f', y)) -> make_c (C_Filter (fun_and f f', y))
| _ -> make_c (C_Filter (f, x))
let app_left a b = make_c (C_AppLeft (a, b)) (* return (fun x y -> x) <*> a <*> b *)
let app_right a b = make_c (C_AppRight (a, b)) (* return (fun x y -> y) <*> a <*> b *)
let int = make_c C_Int
let float = make_c C_Float
let many ?(sep=success) p = make (Many (p, sep, Star))
let many1 ?(sep=success) p = make (Many (p, sep, Plus))
let skip p = make (Skip (p, Star))
let skip1 p = make (Skip (p, Plus))
let opt p =
map
(function
| [x] -> Some x
| [] -> None
| _ -> assert false
) (make (Many (p, success, Question)))
let any_of' s = make_c (C_AnyOf s)
let any_of s = any_of' (set_of_string s)
let char c = any_of' (CharSet.singleton c)
let spaces = skip (any_of " \t")
let spaces1 = skip1 (any_of " \t")
let white = skip (any_of " \t\n")
let white1 = skip1 (any_of " \t\n")
let alpha_lower_ = set_of_string "abcdefghijklmonpqrstuvwxyz"
let alpha_upper_ = set_of_string "ABCDEFGHIJKLMONPQRSTUVWXYZ"
let num_ = set_of_string "0123456789"
let alpha_ = CharSet.union alpha_lower_ alpha_upper_
let symbols_ = set_of_string "|!;$#@%&-_/="
let alpha_lower = any_of' alpha_lower_
let alpha_upper = any_of' alpha_upper_
let num = any_of' num_
let symbols = any_of' symbols_
let alpha = any_of' alpha_
let alpha_num = any_of' (CharSet.union num_ alpha_)
let eof = make_c C_Eof
let switch_c ?default l =
if l = [] then match default with
| None -> invalid_arg "switch_c: empty list";
| Some d -> d
else
let map = List.fold_left
(fun map (c, t) ->
if CharMap.mem c map
then invalid_arg (str "switch_c: duplicate char %c" c);
CharMap.add c t map
) CharMap.empty l
in
make_c (C_SwitchC (map, default))
exception ExnIsFail of string
let make_switch_c a b = make_c (C_SwitchC (a, b))
(* binary choice: compiled into decision tree *)
let rec merge a b =
(* build a switch by first char *)
try
begin match a.st, b.st with
| Compiled (C_SwitchC (map_a, def_a)),
Compiled (C_SwitchC (map_b, def_b)) ->
(* merge jump tables *)
let def = match def_a, def_b with
| None, None -> None
| Some d, None
| None, Some d -> Some d
| Some _, Some _ ->
invalid_arg "choice: ambiguous, several parsers accept any input"
in
let map = CharMap.merge
(fun _ a b -> match a, b with
| Some a', Some b' -> Some (merge a' b')
| Some m, None
| None, Some m -> Some m
| None, None -> assert false
) map_a map_b
in
make_switch_c map def
| Compiled (C_SwitchC (map, def)), other
| other, Compiled (C_SwitchC (map, def)) ->
let map', def' = match pfc_pc other, def with
| AllChars, _ ->
invalid_arg "choice: ambiguous, several parsers accept any input"
| NoChar, None -> map, Some (make_pc other)
| NoChar, Some _ ->
invalid_arg "choice: ambiguous"
| IsFail msg, _ -> raise (ExnIsFail msg)
| NoCharOrSet set, def
| Set set, def ->
if CharSet.exists (fun c -> CharMap.mem c map) set
then invalid_arg
(str "choice: ambiguous parsers (overlap on {%s})"
(print_char_set (CharSet.inter set (domain_of_char_map map))));
(* else: merge jump tables *)
let map = map_add_set map set (make_pc other) in
map, def
in
make_switch_c map' def'
| _ ->
begin match possible_first_chars a, possible_first_chars b with
| (Set set1 | NoCharOrSet set1), (Set set2 | NoCharOrSet set2) ->
if CharSet.exists (fun c -> CharSet.mem c set2) set1
then invalid_arg
(str "choice: ambiguous parsers (overlap on {%s})"
(print_char_set (CharSet.inter set1 set2)));
let map = map_add_set CharMap.empty set1 a in
let map = map_add_set map set2 b in
make_switch_c map None
| IsFail e, _ | _, IsFail e -> raise (ExnIsFail e)
| Set s, NoChar -> make_switch_c (map_add_set CharMap.empty s a) (Some b)
| NoChar, Set s -> make_switch_c (map_add_set CharMap.empty s b) (Some a)
| AllChars, _ | _, AllChars ->
invalid_arg "choice: ambiguous parsers (one accepts everything)"
| (NoChar | NoCharOrSet _), (NoChar | NoCharOrSet _) ->
invalid_arg "choice: ambiguous parsers (both accept nothing)"
end
end
with ExnIsFail msg -> make_c (C_Fail msg)
let rec choice = function
| [] -> invalid_arg "choice: empty list";
| [x] -> x
| a :: tl -> merge a (choice tl)
(* temporary structure for buildings switches *)
type 'a trie =
| TrieLeaf of 'a t
| TrieNode of 'a trie CharMap.t
let trie_empty = TrieNode CharMap.empty
let rec parser_of_trie : type a. a trie -> a t = function
| TrieLeaf p -> p
| TrieNode m ->
make_switch_c (CharMap.map parser_of_trie' m) None
(* consume next char, then build sub-trie *)
and parser_of_trie'
: type a. a trie -> a t
= fun x -> app_right junk (parser_of_trie x)
(* build prefix trie *)
let switch_s l =
if l = [] then invalid_arg "switch_s: empty list";
(* add parser p in trie [t], with key slice of [s] starting at [i] *)
let rec add_trie t s i p =
if i = String.length s
then match t with
| TrieNode m when CharMap.is_empty m -> TrieLeaf p
| TrieNode _ -> invalid_arg (str "key \"%s\" is prefix of another key" s)
| TrieLeaf _ -> invalid_arg (str "duplicate key \"%s\"" s)
else
let c = String.get s i in
match t with
| TrieLeaf _ ->
invalid_arg (str "key \"%s\" is prefixed by another key" s)
| TrieNode map ->
try
let sub = CharMap.find c map in
let sub = add_trie sub s (i+1) p in
TrieNode (CharMap.add c sub map)
with Not_found ->
let sub = add_trie trie_empty s (i+1) p in
TrieNode (CharMap.add c sub map)
in
let trie =
List.fold_left
(fun trie (s, p) ->
if s = "" then invalid_arg "switch_s: empty string";
add_trie trie s 0 p
) trie_empty l
in
parser_of_trie trie
let bool =
switch_s
[ "true", return true
; "false", return false
]
let fix f =
(* outermost lazy needed for the recursive definition *)
let rec r = {
st=Parse (Lazy (lazy (f r)));
} in
r
module Infix = struct
let (>|=) x f = map f x
let (<*>) = app
let (<<) = app_left
let (>>) = app_right
let (<+>) a b = choice [a; b]
let (<::>) a b = pure (fun x l -> x::l) <*> a <*> b
end
include Infix
let word =
pure (fun c s -> str_of_l (c :: s)) <*> alpha <*> many alpha_num
let quoted =
let q = char '"' in
let escaped = char '\\' >> char '"' in
let inner = choice [escaped; alpha_num; any_of "()' \t\n|!;$#@%&-_/=~.,:<>[]"] in
q >> (many inner >|= str_of_l) << q
(** {2 Compilation} *)
let encode_cons x sep tl = pure (fun x _sep tl -> x :: tl) <*> x <*> sep <*> tl
let encode_many
: type a. set:CharSet.t -> p:a t -> self:a list t -> sep:unit t -> a list t
= fun ~set ~p ~self ~sep ->
let on_success = encode_cons p sep self
and on_fail = pure [] in
make_switch_c (map_add_set CharMap.empty set on_success) (Some on_fail)
let encode_opt ~set x =
let mk_one x = [x] in
let on_success = make_c (C_Map (mk_one, x))
and on_fail = pure [] in
make_switch_c (map_add_set CharMap.empty set on_success) (Some on_fail)
let encode_skip
: type a. set:CharSet.t -> p:a t -> self:unit t -> unit t
= fun ~set ~p ~self ->
let on_success = p >> self
and on_fail = pure () in
make_switch_c (map_add_set CharMap.empty set on_success) (Some on_fail)
let many_
: type a. sep:unit t -> mult:multiplicity -> p:a t -> a list t
= fun ~sep ~mult ~p -> match possible_first_chars p with
| Set set ->
begin match mult with
| Star -> fix (fun self -> encode_many ~set ~sep ~p ~self)
| Plus -> encode_cons p sep (fix (fun self -> encode_many ~set ~sep ~p ~self))
| Question -> encode_opt ~set p
end
| IsFail msg -> fail msg
| NoCharOrSet _ -> invalid_arg (str "many: invalid parser (might not consume input)")
| AllChars -> invalid_arg (str "many: invalid parser (always succeeds)")
| NoChar -> invalid_arg (str "many: invalid parser (does not consume input)")
let skip_ : type a. mult:multiplicity -> p:a t -> unit t
= fun ~mult ~p -> match possible_first_chars p with
| Set set ->
begin match mult with
| Star -> fix (fun self -> encode_skip ~set ~p ~self)
| Plus -> p >> fix (fun self -> encode_skip ~set ~p ~self)
| Question -> encode_opt ~set p >> pure ()
end
| IsFail msg -> fail msg
| NoCharOrSet _ -> invalid_arg (str "many: invalid parser (might not consume input)")
| AllChars -> invalid_arg (str "skip: invalid parser (always succeeds)")
| NoChar -> invalid_arg (str "skip: invalid parser (does not consume input)")
let rec compile
: type a. a t -> a compiled
= fun t -> match t.st with
| Compiled c -> c (* already compiled *)
| Parse (Many (p, sep, mult)) ->
let c = compile (many_ ~sep ~mult ~p) in
t.st <- Compiled c;
c
| Parse (Skip (p, mult)) ->
let c = compile (skip_ ~mult ~p) in
t.st <- Compiled c;
c
| Parse (Lazy (lazy p)) ->
let c = compile p in
t.st <- Compiled c;
c
(** {2 Signatures} *)
type error = {
line: int;
col: int;
msg: string;
}
let string_of_error e = str "at %d:%d; %s" e.line e.col e.msg
exception Error of error
module type S = sig
type source
(** Source of characters *)
val parse : source -> 'a t -> ('a, error) result
(** Parse the given source using the parser, and returns the parsed value. *)
val parse': source -> 'a t -> ('a, string) result
(** Same as {!parse}, but returns a user-friendly string in case of failure *)
val parse_exn : source -> 'a t -> 'a
(** Unsafe version of {!parse}.
@raise Error if parsing fails *)
end
(** {2 Build a parser from a given Monadic Input} *)
module type INPUT = sig
type t
val read : t -> Bytes.t -> int -> int -> int
end
type token =
| Yield of char
| EOF
module type READER = sig
type t
type source
val create : source -> t
val peek : t -> token (* peek; do not consume *)
val next : t -> token (* read and consume *)
val junk : t -> unit (* consume last token, obtained with junk *)
val line : t -> int
val col : t -> int
end
module ReaderOfInput(I : INPUT) : READER with type source = I.t = struct
type t = {
mutable rline : int;
mutable rcol : int;
input : I.t;
buf : Bytes.t;
mutable i : int;
mutable len : int;
}
type source = I.t
let line t = t.rline
let col t = t.rcol
let create input = {
rline=1;
rcol=0;
input;
buf = Bytes.make 1024 ' ';
i=1;
len=1; (* trick for initialization *)
}
let read_next t =
let c = Bytes.get t.buf t.i in
t.i <- t.i + 1;
if c = '\n' then (
t.rcol <- 0;
t.rline <- t.rline + 1;
) else (
t.rcol <- t.rcol + 1
);
Yield c
let refill t =
t.len <- I.read t.input t.buf 0 (Bytes.length t.buf);
t.i <- 0;
()
let next t =
if t.len = 0 then EOF
else if t.i = t.len
then (
refill t;
if t.len = 0 then EOF else read_next t
) else read_next t
let peek t =
if t.i = t.len
then refill t;
Yield (Bytes.get t.buf t.i)
let junk t =
assert (t.len > 0 && t.i < t.len);
t.i <- t.i + 1
end
module MakeFromReader(R : READER) : S with type source = R.source = struct
type source = R.source
let error r msg =
raise (Error {
line = R.line r;
col = R.col r;
msg;
})
let errorf r fmt =
Printf.ksprintf
(fun msg -> error r msg)
fmt
let is_int c = Char.code c >= Char.code '0' && Char.code c <= Char.code '9'
let to_int c = Char.code c - Char.code '0'
let rec parse_int r ~sign i = match R.peek r with
| EOF -> i
| Yield c when is_int c ->
R.junk r;
parse_int r ~sign (10 * i + to_int c)
| Yield '-' when i = 0 && sign ->
(* switch sign: only on first char *)
R.junk r;
parse_int r ~sign:false 0
| _ -> if sign then i else -i
let parse_float _r _buf = assert false
let rec parse_rec : type a. R.t -> a t -> a =
fun r p -> match compile p with
| C_Return x -> x
| C_Map (f, x) ->
let y = parse_rec r x in
f y
| C_Filter (f, x) ->
let y = parse_rec r x in
if f y then y else errorf r "filter failed"
| C_App (f, x) ->
let f' = parse_rec r f in
let x' = parse_rec r x in
f' x'
| C_AppLeft (a, b) ->
let a' = parse_rec r a in
let _ = parse_rec r b in
a'
| C_AppRight (a, b) ->
let _ = parse_rec r a in
let b' = parse_rec r b in
b'
| C_Fail msg -> error r msg
| C_Int -> parse_int r ~sign:true 0
| C_Float -> parse_float r (Buffer.create 8)
| C_Junk -> R.junk r
| C_AnyOf set ->
begin match R.next r with
| EOF -> errorf r "expected any of %s, got EOF" (print_char_set set)
| Yield c ->
if CharSet.mem c set then c
else errorf r "expected any of %s, got '%s'" (print_char_set set) (print_char c)
end
| C_SwitchC (map, def) ->
begin match R.peek r with
| EOF -> errorf r "expected any of %s, got EOF" (print_char_map map)
| Yield c ->
begin try
let p' = CharMap.find c map in
parse_rec r p'
with Not_found -> match def with
| None ->
errorf r "expected any of %s, got %c" (print_char_map map) c
| Some d -> parse_rec r d
end
end
| C_Eof ->
begin match R.next r with
| EOF -> ()
| Yield c -> errorf r "expected EOF, got %c" c
end
(* public functions *)
let parse_exn src p =
let r = R.create src in
parse_rec r p
let parse src p =
let r = R.create src in
try
`Ok (parse_rec r p)
with Error e ->
`Error e
let parse' src p = match parse src p with
| `Ok x -> `Ok x
| `Error e -> `Error (string_of_error e)
end
module Make(I : INPUT) = struct
module R = ReaderOfInput(I)
include MakeFromReader(R)
end
module Str = MakeFromReader(struct
(* reader of string *)
type t = {
str : string;
mutable i : int;
mutable rcol : int;
mutable rline : int;
}
type source = string
let create str = {
str;
i = 0;
rcol = 1;
rline = 1;
}
let line t = t.rline
let col t = t.rcol
let peek t =
if t.i = String.length t.str then EOF else Yield (String.get t.str t.i)
let junk t =
assert (t.i < String.length t.str);
t.i <- t.i + 1
let next t =
if t.i = String.length t.str then EOF
else (
let c = String.get t.str t.i in
t.i <- t.i + 1;
if c = '\n' then (
t.rcol <- 1;
t.rline <- t.rline + 1
) else t.rcol <- t.rcol + 1;
Yield c
)
end)
module Chan = Make(struct
type t = in_channel
let read = input
end)

271
src/string/app_parse.mli Normal file
View file

@ -0,0 +1,271 @@
(*
copyright (c) 2013-2015, simon cruanes
all rights reserved.
redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer. redistributions in binary
form must reproduce the above copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other materials provided with
the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*)
(** {1 Applicative Parser Combinators}
Example: basic S-expr parser
{[
open Containers_string.App_parse;;
type sexp = Atom of string | List of sexp list;;
let mkatom a = Atom a;;
let mklist l = List l;;
let ident_char = alpha_num <+> any_of "|!;$#@%&-_/=*.:~+[]<>'" ;;
let ident = many1 ident_char >|= str_of_l ;;
let atom = (ident <+> quoted) >|= mkatom ;;
let sexp = fix (fun sexp ->
white >>
(atom <+>
((char '(' >> many sexp << char ')') >|= mklist)
)
);;
Str.parse_exn "(a (b c d) e)" sexp;;
]}
{b status: experimental}
@since 0.10
*)
type ('a,'b) result = [`Error of 'b | `Ok of 'a]
type 'a t
(** Parser that yields an error or a value of type 'a *)
(** {6 Combinators} *)
val return : 'a -> 'a t
(** Parser that succeeds with the given value *)
val pure : 'a -> 'a t
(** Synonym to {!return} *)
val junk : unit t
(** Skip next char *)
val fail : string -> 'a t
(** [fail msg] fails with the given error message *)
val failf : ('a, unit, string, 'b t) format4 -> 'a
val app : ('a -> 'b) t -> 'a t -> 'b t
(** Applicative *)
val map : ('a -> 'b) -> 'a t -> 'b t
(** Map the parsed value *)
val int : int t
(** Parse an integer *)
val float : float t
(** Parse a floating point number *)
val bool : bool t
(** Parse "true" or "false" *)
val char : char -> char t
(** [char c] parses [c] and [c] only *)
val any_of : string -> char t
(** Parse any of the chars present in the given string *)
val alpha_lower : char t
val alpha_upper : char t
val alpha : char t
val symbols : char t
(** symbols, such as "!-=_"... *)
val num : char t
val alpha_num : char t
val word : string t
(** [word] parses any identifier not starting with an integer and
not containing any whitespace nor delimiter
TODO: specify *)
val quoted : string t
(** Quoted string, following OCaml conventions *)
val str_of_l : char list -> string
(** Helper to build strings from lists of chars *)
val spaces : unit t
(** Parse a sequence of ['\t'] and [' '] *)
val spaces1 : unit t
(** Same as {!spaces} but requires at least one space *)
val white : unit t
(** Parse a sequence of ['\t'], ['\n'] and [' '] *)
val white1 : unit t
val eof : unit t
(** Matches the end of input, fails otherwise *)
val many : ?sep:unit t -> 'a t -> 'a list t
(** 0 or more parsed elements of the given type.
@param sep separator between elements of the list (for instance, {!space}) *)
val many1 : ?sep:unit t -> 'a t -> 'a list t
(** Same as {!many}, but needs at least one element *)
val skip : _ t -> unit t
(** Skip 0 or more instances of the given parser *)
val skip1 : _ t -> unit t
val opt : 'a t -> 'a option t
(** [opt x] tries to parse [x], and returns [None] otherwise *)
val filter : ('a -> bool) -> 'a t -> 'a t
(** [filter f p] parses the same as [p], but fails if the returned value
does not satisfy [f] *)
(* TODO: complement operator any_but (all but \, for instance) *)
(* TODO: a "if-then-else" combinator (assuming the test has a
set of possible first chars) *)
val switch_c : ?default:'a t -> (char * 'a t) list -> 'a t
(** [switch_c l] matches the next char and uses the corresponding parser.
Fails if the next char is not in the list, unless default is defined.
@param default parser to use if no char matches
@raise Invalid_argument if some char occurs several times in [l] *)
val switch_s : (string * 'a t) list -> 'a t
(** [switch_s l] attempts to match matches any of the strings in [l].
If one of those strings matches, the corresponding parser
is used from now on.
@raise Invalid_argument if some string is a prefix of another string,
or is empty, or if the list is empty *)
val choice : 'a t list -> 'a t
(** [choice l] chooses between the parsers, unambiguously
@raise Invalid_argument if the list is empty, or if some parsers
overlap, making the choice ambiguous *)
val fix : ('a t -> 'a t) -> 'a t
(** [fix f] makes a fixpoint *)
module Infix : sig
val (>|=) : 'a t -> ('a -> 'b) -> 'b t
(** Infix version of {!map} *)
val (<*>) : ('a -> 'b) t -> 'a t -> 'b t
(** Synonym to {!app} *)
val (>>) : _ t -> 'a t -> 'a t
(** [a >> b] parses [a], ignores its result, then parses [b] *)
val (<<) : 'a t -> _ t -> 'a t
(** [a << b] parses [a], then [b], and discards [b] to return [a] *)
val (<+>) : 'a t -> 'a t -> 'a t
(** [a <+> b] is [choice [a;b]], a binary choice *)
val (<::>) : 'a t -> 'a list t -> 'a list t
(** [a <::> b] is [app (fun x l -> x::l) a b] *)
end
include module type of Infix
(** {2 Signatures} *)
(** {6 Parsing} *)
type error = {
line: int;
col: int;
msg: string;
}
val string_of_error : error -> string
exception Error of error
module type S = sig
type source
(** Source of characters *)
val parse : source -> 'a t -> ('a, error) result
(** Parse the given source using the parser, and returns the parsed value. *)
val parse': source -> 'a t -> ('a, string) result
(** Same as {!parse}, but returns a user-friendly string in case of failure *)
val parse_exn : source -> 'a t -> 'a
(** Unsafe version of {!parse}.
@raise Error if parsing fails *)
end
(** {2 Parse} *)
module type INPUT = sig
type t
val read : t -> Bytes.t -> int -> int -> int
end
module Make(I : INPUT) : S with type source = I.t
(** {2 Low-level interface} *)
val print : Format.formatter -> _ t -> unit
(** Print a parser structure, for debug purpose *)
type token =
| Yield of char
| EOF
module type READER = sig
type t
type source (* underlying source *)
val create : source -> t
val peek : t -> token (* peek; do not consume *)
val next : t -> token (* read and consume *)
val junk : t -> unit (* consume last token, obtained with junk *)
val line : t -> int
val col : t -> int
end
module MakeFromReader(R : READER) : S with type source = R.source
(** {2 Defaults} *)
module Str : S with type source = string
module Chan : S with type source = in_channel

View file

@ -1,5 +1,6 @@
# OASIS_START
# DO NOT EDIT (digest: eed887f169b0c8e02f98f97c676f846c)
# DO NOT EDIT (digest: 200ff8feb7cb7b8d5e2aea5b7c63241a)
KMP
Levenshtein
App_parse
# OASIS_STOP

2
src/unix/.merlin Normal file
View file

@ -0,0 +1,2 @@
PKG unix
REC

115
src/unix/CCUnix.ml Normal file
View file

@ -0,0 +1,115 @@
(*
copyright (c) 2013-2015, simon cruanes
all rights reserved.
redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer. redistributions in binary
form must reproduce the above copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other materials provided with
the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*)
(** {1 High-level Functions on top of Unix} *)
type 'a or_error = [`Ok of 'a | `Error of string]
type 'a gen = unit -> 'a option
(** {2 Calling Commands} *)
let int_of_process_status = function
| Unix.WEXITED i
| Unix.WSIGNALED i
| Unix.WSTOPPED i -> i
let str_exists s p =
let rec f s p i =
if i = String.length s then false
else p s.[i] || f s p (i+1)
in
f s p 0
let rec iter_gen f g = match g() with
| None -> ()
| Some x -> f x; iter_gen f g
(* print a string, but escaped if required *)
let escape_str buf s =
if str_exists s
(function ' ' | '"' | '\'' | '\n' | '\t'-> true | _ -> false)
then (
Buffer.add_char buf '\'';
String.iter
(function
| '\'' -> Buffer.add_string buf "''"
| c -> Buffer.add_char buf c
) s;
Buffer.add_char buf '\'';
) else Buffer.add_string buf s
let read_all ?(size=1024) ic =
let buf = ref (Bytes.create size) in
let len = ref 0 in
try
while true do
(* resize *)
if !len = Bytes.length !buf then (
buf := Bytes.extend !buf 0 !len;
);
assert (Bytes.length !buf > !len);
let n = input ic !buf !len (Bytes.length !buf - !len) in
len := !len + n;
if n = 0 then raise Exit; (* exhausted *)
done;
assert false (* never reached*)
with Exit ->
Bytes.sub_string !buf 0 !len
type call_result =
< stdout:string;
stderr:string;
status:Unix.process_status;
errcode:int; (** extracted from status *)
>
let kbprintf' buf fmt k = Printf.kbprintf k buf fmt
let call ?(bufsize=2048) ?(stdin=`Str "") ?(env=[||]) cmd =
(* render the command *)
let buf = Buffer.create 256 in
kbprintf' buf cmd
(fun buf ->
let cmd = Buffer.contents buf in
let oc, ic, errc = Unix.open_process_full cmd env in
(* send stdin *)
begin match stdin with
| `Str s -> output_string ic s
| `Gen g -> iter_gen (output_string ic) g
end;
close_out ic;
(* read out and err *)
let out = read_all ~size:bufsize oc in
let err = read_all ~size:bufsize errc in
let status = Unix.close_process_full (oc, ic, errc) in
object
method stdout = out
method stderr = err
method status = status
method errcode = int_of_process_status status
end
)

77
src/unix/CCUnix.mli Normal file
View file

@ -0,0 +1,77 @@
(*
copyright (c) 2013-2015, simon cruanes
all rights reserved.
redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer. redistributions in binary
form must reproduce the above copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other materials provided with
the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*)
(** {1 High-level Functions on top of Unix}
Some useful functions built on top of Unix.
{b status: unstable}
@since 0.10 *)
type 'a or_error = [`Ok of 'a | `Error of string]
type 'a gen = unit -> 'a option
(** {2 Calling Commands} *)
val escape_str : Buffer.t -> string -> unit
(** Escape a string so it can be a shell argument.
*)
(*$T
CCPrint.sprintf "%a" escape_str "foo" = "foo"
CCPrint.sprintf "%a" escape_str "foo bar" = "'foo bar'"
CCPrint.sprintf "%a" escape_str "fo'o b'ar" = "'fo''o b''ar'"
*)
type call_result =
< stdout:string;
stderr:string;
status:Unix.process_status;
errcode:int; (** extracted from status *)
>
val call : ?bufsize:int ->
?stdin:[`Gen of string gen | `Str of string] ->
?env:string array ->
('a, Buffer.t, unit, call_result) format4 ->
'a
(** [call cmd] wraps the result of [Unix.open_process_full cmd] into an
object. It reads the full stdout and stderr of the subprocess before
returning.
@param stdin if provided, the generator or string is consumed and fed to
the subprocess input channel, which is then closed.
@param bufsize buffer size used to read stdout and stderr
@param env environment to run the command in
*)
(*$T
(call ~stdin:(`Str "abc") "cat")#stdout = "abc"
(call "echo %a" escape_str "a'b'c")#stdout = "abc\n"
(call "echo %s" "a'b'c")#stdout = "abc\n"
*)

View file

@ -0,0 +1,4 @@
# OASIS_START
# DO NOT EDIT (digest: cc54fa6ddd5d32bdf577cb187f4cf07c)
CCUnix
# OASIS_STOP

View file

@ -0,0 +1,4 @@
# OASIS_START
# DO NOT EDIT (digest: cc54fa6ddd5d32bdf577cb187f4cf07c)
CCUnix
# OASIS_STOP

View file

@ -1,6 +1,7 @@
(** Tests for persistent union find *)
open OUnit
open Containers_misc
module P = Puf.Make(struct type t = int let get_id i = i end)