wenzelm@48240: (* Title: Pure/PIDE/command.ML wenzelm@48240: Author: Makarius wenzelm@48240: wenzelm@53671: Prover command execution: read -- eval -- print. wenzelm@48240: *) wenzelm@48240: wenzelm@48240: signature COMMAND = wenzelm@48240: sig wenzelm@53737: val read: (unit -> theory) -> Token.T list -> Toplevel.transition wenzelm@53737: type eval wenzelm@53744: val eval_eq: eval * eval -> bool wenzelm@53921: val eval_running: eval -> bool wenzelm@53909: val eval_finished: eval -> bool wenzelm@53673: val eval_result_state: eval -> Toplevel.state wenzelm@53672: val eval: (unit -> theory) -> Token.T list -> eval -> eval wenzelm@53737: type print wenzelm@53987: val print: bool -> (string * string list) list -> string -> wenzelm@53987: eval -> print list -> print list option wenzelm@53663: type print_fn = Toplevel.transition -> Toplevel.state -> unit wenzelm@54090: type print_function = wenzelm@54090: {command_name: string, args: string list} -> wenzelm@54090: {delay: Time.time option, pri: int, persistent: bool, strict: bool, print_fn: print_fn} option wenzelm@54090: val print_function: string -> print_function -> unit wenzelm@53708: val no_print_function: string -> unit wenzelm@53737: type exec = eval * print list wenzelm@53737: val no_exec: exec wenzelm@53737: val exec_ids: exec option -> Document_ID.exec list wenzelm@53743: val exec: Document_ID.execution -> exec -> unit wenzelm@48240: end; wenzelm@48240: wenzelm@48240: structure Command: COMMAND = wenzelm@48240: struct wenzelm@48240: wenzelm@53912: (** memo results **) wenzelm@48245: wenzelm@48245: datatype 'a expr = wenzelm@53733: Expr of Document_ID.exec * (unit -> 'a) | wenzelm@48245: Result of 'a Exn.result; wenzelm@48245: wenzelm@48245: abstype 'a memo = Memo of 'a expr Synchronized.var wenzelm@48245: with wenzelm@48245: wenzelm@53733: fun memo exec_id e = Memo (Synchronized.var "Command.memo" (Expr (exec_id, e))); wenzelm@48245: fun memo_value a = Memo (Synchronized.var "Command.memo" (Result (Exn.Res a))); wenzelm@48245: wenzelm@48246: fun memo_result (Memo v) = wenzelm@48246: (case Synchronized.value v of wenzelm@53744: Expr (exec_id, _) => error ("Unfinished execution result: " ^ Document_ID.print exec_id) wenzelm@53744: | Result res => Exn.release res); wenzelm@48245: wenzelm@53793: fun memo_finished (Memo v) = wenzelm@53922: (case Synchronized.value v of Expr _ => false | Result _ => true); wenzelm@53793: wenzelm@53744: fun memo_exec execution_id (Memo v) = wenzelm@53911: Synchronized.timed_access v (K (SOME Time.zeroTime)) wenzelm@53746: (fn expr => wenzelm@53746: (case expr of wenzelm@53912: Expr (exec_id, body) => wenzelm@53746: uninterruptible (fn restore_attributes => fn () => wenzelm@55286: let val group = Future.worker_subgroup () in wenzelm@55286: if Execution.running execution_id exec_id [group] then wenzelm@55286: let wenzelm@55286: val res = wenzelm@55286: (body wenzelm@55286: |> restore_attributes wenzelm@55286: |> Future.worker_context "Command.memo_exec" group wenzelm@55286: |> Exn.interruptible_capture) (); wenzelm@55286: in SOME ((), Result res) end wenzelm@55286: else SOME ((), expr) wenzelm@55286: end) () wenzelm@53912: | Result _ => SOME ((), expr))) wenzelm@53912: |> (fn NONE => error "Conflicting command execution" | _ => ()); wenzelm@53733: wenzelm@53744: fun memo_fork params execution_id (Memo v) = wenzelm@53733: (case Synchronized.value v of wenzelm@53733: Result _ => () wenzelm@53744: | _ => ignore ((singleton o Future.forks) params (fn () => memo_exec execution_id (Memo v)))); wenzelm@53663: wenzelm@48245: end; wenzelm@48245: wenzelm@48245: wenzelm@53744: wenzelm@53673: (** main phases of execution **) wenzelm@53673: wenzelm@53647: (* read *) wenzelm@53646: wenzelm@53671: fun read init span = wenzelm@53647: let wenzelm@53647: val outer_syntax = #2 (Outer_Syntax.get_syntax ()); wenzelm@53647: val command_reports = Outer_Syntax.command_reports outer_syntax; wenzelm@53646: wenzelm@53671: val proper_range = wenzelm@53671: Position.set_range (Token.position_range_of (#1 (take_suffix Token.is_improper span))); wenzelm@53647: val pos = wenzelm@53647: (case find_first Token.is_command span of wenzelm@53647: SOME tok => Token.position_of tok wenzelm@53647: | NONE => proper_range); wenzelm@53646: wenzelm@53647: val (is_malformed, token_reports) = Thy_Syntax.reports_of_tokens span; wenzelm@53647: val _ = Position.reports_text (token_reports @ maps command_reports span); wenzelm@53647: in wenzelm@53647: if is_malformed then Toplevel.malformed pos "Malformed command syntax" wenzelm@53647: else wenzelm@53647: (case Outer_Syntax.read_spans outer_syntax span of wenzelm@53647: [tr] => wenzelm@53647: if Keyword.is_control (Toplevel.name_of tr) then wenzelm@53647: Toplevel.malformed pos "Illegal control command" wenzelm@53671: else Toplevel.modify_init init tr wenzelm@53671: | [] => Toplevel.ignored (Position.set_range (Token.position_range_of span)) wenzelm@53647: | _ => Toplevel.malformed proper_range "Exactly one command expected") wenzelm@53647: handle ERROR msg => Toplevel.malformed proper_range msg wenzelm@53647: end; wenzelm@53646: wenzelm@53646: wenzelm@53646: (* eval *) wenzelm@48240: wenzelm@53737: type eval_state = wenzelm@53737: {failed: bool, malformed: bool, command: Toplevel.transition, state: Toplevel.state}; wenzelm@53737: val init_eval_state = wenzelm@53737: {failed = false, malformed = false, command = Toplevel.empty, state = Toplevel.toplevel}; wenzelm@53737: wenzelm@53737: datatype eval = Eval of {exec_id: Document_ID.exec, eval_process: eval_state memo}; wenzelm@53737: wenzelm@53744: fun eval_eq (Eval {exec_id, ...}, Eval {exec_id = exec_id', ...}) = exec_id = exec_id'; wenzelm@53744: wenzelm@53921: fun eval_running (Eval {exec_id, ...}) = Execution.is_running_exec exec_id; wenzelm@53909: fun eval_finished (Eval {eval_process, ...}) = memo_finished eval_process; wenzelm@53909: wenzelm@53737: fun eval_result (Eval {eval_process, ...}) = memo_result eval_process; wenzelm@53737: val eval_result_state = #state o eval_result; wenzelm@53737: wenzelm@48240: local wenzelm@48240: wenzelm@48240: fun run int tr st = wenzelm@54326: if Goal.future_enabled 1 andalso Keyword.is_diag (Toplevel.name_of tr) then wenzelm@54329: (Execution.fork {name = "Toplevel.diag", pos = Toplevel.pos_of tr, pri = ~1} wenzelm@52742: (fn () => Toplevel.command_exception int tr st); ([], SOME st)) wenzelm@52421: else Toplevel.command_errors int tr st; wenzelm@48240: wenzelm@53647: fun check_cmts span tr st' = wenzelm@53647: Toplevel.setmp_thread_position tr wenzelm@53647: (fn () => wenzelm@53647: Outer_Syntax.side_comments span |> maps (fn cmt => wenzelm@53647: (Thy_Output.check_text (Token.source_position_of cmt) st'; []) wenzelm@53756: handle exn => wenzelm@53756: if Exn.is_interrupt exn then reraise exn wenzelm@53756: else ML_Compiler.exn_messages_ids exn)) (); wenzelm@53647: wenzelm@48240: fun proof_status tr st = wenzelm@48240: (case try Toplevel.proof_of st of wenzelm@48240: SOME prf => Toplevel.status tr (Proof.status_markup prf) wenzelm@48240: | NONE => ()); wenzelm@48240: wenzelm@53671: fun eval_state span tr ({malformed, state = st, ...}: eval_state) = wenzelm@53646: if malformed then wenzelm@53663: {failed = true, malformed = malformed, command = tr, state = Toplevel.toplevel} wenzelm@49787: else wenzelm@49787: let wenzelm@49787: val malformed' = Toplevel.is_malformed tr; wenzelm@49787: val is_init = Toplevel.is_init tr; wenzelm@49787: val is_proof = Keyword.is_proof (Toplevel.name_of tr); wenzelm@48240: wenzelm@49787: val _ = Multithreading.interrupted (); wenzelm@51216: val _ = Toplevel.status tr Markup.running; wenzelm@49933: val (errs1, result) = run (is_init orelse is_proof) (Toplevel.set_print false tr) st; wenzelm@53646: val errs2 = (case result of NONE => [] | SOME st' => check_cmts span tr st'); wenzelm@49933: val errs = errs1 @ errs2; wenzelm@51216: val _ = Toplevel.status tr Markup.finished; wenzelm@51929: val _ = List.app (Future.error_msg (Toplevel.pos_of tr)) errs; wenzelm@49787: in wenzelm@49787: (case result of wenzelm@49787: NONE => wenzelm@49787: let wenzelm@49787: val _ = if null errs then Exn.interrupt () else (); wenzelm@51216: val _ = Toplevel.status tr Markup.failed; wenzelm@53663: in {failed = true, malformed = malformed', command = tr, state = st} end wenzelm@49787: | SOME st' => wenzelm@49787: let wenzelm@49787: val _ = proof_status tr st'; wenzelm@53663: in {failed = false, malformed = malformed', command = tr, state = st'} end) wenzelm@49787: end; wenzelm@48240: wenzelm@53671: in wenzelm@53671: wenzelm@53671: fun eval init span eval0 = wenzelm@53671: let wenzelm@53671: val exec_id = Document_ID.make (); wenzelm@53671: fun process () = wenzelm@53671: let wenzelm@53671: val tr = wenzelm@53671: Position.setmp_thread_data (Position.id_only (Document_ID.print exec_id)) wenzelm@53673: (fn () => read init span |> Toplevel.exec_id exec_id) (); wenzelm@53671: in eval_state span tr (eval_result eval0) end; wenzelm@53737: in Eval {exec_id = exec_id, eval_process = memo exec_id process} end; wenzelm@53671: wenzelm@48240: end; wenzelm@48240: wenzelm@53646: wenzelm@53646: (* print *) wenzelm@53646: wenzelm@53737: datatype print = Print of wenzelm@53987: {name: string, args: string list, delay: Time.time option, pri: int, persistent: bool, wenzelm@53737: exec_id: Document_ID.exec, print_process: unit memo}; wenzelm@53737: wenzelm@53663: type print_fn = Toplevel.transition -> Toplevel.state -> unit; wenzelm@53652: wenzelm@53784: type print_function = wenzelm@53987: {command_name: string, args: string list} -> wenzelm@54090: {delay: Time.time option, pri: int, persistent: bool, strict: bool, print_fn: print_fn} option; wenzelm@53784: wenzelm@53648: local wenzelm@53648: wenzelm@53784: val print_functions = wenzelm@53784: Synchronized.var "Command.print_functions" ([]: (string * print_function) list); wenzelm@53648: wenzelm@53707: fun print_error tr e = wenzelm@54846: (Toplevel.setmp_thread_position tr o Toplevel.controlled_execution) e () wenzelm@53756: handle exn => wenzelm@53756: if Exn.is_interrupt exn then reraise exn wenzelm@53756: else List.app (Future.error_msg (Toplevel.pos_of tr)) (ML_Compiler.exn_messages_ids exn); wenzelm@53653: wenzelm@53744: fun print_eq (Print {exec_id, ...}, Print {exec_id = exec_id', ...}) = exec_id = exec_id'; wenzelm@53744: wenzelm@53912: fun print_finished (Print {print_process, ...}) = memo_finished print_process; wenzelm@53793: wenzelm@53737: fun print_persistent (Print {persistent, ...}) = persistent; wenzelm@53733: wenzelm@53990: val overlay_ord = prod_ord string_ord (list_ord string_ord); wenzelm@53990: wenzelm@53648: in wenzelm@53646: wenzelm@53987: fun print command_visible command_overlays command_name eval old_prints = wenzelm@53707: let wenzelm@53987: val print_functions = Synchronized.value print_functions; wenzelm@53987: wenzelm@54136: fun make_print name args {delay, pri, persistent, strict, print_fn} = wenzelm@54136: let wenzelm@54136: val exec_id = Document_ID.make (); wenzelm@54136: fun process () = wenzelm@54136: let wenzelm@54136: val {failed, command, state = st', ...} = eval_result eval; wenzelm@54136: val tr = Toplevel.exec_id exec_id command; wenzelm@54136: in wenzelm@54136: if failed andalso strict then () wenzelm@54136: else print_error tr (fn () => print_fn tr st') wenzelm@54136: end; wenzelm@54136: in wenzelm@54136: Print { wenzelm@54136: name = name, args = args, delay = delay, pri = pri, persistent = persistent, wenzelm@54136: exec_id = exec_id, print_process = memo exec_id process} wenzelm@54136: end; wenzelm@54136: wenzelm@54136: fun bad_print name args exn = wenzelm@54136: make_print name args {delay = NONE, pri = 0, persistent = false, wenzelm@54136: strict = false, print_fn = fn _ => fn _ => reraise exn}; wenzelm@54136: wenzelm@53987: fun new_print name args get_pr = wenzelm@53707: let wenzelm@53987: val params = {command_name = command_name, args = args}; wenzelm@53707: in wenzelm@54846: (case Exn.capture (Toplevel.controlled_execution get_pr) params of wenzelm@53707: Exn.Res NONE => NONE wenzelm@54136: | Exn.Res (SOME pr) => SOME (make_print name args pr) wenzelm@54136: | Exn.Exn exn => SOME (bad_print name args exn)) wenzelm@53707: end; wenzelm@53707: wenzelm@53987: fun get_print (a, b) = wenzelm@53987: (case find_first (fn Print {name, args, ...} => name = a andalso args = b) old_prints of wenzelm@53987: NONE => wenzelm@53987: (case AList.lookup (op =) print_functions a of wenzelm@54136: NONE => SOME (bad_print a b (ERROR ("Missing print function " ^ quote a))) wenzelm@53987: | SOME get_pr => new_print a b get_pr) wenzelm@53987: | some => some); wenzelm@53987: wenzelm@53707: val new_prints = wenzelm@53707: if command_visible then wenzelm@53990: fold (fn (a, _) => cons (a, [])) print_functions command_overlays wenzelm@53990: |> sort_distinct overlay_ord wenzelm@53987: |> map_filter get_print wenzelm@53793: else filter (fn print => print_finished print andalso print_persistent print) old_prints; wenzelm@53707: in wenzelm@53737: if eq_list print_eq (old_prints, new_prints) then NONE else SOME new_prints wenzelm@53707: end; wenzelm@53648: wenzelm@53784: fun print_function name f = wenzelm@53648: Synchronized.change print_functions (fn funs => wenzelm@53648: (if not (AList.defined (op =) funs name) then () wenzelm@53648: else warning ("Redefining command print function: " ^ quote name); wenzelm@53784: AList.update (op =) (name, f) funs)); wenzelm@53648: wenzelm@53708: fun no_print_function name = wenzelm@53708: Synchronized.change print_functions (filter_out (equal name o #1)); wenzelm@53708: wenzelm@53648: end; wenzelm@53648: wenzelm@53663: val _ = wenzelm@53784: print_function "print_state" wenzelm@53987: (fn {command_name, ...} => wenzelm@54541: SOME {delay = NONE, pri = 1, persistent = false, strict = true, wenzelm@53788: print_fn = fn tr => fn st' => wenzelm@53788: let wenzelm@53788: val is_init = Keyword.is_theory_begin command_name; wenzelm@53788: val is_proof = Keyword.is_proof command_name; wenzelm@53788: val do_print = wenzelm@53788: not is_init andalso wenzelm@53788: (Toplevel.print_of tr orelse (is_proof andalso Toplevel.is_proof st')); wenzelm@53788: in if do_print then Toplevel.print_state false st' else () end}); wenzelm@53646: wenzelm@53669: wenzelm@53737: (* combined execution *) wenzelm@53669: wenzelm@53737: type exec = eval * print list; wenzelm@53737: val no_exec: exec = wenzelm@53737: (Eval {exec_id = Document_ID.none, eval_process = memo_value init_eval_state}, []); wenzelm@53737: wenzelm@53737: fun exec_ids NONE = [] wenzelm@53737: | exec_ids (SOME (Eval {exec_id, ...}, prints)) = wenzelm@53737: exec_id :: map (fn Print {exec_id, ...} => exec_id) prints; wenzelm@53737: wenzelm@53737: local wenzelm@53737: wenzelm@53788: fun run_print execution_id (Print {name, delay, pri, print_process, ...}) = wenzelm@55113: if Multithreading.enabled () orelse pri <= 0 then wenzelm@53788: let wenzelm@53788: val group = Future.worker_subgroup (); wenzelm@53788: fun fork () = wenzelm@53788: memo_fork {name = name, group = SOME group, deps = [], pri = pri, interrupts = true} wenzelm@53788: execution_id print_process; wenzelm@53788: in wenzelm@53899: (case delay of wenzelm@53899: NONE => fork () wenzelm@53899: | SOME d => ignore (Event_Timer.request (Time.+ (Time.now (), d)) fork)) wenzelm@53788: end wenzelm@53788: else memo_exec execution_id print_process; wenzelm@53696: wenzelm@53737: in wenzelm@53737: wenzelm@53744: fun exec execution_id (Eval {eval_process, ...}, prints) = wenzelm@53744: (memo_exec execution_id eval_process; List.app (run_print execution_id) prints); wenzelm@53669: wenzelm@48240: end; wenzelm@48240: wenzelm@53737: end; wenzelm@53737: