src/Pure/context.ML
author wenzelm
Fri, 17 Jun 2005 18:33:21 +0200
changeset 16436 7eb6b6cbd166
parent 15801 d2f5ca3c048d
child 16489 f66ab8a4e98f
permissions -rw-r--r--
added type theory: generic theory contexts with unique identity, arbitrarily typed data, linear and graph development -- a complete rewrite of code that used to be spread over in sign.ML, theory.ML, theory_data.ML, pure_thy.ML;

(*  Title:      Pure/context.ML
    ID:         $Id$
    Author:     Markus Wenzel, TU Muenchen

Generic theory contexts with unique identity, arbitrarily typed data,
linear and graph development.  Implicit ML theory context.
*)

signature BASIC_CONTEXT =
sig
  type theory
  type theory_ref
  exception THEORY of string * theory list
  val context: theory -> unit
  val the_context: unit -> theory
end;

signature CONTEXT =
sig
  include BASIC_CONTEXT
  (*theory context*)
  val parents_of: theory -> theory list
  val ancestors_of: theory -> theory list
  val is_stale: theory -> bool
  val ProtoPureN: string
  val PureN: string
  val CPureN: string
  val draftN: string
  val is_draft: theory -> bool
  val exists_name: string -> theory -> bool
  val names_of: theory -> string list
  val pretty_thy: theory -> Pretty.T
  val string_of_thy: theory -> string
  val pprint_thy: theory -> pprint_args -> unit
  val pretty_abbrev_thy: theory -> Pretty.T
  val str_of_thy: theory -> string
  val check_thy: string -> theory -> theory
  val eq_thy: theory * theory -> bool
  val subthy: theory * theory -> bool
  val self_ref: theory -> theory_ref
  val deref: theory_ref -> theory
  exception DATA_FAIL of exn * string
  val theory_data: theory -> string list
  val print_all_data: theory -> unit
  val copy_thy: theory -> theory
  val checkpoint_thy: theory -> theory
  val pre_pure: theory
  val merge: theory * theory -> theory                     (*exception TERM*)
  val merge_refs: theory_ref * theory_ref -> theory_ref    (*exception TERM*)
  val theory_name: theory -> string
  val begin_theory: (theory -> Pretty.pp) -> string -> theory list -> theory
  val end_theory: theory -> theory

  (*ML context*)
  val get_context: unit -> theory option
  val set_context: theory option -> unit
  val reset_context: unit -> unit
  val setmp: theory option -> ('a -> 'b) -> 'a -> 'b
  val pass: theory option -> ('a -> 'b) -> 'a -> 'b * theory option
  val pass_theory: theory -> ('a -> 'b) -> 'a -> 'b * theory
  val save: ('a -> 'b) -> 'a -> 'b
  val >> : (theory -> theory) -> unit
  val ml_output: (string -> unit) * (string -> unit)
  val use_mltext: string -> bool -> theory option -> unit
  val use_mltext_theory: string -> bool -> theory -> theory
  val use_let: string -> string -> string -> theory -> theory
  val add_setup: (theory -> theory) list -> unit
  val setup: unit -> (theory -> theory) list
end;

signature PRIVATE_CONTEXT =
sig
  include CONTEXT
  structure TheoryData:
  sig
    val declare: string -> Object.T -> (Object.T -> Object.T) ->
      (Object.T -> Object.T) -> (Pretty.pp -> Object.T * Object.T -> Object.T) ->
      (theory -> Object.T -> unit) -> serial
    val init: serial -> theory -> theory
    val print: serial -> theory -> unit
    val get: serial -> (Object.T -> 'a) -> theory -> 'a
    val put: serial -> ('a -> Object.T) -> 'a -> theory -> theory
  end;
end;

structure Context: PRIVATE_CONTEXT =
struct

(*** theory context ***)

datatype theory =
  Theory of
  (*identity*)
   {self: theory ref option,
    id: serial * string,
    ids: string Inttab.table} *
  (*data*)
  Object.T Inttab.table *
  (*history of linear development*)
   {name: string,
    version: int,
    intermediates: theory list} *
  (*ancestry of graph development*)
   {parents: theory list,
    ancestors: theory list};

exception THEORY of string * theory list;

fun rep_theory (Theory args) = args;

val identity_of = #1 o rep_theory;
val data_of     = #2 o rep_theory;
val history_of  = #3 o rep_theory;
val ancestry_of = #4 o rep_theory;

fun make_identity self id ids = {self = self, id = id, ids = ids};
fun make_history name vers ints = {name = name, version = vers, intermediates = ints};
fun make_ancestry parents ancestors = {parents = parents, ancestors = ancestors};

val parents_of = #parents o ancestry_of;
val ancestors_of = #ancestors o ancestry_of;



(** theory identity **)

(* staleness *)

fun eq_id ((i: int, _), (j, _)) = i = j;

fun is_stale
    (Theory ({self = SOME (ref (Theory ({id = id', ...}, _, _, _))), id, ...}, _, _, _)) =
      not (eq_id (id, id'))
  | is_stale (Theory ({self = NONE, ...}, _, _, _)) = true;

fun vitalize (thy as Theory ({self = SOME r, ...}, _, _, _)) = (r := thy; thy)
  | vitalize (thy as Theory ({self = NONE, id, ids}, data, history, ancestry)) =
      let
        val r = ref thy;
        val thy' = Theory (make_identity (SOME r) id ids, data, history, ancestry);
      in r := thy'; thy' end;


(* names *)

val ProtoPureN = "ProtoPure";
val PureN = "Pure";
val CPureN = "CPure";

val draftN = "#";
fun draft_id (_, name) = (name = draftN);
val is_draft = draft_id o #id o identity_of;

fun exists_name name (Theory ({id, ids, ...}, _, _, _)) =
  name = #2 id orelse Inttab.exists (equal name o #2) ids;

fun names_of (Theory ({id, ids, ...}, _, _, _)) =
  map #2 (Inttab.dest ids @ [id]);

fun pretty_thy thy =
  Pretty.str_list "{" "}" (names_of thy @ (if is_stale thy then ["!"] else []));

val string_of_thy = Pretty.string_of o pretty_thy;
val pprint_thy = Pretty.pprint o pretty_thy;

fun pretty_abbrev_thy thy =
  let
    val names = names_of thy;
    val n = length names;
    val abbrev = if n > 5 then "..." :: List.drop (names, n - 5) else names;
  in Pretty.str_list "{" "}" abbrev end;

val str_of_thy = Pretty.str_of o pretty_abbrev_thy;


(* consistency *)

fun check_thy pos thy =
  if is_stale thy then
    raise TERM ("Stale theory encountered (see " ^ pos ^ "):\n" ^ string_of_thy thy, [])
  else thy;

fun check_insert id ids =
  if draft_id id orelse is_some (Inttab.lookup (ids, #1 id)) then ids
  else if Inttab.exists (equal (#2 id) o #2) ids then
    raise TERM ("Different versions of theory component " ^ quote (#2 id), [])
  else Inttab.update (id, ids);

fun check_merge thy1 thy2 =
  let
    val {id = id1, ids = ids1, ...} = identity_of thy1;
    val {id = id2, ids = ids2, ...} = identity_of thy2;
  in check_insert id2 (Inttab.fold check_insert ids2 (check_insert id1 ids1)) end;


(* equality and inclusion *)

local

fun exists_ids (Theory ({id, ids, ...}, _, _, _)) (i, _) =
  i = #1 id orelse is_some (Inttab.lookup (ids, i));

fun member_ids (Theory ({id, ...}, _, _, _), thy) = exists_ids thy id;

fun subset_ids (Theory ({id, ids, ...}, _, _, _), thy) =
  exists_ids thy id andalso Inttab.forall (exists_ids thy) ids;

in

val eq_thy = eq_id o pairself (#id o identity_of o check_thy "Context.eq_thy");
fun subthy thys = eq_thy thys orelse member_ids thys;
fun subthy_internal thys = eq_thy thys orelse subset_ids thys;

end;


(* external references *)

datatype theory_ref = TheoryRef of theory ref;

val self_ref = TheoryRef o the o #self o identity_of o check_thy "Context.self_ref";
fun deref (TheoryRef (ref thy)) = thy;



(** theory data **)

(* data kinds and access methods *)

exception DATA_FAIL of exn * string;

local

type kind =
 {name: string,
  empty: Object.T,
  copy: Object.T -> Object.T,
  extend: Object.T -> Object.T,
  merge: Pretty.pp -> Object.T * Object.T -> Object.T,
  print: theory -> Object.T -> unit};

val kinds = ref (Inttab.empty: kind Inttab.table);

fun invoke meth_name meth_fn k =
  (case Inttab.lookup (! kinds, k) of
    SOME kind => meth_fn kind |> transform_failure (fn exn =>
      DATA_FAIL (exn, "Theory data method " ^ #name kind ^ "." ^ meth_name ^ " failed"))
  | NONE => sys_error ("Invalid theory data identifier " ^ string_of_int k));

in

fun invoke_name k    = invoke "name" (K o #name) k ();
fun invoke_empty k   = invoke "empty" (K o #empty) k ();
val invoke_copy      = invoke "copy" #copy;
val invoke_extend    = invoke "extend" #extend;
fun invoke_merge pp  = invoke "merge" (fn kind => #merge kind pp);
fun invoke_print thy = invoke "print" (fn kind => #print kind thy);

fun declare name e cp ext mrg prt =
  let
    val k = serial ();
    val kind = {name = name, empty = e, copy = cp, extend = ext, merge = mrg, print = prt};
    val _ = conditional (Inttab.exists (equal name o #name o #2) (! kinds)) (fn () =>
      warning ("Duplicate declaration of theory data " ^ quote name));
    val _ = kinds := Inttab.update ((k, kind), ! kinds);
  in k end;

val copy_data = Inttab.map' invoke_copy;
val extend_data = Inttab.map' invoke_extend;
fun merge_data pp = Inttab.join (SOME oo invoke_merge pp) o pairself extend_data;

fun theory_data thy =
  map invoke_name (Inttab.keys (data_of thy))
  |> map (rpair ()) |> Symtab.make_multi |> Symtab.dest
  |> map (apsnd length)
  |> map (fn (name, 1) => name | (name, n) => name ^ enclose "[" "]" (string_of_int n));

fun print_all_data thy =
  List.app (uncurry (invoke_print thy)) (Inttab.dest (data_of thy));

end;



(** build theories **)

(* primitives *)

fun create_thy name self id ids data history ancestry =
  let
    val ids' = check_insert id ids;
    val id' = (serial (), name);
    val _ = check_insert id' ids';
    val identity' = make_identity self id' ids';
  in vitalize (Theory (identity', data, history, ancestry)) end;

fun copy_thy (thy as Theory ({id, ids, ...}, data, history, ancestry)) =
  let val _ = check_thy "Context.copy_thy" thy;
  in create_thy draftN NONE id ids (copy_data data) history ancestry end;

fun change_thy name f (thy as Theory ({self, id, ids}, data, history, ancestry)) =
  let
    val _ = check_thy "Context.change_thy" thy;
    val (self', data', ancestry') =
      if is_draft thy then (self, data, ancestry)
      else (NONE, extend_data data, make_ancestry [thy] (thy :: #ancestors ancestry));
  in create_thy name self' id ids (f data') history ancestry' end;

fun name_thy name = change_thy name I;
val map_thy = change_thy draftN;
val extend_thy = map_thy I;

fun checkpoint_thy thy =
  if not (is_draft thy) then thy
  else
    let
      val {name, version, intermediates} = history_of thy;
      val thy' as Theory (identity', data', _, ancestry') =
        name_thy (name ^ ":" ^ string_of_int version) thy;
      val history' = make_history name (version + 1) (thy' :: intermediates);
    in vitalize (Theory (identity', data', history', ancestry')) end;


(* theory data *)

structure TheoryData =
struct

val declare = declare;

fun get k dest thy =
  (case Inttab.lookup (data_of thy, k) of
    SOME x => (dest x handle Match =>
      error ("Failed to access theory data " ^ quote (invoke_name k)))
  | NONE => error ("Uninitialized theory data " ^ quote (invoke_name k)));

fun print k thy = invoke_print thy k (get k I thy);
fun put k mk x = map_thy (curry Inttab.update (k, mk x));
fun init k = put k I (invoke_empty k);

end;


(* pre_pure *)

val pre_pure = create_thy draftN NONE (serial (), draftN) Inttab.empty
  Inttab.empty (make_history ProtoPureN 0 []) (make_ancestry [] []);


(* trivial merge *)

fun merge (thy1, thy2) =
  if subthy (thy2, thy1) then thy1
  else if subthy (thy1, thy2) then thy2
  else (check_merge thy1 thy2;
    raise TERM (cat_lines ["Attempt to perform non-trivial merge of theories:",
      str_of_thy thy1, str_of_thy thy2], []));

fun merge_refs (ref1, ref2) = self_ref (merge (deref ref1, deref ref2));


(* non-trivial merge *)

fun merge_internal pp (thy1, thy2) =
  if subthy_internal (thy2, thy1) then thy1
  else if subthy_internal (thy1, thy2) then thy2
  else if exists_name CPureN thy1 <> exists_name CPureN thy2 then
    error "Cannot merge Pure and CPure developments"
  else
    let
      val ids = check_merge thy1 thy2;
      val data = merge_data (pp thy1) (data_of thy1, data_of thy2);
      val history = make_history "" 0 [];
      val ancestry = make_ancestry [] [];
    in create_thy draftN NONE (serial (), draftN) ids data history ancestry end;


(* named theory nodes *)

val theory_name = #name o history_of;

fun begin_theory pp name imports =
  if name = draftN then
    error ("Illegal theory name: " ^ quote draftN)
  else if exists is_draft imports then
    error "Attempt to import draft theories"
  else
    let
      val parents = gen_distinct eq_thy imports;
      val ancestors = gen_distinct eq_thy (parents @ List.concat (map ancestors_of parents));
      val Theory ({id, ids, ...}, data, _, _) =
        (case parents of
          [] => error "No parent theories"
        | thy :: thys => extend_thy (Library.foldl (merge_internal pp) (thy, thys)));
      val history = make_history name 0 [];
      val ancestry = make_ancestry parents ancestors;
    in create_thy draftN NONE id ids data history ancestry end;

fun end_theory thy =
  thy
(*|> purge_thy  FIXME *)
  |> name_thy (theory_name thy);




(*** ML theory context ***)

local
  val current_theory = ref (NONE: theory option);
in
  fun get_context () = ! current_theory;
  fun set_context opt_thy = current_theory := opt_thy;
  fun setmp opt_thy f x = Library.setmp current_theory opt_thy f x;
end;

fun the_context () =
  (case get_context () of
    SOME thy => thy
  | _ => error "Unknown theory context");

fun context thy = set_context (SOME thy);
fun reset_context () = set_context NONE;

fun pass opt_thy f x =
  setmp opt_thy (fn x => let val y = f x in (y, get_context ()) end) x;

fun pass_theory thy f x =
  (case pass (SOME thy) f x of
    (y, SOME thy') => (y, thy')
  | (_, NONE) => error "Lost theory context in ML");

fun save f x = setmp (get_context ()) f x;


(* map context *)

nonfix >>;
fun >> f = set_context (SOME (f (the_context ())));


(* use ML text *)

val ml_output = (writeln, error_msg);

fun use_output verb txt = use_text ml_output verb (Symbol.escape txt);

fun use_mltext txt verb opt_thy = setmp opt_thy (fn () => use_output verb txt) ();
fun use_mltext_theory txt verb thy = #2 (pass_theory thy (use_output verb) txt);

fun use_context txt = use_mltext_theory ("Context.>> (" ^ txt ^ ");") false;

fun use_let bind body txt =
  use_context ("let " ^ bind ^ " = " ^ txt ^ " in\n" ^ body ^ " end");


(* delayed theory setup *)

local
  val setup_fns = ref ([]: (theory -> theory) list);
in
  fun add_setup fns = setup_fns := ! setup_fns @ fns;
  fun setup () = let val fns = ! setup_fns in setup_fns := []; fns end;
end;

end;

structure BasicContext: BASIC_CONTEXT = Context;
open BasicContext;



(*** type-safe interface for theory data ***)

signature THEORY_DATA_ARGS =
sig
  val name: string
  type T
  val empty: T
  val copy: T -> T
  val extend: T -> T
  val merge: Pretty.pp -> T * T -> T
  val print: theory -> T -> unit
end;

signature THEORY_DATA =
sig
  type T
  val init: theory -> theory
  val print: theory -> unit
  val get: theory -> T
  val put: T -> theory -> theory
  val map: (T -> T) -> theory -> theory
end;

functor TheoryDataFun(Data: THEORY_DATA_ARGS): THEORY_DATA =
struct

structure TheoryData = Context.TheoryData;

type T = Data.T;
exception Data of T;

val kind = TheoryData.declare Data.name
  (Data Data.empty)
  (fn Data x => Data (Data.copy x))
  (fn Data x => Data (Data.extend x))
  (fn pp => fn (Data x1, Data x2) => Data (Data.merge pp (x1, x2)))
  (fn thy => fn Data x => Data.print thy x);

val init = TheoryData.init kind;
val print = TheoryData.print kind;
val get = TheoryData.get kind (fn Data x => x);
val get_sg = get;    (*obsolete*)
val put = TheoryData.put kind Data;
fun map f thy = put (f (get thy)) thy;

end;

(*hide private interface!*)
structure Context: CONTEXT = Context;