simplified Symbol based on lazy Symbol.Interpretation -- reduced odd "functorial style";
tuned implicit build/init messages;
1 /* Title: Pure/System/session.scala
3 Options: :folding=explicit:collapseFolds=1:
5 Main Isabelle/Scala session, potentially with running prover process.
10 import java.lang.System
12 import scala.actors.TIMEOUT
13 import scala.actors.Actor
14 import scala.actors.Actor._
19 /* abstract file store */
21 abstract class File_Store
23 def read(path: Path): String
29 case object Global_Settings
30 case object Perspective
31 case object Assignment
32 case class Commands_Changed(set: Set[Command])
34 sealed abstract class Phase
35 case object Inactive extends Phase
36 case object Startup extends Phase // transient
37 case object Failed extends Phase
38 case object Ready extends Phase
39 case object Shutdown extends Phase // transient
43 class Session(val file_store: Session.File_Store)
45 /* real time parameters */ // FIXME properties or settings (!?)
47 // user input (e.g. text edits, cursor movement)
48 val input_delay = Time.seconds(0.3)
50 // prover output (markup, common messages)
51 val output_delay = Time.seconds(0.1)
54 val update_delay = Time.seconds(0.5)
57 /* pervasive event buses */
59 val phase_changed = new Event_Bus[Session.Phase]
60 val global_settings = new Event_Bus[Session.Global_Settings.type]
61 val raw_messages = new Event_Bus[Isabelle_Process.Result]
62 val commands_changed = new Event_Bus[Session.Commands_Changed]
63 val perspective = new Event_Bus[Session.Perspective.type]
64 val assignments = new Event_Bus[Session.Assignment.type]
67 /** buffered command changes (delay_first discipline) **/
69 private case object Stop
71 private val (_, command_change_buffer) =
72 Simple_Thread.actor("command_change_buffer", daemon = true)
75 var changed: Set[Command] = Set()
76 var flush_time: Option[Long] = None
78 def flush_timeout: Long =
81 case Some(time) => (time - System.currentTimeMillis()) max 0
86 if (!changed.isEmpty) commands_changed.event(Session.Commands_Changed(changed))
93 val now = System.currentTimeMillis()
95 case None => flush_time = Some(now + output_delay.ms)
96 case Some(time) => if (now >= time) flush()
102 receiveWithin(flush_timeout) {
103 case command: Command => changed += command; invoke()
104 case TIMEOUT => flush()
105 case Stop => finished = true; reply(())
106 case bad => System.err.println("command_change_buffer: ignoring bad message " + bad)
114 /** main protocol actor **/
118 @volatile var loaded_theories: Set[String] = Set()
120 @volatile private var syntax = new Outer_Syntax
121 def current_syntax(): Outer_Syntax = syntax
123 @volatile private var reverse_syslog = List[XML.Elem]()
124 def syslog(): String = reverse_syslog.reverse.map(msg => XML.content(msg).mkString).mkString("\n")
126 @volatile private var _phase: Session.Phase = Session.Inactive
128 private def phase_=(new_phase: Session.Phase)
131 phase_changed.event(new_phase)
133 def is_ready: Boolean = phase == Session.Ready
135 private val global_state = new Volatile(Document.State.init)
136 def current_state(): Document.State = global_state.peek()
141 val thy_load = new Thy_Load
143 override def is_loaded(name: String): Boolean =
144 loaded_theories.contains(name)
146 override def check_thy(dir: Path, name: String): (String, Thy_Header.Header) =
148 val file = Isabelle_System.platform_file(dir + Thy_Header.thy_path(name))
149 if (!file.exists || !file.isFile) error("No such file: " + quote(file.toString))
150 val text = Standard_System.read_file(file)
151 val header = Thy_Header.read(text)
156 val thy_info = new Thy_Info(thy_load)
161 private case object Interrupt_Prover
162 private case class Edit_Node(thy_name: String,
163 header: Exn.Result[Thy_Header.Header], edits: List[Text.Edit])
164 private case class Init_Node(thy_name: String,
165 header: Exn.Result[Thy_Header.Header], text: String)
166 private case class Start(timeout: Time, args: List[String])
168 var verbose: Boolean = false
170 private val (_, session_actor) = Simple_Thread.actor("session_actor", daemon = true)
172 val this_actor = self
173 var prover: Option[Isabelle_Process with Isar_Document] = None
176 /* document changes */
178 def handle_change(change: Document.Change)
181 val previous = change.previous.get_finished
182 val (node_edits, version) = change.result.get_finished
184 var former_assignment = global_state.peek().the_assignment(previous).get_finished
186 (name, Some(cmd_edits)) <- node_edits
187 (prev, None) <- cmd_edits
188 removed <- previous.nodes(name).commands.get_after(prev)
189 } former_assignment -= removed
193 case (name, None) => (name, None)
194 case (name, Some(cmd_edits)) =>
198 val id1 = c1.map(_.id)
202 case Some(command) =>
203 if (global_state.peek().lookup_command(command.id).isEmpty) {
204 global_state.change(_.define_command(command))
205 prover.get.define_command(command.id, Symbol.encode(command.source))
213 global_state.change(_.define_version(version, former_assignment))
214 prover.get.edit_version(previous.id, version.id, id_edits)
221 def bad_result(result: Isabelle_Process.Result)
224 System.err.println("Ignoring prover result: " + result.message.toString)
227 def handle_result(result: Isabelle_Process.Result)
230 result.properties match {
231 case Position.Id(state_id) =>
233 val st = global_state.change_yield(_.accumulate(state_id, result.message))
234 command_change_buffer ! st.command
236 catch { case _: Document.State.Fail => bad_result(result) }
238 if (result.is_syslog) {
239 reverse_syslog ::= result.message
240 if (result.is_ready) {
241 // FIXME move to ML side
242 syntax += ("hence", Keyword.PRF_ASM_GOAL, "then have")
243 syntax += ("thus", Keyword.PRF_ASM_GOAL, "then show")
244 phase = Session.Ready
246 else if (result.is_exit && phase == Session.Startup) phase = Session.Failed
247 else if (result.is_exit) phase = Session.Inactive
249 else if (result.is_stdout) { }
250 else if (result.is_status) {
252 case List(Isar_Document.Assign(id, edits)) =>
254 val cmds: List[Command] = global_state.change_yield(_.assign(id, edits))
255 for (cmd <- cmds) command_change_buffer ! cmd
256 assignments.event(Session.Assignment)
258 catch { case _: Document.State.Fail => bad_result(result) }
259 case List(Keyword.Command_Decl(name, kind)) => syntax += (name, kind)
260 case List(Keyword.Keyword_Decl(name)) => syntax += name
261 case List(Thy_Info.Loaded_Theory(name)) => loaded_theories += name
262 case _ => bad_result(result)
265 else bad_result(result)
268 raw_messages.event(result)
273 def edit_version(edits: List[Document.Edit_Text])
276 val previous = global_state.peek().history.tip.version
277 val syntax = current_syntax()
278 val result = Future.fork { Thy_Syntax.text_edits(syntax, previous.join, edits) }
279 val change = global_state.change_yield(_.extend_history(previous, edits, result))
281 change.version.map(_ => {
282 assignments.await { global_state.peek().is_assigned(previous.get_finished) }
283 this_actor ! change })
293 case Interrupt_Prover =>
294 prover.map(_.interrupt)
296 case Edit_Node(thy_name, header, text_edits) if prover.isDefined =>
297 edit_version(List((thy_name, Some(text_edits))))
300 case Init_Node(thy_name, header, text) if prover.isDefined =>
301 // FIXME compare with existing node
304 (thy_name, Some(List(Text.Edit.insert(0, text))))))
307 case change: Document.Change if prover.isDefined =>
308 handle_change(change)
310 case result: Isabelle_Process.Result => handle_result(result)
312 case Start(timeout, args) if prover.isEmpty =>
313 if (phase == Session.Inactive || phase == Session.Failed) {
314 phase = Session.Startup
315 prover = Some(new Isabelle_Process(timeout, this_actor, args:_*) with Isar_Document)
319 if (phase == Session.Ready) {
320 global_state.change(_ => Document.State.init) // FIXME event bus!?
321 phase = Session.Shutdown
323 phase = Session.Inactive
328 case bad if prover.isDefined =>
329 System.err.println("session_actor: ignoring bad message " + bad)
338 def start(timeout: Time, args: List[String]) { session_actor ! Start(timeout, args) }
340 def stop() { command_change_buffer !? Stop; session_actor !? Stop }
342 def interrupt() { session_actor ! Interrupt_Prover }
344 def edit_node(thy_name: String, header: Exn.Result[Thy_Header.Header], edits: List[Text.Edit])
346 session_actor !? Edit_Node(thy_name, header, edits)
349 def init_node(thy_name: String, header: Exn.Result[Thy_Header.Header], text: String)
351 session_actor !? Init_Node(thy_name, header, text)
354 def snapshot(name: String, pending_edits: List[Text.Edit]): Document.Snapshot =
355 global_state.peek().snapshot(name, pending_edits)