src/Pure/Thy/thy_info.ML
author wenzelm
Sat, 14 Jan 2006 17:14:06 +0100
changeset 18678 dd0c569fa43d
parent 17756 d4a35f82fbb4
child 18721 54693225c2f4
permissions -rw-r--r--
sane ERROR handling;
     1 (*  Title:      Pure/Thy/thy_info.ML
     2     ID:         $Id$
     3     Author:     Markus Wenzel, TU Muenchen
     4 
     5 Main part of theory loader database, including handling of theory and
     6 file dependencies.
     7 *)
     8 
     9 signature BASIC_THY_INFO =
    10 sig
    11   val theory: string -> theory
    12 (*val use: string -> unit*)             (*exported later*)
    13   val time_use: string -> unit
    14   val touch_thy: string -> unit
    15   val use_thy: string -> unit
    16   val update_thy: string -> unit
    17   val remove_thy: string -> unit
    18   val time_use_thy: string -> unit
    19   val use_thy_only: string -> unit
    20   val update_thy_only: string -> unit
    21 end;
    22 
    23 signature THY_INFO =
    24 sig
    25   include BASIC_THY_INFO
    26   datatype action = Update | Outdate | Remove
    27   val str_of_action: action -> string
    28   val add_hook: (action -> string -> unit) -> unit
    29   val names: unit -> string list
    30   val known_thy: string -> bool
    31   val check_known_thy: string -> bool
    32   val if_known_thy: (string -> unit) -> string -> unit
    33   val lookup_theory: string -> theory option
    34   val get_theory: string -> theory
    35   val get_preds: string -> string list
    36   val loaded_files: string -> Path.T list
    37   val touch_child_thys: string -> unit
    38   val touch_all_thys: unit -> unit
    39   val load_file: bool -> Path.T -> unit
    40   val use: string -> unit
    41   val quiet_update_thy: bool -> string -> unit
    42   val pretend_use_thy_only: string -> unit
    43   val begin_theory: (Path.T option -> string -> string list ->
    44       (Path.T * bool) list -> theory -> theory) ->
    45     string -> string list -> (Path.T * bool) list -> bool -> theory
    46   val end_theory: theory -> theory
    47   val finish: unit -> unit
    48   val register_theory: theory -> unit
    49   val pretty_theory: theory -> Pretty.T
    50 end;
    51 
    52 structure ThyInfo: THY_INFO =
    53 struct
    54 
    55 
    56 (** theory loader actions and hooks **)
    57 
    58 datatype action = Update | Outdate | Remove;
    59 val str_of_action = fn Update => "Update" | Outdate => "Outdate" | Remove => "Remove";
    60 
    61 local
    62   val hooks = ref ([]: (action -> string -> unit) list);
    63 in
    64   fun add_hook f = hooks := f :: ! hooks;
    65   fun perform action name = List.app (fn f => (try (fn () => f action name) (); ())) (! hooks);
    66 end;
    67 
    68 
    69 
    70 (** thy database **)
    71 
    72 (* messages *)
    73 
    74 fun gen_msg txt [] = txt
    75   | gen_msg txt names = txt ^ " " ^ commas_quote names;
    76 
    77 fun loader_msg txt names = gen_msg ("Theory loader: " ^ txt) names;
    78 
    79 val show_path = space_implode " via " o map quote;
    80 fun cycle_msg names = loader_msg ("cyclic dependency of " ^ show_path names) [];
    81 
    82 
    83 (* derived graph operations *)
    84 
    85 fun add_deps name parents G = Graph.add_deps_acyclic (name, parents) G
    86   handle Graph.CYCLES namess => error (cat_lines (map cycle_msg namess));
    87 
    88 fun upd_deps name entry G =
    89   Library.foldl (fn (H, parent) => Graph.del_edge (parent, name) H) (G, Graph.imm_preds G name)
    90   |> Graph.map_node name (K entry);
    91 
    92 fun update_node name parents entry G =
    93   (if can (Graph.get_node G) name then upd_deps name entry G else Graph.new_node (name, entry) G)
    94   |> add_deps name parents;
    95 
    96 
    97 (* thy database *)
    98 
    99 type deps =
   100   {present: bool, outdated: bool,
   101     master: ThyLoad.master option, files: (Path.T * (Path.T * File.info) option) list};
   102 
   103 fun make_deps present outdated master files =
   104   {present = present, outdated = outdated, master = master, files = files}: deps;
   105 
   106 fun init_deps master files = SOME (make_deps false true master (map (rpair NONE) files));
   107 
   108 
   109 type thy = deps option * theory option;
   110 
   111 local
   112   val database = ref (Graph.empty: thy Graph.T);
   113 in
   114   fun get_thys () = ! database;
   115   val change_thys = Library.change database;
   116 end;
   117 
   118 
   119 (* access thy graph *)
   120 
   121 fun thy_graph f x = f (get_thys ()) x;
   122 
   123 (*theory names in topological order*)
   124 fun get_names () =
   125   let val G = get_thys ()
   126   in Graph.all_succs G (Graph.minimals G) end;
   127 
   128 
   129 (* access thy *)
   130 
   131 fun lookup_thy name =
   132   SOME (thy_graph Graph.get_node name) handle Graph.UNDEF _ => NONE;
   133 
   134 val known_thy = is_some o lookup_thy;
   135 fun check_known_thy name = known_thy name orelse (warning ("Unknown theory " ^ quote name); false);
   136 fun if_known_thy f name = if check_known_thy name then f name else ();
   137 
   138 fun get_thy name =
   139   (case lookup_thy name of
   140     SOME thy => thy
   141   | NONE => error (loader_msg "nothing known about theory" [name]));
   142 
   143 fun change_thy name f = (get_thy name; change_thys (Graph.map_node name f));
   144 
   145 
   146 (* access deps *)
   147 
   148 val lookup_deps = Option.map #1 o lookup_thy;
   149 val get_deps = #1 o get_thy;
   150 fun change_deps name f = change_thy name (fn (deps, x) => (f deps, x));
   151 
   152 fun is_finished name = is_none (get_deps name);
   153 fun get_files name = (case get_deps name of SOME {files, ...} => files | _ => []);
   154 
   155 fun loaded_files name =
   156   (case get_deps name of
   157     NONE => []
   158   | SOME {master, files, ...} =>
   159       (case master of SOME m => [#1 (ThyLoad.get_thy m)] | NONE => []) @
   160       (List.mapPartial (Option.map #1 o #2) files));
   161 
   162 fun get_preds name =
   163   (thy_graph Graph.imm_preds name) handle Graph.UNDEF _ =>
   164     error (loader_msg "nothing known about theory" [name]);
   165 
   166 
   167 (* pretty printing a theory *)
   168 
   169 local
   170 
   171 fun relevant_names names =
   172   let
   173     val (finished, unfinished) =
   174       List.filter (equal Context.draftN orf known_thy) names
   175       |> List.partition (not_equal Context.draftN andf is_finished);
   176   in (if not (null finished) then [List.last finished] else []) @ unfinished end;
   177 
   178 in
   179 
   180 fun pretty_theory thy =
   181   Pretty.str_list "{" "}" (relevant_names (Context.names_of thy));
   182 
   183 end;
   184 
   185 
   186 (* access theory *)
   187 
   188 fun lookup_theory name =
   189   (case lookup_thy name of
   190     SOME (_, SOME thy) => SOME thy
   191   | _ => NONE);
   192 
   193 fun get_theory name =
   194   (case lookup_theory name of
   195     (SOME theory) => theory
   196   | _ => error (loader_msg "undefined theory entry for" [name]));
   197 
   198 fun put_theory name theory = change_thy name (fn (deps, _) => (deps, SOME theory));
   199 
   200 
   201 
   202 (** thy operations **)
   203 
   204 (* maintain 'outdated' flag *)
   205 
   206 local
   207 
   208 fun is_outdated name =
   209   (case lookup_deps name of
   210     SOME (SOME {outdated, ...}) => outdated
   211   | _ => false);
   212 
   213 fun outdate_thy name =
   214   if is_finished name orelse is_outdated name then ()
   215   else (change_deps name (Option.map (fn {present, outdated = _, master, files} =>
   216     make_deps present true master files)); perform Outdate name);
   217 
   218 fun check_unfinished name =
   219   if is_finished name then (warning (loader_msg "tried to touch finished theory" [name]); NONE)
   220   else SOME name;
   221 
   222 in
   223 
   224 fun touch_thys names =
   225   List.app outdate_thy (thy_graph Graph.all_succs (List.mapPartial check_unfinished names));
   226 
   227 fun touch_thy name = touch_thys [name];
   228 fun touch_child_thys name = touch_thys (thy_graph Graph.imm_succs name);
   229 
   230 fun touch_all_thys () = List.app outdate_thy (get_names ());
   231 
   232 end;
   233 
   234 
   235 (* check 'finished' state *)
   236 
   237 fun check_unfinished fail name =
   238   if known_thy name andalso is_finished name then
   239     fail (loader_msg "cannot update finished theory" [name])
   240   else ();
   241 
   242 
   243 (* load_file *)
   244 
   245 val opt_path = Option.map (Path.dir o fst o ThyLoad.get_thy);
   246 fun opt_path' (d: deps option) = if_none (Option.map (opt_path o #master) d) NONE;
   247 fun opt_path'' d = if_none (Option.map opt_path' d) NONE;
   248 
   249 local
   250 
   251 fun provide path name info (deps as SOME {present, outdated, master, files}) =
   252      (if exists (equal path o #1) files then ()
   253       else warning (loader_msg "undeclared dependency of theory" [name] ^
   254         " on file: " ^ quote (Path.pack path));
   255       SOME (make_deps present outdated master (AList.update (op =) (path, SOME info) files)))
   256   | provide _ _ _ NONE = NONE;
   257 
   258 fun run_file path =
   259   (case Option.map Context.theory_name (Context.get_context ()) of
   260     NONE => (ThyLoad.load_file NONE path; ())
   261   | SOME name => (case lookup_deps name of
   262       SOME deps => change_deps name (provide path name
   263         (ThyLoad.load_file (opt_path' deps) path))
   264     | NONE => (ThyLoad.load_file NONE path; ())));
   265 
   266 in
   267 
   268 fun load_file time path =
   269   if time then
   270     let val name = Path.pack path in
   271       timeit (fn () =>
   272        (priority ("\n**** Starting file " ^ quote name ^ " ****");
   273         run_file path;
   274         priority ("**** Finished file " ^ quote name ^ " ****\n")))
   275     end
   276   else run_file path;
   277 
   278 val use = load_file false o Path.unpack;
   279 val time_use = load_file true o Path.unpack;
   280 
   281 end;
   282 
   283 
   284 (* load_thy *)
   285 
   286 fun required_by _ [] = ""
   287   | required_by s initiators = s ^ "(required by " ^ show_path (rev initiators) ^ ")";
   288 
   289 fun load_thy really ml time initiators dir name =
   290   let
   291     val _ =
   292       if really then priority ("Loading theory " ^ quote name ^ required_by " " initiators)
   293       else priority ("Registering theory " ^ quote name);
   294 
   295     val _ = touch_thy name;
   296     val master =
   297       if really then ThyLoad.load_thy dir name ml time
   298       else #1 (ThyLoad.deps_thy dir name ml);
   299 
   300     val files = get_files name;
   301     val missing_files = List.mapPartial (fn (path, NONE) => SOME (Path.pack path) | _ => NONE) files;
   302   in
   303     if null missing_files then ()
   304     else warning (loader_msg "unresolved dependencies of theory" [name] ^
   305       " on file(s): " ^ commas_quote missing_files);
   306     change_deps name (fn _ => SOME (make_deps true false (SOME master) files));
   307     perform Update name
   308   end;
   309 
   310 
   311 (* require_thy *)
   312 
   313 fun base_of_path s = Path.pack (Path.base (Path.unpack s));
   314 
   315 local
   316 
   317 fun load_deps ml dir name =
   318   let val (master, (parents, files)) = ThyLoad.deps_thy dir name ml
   319   in (SOME (init_deps (SOME master) files), parents) end;
   320 
   321 fun file_current master (_, NONE) = false
   322   | file_current master (path, info) =
   323       (info = ThyLoad.check_file (opt_path master) path);
   324 
   325 fun current_deps ml strict dir name =
   326   (case lookup_deps name of
   327     NONE => (false, load_deps ml dir name)
   328   | SOME deps =>
   329       let
   330         fun get_name s = (case opt_path'' (lookup_deps s) of NONE => s
   331           | SOME p => Path.pack (Path.append p (Path.basic s)));
   332         val same_deps = (NONE, map get_name (thy_graph Graph.imm_preds name))
   333       in
   334         (case deps of
   335           NONE => (true, same_deps)
   336         | SOME {present, outdated, master, files} =>
   337             if present andalso not strict then (true, same_deps)
   338             else
   339               let val master' = SOME (ThyLoad.check_thy dir name ml) in
   340                 if master <> master' then (false, load_deps ml dir name)
   341                 else (not outdated andalso forall (file_current master') files, same_deps)
   342               end)
   343       end);
   344 
   345 fun require_thy really ml time strict keep_strict initiators prfx (visited, str) =
   346   let
   347     val path = Path.expand (Path.unpack str);
   348     val name = Path.pack (Path.base path);
   349   in
   350     if name mem_string initiators then error (cycle_msg initiators) else ();
   351     if known_thy name andalso is_finished name orelse name mem_string visited then
   352       (visited, (true, name))
   353     else
   354       let
   355         val dir = Path.append prfx (Path.dir path);
   356         val req_parent = require_thy true true time (strict andalso keep_strict) keep_strict
   357           (name :: initiators);
   358 
   359         val (current, (new_deps, parents)) = current_deps ml strict dir name
   360           handle ERROR msg => cat_error msg
   361             (loader_msg "the error(s) above occurred while examining theory" [name] ^
   362               (if null initiators then "" else required_by "\n" initiators));
   363         val dir' = if_none (opt_path'' new_deps) dir;
   364         val (visited', parent_results) = foldl_map (req_parent dir') (name :: visited, parents);
   365 
   366         val thy = if not really then SOME (get_theory name) else NONE;
   367         val result =
   368           if current andalso forall fst parent_results then true
   369           else
   370             ((case new_deps of
   371               SOME deps => change_thys (update_node name (map base_of_path parents) (deps, thy))
   372             | NONE => ());
   373              load_thy really ml (time orelse ! Output.timing) initiators dir name;
   374              false);
   375       in (visited', (result, name)) end
   376   end;
   377 
   378 fun gen_use_thy' req prfx s =
   379   let val (_, (_, name)) = req [] prfx ([], s)
   380   in Context.context (get_theory name) end;
   381 
   382 fun gen_use_thy req = gen_use_thy' req Path.current;
   383 
   384 fun warn_finished f name = (check_unfinished warning name; f name);
   385 
   386 in
   387 
   388 val weak_use_thy         = gen_use_thy' (require_thy true true false false false);
   389 fun quiet_update_thy' ml = gen_use_thy' (require_thy true ml false true true);
   390 fun quiet_update_thy ml  = gen_use_thy (require_thy true ml false true true);
   391 
   392 val use_thy          = warn_finished (gen_use_thy (require_thy true true false true false));
   393 val time_use_thy     = warn_finished (gen_use_thy (require_thy true true true true false));
   394 val use_thy_only     = warn_finished (gen_use_thy (require_thy true false false true false));
   395 val update_thy       = warn_finished (gen_use_thy (require_thy true true false true true));
   396 val update_thy_only  = warn_finished (gen_use_thy (require_thy true false false true true));
   397 val pretend_use_thy_only = warn_finished (gen_use_thy (require_thy false false false true false));
   398 
   399 end;
   400 
   401 
   402 (* remove_thy *)
   403 
   404 fun remove_thy name =
   405   if is_finished name then error (loader_msg "cannot remove finished theory" [name])
   406   else
   407     let val succs = thy_graph Graph.all_succs [name] in
   408       priority (loader_msg "removing" succs);
   409       List.app (perform Remove) succs;
   410       change_thys (Graph.del_nodes succs)
   411     end;
   412 
   413 
   414 (* begin / end theory *)
   415 
   416 fun check_uses name uses =
   417   let val illegal = map (fn ext => Path.ext ext (Path.basic name)) ThyLoad.ml_exts in
   418     (case find_first (member (op =) illegal o Path.base o Path.expand o #1) uses of
   419       NONE => ()
   420     | SOME (path, _) => error ("Illegal use of theory ML file: " ^ quote (Path.pack path)))
   421   end;
   422 
   423 fun begin_theory present name parents uses int =
   424   let
   425     val bparents = map base_of_path parents;
   426     val dir' = opt_path'' (lookup_deps name);
   427     val dir = if_none dir' Path.current;
   428     val assert_thy = if int then quiet_update_thy' true dir else weak_use_thy dir;
   429     val _ = check_unfinished error name;
   430     val _ = List.app assert_thy parents;
   431     val _ = check_uses name uses;
   432 
   433     val theory = Theory.begin_theory name (map get_theory bparents);
   434     val deps =
   435       if known_thy name then get_deps name
   436       else (init_deps NONE (map #1 uses));   (*records additional ML files only!*)
   437     val uses_now = List.mapPartial (fn (x, true) => SOME x | _ => NONE) uses;
   438 
   439     val _ = change_thys (update_node name bparents (deps, SOME (Theory.copy theory)));
   440     val theory' = theory |> present dir' name bparents uses;
   441     val _ = put_theory name (Theory.copy theory');
   442     val ((), theory'') = Context.pass_theory theory' (List.app (load_file false)) uses_now;
   443     val _ = put_theory name (Theory.copy theory'');
   444   in theory'' end;
   445 
   446 fun end_theory theory =
   447   let
   448     val name = Context.theory_name theory;
   449     val theory' = Theory.end_theory theory;
   450   in put_theory name theory'; theory' end;
   451 
   452 
   453 (* finish all theories *)
   454 
   455 fun finish () = change_thys (Graph.map_nodes (fn (_, entry) => (NONE, entry)));
   456 
   457 
   458 (* register existing theories *)
   459 
   460 fun register_theory theory =
   461   let
   462     val name = Context.theory_name theory;
   463     val parents = Theory.parents_of theory;
   464     val parent_names = map Context.theory_name parents;
   465 
   466     fun err txt bads =
   467       error (loader_msg txt bads ^ "\n" ^ gen_msg "cannot register theory" [name]);
   468 
   469     val nonfinished = filter_out is_finished parent_names;
   470     fun get_variant (x, y_name) =
   471       if Theory.eq_thy (x, get_theory y_name) then NONE
   472       else SOME y_name;
   473     val variants = List.mapPartial get_variant (parents ~~ parent_names);
   474 
   475     fun register G =
   476       (Graph.new_node (name, (NONE, SOME theory)) G
   477         handle Graph.DUP _ => err "duplicate theory entry" [])
   478       |> add_deps name parent_names;
   479   in
   480     if not (null nonfinished) then err "non-finished parent theories" nonfinished
   481     else if not (null variants) then err "different versions of parent theories" variants
   482     else (change_thys register; perform Update name)
   483   end;
   484 
   485 val _ = register_theory ProtoPure.thy;
   486 
   487 
   488 (*final declarations of this structure*)
   489 val theory = get_theory;
   490 val names = get_names;
   491 
   492 end;
   493 
   494 structure BasicThyInfo: BASIC_THY_INFO = ThyInfo;
   495 open BasicThyInfo;