summaryrefslogtreecommitdiff
path: root/lib/hashcons.ml
diff options
context:
space:
mode:
Diffstat (limited to 'lib/hashcons.ml')
-rw-r--r--lib/hashcons.ml199
1 files changed, 199 insertions, 0 deletions
diff --git a/lib/hashcons.ml b/lib/hashcons.ml
new file mode 100644
index 00000000..5f083459
--- /dev/null
+++ b/lib/hashcons.ml
@@ -0,0 +1,199 @@
+(************************************************************************)
+(* v * The Coq Proof Assistant / The Coq Development Team *)
+(* <O___,, * CNRS-Ecole Polytechnique-INRIA Futurs-Universite Paris Sud *)
+(* \VV/ **************************************************************)
+(* // * This file is distributed under the terms of the *)
+(* * GNU Lesser General Public License Version 2.1 *)
+(************************************************************************)
+
+(* $Id: hashcons.ml,v 1.3.16.1 2004/07/16 19:30:30 herbelin Exp $ *)
+
+(* Hash consing of datastructures *)
+
+(* The generic hash-consing functions (does not use Obj) *)
+
+(* [t] is the type of object to hash-cons
+ * [u] is the type of hash-cons functions for the sub-structures
+ * of objects of type t (u usually has the form (t1->t1)*(t2->t2)*...).
+ * [hash_sub u x] is a function that hash-cons the sub-structures of x using
+ * the hash-consing functions u provides.
+ * [equal] is a comparison function. It is allowed to use physical equality
+ * on the sub-terms hash-consed by the hash_sub function.
+ * [hash] is the hash function given to the Hashtbl.Make function
+ *
+ * Note that this module type coerces to the argument of Hashtbl.Make.
+ *)
+
+module type Comp =
+ sig
+ type t
+ type u
+ val hash_sub : u -> t -> t
+ val equal : t -> t -> bool
+ val hash : t -> int
+ end
+
+(* The output is a function f such that
+ * [f ()] has the side-effect of creating (internally) a hash-table of the
+ * hash-consed objects. The result is a function taking the sub-hashcons
+ * functions and an object, and hashcons it. It does not really make sense
+ * to call f() with different sub-hcons functions. That's why we use the
+ * wrappers simple_hcons, recursive_hcons, ... The latter just take as
+ * argument the sub-hcons functions (the tables are created at that moment),
+ * and returns the hcons function for t.
+ *)
+
+module type S =
+ sig
+ type t
+ type u
+ val f : unit -> (u -> t -> t)
+ end
+
+module Make(X:Comp) =
+ struct
+ type t = X.t
+ type u = X.u
+
+ (* We create the type of hashtables for t, with our comparison fun.
+ * An invariant is that the table never contains two entries equals
+ * w.r.t (=), although the equality on keys is X.equal. This is
+ * granted since we hcons the subterms before looking up in the table.
+ *)
+ module Htbl = Hashtbl.Make(
+ struct type t=X.t
+ type u=X.u
+ let hash=X.hash
+ let equal x1 x2 = (*incr comparaison;*) X.equal x1 x2
+ end)
+
+ (* The table is created when () is applied.
+ * Hashconsing is then very simple:
+ * 1- hashcons the subterms using hash_sub and u
+ * 2- look up in the table, if we do not get a hit, we add it
+ *)
+ let f () =
+ let tab = Htbl.create 97 in
+ (fun u x ->
+ let y = X.hash_sub u x in
+ (* incr acces;*)
+ try let r = Htbl.find tab y in(* incr succes;*) r
+ with Not_found -> Htbl.add tab y y; y)
+ end
+
+(* A few usefull wrappers:
+ * takes as argument the function f above and build a function of type
+ * u -> t -> t that creates a fresh table each time it is applied to the
+ * sub-hcons functions. *)
+
+(* For non-recursive types it is quite easy. *)
+let simple_hcons h u = h () u
+
+(* For a recursive type T, we write the module of sig Comp with u equals
+ * to (T -> T) * u0
+ * The first component will be used to hash-cons the recursive subterms
+ * The second one to hashcons the other sub-structures.
+ * We just have to take the fixpoint of h
+ *)
+let recursive_hcons h u =
+ let hc = h () in
+ let rec hrec x = hc (hrec,u) x in
+ hrec
+
+(* If the structure may contain loops, use this one. *)
+let recursive_loop_hcons h u =
+ let hc = h () in
+ let rec hrec visited x =
+ if List.memq x visited then x
+ else hc (hrec (x::visited),u) x
+ in
+ hrec []
+
+(* For 2 mutually recursive types *)
+let recursive2_hcons h1 h2 u1 u2 =
+ let hc1 = h1 () in
+ let hc2 = h2 () in
+ let rec hrec1 x = hc1 (hrec1,hrec2,u1) x
+ and hrec2 x = hc2 (hrec1,hrec2,u2) x
+ in (hrec1,hrec2)
+
+(* A set of global hashcons functions *)
+let hashcons_resets = ref []
+let init() = List.iter (fun f -> f()) !hashcons_resets
+
+(* [register_hcons h u] registers the hcons function h, result of the above
+ * wrappers. It returns another hcons function that always uses the same
+ * table, which can be reinitialized by init()
+ *)
+let register_hcons h u =
+ let hf = ref (h u) in
+ let reset() = hf := h u in
+ hashcons_resets := reset :: !hashcons_resets;
+ (fun x -> !hf x)
+
+(* Basic hashcons modules for string and obj. Integers do not need be
+ hashconsed. *)
+
+(* string *)
+module Hstring = Make(
+ struct
+ type t = string
+ type u = unit
+ let hash_sub () s =(* incr accesstr;*) s
+ let equal s1 s2 =(* incr comparaisonstr;
+ if*) s1=s2(* then (incr successtr; true) else false*)
+ let hash = Hashtbl.hash
+ end)
+
+(* Obj.t *)
+exception NotEq
+
+(* From CAMLLIB/caml/mlvalues.h *)
+let no_scan_tag = 251
+let tuple_p obj = Obj.is_block obj & (Obj.tag obj < no_scan_tag)
+
+let comp_obj o1 o2 =
+ if tuple_p o1 & tuple_p o2 then
+ let n1 = Obj.size o1 and n2 = Obj.size o2 in
+ if n1=n2 then
+ try
+ for i = 0 to pred n1 do
+ if not (Obj.field o1 i == Obj.field o2 i) then raise NotEq
+ done; true
+ with NotEq -> false
+ else false
+ else o1=o2
+
+let hash_obj hrec o =
+ begin
+ if tuple_p o then
+ let n = Obj.size o in
+ for i = 0 to pred n do
+ Obj.set_field o i (hrec (Obj.field o i))
+ done
+ end;
+ o
+
+module Hobj = Make(
+ struct
+ type t = Obj.t
+ type u = (Obj.t -> Obj.t) * unit
+ let hash_sub (hrec,_) = hash_obj hrec
+ let equal = comp_obj
+ let hash = Hashtbl.hash
+ end)
+
+(* Hashconsing functions for string and obj. Always use the same
+ * global tables. The latter can be reinitialized with init()
+ *)
+(* string : string -> string *)
+(* obj : Obj.t -> Obj.t *)
+let string = register_hcons (simple_hcons Hstring.f) ()
+let obj = register_hcons (recursive_hcons Hobj.f) ()
+
+(* The unsafe polymorphic hashconsing function *)
+let magic_hash (c : 'a) =
+ init();
+ let r = obj (Obj.repr c) in
+ init();
+ (Obj.magic r : 'a)