ocaml/typing/ident.ml

247 lines
6.5 KiB
OCaml

(***********************************************************************)
(* *)
(* OCaml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* en Automatique. All rights reserved. This file is distributed *)
(* under the terms of the Q Public License version 1.0. *)
(* *)
(***********************************************************************)
open Format
type t = { stamp: int; name: string; mutable flags: int }
let global_flag = 1
let predef_exn_flag = 2
(* A stamp of 0 denotes a persistent identifier *)
let currentstamp = ref 0
let create s =
incr currentstamp;
{ name = s; stamp = !currentstamp; flags = 0 }
let create_predef_exn s =
incr currentstamp;
{ name = s; stamp = !currentstamp; flags = predef_exn_flag }
let create_persistent s =
{ name = s; stamp = 0; flags = global_flag }
let rename i =
incr currentstamp;
{ i with stamp = !currentstamp }
let name i = i.name
let unique_name i = i.name ^ "_" ^ string_of_int i.stamp
let unique_toplevel_name i = i.name ^ "/" ^ string_of_int i.stamp
let persistent i = (i.stamp = 0)
let equal i1 i2 = i1.name = i2.name
let same i1 i2 = i1 = i2
(* Possibly more efficient version (with a real compiler, at least):
if i1.stamp <> 0
then i1.stamp = i2.stamp
else i2.stamp = 0 && i1.name = i2.name *)
let compare i1 i2 = Pervasives.compare i1 i2
let binding_time i = i.stamp
let current_time() = !currentstamp
let set_current_time t = currentstamp := max !currentstamp t
let reinit_level = ref (-1)
let reinit () =
if !reinit_level < 0
then reinit_level := !currentstamp
else currentstamp := !reinit_level
let hide i =
{ i with stamp = -1 }
let make_global i =
i.flags <- i.flags lor global_flag
let global i =
(i.flags land global_flag) <> 0
let is_predef_exn i =
(i.flags land predef_exn_flag) <> 0
let print ppf i =
match i.stamp with
| 0 -> fprintf ppf "%s!" i.name
| -1 -> fprintf ppf "%s#" i.name
| n -> fprintf ppf "%s/%i%s" i.name n (if global i then "g" else "")
type 'a tbl =
Empty
| Node of 'a tbl * 'a data * 'a tbl * int
and 'a data =
{ ident: t;
data: 'a;
previous: 'a data option }
let empty = Empty
(* Inline expansion of height for better speed
* let height = function
* Empty -> 0
* | Node(_,_,_,h) -> h
*)
let mknode l d r =
let hl = match l with Empty -> 0 | Node(_,_,_,h) -> h
and hr = match r with Empty -> 0 | Node(_,_,_,h) -> h in
Node(l, d, r, (if hl >= hr then hl + 1 else hr + 1))
let balance l d r =
let hl = match l with Empty -> 0 | Node(_,_,_,h) -> h
and hr = match r with Empty -> 0 | Node(_,_,_,h) -> h in
if hl > hr + 1 then
match l with
| Node (ll, ld, lr, _)
when (match ll with Empty -> 0 | Node(_,_,_,h) -> h) >=
(match lr with Empty -> 0 | Node(_,_,_,h) -> h) ->
mknode ll ld (mknode lr d r)
| Node (ll, ld, Node(lrl, lrd, lrr, _), _) ->
mknode (mknode ll ld lrl) lrd (mknode lrr d r)
| _ -> assert false
else if hr > hl + 1 then
match r with
| Node (rl, rd, rr, _)
when (match rr with Empty -> 0 | Node(_,_,_,h) -> h) >=
(match rl with Empty -> 0 | Node(_,_,_,h) -> h) ->
mknode (mknode l d rl) rd rr
| Node (Node (rll, rld, rlr, _), rd, rr, _) ->
mknode (mknode l d rll) rld (mknode rlr rd rr)
| _ -> assert false
else
mknode l d r
let rec add id data = function
Empty ->
Node(Empty, {ident = id; data = data; previous = None}, Empty, 1)
| Node(l, k, r, h) ->
let c = compare id.name k.ident.name in
if c = 0 then
Node(l, {ident = id; data = data; previous = Some k}, r, h)
else if c < 0 then
balance (add id data l) k r
else
balance l k (add id data r)
let rec find_stamp s = function
None ->
raise Not_found
| Some k ->
if k.ident.stamp = s then k.data else find_stamp s k.previous
let rec find_same id = function
Empty ->
raise Not_found
| Node(l, k, r, _) ->
let c = compare id.name k.ident.name in
if c = 0 then
if id.stamp = k.ident.stamp
then k.data
else find_stamp id.stamp k.previous
else
find_same id (if c < 0 then l else r)
let rec find_name name = function
Empty ->
raise Not_found
| Node(l, k, r, _) ->
let c = compare name k.ident.name in
if c = 0 then
k.data
else
find_name name (if c < 0 then l else r)
let rec get_all = function
| None -> []
| Some k -> k.data :: get_all k.previous
let rec find_all name = function
Empty ->
[]
| Node(l, k, r, _) ->
let c = compare name k.ident.name in
if c = 0 then
k.data :: get_all k.previous
else
find_all name (if c < 0 then l else r)
let rec fold_aux f stack accu = function
Empty ->
begin match stack with
[] -> accu
| a :: l -> fold_aux f l accu a
end
| Node(l, k, r, _) ->
fold_aux f (l :: stack) (f k accu) r
let fold_name f tbl accu = fold_aux (fun k -> f k.ident k.data) [] accu tbl
let rec fold_data f d accu =
match d with
None -> accu
| Some k -> f k.ident k.data (fold_data f k.previous accu)
let fold_all f tbl accu =
fold_aux (fun k -> fold_data f (Some k)) [] accu tbl
(* let keys tbl = fold_name (fun k _ accu -> k::accu) tbl [] *)
let rec iter f = function
Empty -> ()
| Node(l, k, r, _) ->
iter f l; f k.ident k.data; iter f r
(* Idents for sharing keys *)
(* They should be 'totally fresh' -> neg numbers *)
let key_name = ""
let make_key_generator () =
let c = ref 1 in
fun id ->
let stamp = !c in
decr c ;
{ id with name = key_name; stamp = stamp; }
let compare x y =
let c = x.stamp - y.stamp in
if c <> 0 then c
else
let c = compare x.name y.name in
if c <> 0 then c
else
compare x.flags y.flags
let output oc id = output_string oc (unique_name id)
let hash i = (Char.code i.name.[0]) lxor i.stamp
let original_equal = equal
include Identifiable.Make (struct
type nonrec t = t
let compare = compare
let output = output
let print = print
let hash = hash
let equal = same
end)
let equal = original_equal