File ‹discrimination_tree.ML›
structure Discrimination_Tree : TERM_INDEX =
struct
structure TIB = Term_Index_Base
val norm_term = Term_Normalisation.beta_eta_short
datatype key = CombK | VarK | AtomK of string
fun add_key_of_terms (t, ks) =
let fun rands (t, ks) = case t of
t1 $ t2 => CombK :: rands (t1, add_key_of_terms (t2, ks))
| Const (n, _) => AtomK n :: ks
| Free (n, _) => AtomK n :: ks
| Bound i => AtomK (Name.bound i) :: ks
in case head_of t of
Var _ => VarK :: ks
| Abs _ => VarK :: ks
| _ => rands (t, ks)
end
fun key_of_term t = add_key_of_terms (t, [])
datatype 'a term_index =
Leaf of 'a list
| Tree of {comb: 'a term_index, var: 'a term_index, atoms: 'a term_index Symtab.table}
val empty = Leaf []
fun is_empty (Leaf []) = true
| is_empty _ = false
val empty_tree = Tree {comb=empty, var=empty, atoms=Symtab.empty}
fun insert_keys is_dup x ksdtp = case ksdtp of
([], Leaf xs) => if exists is_dup xs then raise TIB.INSERT else Leaf (x :: xs)
| (keys, Leaf []) => insert_keys is_dup x (keys, empty_tree)
| (CombK :: keys, Tree {comb, var, atoms}) =>
Tree {comb=insert_keys is_dup x (keys, comb), var=var, atoms=atoms}
| (VarK :: keys, Tree {comb, var, atoms}) =>
Tree {comb=comb, var=insert_keys is_dup x (keys, var), atoms=atoms}
| (AtomK a :: keys, Tree {comb, var, atoms}) =>
let val atoms' = Symtab.map_default (a, empty) (fn dt' => insert_keys is_dup x (keys, dt')) atoms
in Tree {comb=comb, var=var, atoms=atoms'} end
fun insert is_dup (t, x) dt = insert_keys is_dup x (key_of_term t, dt)
fun insert_safe is_dup entry dt = insert is_dup entry dt handle TIB.INSERT => dt
fun new_tree (args as {comb, var, atoms}) =
if is_empty comb andalso is_empty var andalso Symtab.is_empty atoms then empty
else Tree args
fun delete_keys sel ksdtp = case ksdtp of
([], Leaf xs) => if exists sel xs then Leaf (filter_out sel xs) else raise TIB.DELETE
| (_, Leaf []) => raise TIB.DELETE
| (CombK :: keys, Tree {comb, var, atoms}) =>
new_tree {comb=delete_keys sel (keys,comb), var=var, atoms=atoms}
| (VarK :: keys, Tree {comb, var, atoms}) =>
new_tree {comb=comb, var=delete_keys sel (keys,var), atoms=atoms}
| (AtomK a :: keys, Tree {comb, var, atoms}) =>
let val atoms' = (case Symtab.lookup atoms a of
NONE => raise TIB.DELETE
| SOME dt' =>
(case delete_keys sel (keys, dt') of
Leaf [] => Symtab.delete a atoms
| dt'' => Symtab.update (a, dt'') atoms))
in new_tree {comb=comb, var=var, atoms=atoms'} end
fun delete eq t dt = delete_keys eq (key_of_term t, dt)
fun delete_safe eq t dt = delete eq t dt handle TIB.DELETE => dt
type 'a retrieval = 'a term_index -> term -> 'a list
fun variants_keys dt keys = case (dt, keys) of
(Leaf xs, []) => xs
| (Leaf _, (_ :: _)) => []
| (Tree {comb, ...}, (CombK :: keys)) => variants_keys comb keys
| (Tree {var, ...}, (VarK :: keys)) => variants_keys var keys
| (Tree {atoms, ...}, (AtomK a :: keys)) =>
case Symtab.lookup atoms a of
SOME dt => variants_keys dt keys
| NONE => []
fun variants dt t = variants_keys dt (key_of_term t)
fun tree_skip (Leaf _) dts = dts
| tree_skip (Tree {comb, var, atoms}) dts =
var :: dts
|> Symtab.fold (fn (_, dt) => fn dts => dt :: dts) atoms
|> fold_rev tree_skip (tree_skip comb [])
fun look1 (atoms, a) dts = case Symtab.lookup atoms a of
NONE => dts
| SOME dt => dt :: dts
datatype query = Instances | Generalisations | Unifiables
fun query q t dt dts =
let
fun rands _ (Leaf _) dts = dts
| rands t (Tree {comb, atoms, ...}) dts = case t of
f $ t => fold_rev (query q t) (rands f comb []) dts
| Const (n, _) => look1 (atoms, n) dts
| Free (n, _) => look1 (atoms, n) dts
| Bound i => look1 (atoms, Name.bound i) dts
in case dt of
Leaf _ => dts
| Tree {var, ...} => case (head_of t, q) of
(Var _, Generalisations) => var :: dts
| (Var _, _) => tree_skip dt dts
| (Abs _, Generalisations) => var :: dts
| (Abs _, _) => tree_skip dt dts
| (_, Instances) => rands t dt dts
| (_, _) => rands t dt (var :: dts)
end
fun extract_leaves ls = maps (fn Leaf xs => xs) ls
fun generalisations dt t = query Generalisations t dt [] |> extract_leaves
fun unifiables dt t = query Unifiables t dt [] |> extract_leaves
fun instances dt t = query Instances t dt [] |> extract_leaves
fun cons_fst x (xs, y) = (x :: xs, y)
fun dest (Leaf xs) = map (pair []) xs
| dest (Tree {comb, var, atoms}) = flat [
map (cons_fst CombK) (dest comb),
map (cons_fst VarK) (dest var),
maps (fn (a, net) => map (cons_fst (AtomK a)) (dest net)) (Symtab.dest atoms)
]
fun content dt = map #2 (dest dt)
fun merge eq dt1 dt2 =
if pointer_eq (dt1, dt2) then dt1
else if is_empty dt1 then dt2
else fold
(fn (ks, v) => fn dt => insert_keys (curry eq v) v (ks, dt) handle TIB.INSERT => dt)
(dest dt2) dt1
end