mirror of
https://github.com/c-cube/ocaml-containers.git
synced 2025-12-06 03:05:28 -05:00
Squashed 'gen/' content from commit 5a46e03
git-subtree-dir: gen git-subtree-split: 5a46e03b308dc0f30da2017b9fa6e3be7130b1eb
This commit is contained in:
commit
4793a6acde
20 changed files with 10448 additions and 0 deletions
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
.*.swp
|
||||
.*.swo
|
||||
_build
|
||||
*.native
|
||||
*.byte
|
||||
.session
|
||||
TAGS
|
||||
*.docdir
|
||||
setup.log
|
||||
setup.data
|
||||
qtest
|
||||
5
.merlin
Normal file
5
.merlin
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
S .
|
||||
B _build
|
||||
S tests
|
||||
B _build/tests
|
||||
PKG oUnit
|
||||
11
META
Normal file
11
META
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# OASIS_START
|
||||
# DO NOT EDIT (digest: c6b7b0973d898c3e8b7f565b701ee0d0)
|
||||
version = "0.2.2"
|
||||
description = "Simple, efficient iterators for OCaml"
|
||||
archive(byte) = "gen.cma"
|
||||
archive(byte, plugin) = "gen.cma"
|
||||
archive(native) = "gen.cmxa"
|
||||
archive(native, plugin) = "gen.cmxs"
|
||||
exists_if = "gen.cma"
|
||||
# OASIS_STOP
|
||||
|
||||
59
Makefile
Normal file
59
Makefile
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
# 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
|
||||
|
||||
push_doc: all doc
|
||||
scp -r gen.docdir/* cedeela.fr:~/simon/root/software/gen/
|
||||
|
||||
qtest-gen:
|
||||
mkdir -p qtest
|
||||
qtest extract gen.ml > qtest/run_qtest.ml
|
||||
|
||||
test-all:
|
||||
./run_tests.native
|
||||
./run_qtest.native
|
||||
|
||||
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
|
||||
32
README.md
Normal file
32
README.md
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
Gen
|
||||
===
|
||||
|
||||
Iterators for OCaml, both restartable and consumable. Performances should
|
||||
be good, yet the code is simple and straightforward.
|
||||
|
||||
The documentation can be found [here](http://cedeela.fr/~simon/software/gen)
|
||||
|
||||
## Use
|
||||
|
||||
You can either build and install the library (see `Build`), or just copy
|
||||
files to your own project. The last solution has the benefits that you
|
||||
don't have additional dependencies nor build complications (and it may enable
|
||||
more inlining). I therefore recommand it for its simplicity.
|
||||
|
||||
If you have comments, requests, or bugfixes, please share them! :-)
|
||||
|
||||
## Build
|
||||
|
||||
There are no dependencies. This should work with OCaml>=3.12.
|
||||
|
||||
$ make
|
||||
|
||||
To build and run tests (requires `oUnit`):
|
||||
|
||||
$ opam install oUnit
|
||||
$ make tests
|
||||
$ ./tests.native
|
||||
|
||||
## License
|
||||
|
||||
This code is free, under the BSD license.
|
||||
65
_oasis
Normal file
65
_oasis
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
OASISFormat: 0.3
|
||||
Name: gen
|
||||
Version: 0.2.2
|
||||
Homepage: https://github.com/c-cube/gen
|
||||
Authors: Simon Cruanes
|
||||
License: BSD3
|
||||
LicenseFile: LICENSE
|
||||
Plugins: META (0.3), DevFiles (0.3)
|
||||
BuildTools: ocamlbuild
|
||||
|
||||
Synopsis: Simple, efficient iterators for OCaml
|
||||
|
||||
Flag "bench"
|
||||
Description: build benchmark
|
||||
Default: false
|
||||
|
||||
Library "gen"
|
||||
Path: .
|
||||
Pack: false
|
||||
Modules: Gen, Gen_intf
|
||||
|
||||
Document gen
|
||||
Title: Containers docs
|
||||
Type: ocamlbuild (0.3)
|
||||
BuildTools+: ocamldoc
|
||||
Install: true
|
||||
XOCamlbuildPath: .
|
||||
XOCamlbuildLibraries: gen
|
||||
|
||||
PreBuildCommand: make qtest-gen
|
||||
|
||||
Executable run_tests
|
||||
Path: tests/
|
||||
Install: false
|
||||
CompiledObject: native
|
||||
MainIs: run_tests.ml
|
||||
Build$: flag(tests)
|
||||
BuildDepends: gen,oUnit
|
||||
|
||||
Executable run_qtest
|
||||
Path: qtest/
|
||||
Install: false
|
||||
CompiledObject: native
|
||||
MainIs: run_qtest.ml
|
||||
Build$: flag(tests)
|
||||
BuildDepends: containers, containers.misc, containers.string,
|
||||
oUnit, QTest2Lib
|
||||
|
||||
Test all
|
||||
Command: make test-all
|
||||
TestTools: run_tests, run_qtest
|
||||
Run$: flag(tests)
|
||||
|
||||
Executable bench_persistent
|
||||
Path: bench/
|
||||
Install: false
|
||||
CompiledObject: native
|
||||
MainIs: bench_persistent.ml
|
||||
Build$: flag(bench)
|
||||
BuildDepends: gen,benchmark
|
||||
|
||||
SourceRepository head
|
||||
Type: git
|
||||
Location: https://github.com/c-cube/gen
|
||||
Browser: https://github.com/c-cube/gen/tree/master/src
|
||||
43
_tags
Normal file
43
_tags
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# OASIS_START
|
||||
# DO NOT EDIT (digest: a9f4ed4316e4221c9e3cad121ae7a8a9)
|
||||
# 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
|
||||
true: annot, bin_annot
|
||||
<**/.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 gen
|
||||
"gen.cmxs": use_gen
|
||||
# Executable run_tests
|
||||
"tests/run_tests.native": pkg_oUnit
|
||||
"tests/run_tests.native": use_gen
|
||||
<tests/*.ml{,i,y}>: pkg_oUnit
|
||||
<tests/*.ml{,i,y}>: use_gen
|
||||
# Executable run_qtest
|
||||
"qtest/run_qtest.native": pkg_QTest2Lib
|
||||
"qtest/run_qtest.native": pkg_containers
|
||||
"qtest/run_qtest.native": pkg_containers.misc
|
||||
"qtest/run_qtest.native": pkg_containers.string
|
||||
"qtest/run_qtest.native": pkg_oUnit
|
||||
<qtest/*.ml{,i,y}>: pkg_QTest2Lib
|
||||
<qtest/*.ml{,i,y}>: pkg_containers
|
||||
<qtest/*.ml{,i,y}>: pkg_containers.misc
|
||||
<qtest/*.ml{,i,y}>: pkg_containers.string
|
||||
<qtest/*.ml{,i,y}>: pkg_oUnit
|
||||
# Executable bench_persistent
|
||||
"bench/bench_persistent.native": pkg_benchmark
|
||||
"bench/bench_persistent.native": use_gen
|
||||
<bench/*.ml{,i,y}>: pkg_benchmark
|
||||
<bench/*.ml{,i,y}>: use_gen
|
||||
# OASIS_STOP
|
||||
"qtest": include
|
||||
<**/*.ml>: warn_A, warn(-4), warn(-44)
|
||||
|
||||
4
bench/.merlin
Normal file
4
bench/.merlin
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
S .
|
||||
B ../_build/bench/
|
||||
REC
|
||||
PKG benchmark
|
||||
161
bench/bench_persistent.ml
Normal file
161
bench/bench_persistent.ml
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
|
||||
let _sum g =
|
||||
Gen.Restart.fold (+) 0 g
|
||||
|
||||
|
||||
module MList = struct
|
||||
type 'a t = 'a node option ref
|
||||
and 'a node = {
|
||||
content : 'a;
|
||||
mutable prev : 'a node;
|
||||
mutable next : 'a node;
|
||||
}
|
||||
|
||||
let create () = ref None
|
||||
|
||||
let is_empty d =
|
||||
match !d with
|
||||
| None -> true
|
||||
| Some _ -> false
|
||||
|
||||
let push_back d x =
|
||||
match !d with
|
||||
| None ->
|
||||
let rec elt = {
|
||||
content = x; prev = elt; next = elt; } in
|
||||
d := Some elt
|
||||
| Some first ->
|
||||
let elt = { content = x; next=first; prev=first.prev; } in
|
||||
first.prev.next <- elt;
|
||||
first.prev <- elt
|
||||
|
||||
(* conversion to gen *)
|
||||
let to_gen d =
|
||||
fun () ->
|
||||
match !d with
|
||||
| None -> (fun () -> None)
|
||||
| Some first ->
|
||||
let cur = ref first in (* current element of the list *)
|
||||
let stop = ref false in (* are we done yet? *)
|
||||
fun () ->
|
||||
if !stop then None
|
||||
else begin
|
||||
let x = (!cur).content in
|
||||
cur := (!cur).next;
|
||||
(if !cur == first then stop := true); (* EOG, we made a full cycle *)
|
||||
Some x
|
||||
end
|
||||
end
|
||||
|
||||
(** Store content of the generator in an enum *)
|
||||
let persistent_mlist gen =
|
||||
let l = MList.create () in
|
||||
Gen.iter (MList.push_back l) gen;
|
||||
MList.to_gen l
|
||||
|
||||
let bench_mlist n =
|
||||
for _i = 0 to 100 do
|
||||
let g = persistent_mlist Gen.(1 -- n) in
|
||||
ignore (_sum g)
|
||||
done
|
||||
|
||||
(** {6 Unrolled mutable list} *)
|
||||
module UnrolledList = struct
|
||||
type 'a node =
|
||||
| Nil
|
||||
| Partial of 'a array * int
|
||||
| Cons of 'a array * 'a node ref
|
||||
|
||||
let of_gen gen =
|
||||
let start = ref Nil in
|
||||
let chunk_size = ref 16 in
|
||||
let rec fill prev cur =
|
||||
match cur, gen() with
|
||||
| Partial (a,n), None ->
|
||||
prev := Cons (Array.sub a 0 n, ref Nil); () (* done *)
|
||||
| _, None -> prev := cur; () (* done *)
|
||||
| Nil, Some x ->
|
||||
let n = !chunk_size in
|
||||
if n < 4096 then chunk_size := 2 * !chunk_size;
|
||||
fill prev (Partial (Array.make n x, 1))
|
||||
| Partial (a, n), Some x ->
|
||||
assert (n < Array.length a);
|
||||
a.(n) <- x;
|
||||
if n+1 = Array.length a
|
||||
then begin
|
||||
let r = ref Nil in
|
||||
prev := Cons(a, r);
|
||||
fill r Nil
|
||||
end else fill prev (Partial (a, n+1))
|
||||
| Cons _, _ -> assert false
|
||||
in
|
||||
fill start !start ;
|
||||
!start
|
||||
|
||||
let to_gen l () =
|
||||
let cur = ref l in
|
||||
let i = ref 0 in
|
||||
let rec next() = match !cur with
|
||||
| Nil -> None
|
||||
| Cons (a,l') ->
|
||||
if !i = Array.length a
|
||||
then begin
|
||||
cur := !l';
|
||||
i := 0;
|
||||
next()
|
||||
end else begin
|
||||
let y = a.(!i) in
|
||||
incr i;
|
||||
Some y
|
||||
end
|
||||
| Partial _ -> assert false
|
||||
in
|
||||
next
|
||||
end
|
||||
|
||||
(** Store content of the generator in an enum *)
|
||||
let persistent_unrolled gen =
|
||||
let l = UnrolledList.of_gen gen in
|
||||
UnrolledList.to_gen l
|
||||
|
||||
let bench_unrolled n =
|
||||
for _i = 0 to 100 do
|
||||
let g = persistent_unrolled Gen.(1 -- n) in
|
||||
ignore (_sum g)
|
||||
done
|
||||
|
||||
let bench_naive n =
|
||||
for _i = 0 to 100 do
|
||||
let l = Gen.to_rev_list Gen.(1 -- n) in
|
||||
let g = Gen.Restart.of_list (List.rev l) in
|
||||
ignore (_sum g)
|
||||
done
|
||||
|
||||
let bench_current n =
|
||||
for _i = 0 to 100 do
|
||||
let g = Gen.persistent Gen.(1 -- n) in
|
||||
ignore (_sum g)
|
||||
done
|
||||
|
||||
let bench_current_lazy n =
|
||||
for _i = 0 to 100 do
|
||||
let g = Gen.persistent_lazy Gen.(1 -- n) in
|
||||
ignore (_sum g)
|
||||
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
|
||||
; "unrolled", bench_unrolled, n
|
||||
; "current", bench_current, n
|
||||
; "current_lazy", bench_current_lazy, n
|
||||
]
|
||||
in Benchmark.tabulate res
|
||||
in
|
||||
bench_n 100;
|
||||
bench_n 100_000;
|
||||
()
|
||||
|
||||
27
configure
vendored
Executable file
27
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
|
||||
5
gen.mldylib
Normal file
5
gen.mldylib
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# OASIS_START
|
||||
# DO NOT EDIT (digest: f69818d114f140be18d72c90abdc60e8)
|
||||
Gen
|
||||
Gen_intf
|
||||
# OASIS_STOP
|
||||
102
gen.mli
Normal file
102
gen.mli
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
(*
|
||||
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 Generators}
|
||||
|
||||
Values of type ['a Gen.t] represent a possibly infinite sequence of values
|
||||
of type 'a. One can only iterate once on the sequence, as it is consumed
|
||||
by iteration/deconstruction/access. [None] is returned when the generator
|
||||
is exhausted.
|
||||
|
||||
The submodule {!Restart} provides utilities to work with
|
||||
{b restartable generators}, that is, functions [unit -> 'a Gen.t] that
|
||||
allow to build as many generators from the same source as needed.
|
||||
*)
|
||||
|
||||
(** {2 Global type declarations} *)
|
||||
|
||||
type 'a t = unit -> 'a option
|
||||
(** A generator may be called several times, yielding the next value
|
||||
each time. It returns [None] when no elements remain *)
|
||||
|
||||
type 'a gen = 'a t
|
||||
|
||||
module type S = Gen_intf.S
|
||||
|
||||
(** {2 Transient generators} *)
|
||||
|
||||
val get : 'a t -> 'a option
|
||||
(** Get the next value *)
|
||||
|
||||
val next : 'a t -> 'a option
|
||||
(** Synonym for {!get} *)
|
||||
|
||||
val get_exn : 'a t -> 'a
|
||||
(** Get the next value, or fails
|
||||
@raise Invalid_argument if no element remains *)
|
||||
|
||||
val junk : 'a t -> unit
|
||||
(** Drop the next value, discarding it. *)
|
||||
|
||||
val repeatedly : (unit -> 'a) -> 'a t
|
||||
(** Call the same function an infinite number of times (useful for instance
|
||||
if the function is a random generator). *)
|
||||
|
||||
include S with type 'a t := 'a gen
|
||||
(** Operations on {b transient} generators *)
|
||||
|
||||
(** {2 Restartable generators} *)
|
||||
|
||||
module Restart : sig
|
||||
type 'a t = unit -> 'a gen
|
||||
|
||||
type 'a restartable = 'a t
|
||||
|
||||
include S with type 'a t := 'a restartable
|
||||
|
||||
val cycle : 'a t -> 'a t
|
||||
(** Cycle through the enum, endlessly. The enum must not be empty. *)
|
||||
|
||||
val lift : ('a gen -> 'b) -> 'a t -> 'b
|
||||
|
||||
val lift2 : ('a gen -> 'b gen -> 'c) -> 'a t -> 'b t -> 'c
|
||||
end
|
||||
|
||||
(** {2 Utils} *)
|
||||
|
||||
val persistent : 'a t -> 'a Restart.t
|
||||
(** Store content of the transient generator in memory, to be able to iterate
|
||||
on it several times later. If possible, consider using combinators
|
||||
from {!Restart} directly instead. *)
|
||||
|
||||
val persistent_lazy : 'a t -> 'a Restart.t
|
||||
(** Same as {!persistent}, but consumes the generator on demand (by chunks).
|
||||
This allows to make a restartable generator out of an ephemeral one,
|
||||
without paying a big cost upfront (nor even consuming it fully).
|
||||
@since 0.2.2 *)
|
||||
|
||||
val start : 'a Restart.t -> 'a t
|
||||
(** Create a new transient generator.
|
||||
[start gen] is the same as [gen ()] but is included for readability. *)
|
||||
5
gen.mllib
Normal file
5
gen.mllib
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# OASIS_START
|
||||
# DO NOT EDIT (digest: f69818d114f140be18d72c90abdc60e8)
|
||||
Gen
|
||||
Gen_intf
|
||||
# OASIS_STOP
|
||||
5
gen.odocl
Normal file
5
gen.odocl
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# OASIS_START
|
||||
# DO NOT EDIT (digest: f69818d114f140be18d72c90abdc60e8)
|
||||
Gen
|
||||
Gen_intf
|
||||
# OASIS_STOP
|
||||
321
gen_intf.ml
Normal file
321
gen_intf.ml
Normal file
|
|
@ -0,0 +1,321 @@
|
|||
(*
|
||||
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 Common signature for transient and restartable generators}
|
||||
|
||||
The signature {!S} abstracts on a type ['a t], where the [t] can be
|
||||
the type of transient or restartable generators. Some functions specify
|
||||
explicitely that they use ['a gen] (transient generators). *)
|
||||
|
||||
type 'a gen = unit -> 'a option
|
||||
|
||||
module type S = sig
|
||||
type 'a t
|
||||
|
||||
val empty : 'a t
|
||||
(** Empty generator, with no elements *)
|
||||
|
||||
val singleton : 'a -> 'a t
|
||||
(** One-element generator *)
|
||||
|
||||
val repeat : 'a -> 'a t
|
||||
(** Repeat same element endlessly *)
|
||||
|
||||
val iterate : 'a -> ('a -> 'a) -> 'a t
|
||||
(** [iterate x f] is [[x; f x; f (f x); f (f (f x)); ...]] *)
|
||||
|
||||
val unfold : ('b -> ('a * 'b) option) -> 'b -> 'a t
|
||||
(** Dual of {!fold}, with a deconstructing operation. It keeps on
|
||||
unfolding the ['b] value into a new ['b], and a ['a] which is yielded,
|
||||
until [None] is returned. *)
|
||||
|
||||
val init : ?limit:int -> (int -> 'a) -> 'a t
|
||||
(** Calls the function, starting from 0, on increasing indices.
|
||||
If [limit] is provided and is a positive int, iteration will
|
||||
stop at the limit (excluded).
|
||||
For instance [init ~limit:4 id] will yield 0, 1, 2, and 3. *)
|
||||
|
||||
(** {2 Basic combinators} *)
|
||||
|
||||
val is_empty : _ t -> bool
|
||||
(** Check whether the enum is empty. Pops an element, if any *)
|
||||
|
||||
val fold : ('b -> 'a -> 'b) -> 'b -> 'a t -> 'b
|
||||
(** Fold on the generator, tail-recursively. Consumes the generator. *)
|
||||
|
||||
val reduce : ('a -> 'a -> 'a) -> 'a t -> 'a
|
||||
(** Fold on non-empty sequences. Consumes the generator.
|
||||
@raise Invalid_argument on an empty gen *)
|
||||
|
||||
val scan : ('b -> 'a -> 'b) -> 'b -> 'a t -> 'b t
|
||||
(** Like {!fold}, but keeping successive values of the accumulator.
|
||||
Consumes the generator. *)
|
||||
|
||||
val unfold_scan : ('b -> 'a -> 'b * 'c) -> 'b -> 'a t -> 'c t
|
||||
(** A mix of {!unfold} and {!scan}. The current state is combined with
|
||||
the current element to produce a new state, and an output value
|
||||
of type 'c.
|
||||
@since 0.2.2 *)
|
||||
|
||||
val iter : ('a -> unit) -> 'a t -> unit
|
||||
(** Iterate on the enum, consumes it. *)
|
||||
|
||||
val iteri : (int -> 'a -> unit) -> 'a t -> unit
|
||||
(** Iterate on elements with their index in the enum, from 0, consuming it. *)
|
||||
|
||||
val length : _ t -> int
|
||||
(** Length of an enum (linear time), consuming it *)
|
||||
|
||||
val map : ('a -> 'b) -> 'a t -> 'b t
|
||||
(** Lazy map. No iteration is performed now, the function will be called
|
||||
when the result is traversed. *)
|
||||
|
||||
val append : 'a t -> 'a t -> 'a t
|
||||
(** Append the two enums; the result contains the elements of the first,
|
||||
then the elements of the second enum. *)
|
||||
|
||||
val flatten : 'a gen t -> 'a t
|
||||
(** Flatten the enumeration of generators *)
|
||||
|
||||
val flat_map : ('a -> 'b gen) -> 'a t -> 'b t
|
||||
(** Monadic bind; each element is transformed to a sub-enum
|
||||
which is then iterated on, before the next element is processed,
|
||||
and so on. *)
|
||||
|
||||
val mem : ?eq:('a -> 'a -> bool) -> 'a -> 'a t -> bool
|
||||
(** Is the given element, member of the enum? *)
|
||||
|
||||
val take : int -> 'a t -> 'a t
|
||||
(** Take at most n elements *)
|
||||
|
||||
val drop : int -> 'a t -> 'a t
|
||||
(** Drop n elements *)
|
||||
|
||||
val nth : int -> 'a t -> 'a
|
||||
(** n-th element, or Not_found
|
||||
@raise Not_found if the generator contains less than [n] arguments *)
|
||||
|
||||
val take_nth : int -> 'a t -> 'a t
|
||||
(** [take_nth n g] returns every element of [g] whose index
|
||||
is a multiple of [n]. For instance [take_nth 2 (1--10) |> to_list]
|
||||
will return [1;3;5;7;9] *)
|
||||
|
||||
val filter : ('a -> bool) -> 'a t -> 'a t
|
||||
(** Filter out elements that do not satisfy the predicate. *)
|
||||
|
||||
val take_while : ('a -> bool) -> 'a t -> 'a t
|
||||
(** Take elements while they satisfy the predicate *)
|
||||
|
||||
val drop_while : ('a -> bool) -> 'a t -> 'a t
|
||||
(** Drop elements while they satisfy the predicate *)
|
||||
|
||||
val filter_map : ('a -> 'b option) -> 'a t -> 'b t
|
||||
(** Maps some elements to 'b, drop the other ones *)
|
||||
|
||||
val zip_index : 'a t -> (int * 'a) t
|
||||
(** Zip elements with their index in the enum *)
|
||||
|
||||
val unzip : ('a * 'b) t -> 'a t * 'b t
|
||||
(** Unzip into two sequences, splitting each pair *)
|
||||
|
||||
val partition : ('a -> bool) -> 'a t -> 'a t * 'a t
|
||||
(** [partition p l] returns the elements that satisfy [p],
|
||||
and the elements that do not satisfy [p] *)
|
||||
|
||||
val for_all : ('a -> bool) -> 'a t -> bool
|
||||
(** Is the predicate true for all elements? *)
|
||||
|
||||
val exists : ('a -> bool) -> 'a t -> bool
|
||||
(** Is the predicate true for at least one element? *)
|
||||
|
||||
val min : ?lt:('a -> 'a -> bool) -> 'a t -> 'a
|
||||
(** Minimum element, according to the given comparison function.
|
||||
@raise Invalid_argument if the generator is empty *)
|
||||
|
||||
val max : ?lt:('a -> 'a -> bool) -> 'a t -> 'a
|
||||
(** Maximum element, see {!min}
|
||||
@raise Invalid_argument if the generator is empty *)
|
||||
|
||||
val eq : ?eq:('a -> 'a -> bool) -> 'a t -> 'a t -> bool
|
||||
(** Equality of generators. *)
|
||||
|
||||
val lexico : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t -> int
|
||||
(** Lexicographic comparison of generators. If a generator is a prefix
|
||||
of the other one, it is considered smaller. *)
|
||||
|
||||
val compare : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t -> int
|
||||
(** Synonym for {! lexico} *)
|
||||
|
||||
val find : ('a -> bool) -> 'a t -> 'a option
|
||||
(** [find p e] returns the first element of [e] to satisfy [p],
|
||||
or None. *)
|
||||
|
||||
val sum : int t -> int
|
||||
(** Sum of all elements *)
|
||||
|
||||
(** {2 Multiple iterators} *)
|
||||
|
||||
val map2 : ('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c t
|
||||
(** Map on the two sequences. Stops once one of them is exhausted.*)
|
||||
|
||||
val iter2 : ('a -> 'b -> unit) -> 'a t -> 'b t -> unit
|
||||
(** Iterate on the two sequences. Stops once one of them is exhausted.*)
|
||||
|
||||
val fold2 : ('acc -> 'a -> 'b -> 'acc) -> 'acc -> 'a t -> 'b t -> 'acc
|
||||
(** Fold the common prefix of the two iterators *)
|
||||
|
||||
val for_all2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool
|
||||
(** Succeeds if all pairs of elements satisfy the predicate.
|
||||
Ignores elements of an iterator if the other runs dry. *)
|
||||
|
||||
val exists2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool
|
||||
(** Succeeds if some pair of elements satisfy the predicate.
|
||||
Ignores elements of an iterator if the other runs dry. *)
|
||||
|
||||
val zip_with : ('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c t
|
||||
(** Combine common part of the enums (stops when one is exhausted) *)
|
||||
|
||||
val zip : 'a t -> 'b t -> ('a * 'b) t
|
||||
(** Zip together the common part of the enums *)
|
||||
|
||||
(** {2 Complex combinators} *)
|
||||
|
||||
val merge : 'a gen t -> 'a t
|
||||
(** Pick elements fairly in each sub-generator. The merge of enums
|
||||
[e1, e2, ... ] picks elements in [e1], [e2],
|
||||
in [e3], [e1], [e2] .... Once a generator is empty, it is skipped;
|
||||
when they are all empty, and none remains in the input,
|
||||
their merge is also empty.
|
||||
For instance, [merge [1;3;5] [2;4;6]] will be, in disorder, [1;2;3;4;5;6]. *)
|
||||
|
||||
val intersection : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t -> 'a t
|
||||
(** Intersection of two sorted sequences. Only elements that occur in both
|
||||
inputs appear in the output *)
|
||||
|
||||
val sorted_merge : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t -> 'a t
|
||||
(** Merge two sorted sequences into a sorted sequence *)
|
||||
|
||||
val sorted_merge_n : ?cmp:('a -> 'a -> int) -> 'a t list -> 'a t
|
||||
(** Sorted merge of multiple sorted sequences *)
|
||||
|
||||
val tee : ?n:int -> 'a t -> 'a gen list
|
||||
(** Duplicate the enum into [n] generators (default 2). The generators
|
||||
share the same underlying instance of the enum, so the optimal case is
|
||||
when they are consumed evenly *)
|
||||
|
||||
val round_robin : ?n:int -> 'a t -> 'a gen list
|
||||
(** Split the enum into [n] generators in a fair way. Elements with
|
||||
[index = k mod n] with go to the k-th enum. [n] default value
|
||||
is 2. *)
|
||||
|
||||
val interleave : 'a t -> 'a t -> 'a t
|
||||
(** [interleave a b] yields an element of [a], then an element of [b],
|
||||
and so on. When a generator is exhausted, this behaves like the
|
||||
other generator. *)
|
||||
|
||||
val intersperse : 'a -> 'a t -> 'a t
|
||||
(** Put the separator element between all elements of the given enum *)
|
||||
|
||||
val product : 'a t -> 'b t -> ('a * 'b) t
|
||||
(** Cartesian product, in no predictable order. Works even if some of the
|
||||
arguments are infinite. *)
|
||||
|
||||
val group : ?eq:('a -> 'a -> bool) -> 'a t -> 'a list t
|
||||
(** Group equal consecutive elements together. *)
|
||||
|
||||
val uniq : ?eq:('a -> 'a -> bool) -> 'a t -> 'a t
|
||||
(** Remove consecutive duplicate elements. Basically this is
|
||||
like [fun e -> map List.hd (group e)]. *)
|
||||
|
||||
val sort : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t
|
||||
(** Sort according to the given comparison function. The enum must be finite. *)
|
||||
|
||||
val sort_uniq : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t
|
||||
(** Sort and remove duplicates. The enum must be finite. *)
|
||||
|
||||
val chunks : int -> 'a t -> 'a array t
|
||||
(** [chunks n e] returns a generator of arrays of length [n], composed
|
||||
of successive elements of [e]. The last array may be smaller
|
||||
than [n] *)
|
||||
|
||||
val permutations : 'a t -> 'a list t
|
||||
(** Permutations of the enum.
|
||||
@since 0.2.2 *)
|
||||
|
||||
val combinations : int -> 'a t -> 'a list t
|
||||
(** Combinations of given length. The ordering of the elements within
|
||||
each combination is unspecified.
|
||||
Example (ignoring ordering):
|
||||
[combinations 2 (1--3) |> to_list = [[1;2]; [1;3]; [2;3]]]
|
||||
@since 0.2.2 *)
|
||||
|
||||
val power_set : 'a t -> 'a list t
|
||||
(** All subsets of the enum (in no particular order). The ordering of
|
||||
the elements within each subset is unspecified.
|
||||
@since 0.2.2 *)
|
||||
|
||||
(** {2 Basic conversion functions} *)
|
||||
|
||||
val of_list : 'a list -> 'a t
|
||||
(** Enumerate elements of the list *)
|
||||
|
||||
val to_list : 'a t -> 'a list
|
||||
(** non tail-call trasnformation to list, in the same order *)
|
||||
|
||||
val to_rev_list : 'a t -> 'a list
|
||||
(** Tail call conversion to list, in reverse order (more efficient) *)
|
||||
|
||||
val to_array : 'a t -> 'a array
|
||||
(** Convert the enum to an array (not very efficient) *)
|
||||
|
||||
val of_array : ?start:int -> ?len:int -> 'a array -> 'a t
|
||||
(** Iterate on (a slice of) the given array *)
|
||||
|
||||
val rand_int : int -> int t
|
||||
(** Random ints in the given range. *)
|
||||
|
||||
val int_range : int -> int -> int t
|
||||
(** [int_range a b] enumerates integers between [a] and [b], included. [a]
|
||||
is assumed to be smaller than [b]. *)
|
||||
|
||||
module Infix : sig
|
||||
val (--) : int -> int -> int t
|
||||
(** Synonym for {! int_range} *)
|
||||
|
||||
val (>>=) : 'a t -> ('a -> 'b gen) -> 'b t
|
||||
(** Monadic bind operator *)
|
||||
end
|
||||
|
||||
val (--) : int -> int -> int t
|
||||
(** Synonym for {! int_range} *)
|
||||
|
||||
val (>>=) : 'a t -> ('a -> 'b gen) -> 'b t
|
||||
(** Monadic bind operator *)
|
||||
|
||||
val pp : ?start:string -> ?stop:string -> ?sep:string -> ?horizontal:bool ->
|
||||
(Format.formatter -> 'a -> unit) -> Format.formatter -> 'a t -> unit
|
||||
(** Pretty print the content of the generator on a formatter. *)
|
||||
end
|
||||
|
||||
623
myocamlbuild.ml
Normal file
623
myocamlbuild.ml
Normal file
|
|
@ -0,0 +1,623 @@
|
|||
(* OASIS_START *)
|
||||
(* DO NOT EDIT (digest: 8b03085ed54d5ff9a8cbd756150607bd) *)
|
||||
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
|
||||
|
||||
type conf =
|
||||
{ no_automatic_syntax: bool;
|
||||
}
|
||||
|
||||
(* 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 (exec_from_conf "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 conf =
|
||||
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";
|
||||
|
||||
if not (conf.no_automatic_syntax) then begin
|
||||
(* 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, pargs) =
|
||||
(* 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, syn_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;
|
||||
|
||||
(* TODO: Check if this is allowed for OCaml < 3.12.1 *)
|
||||
flag ["ocaml"; "compile"; "package("^pkg^")"] & S pargs;
|
||||
flag ["ocaml"; "ocamldep"; "package("^pkg^")"] & S pargs;
|
||||
flag ["ocaml"; "doc"; "package("^pkg^")"] & S pargs;
|
||||
flag ["ocaml"; "infer_interface"; "package("^pkg^")"] & S pargs;
|
||||
end
|
||||
(find_packages ());
|
||||
end;
|
||||
|
||||
(* 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.
|
||||
This holds both for programs and for libraries.
|
||||
*)
|
||||
dep ["link"; "ocaml"; tag_libstubs lib]
|
||||
[dir/"lib"^(nm_libstubs lib)^"."^(!Options.ext_lib)];
|
||||
|
||||
dep ["compile"; "ocaml"; 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 conf t =
|
||||
dispatch_combine
|
||||
[
|
||||
dispatch t;
|
||||
MyOCamlbuildFindlib.dispatch conf;
|
||||
]
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
# 606 "myocamlbuild.ml"
|
||||
open Ocamlbuild_plugin;;
|
||||
let package_default =
|
||||
{
|
||||
MyOCamlbuildBase.lib_ocaml = [("gen", [], [])];
|
||||
lib_c = [];
|
||||
flags = [];
|
||||
includes = []
|
||||
}
|
||||
;;
|
||||
|
||||
let conf = {MyOCamlbuildFindlib.no_automatic_syntax = false}
|
||||
|
||||
let dispatch_default = MyOCamlbuildBase.dispatch_default conf package_default;;
|
||||
|
||||
# 622 "myocamlbuild.ml"
|
||||
(* OASIS_STOP *)
|
||||
Ocamlbuild_plugin.dispatch dispatch_default;;
|
||||
4
tests/run_tests.ml
Normal file
4
tests/run_tests.ml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
let () =
|
||||
let _ = OUnit.run_test_tt_main Test_gen.suite in
|
||||
()
|
||||
146
tests/test_gen.ml
Normal file
146
tests/test_gen.ml
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
|
||||
open OUnit
|
||||
open Gen.Infix
|
||||
|
||||
module GR = Gen.Restart
|
||||
|
||||
let pint i = string_of_int i
|
||||
let pilist l =
|
||||
let b = Buffer.create 15 in
|
||||
let fmt = Format.formatter_of_buffer b in
|
||||
Format.fprintf fmt "%a@?"
|
||||
(Gen.pp Format.pp_print_int) (Gen.of_list l);
|
||||
Buffer.contents b
|
||||
let pi2list l =
|
||||
let b = Buffer.create 15 in
|
||||
let fmt = Format.formatter_of_buffer b in
|
||||
Format.fprintf fmt "%a@?"
|
||||
(Gen.pp (fun fmt (a,b) -> Format.fprintf fmt "%d,%d" a b))
|
||||
(Gen.of_list l);
|
||||
Buffer.contents b
|
||||
let pstrlist l =
|
||||
let b = Buffer.create 15 in
|
||||
let fmt = Format.formatter_of_buffer b in
|
||||
Format.fprintf fmt "%a@?"
|
||||
(Gen.pp Format.pp_print_string) (Gen.of_list l);
|
||||
Buffer.contents b
|
||||
|
||||
let test_singleton () =
|
||||
let gen = Gen.singleton 42 in
|
||||
OUnit.assert_equal (Some 42) (Gen.get gen);
|
||||
OUnit.assert_equal None (Gen.get gen);
|
||||
let gen = Gen.singleton 42 in
|
||||
OUnit.assert_equal 1 (Gen.length gen);
|
||||
()
|
||||
|
||||
let test_iter () =
|
||||
let e = GR.(1 -- 10) in
|
||||
OUnit.assert_equal ~printer:pint 10 (GR.length e);
|
||||
OUnit.assert_equal [1;2] GR.(to_list (1 -- 2));
|
||||
OUnit.assert_equal [1;2;3;4;5] (GR.to_list (GR.take 5 e));
|
||||
()
|
||||
|
||||
let test_map () =
|
||||
let e = 1 -- 10 in
|
||||
let e' = Gen.map string_of_int e in
|
||||
OUnit.assert_equal ~printer:pstrlist ["9"; "10"] (Gen.to_list (Gen.drop 8 e'));
|
||||
()
|
||||
|
||||
let test_append () =
|
||||
let e = Gen.append (1 -- 5) (6 -- 10) in
|
||||
OUnit.assert_equal [10;9;8;7;6;5;4;3;2;1] (Gen.to_rev_list e);
|
||||
()
|
||||
|
||||
let test_flat_map () =
|
||||
let e = 1 -- 3 in
|
||||
let e' = e >>= (fun x -> x -- (x+1)) in
|
||||
OUnit.assert_equal [1;2;2;3;3;4] (Gen.to_list e');
|
||||
()
|
||||
|
||||
let test_zip () =
|
||||
let e = Gen.zip_with (+) (Gen.repeat 1) (4--7) in
|
||||
OUnit.assert_equal [5;6;7;8] (Gen.to_list e);
|
||||
()
|
||||
|
||||
let test_filter_map () =
|
||||
let f x = if x mod 2 = 0 then Some (string_of_int x) else None in
|
||||
let e = Gen.filter_map f (1 -- 10) in
|
||||
OUnit.assert_equal ["2"; "4"; "6"; "8"; "10"] (Gen.to_list e);
|
||||
()
|
||||
|
||||
let test_merge () =
|
||||
let e = Gen.of_list [1--3; 4--6; 7--9] in
|
||||
let e' = Gen.merge e in
|
||||
OUnit.assert_equal [1;2;3;4;5;6;7;8;9] (Gen.to_list e' |> List.sort compare);
|
||||
()
|
||||
|
||||
let test_persistent () =
|
||||
let i = ref 0 in
|
||||
let gen () =
|
||||
let j = !i in
|
||||
if j > 5 then None else (incr i; Some j)
|
||||
in
|
||||
let e = Gen.persistent gen in
|
||||
OUnit.assert_equal [0;1;2;3;4;5] (GR.to_list e);
|
||||
OUnit.assert_equal [0;1;2;3;4;5] (GR.to_list e);
|
||||
OUnit.assert_equal [0;1;2;3;4;5] (GR.to_list e);
|
||||
()
|
||||
|
||||
let test_round_robin () =
|
||||
let e = GR.round_robin ~n:2 GR.(1--10) in
|
||||
match e with
|
||||
| [a;b] ->
|
||||
OUnit.assert_equal [1;3;5;7;9] (Gen.to_list a);
|
||||
OUnit.assert_equal [2;4;6;8;10] (Gen.to_list b)
|
||||
| _ -> OUnit.assert_failure "wrong list lenght"
|
||||
|
||||
let test_big_rr () =
|
||||
let e = GR.round_robin ~n:3 GR.(1 -- 999) in
|
||||
let l = List.map Gen.length e in
|
||||
OUnit.assert_equal [333;333;333] l;
|
||||
()
|
||||
|
||||
let test_merge_sorted () =
|
||||
[Gen.of_list [1;3;5]; Gen.of_list [0;1;1;3;4;6;10]; Gen.of_list [2;2;11]]
|
||||
|> Gen.sorted_merge_n ?cmp:None
|
||||
|> Gen.to_list
|
||||
|> OUnit.assert_equal ~printer:pilist [0;1;1;1;2;2;3;3;4;5;6;10;11]
|
||||
|
||||
let test_interleave () =
|
||||
let e1 = Gen.of_list [1;3;5;7;9] in
|
||||
let e2 = Gen.of_list [2;4;6;8;10] in
|
||||
let e = Gen.interleave e1 e2 in
|
||||
OUnit.assert_equal [1;2;3;4;5;6;7;8;9;10] (Gen.to_list e);
|
||||
()
|
||||
|
||||
let test_intersperse () =
|
||||
let e = 1 -- 5 in
|
||||
let e' = Gen.intersperse 0 e in
|
||||
OUnit.assert_equal [1;0;2;0;3;0;4;0;5] (Gen.to_list e');
|
||||
()
|
||||
|
||||
let test_product () =
|
||||
let printer = pi2list in
|
||||
let e = Gen.product (1--3) (4--5) in
|
||||
OUnit.assert_equal ~printer [1,4; 1,5; 2,4; 2,5; 3,4; 3,5]
|
||||
(List.sort compare (Gen.to_list e));
|
||||
()
|
||||
|
||||
let suite =
|
||||
"test_gen" >:::
|
||||
[ "test_singleton" >:: test_singleton;
|
||||
"test_iter" >:: test_iter;
|
||||
"test_map" >:: test_map;
|
||||
"test_append" >:: test_append;
|
||||
"test_flat_map" >:: test_flat_map;
|
||||
"test_zip" >:: test_zip;
|
||||
"test_filter_map" >:: test_filter_map;
|
||||
"test_merge" >:: test_merge;
|
||||
"test_persistent" >:: test_persistent;
|
||||
"test_round_robin" >:: test_round_robin;
|
||||
"test_big_rr" >:: test_big_rr;
|
||||
"test_merge_sorted" >:: test_merge_sorted;
|
||||
"test_interleave" >:: test_interleave;
|
||||
"test_intersperse" >:: test_intersperse;
|
||||
"test_product" >:: test_product;
|
||||
]
|
||||
Loading…
Add table
Reference in a new issue