src/Pure/Isar/isar_document.ML
author wenzelm
Mon May 03 14:25:56 2010 +0200 (2010-05-03)
changeset 36610 bafd82950e24
parent 34285 218fa4267718
child 36950 75b8f26f2f07
permissions -rw-r--r--
renamed ProofContext.init to ProofContext.init_global to emphasize that this is not the real thing;
     1 (*  Title:      Pure/Isar/isar_document.ML
     2     Author:     Makarius
     3 
     4 Interactive Isar documents.
     5 *)
     6 
     7 structure Isar_Document: sig end =
     8 struct
     9 
    10 (* unique identifiers *)
    11 
    12 type state_id = string;
    13 type command_id = string;
    14 type document_id = string;
    15 
    16 val no_id = "";
    17 
    18 local
    19   val id_count = Synchronized.var "id" 0;
    20 in
    21   fun create_id () =
    22     Synchronized.change_result id_count
    23       (fn i =>
    24         let val i' = i + 1
    25         in ("i" ^ string_of_int i', i') end);
    26 end;
    27 
    28 fun err_dup kind id = error ("Duplicate " ^ kind ^ ": " ^ quote id);
    29 fun err_undef kind id = error ("Undefined " ^ kind ^ ": " ^ quote id);
    30 
    31 
    32 
    33 (** documents **)
    34 
    35 (* command entries *)
    36 
    37 datatype entry = Entry of {next: command_id option, state: state_id option};
    38 fun make_entry next state = Entry {next = next, state = state};
    39 
    40 fun the_entry entries (id: command_id) =
    41   (case Symtab.lookup entries id of
    42     NONE => err_undef "document entry" id
    43   | SOME (Entry entry) => entry);
    44 
    45 fun put_entry (id: command_id, entry: entry) = Symtab.update (id, entry);
    46 
    47 fun put_entry_state (id: command_id) state entries =
    48   let val {next, ...} = the_entry entries id
    49   in put_entry (id, make_entry next state) entries end;
    50 
    51 fun reset_entry_state id = put_entry_state id NONE;
    52 fun set_entry_state (id, state_id) = put_entry_state id (SOME state_id);
    53 
    54 
    55 (* document *)
    56 
    57 datatype document = Document of
    58  {dir: Path.T,                   (*master directory*)
    59   name: string,                  (*theory name*)
    60   entries: entry Symtab.table};  (*unique command entries indexed by command_id, start with no_id*)
    61 
    62 fun make_document dir name entries =
    63   Document {dir = dir, name = name, entries = entries};
    64 
    65 fun map_entries f (Document {dir, name, entries}) =
    66   make_document dir name (f entries);
    67 
    68 
    69 (* iterate entries *)
    70 
    71 fun fold_entries id0 f (Document {entries, ...}) =
    72   let
    73     fun apply NONE x = x
    74       | apply (SOME id) x =
    75           let val entry = the_entry entries id
    76           in apply (#next entry) (f (id, entry) x) end;
    77   in if Symtab.defined entries id0 then apply (SOME id0) else I end;
    78 
    79 fun first_entry P (Document {entries, ...}) =
    80   let
    81     fun first _ NONE = NONE
    82       | first prev (SOME id) =
    83           let val entry = the_entry entries id
    84           in if P (id, entry) then SOME (prev, id, entry) else first (SOME id) (#next entry) end;
    85   in first NONE (SOME no_id) end;
    86 
    87 
    88 (* modify entries *)
    89 
    90 fun insert_after (id: command_id) (id2: command_id) = map_entries (fn entries =>
    91   let val {next, state} = the_entry entries id in
    92     entries
    93     |> put_entry (id, make_entry (SOME id2) state)
    94     |> put_entry (id2, make_entry next NONE)
    95   end);
    96 
    97 fun delete_after (id: command_id) = map_entries (fn entries =>
    98   let val {next, state} = the_entry entries id in
    99     (case next of
   100       NONE => error ("No next entry to delete: " ^ quote id)
   101     | SOME id2 =>
   102         entries |>
   103           (case #next (the_entry entries id2) of
   104             NONE => put_entry (id, make_entry NONE state)
   105           | SOME id3 => put_entry (id, make_entry (SOME id3) state) #> reset_entry_state id3))
   106   end);
   107 
   108 
   109 
   110 (** global configuration **)
   111 
   112 (* states *)
   113 
   114 val empty_state = Future.value (SOME Toplevel.toplevel);
   115 
   116 local
   117 
   118 val global_states = Unsynchronized.ref (Symtab.make [(no_id, empty_state)]);
   119 
   120 in
   121 
   122 fun define_state (id: state_id) =
   123   Unsynchronized.change global_states (Symtab.update_new (id, empty_state))
   124     handle Symtab.DUP dup => err_dup "state" dup;
   125 
   126 fun put_state (id: state_id) state =
   127   Unsynchronized.change global_states (Symtab.update (id, state));
   128 
   129 fun the_state (id: state_id) =
   130   (case Symtab.lookup (! global_states) id of
   131     NONE => err_undef "state" id
   132   | SOME state => state);
   133 
   134 end;
   135 
   136 
   137 (* commands *)
   138 
   139 local
   140 
   141 val global_commands = Unsynchronized.ref (Symtab.make [(no_id, Toplevel.empty)]);
   142 
   143 in
   144 
   145 fun define_command id tr =
   146   NAMED_CRITICAL "Isar" (fn () =>
   147     Unsynchronized.change global_commands (Symtab.update_new (id, Toplevel.put_id id tr))
   148       handle Symtab.DUP dup => err_dup "command" dup);
   149 
   150 fun the_command (id: command_id) =
   151   (case Symtab.lookup (! global_commands) id of
   152     NONE => err_undef "command" id
   153   | SOME tr => tr);
   154 
   155 end;
   156 
   157 
   158 (* documents *)
   159 
   160 local
   161 
   162 val global_documents = Unsynchronized.ref (Symtab.empty: document Symtab.table);
   163 
   164 in
   165 
   166 fun define_document (id: document_id) document =
   167   NAMED_CRITICAL "Isar" (fn () =>
   168     Unsynchronized.change global_documents (Symtab.update_new (id, document))
   169       handle Symtab.DUP dup => err_dup "document" dup);
   170 
   171 fun the_document (id: document_id) =
   172   (case Symtab.lookup (! global_documents) id of
   173     NONE => err_undef "document" id
   174   | SOME document => document);
   175 
   176 end;
   177 
   178 
   179 
   180 (** main operations **)
   181 
   182 (* begin/end document *)
   183 
   184 val no_entries = Symtab.make [(no_id, make_entry NONE (SOME no_id))];
   185 
   186 fun begin_document (id: document_id) path =
   187   let
   188     val (dir, name) = ThyLoad.split_thy_path path;
   189     val _ = define_document id (make_document dir name no_entries);
   190   in () end;
   191 
   192 fun end_document (id: document_id) =
   193   NAMED_CRITICAL "Isar" (fn () =>
   194     let
   195       val document as Document {name, ...} = the_document id;
   196       val end_state =
   197         the_state (the (#state (#3 (the
   198           (first_entry (fn (_, {next, ...}) => is_none next) document)))));
   199       val _ =
   200         Future.fork_deps [end_state] (fn () =>
   201           (case Future.join end_state of
   202             SOME st =>
   203              (Toplevel.run_command name (Toplevel.put_id id (Toplevel.commit_exit Position.none)) st;
   204               ThyInfo.touch_child_thys name;
   205               ThyInfo.register_thy name)
   206           | NONE => error ("Failed to finish theory " ^ quote name)));
   207     in () end);
   208 
   209 
   210 (* document editing *)
   211 
   212 local
   213 
   214 fun is_changed entries' (id, {next = _, state}) =
   215   (case try (the_entry entries') id of
   216     NONE => true
   217   | SOME {next = _, state = state'} => state' <> state);
   218 
   219 fun new_state name (id, _) (state_id, updates) =
   220   let
   221     val state_id' = create_id ();
   222     val _ = define_state state_id';
   223     val tr = Toplevel.put_id state_id' (the_command id);
   224     fun make_state' () =
   225       let
   226         val state = the_state state_id;
   227         val state' =
   228           Future.fork_deps [state] (fn () =>
   229             (case Future.join state of
   230               NONE => NONE
   231             | SOME st => Toplevel.run_command name tr st));
   232       in put_state state_id' state' end;
   233   in (state_id', ((id, state_id'), make_state') :: updates) end;
   234 
   235 fun report_updates updates =
   236   implode (map (fn (id, state_id) => Markup.markup (Markup.edit id state_id) "") updates)
   237   |> Markup.markup Markup.assign
   238   |> Output.status;
   239 
   240 in
   241 
   242 fun edit_document (old_id: document_id) (new_id: document_id) edits =
   243   NAMED_CRITICAL "Isar" (fn () =>
   244     let
   245       val old_document as Document {name, entries = old_entries, ...} = the_document old_id;
   246       val new_document as Document {entries = new_entries, ...} = old_document
   247         |> fold (fn (id, SOME id2) => insert_after id id2 | (id, NONE) => delete_after id) edits;
   248 
   249       fun cancel_old id = fold_entries id
   250         (fn (_, {state = SOME state_id, ...}) => K (Future.cancel (the_state state_id)) | _ => K ())
   251         old_document ();
   252 
   253       val (updates, new_document') =
   254         (case first_entry (is_changed old_entries) new_document of
   255           NONE =>
   256             (case first_entry (is_changed new_entries) old_document of
   257               NONE => ([], new_document)
   258             | SOME (_, id, _) => (cancel_old id; ([], new_document)))
   259         | SOME (prev, id, _) =>
   260             let
   261               val _ = cancel_old id;
   262               val prev_state_id = the (#state (the_entry new_entries (the prev)));
   263               val (_, updates) = fold_entries id (new_state name) new_document (prev_state_id, []);
   264               val new_document' = new_document |> map_entries (fold (set_entry_state o #1) updates);
   265             in (rev updates, new_document') end);
   266 
   267       val _ = define_document new_id new_document';
   268       val _ = report_updates (map #1 updates);
   269       val _ = List.app (fn (_, run) => run ()) updates;
   270     in () end);
   271 
   272 end;
   273 
   274 
   275 
   276 (** concrete syntax **)
   277 
   278 local structure P = OuterParse structure V = ValueParse in
   279 
   280 val _ =
   281   OuterSyntax.internal_command "Isar.define_command"
   282     (P.string -- P.string >> (fn (id, text) =>
   283       Toplevel.position (Position.id_only id) o
   284       Toplevel.imperative (fn () =>
   285         define_command id (OuterSyntax.prepare_command (Position.id id) text))));
   286 
   287 val _ =
   288   OuterSyntax.internal_command "Isar.begin_document"
   289     (P.string -- P.string >> (fn (id, path) =>
   290       Toplevel.imperative (fn () => begin_document id (Path.explode path))));
   291 
   292 val _ =
   293   OuterSyntax.internal_command "Isar.end_document"
   294     (P.string >> (fn id => Toplevel.imperative (fn () => end_document id)));
   295 
   296 val _ =
   297   OuterSyntax.internal_command "Isar.edit_document"
   298     (P.string -- P.string -- V.list (P.string -- (P.string >> SOME) || P.string >> rpair NONE)
   299       >> (fn ((id, new_id), edits) =>
   300         Toplevel.position (Position.id_only new_id) o
   301         Toplevel.imperative (fn () => edit_document id new_id edits)));
   302 
   303 end;
   304 
   305 end;
   306