wenzelm@26605
|
1 |
(* Title: Pure/Isar/isar.ML
|
wenzelm@26605
|
2 |
ID: $Id$
|
wenzelm@26605
|
3 |
Author: Makarius
|
wenzelm@26605
|
4 |
|
wenzelm@26605
|
5 |
The global Isabelle/Isar state and main read-eval-print loop.
|
wenzelm@26605
|
6 |
*)
|
wenzelm@26605
|
7 |
|
wenzelm@26605
|
8 |
signature ISAR =
|
wenzelm@26605
|
9 |
sig
|
wenzelm@26605
|
10 |
val state: unit -> Toplevel.state
|
wenzelm@26605
|
11 |
val exn: unit -> (exn * string) option
|
wenzelm@26605
|
12 |
val context: unit -> Proof.context
|
wenzelm@26605
|
13 |
val goal: unit -> thm
|
wenzelm@26605
|
14 |
val >> : Toplevel.transition -> bool
|
wenzelm@26605
|
15 |
val >>> : Toplevel.transition list -> unit
|
wenzelm@27529
|
16 |
val linear_undo: int -> unit
|
wenzelm@27524
|
17 |
val undo: int -> unit
|
wenzelm@26605
|
18 |
val crashes: exn list ref
|
wenzelm@26643
|
19 |
val toplevel_loop: {init: bool, welcome: bool, sync: bool, secure: bool} -> unit
|
wenzelm@26606
|
20 |
val loop: unit -> unit
|
wenzelm@26605
|
21 |
val main: unit -> unit
|
wenzelm@26605
|
22 |
end;
|
wenzelm@26605
|
23 |
|
wenzelm@26605
|
24 |
structure Isar: ISAR =
|
wenzelm@26605
|
25 |
struct
|
wenzelm@26605
|
26 |
|
wenzelm@27432
|
27 |
|
wenzelm@27428
|
28 |
(** individual toplevel commands **)
|
wenzelm@26605
|
29 |
|
wenzelm@27428
|
30 |
(* unique identification *)
|
wenzelm@26605
|
31 |
|
wenzelm@27428
|
32 |
type id = string;
|
wenzelm@27428
|
33 |
val no_id : id = "";
|
wenzelm@27428
|
34 |
|
wenzelm@27428
|
35 |
fun identify tr =
|
wenzelm@27428
|
36 |
(case Toplevel.get_id tr of
|
wenzelm@27428
|
37 |
SOME id => (id, tr)
|
wenzelm@27428
|
38 |
| NONE =>
|
wenzelm@27428
|
39 |
let val id = "isabelle:" ^ serial_string ()
|
wenzelm@27428
|
40 |
in (id, Toplevel.put_id id tr) end);
|
wenzelm@27428
|
41 |
|
wenzelm@27428
|
42 |
|
wenzelm@27438
|
43 |
(* command category *)
|
wenzelm@27428
|
44 |
|
wenzelm@27524
|
45 |
datatype category = Empty | Theory | Proof | Diag | Control;
|
wenzelm@27438
|
46 |
|
wenzelm@27438
|
47 |
fun category_of tr =
|
wenzelm@27438
|
48 |
let val name = Toplevel.name_of tr in
|
wenzelm@27438
|
49 |
if name = "" then Empty
|
wenzelm@27524
|
50 |
else if OuterKeyword.is_theory name then Theory
|
wenzelm@27524
|
51 |
else if OuterKeyword.is_proof name then Proof
|
wenzelm@27524
|
52 |
else if OuterKeyword.is_diag name then Diag
|
wenzelm@27524
|
53 |
else Control
|
wenzelm@27438
|
54 |
end;
|
wenzelm@27428
|
55 |
|
wenzelm@27524
|
56 |
val is_theory = fn Theory => true | _ => false;
|
wenzelm@27524
|
57 |
val is_proper = fn Theory => true | Proof => true | _ => false;
|
wenzelm@27524
|
58 |
|
wenzelm@27428
|
59 |
|
wenzelm@27428
|
60 |
(* datatype command *)
|
wenzelm@27428
|
61 |
|
wenzelm@27438
|
62 |
datatype status =
|
wenzelm@27438
|
63 |
Initial |
|
wenzelm@27518
|
64 |
Result of Toplevel.state * (exn * string) option;
|
wenzelm@27438
|
65 |
|
wenzelm@27428
|
66 |
datatype command = Command of
|
wenzelm@27501
|
67 |
{category: category,
|
wenzelm@27438
|
68 |
transition: Toplevel.transition,
|
wenzelm@27428
|
69 |
status: status};
|
wenzelm@27428
|
70 |
|
wenzelm@27501
|
71 |
fun make_command (category, transition, status) =
|
wenzelm@27501
|
72 |
Command {category = category, transition = transition, status = status};
|
wenzelm@27428
|
73 |
|
wenzelm@27438
|
74 |
val empty_command =
|
wenzelm@27518
|
75 |
make_command (Empty, Toplevel.empty, Result (Toplevel.toplevel, NONE));
|
wenzelm@27428
|
76 |
|
wenzelm@27501
|
77 |
fun map_command f (Command {category, transition, status}) =
|
wenzelm@27501
|
78 |
make_command (f (category, transition, status));
|
wenzelm@27438
|
79 |
|
wenzelm@27501
|
80 |
fun map_status f = map_command (fn (category, transition, status) =>
|
wenzelm@27501
|
81 |
(category, transition, f status));
|
wenzelm@27428
|
82 |
|
wenzelm@27428
|
83 |
|
wenzelm@27501
|
84 |
(* global collection of identified commands *)
|
wenzelm@27428
|
85 |
|
wenzelm@27518
|
86 |
fun err_dup id = sys_error ("Duplicate command " ^ quote id);
|
wenzelm@27518
|
87 |
fun err_undef id = sys_error ("Unknown command " ^ quote id);
|
wenzelm@27518
|
88 |
|
wenzelm@27428
|
89 |
local
|
wenzelm@27428
|
90 |
|
wenzelm@27501
|
91 |
val empty_commands = Graph.empty: command Graph.T;
|
wenzelm@27428
|
92 |
val global_commands = ref empty_commands;
|
wenzelm@27428
|
93 |
|
wenzelm@27428
|
94 |
in
|
wenzelm@27428
|
95 |
|
wenzelm@27501
|
96 |
fun change_commands f = NAMED_CRITICAL "Isar" (fn () => change global_commands f)
|
wenzelm@27518
|
97 |
handle Graph.DUP id => err_dup id | Graph.UNDEF id => err_undef id;
|
wenzelm@27501
|
98 |
|
wenzelm@27428
|
99 |
fun init_commands () = change_commands (K empty_commands);
|
wenzelm@27428
|
100 |
|
wenzelm@27428
|
101 |
fun the_command id =
|
wenzelm@27524
|
102 |
let val Command cmd =
|
wenzelm@27524
|
103 |
if id = no_id then empty_command
|
wenzelm@27524
|
104 |
else (Graph.get_node (! global_commands) id handle Graph.UNDEF _ => err_undef id)
|
wenzelm@27524
|
105 |
in cmd end;
|
wenzelm@27518
|
106 |
|
wenzelm@27518
|
107 |
fun prev_command id =
|
wenzelm@27518
|
108 |
if id = no_id then NONE
|
wenzelm@27518
|
109 |
else
|
wenzelm@27518
|
110 |
(case Graph.imm_preds (! global_commands) id handle Graph.UNDEF _ => err_undef id of
|
wenzelm@27518
|
111 |
[] => NONE
|
wenzelm@27518
|
112 |
| [prev] => SOME prev
|
wenzelm@27518
|
113 |
| _ => sys_error ("Non-linear command dependency " ^ quote id));
|
wenzelm@27501
|
114 |
|
wenzelm@27501
|
115 |
end;
|
wenzelm@27501
|
116 |
|
wenzelm@27428
|
117 |
|
wenzelm@27518
|
118 |
fun the_result id =
|
wenzelm@27524
|
119 |
(case the_command id of
|
wenzelm@27524
|
120 |
{status = Result res, ...} => res
|
wenzelm@27524
|
121 |
| {transition, ...} => error ("Unfinished command " ^ Toplevel.str_of transition));
|
wenzelm@27524
|
122 |
|
wenzelm@27524
|
123 |
val the_state = #1 o the_result;
|
wenzelm@27428
|
124 |
|
wenzelm@27501
|
125 |
fun new_command prev (id, cmd) =
|
wenzelm@27501
|
126 |
change_commands (Graph.new_node (id, cmd) #> prev <> no_id ? Graph.add_edge (prev, id));
|
wenzelm@27501
|
127 |
|
wenzelm@27501
|
128 |
fun dispose_command id = change_commands (Graph.del_nodes [id]);
|
wenzelm@27501
|
129 |
|
wenzelm@27501
|
130 |
fun change_command_status id f = change_commands (Graph.map_node id (map_status f));
|
wenzelm@27501
|
131 |
|
wenzelm@27428
|
132 |
|
wenzelm@27428
|
133 |
|
wenzelm@27428
|
134 |
(** TTY interaction **)
|
wenzelm@27428
|
135 |
|
wenzelm@27428
|
136 |
(* global point *)
|
wenzelm@27428
|
137 |
|
wenzelm@27428
|
138 |
local val global_point = ref no_id in
|
wenzelm@27428
|
139 |
|
wenzelm@27428
|
140 |
fun change_point f = NAMED_CRITICAL "Isar" (fn () => change global_point f);
|
wenzelm@27524
|
141 |
fun point () = NAMED_CRITICAL "Isar" (fn () => ! global_point);
|
wenzelm@27428
|
142 |
|
wenzelm@27501
|
143 |
end;
|
wenzelm@27501
|
144 |
|
wenzelm@27524
|
145 |
fun set_point id = change_point (K id);
|
wenzelm@27524
|
146 |
|
wenzelm@27524
|
147 |
fun point_result () = NAMED_CRITICAL "Isar" (fn () =>
|
wenzelm@27524
|
148 |
let val id = point () in (id, the_result id) end);
|
wenzelm@27501
|
149 |
|
wenzelm@27518
|
150 |
fun state () = #1 (#2 (point_result ()));
|
wenzelm@27518
|
151 |
fun exn () = #2 (#2 (point_result ()));
|
wenzelm@26605
|
152 |
|
wenzelm@26605
|
153 |
fun context () =
|
wenzelm@26605
|
154 |
Toplevel.context_of (state ())
|
wenzelm@26605
|
155 |
handle Toplevel.UNDEF => error "Unknown context";
|
wenzelm@26605
|
156 |
|
wenzelm@26605
|
157 |
fun goal () =
|
wenzelm@26605
|
158 |
#2 (#2 (Proof.get_goal (Toplevel.proof_of (state ()))))
|
wenzelm@26605
|
159 |
handle Toplevel.UNDEF => error "No goal present";
|
wenzelm@26605
|
160 |
|
wenzelm@26605
|
161 |
|
wenzelm@26605
|
162 |
(* interactive state transformations --- NOT THREAD-SAFE! *)
|
wenzelm@26605
|
163 |
|
wenzelm@26605
|
164 |
nonfix >> >>>;
|
wenzelm@26605
|
165 |
|
wenzelm@27428
|
166 |
fun >> raw_tr =
|
wenzelm@27428
|
167 |
let
|
wenzelm@27428
|
168 |
val (id, tr) = identify raw_tr;
|
wenzelm@27518
|
169 |
val (prev, (prev_state, _)) = point_result ();
|
wenzelm@27524
|
170 |
val category = category_of tr;
|
wenzelm@27524
|
171 |
val _ = new_command prev (id, make_command (category, tr, Initial));
|
wenzelm@27428
|
172 |
in
|
wenzelm@27428
|
173 |
(case Toplevel.transition true tr prev_state of
|
wenzelm@27501
|
174 |
NONE => (dispose_command id; false)
|
wenzelm@27527
|
175 |
| SOME (result as (_, err)) =>
|
wenzelm@27527
|
176 |
(change_command_status id (K (Result result));
|
wenzelm@27527
|
177 |
Option.map (Toplevel.error_msg tr) err;
|
wenzelm@27527
|
178 |
if is_some err orelse category = Control then dispose_command id
|
wenzelm@27524
|
179 |
else set_point id;
|
wenzelm@27428
|
180 |
true))
|
wenzelm@27428
|
181 |
end;
|
wenzelm@26605
|
182 |
|
wenzelm@26605
|
183 |
fun >>> [] = ()
|
wenzelm@26605
|
184 |
| >>> (tr :: trs) = if >> tr then >>> trs else ();
|
wenzelm@26605
|
185 |
|
wenzelm@26605
|
186 |
|
wenzelm@27524
|
187 |
(* old-style navigation *)
|
wenzelm@27524
|
188 |
|
wenzelm@27524
|
189 |
local
|
wenzelm@27524
|
190 |
|
wenzelm@27524
|
191 |
fun err_undo () = error "Undo history exhausted";
|
wenzelm@27524
|
192 |
|
wenzelm@27524
|
193 |
fun get_prev id = the_default no_id (prev_command id);
|
wenzelm@27524
|
194 |
|
wenzelm@27524
|
195 |
fun find_command pred id =
|
wenzelm@27524
|
196 |
(case #category (the_command id) of
|
wenzelm@27524
|
197 |
Empty => err_undo ()
|
wenzelm@27524
|
198 |
| category => if pred category then id else find_command pred (get_prev id));
|
wenzelm@27524
|
199 |
|
wenzelm@27529
|
200 |
fun undo_command id =
|
wenzelm@27529
|
201 |
let val {category, transition, ...} = the_command id in
|
wenzelm@27524
|
202 |
(case Toplevel.init_of transition of
|
wenzelm@27529
|
203 |
SOME name => get_prev id before ThyInfo.kill_thy name
|
wenzelm@27529
|
204 |
| NONE => get_prev id)
|
wenzelm@27524
|
205 |
end;
|
wenzelm@27524
|
206 |
|
wenzelm@27529
|
207 |
fun undo_linear id = undo_command (find_command is_proper id);
|
wenzelm@27529
|
208 |
fun undo_nested id = undo_command
|
wenzelm@27529
|
209 |
(find_command (if Toplevel.is_proof (the_state id) then is_proper else is_theory) id);
|
wenzelm@27529
|
210 |
|
wenzelm@27524
|
211 |
in
|
wenzelm@27524
|
212 |
|
wenzelm@27529
|
213 |
fun linear_undo n = change_point (funpow n undo_linear);
|
wenzelm@27529
|
214 |
fun undo n = change_point (funpow n undo_nested);
|
wenzelm@27524
|
215 |
|
wenzelm@27524
|
216 |
end;
|
wenzelm@27524
|
217 |
|
wenzelm@27524
|
218 |
|
wenzelm@26606
|
219 |
(* toplevel loop *)
|
wenzelm@26605
|
220 |
|
wenzelm@26605
|
221 |
val crashes = ref ([]: exn list);
|
wenzelm@26605
|
222 |
|
wenzelm@26605
|
223 |
local
|
wenzelm@26605
|
224 |
|
wenzelm@26605
|
225 |
fun raw_loop secure src =
|
wenzelm@26605
|
226 |
let
|
wenzelm@26605
|
227 |
fun check_secure () =
|
wenzelm@26605
|
228 |
(if secure then warning "Secure loop -- cannot exit to ML" else (); secure);
|
wenzelm@27524
|
229 |
val prev = point ();
|
wenzelm@27524
|
230 |
val prev_name = Toplevel.name_of (#transition (the_command prev));
|
wenzelm@27428
|
231 |
val prompt_markup =
|
wenzelm@27524
|
232 |
prev <> no_id ? Markup.markup
|
wenzelm@27524
|
233 |
(Markup.properties [(Markup.idN, prev), (Markup.nameN, prev_name)] Markup.prompt);
|
wenzelm@26605
|
234 |
in
|
wenzelm@27428
|
235 |
(case Source.get_single (Source.set_prompt (prompt_markup Source.default_prompt) src) of
|
wenzelm@26606
|
236 |
NONE => if secure then quit () else ()
|
wenzelm@26606
|
237 |
| SOME (tr, src') => if >> tr orelse check_secure () then raw_loop secure src' else ())
|
wenzelm@26605
|
238 |
handle exn => (Output.error_msg (Toplevel.exn_message exn) handle crash =>
|
wenzelm@26605
|
239 |
(CRITICAL (fn () => change crashes (cons crash));
|
wenzelm@26605
|
240 |
warning "Recovering after Isar toplevel crash -- see also Isar.crashes");
|
wenzelm@26605
|
241 |
raw_loop secure src)
|
wenzelm@26605
|
242 |
end;
|
wenzelm@26605
|
243 |
|
wenzelm@26605
|
244 |
in
|
wenzelm@26605
|
245 |
|
wenzelm@27428
|
246 |
fun toplevel_loop {init, welcome, sync, secure} =
|
wenzelm@26605
|
247 |
(Context.set_thread_data NONE;
|
wenzelm@27528
|
248 |
if init then (set_point no_id; init_commands ()) else ();
|
wenzelm@26643
|
249 |
if welcome then writeln (Session.welcome ()) else ();
|
wenzelm@26606
|
250 |
uninterruptible (fn _ => fn () => raw_loop secure (OuterSyntax.isar sync)) ());
|
wenzelm@26605
|
251 |
|
wenzelm@26605
|
252 |
end;
|
wenzelm@26605
|
253 |
|
wenzelm@26643
|
254 |
fun loop () =
|
wenzelm@26643
|
255 |
toplevel_loop {init = false, welcome = false, sync = false, secure = Secure.is_secure ()};
|
wenzelm@27528
|
256 |
|
wenzelm@26643
|
257 |
fun main () =
|
wenzelm@26643
|
258 |
toplevel_loop {init = true, welcome = true, sync = false, secure = Secure.is_secure ()};
|
wenzelm@26605
|
259 |
|
wenzelm@26605
|
260 |
end;
|
wenzelm@26605
|
261 |
|