mirror of
https://github.com/c-cube/ocaml-containers.git
synced 2026-01-28 03:44:51 -05:00
Merge commit 'c8d834b722a95b9feedf17f91511d138662c6d6f' as 'sequence'
This commit is contained in:
commit
bb64cc9a6d
32 changed files with 10804 additions and 0 deletions
9
sequence/.gitignore
vendored
Normal file
9
sequence/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
.*.swp
|
||||||
|
_build
|
||||||
|
*.native
|
||||||
|
*.docdir
|
||||||
|
*.html
|
||||||
|
man/
|
||||||
|
sequence.install
|
||||||
|
setup.log
|
||||||
|
setup.data
|
||||||
8
sequence/.merlin
Normal file
8
sequence/.merlin
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
S .
|
||||||
|
S bench/
|
||||||
|
S tests/
|
||||||
|
B _build
|
||||||
|
B _build/tests/
|
||||||
|
B _build/bench/
|
||||||
|
PKG oUnit
|
||||||
|
PKG benchmark
|
||||||
5
sequence/.ocamlinit
Normal file
5
sequence/.ocamlinit
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
#directory "_build";;
|
||||||
|
#load "sequence.cma";;
|
||||||
|
open Sequence.Infix;;
|
||||||
|
(* vim:syntax=ocaml
|
||||||
|
*)
|
||||||
70
sequence/CHANGELOG.md
Normal file
70
sequence/CHANGELOG.md
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
## 0.5.1
|
||||||
|
|
||||||
|
- `Sequence.IO` module, a very very simple way to read/write files
|
||||||
|
- options: `to_opt/of_opt/head/head_exn`
|
||||||
|
|
||||||
|
## 0.5
|
||||||
|
|
||||||
|
- conversion with `klist`
|
||||||
|
- add monadic, choice and applicative infix operators and `>|=`
|
||||||
|
- add several functions:
|
||||||
|
* `product2`
|
||||||
|
* `find`, `mem`
|
||||||
|
* `doubleton`, `cons`, `snoc`
|
||||||
|
* `drop_while`, `take_while`...
|
||||||
|
* `concat_str`
|
||||||
|
- aliases to existing functions
|
||||||
|
- use `delimcc` in a new module, `SequenceInvert`, in order to reverse the
|
||||||
|
control flow (here with conversion to Gen)
|
||||||
|
- fix examples, tests and doc (about `product`)
|
||||||
|
- reading benchmark for persistent sequences.
|
||||||
|
- replace `Bench` with `Benchmark`
|
||||||
|
|
||||||
|
## 0.4.1
|
||||||
|
|
||||||
|
- `persistent_lazy`
|
||||||
|
- use bin_annot
|
||||||
|
|
||||||
|
## 0.4
|
||||||
|
|
||||||
|
- API change for `persistent`
|
||||||
|
- more efficient implementation for `persistent`
|
||||||
|
- remove `TypeClass`
|
||||||
|
- API change for `min`/`max` (in case the sequence is empty)
|
||||||
|
- conversion with `Gen`
|
||||||
|
- use Oasis
|
||||||
|
|
||||||
|
## 0.3.7
|
||||||
|
|
||||||
|
- decreasing int range
|
||||||
|
- printing functions
|
||||||
|
|
||||||
|
## 0.3.6.1
|
||||||
|
|
||||||
|
- documentation
|
||||||
|
- bugfixes
|
||||||
|
|
||||||
|
## 0.3.6
|
||||||
|
|
||||||
|
- `fmap`
|
||||||
|
- functors to adapt `Set` and `Map`
|
||||||
|
|
||||||
|
## 0.3.5
|
||||||
|
|
||||||
|
- tests and benchmarks
|
||||||
|
- `join` combinator
|
||||||
|
- optimization for `Sequence.persistent`
|
||||||
|
|
||||||
|
## 0.3.4
|
||||||
|
|
||||||
|
- `sort`, `uniq`, `group` and `sort_uniq` combinators implemented
|
||||||
|
- some conversion functions that use `Sequence.t2`
|
||||||
|
- infix operators in `Sequence.Infix`
|
||||||
|
- `Sequence.t2` type for efficient iteration on pairs of elements
|
||||||
|
- some combinators are adapted to `Sequence.t2`
|
||||||
|
- `zip`, `unzip` and `zip_i` to convert between `t` and `t2`
|
||||||
|
- added `scan` combinator
|
||||||
|
|
||||||
|
note: git log --no-merges previous_version..HEAD --pretty=%s
|
||||||
22
sequence/LICENSE
Normal file
22
sequence/LICENSE
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
Copyright (c) 2012, 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.
|
||||||
21
sequence/META
Normal file
21
sequence/META
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
# OASIS_START
|
||||||
|
# DO NOT EDIT (digest: 3b9ebef180f5e4bdb720d2103ba95667)
|
||||||
|
version = "0.5.1"
|
||||||
|
description = "Simple sequence (iterator) datatype and combinators"
|
||||||
|
archive(byte) = "sequence.cma"
|
||||||
|
archive(byte, plugin) = "sequence.cma"
|
||||||
|
archive(native) = "sequence.cmxa"
|
||||||
|
archive(native, plugin) = "sequence.cmxs"
|
||||||
|
exists_if = "sequence.cma"
|
||||||
|
package "invert" (
|
||||||
|
version = "0.5.1"
|
||||||
|
description = "Simple sequence (iterator) datatype and combinators"
|
||||||
|
requires = "sequence delimcc"
|
||||||
|
archive(byte) = "invert.cma"
|
||||||
|
archive(byte, plugin) = "invert.cma"
|
||||||
|
archive(native) = "invert.cmxa"
|
||||||
|
archive(native, plugin) = "invert.cmxs"
|
||||||
|
exists_if = "invert.cma"
|
||||||
|
)
|
||||||
|
# OASIS_STOP
|
||||||
|
|
||||||
67
sequence/Makefile
Normal file
67
sequence/Makefile
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
# OASIS_START
|
||||||
|
# DO NOT EDIT (digest: a3c674b4239234cbbe53afe090018954)
|
||||||
|
|
||||||
|
SETUP = ocaml setup.ml
|
||||||
|
|
||||||
|
build: setup.data
|
||||||
|
$(SETUP) -build $(BUILDFLAGS)
|
||||||
|
|
||||||
|
doc: setup.data build
|
||||||
|
$(SETUP) -doc $(DOCFLAGS)
|
||||||
|
|
||||||
|
test: setup.data build
|
||||||
|
$(SETUP) -test $(TESTFLAGS)
|
||||||
|
|
||||||
|
all:
|
||||||
|
$(SETUP) -all $(ALLFLAGS)
|
||||||
|
|
||||||
|
install: setup.data
|
||||||
|
$(SETUP) -install $(INSTALLFLAGS)
|
||||||
|
|
||||||
|
uninstall: setup.data
|
||||||
|
$(SETUP) -uninstall $(UNINSTALLFLAGS)
|
||||||
|
|
||||||
|
reinstall: setup.data
|
||||||
|
$(SETUP) -reinstall $(REINSTALLFLAGS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(SETUP) -clean $(CLEANFLAGS)
|
||||||
|
|
||||||
|
distclean:
|
||||||
|
$(SETUP) -distclean $(DISTCLEANFLAGS)
|
||||||
|
|
||||||
|
setup.data:
|
||||||
|
$(SETUP) -configure $(CONFIGUREFLAGS)
|
||||||
|
|
||||||
|
configure:
|
||||||
|
$(SETUP) -configure $(CONFIGUREFLAGS)
|
||||||
|
|
||||||
|
.PHONY: build doc test all install uninstall reinstall clean distclean configure
|
||||||
|
|
||||||
|
# OASIS_STOP
|
||||||
|
|
||||||
|
run-tests:
|
||||||
|
./run_tests.native
|
||||||
|
|
||||||
|
examples:
|
||||||
|
ocamlbuild examples/test_sexpr.native
|
||||||
|
|
||||||
|
push_doc: all doc
|
||||||
|
scp -r sequence.docdir/* cedeela.fr:~/simon/root/software/sequence/
|
||||||
|
|
||||||
|
push_stable: all
|
||||||
|
git checkout stable
|
||||||
|
git merge master -m 'merge from master'
|
||||||
|
oasis setup
|
||||||
|
git commit -a -m 'oasis files'
|
||||||
|
git push origin
|
||||||
|
git checkout master
|
||||||
|
|
||||||
|
VERSION=$(shell awk '/^Version:/ {print $$2}' _oasis)
|
||||||
|
|
||||||
|
update_next_tag:
|
||||||
|
@echo "update version to $(VERSION)..."
|
||||||
|
sed -i "s/NEXT_VERSION/$(VERSION)/g" *.ml *.mli
|
||||||
|
sed -i "s/NEXT_RELEASE/$(VERSION)/g" *.ml *.mli
|
||||||
|
|
||||||
|
.PHONY: benchs tests examples update_next_tag push_doc push_stable
|
||||||
50
sequence/README.md
Normal file
50
sequence/README.md
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
Sequence
|
||||||
|
========
|
||||||
|
|
||||||
|
Simple sequence abstract datatype, intended to transfer a finite number of
|
||||||
|
elements from one data structure to another. Some transformations on sequences,
|
||||||
|
like `filter`, `map`, `take`, `drop` and `append` can be performed before the
|
||||||
|
sequence is iterated/folded on.
|
||||||
|
|
||||||
|
Sequence is not designed to be as general-purpose or flexible as, say,
|
||||||
|
Batteries' `Enum.t`. Rather, it aims at providing a very simple and efficient
|
||||||
|
way of iterating on a finite number of values, only allocating (most of the time)
|
||||||
|
one intermediate closure to do so. For instance, iterating on keys, or values,
|
||||||
|
of a `Hashtbl.t`, without creating a list.
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
=============
|
||||||
|
|
||||||
|
See [the online API](http://cedeela.fr/~simon/software/sequence/Sequence.html).
|
||||||
|
|
||||||
|
Build
|
||||||
|
=====
|
||||||
|
|
||||||
|
1. via opam `opam install sequence`
|
||||||
|
2. manually (need OCaml >= 3.12): `make all install`
|
||||||
|
|
||||||
|
If you have `OUnit` installed, you can build and run tests with
|
||||||
|
|
||||||
|
$ make tests
|
||||||
|
$ ./run_tests.native
|
||||||
|
|
||||||
|
If you have `Bench` installed, you can build and run benchmarks with
|
||||||
|
|
||||||
|
$ make benchs
|
||||||
|
$ ./benchs.native
|
||||||
|
|
||||||
|
To see how to use the library, check the `examples` directory.
|
||||||
|
`tests.ml` has a few examples of how to convert basic data structures into
|
||||||
|
sequences, and conversely.
|
||||||
|
|
||||||
|
Examples
|
||||||
|
========
|
||||||
|
|
||||||
|
The module `examples/sexpr.mli` exposes the interface of the S-expression
|
||||||
|
example library. It requires OCaml>=4.0 to compile, because of the GADT
|
||||||
|
structure used in the monadic parser combinators part of `examples/sexpr.ml`.
|
||||||
|
|
||||||
|
License
|
||||||
|
=======
|
||||||
|
|
||||||
|
Sequence is available under the BSD license.
|
||||||
88
sequence/_oasis
Normal file
88
sequence/_oasis
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
OASISFormat: 0.4
|
||||||
|
Name: sequence
|
||||||
|
Version: 0.5.1
|
||||||
|
Homepage: https://github.com/c-cube/sequence
|
||||||
|
Authors: Simon Cruanes
|
||||||
|
License: BSD-2-clause
|
||||||
|
LicenseFile: LICENSE
|
||||||
|
Plugins: META (0.3), DevFiles (0.3)
|
||||||
|
BuildTools: ocamlbuild
|
||||||
|
|
||||||
|
Synopsis: Simple sequence (iterator) datatype and combinators
|
||||||
|
Description:
|
||||||
|
Simple sequence datatype, intended to transfer a finite number of
|
||||||
|
elements from one data structure to another. Some transformations on sequences,
|
||||||
|
like `filter`, `map`, `take`, `drop` and `append` can be performed before the
|
||||||
|
sequence is iterated/folded on.
|
||||||
|
|
||||||
|
Flag bench
|
||||||
|
Description: enable benchmarks (require library Benchmark)
|
||||||
|
Default: false
|
||||||
|
|
||||||
|
Flag invert
|
||||||
|
Description: build sequence.invert (requires Delimcc)
|
||||||
|
Default: false
|
||||||
|
|
||||||
|
Library "sequence"
|
||||||
|
Path: .
|
||||||
|
Modules: Sequence
|
||||||
|
|
||||||
|
Library "invert"
|
||||||
|
Path: invert
|
||||||
|
Build$: flag(invert)
|
||||||
|
Install$: flag(invert)
|
||||||
|
Modules: SequenceInvert
|
||||||
|
FindlibName: invert
|
||||||
|
FindlibParent: sequence
|
||||||
|
BuildDepends: sequence,delimcc
|
||||||
|
|
||||||
|
Document sequence
|
||||||
|
Title: Sequence docs
|
||||||
|
Type: ocamlbuild (0.3)
|
||||||
|
BuildTools+: ocamldoc
|
||||||
|
Install: true
|
||||||
|
XOCamlbuildPath: .
|
||||||
|
XOCamlbuildLibraries: sequence
|
||||||
|
|
||||||
|
Test all
|
||||||
|
Type: custom
|
||||||
|
Command: make run-tests
|
||||||
|
TestTools: run_tests
|
||||||
|
Run$: flag(tests)
|
||||||
|
|
||||||
|
Executable run_tests
|
||||||
|
Path: tests/
|
||||||
|
Install: false
|
||||||
|
CompiledObject: native
|
||||||
|
MainIs: run_tests.ml
|
||||||
|
Build$: flag(tests)
|
||||||
|
BuildDepends: sequence,oUnit
|
||||||
|
|
||||||
|
Executable benchs
|
||||||
|
Path: bench
|
||||||
|
Install: false
|
||||||
|
CompiledObject: native
|
||||||
|
Build$: flag(bench)
|
||||||
|
BuildDepends: sequence,benchmark
|
||||||
|
MainIs: benchs.ml
|
||||||
|
|
||||||
|
Executable bench_persistent
|
||||||
|
Path: bench
|
||||||
|
Install: false
|
||||||
|
CompiledObject: native
|
||||||
|
Build$: flag(bench)
|
||||||
|
BuildDepends: sequence,benchmark
|
||||||
|
MainIs: bench_persistent.ml
|
||||||
|
|
||||||
|
Executable bench_persistent_read
|
||||||
|
Path: bench
|
||||||
|
Install: false
|
||||||
|
CompiledObject: native
|
||||||
|
Build$: flag(bench)
|
||||||
|
BuildDepends: sequence,benchmark
|
||||||
|
MainIs: bench_persistent_read.ml
|
||||||
|
|
||||||
|
SourceRepository head
|
||||||
|
Type: git
|
||||||
|
Location: https://github.com/c-cube/sequence
|
||||||
|
Browser: https://github.com/c-cube/sequence/tree/master/src
|
||||||
39
sequence/_tags
Normal file
39
sequence/_tags
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
# OASIS_START
|
||||||
|
# DO NOT EDIT (digest: e8d5fe31ff471d3c0ec54943fe50d011)
|
||||||
|
# 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
|
||||||
|
<**/.svn>: -traverse
|
||||||
|
<**/.svn>: not_hygienic
|
||||||
|
".bzr": -traverse
|
||||||
|
".bzr": not_hygienic
|
||||||
|
".hg": -traverse
|
||||||
|
".hg": not_hygienic
|
||||||
|
".git": -traverse
|
||||||
|
".git": not_hygienic
|
||||||
|
"_darcs": -traverse
|
||||||
|
"_darcs": not_hygienic
|
||||||
|
# Library sequence
|
||||||
|
"sequence.cmxs": use_sequence
|
||||||
|
# Library invert
|
||||||
|
"invert/invert.cmxs": use_invert
|
||||||
|
<invert/*.ml{,i}>: pkg_delimcc
|
||||||
|
<invert/*.ml{,i}>: use_sequence
|
||||||
|
# Executable run_tests
|
||||||
|
"tests/run_tests.native": pkg_oUnit
|
||||||
|
"tests/run_tests.native": use_sequence
|
||||||
|
<tests/*.ml{,i}>: pkg_oUnit
|
||||||
|
<tests/*.ml{,i}>: use_sequence
|
||||||
|
# Executable benchs
|
||||||
|
"bench/benchs.native": pkg_benchmark
|
||||||
|
"bench/benchs.native": use_sequence
|
||||||
|
# Executable bench_persistent
|
||||||
|
"bench/bench_persistent.native": pkg_benchmark
|
||||||
|
"bench/bench_persistent.native": use_sequence
|
||||||
|
# Executable bench_persistent_read
|
||||||
|
"bench/bench_persistent_read.native": pkg_benchmark
|
||||||
|
"bench/bench_persistent_read.native": use_sequence
|
||||||
|
<bench/*.ml{,i}>: pkg_benchmark
|
||||||
|
<bench/*.ml{,i}>: use_sequence
|
||||||
|
# OASIS_STOP
|
||||||
|
true: bin_annot
|
||||||
128
sequence/bench/bench_persistent.ml
Normal file
128
sequence/bench/bench_persistent.ml
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
module MList = struct
|
||||||
|
type 'a t = {
|
||||||
|
content : 'a array; (* elements of the node *)
|
||||||
|
mutable len : int; (* number of elements in content *)
|
||||||
|
mutable tl : 'a t; (* tail *)
|
||||||
|
} (** A list that contains some elements, and may point to another list *)
|
||||||
|
|
||||||
|
let _empty () : 'a t = Obj.magic 0
|
||||||
|
(** Empty list, for the tl field *)
|
||||||
|
|
||||||
|
let make n =
|
||||||
|
assert (n > 0);
|
||||||
|
{ content = Array.make n (Obj.magic 0);
|
||||||
|
len = 0;
|
||||||
|
tl = _empty ();
|
||||||
|
}
|
||||||
|
|
||||||
|
let rec is_empty l =
|
||||||
|
l.len = 0 && (l.tl == _empty () || is_empty l.tl)
|
||||||
|
|
||||||
|
let rec iter f l =
|
||||||
|
for i = 0 to l.len - 1 do f l.content.(i); done;
|
||||||
|
if l.tl != _empty () then iter f l.tl
|
||||||
|
|
||||||
|
let iteri f l =
|
||||||
|
let rec iteri i f l =
|
||||||
|
for j = 0 to l.len - 1 do f (i+j) l.content.(j); done;
|
||||||
|
if l.tl != _empty () then iteri (i+l.len) f l.tl
|
||||||
|
in iteri 0 f l
|
||||||
|
|
||||||
|
let rec iter_rev f l =
|
||||||
|
(if l.tl != _empty () then iter_rev f l.tl);
|
||||||
|
for i = l.len - 1 downto 0 do f l.content.(i); done
|
||||||
|
|
||||||
|
let length l =
|
||||||
|
let rec len acc l =
|
||||||
|
if l.tl == _empty () then acc+l.len else len (acc+l.len) l.tl
|
||||||
|
in len 0 l
|
||||||
|
|
||||||
|
(** Get element by index *)
|
||||||
|
let rec get l i =
|
||||||
|
if i < l.len then l.content.(i)
|
||||||
|
else if i >= l.len && l.tl == _empty () then raise (Invalid_argument "MList.get")
|
||||||
|
else get l.tl (i - l.len)
|
||||||
|
|
||||||
|
(** Push [x] at the end of the list. It returns the block in which the
|
||||||
|
element is inserted. *)
|
||||||
|
let rec push x l =
|
||||||
|
if l.len = Array.length l.content
|
||||||
|
then begin (* insert in the next block *)
|
||||||
|
(if l.tl == _empty () then
|
||||||
|
let n = Array.length l.content in
|
||||||
|
l.tl <- make (n + n lsr 1));
|
||||||
|
push x l.tl
|
||||||
|
end else begin (* insert in l *)
|
||||||
|
l.content.(l.len) <- x;
|
||||||
|
l.len <- l.len + 1;
|
||||||
|
l
|
||||||
|
end
|
||||||
|
|
||||||
|
(** Reverse list (in place), and returns the new head *)
|
||||||
|
let rev l =
|
||||||
|
let rec rev prev l =
|
||||||
|
(* reverse array *)
|
||||||
|
for i = 0 to (l.len-1) / 2 do
|
||||||
|
let x = l.content.(i) in
|
||||||
|
l.content.(i) <- l.content.(l.len - i - 1);
|
||||||
|
l.content.(l.len - i - 1) <- x;
|
||||||
|
done;
|
||||||
|
(* reverse next block *)
|
||||||
|
let l' = l.tl in
|
||||||
|
l.tl <- prev;
|
||||||
|
if l' == _empty () then l else rev l l'
|
||||||
|
in
|
||||||
|
rev (_empty ()) l
|
||||||
|
|
||||||
|
(** Build a MList of elements of the Seq. The optional argument indicates
|
||||||
|
the size of the blocks *)
|
||||||
|
let of_seq ?(size=8) seq =
|
||||||
|
(* read sequence into a MList.t *)
|
||||||
|
let start = make size in
|
||||||
|
let l = ref start in
|
||||||
|
seq (fun x -> l := push x !l);
|
||||||
|
start
|
||||||
|
|
||||||
|
let to_seq l =
|
||||||
|
fun k -> iter k l
|
||||||
|
end
|
||||||
|
|
||||||
|
(** Store content of the seqerator in an enum *)
|
||||||
|
let persistent_mlist seq =
|
||||||
|
let l = MList.of_seq seq in
|
||||||
|
MList.to_seq l
|
||||||
|
|
||||||
|
let bench_mlist n =
|
||||||
|
for i = 0 to 100 do
|
||||||
|
let _ = persistent_mlist Sequence.(1 -- n) in
|
||||||
|
()
|
||||||
|
done
|
||||||
|
|
||||||
|
let bench_naive n =
|
||||||
|
for i = 0 to 100 do
|
||||||
|
let l = Sequence.to_rev_list Sequence.(1 -- n) in
|
||||||
|
let _ = Sequence.of_list (List.rev l) in
|
||||||
|
()
|
||||||
|
done
|
||||||
|
|
||||||
|
let bench_current n =
|
||||||
|
for i = 0 to 100 do
|
||||||
|
let _ = Sequence.persistent Sequence.(1 -- n) in
|
||||||
|
()
|
||||||
|
done
|
||||||
|
|
||||||
|
let () =
|
||||||
|
let bench_n n =
|
||||||
|
Printf.printf "BENCH for %d\n" n;
|
||||||
|
let res = Benchmark.throughputN 5
|
||||||
|
[ "mlist", bench_mlist, n
|
||||||
|
; "naive", bench_naive, n
|
||||||
|
; "current", bench_current, n
|
||||||
|
]
|
||||||
|
in Benchmark.tabulate res
|
||||||
|
in
|
||||||
|
bench_n 100;
|
||||||
|
bench_n 100_000;
|
||||||
|
()
|
||||||
|
|
||||||
|
(* vim:Use benchmark: *)
|
||||||
139
sequence/bench/bench_persistent_read.ml
Normal file
139
sequence/bench/bench_persistent_read.ml
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
module MList = struct
|
||||||
|
type 'a t = {
|
||||||
|
content : 'a array; (* elements of the node *)
|
||||||
|
mutable len : int; (* number of elements in content *)
|
||||||
|
mutable tl : 'a t; (* tail *)
|
||||||
|
} (** A list that contains some elements, and may point to another list *)
|
||||||
|
|
||||||
|
let _empty () : 'a t = Obj.magic 0
|
||||||
|
(** Empty list, for the tl field *)
|
||||||
|
|
||||||
|
let make n =
|
||||||
|
assert (n > 0);
|
||||||
|
{ content = Array.make n (Obj.magic 0);
|
||||||
|
len = 0;
|
||||||
|
tl = _empty ();
|
||||||
|
}
|
||||||
|
|
||||||
|
let rec is_empty l =
|
||||||
|
l.len = 0 && (l.tl == _empty () || is_empty l.tl)
|
||||||
|
|
||||||
|
let rec iter f l =
|
||||||
|
for i = 0 to l.len - 1 do f l.content.(i); done;
|
||||||
|
if l.tl != _empty () then iter f l.tl
|
||||||
|
|
||||||
|
let iteri f l =
|
||||||
|
let rec iteri i f l =
|
||||||
|
for j = 0 to l.len - 1 do f (i+j) l.content.(j); done;
|
||||||
|
if l.tl != _empty () then iteri (i+l.len) f l.tl
|
||||||
|
in iteri 0 f l
|
||||||
|
|
||||||
|
let rec iter_rev f l =
|
||||||
|
(if l.tl != _empty () then iter_rev f l.tl);
|
||||||
|
for i = l.len - 1 downto 0 do f l.content.(i); done
|
||||||
|
|
||||||
|
let length l =
|
||||||
|
let rec len acc l =
|
||||||
|
if l.tl == _empty () then acc+l.len else len (acc+l.len) l.tl
|
||||||
|
in len 0 l
|
||||||
|
|
||||||
|
(** Get element by index *)
|
||||||
|
let rec get l i =
|
||||||
|
if i < l.len then l.content.(i)
|
||||||
|
else if i >= l.len && l.tl == _empty () then raise (Invalid_argument "MList.get")
|
||||||
|
else get l.tl (i - l.len)
|
||||||
|
|
||||||
|
(** Push [x] at the end of the list. It returns the block in which the
|
||||||
|
element is inserted. *)
|
||||||
|
let rec push x l =
|
||||||
|
if l.len = Array.length l.content
|
||||||
|
then begin (* insert in the next block *)
|
||||||
|
(if l.tl == _empty () then
|
||||||
|
let n = Array.length l.content in
|
||||||
|
l.tl <- make (n + n lsr 1));
|
||||||
|
push x l.tl
|
||||||
|
end else begin (* insert in l *)
|
||||||
|
l.content.(l.len) <- x;
|
||||||
|
l.len <- l.len + 1;
|
||||||
|
l
|
||||||
|
end
|
||||||
|
|
||||||
|
(** Reverse list (in place), and returns the new head *)
|
||||||
|
let rev l =
|
||||||
|
let rec rev prev l =
|
||||||
|
(* reverse array *)
|
||||||
|
for i = 0 to (l.len-1) / 2 do
|
||||||
|
let x = l.content.(i) in
|
||||||
|
l.content.(i) <- l.content.(l.len - i - 1);
|
||||||
|
l.content.(l.len - i - 1) <- x;
|
||||||
|
done;
|
||||||
|
(* reverse next block *)
|
||||||
|
let l' = l.tl in
|
||||||
|
l.tl <- prev;
|
||||||
|
if l' == _empty () then l else rev l l'
|
||||||
|
in
|
||||||
|
rev (_empty ()) l
|
||||||
|
|
||||||
|
(** Build a MList of elements of the Seq. The optional argument indicates
|
||||||
|
the size of the blocks *)
|
||||||
|
let of_seq ?(size=8) seq =
|
||||||
|
(* read sequence into a MList.t *)
|
||||||
|
let start = make size in
|
||||||
|
let l = ref start in
|
||||||
|
seq (fun x -> l := push x !l);
|
||||||
|
start
|
||||||
|
|
||||||
|
let to_seq l =
|
||||||
|
fun k -> iter k l
|
||||||
|
end
|
||||||
|
|
||||||
|
(** Store content of the seqerator in an enum *)
|
||||||
|
let persistent_mlist seq =
|
||||||
|
let l = MList.of_seq seq in
|
||||||
|
MList.to_seq l
|
||||||
|
|
||||||
|
let bench_mlist n =
|
||||||
|
persistent_mlist Sequence.(1 -- n)
|
||||||
|
|
||||||
|
let bench_list n =
|
||||||
|
let l = Sequence.to_rev_list Sequence.(1 -- n) in
|
||||||
|
Sequence.of_list (List.rev l)
|
||||||
|
|
||||||
|
let bench_naive n =
|
||||||
|
let s = Sequence.(1 -- n) in
|
||||||
|
Sequence.iter ignore s ;
|
||||||
|
s
|
||||||
|
|
||||||
|
let bench_current n =
|
||||||
|
Sequence.persistent Sequence.(1 -- n)
|
||||||
|
|
||||||
|
let bench_array n =
|
||||||
|
let a = Sequence.to_array Sequence.(1 -- n) in
|
||||||
|
Sequence.of_array a
|
||||||
|
|
||||||
|
let read s =
|
||||||
|
Sequence.map (fun x -> x + 1) s
|
||||||
|
|
||||||
|
let () =
|
||||||
|
let bench_n n =
|
||||||
|
Printf.printf "BENCH for %d\n" n;
|
||||||
|
let res =
|
||||||
|
let mlist = bench_mlist n in
|
||||||
|
let list = bench_list n in
|
||||||
|
let current = bench_current n in
|
||||||
|
let array = bench_current n in
|
||||||
|
let naive = bench_naive n in
|
||||||
|
Benchmark.throughputN 5
|
||||||
|
[ "mlist", read, mlist
|
||||||
|
; "list", read, list
|
||||||
|
; "current", read, current
|
||||||
|
; "array", read, array
|
||||||
|
; "naive", read, naive
|
||||||
|
]
|
||||||
|
in Benchmark.tabulate res
|
||||||
|
in
|
||||||
|
bench_n 100;
|
||||||
|
bench_n 100_000;
|
||||||
|
()
|
||||||
|
|
||||||
|
(* vim:Use benchmark: *)
|
||||||
34
sequence/bench/benchs.ml
Normal file
34
sequence/bench/benchs.ml
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
|
||||||
|
module S = Sequence
|
||||||
|
open Sequence.Infix
|
||||||
|
|
||||||
|
let small = [10;20;50;100;500]
|
||||||
|
let medium = small @ [1000;10_000;100_000]
|
||||||
|
let big = medium @ [500_000; 1_000_000; 2_000_000]
|
||||||
|
|
||||||
|
let bench_fold n =
|
||||||
|
0 -- n |> S.fold (+) 0 |> ignore
|
||||||
|
|
||||||
|
let bench_flatmap n =
|
||||||
|
0 -- n |> S.flatMap (fun i -> i -- (i+5)) |> (fun _ -> ())
|
||||||
|
|
||||||
|
let bench_product n =
|
||||||
|
S.product (0 -- n) (0 -- n) (fun (i,j) -> ())
|
||||||
|
|
||||||
|
let _ =
|
||||||
|
List.iter
|
||||||
|
(fun (name,bench,sizes) ->
|
||||||
|
Format.printf "-------------------------------------------------------@.";
|
||||||
|
Format.printf "bench %s@." name;
|
||||||
|
List.iter
|
||||||
|
(fun n ->
|
||||||
|
let name = name ^ " on " ^ string_of_int n in
|
||||||
|
let res = Benchmark.throughput1 2 ~name bench n in
|
||||||
|
Benchmark.tabulate res;
|
||||||
|
) sizes
|
||||||
|
)
|
||||||
|
[ "fold", bench_fold, big
|
||||||
|
; "flatmap", bench_flatmap, medium
|
||||||
|
; "product", bench_product, small
|
||||||
|
];
|
||||||
|
()
|
||||||
11
sequence/bench/simple_bench.ml
Normal file
11
sequence/bench/simple_bench.ml
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
open Sequence.Infix
|
||||||
|
|
||||||
|
let _ =
|
||||||
|
let n = int_of_string Sys.argv.(1) in
|
||||||
|
let seq = 0 -- n in
|
||||||
|
let start = Unix.gettimeofday () in
|
||||||
|
seq |> Sequence.persistent |> Sequence.fold (+) 0 |> ignore;
|
||||||
|
let stop = Unix.gettimeofday () in
|
||||||
|
Format.printf "iter on %d: %.4f@." n (stop -. start);
|
||||||
|
()
|
||||||
27
sequence/configure
vendored
Executable file
27
sequence/configure
vendored
Executable file
|
|
@ -0,0 +1,27 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# OASIS_START
|
||||||
|
# DO NOT EDIT (digest: dc86c2ad450f91ca10c931b6045d0499)
|
||||||
|
set -e
|
||||||
|
|
||||||
|
FST=true
|
||||||
|
for i in "$@"; do
|
||||||
|
if $FST; then
|
||||||
|
set --
|
||||||
|
FST=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
case $i in
|
||||||
|
--*=*)
|
||||||
|
ARG=${i%%=*}
|
||||||
|
VAL=${i##*=}
|
||||||
|
set -- "$@" "$ARG" "$VAL"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
set -- "$@" "$i"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
ocaml setup.ml -configure "$@"
|
||||||
|
# OASIS_STOP
|
||||||
305
sequence/examples/sexpr.ml
Normal file
305
sequence/examples/sexpr.ml
Normal file
|
|
@ -0,0 +1,305 @@
|
||||||
|
(*
|
||||||
|
Zipperposition: a functional superposition prover for prototyping
|
||||||
|
Copyright (C) 2012 Simon Cruanes
|
||||||
|
|
||||||
|
This is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||||
|
02110-1301 USA.
|
||||||
|
*)
|
||||||
|
|
||||||
|
(* {1 Basic S-expressions, with printing and parsing} *)
|
||||||
|
|
||||||
|
(** S-expression *)
|
||||||
|
type t =
|
||||||
|
| Atom of string (** An atom *)
|
||||||
|
| List of t list (** A list of S-expressions *)
|
||||||
|
|
||||||
|
(** Token that compose a Sexpr once serialized *)
|
||||||
|
type token = [`Open | `Close | `Atom of string]
|
||||||
|
|
||||||
|
(** {2 Traverse a sequence of tokens} *)
|
||||||
|
|
||||||
|
(** Iterate on the S-expression, calling the callback with tokens *)
|
||||||
|
let rec iter f s = match s with
|
||||||
|
| Atom a -> f (`Atom a)
|
||||||
|
| List l -> f `Open; iter_list f l; f `Close
|
||||||
|
and iter_list f l = match l with
|
||||||
|
| [] -> ()
|
||||||
|
| x::l' -> iter f x; iter_list f l'
|
||||||
|
|
||||||
|
(** Traverse. This yields a sequence of tokens *)
|
||||||
|
let traverse s = Sequence.from_iter (fun k -> iter k s)
|
||||||
|
|
||||||
|
(** Returns the same sequence of tokens, but during iteration, if
|
||||||
|
the structure of the Sexpr corresponding to the sequence
|
||||||
|
is wrong (bad parenthesing), Invalid_argument is raised
|
||||||
|
and iteration is stoped *)
|
||||||
|
let validate seq =
|
||||||
|
let depth = ref 0 in
|
||||||
|
Sequence.map
|
||||||
|
(fun tok -> match tok with
|
||||||
|
| `Open -> incr depth; tok
|
||||||
|
| `Close -> if !depth = 0
|
||||||
|
then raise (Invalid_argument "wrong parenthesing")
|
||||||
|
else decr depth; tok
|
||||||
|
| _ -> tok)
|
||||||
|
seq
|
||||||
|
|
||||||
|
(** {2 Text <-> tokens} *)
|
||||||
|
|
||||||
|
(** Lex: create a sequence of tokens from the given in_channel. *)
|
||||||
|
let lex input =
|
||||||
|
let seq_fun k =
|
||||||
|
let in_word = ref false in
|
||||||
|
let buf = Buffer.create 128 in
|
||||||
|
(* loop. TODO handle escaping of (), and "" *)
|
||||||
|
let rec next c =
|
||||||
|
match c with
|
||||||
|
| '(' -> k `Open
|
||||||
|
| ')' -> flush_word(); k `Close
|
||||||
|
| ' ' | '\t' | '\n' -> flush_word ()
|
||||||
|
| c -> in_word := true; Buffer.add_char buf c
|
||||||
|
(* finish the previous word token *)
|
||||||
|
and flush_word () =
|
||||||
|
if !in_word then begin
|
||||||
|
(* this whitespace follows a word *)
|
||||||
|
let word = Buffer.contents buf in
|
||||||
|
Buffer.clear buf;
|
||||||
|
in_word := false;
|
||||||
|
k (`Atom word)
|
||||||
|
end
|
||||||
|
in
|
||||||
|
Sequence.iter next input
|
||||||
|
in
|
||||||
|
Sequence.from_iter seq_fun
|
||||||
|
|
||||||
|
(** Build a Sexpr from a sequence of tokens *)
|
||||||
|
let of_seq seq =
|
||||||
|
(* called on every token *)
|
||||||
|
let rec k stack token = match token with
|
||||||
|
| `Open -> `Open :: stack
|
||||||
|
| `Close -> collapse [] stack
|
||||||
|
| `Atom a -> (`Expr (Atom a)) :: stack
|
||||||
|
(* collapse last list into an `Expr *)
|
||||||
|
and collapse acc stack = match stack with
|
||||||
|
| `Open::stack' -> `Expr (List acc) :: stack'
|
||||||
|
| `Expr a::stack' -> collapse (a :: acc) stack'
|
||||||
|
| _ -> assert false
|
||||||
|
in
|
||||||
|
(* iterate on the sequence, given an empty initial stack *)
|
||||||
|
let stack = Sequence.fold k [] seq in
|
||||||
|
(* stack should contain exactly one expression *)
|
||||||
|
match stack with
|
||||||
|
| [`Expr expr] -> expr
|
||||||
|
| [] -> failwith "no Sexpr could be parsed"
|
||||||
|
| _ -> failwith "too many elements on the stack"
|
||||||
|
|
||||||
|
(** {2 Printing} *)
|
||||||
|
|
||||||
|
(** Print a token on the given formatter *)
|
||||||
|
let pp_token formatter token = match token with
|
||||||
|
| `Open -> Format.fprintf formatter "@[("
|
||||||
|
| `Close -> Format.fprintf formatter ")@]"
|
||||||
|
| `Atom s -> Format.pp_print_string formatter s
|
||||||
|
|
||||||
|
(** Print a sequence of Sexpr tokens on the given formatter *)
|
||||||
|
let pp_tokens formatter tokens =
|
||||||
|
let first = ref true in
|
||||||
|
let last = ref false in
|
||||||
|
Sequence.iter
|
||||||
|
(fun token ->
|
||||||
|
(match token with
|
||||||
|
| `Open -> (if not !first then Format.fprintf formatter " "); first := true
|
||||||
|
| `Close -> first := false; last := true
|
||||||
|
| _ -> if !first then first := false else Format.fprintf formatter " ");
|
||||||
|
pp_token formatter token;
|
||||||
|
if !last then last := false)
|
||||||
|
tokens
|
||||||
|
|
||||||
|
(** Pretty-print the S-expr. If [indent] is true, the S-expression
|
||||||
|
is printed with indentation. *)
|
||||||
|
let pp_sexpr ?(indent=false) formatter s =
|
||||||
|
if indent
|
||||||
|
then Format.fprintf formatter "@[<hov 4>%a@]" pp_tokens (traverse s)
|
||||||
|
else pp_tokens formatter (traverse s)
|
||||||
|
|
||||||
|
(** {2 Serializing} *)
|
||||||
|
|
||||||
|
let output_seq name subexpr k =
|
||||||
|
k `Open;
|
||||||
|
k (`Atom name);
|
||||||
|
Sequence.iter k subexpr;
|
||||||
|
k `Close
|
||||||
|
|
||||||
|
let output_str name str k =
|
||||||
|
k `Open;
|
||||||
|
k (`Atom name);
|
||||||
|
k (`Atom str);
|
||||||
|
k `Close
|
||||||
|
|
||||||
|
(** {2 Parsing} *)
|
||||||
|
|
||||||
|
(** Monadic combinators for parsing data from a sequence of tokens,
|
||||||
|
without converting to concrete S-expressions.
|
||||||
|
|
||||||
|
The [one] parser can raise ParseFailure if it fails to parse
|
||||||
|
the atomic type. *)
|
||||||
|
|
||||||
|
(** parser that returns a 'a *)
|
||||||
|
type 'a parser =
|
||||||
|
| Return : 'a -> 'a parser
|
||||||
|
| One : (token -> 'a) -> 'a parser
|
||||||
|
| Zero : (token -> 'a parser) -> 'a parser
|
||||||
|
(* | Maybe of (token -> 'a option) *)
|
||||||
|
| Bind : ('b parser * ('b -> 'a parser)) -> 'a parser
|
||||||
|
| Fail : string -> 'a parser
|
||||||
|
|
||||||
|
exception ParseFailure of string
|
||||||
|
|
||||||
|
let (>>=) p f = Bind (p, f)
|
||||||
|
|
||||||
|
let (>>) p p' = p >>= fun _ -> p'
|
||||||
|
|
||||||
|
let return x = Return x
|
||||||
|
|
||||||
|
let fail reason = Fail reason
|
||||||
|
|
||||||
|
let one f = One f
|
||||||
|
|
||||||
|
let skip = One (fun _ -> ())
|
||||||
|
|
||||||
|
let lookahead f = Zero f
|
||||||
|
|
||||||
|
let left = One (function | `Open -> ()
|
||||||
|
| _ -> raise (ParseFailure "expected '('"))
|
||||||
|
|
||||||
|
let right = One (function | `Close -> ()
|
||||||
|
| _ -> raise (ParseFailure "expected ')'"))
|
||||||
|
|
||||||
|
let pair f g =
|
||||||
|
f >>= fun x ->
|
||||||
|
g >>= fun y ->
|
||||||
|
return (x, y)
|
||||||
|
|
||||||
|
let triple f g h =
|
||||||
|
f >>= fun x ->
|
||||||
|
g >>= fun y ->
|
||||||
|
h >>= fun z ->
|
||||||
|
return (x, y, z)
|
||||||
|
|
||||||
|
(** [(name,p) ^|| p'] behaves as p if the next token is [`Atom name], and
|
||||||
|
like [p'] otherwise *)
|
||||||
|
let (^||) (name,p) p' =
|
||||||
|
lookahead
|
||||||
|
(fun token -> match token with
|
||||||
|
| `Atom s when s = name -> skip >> p ()
|
||||||
|
| _ -> p')
|
||||||
|
|
||||||
|
(** Maps the value returned by the parser *)
|
||||||
|
let map p f = p >>= fun x -> return (f x)
|
||||||
|
|
||||||
|
let p_str = one
|
||||||
|
(function | `Atom s -> s | _ -> raise (ParseFailure "expected string"))
|
||||||
|
|
||||||
|
let p_int = one
|
||||||
|
(function | `Atom s -> (try int_of_string s
|
||||||
|
with Failure _ -> raise (ParseFailure "expected int"))
|
||||||
|
| _ -> raise (ParseFailure "expected int"))
|
||||||
|
|
||||||
|
let p_bool = one
|
||||||
|
(function | `Atom s -> (try bool_of_string s
|
||||||
|
with Failure _ -> raise (ParseFailure "expected bool"))
|
||||||
|
| _ -> raise (ParseFailure "expected bool"))
|
||||||
|
|
||||||
|
let p_float = one
|
||||||
|
(function | `Atom s -> (try float_of_string s
|
||||||
|
with Failure _ -> raise (ParseFailure "expected float"))
|
||||||
|
| _ -> raise (ParseFailure "expected float"))
|
||||||
|
|
||||||
|
let many p =
|
||||||
|
let rec elements token =
|
||||||
|
match token with
|
||||||
|
| `Close -> return []
|
||||||
|
| _ ->
|
||||||
|
p >>= fun x ->
|
||||||
|
lookahead elements >>= fun l ->
|
||||||
|
return (x :: l)
|
||||||
|
in
|
||||||
|
left >> lookahead elements >>= fun l -> right >> return l
|
||||||
|
|
||||||
|
let many1 p =
|
||||||
|
p >>= fun x ->
|
||||||
|
many p >>= fun l ->
|
||||||
|
return (x::l)
|
||||||
|
|
||||||
|
(** parsing state that returns a 'a *)
|
||||||
|
type 'a state =
|
||||||
|
| Bottom : 'a state
|
||||||
|
| Push : ('b parser * ('b -> 'a state)) -> 'a state
|
||||||
|
|
||||||
|
(** Actually parse the sequence of tokens, with a callback to be called
|
||||||
|
on every parsed value. The callback decides whether to push another
|
||||||
|
state or whether to continue. *)
|
||||||
|
let parse_k p tokens k =
|
||||||
|
let rec state = Push(p, fun x -> match k x with `Stop -> Bottom | `Continue -> state) in
|
||||||
|
(* Token handler. It also takes the current parser. *)
|
||||||
|
let rec one_step state token =
|
||||||
|
match reduce state with
|
||||||
|
| Bottom -> (* should not happen, unless there are too many tokens *)
|
||||||
|
raise (ParseFailure "unexpected ')'")
|
||||||
|
| Push (Return _, cont) ->
|
||||||
|
assert false (* should be reduced *)
|
||||||
|
| Push (Zero f, cont) ->
|
||||||
|
let p' = f token in
|
||||||
|
let state' = Push (p', cont) in
|
||||||
|
one_step state' token (* do not consume token *)
|
||||||
|
| Push (One f, cont) ->
|
||||||
|
let x = f token in
|
||||||
|
let state' = cont x in
|
||||||
|
reduce state' (* consume token *)
|
||||||
|
(* | Maybe f, _ -> let x = f token in (Obj.magic cont) x *)
|
||||||
|
| Push (Bind (p', cont'), cont) ->
|
||||||
|
let cont'' x =
|
||||||
|
let p'' = cont' x in
|
||||||
|
Push (p'', cont)
|
||||||
|
in
|
||||||
|
let state' = Push (p', cont'') in
|
||||||
|
one_step state' token (* do not consume token *)
|
||||||
|
| Push (Fail reason, _) -> raise (ParseFailure reason)
|
||||||
|
(* Reduce parser state *)
|
||||||
|
and reduce state = match state with
|
||||||
|
| Push (Return x, cont) ->
|
||||||
|
let state' = cont x in
|
||||||
|
reduce state'
|
||||||
|
| _ -> state
|
||||||
|
in
|
||||||
|
(* iterate on the tokens *)
|
||||||
|
ignore (Sequence.fold one_step state tokens)
|
||||||
|
|
||||||
|
(** Parse one value *)
|
||||||
|
let parse p tokens =
|
||||||
|
let res = ref None in
|
||||||
|
parse_k p tokens (fun x -> res := Some x; `Stop);
|
||||||
|
(* return result *)
|
||||||
|
match !res with
|
||||||
|
| None -> raise (ParseFailure "incomplete input")
|
||||||
|
| Some x -> x
|
||||||
|
|
||||||
|
(** Parse a sequence of values *)
|
||||||
|
let parse_seq p tokens =
|
||||||
|
let seq_fun k =
|
||||||
|
parse_k p tokens (fun x -> k x; `Continue)
|
||||||
|
in
|
||||||
|
Sequence.from_iter seq_fun
|
||||||
|
|
||||||
132
sequence/examples/sexpr.mli
Normal file
132
sequence/examples/sexpr.mli
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
(*
|
||||||
|
Zipperposition: a functional superposition prover for prototyping
|
||||||
|
Copyright (C) 2012 Simon Cruanes
|
||||||
|
|
||||||
|
This is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||||
|
02110-1301 USA.
|
||||||
|
*)
|
||||||
|
|
||||||
|
(* {1 Basic S-expressions, with printing and parsing} *)
|
||||||
|
|
||||||
|
type t =
|
||||||
|
| Atom of string (** An atom *)
|
||||||
|
| List of t list (** A list of S-expressions *)
|
||||||
|
(** S-expression *)
|
||||||
|
|
||||||
|
type token = [`Open | `Close | `Atom of string]
|
||||||
|
(** Token that compose a Sexpr once serialized *)
|
||||||
|
|
||||||
|
(** {2 Traverse a sequence of tokens} *)
|
||||||
|
|
||||||
|
val iter : (token -> unit) -> t -> unit
|
||||||
|
(** Iterate on the S-expression, calling the callback with tokens *)
|
||||||
|
|
||||||
|
val traverse : t -> token Sequence.t
|
||||||
|
(** Traverse. This yields a sequence of tokens *)
|
||||||
|
|
||||||
|
val validate : token Sequence.t -> token Sequence.t
|
||||||
|
(** Returns the same sequence of tokens, but during iteration, if
|
||||||
|
the structure of the Sexpr corresponding to the sequence
|
||||||
|
is wrong (bad parenthesing), Invalid_argument is raised
|
||||||
|
and iteration is stoped *)
|
||||||
|
|
||||||
|
(** {2 Text <-> tokens} *)
|
||||||
|
|
||||||
|
val lex : char Sequence.t -> token Sequence.t
|
||||||
|
(** Lex: create a sequence of tokens from the given sequence of chars. *)
|
||||||
|
|
||||||
|
val of_seq : token Sequence.t -> t
|
||||||
|
(** Build a Sexpr from a sequence of tokens, or raise Failure *)
|
||||||
|
|
||||||
|
(** {2 Printing} *)
|
||||||
|
|
||||||
|
val pp_token : Format.formatter -> token -> unit
|
||||||
|
(** Print a token on the given formatter *)
|
||||||
|
|
||||||
|
val pp_tokens : Format.formatter -> token Sequence.t -> unit
|
||||||
|
(** Print a sequence of Sexpr tokens on the given formatter *)
|
||||||
|
|
||||||
|
val pp_sexpr : ?indent:bool -> Format.formatter -> t -> unit
|
||||||
|
(** Pretty-print the S-expr. If [indent] is true, the S-expression
|
||||||
|
is printed with indentation. *)
|
||||||
|
|
||||||
|
(** {2 Serializing} *)
|
||||||
|
|
||||||
|
val output_seq : string -> token Sequence.t -> (token -> unit) -> unit
|
||||||
|
(** print a pair "(name @,sequence)" *)
|
||||||
|
|
||||||
|
val output_str : string -> string -> (token -> unit) -> unit
|
||||||
|
(** print a pair "(name str)" *)
|
||||||
|
|
||||||
|
(** {2 Parsing} *)
|
||||||
|
|
||||||
|
(** Monadic combinators for parsing data from a sequence of tokens,
|
||||||
|
without converting to concrete S-expressions. *)
|
||||||
|
|
||||||
|
type 'a parser
|
||||||
|
|
||||||
|
exception ParseFailure of string
|
||||||
|
|
||||||
|
val (>>=) : 'a parser -> ('a -> 'b parser) -> 'b parser
|
||||||
|
(** Monadic bind: computes a parser from the result of
|
||||||
|
the first parser *)
|
||||||
|
|
||||||
|
val (>>) : 'a parser -> 'b parser -> 'b parser
|
||||||
|
(** Like (>>=), but ignores the result of the first parser *)
|
||||||
|
|
||||||
|
val return : 'a -> 'a parser
|
||||||
|
(** Parser that consumes no input and return the given value *)
|
||||||
|
|
||||||
|
val fail : string -> 'a parser
|
||||||
|
(** Fails parsing with the given message *)
|
||||||
|
|
||||||
|
val one : (token -> 'a) -> 'a parser
|
||||||
|
(** consumes one token with the function *)
|
||||||
|
|
||||||
|
val skip : unit parser
|
||||||
|
(** Skip the token *)
|
||||||
|
|
||||||
|
val lookahead : (token -> 'a parser) -> 'a parser
|
||||||
|
(** choose parser given current token *)
|
||||||
|
|
||||||
|
val left : unit parser
|
||||||
|
(** Parses a `Open *)
|
||||||
|
|
||||||
|
val right : unit parser
|
||||||
|
(** Parses a `Close *)
|
||||||
|
|
||||||
|
val pair : 'a parser -> 'b parser -> ('a * 'b) parser
|
||||||
|
val triple : 'a parser -> 'b parser -> 'c parser -> ('a * 'b * 'c) parser
|
||||||
|
|
||||||
|
val (^||) : (string * (unit -> 'a parser)) -> 'a parser -> 'a parser
|
||||||
|
(** [(name,p) ^|| p'] behaves as [p ()] if the next token is [`Atom name], and
|
||||||
|
like [p'] otherwise *)
|
||||||
|
|
||||||
|
val map : 'a parser -> ('a -> 'b) -> 'b parser
|
||||||
|
(** Maps the value returned by the parser *)
|
||||||
|
|
||||||
|
val p_str : string parser
|
||||||
|
val p_int : int parser
|
||||||
|
val p_bool : bool parser
|
||||||
|
|
||||||
|
val many : 'a parser -> 'a list parser
|
||||||
|
val many1 : 'a parser -> 'a list parser
|
||||||
|
|
||||||
|
val parse : 'a parser -> token Sequence.t -> 'a
|
||||||
|
(** Parses exactly one value from the sequence of tokens. Raises
|
||||||
|
ParseFailure if anything goes wrong. *)
|
||||||
|
|
||||||
|
val parse_seq : 'a parser -> token Sequence.t -> 'a Sequence.t
|
||||||
|
(** Parses a sequence of values *)
|
||||||
131
sequence/examples/test_sexpr.ml
Normal file
131
sequence/examples/test_sexpr.ml
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
|
||||||
|
(** {2 Test sequences} *)
|
||||||
|
|
||||||
|
(** print a list of items using the printing function *)
|
||||||
|
let pp_list ?(sep=", ") pp_item formatter l =
|
||||||
|
Sequence.pp_seq ~sep pp_item formatter (Sequence.of_list l)
|
||||||
|
|
||||||
|
(** Set of integers *)
|
||||||
|
module ISet = Set.Make(struct type t = int let compare = compare end)
|
||||||
|
let iset = (module ISet : Set.S with type elt = int and type t = ISet.t)
|
||||||
|
|
||||||
|
module OrderedString = struct type t = string let compare = compare end
|
||||||
|
module SMap = Sequence.Map.Make(OrderedString)
|
||||||
|
|
||||||
|
let my_map = SMap.of_seq (Sequence.of_list ["1", 1; "2", 2; "3", 3; "answer", 42])
|
||||||
|
|
||||||
|
let sexpr = "(foo bar (bazz quux hello 42) world (zoo foo bar (1 2 (3 4))))"
|
||||||
|
|
||||||
|
type term = | Lambda of term | Const of string | Var of int | Apply of term * term
|
||||||
|
|
||||||
|
let random_term () =
|
||||||
|
let max = 10
|
||||||
|
and num = ref 0 in
|
||||||
|
let rec build depth =
|
||||||
|
if depth > 4 || !num > max then Const (random_const ()) else
|
||||||
|
match Random.int 6 with
|
||||||
|
| 0 -> if depth > 0 then Var (Random.int depth) else Const (random_const ())
|
||||||
|
| 1 -> incr num; Lambda (build (depth+1))
|
||||||
|
| 2 -> Const (random_const ())
|
||||||
|
| _ -> incr num; Apply ((build depth), (build depth))
|
||||||
|
and random_const () = [|"a"; "b"; "c"; "f"; "g"; "h"|].(Random.int 6)
|
||||||
|
in build 0
|
||||||
|
|
||||||
|
let rec sexpr_of_term t =
|
||||||
|
let f t k = match t with
|
||||||
|
| Var i -> Sexpr.output_str "var" (string_of_int i) k
|
||||||
|
| Lambda t' -> Sexpr.output_seq "lambda" (sexpr_of_term t') k
|
||||||
|
| Apply (t1, t2) -> Sexpr.output_seq "apply" (Sequence.append (sexpr_of_term t1) (sexpr_of_term t2)) k
|
||||||
|
| Const s -> Sexpr.output_str "const" s k
|
||||||
|
in Sequence.from_iter (f t)
|
||||||
|
|
||||||
|
let term_parser =
|
||||||
|
let open Sexpr in
|
||||||
|
let rec p_term () =
|
||||||
|
left >>
|
||||||
|
(("lambda", p_lambda) ^|| ("var", p_var) ^|| ("const", p_const) ^||
|
||||||
|
("apply", p_apply) ^|| fail "bad term") >>= fun x ->
|
||||||
|
right >> return x
|
||||||
|
and p_apply () =
|
||||||
|
p_term () >>= fun x ->
|
||||||
|
p_term () >>= fun y ->
|
||||||
|
return (Apply (x,y))
|
||||||
|
and p_var () = p_int >>= fun i -> return (Var i)
|
||||||
|
and p_const () = p_str >>= fun s -> return (Const s)
|
||||||
|
and p_lambda () = p_term () >>= fun t -> return (Lambda t)
|
||||||
|
in p_term ()
|
||||||
|
|
||||||
|
let term_of_sexp seq = Sexpr.parse term_parser seq
|
||||||
|
|
||||||
|
let test_term () =
|
||||||
|
let t = random_term () in
|
||||||
|
Format.printf "@[<h>random term: %a@]@." Sexpr.pp_tokens (sexpr_of_term t);
|
||||||
|
let tokens = sexpr_of_term t in
|
||||||
|
let t' = term_of_sexp tokens in
|
||||||
|
Format.printf "@[<h>parsed: %a@]@." Sexpr.pp_tokens (sexpr_of_term t');
|
||||||
|
()
|
||||||
|
|
||||||
|
let _ =
|
||||||
|
(* lists *)
|
||||||
|
let l = [0;1;2;3;4;5;6] in
|
||||||
|
let l' = Sequence.to_list
|
||||||
|
(Sequence.filter (fun x -> x mod 2 = 0) (Sequence.of_list l)) in
|
||||||
|
let l'' = Sequence.to_list
|
||||||
|
(Sequence.take 3 (Sequence.drop 1 (Sequence.of_list l))) in
|
||||||
|
let h = Hashtbl.create 3 in
|
||||||
|
for i = 0 to 5 do
|
||||||
|
Hashtbl.add h i (i*i);
|
||||||
|
done;
|
||||||
|
let l2 = Sequence.to_list
|
||||||
|
(Sequence.map (fun (x, y) -> (string_of_int x) ^ " -> " ^ (string_of_int y))
|
||||||
|
(Sequence.of_hashtbl h))
|
||||||
|
in
|
||||||
|
let l3 = Sequence.to_list (Sequence.rev (Sequence.int_range ~start:0 ~stop:42)) in
|
||||||
|
let set = List.fold_left (fun set x -> ISet.add x set) ISet.empty [4;3;100;42] in
|
||||||
|
let l4 = Sequence.to_list (Sequence.of_set iset set) in
|
||||||
|
Format.printf "l=@[<h>[%a]@]@." (pp_list Format.pp_print_int) l;
|
||||||
|
Format.printf "l'=@[<h>[%a]@]@." (pp_list Format.pp_print_int) l';
|
||||||
|
Format.printf "l''=@[<h>[%a]@]@." (pp_list Format.pp_print_int) l'';
|
||||||
|
Format.printf "l2=@[<h>[%a]@]@." (pp_list Format.pp_print_string) l2;
|
||||||
|
Format.printf "l3=@[<h>[%a]@]@." (pp_list Format.pp_print_int) l3;
|
||||||
|
Format.printf "s={@[<h>%a@]}@." (Sequence.pp_seq Format.pp_print_int) (Sequence.of_set iset set);
|
||||||
|
Format.printf "l4=@[<h>[%a]@]@." (pp_list Format.pp_print_int) l4;
|
||||||
|
Format.printf "l3[:5]+l4=@[<h>[%a]@]@." (Sequence.pp_seq Format.pp_print_int)
|
||||||
|
(Sequence.of_array
|
||||||
|
(Sequence.to_array (Sequence.append
|
||||||
|
(Sequence.take 5 (Sequence.of_list l3)) (Sequence.of_list l4))));
|
||||||
|
(* sequence, persistent, etc *)
|
||||||
|
let seq = Sequence.int_range ~start:0 ~stop:100000 in
|
||||||
|
let seq' = Sequence.persistent seq in
|
||||||
|
let stream = Sequence.to_stream seq' in
|
||||||
|
Format.printf "test length [0..100000]: persistent1 %d, stream %d, persistent2 %d"
|
||||||
|
(Sequence.length seq') (Sequence.length (Sequence.of_stream stream)) (Sequence.length seq');
|
||||||
|
(* maps *)
|
||||||
|
Format.printf "@[<h>map: %a@]@."
|
||||||
|
(Sequence.pp_seq (fun formatter (k,v) -> Format.fprintf formatter "\"%s\" -> %d" k v))
|
||||||
|
(SMap.to_seq my_map);
|
||||||
|
let module MyMapSeq = Sequence.Map.Adapt(Map.Make(OrderedString)) in
|
||||||
|
let my_map' = MyMapSeq.of_seq (Sequence.of_list ["1", 1; "2", 2; "3", 3; "answer", 42]) in
|
||||||
|
Format.printf "@[<h>map: %a@]@."
|
||||||
|
(Sequence.pp_seq (fun formatter (k,v) -> Format.fprintf formatter "\"%s\" -> %d" k v))
|
||||||
|
(MyMapSeq.to_seq my_map');
|
||||||
|
(* sum *)
|
||||||
|
let n = 1000000 in
|
||||||
|
let sum = Sequence.fold (+) 0 (Sequence.take n (Sequence.repeat 1)) in
|
||||||
|
Format.printf "%dx1 = %d@." n sum;
|
||||||
|
assert (n=sum);
|
||||||
|
(* sexpr *)
|
||||||
|
let s = Sexpr.of_seq (Sexpr.lex (Sequence.of_str sexpr)) in
|
||||||
|
let s = Sexpr.of_seq (Sequence.map
|
||||||
|
(function | `Atom s -> `Atom (String.capitalize s) | tok -> tok)
|
||||||
|
(Sexpr.traverse s))
|
||||||
|
in
|
||||||
|
Format.printf "@[<hov2>transform @[<h>%s@] into @[<h>%a@]@]@." sexpr (Sexpr.pp_sexpr ~indent:false) s;
|
||||||
|
Format.printf "@[<hv2> cycle:%a@]@." Sexpr.pp_tokens
|
||||||
|
(Sequence.concat (Sequence.take 10 (Sequence.repeat (Sexpr.traverse s))));
|
||||||
|
(* sexpr parsing/printing *)
|
||||||
|
for i = 0 to 20 do
|
||||||
|
Format.printf "%d-th term test@." i;
|
||||||
|
test_term ();
|
||||||
|
done;
|
||||||
|
()
|
||||||
2
sequence/invert/.merlin
Normal file
2
sequence/invert/.merlin
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
REC
|
||||||
|
PKG delimcc
|
||||||
4
sequence/invert/invert.mldylib
Normal file
4
sequence/invert/invert.mldylib
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
# OASIS_START
|
||||||
|
# DO NOT EDIT (digest: d74492d261fcc87665b60e0331c04236)
|
||||||
|
SequenceInvert
|
||||||
|
# OASIS_STOP
|
||||||
4
sequence/invert/invert.mllib
Normal file
4
sequence/invert/invert.mllib
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
# OASIS_START
|
||||||
|
# DO NOT EDIT (digest: d74492d261fcc87665b60e0331c04236)
|
||||||
|
SequenceInvert
|
||||||
|
# OASIS_STOP
|
||||||
62
sequence/invert/sequenceInvert.ml
Normal file
62
sequence/invert/sequenceInvert.ml
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
(*
|
||||||
|
Copyright (c) 2014, 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 Interface to Delimcc (Invert control flow)} *)
|
||||||
|
|
||||||
|
type 'a gen = unit -> 'a option
|
||||||
|
|
||||||
|
type 'a res =
|
||||||
|
| Start
|
||||||
|
| Yield of 'a
|
||||||
|
| Stop
|
||||||
|
|
||||||
|
let _ret_none () = None
|
||||||
|
let _ret_unit () = ()
|
||||||
|
|
||||||
|
let to_gen seq =
|
||||||
|
let p = Delimcc.new_prompt () in
|
||||||
|
let _next = ref None in
|
||||||
|
ignore (Delimcc.push_prompt p
|
||||||
|
(fun () ->
|
||||||
|
Delimcc.take_subcont p (fun c () -> _next := Some c; Start);
|
||||||
|
seq
|
||||||
|
(fun x ->
|
||||||
|
Delimcc.take_subcont p (fun c () -> _next := Some c; Yield x)
|
||||||
|
);
|
||||||
|
_next := None;
|
||||||
|
Stop
|
||||||
|
));
|
||||||
|
(* call next subcont *)
|
||||||
|
let rec next () =
|
||||||
|
match !_next with
|
||||||
|
| None -> None
|
||||||
|
| Some f ->
|
||||||
|
begin match Delimcc.push_delim_subcont f _ret_unit with
|
||||||
|
| Start -> next ()
|
||||||
|
| Yield x -> Some x
|
||||||
|
| Stop -> None
|
||||||
|
end
|
||||||
|
in
|
||||||
|
next
|
||||||
32
sequence/invert/sequenceInvert.mli
Normal file
32
sequence/invert/sequenceInvert.mli
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
(*
|
||||||
|
Copyright (c) 2014, 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 Interface to Delimcc (Invert control flow)} *)
|
||||||
|
|
||||||
|
type 'a gen = unit -> 'a option
|
||||||
|
|
||||||
|
val to_gen : 'a Sequence.t -> 'a gen
|
||||||
|
(** Use delimited continuations to iterate on the sequence step by step.
|
||||||
|
Relatively costly but still useful *)
|
||||||
610
sequence/myocamlbuild.ml
Normal file
610
sequence/myocamlbuild.ml
Normal file
|
|
@ -0,0 +1,610 @@
|
||||||
|
(* OASIS_START *)
|
||||||
|
(* DO NOT EDIT (digest: c4bb6d2ca42efb069d5612eb2bbcf244) *)
|
||||||
|
module OASISGettext = struct
|
||||||
|
(* # 22 "src/oasis/OASISGettext.ml" *)
|
||||||
|
|
||||||
|
|
||||||
|
let ns_ str =
|
||||||
|
str
|
||||||
|
|
||||||
|
|
||||||
|
let s_ str =
|
||||||
|
str
|
||||||
|
|
||||||
|
|
||||||
|
let f_ (str: ('a, 'b, 'c, 'd) format4) =
|
||||||
|
str
|
||||||
|
|
||||||
|
|
||||||
|
let fn_ fmt1 fmt2 n =
|
||||||
|
if n = 1 then
|
||||||
|
fmt1^^""
|
||||||
|
else
|
||||||
|
fmt2^^""
|
||||||
|
|
||||||
|
|
||||||
|
let init =
|
||||||
|
[]
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
module OASISExpr = struct
|
||||||
|
(* # 22 "src/oasis/OASISExpr.ml" *)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
open OASISGettext
|
||||||
|
|
||||||
|
|
||||||
|
type test = string
|
||||||
|
|
||||||
|
|
||||||
|
type flag = string
|
||||||
|
|
||||||
|
|
||||||
|
type t =
|
||||||
|
| EBool of bool
|
||||||
|
| ENot of t
|
||||||
|
| EAnd of t * t
|
||||||
|
| EOr of t * t
|
||||||
|
| EFlag of flag
|
||||||
|
| ETest of test * string
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
type 'a choices = (t * 'a) list
|
||||||
|
|
||||||
|
|
||||||
|
let eval var_get t =
|
||||||
|
let rec eval' =
|
||||||
|
function
|
||||||
|
| EBool b ->
|
||||||
|
b
|
||||||
|
|
||||||
|
| ENot e ->
|
||||||
|
not (eval' e)
|
||||||
|
|
||||||
|
| EAnd (e1, e2) ->
|
||||||
|
(eval' e1) && (eval' e2)
|
||||||
|
|
||||||
|
| EOr (e1, e2) ->
|
||||||
|
(eval' e1) || (eval' e2)
|
||||||
|
|
||||||
|
| EFlag nm ->
|
||||||
|
let v =
|
||||||
|
var_get nm
|
||||||
|
in
|
||||||
|
assert(v = "true" || v = "false");
|
||||||
|
(v = "true")
|
||||||
|
|
||||||
|
| ETest (nm, vl) ->
|
||||||
|
let v =
|
||||||
|
var_get nm
|
||||||
|
in
|
||||||
|
(v = vl)
|
||||||
|
in
|
||||||
|
eval' t
|
||||||
|
|
||||||
|
|
||||||
|
let choose ?printer ?name var_get lst =
|
||||||
|
let rec choose_aux =
|
||||||
|
function
|
||||||
|
| (cond, vl) :: tl ->
|
||||||
|
if eval var_get cond then
|
||||||
|
vl
|
||||||
|
else
|
||||||
|
choose_aux tl
|
||||||
|
| [] ->
|
||||||
|
let str_lst =
|
||||||
|
if lst = [] then
|
||||||
|
s_ "<empty>"
|
||||||
|
else
|
||||||
|
String.concat
|
||||||
|
(s_ ", ")
|
||||||
|
(List.map
|
||||||
|
(fun (cond, vl) ->
|
||||||
|
match printer with
|
||||||
|
| Some p -> p vl
|
||||||
|
| None -> s_ "<no printer>")
|
||||||
|
lst)
|
||||||
|
in
|
||||||
|
match name with
|
||||||
|
| Some nm ->
|
||||||
|
failwith
|
||||||
|
(Printf.sprintf
|
||||||
|
(f_ "No result for the choice list '%s': %s")
|
||||||
|
nm str_lst)
|
||||||
|
| None ->
|
||||||
|
failwith
|
||||||
|
(Printf.sprintf
|
||||||
|
(f_ "No result for a choice list: %s")
|
||||||
|
str_lst)
|
||||||
|
in
|
||||||
|
choose_aux (List.rev lst)
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# 132 "myocamlbuild.ml"
|
||||||
|
module BaseEnvLight = struct
|
||||||
|
(* # 22 "src/base/BaseEnvLight.ml" *)
|
||||||
|
|
||||||
|
|
||||||
|
module MapString = Map.Make(String)
|
||||||
|
|
||||||
|
|
||||||
|
type t = string MapString.t
|
||||||
|
|
||||||
|
|
||||||
|
let default_filename =
|
||||||
|
Filename.concat
|
||||||
|
(Sys.getcwd ())
|
||||||
|
"setup.data"
|
||||||
|
|
||||||
|
|
||||||
|
let load ?(allow_empty=false) ?(filename=default_filename) () =
|
||||||
|
if Sys.file_exists filename then
|
||||||
|
begin
|
||||||
|
let chn =
|
||||||
|
open_in_bin filename
|
||||||
|
in
|
||||||
|
let st =
|
||||||
|
Stream.of_channel chn
|
||||||
|
in
|
||||||
|
let line =
|
||||||
|
ref 1
|
||||||
|
in
|
||||||
|
let st_line =
|
||||||
|
Stream.from
|
||||||
|
(fun _ ->
|
||||||
|
try
|
||||||
|
match Stream.next st with
|
||||||
|
| '\n' -> incr line; Some '\n'
|
||||||
|
| c -> Some c
|
||||||
|
with Stream.Failure -> None)
|
||||||
|
in
|
||||||
|
let lexer =
|
||||||
|
Genlex.make_lexer ["="] st_line
|
||||||
|
in
|
||||||
|
let rec read_file mp =
|
||||||
|
match Stream.npeek 3 lexer with
|
||||||
|
| [Genlex.Ident nm; Genlex.Kwd "="; Genlex.String value] ->
|
||||||
|
Stream.junk lexer;
|
||||||
|
Stream.junk lexer;
|
||||||
|
Stream.junk lexer;
|
||||||
|
read_file (MapString.add nm value mp)
|
||||||
|
| [] ->
|
||||||
|
mp
|
||||||
|
| _ ->
|
||||||
|
failwith
|
||||||
|
(Printf.sprintf
|
||||||
|
"Malformed data file '%s' line %d"
|
||||||
|
filename !line)
|
||||||
|
in
|
||||||
|
let mp =
|
||||||
|
read_file MapString.empty
|
||||||
|
in
|
||||||
|
close_in chn;
|
||||||
|
mp
|
||||||
|
end
|
||||||
|
else if allow_empty then
|
||||||
|
begin
|
||||||
|
MapString.empty
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
failwith
|
||||||
|
(Printf.sprintf
|
||||||
|
"Unable to load environment, the file '%s' doesn't exist."
|
||||||
|
filename)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
let rec var_expand str env =
|
||||||
|
let buff =
|
||||||
|
Buffer.create ((String.length str) * 2)
|
||||||
|
in
|
||||||
|
Buffer.add_substitute
|
||||||
|
buff
|
||||||
|
(fun var ->
|
||||||
|
try
|
||||||
|
var_expand (MapString.find var env) env
|
||||||
|
with Not_found ->
|
||||||
|
failwith
|
||||||
|
(Printf.sprintf
|
||||||
|
"No variable %s defined when trying to expand %S."
|
||||||
|
var
|
||||||
|
str))
|
||||||
|
str;
|
||||||
|
Buffer.contents buff
|
||||||
|
|
||||||
|
|
||||||
|
let var_get name env =
|
||||||
|
var_expand (MapString.find name env) env
|
||||||
|
|
||||||
|
|
||||||
|
let var_choose lst env =
|
||||||
|
OASISExpr.choose
|
||||||
|
(fun nm -> var_get nm env)
|
||||||
|
lst
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# 237 "myocamlbuild.ml"
|
||||||
|
module MyOCamlbuildFindlib = struct
|
||||||
|
(* # 22 "src/plugins/ocamlbuild/MyOCamlbuildFindlib.ml" *)
|
||||||
|
|
||||||
|
|
||||||
|
(** OCamlbuild extension, copied from
|
||||||
|
* http://brion.inria.fr/gallium/index.php/Using_ocamlfind_with_ocamlbuild
|
||||||
|
* by N. Pouillard and others
|
||||||
|
*
|
||||||
|
* Updated on 2009/02/28
|
||||||
|
*
|
||||||
|
* Modified by Sylvain Le Gall
|
||||||
|
*)
|
||||||
|
open Ocamlbuild_plugin
|
||||||
|
|
||||||
|
|
||||||
|
(* these functions are not really officially exported *)
|
||||||
|
let run_and_read =
|
||||||
|
Ocamlbuild_pack.My_unix.run_and_read
|
||||||
|
|
||||||
|
|
||||||
|
let blank_sep_strings =
|
||||||
|
Ocamlbuild_pack.Lexers.blank_sep_strings
|
||||||
|
|
||||||
|
|
||||||
|
let exec_from_conf exec =
|
||||||
|
let exec =
|
||||||
|
let env_filename = Pathname.basename BaseEnvLight.default_filename in
|
||||||
|
let env = BaseEnvLight.load ~filename:env_filename ~allow_empty:true () in
|
||||||
|
try
|
||||||
|
BaseEnvLight.var_get exec env
|
||||||
|
with Not_found ->
|
||||||
|
Printf.eprintf "W: Cannot get variable %s\n" exec;
|
||||||
|
exec
|
||||||
|
in
|
||||||
|
let fix_win32 str =
|
||||||
|
if Sys.os_type = "Win32" then begin
|
||||||
|
let buff = Buffer.create (String.length str) in
|
||||||
|
(* Adapt for windowsi, ocamlbuild + win32 has a hard time to handle '\\'.
|
||||||
|
*)
|
||||||
|
String.iter
|
||||||
|
(fun c -> Buffer.add_char buff (if c = '\\' then '/' else c))
|
||||||
|
str;
|
||||||
|
Buffer.contents buff
|
||||||
|
end else begin
|
||||||
|
str
|
||||||
|
end
|
||||||
|
in
|
||||||
|
fix_win32 exec
|
||||||
|
|
||||||
|
let split s ch =
|
||||||
|
let buf = Buffer.create 13 in
|
||||||
|
let x = ref [] in
|
||||||
|
let flush () =
|
||||||
|
x := (Buffer.contents buf) :: !x;
|
||||||
|
Buffer.clear buf
|
||||||
|
in
|
||||||
|
String.iter
|
||||||
|
(fun c ->
|
||||||
|
if c = ch then
|
||||||
|
flush ()
|
||||||
|
else
|
||||||
|
Buffer.add_char buf c)
|
||||||
|
s;
|
||||||
|
flush ();
|
||||||
|
List.rev !x
|
||||||
|
|
||||||
|
|
||||||
|
let split_nl s = split s '\n'
|
||||||
|
|
||||||
|
|
||||||
|
let before_space s =
|
||||||
|
try
|
||||||
|
String.before s (String.index s ' ')
|
||||||
|
with Not_found -> s
|
||||||
|
|
||||||
|
(* ocamlfind command *)
|
||||||
|
let ocamlfind x = S[Sh (exec_from_conf "ocamlfind"); x]
|
||||||
|
|
||||||
|
(* This lists all supported packages. *)
|
||||||
|
let find_packages () =
|
||||||
|
List.map before_space (split_nl & run_and_read "ocamlfind list")
|
||||||
|
|
||||||
|
|
||||||
|
(* Mock to list available syntaxes. *)
|
||||||
|
let find_syntaxes () = ["camlp4o"; "camlp4r"]
|
||||||
|
|
||||||
|
|
||||||
|
let well_known_syntax = [
|
||||||
|
"camlp4.quotations.o";
|
||||||
|
"camlp4.quotations.r";
|
||||||
|
"camlp4.exceptiontracer";
|
||||||
|
"camlp4.extend";
|
||||||
|
"camlp4.foldgenerator";
|
||||||
|
"camlp4.listcomprehension";
|
||||||
|
"camlp4.locationstripper";
|
||||||
|
"camlp4.macro";
|
||||||
|
"camlp4.mapgenerator";
|
||||||
|
"camlp4.metagenerator";
|
||||||
|
"camlp4.profiler";
|
||||||
|
"camlp4.tracer"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
let dispatch =
|
||||||
|
function
|
||||||
|
| After_options ->
|
||||||
|
(* By using Before_options one let command line options have an higher
|
||||||
|
* priority on the contrary using After_options will guarantee to have
|
||||||
|
* the higher priority override default commands by ocamlfind ones *)
|
||||||
|
Options.ocamlc := ocamlfind & A"ocamlc";
|
||||||
|
Options.ocamlopt := ocamlfind & A"ocamlopt";
|
||||||
|
Options.ocamldep := ocamlfind & A"ocamldep";
|
||||||
|
Options.ocamldoc := ocamlfind & A"ocamldoc";
|
||||||
|
Options.ocamlmktop := ocamlfind & A"ocamlmktop";
|
||||||
|
Options.ocamlmklib := ocamlfind & A"ocamlmklib"
|
||||||
|
|
||||||
|
| After_rules ->
|
||||||
|
|
||||||
|
(* When one link an OCaml library/binary/package, one should use
|
||||||
|
* -linkpkg *)
|
||||||
|
flag ["ocaml"; "link"; "program"] & A"-linkpkg";
|
||||||
|
|
||||||
|
(* For each ocamlfind package one inject the -package option when
|
||||||
|
* compiling, computing dependencies, generating documentation and
|
||||||
|
* linking. *)
|
||||||
|
List.iter
|
||||||
|
begin fun pkg ->
|
||||||
|
let base_args = [A"-package"; A pkg] in
|
||||||
|
(* TODO: consider how to really choose camlp4o or camlp4r. *)
|
||||||
|
let syn_args = [A"-syntax"; A "camlp4o"] in
|
||||||
|
let args =
|
||||||
|
(* Heuristic to identify syntax extensions: whether they end in
|
||||||
|
".syntax"; some might not.
|
||||||
|
*)
|
||||||
|
if Filename.check_suffix pkg "syntax" ||
|
||||||
|
List.mem pkg well_known_syntax then
|
||||||
|
syn_args @ base_args
|
||||||
|
else
|
||||||
|
base_args
|
||||||
|
in
|
||||||
|
flag ["ocaml"; "compile"; "pkg_"^pkg] & S args;
|
||||||
|
flag ["ocaml"; "ocamldep"; "pkg_"^pkg] & S args;
|
||||||
|
flag ["ocaml"; "doc"; "pkg_"^pkg] & S args;
|
||||||
|
flag ["ocaml"; "link"; "pkg_"^pkg] & S base_args;
|
||||||
|
flag ["ocaml"; "infer_interface"; "pkg_"^pkg] & S args;
|
||||||
|
end
|
||||||
|
(find_packages ());
|
||||||
|
|
||||||
|
(* Like -package but for extensions syntax. Morover -syntax is useless
|
||||||
|
* when linking. *)
|
||||||
|
List.iter begin fun syntax ->
|
||||||
|
flag ["ocaml"; "compile"; "syntax_"^syntax] & S[A"-syntax"; A syntax];
|
||||||
|
flag ["ocaml"; "ocamldep"; "syntax_"^syntax] & S[A"-syntax"; A syntax];
|
||||||
|
flag ["ocaml"; "doc"; "syntax_"^syntax] & S[A"-syntax"; A syntax];
|
||||||
|
flag ["ocaml"; "infer_interface"; "syntax_"^syntax] &
|
||||||
|
S[A"-syntax"; A syntax];
|
||||||
|
end (find_syntaxes ());
|
||||||
|
|
||||||
|
(* The default "thread" tag is not compatible with ocamlfind.
|
||||||
|
* Indeed, the default rules add the "threads.cma" or "threads.cmxa"
|
||||||
|
* options when using this tag. When using the "-linkpkg" option with
|
||||||
|
* ocamlfind, this module will then be added twice on the command line.
|
||||||
|
*
|
||||||
|
* To solve this, one approach is to add the "-thread" option when using
|
||||||
|
* the "threads" package using the previous plugin.
|
||||||
|
*)
|
||||||
|
flag ["ocaml"; "pkg_threads"; "compile"] (S[A "-thread"]);
|
||||||
|
flag ["ocaml"; "pkg_threads"; "doc"] (S[A "-I"; A "+threads"]);
|
||||||
|
flag ["ocaml"; "pkg_threads"; "link"] (S[A "-thread"]);
|
||||||
|
flag ["ocaml"; "pkg_threads"; "infer_interface"] (S[A "-thread"]);
|
||||||
|
flag ["ocaml"; "package(threads)"; "compile"] (S[A "-thread"]);
|
||||||
|
flag ["ocaml"; "package(threads)"; "doc"] (S[A "-I"; A "+threads"]);
|
||||||
|
flag ["ocaml"; "package(threads)"; "link"] (S[A "-thread"]);
|
||||||
|
flag ["ocaml"; "package(threads)"; "infer_interface"] (S[A "-thread"]);
|
||||||
|
|
||||||
|
| _ ->
|
||||||
|
()
|
||||||
|
end
|
||||||
|
|
||||||
|
module MyOCamlbuildBase = struct
|
||||||
|
(* # 22 "src/plugins/ocamlbuild/MyOCamlbuildBase.ml" *)
|
||||||
|
|
||||||
|
|
||||||
|
(** Base functions for writing myocamlbuild.ml
|
||||||
|
@author Sylvain Le Gall
|
||||||
|
*)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
open Ocamlbuild_plugin
|
||||||
|
module OC = Ocamlbuild_pack.Ocaml_compiler
|
||||||
|
|
||||||
|
|
||||||
|
type dir = string
|
||||||
|
type file = string
|
||||||
|
type name = string
|
||||||
|
type tag = string
|
||||||
|
|
||||||
|
|
||||||
|
(* # 62 "src/plugins/ocamlbuild/MyOCamlbuildBase.ml" *)
|
||||||
|
|
||||||
|
|
||||||
|
type t =
|
||||||
|
{
|
||||||
|
lib_ocaml: (name * dir list * string list) list;
|
||||||
|
lib_c: (name * dir * file list) list;
|
||||||
|
flags: (tag list * (spec OASISExpr.choices)) list;
|
||||||
|
(* Replace the 'dir: include' from _tags by a precise interdepends in
|
||||||
|
* directory.
|
||||||
|
*)
|
||||||
|
includes: (dir * dir list) list;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let env_filename =
|
||||||
|
Pathname.basename
|
||||||
|
BaseEnvLight.default_filename
|
||||||
|
|
||||||
|
|
||||||
|
let dispatch_combine lst =
|
||||||
|
fun e ->
|
||||||
|
List.iter
|
||||||
|
(fun dispatch -> dispatch e)
|
||||||
|
lst
|
||||||
|
|
||||||
|
|
||||||
|
let tag_libstubs nm =
|
||||||
|
"use_lib"^nm^"_stubs"
|
||||||
|
|
||||||
|
|
||||||
|
let nm_libstubs nm =
|
||||||
|
nm^"_stubs"
|
||||||
|
|
||||||
|
|
||||||
|
let dispatch t e =
|
||||||
|
let env =
|
||||||
|
BaseEnvLight.load
|
||||||
|
~filename:env_filename
|
||||||
|
~allow_empty:true
|
||||||
|
()
|
||||||
|
in
|
||||||
|
match e with
|
||||||
|
| Before_options ->
|
||||||
|
let no_trailing_dot s =
|
||||||
|
if String.length s >= 1 && s.[0] = '.' then
|
||||||
|
String.sub s 1 ((String.length s) - 1)
|
||||||
|
else
|
||||||
|
s
|
||||||
|
in
|
||||||
|
List.iter
|
||||||
|
(fun (opt, var) ->
|
||||||
|
try
|
||||||
|
opt := no_trailing_dot (BaseEnvLight.var_get var env)
|
||||||
|
with Not_found ->
|
||||||
|
Printf.eprintf "W: Cannot get variable %s\n" var)
|
||||||
|
[
|
||||||
|
Options.ext_obj, "ext_obj";
|
||||||
|
Options.ext_lib, "ext_lib";
|
||||||
|
Options.ext_dll, "ext_dll";
|
||||||
|
]
|
||||||
|
|
||||||
|
| After_rules ->
|
||||||
|
(* Declare OCaml libraries *)
|
||||||
|
List.iter
|
||||||
|
(function
|
||||||
|
| nm, [], intf_modules ->
|
||||||
|
ocaml_lib nm;
|
||||||
|
let cmis =
|
||||||
|
List.map (fun m -> (String.uncapitalize m) ^ ".cmi")
|
||||||
|
intf_modules in
|
||||||
|
dep ["ocaml"; "link"; "library"; "file:"^nm^".cma"] cmis
|
||||||
|
| nm, dir :: tl, intf_modules ->
|
||||||
|
ocaml_lib ~dir:dir (dir^"/"^nm);
|
||||||
|
List.iter
|
||||||
|
(fun dir ->
|
||||||
|
List.iter
|
||||||
|
(fun str ->
|
||||||
|
flag ["ocaml"; "use_"^nm; str] (S[A"-I"; P dir]))
|
||||||
|
["compile"; "infer_interface"; "doc"])
|
||||||
|
tl;
|
||||||
|
let cmis =
|
||||||
|
List.map (fun m -> dir^"/"^(String.uncapitalize m)^".cmi")
|
||||||
|
intf_modules in
|
||||||
|
dep ["ocaml"; "link"; "library"; "file:"^dir^"/"^nm^".cma"]
|
||||||
|
cmis)
|
||||||
|
t.lib_ocaml;
|
||||||
|
|
||||||
|
(* Declare directories dependencies, replace "include" in _tags. *)
|
||||||
|
List.iter
|
||||||
|
(fun (dir, include_dirs) ->
|
||||||
|
Pathname.define_context dir include_dirs)
|
||||||
|
t.includes;
|
||||||
|
|
||||||
|
(* Declare C libraries *)
|
||||||
|
List.iter
|
||||||
|
(fun (lib, dir, headers) ->
|
||||||
|
(* Handle C part of library *)
|
||||||
|
flag ["link"; "library"; "ocaml"; "byte"; tag_libstubs lib]
|
||||||
|
(S[A"-dllib"; A("-l"^(nm_libstubs lib)); A"-cclib";
|
||||||
|
A("-l"^(nm_libstubs lib))]);
|
||||||
|
|
||||||
|
flag ["link"; "library"; "ocaml"; "native"; tag_libstubs lib]
|
||||||
|
(S[A"-cclib"; A("-l"^(nm_libstubs lib))]);
|
||||||
|
|
||||||
|
flag ["link"; "program"; "ocaml"; "byte"; tag_libstubs lib]
|
||||||
|
(S[A"-dllib"; A("dll"^(nm_libstubs lib))]);
|
||||||
|
|
||||||
|
(* When ocaml link something that use the C library, then one
|
||||||
|
need that file to be up to date.
|
||||||
|
*)
|
||||||
|
dep ["link"; "ocaml"; "program"; tag_libstubs lib]
|
||||||
|
[dir/"lib"^(nm_libstubs lib)^"."^(!Options.ext_lib)];
|
||||||
|
|
||||||
|
dep ["compile"; "ocaml"; "program"; tag_libstubs lib]
|
||||||
|
[dir/"lib"^(nm_libstubs lib)^"."^(!Options.ext_lib)];
|
||||||
|
|
||||||
|
(* TODO: be more specific about what depends on headers *)
|
||||||
|
(* Depends on .h files *)
|
||||||
|
dep ["compile"; "c"]
|
||||||
|
headers;
|
||||||
|
|
||||||
|
(* Setup search path for lib *)
|
||||||
|
flag ["link"; "ocaml"; "use_"^lib]
|
||||||
|
(S[A"-I"; P(dir)]);
|
||||||
|
)
|
||||||
|
t.lib_c;
|
||||||
|
|
||||||
|
(* Add flags *)
|
||||||
|
List.iter
|
||||||
|
(fun (tags, cond_specs) ->
|
||||||
|
let spec = BaseEnvLight.var_choose cond_specs env in
|
||||||
|
let rec eval_specs =
|
||||||
|
function
|
||||||
|
| S lst -> S (List.map eval_specs lst)
|
||||||
|
| A str -> A (BaseEnvLight.var_expand str env)
|
||||||
|
| spec -> spec
|
||||||
|
in
|
||||||
|
flag tags & (eval_specs spec))
|
||||||
|
t.flags
|
||||||
|
| _ ->
|
||||||
|
()
|
||||||
|
|
||||||
|
|
||||||
|
let dispatch_default t =
|
||||||
|
dispatch_combine
|
||||||
|
[
|
||||||
|
dispatch t;
|
||||||
|
MyOCamlbuildFindlib.dispatch;
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# 594 "myocamlbuild.ml"
|
||||||
|
open Ocamlbuild_plugin;;
|
||||||
|
let package_default =
|
||||||
|
{
|
||||||
|
MyOCamlbuildBase.lib_ocaml =
|
||||||
|
[("sequence", [], []); ("invert", ["invert"], [])];
|
||||||
|
lib_c = [];
|
||||||
|
flags = [];
|
||||||
|
includes = []
|
||||||
|
}
|
||||||
|
;;
|
||||||
|
|
||||||
|
let dispatch_default = MyOCamlbuildBase.dispatch_default package_default;;
|
||||||
|
|
||||||
|
# 609 "myocamlbuild.ml"
|
||||||
|
(* OASIS_STOP *)
|
||||||
|
Ocamlbuild_plugin.dispatch dispatch_default;;
|
||||||
771
sequence/sequence.ml
Normal file
771
sequence/sequence.ml
Normal file
|
|
@ -0,0 +1,771 @@
|
||||||
|
(*
|
||||||
|
Copyright (c) 2013, 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 Transient iterators, that abstract on a finite sequence of elements.} *)
|
||||||
|
|
||||||
|
(** Sequence abstract iterator type *)
|
||||||
|
type 'a t = ('a -> unit) -> unit
|
||||||
|
|
||||||
|
type 'a sequence = 'a t
|
||||||
|
|
||||||
|
type (+'a, +'b) t2 = ('a -> 'b -> unit) -> unit
|
||||||
|
(** Sequence of pairs of values of type ['a] and ['b]. *)
|
||||||
|
|
||||||
|
(** Build a sequence from a iter function *)
|
||||||
|
let from_iter f = f
|
||||||
|
|
||||||
|
let rec from_fun f k = match f () with
|
||||||
|
| None -> ()
|
||||||
|
| Some x -> k x; from_fun f k
|
||||||
|
|
||||||
|
let empty k = ()
|
||||||
|
|
||||||
|
let singleton x k = k x
|
||||||
|
let return x k = k x
|
||||||
|
let pure f k = k f
|
||||||
|
|
||||||
|
let doubleton x y k = k x; k y
|
||||||
|
|
||||||
|
let cons x l k = k x; l k
|
||||||
|
let snoc l x k = l k; k x
|
||||||
|
|
||||||
|
let repeat x k = while true do k x done
|
||||||
|
|
||||||
|
let rec iterate f x k =
|
||||||
|
k x;
|
||||||
|
iterate f (f x) k
|
||||||
|
|
||||||
|
let rec forever f k =
|
||||||
|
k (f ());
|
||||||
|
forever f k
|
||||||
|
|
||||||
|
let cycle s k = while true do s k; done
|
||||||
|
|
||||||
|
let iter f seq = seq f
|
||||||
|
|
||||||
|
let iteri f seq =
|
||||||
|
let r = ref 0 in
|
||||||
|
seq
|
||||||
|
(fun x ->
|
||||||
|
f !r x;
|
||||||
|
incr r)
|
||||||
|
|
||||||
|
let fold f init seq =
|
||||||
|
let r = ref init in
|
||||||
|
seq (fun elt -> r := f !r elt);
|
||||||
|
!r
|
||||||
|
|
||||||
|
let foldi f init seq =
|
||||||
|
let i = ref 0 in
|
||||||
|
let r = ref init in
|
||||||
|
seq
|
||||||
|
(fun elt ->
|
||||||
|
r := f !r !i elt;
|
||||||
|
incr i);
|
||||||
|
!r
|
||||||
|
|
||||||
|
let map f seq k = seq (fun x -> k (f x))
|
||||||
|
|
||||||
|
let mapi f seq k =
|
||||||
|
let i = ref 0 in
|
||||||
|
seq (fun x -> k (f !i x); incr i)
|
||||||
|
|
||||||
|
let filter p seq k = seq (fun x -> if p x then k x)
|
||||||
|
|
||||||
|
let append s1 s2 k = s1 k; s2 k
|
||||||
|
|
||||||
|
let concat s k = s (fun s' -> s' k)
|
||||||
|
|
||||||
|
let flatten s = concat s
|
||||||
|
|
||||||
|
let flatMap f seq k = seq (fun x -> f x k)
|
||||||
|
|
||||||
|
let flat_map = flatMap
|
||||||
|
|
||||||
|
let fmap f seq k =
|
||||||
|
seq (fun x -> match f x with
|
||||||
|
| None -> ()
|
||||||
|
| Some y -> k y
|
||||||
|
)
|
||||||
|
|
||||||
|
let filter_map = fmap
|
||||||
|
|
||||||
|
let intersperse elem seq k =
|
||||||
|
let first = ref true in
|
||||||
|
seq (fun x -> (if !first then first := false else k elem); k x)
|
||||||
|
|
||||||
|
(** Mutable unrolled list to serve as intermediate storage *)
|
||||||
|
module MList = struct
|
||||||
|
type 'a node =
|
||||||
|
| Nil
|
||||||
|
| Cons of 'a array * int ref * 'a node ref
|
||||||
|
|
||||||
|
(* build and call callback on every element *)
|
||||||
|
let of_seq_with seq k =
|
||||||
|
let start = ref Nil in
|
||||||
|
let chunk_size = ref 8 in
|
||||||
|
(* fill the list. prev: tail-reference from previous node *)
|
||||||
|
let prev, cur = ref start, ref Nil in
|
||||||
|
seq
|
||||||
|
(fun x ->
|
||||||
|
k x; (* callback *)
|
||||||
|
match !cur with
|
||||||
|
| Nil ->
|
||||||
|
let n = !chunk_size in
|
||||||
|
if n < 4096 then chunk_size := 2 * !chunk_size;
|
||||||
|
cur := Cons (Array.make n x, ref 1, ref Nil)
|
||||||
|
| Cons (a,n,next) ->
|
||||||
|
assert (!n < Array.length a);
|
||||||
|
a.(!n) <- x;
|
||||||
|
incr n;
|
||||||
|
if !n = Array.length a then begin
|
||||||
|
!prev := !cur;
|
||||||
|
prev := next;
|
||||||
|
cur := Nil
|
||||||
|
end
|
||||||
|
);
|
||||||
|
!prev := !cur;
|
||||||
|
!start
|
||||||
|
|
||||||
|
let of_seq seq =
|
||||||
|
of_seq_with seq (fun _ -> ())
|
||||||
|
|
||||||
|
let is_empty = function
|
||||||
|
| Nil -> true
|
||||||
|
| Cons _ -> false
|
||||||
|
|
||||||
|
let rec iter f l = match l with
|
||||||
|
| Nil -> ()
|
||||||
|
| Cons (a, n, tl) ->
|
||||||
|
for i=0 to !n - 1 do f a.(i) done;
|
||||||
|
iter f !tl
|
||||||
|
|
||||||
|
let iteri f l =
|
||||||
|
let rec iteri i f l = match l with
|
||||||
|
| Nil -> ()
|
||||||
|
| Cons (a, n, tl) ->
|
||||||
|
for j=0 to !n - 1 do f (i+j) a.(j) done;
|
||||||
|
iteri (i+ !n) f !tl
|
||||||
|
in iteri 0 f l
|
||||||
|
|
||||||
|
let rec iter_rev f l = match l with
|
||||||
|
| Nil -> ()
|
||||||
|
| Cons (a, n, tl) ->
|
||||||
|
iter_rev f !tl;
|
||||||
|
for i = !n-1 downto 0 do f a.(i) done
|
||||||
|
|
||||||
|
let length l =
|
||||||
|
let rec len acc l = match l with
|
||||||
|
| Nil -> acc
|
||||||
|
| Cons (_, n, tl) -> len (acc+ !n) !tl
|
||||||
|
in len 0 l
|
||||||
|
|
||||||
|
(** Get element by index *)
|
||||||
|
let rec get l i = match l with
|
||||||
|
| Nil -> raise (Invalid_argument "MList.get")
|
||||||
|
| Cons (a, n, _) when i < !n -> a.(i)
|
||||||
|
| Cons (_, n, tl) -> get !tl (i- !n)
|
||||||
|
|
||||||
|
let to_seq l k = iter k l
|
||||||
|
|
||||||
|
let _to_next arg l =
|
||||||
|
let cur = ref l in
|
||||||
|
let i = ref 0 in (* offset in cons *)
|
||||||
|
let rec get_next _ = match !cur with
|
||||||
|
| Nil -> None
|
||||||
|
| Cons (_, n, tl) when !i = !n ->
|
||||||
|
cur := !tl;
|
||||||
|
i := 0;
|
||||||
|
get_next arg
|
||||||
|
| Cons (a, n, _) ->
|
||||||
|
let x = a.(!i) in
|
||||||
|
incr i;
|
||||||
|
Some x
|
||||||
|
in get_next
|
||||||
|
|
||||||
|
let to_gen l = _to_next () l
|
||||||
|
|
||||||
|
let to_stream l =
|
||||||
|
Stream.from (_to_next 42 l) (* 42=magic cookiiiiiie *)
|
||||||
|
|
||||||
|
let to_klist l =
|
||||||
|
let rec make (l,i) () = match l with
|
||||||
|
| Nil -> `Nil
|
||||||
|
| Cons (_, n, tl) when i = !n -> make (!tl,0) ()
|
||||||
|
| Cons (a, n, _) -> `Cons (a.(i), make (l,i+1))
|
||||||
|
in make (l,0)
|
||||||
|
end
|
||||||
|
|
||||||
|
let persistent seq =
|
||||||
|
let l = MList.of_seq seq in
|
||||||
|
MList.to_seq l
|
||||||
|
|
||||||
|
type 'a lazy_state =
|
||||||
|
| LazySuspend
|
||||||
|
| LazyCached of 'a t
|
||||||
|
|
||||||
|
let persistent_lazy (seq:'a t) =
|
||||||
|
let r = ref LazySuspend in
|
||||||
|
fun k ->
|
||||||
|
match !r with
|
||||||
|
| LazyCached seq' -> seq' k
|
||||||
|
| LazySuspend ->
|
||||||
|
(* here if this traversal is interruted, no caching occurs *)
|
||||||
|
let seq' = MList.of_seq_with seq k in
|
||||||
|
r := LazyCached (MList.to_seq seq')
|
||||||
|
|
||||||
|
let sort ?(cmp=Pervasives.compare) seq =
|
||||||
|
(* use an intermediate list, then sort the list *)
|
||||||
|
let l = fold (fun l x -> x::l) [] seq in
|
||||||
|
let l = List.fast_sort cmp l in
|
||||||
|
fun k -> List.iter k l
|
||||||
|
|
||||||
|
let group ?(eq=fun x y -> x = y) seq k =
|
||||||
|
let cur = ref [] in
|
||||||
|
seq (fun x ->
|
||||||
|
match !cur with
|
||||||
|
| [] -> cur := [x]
|
||||||
|
| (y::_) as l when eq x y ->
|
||||||
|
cur := x::l (* [x] belongs to the group *)
|
||||||
|
| (_::_) as l ->
|
||||||
|
k l; (* yield group, and start another one *)
|
||||||
|
cur := [x]);
|
||||||
|
(* last list *)
|
||||||
|
if !cur <> [] then k !cur
|
||||||
|
|
||||||
|
let uniq ?(eq=fun x y -> x = y) seq k =
|
||||||
|
let has_prev = ref false
|
||||||
|
and prev = ref (Obj.magic 0) in (* avoid option type, costly *)
|
||||||
|
seq (fun x ->
|
||||||
|
if !has_prev && eq !prev x
|
||||||
|
then () (* duplicate *)
|
||||||
|
else begin
|
||||||
|
has_prev := true;
|
||||||
|
prev := x;
|
||||||
|
k x
|
||||||
|
end)
|
||||||
|
|
||||||
|
let sort_uniq (type elt) ?(cmp=Pervasives.compare) seq =
|
||||||
|
let module S = Set.Make(struct
|
||||||
|
type t = elt
|
||||||
|
let compare = cmp
|
||||||
|
end) in
|
||||||
|
let set = fold (fun acc x -> S.add x acc) S.empty seq in
|
||||||
|
fun k -> S.iter k set
|
||||||
|
|
||||||
|
let product outer inner k =
|
||||||
|
outer (fun x ->
|
||||||
|
inner (fun y -> k (x,y))
|
||||||
|
)
|
||||||
|
|
||||||
|
let product2 outer inner k =
|
||||||
|
outer (fun x ->
|
||||||
|
inner (fun y -> k x y)
|
||||||
|
)
|
||||||
|
|
||||||
|
let join ~join_row s1 s2 k =
|
||||||
|
s1 (fun a ->
|
||||||
|
s2 (fun b ->
|
||||||
|
match join_row a b with
|
||||||
|
| None -> ()
|
||||||
|
| Some c -> k c
|
||||||
|
)
|
||||||
|
) (* yield the combination of [a] and [b] *)
|
||||||
|
|
||||||
|
let rec unfoldr f b k = match f b with
|
||||||
|
| None -> ()
|
||||||
|
| Some (x, b') ->
|
||||||
|
k x;
|
||||||
|
unfoldr f b' k
|
||||||
|
|
||||||
|
let scan f acc seq k =
|
||||||
|
k acc;
|
||||||
|
let acc = ref acc in
|
||||||
|
seq (fun elt -> let acc' = f !acc elt in k acc'; acc := acc')
|
||||||
|
|
||||||
|
let max ?(lt=fun x y -> x < y) seq =
|
||||||
|
let ret = ref None in
|
||||||
|
seq (fun x -> match !ret with
|
||||||
|
| None -> ret := Some x
|
||||||
|
| Some y -> if lt y x then ret := Some x);
|
||||||
|
!ret
|
||||||
|
|
||||||
|
let min ?(lt=fun x y -> x < y) seq =
|
||||||
|
let ret = ref None in
|
||||||
|
seq (fun x -> match !ret with
|
||||||
|
| None -> ret := Some x
|
||||||
|
| Some y -> if lt x y then ret := Some x);
|
||||||
|
!ret
|
||||||
|
|
||||||
|
exception ExitSequence
|
||||||
|
|
||||||
|
let head seq =
|
||||||
|
let r = ref None in
|
||||||
|
try
|
||||||
|
seq (fun x -> r := Some x; raise ExitSequence); None
|
||||||
|
with ExitSequence -> !r
|
||||||
|
|
||||||
|
let head_exn seq =
|
||||||
|
match head seq with
|
||||||
|
| None -> invalid_arg "Sequence.head_exn"
|
||||||
|
| Some x -> x
|
||||||
|
|
||||||
|
let take n seq k =
|
||||||
|
let count = ref 0 in
|
||||||
|
try
|
||||||
|
seq (fun x ->
|
||||||
|
incr count;
|
||||||
|
k x;
|
||||||
|
if !count = n then raise ExitSequence
|
||||||
|
)
|
||||||
|
with ExitSequence -> ()
|
||||||
|
|
||||||
|
let take_while p seq k =
|
||||||
|
try
|
||||||
|
seq (fun x -> if p x then k x else raise ExitSequence)
|
||||||
|
with ExitSequence -> ()
|
||||||
|
|
||||||
|
let drop n seq k =
|
||||||
|
let count = ref 0 in
|
||||||
|
seq (fun x -> if !count >= n then k x else incr count)
|
||||||
|
|
||||||
|
let drop_while p seq k =
|
||||||
|
let drop = ref true in
|
||||||
|
seq (fun x ->
|
||||||
|
if !drop
|
||||||
|
then if p x then () else (drop := false; k x)
|
||||||
|
else k x)
|
||||||
|
|
||||||
|
let rev seq =
|
||||||
|
let l = MList.of_seq seq in
|
||||||
|
fun k -> MList.iter_rev k l
|
||||||
|
|
||||||
|
let for_all p seq =
|
||||||
|
try
|
||||||
|
seq (fun x -> if not (p x) then raise ExitSequence);
|
||||||
|
true
|
||||||
|
with ExitSequence -> false
|
||||||
|
|
||||||
|
(** Exists there some element satisfying the predicate? *)
|
||||||
|
let exists p seq =
|
||||||
|
try
|
||||||
|
seq (fun x -> if p x then raise ExitSequence);
|
||||||
|
false
|
||||||
|
with ExitSequence -> true
|
||||||
|
|
||||||
|
let mem ?(eq=(=)) x seq = exists (eq x) seq
|
||||||
|
|
||||||
|
let find f seq =
|
||||||
|
let r = ref None in
|
||||||
|
begin try
|
||||||
|
seq (fun x -> match f x with
|
||||||
|
| None -> ()
|
||||||
|
| Some _ as res -> r := res
|
||||||
|
);
|
||||||
|
with ExitSequence -> ()
|
||||||
|
end;
|
||||||
|
!r
|
||||||
|
|
||||||
|
let length seq =
|
||||||
|
let r = ref 0 in
|
||||||
|
seq (fun _ -> incr r);
|
||||||
|
!r
|
||||||
|
|
||||||
|
let is_empty seq =
|
||||||
|
try seq (fun _ -> raise ExitSequence); true
|
||||||
|
with ExitSequence -> false
|
||||||
|
|
||||||
|
(** {2 Transform a sequence} *)
|
||||||
|
|
||||||
|
let empty2 k = ()
|
||||||
|
|
||||||
|
let is_empty2 seq2 =
|
||||||
|
try ignore (seq2 (fun _ _ -> raise ExitSequence)); true
|
||||||
|
with ExitSequence -> false
|
||||||
|
|
||||||
|
let length2 seq2 =
|
||||||
|
let r = ref 0 in
|
||||||
|
seq2 (fun _ _ -> incr r);
|
||||||
|
!r
|
||||||
|
|
||||||
|
let zip seq2 k = seq2 (fun x y -> k (x,y))
|
||||||
|
|
||||||
|
let unzip seq k = seq (fun (x,y) -> k x y)
|
||||||
|
|
||||||
|
let zip_i seq k =
|
||||||
|
let r = ref 0 in
|
||||||
|
seq (fun x -> let n = !r in incr r; k n x)
|
||||||
|
|
||||||
|
let fold2 f acc seq2 =
|
||||||
|
let acc = ref acc in
|
||||||
|
seq2 (fun x y -> acc := f !acc x y);
|
||||||
|
!acc
|
||||||
|
|
||||||
|
let iter2 f seq2 = seq2 f
|
||||||
|
|
||||||
|
let map2 f seq2 k = seq2 (fun x y -> k (f x y))
|
||||||
|
|
||||||
|
let map2_2 f g seq2 k =
|
||||||
|
seq2 (fun x y -> k (f x y) (g x y))
|
||||||
|
|
||||||
|
(** {2 Basic data structures converters} *)
|
||||||
|
|
||||||
|
let to_list seq = List.rev (fold (fun y x -> x::y) [] seq)
|
||||||
|
|
||||||
|
let to_rev_list seq = fold (fun y x -> x :: y) [] seq
|
||||||
|
|
||||||
|
let to_opt = head
|
||||||
|
|
||||||
|
let of_opt o k = match o with
|
||||||
|
| None -> ()
|
||||||
|
| Some x -> k x
|
||||||
|
|
||||||
|
let of_list l k = List.iter k l
|
||||||
|
|
||||||
|
let to_array seq =
|
||||||
|
let l = MList.of_seq seq in
|
||||||
|
let n = MList.length l in
|
||||||
|
if n = 0
|
||||||
|
then [||]
|
||||||
|
else begin
|
||||||
|
let a = Array.make n (MList.get l 0) in
|
||||||
|
MList.iteri (fun i x -> a.(i) <- x) l;
|
||||||
|
a
|
||||||
|
end
|
||||||
|
|
||||||
|
let of_array a k =
|
||||||
|
for i = 0 to Array.length a - 1 do
|
||||||
|
k (Array.unsafe_get a i)
|
||||||
|
done
|
||||||
|
|
||||||
|
let of_array_i a k =
|
||||||
|
for i = 0 to Array.length a - 1 do
|
||||||
|
k (i, Array.unsafe_get a i)
|
||||||
|
done
|
||||||
|
|
||||||
|
let of_array2 a k =
|
||||||
|
for i = 0 to Array.length a - 1 do
|
||||||
|
k i (Array.unsafe_get a i)
|
||||||
|
done
|
||||||
|
|
||||||
|
let array_slice a i j k =
|
||||||
|
assert (i >= 0 && j < Array.length a);
|
||||||
|
for idx = i to j do
|
||||||
|
k a.(idx); (* iterate on sub-array *)
|
||||||
|
done
|
||||||
|
|
||||||
|
let of_stream s k = Stream.iter k s
|
||||||
|
|
||||||
|
let to_stream seq =
|
||||||
|
let l = MList.of_seq seq in
|
||||||
|
MList.to_stream l
|
||||||
|
|
||||||
|
let to_stack s seq = iter (fun x -> Stack.push x s) seq
|
||||||
|
|
||||||
|
let of_stack s k = Stack.iter k s
|
||||||
|
|
||||||
|
let to_queue q seq = seq (fun x -> Queue.push x q)
|
||||||
|
|
||||||
|
let of_queue q k = Queue.iter k q
|
||||||
|
|
||||||
|
let hashtbl_add h seq =
|
||||||
|
seq (fun (k,v) -> Hashtbl.add h k v)
|
||||||
|
|
||||||
|
let hashtbl_replace h seq =
|
||||||
|
seq (fun (k,v) -> Hashtbl.replace h k v)
|
||||||
|
|
||||||
|
let to_hashtbl seq =
|
||||||
|
let h = Hashtbl.create 3 in
|
||||||
|
hashtbl_replace h seq;
|
||||||
|
h
|
||||||
|
|
||||||
|
let to_hashtbl2 seq2 =
|
||||||
|
let h = Hashtbl.create 3 in
|
||||||
|
seq2 (fun k v -> Hashtbl.replace h k v);
|
||||||
|
h
|
||||||
|
|
||||||
|
let of_hashtbl h k = Hashtbl.iter (fun a b -> k (a, b)) h
|
||||||
|
|
||||||
|
let of_hashtbl2 h k = Hashtbl.iter k h
|
||||||
|
|
||||||
|
let hashtbl_keys h k = Hashtbl.iter (fun a b -> k a) h
|
||||||
|
|
||||||
|
let hashtbl_values h k = Hashtbl.iter (fun a b -> k b) h
|
||||||
|
|
||||||
|
let of_str s k = String.iter k s
|
||||||
|
|
||||||
|
let to_str seq =
|
||||||
|
let b = Buffer.create 64 in
|
||||||
|
iter (fun c -> Buffer.add_char b c) seq;
|
||||||
|
Buffer.contents b
|
||||||
|
|
||||||
|
let concat_str seq =
|
||||||
|
let b = Buffer.create 64 in
|
||||||
|
iter (Buffer.add_string b) seq;
|
||||||
|
Buffer.contents b
|
||||||
|
|
||||||
|
exception OneShotSequence
|
||||||
|
|
||||||
|
let of_in_channel ic =
|
||||||
|
let first = ref true in
|
||||||
|
fun k ->
|
||||||
|
if not !first
|
||||||
|
then raise OneShotSequence
|
||||||
|
else (
|
||||||
|
first := false;
|
||||||
|
try
|
||||||
|
while true do
|
||||||
|
let c = input_char ic in k c
|
||||||
|
done
|
||||||
|
with End_of_file -> ()
|
||||||
|
)
|
||||||
|
|
||||||
|
let to_buffer seq buf =
|
||||||
|
seq (fun c -> Buffer.add_char buf c)
|
||||||
|
|
||||||
|
(** Iterator on integers in [start...stop] by steps 1 *)
|
||||||
|
let int_range ~start ~stop k =
|
||||||
|
for i = start to stop do k i done
|
||||||
|
|
||||||
|
let int_range_dec ~start ~stop k =
|
||||||
|
for i = start downto stop do k i done
|
||||||
|
|
||||||
|
let of_set (type s) (type v) m set =
|
||||||
|
let module S = (val m : Set.S with type t = s and type elt = v) in
|
||||||
|
fun k -> S.iter k set
|
||||||
|
|
||||||
|
let to_set (type s) (type v) m seq =
|
||||||
|
let module S = (val m : Set.S with type t = s and type elt = v) in
|
||||||
|
fold
|
||||||
|
(fun set x -> S.add x set)
|
||||||
|
S.empty seq
|
||||||
|
|
||||||
|
type 'a gen = unit -> 'a option
|
||||||
|
type 'a klist = unit -> [`Nil | `Cons of 'a * 'a klist]
|
||||||
|
|
||||||
|
let of_gen g =
|
||||||
|
(* consume the generator to build a MList *)
|
||||||
|
let rec iter1 k = match g () with
|
||||||
|
| None -> ()
|
||||||
|
| Some x -> k x; iter1 k
|
||||||
|
in
|
||||||
|
let l = MList.of_seq iter1 in
|
||||||
|
MList.to_seq l
|
||||||
|
|
||||||
|
let to_gen seq =
|
||||||
|
let l = MList.of_seq seq in
|
||||||
|
MList.to_gen l
|
||||||
|
|
||||||
|
let rec of_klist l k = match l() with
|
||||||
|
| `Nil -> ()
|
||||||
|
| `Cons (x,tl) -> k x; of_klist tl k
|
||||||
|
|
||||||
|
let to_klist seq =
|
||||||
|
let l = MList.of_seq seq in
|
||||||
|
MList.to_klist l
|
||||||
|
|
||||||
|
(** {2 Functorial conversions between sets and sequences} *)
|
||||||
|
|
||||||
|
module Set = struct
|
||||||
|
module type S = sig
|
||||||
|
include Set.S
|
||||||
|
val of_seq : elt sequence -> t
|
||||||
|
val to_seq : t -> elt sequence
|
||||||
|
val to_list : t -> elt list
|
||||||
|
val of_list : elt list -> t
|
||||||
|
end
|
||||||
|
|
||||||
|
(** Create an enriched Set module from the given one *)
|
||||||
|
module Adapt(X : Set.S) = struct
|
||||||
|
let to_seq set k = X.iter k set
|
||||||
|
|
||||||
|
let of_seq seq = fold (fun set x -> X.add x set) X.empty seq
|
||||||
|
|
||||||
|
let of_list l = of_seq (of_list l)
|
||||||
|
|
||||||
|
let to_list set = to_list (to_seq set)
|
||||||
|
|
||||||
|
include X
|
||||||
|
end
|
||||||
|
|
||||||
|
(** Functor to build an extended Set module from an ordered type *)
|
||||||
|
module Make(X : Set.OrderedType) = struct
|
||||||
|
module MySet = Set.Make(X)
|
||||||
|
include Adapt(MySet)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
(** {2 Conversion between maps and sequences.} *)
|
||||||
|
|
||||||
|
module Map = struct
|
||||||
|
module type S = sig
|
||||||
|
include Map.S
|
||||||
|
val to_seq : 'a t -> (key * 'a) sequence
|
||||||
|
val of_seq : (key * 'a) sequence -> 'a t
|
||||||
|
val keys : 'a t -> key sequence
|
||||||
|
val values : 'a t -> 'a sequence
|
||||||
|
val to_list : 'a t -> (key * 'a) list
|
||||||
|
val of_list : (key * 'a) list -> 'a t
|
||||||
|
end
|
||||||
|
|
||||||
|
(** Adapt a pre-existing Map module to make it sequence-aware *)
|
||||||
|
module Adapt(M : Map.S) = struct
|
||||||
|
let to_seq m = from_iter (fun k -> M.iter (fun x y -> k (x,y)) m)
|
||||||
|
|
||||||
|
let of_seq seq = fold (fun m (k,v) -> M.add k v m) M.empty seq
|
||||||
|
|
||||||
|
let keys m = from_iter (fun k -> M.iter (fun x _ -> k x) m)
|
||||||
|
|
||||||
|
let values m = from_iter (fun k -> M.iter (fun _ y -> k y) m)
|
||||||
|
|
||||||
|
let of_list l = of_seq (of_list l)
|
||||||
|
|
||||||
|
let to_list x = to_list (to_seq x)
|
||||||
|
|
||||||
|
include M
|
||||||
|
end
|
||||||
|
|
||||||
|
(** Create an enriched Map module, with sequence-aware functions *)
|
||||||
|
module Make(V : Map.OrderedType) : S with type key = V.t = struct
|
||||||
|
module M = Map.Make(V)
|
||||||
|
include Adapt(M)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
(** {2 Infinite sequences of random values} *)
|
||||||
|
|
||||||
|
let random_int bound = forever (fun () -> Random.int bound)
|
||||||
|
|
||||||
|
let random_bool = forever Random.bool
|
||||||
|
|
||||||
|
let random_float bound = forever (fun () -> Random.float bound)
|
||||||
|
|
||||||
|
let random_array a k =
|
||||||
|
assert (Array.length a > 0);
|
||||||
|
while true do
|
||||||
|
let i = Random.int (Array.length a) in
|
||||||
|
k a.(i);
|
||||||
|
done
|
||||||
|
|
||||||
|
let random_list l = random_array (Array.of_list l)
|
||||||
|
|
||||||
|
(** {2 Infix functions} *)
|
||||||
|
|
||||||
|
module Infix = struct
|
||||||
|
let (--) i j = int_range ~start:i ~stop:j
|
||||||
|
|
||||||
|
let (--^) i j = int_range_dec ~start:i ~stop:j
|
||||||
|
|
||||||
|
let (>>=) x f = flat_map f x
|
||||||
|
|
||||||
|
let (>|=) x f = map f x
|
||||||
|
|
||||||
|
let (<*>) funs args k =
|
||||||
|
funs (fun f -> args (fun x -> k (f x)))
|
||||||
|
|
||||||
|
let (<+>) = append
|
||||||
|
end
|
||||||
|
|
||||||
|
include Infix
|
||||||
|
|
||||||
|
(** {2 Pretty printing of sequences} *)
|
||||||
|
|
||||||
|
(** Pretty print a sequence of ['a], using the given pretty printer
|
||||||
|
to print each elements. An optional separator string can be provided. *)
|
||||||
|
let pp_seq ?(sep=", ") pp_elt formatter seq =
|
||||||
|
let first = ref true in
|
||||||
|
seq
|
||||||
|
(fun x ->
|
||||||
|
(if !first then first := false
|
||||||
|
else begin
|
||||||
|
Format.pp_print_string formatter sep;
|
||||||
|
Format.pp_print_cut formatter ();
|
||||||
|
end);
|
||||||
|
pp_elt formatter x)
|
||||||
|
|
||||||
|
let pp_buf ?(sep=", ") pp_elt buf seq =
|
||||||
|
let first = ref true in
|
||||||
|
seq
|
||||||
|
(fun x ->
|
||||||
|
if !first then first := false else Buffer.add_string buf sep;
|
||||||
|
pp_elt buf x)
|
||||||
|
|
||||||
|
let to_string ?sep pp_elt seq =
|
||||||
|
let buf = Buffer.create 25 in
|
||||||
|
pp_buf ?sep (fun buf x -> Buffer.add_string buf (pp_elt x)) buf seq;
|
||||||
|
Buffer.contents buf
|
||||||
|
|
||||||
|
(** {2 Basic IO} *)
|
||||||
|
|
||||||
|
module IO = struct
|
||||||
|
let lines_of ?(mode=0o644) ?(flags=[Open_rdonly]) filename =
|
||||||
|
fun k ->
|
||||||
|
let ic = open_in_gen flags mode filename in
|
||||||
|
try
|
||||||
|
while true do
|
||||||
|
let line = input_line ic in
|
||||||
|
k line
|
||||||
|
done
|
||||||
|
with
|
||||||
|
| End_of_file -> close_in ic
|
||||||
|
| e -> close_in_noerr ic; raise e
|
||||||
|
|
||||||
|
let chunks_of ?(mode=0o644) ?(flags=[]) ?(size=1024) filename =
|
||||||
|
fun k ->
|
||||||
|
let ic = open_in_gen flags mode filename in
|
||||||
|
try
|
||||||
|
let buf = String.create size in
|
||||||
|
let n = ref 0 in
|
||||||
|
let stop = ref false in
|
||||||
|
while not !stop do
|
||||||
|
n := 0;
|
||||||
|
(* try to read [size] chars. If [input] returns [0] it means
|
||||||
|
the end of file, so we stop, but first we yield the current chunk *)
|
||||||
|
while !n < size && not !stop do
|
||||||
|
let n' = input ic buf !n (size - !n) in
|
||||||
|
if n' = 0 then stop := true else n := !n + n';
|
||||||
|
done;
|
||||||
|
if !n > 0
|
||||||
|
then k (String.sub buf 0 !n)
|
||||||
|
done;
|
||||||
|
close_in ic
|
||||||
|
with e ->
|
||||||
|
close_in_noerr ic;
|
||||||
|
raise e
|
||||||
|
|
||||||
|
let write_to ?(mode=0o644) ?(flags=[Open_creat;Open_wronly]) filename seq =
|
||||||
|
let oc = open_out_gen flags mode filename in
|
||||||
|
try
|
||||||
|
seq (fun s -> output oc s 0 (String.length s));
|
||||||
|
close_out oc
|
||||||
|
with e ->
|
||||||
|
close_out oc;
|
||||||
|
raise e
|
||||||
|
|
||||||
|
let write_lines ?mode ?flags filename seq =
|
||||||
|
write_to ?mode ?flags filename (snoc (intersperse "\n" seq) "\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
4
sequence/sequence.mldylib
Normal file
4
sequence/sequence.mldylib
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
# OASIS_START
|
||||||
|
# DO NOT EDIT (digest: 3ff39d3acb327553070a64ef0cb321d5)
|
||||||
|
Sequence
|
||||||
|
# OASIS_STOP
|
||||||
587
sequence/sequence.mli
Normal file
587
sequence/sequence.mli
Normal file
|
|
@ -0,0 +1,587 @@
|
||||||
|
(*
|
||||||
|
copyright (c) 2013, 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 Simple and Efficient Iterators} *)
|
||||||
|
|
||||||
|
(** The iterators are designed to allow easy transfer (mappings) between data
|
||||||
|
structures, without defining [n^2] conversions between the [n] types. The
|
||||||
|
implementation relies on the assumption that a sequence can be iterated
|
||||||
|
on as many times as needed; this choice allows for high performance
|
||||||
|
of many combinators. However, for transient iterators, the {!persistent}
|
||||||
|
function is provided, storing elements of a transient iterator
|
||||||
|
in memory; the iterator can then be used several times (See further).
|
||||||
|
|
||||||
|
Note that some combinators also return sequences (e.g. {!group}). The
|
||||||
|
transformation is computed on the fly every time one iterates over
|
||||||
|
the resulting sequence. If a transformation performs heavy computation,
|
||||||
|
{!persistent} can also be used as intermediate storage.
|
||||||
|
|
||||||
|
Most functions are {b lazy}, i.e. they do not actually use their arguments
|
||||||
|
until their result is iterated on. For instance, if one calls {!map}
|
||||||
|
on a sequence, one gets a new sequence, but nothing else happens until
|
||||||
|
this new sequence is used (by folding or iterating on it).
|
||||||
|
|
||||||
|
If a sequence is built from an iteration function that is {b repeatable}
|
||||||
|
(i.e. calling it several times always iterates on the same set of
|
||||||
|
elements, for instance List.iter or Map.iter), then
|
||||||
|
the resulting {!t} object is also repeatable. For {b one-time iter functions}
|
||||||
|
such as iteration on a file descriptor or a {!Stream},
|
||||||
|
the {!persistent} function can be used to iterate and store elements in
|
||||||
|
a memory structure; the result is a sequence that iterates on the elements
|
||||||
|
of this memory structure, cheaply and repeatably. *)
|
||||||
|
|
||||||
|
type +'a t = ('a -> unit) -> unit
|
||||||
|
(** A sequence of values of type ['a]. If you give it a function ['a -> unit]
|
||||||
|
it will be applied to every element of the sequence successively. *)
|
||||||
|
|
||||||
|
type +'a sequence = 'a t
|
||||||
|
|
||||||
|
type (+'a, +'b) t2 = ('a -> 'b -> unit) -> unit
|
||||||
|
(** Sequence of pairs of values of type ['a] and ['b]. *)
|
||||||
|
|
||||||
|
(** {2 Build a sequence} *)
|
||||||
|
|
||||||
|
val from_iter : (('a -> unit) -> unit) -> 'a t
|
||||||
|
(** Build a sequence from a iter function *)
|
||||||
|
|
||||||
|
val from_fun : (unit -> 'a option) -> 'a t
|
||||||
|
(** Call the function repeatedly until it returns None. This
|
||||||
|
sequence is transient, use {!persistent} if needed! *)
|
||||||
|
|
||||||
|
val empty : 'a t
|
||||||
|
(** Empty sequence. It contains no element. *)
|
||||||
|
|
||||||
|
val singleton : 'a -> 'a t
|
||||||
|
(** Singleton sequence, with exactly one element. *)
|
||||||
|
|
||||||
|
val doubleton : 'a -> 'a -> 'a t
|
||||||
|
(** Sequence with exactly two elements *)
|
||||||
|
|
||||||
|
val cons : 'a -> 'a t -> 'a t
|
||||||
|
(** [cons x l] yields [x], then yields from [l].
|
||||||
|
Same as [append (singleton x) l] *)
|
||||||
|
|
||||||
|
val snoc : 'a t -> 'a -> 'a t
|
||||||
|
(** Same as {!cons} but yields the element after iterating on [l] *)
|
||||||
|
|
||||||
|
val return : 'a -> 'a t
|
||||||
|
(** Synonym to {!singleton} *)
|
||||||
|
|
||||||
|
val pure : 'a -> 'a t
|
||||||
|
(** Synonym to {!singleton} *)
|
||||||
|
|
||||||
|
val repeat : 'a -> 'a t
|
||||||
|
(** Infinite sequence of the same element. You may want to look
|
||||||
|
at {!take} and the likes if you iterate on it. *)
|
||||||
|
|
||||||
|
val iterate : ('a -> 'a) -> 'a -> 'a t
|
||||||
|
(** [iterate f x] is the infinite sequence [x, f(x), f(f(x)), ...] *)
|
||||||
|
|
||||||
|
val forever : (unit -> 'b) -> 'b t
|
||||||
|
(** Sequence that calls the given function to produce elements.
|
||||||
|
The sequence may be transient (depending on the function), and definitely
|
||||||
|
is infinite. You may want to use {!take} and {!persistent}. *)
|
||||||
|
|
||||||
|
val cycle : 'a t -> 'a t
|
||||||
|
(** Cycle forever through the given sequence. Assume the given sequence can
|
||||||
|
be traversed any amount of times (not transient). This yields an
|
||||||
|
infinite sequence, you should use something like {!take} not to loop
|
||||||
|
forever. *)
|
||||||
|
|
||||||
|
(** {2 Consume a sequence} *)
|
||||||
|
|
||||||
|
val iter : ('a -> unit) -> 'a t -> unit
|
||||||
|
(** Consume the sequence, passing all its arguments to the function.
|
||||||
|
Basically [iter f seq] is just [seq f]. *)
|
||||||
|
|
||||||
|
val iteri : (int -> 'a -> unit) -> 'a t -> unit
|
||||||
|
(** Iterate on elements and their index in the sequence *)
|
||||||
|
|
||||||
|
val fold : ('b -> 'a -> 'b) -> 'b -> 'a t -> 'b
|
||||||
|
(** Fold over elements of the sequence, consuming it *)
|
||||||
|
|
||||||
|
val foldi : ('b -> int -> 'a -> 'b) -> 'b -> 'a t -> 'b
|
||||||
|
(** Fold over elements of the sequence and their index, consuming it *)
|
||||||
|
|
||||||
|
val map : ('a -> 'b) -> 'a t -> 'b t
|
||||||
|
(** Map objects of the sequence into other elements, lazily *)
|
||||||
|
|
||||||
|
val mapi : (int -> 'a -> 'b) -> 'a t -> 'b t
|
||||||
|
(** Map objects, along with their index in the sequence *)
|
||||||
|
|
||||||
|
val for_all : ('a -> bool) -> 'a t -> bool
|
||||||
|
(** Do all elements satisfy the predicate? *)
|
||||||
|
|
||||||
|
val exists : ('a -> bool) -> 'a t -> bool
|
||||||
|
(** Exists there some element satisfying the predicate? *)
|
||||||
|
|
||||||
|
val mem : ?eq:('a -> 'a -> bool) -> 'a -> 'a t -> bool
|
||||||
|
(** Is the value a member of the sequence?
|
||||||
|
@param eq the equality predicate to use (default [(=)])
|
||||||
|
@since 0.5 *)
|
||||||
|
|
||||||
|
val find : ('a -> 'b option) -> 'a t -> 'b option
|
||||||
|
(** Find the first element on which the function doesn't return [None]
|
||||||
|
@since 0.5 *)
|
||||||
|
|
||||||
|
val length : 'a t -> int
|
||||||
|
(** How long is the sequence? Forces the sequence. *)
|
||||||
|
|
||||||
|
val is_empty : 'a t -> bool
|
||||||
|
(** Is the sequence empty? Forces the sequence. *)
|
||||||
|
|
||||||
|
(** {2 Transform a sequence} *)
|
||||||
|
|
||||||
|
val filter : ('a -> bool) -> 'a t -> 'a t
|
||||||
|
(** Filter on elements of the sequence *)
|
||||||
|
|
||||||
|
val append : 'a t -> 'a t -> 'a t
|
||||||
|
(** Append two sequences. Iterating on the result is like iterating
|
||||||
|
on the first, then on the second. *)
|
||||||
|
|
||||||
|
val concat : 'a t t -> 'a t
|
||||||
|
(** Concatenate a sequence of sequences into one sequence. *)
|
||||||
|
|
||||||
|
val flatten : 'a t t -> 'a t
|
||||||
|
(** Alias for {!concat} *)
|
||||||
|
|
||||||
|
val flatMap : ('a -> 'b t) -> 'a t -> 'b t
|
||||||
|
(** Monadic bind. Intuitively, it applies the function to every element of the
|
||||||
|
initial sequence, and calls {!concat}. *)
|
||||||
|
|
||||||
|
val flat_map : ('a -> 'b t) -> 'a t -> 'b t
|
||||||
|
(** Alias to {!flatMap} with a more explicit name
|
||||||
|
@since 0.5 *)
|
||||||
|
|
||||||
|
val fmap : ('a -> 'b option) -> 'a t -> 'b t
|
||||||
|
(** Specialized version of {!flatMap} for options. *)
|
||||||
|
|
||||||
|
val filter_map : ('a -> 'b option) -> 'a t -> 'b t
|
||||||
|
(** Alias to {!fmap} with a more explicit name
|
||||||
|
@since 0.5 *)
|
||||||
|
|
||||||
|
val intersperse : 'a -> 'a t -> 'a t
|
||||||
|
(** Insert the single element between every element of the sequence *)
|
||||||
|
|
||||||
|
(** {2 Caching} *)
|
||||||
|
|
||||||
|
val persistent : 'a t -> 'a t
|
||||||
|
(** Iterate on the sequence, storing elements in an efficient internal structure..
|
||||||
|
The resulting sequence can be iterated on as many times as needed.
|
||||||
|
{b Note}: calling persistent on an already persistent sequence
|
||||||
|
will still make a new copy of the sequence! *)
|
||||||
|
|
||||||
|
val persistent_lazy : 'a t -> 'a t
|
||||||
|
(** Lazy version of {!persistent}. When calling [persistent_lazy s],
|
||||||
|
a new sequence [s'] is immediately returned (without actually consuming
|
||||||
|
[s]) in constant time; the first time [s'] is iterated on,
|
||||||
|
it also consumes [s] and caches its content into a inner data
|
||||||
|
structure that will back [s'] for future iterations.
|
||||||
|
|
||||||
|
{b warning}: on the first traversal of [s'], if the traversal
|
||||||
|
is interrupted prematurely ({!take}, etc.) then [s'] will not be
|
||||||
|
memorized, and the next call to [s'] will traverse [s] again. *)
|
||||||
|
|
||||||
|
(** {2 Misc} *)
|
||||||
|
|
||||||
|
val sort : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t
|
||||||
|
(** Sort the sequence. Eager, O(n) ram and O(n ln(n)) time.
|
||||||
|
It iterates on elements of the argument sequence immediately,
|
||||||
|
before it sorts them. *)
|
||||||
|
|
||||||
|
val sort_uniq : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t
|
||||||
|
(** Sort the sequence and remove duplicates. Eager, same as [sort] *)
|
||||||
|
|
||||||
|
val group : ?eq:('a -> 'a -> bool) -> 'a t -> 'a list t
|
||||||
|
(** Group equal consecutive elements. *)
|
||||||
|
|
||||||
|
val uniq : ?eq:('a -> 'a -> bool) -> 'a t -> 'a t
|
||||||
|
(** Remove consecutive duplicate elements. Basically this is
|
||||||
|
like [fun seq -> map List.hd (group seq)]. *)
|
||||||
|
|
||||||
|
val product : 'a t -> 'b t -> ('a * 'b) t
|
||||||
|
(** Cartesian product of the sequences. When calling [product a b],
|
||||||
|
the caller {b MUST} ensure that [b] can be traversed as many times
|
||||||
|
as required (several times), possibly by calling {!persistent} on it
|
||||||
|
beforehand. *)
|
||||||
|
|
||||||
|
val product2 : 'a t -> 'b t -> ('a, 'b) t2
|
||||||
|
(** Binary version of {!product}. Same requirements.
|
||||||
|
@since 0.5 *)
|
||||||
|
|
||||||
|
val join : join_row:('a -> 'b -> 'c option) -> 'a t -> 'b t -> 'c t
|
||||||
|
(** [join ~join_row a b] combines every element of [a] with every
|
||||||
|
element of [b] using [join_row]. If [join_row] returns None, then
|
||||||
|
the two elements do not combine. Assume that [b] allows for multiple
|
||||||
|
iterations. *)
|
||||||
|
|
||||||
|
val unfoldr : ('b -> ('a * 'b) option) -> 'b -> 'a t
|
||||||
|
(** [unfoldr f b] will apply [f] to [b]. If it
|
||||||
|
yields [Some (x,b')] then [x] is returned
|
||||||
|
and unfoldr recurses with [b']. *)
|
||||||
|
|
||||||
|
val scan : ('b -> 'a -> 'b) -> 'b -> 'a t -> 'b t
|
||||||
|
(** Sequence of intermediate results *)
|
||||||
|
|
||||||
|
val max : ?lt:('a -> 'a -> bool) -> 'a t -> 'a option
|
||||||
|
(** Max element of the sequence, using the given comparison function.
|
||||||
|
@return None if the sequence is empty, Some [m] where [m] is the maximal
|
||||||
|
element otherwise *)
|
||||||
|
|
||||||
|
val min : ?lt:('a -> 'a -> bool) -> 'a t -> 'a option
|
||||||
|
(** Min element of the sequence, using the given comparison function.
|
||||||
|
see {!max} for more details. *)
|
||||||
|
|
||||||
|
val head : 'a t -> 'a option
|
||||||
|
(** First element, if any, otherwise [None]
|
||||||
|
@since 0.5.1 *)
|
||||||
|
|
||||||
|
val head_exn : 'a t -> 'a
|
||||||
|
(** First element, if any, fails
|
||||||
|
@raise Invalid_argument if the sequence is empty
|
||||||
|
@since 0.5.1 *)
|
||||||
|
|
||||||
|
val take : int -> 'a t -> 'a t
|
||||||
|
(** Take at most [n] elements from the sequence. Works on infinite
|
||||||
|
sequences. *)
|
||||||
|
|
||||||
|
val take_while : ('a -> bool) -> 'a t -> 'a t
|
||||||
|
(** Take elements while they satisfy the predicate, then stops iterating.
|
||||||
|
Will work on an infinite sequence [s] if the predicate is false for at
|
||||||
|
least one element of [s]. *)
|
||||||
|
|
||||||
|
val drop : int -> 'a t -> 'a t
|
||||||
|
(** Drop the [n] first elements of the sequence. Lazy. *)
|
||||||
|
|
||||||
|
val drop_while : ('a -> bool) -> 'a t -> 'a t
|
||||||
|
(** Predicate version of {!drop} *)
|
||||||
|
|
||||||
|
val rev : 'a t -> 'a t
|
||||||
|
(** Reverse the sequence. O(n) memory and time, needs the
|
||||||
|
sequence to be finite. The result is persistent and does
|
||||||
|
not depend on the input being repeatable. *)
|
||||||
|
|
||||||
|
(** {2 Binary sequences} *)
|
||||||
|
|
||||||
|
val empty2 : ('a, 'b) t2
|
||||||
|
|
||||||
|
val is_empty2 : (_, _) t2 -> bool
|
||||||
|
|
||||||
|
val length2 : (_, _) t2 -> int
|
||||||
|
|
||||||
|
val zip : ('a, 'b) t2 -> ('a * 'b) t
|
||||||
|
|
||||||
|
val unzip : ('a * 'b) t -> ('a, 'b) t2
|
||||||
|
|
||||||
|
val zip_i : 'a t -> (int, 'a) t2
|
||||||
|
(** Zip elements of the sequence with their index in the sequence *)
|
||||||
|
|
||||||
|
val fold2 : ('c -> 'a -> 'b -> 'c) -> 'c -> ('a, 'b) t2 -> 'c
|
||||||
|
|
||||||
|
val iter2 : ('a -> 'b -> unit) -> ('a, 'b) t2 -> unit
|
||||||
|
|
||||||
|
val map2 : ('a -> 'b -> 'c) -> ('a, 'b) t2 -> 'c t
|
||||||
|
|
||||||
|
val map2_2 : ('a -> 'b -> 'c) -> ('a -> 'b -> 'd) -> ('a, 'b) t2 -> ('c, 'd) t2
|
||||||
|
(** [map2_2 f g seq2] maps each [x, y] of seq2 into [f x y, g x y] *)
|
||||||
|
|
||||||
|
(** {2 Basic data structures converters} *)
|
||||||
|
|
||||||
|
val to_list : 'a t -> 'a list
|
||||||
|
(** Convert the sequence into a list. Preserves order of elements.
|
||||||
|
This function is tail-recursive, but consumes 2*n memory.
|
||||||
|
If order doesn't matter to you, consider {!to_rev_list}. *)
|
||||||
|
|
||||||
|
val to_rev_list : 'a t -> 'a list
|
||||||
|
(** Get the list of the reversed sequence (more efficient than {!to_list}) *)
|
||||||
|
|
||||||
|
val of_list : 'a list -> 'a t
|
||||||
|
|
||||||
|
val to_opt : 'a t -> 'a option
|
||||||
|
(** Alias to {!head}
|
||||||
|
@since 0.5.1 *)
|
||||||
|
|
||||||
|
val to_array : 'a t -> 'a array
|
||||||
|
(** Convert to an array. Currently not very efficient because
|
||||||
|
an intermediate list is used. *)
|
||||||
|
|
||||||
|
val of_array : 'a array -> 'a t
|
||||||
|
|
||||||
|
val of_array_i : 'a array -> (int * 'a) t
|
||||||
|
(** Elements of the array, with their index *)
|
||||||
|
|
||||||
|
val of_array2 : 'a array -> (int, 'a) t2
|
||||||
|
|
||||||
|
val array_slice : 'a array -> int -> int -> 'a t
|
||||||
|
(** [array_slice a i j] Sequence of elements whose indexes range
|
||||||
|
from [i] to [j] *)
|
||||||
|
|
||||||
|
val of_opt : 'a option -> 'a t
|
||||||
|
(** Iterate on 0 or 1 values.
|
||||||
|
@since 0.5.1 *)
|
||||||
|
|
||||||
|
val of_stream : 'a Stream.t -> 'a t
|
||||||
|
(** Sequence of elements of a stream (usable only once) *)
|
||||||
|
|
||||||
|
val to_stream : 'a t -> 'a Stream.t
|
||||||
|
(** Convert to a stream. linear in memory and time (a copy is made in memory) *)
|
||||||
|
|
||||||
|
val to_stack : 'a Stack.t -> 'a t -> unit
|
||||||
|
(** Push elements of the sequence on the stack *)
|
||||||
|
|
||||||
|
val of_stack : 'a Stack.t -> 'a t
|
||||||
|
(** Sequence of elements of the stack (same order as [Stack.iter]) *)
|
||||||
|
|
||||||
|
val to_queue : 'a Queue.t -> 'a t -> unit
|
||||||
|
(** Push elements of the sequence into the queue *)
|
||||||
|
|
||||||
|
val of_queue : 'a Queue.t -> 'a t
|
||||||
|
(** Sequence of elements contained in the queue, FIFO order *)
|
||||||
|
|
||||||
|
val hashtbl_add : ('a, 'b) Hashtbl.t -> ('a * 'b) t -> unit
|
||||||
|
(** Add elements of the sequence to the hashtable, with
|
||||||
|
Hashtbl.add *)
|
||||||
|
|
||||||
|
val hashtbl_replace : ('a, 'b) Hashtbl.t -> ('a * 'b) t -> unit
|
||||||
|
(** Add elements of the sequence to the hashtable, with
|
||||||
|
Hashtbl.replace (erases conflicting bindings) *)
|
||||||
|
|
||||||
|
val to_hashtbl : ('a * 'b) t -> ('a, 'b) Hashtbl.t
|
||||||
|
(** Build a hashtable from a sequence of key/value pairs *)
|
||||||
|
|
||||||
|
val to_hashtbl2 : ('a, 'b) t2 -> ('a, 'b) Hashtbl.t
|
||||||
|
(** Build a hashtable from a sequence of key/value pairs *)
|
||||||
|
|
||||||
|
val of_hashtbl : ('a, 'b) Hashtbl.t -> ('a * 'b) t
|
||||||
|
(** Sequence of key/value pairs from the hashtable *)
|
||||||
|
|
||||||
|
val of_hashtbl2 : ('a, 'b) Hashtbl.t -> ('a, 'b) t2
|
||||||
|
(** Sequence of key/value pairs from the hashtable *)
|
||||||
|
|
||||||
|
val hashtbl_keys : ('a, 'b) Hashtbl.t -> 'a t
|
||||||
|
val hashtbl_values : ('a, 'b) Hashtbl.t -> 'b t
|
||||||
|
|
||||||
|
val of_str : string -> char t
|
||||||
|
val to_str : char t -> string
|
||||||
|
|
||||||
|
val concat_str : string t -> string
|
||||||
|
(** Concatenate strings together, eagerly.
|
||||||
|
Also see {!intersperse} to add a separator.
|
||||||
|
@since 0.5 *)
|
||||||
|
|
||||||
|
exception OneShotSequence
|
||||||
|
(** Raised when the user tries to iterate several times on
|
||||||
|
a transient iterator *)
|
||||||
|
|
||||||
|
val of_in_channel : in_channel -> char t
|
||||||
|
(** Iterates on characters of the input (can block when one
|
||||||
|
iterates over the sequence). If you need to iterate
|
||||||
|
several times on this sequence, use {!persistent}.
|
||||||
|
@raise OneShotSequence when used more than once. *)
|
||||||
|
|
||||||
|
val to_buffer : char t -> Buffer.t -> unit
|
||||||
|
(** Copy content of the sequence into the buffer *)
|
||||||
|
|
||||||
|
val int_range : start:int -> stop:int -> int t
|
||||||
|
(** Iterator on integers in [start...stop] by steps 1. Also see
|
||||||
|
{!(--)} for an infix version. *)
|
||||||
|
|
||||||
|
val int_range_dec : start:int -> stop:int -> int t
|
||||||
|
(** Iterator on decreasing integers in [stop...start] by steps -1.
|
||||||
|
See {!(--^)} for an infix version *)
|
||||||
|
|
||||||
|
val of_set : (module Set.S with type elt = 'a and type t = 'b) -> 'b -> 'a t
|
||||||
|
(** Convert the given set to a sequence. The set module must be provided. *)
|
||||||
|
|
||||||
|
val to_set : (module Set.S with type elt = 'a and type t = 'b) -> 'a t -> 'b
|
||||||
|
(** Convert the sequence to a set, given the proper set module *)
|
||||||
|
|
||||||
|
type 'a gen = unit -> 'a option
|
||||||
|
type 'a klist = unit -> [`Nil | `Cons of 'a * 'a klist]
|
||||||
|
|
||||||
|
val of_gen : 'a gen -> 'a t
|
||||||
|
(** Traverse eagerly the generator and build a sequence from it *)
|
||||||
|
|
||||||
|
val to_gen : 'a t -> 'a gen
|
||||||
|
(** Make the sequence persistent (O(n)) and then iterate on it. Eager. *)
|
||||||
|
|
||||||
|
val of_klist : 'a klist -> 'a t
|
||||||
|
(** Iterate on the lazy list *)
|
||||||
|
|
||||||
|
val to_klist : 'a t -> 'a klist
|
||||||
|
(** Make the sequence persistent and then iterate on it. Eager. *)
|
||||||
|
|
||||||
|
(** {2 Functorial conversions between sets and sequences} *)
|
||||||
|
|
||||||
|
module Set : sig
|
||||||
|
module type S = sig
|
||||||
|
include Set.S
|
||||||
|
val of_seq : elt sequence -> t
|
||||||
|
val to_seq : t -> elt sequence
|
||||||
|
val to_list : t -> elt list
|
||||||
|
val of_list : elt list -> t
|
||||||
|
end
|
||||||
|
|
||||||
|
(** Create an enriched Set module from the given one *)
|
||||||
|
module Adapt(X : Set.S) : S with type elt = X.elt and type t = X.t
|
||||||
|
|
||||||
|
(** Functor to build an extended Set module from an ordered type *)
|
||||||
|
module Make(X : Set.OrderedType) : S with type elt = X.t
|
||||||
|
end
|
||||||
|
|
||||||
|
(** {2 Conversion between maps and sequences.} *)
|
||||||
|
|
||||||
|
module Map : sig
|
||||||
|
module type S = sig
|
||||||
|
include Map.S
|
||||||
|
val to_seq : 'a t -> (key * 'a) sequence
|
||||||
|
val of_seq : (key * 'a) sequence -> 'a t
|
||||||
|
val keys : 'a t -> key sequence
|
||||||
|
val values : 'a t -> 'a sequence
|
||||||
|
val to_list : 'a t -> (key * 'a) list
|
||||||
|
val of_list : (key * 'a) list -> 'a t
|
||||||
|
end
|
||||||
|
|
||||||
|
(** Adapt a pre-existing Map module to make it sequence-aware *)
|
||||||
|
module Adapt(M : Map.S) : S with type key = M.key and type 'a t = 'a M.t
|
||||||
|
|
||||||
|
(** Create an enriched Map module, with sequence-aware functions *)
|
||||||
|
module Make(V : Map.OrderedType) : S with type key = V.t
|
||||||
|
end
|
||||||
|
|
||||||
|
(** {2 Infinite sequences of random values} *)
|
||||||
|
|
||||||
|
val random_int : int -> int t
|
||||||
|
(** Infinite sequence of random integers between 0 and
|
||||||
|
the given higher bound (see Random.int) *)
|
||||||
|
|
||||||
|
val random_bool : bool t
|
||||||
|
(** Infinite sequence of random bool values *)
|
||||||
|
|
||||||
|
val random_float : float -> float t
|
||||||
|
|
||||||
|
val random_array : 'a array -> 'a t
|
||||||
|
(** Sequence of choices of an element in the array *)
|
||||||
|
|
||||||
|
val random_list : 'a list -> 'a t
|
||||||
|
(** Infinite sequence of random elements of the list. Basically the
|
||||||
|
same as {!random_array}. *)
|
||||||
|
|
||||||
|
(** {2 Infix functions} *)
|
||||||
|
|
||||||
|
module Infix : sig
|
||||||
|
val (--) : int -> int -> int t
|
||||||
|
(** [a -- b] is the range of integers from [a] to [b], both included,
|
||||||
|
in increasing order. It will therefore be empty if [a > b]. *)
|
||||||
|
|
||||||
|
val (--^) : int -> int -> int t
|
||||||
|
(** [a --^ b] is the range of integers from [b] to [a], both included,
|
||||||
|
in decreasing order (starts from [a]).
|
||||||
|
It will therefore be empty if [a < b]. *)
|
||||||
|
|
||||||
|
val (>>=) : 'a t -> ('a -> 'b t) -> 'b t
|
||||||
|
(** Monadic bind (infix version of {!flat_map}
|
||||||
|
@since 0.5 *)
|
||||||
|
|
||||||
|
val (>|=) : 'a t -> ('a -> 'b) -> 'b t
|
||||||
|
(** Infix version of {!map}
|
||||||
|
@since 0.5 *)
|
||||||
|
|
||||||
|
val (<*>) : ('a -> 'b) t -> 'a t -> 'b t
|
||||||
|
(** Applicative operator (product+application)
|
||||||
|
@since 0.5 *)
|
||||||
|
|
||||||
|
val (<+>) : 'a t -> 'a t -> 'a t
|
||||||
|
(** Concatenation of sequences
|
||||||
|
@since 0.5 *)
|
||||||
|
end
|
||||||
|
|
||||||
|
include module type of Infix
|
||||||
|
|
||||||
|
|
||||||
|
(** {2 Pretty printing of sequences} *)
|
||||||
|
|
||||||
|
val pp_seq : ?sep:string -> (Format.formatter -> 'a -> unit) ->
|
||||||
|
Format.formatter -> 'a t -> unit
|
||||||
|
(** Pretty print a sequence of ['a], using the given pretty printer
|
||||||
|
to print each elements. An optional separator string can be provided. *)
|
||||||
|
|
||||||
|
val pp_buf : ?sep:string -> (Buffer.t -> 'a -> unit) ->
|
||||||
|
Buffer.t -> 'a t -> unit
|
||||||
|
(** Print into a buffer *)
|
||||||
|
|
||||||
|
val to_string : ?sep:string -> ('a -> string) -> 'a t -> string
|
||||||
|
(** Print into a string *)
|
||||||
|
|
||||||
|
(** {2 Basic IO}
|
||||||
|
|
||||||
|
Very basic interface to manipulate files as sequence of chunks/lines. The
|
||||||
|
sequences take care of opening and closing files properly; every time
|
||||||
|
one iterates over a sequence, the file is opened/closed again.
|
||||||
|
|
||||||
|
Example: copy a file ["a"] into file ["b"], removing blank lines:
|
||||||
|
|
||||||
|
{[
|
||||||
|
Sequence.(IO.lines_of "a" |> filter (fun l-> l<> "") |> IO.write_lines "b");;
|
||||||
|
]}
|
||||||
|
|
||||||
|
By chunks of [4096] bytes:
|
||||||
|
|
||||||
|
{[
|
||||||
|
Sequence.IO.(chunks_of ~size:4096 "a" |> write_to "b");;
|
||||||
|
]}
|
||||||
|
|
||||||
|
@since 0.5.1 *)
|
||||||
|
|
||||||
|
module IO : sig
|
||||||
|
val lines_of : ?mode:int -> ?flags:open_flag list ->
|
||||||
|
string -> string t
|
||||||
|
(** [lines_of filename] reads all lines of the given file. It raises the
|
||||||
|
same exception as would opening the file and read from it, except
|
||||||
|
from [End_of_file] (which is caught). The file is {b always} properly
|
||||||
|
closed.
|
||||||
|
Every time the sequence is iterated on, the file is opened again, so
|
||||||
|
different iterations might return different results
|
||||||
|
@param mode default [0o644]
|
||||||
|
@param flags default: [[Open_rdonly]] *)
|
||||||
|
|
||||||
|
val chunks_of : ?mode:int -> ?flags:open_flag list -> ?size:int ->
|
||||||
|
string -> string t
|
||||||
|
(** Read chunks of the given [size] from the file. The last chunk might be
|
||||||
|
smaller. Behaves like {!lines_of} regarding errors and options.
|
||||||
|
Every time the sequence is iterated on, the file is opened again, so
|
||||||
|
different iterations might return different results *)
|
||||||
|
|
||||||
|
val write_to : ?mode:int -> ?flags:open_flag list ->
|
||||||
|
string -> string t -> unit
|
||||||
|
(** [write_to filename seq] writes all strings from [seq] into the given
|
||||||
|
file. It takes care of opening and closing the file.
|
||||||
|
@param mode default [0o644]
|
||||||
|
@param flags used by [open_out_gen]. Default: [[Open_creat;Open_wronly]]. *)
|
||||||
|
|
||||||
|
val write_lines : ?mode:int -> ?flags:open_flag list ->
|
||||||
|
string -> string t -> unit
|
||||||
|
(** Same as {!write_to}, but intercales ['\n'] between each string *)
|
||||||
|
end
|
||||||
4
sequence/sequence.mllib
Normal file
4
sequence/sequence.mllib
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
# OASIS_START
|
||||||
|
# DO NOT EDIT (digest: 3ff39d3acb327553070a64ef0cb321d5)
|
||||||
|
Sequence
|
||||||
|
# OASIS_STOP
|
||||||
4
sequence/sequence.odocl
Normal file
4
sequence/sequence.odocl
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
# OASIS_START
|
||||||
|
# DO NOT EDIT (digest: 3ff39d3acb327553070a64ef0cb321d5)
|
||||||
|
Sequence
|
||||||
|
# OASIS_STOP
|
||||||
7205
sequence/setup.ml
Normal file
7205
sequence/setup.ml
Normal file
File diff suppressed because it is too large
Load diff
9
sequence/tests/run_tests.ml
Normal file
9
sequence/tests/run_tests.ml
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
open OUnit
|
||||||
|
|
||||||
|
let suite =
|
||||||
|
"run_tests" >:::
|
||||||
|
[ Test_sequence.suite; ]
|
||||||
|
|
||||||
|
let _ =
|
||||||
|
OUnit.run_test_tt_main suite
|
||||||
220
sequence/tests/test_sequence.ml
Normal file
220
sequence/tests/test_sequence.ml
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
|
||||||
|
open OUnit
|
||||||
|
|
||||||
|
module S = Sequence
|
||||||
|
open Sequence.Infix
|
||||||
|
|
||||||
|
let pp_ilist l =
|
||||||
|
let b = Buffer.create 15 in
|
||||||
|
Format.bprintf b "@[<h>%a@]" (S.pp_seq Format.pp_print_int) (S.of_list l);
|
||||||
|
Buffer.contents b
|
||||||
|
|
||||||
|
let test_empty () =
|
||||||
|
let seq = S.empty in
|
||||||
|
OUnit.assert_bool "empty" (S.is_empty seq);
|
||||||
|
OUnit.assert_bool "empty"
|
||||||
|
(try S.iter (fun _ -> raise Exit) seq; true with Exit -> false);
|
||||||
|
()
|
||||||
|
|
||||||
|
let test_repeat () =
|
||||||
|
let seq = S.repeat "hello" in
|
||||||
|
OUnit.assert_equal ["hello"; "hello"; "hello"]
|
||||||
|
(seq |> S.take 3 |> S.to_list);
|
||||||
|
()
|
||||||
|
|
||||||
|
let test_concat () =
|
||||||
|
let s1 = (1 -- 5) in
|
||||||
|
let s2 = (6 -- 10) in
|
||||||
|
let l = [1;2;3;4;5;6;7;8;9;10] in
|
||||||
|
OUnit.assert_equal l (S.to_list (S.append s1 s2));
|
||||||
|
()
|
||||||
|
|
||||||
|
let test_fold () =
|
||||||
|
let n = (1 -- 10)
|
||||||
|
|> S.fold (+) 0 in
|
||||||
|
OUnit.assert_equal 55 n;
|
||||||
|
()
|
||||||
|
|
||||||
|
let test_foldi () =
|
||||||
|
let l = ["hello"; "world"]
|
||||||
|
|> S.of_list
|
||||||
|
|> S.foldi (fun acc i x -> (i,x) :: acc) [] in
|
||||||
|
OUnit.assert_equal [1, "world"; 0, "hello"] l;
|
||||||
|
()
|
||||||
|
|
||||||
|
let test_exists () =
|
||||||
|
(1 -- 100)
|
||||||
|
|> S.exists (fun x -> x = 59)
|
||||||
|
|> OUnit.assert_bool "exists";
|
||||||
|
(1 -- 100)
|
||||||
|
|> S.exists (fun x -> x < 0)
|
||||||
|
|> (fun x -> not x)
|
||||||
|
|> OUnit.assert_bool "not exists";
|
||||||
|
()
|
||||||
|
|
||||||
|
let test_length () =
|
||||||
|
(1 -- 1000) |> S.length |> OUnit.assert_equal 1000
|
||||||
|
|
||||||
|
let test_concat () =
|
||||||
|
1 -- 1000
|
||||||
|
|> S.map (fun i -> (i -- (i+1)))
|
||||||
|
|> S.concat
|
||||||
|
|> S.length
|
||||||
|
|> OUnit.assert_equal 2000
|
||||||
|
|
||||||
|
let test_flatMap () =
|
||||||
|
1 -- 1000
|
||||||
|
|> S.flatMap (fun i -> (i -- (i+1)))
|
||||||
|
|> S.length
|
||||||
|
|> OUnit.assert_equal 2000
|
||||||
|
|
||||||
|
let test_intersperse () =
|
||||||
|
1 -- 100
|
||||||
|
|> (fun seq -> S.intersperse 0 seq)
|
||||||
|
|> S.take 10
|
||||||
|
|> S.to_list
|
||||||
|
|> OUnit.assert_equal [1;0;2;0;3;0;4;0;5;0]
|
||||||
|
|
||||||
|
let test_not_persistent () =
|
||||||
|
let printer = pp_ilist in
|
||||||
|
let stream = Stream.from (fun i -> if i < 5 then Some i else None) in
|
||||||
|
let seq = S.of_stream stream in
|
||||||
|
OUnit.assert_equal ~printer [0;1;2;3;4] (seq |> S.to_list);
|
||||||
|
OUnit.assert_equal ~printer [] (seq |> S.to_list);
|
||||||
|
()
|
||||||
|
|
||||||
|
let test_persistent () =
|
||||||
|
let printer = pp_ilist in
|
||||||
|
let stream = Stream.from (fun i -> if i < 5 then Some i else None) in
|
||||||
|
let seq = S.of_stream stream in
|
||||||
|
(* consume seq into a persistent version of itself *)
|
||||||
|
let seq' = S.persistent seq in
|
||||||
|
OUnit.assert_equal ~printer [] (seq |> S.to_list);
|
||||||
|
OUnit.assert_equal ~printer [0;1;2;3;4] (seq' |> S.to_list);
|
||||||
|
OUnit.assert_equal ~printer [0;1;2;3;4] (seq' |> S.to_list);
|
||||||
|
OUnit.assert_equal ~printer [0;1;2;3;4] (seq' |> S.to_stream |> S.of_stream |> S.to_list);
|
||||||
|
()
|
||||||
|
|
||||||
|
let test_big_persistent () =
|
||||||
|
let printer = pp_ilist in
|
||||||
|
let seq = 0 -- 10_000 in
|
||||||
|
let seq' = S.persistent seq in
|
||||||
|
OUnit.assert_equal 10_001 (S.length seq');
|
||||||
|
OUnit.assert_equal 10_001 (S.length seq');
|
||||||
|
OUnit.assert_equal ~printer [0;1;2;3] (seq' |> S.take 4 |> S.to_list);
|
||||||
|
()
|
||||||
|
|
||||||
|
let test_sort () =
|
||||||
|
1 -- 100
|
||||||
|
|> S.sort ~cmp:(fun i j -> j - i)
|
||||||
|
|> S.take 4
|
||||||
|
|> S.to_list
|
||||||
|
|> OUnit.assert_equal [100;99;98;97]
|
||||||
|
|
||||||
|
let test_sort_uniq () =
|
||||||
|
[42;1;2;3;4;5;4;3;2;1]
|
||||||
|
|> S.of_list
|
||||||
|
|> S.sort_uniq
|
||||||
|
|> S.to_list
|
||||||
|
|> OUnit.assert_equal [1;2;3;4;5;42]
|
||||||
|
|
||||||
|
let test_group () =
|
||||||
|
[1;2;3;3;2;2;3;4]
|
||||||
|
|> S.of_list |> S.group |> S.to_list
|
||||||
|
|> OUnit.assert_equal [[1];[2];[3;3];[2;2];[3];[4]]
|
||||||
|
|
||||||
|
let test_uniq () =
|
||||||
|
[1;2;2;3;4;4;4;3;3]
|
||||||
|
|> S.of_list |> S.uniq |> S.to_list
|
||||||
|
|> OUnit.assert_equal [1;2;3;4;3]
|
||||||
|
|
||||||
|
let test_product () =
|
||||||
|
let stream = Stream.from (fun i -> if i < 3 then Some i else None) in
|
||||||
|
let a = S.of_stream stream in
|
||||||
|
let b = S.of_list ["a";"b";"c"] in
|
||||||
|
let s = S.product a b |> S.map (fun (x,y) -> y,x)
|
||||||
|
|> S.to_list |> List.sort compare in
|
||||||
|
OUnit.assert_equal ["a",0; "a", 1; "a", 2;
|
||||||
|
"b",0; "b", 1; "b", 2;
|
||||||
|
"c",0; "c", 1; "c", 2;] s
|
||||||
|
|
||||||
|
let test_join () =
|
||||||
|
let s1 = (1 -- 3) in
|
||||||
|
let s2 = S.of_list ["1"; "2"] in
|
||||||
|
let join_row i j =
|
||||||
|
if string_of_int i = j then Some (string_of_int i ^ " = " ^ j) else None
|
||||||
|
in
|
||||||
|
let s = S.join ~join_row s1 s2 in
|
||||||
|
OUnit.assert_equal ["1 = 1"; "2 = 2"] (S.to_list s);
|
||||||
|
()
|
||||||
|
|
||||||
|
let test_scan () =
|
||||||
|
1 -- 5
|
||||||
|
|> S.scan (+) 0
|
||||||
|
|> S.to_list
|
||||||
|
|> OUnit.assert_equal ~printer:pp_ilist [0;1;3;6;10;15]
|
||||||
|
|
||||||
|
let test_drop () =
|
||||||
|
1 -- 5 |> S.drop 2 |> S.to_list |> OUnit.assert_equal [3;4;5]
|
||||||
|
|
||||||
|
let test_rev () =
|
||||||
|
1 -- 5 |> S.rev |> S.to_list |> OUnit.assert_equal [5;4;3;2;1]
|
||||||
|
|
||||||
|
let test_unfoldr () =
|
||||||
|
let f x = if x < 5 then Some (string_of_int x,x+1) else None in
|
||||||
|
S.unfoldr f 0
|
||||||
|
|> S.to_list
|
||||||
|
|> OUnit.assert_equal ["0"; "1"; "2"; "3"; "4"]
|
||||||
|
|
||||||
|
let test_hashtbl () =
|
||||||
|
let h = 1 -- 5
|
||||||
|
|> S.zip_i
|
||||||
|
|> S.to_hashtbl2 in
|
||||||
|
0 -- 4
|
||||||
|
|> S.iter (fun i -> OUnit.assert_equal (i+1) (Hashtbl.find h i));
|
||||||
|
OUnit.assert_equal [0;1;2;3;4] (S.hashtbl_keys h |> S.sort |> S.to_list);
|
||||||
|
()
|
||||||
|
|
||||||
|
let test_buff () =
|
||||||
|
let b = Buffer.create 4 in
|
||||||
|
"hello world"
|
||||||
|
|> S.of_str |> S.rev |> S.map Char.uppercase
|
||||||
|
|> (fun seq -> S.to_buffer seq b);
|
||||||
|
OUnit.assert_equal "DLROW OLLEH" (Buffer.contents b);
|
||||||
|
()
|
||||||
|
|
||||||
|
let test_int_range () =
|
||||||
|
OUnit.assert_equal ~printer:pp_ilist [1;2;3;4] S.(to_list (1--4));
|
||||||
|
OUnit.assert_equal ~printer:pp_ilist [10;9;8;7;6] S.(to_list (10 --^ 6));
|
||||||
|
OUnit.assert_equal ~printer:pp_ilist [] S.(to_list (10--4));
|
||||||
|
OUnit.assert_equal ~printer:pp_ilist [] S.(to_list (10 --^ 60));
|
||||||
|
()
|
||||||
|
|
||||||
|
let suite =
|
||||||
|
"test_sequence" >:::
|
||||||
|
[ "test_empty" >:: test_empty;
|
||||||
|
"test_repeat" >:: test_repeat;
|
||||||
|
"test_concat" >:: test_concat;
|
||||||
|
"test_fold" >:: test_fold;
|
||||||
|
"test_foldi" >:: test_foldi;
|
||||||
|
"test_exists" >:: test_exists;
|
||||||
|
"test_length" >:: test_length;
|
||||||
|
"test_concat" >:: test_concat;
|
||||||
|
"test_flatMap" >:: test_flatMap;
|
||||||
|
"test_intersperse" >:: test_intersperse;
|
||||||
|
"test_not_persistent" >:: test_not_persistent;
|
||||||
|
"test_persistent" >:: test_persistent;
|
||||||
|
"test_big_persistent" >:: test_big_persistent;
|
||||||
|
"test_sort" >:: test_sort;
|
||||||
|
"test_sort_uniq" >:: test_sort;
|
||||||
|
"test_group" >:: test_group;
|
||||||
|
"test_uniq" >:: test_uniq;
|
||||||
|
"test_product" >:: test_product;
|
||||||
|
"test_join" >:: test_join;
|
||||||
|
"test_scan" >:: test_scan;
|
||||||
|
"test_drop" >:: test_drop;
|
||||||
|
"test_rev" >:: test_rev;
|
||||||
|
"test_unfoldr" >:: test_unfoldr;
|
||||||
|
"test_hashtbl" >:: test_hashtbl;
|
||||||
|
"test_int_range" >:: test_int_range;
|
||||||
|
]
|
||||||
Loading…
Add table
Reference in a new issue