src/Pure/System/session.scala
author wenzelm
Thu, 07 Jul 2011 13:48:30 +0200
changeset 44569 5130dfe1b7be
parent 44548 29eb1cd29961
child 44571 77ce24aa1770
permissions -rw-r--r--
simplified Symbol based on lazy Symbol.Interpretation -- reduced odd "functorial style";
tuned implicit build/init messages;
     1 /*  Title:      Pure/System/session.scala
     2     Author:     Makarius
     3     Options:    :folding=explicit:collapseFolds=1:
     4 
     5 Main Isabelle/Scala session, potentially with running prover process.
     6 */
     7 
     8 package isabelle
     9 
    10 import java.lang.System
    11 
    12 import scala.actors.TIMEOUT
    13 import scala.actors.Actor
    14 import scala.actors.Actor._
    15 
    16 
    17 object Session
    18 {
    19   /* abstract file store */
    20 
    21   abstract class File_Store
    22   {
    23     def read(path: Path): String
    24   }
    25 
    26 
    27   /* events */
    28 
    29   case object Global_Settings
    30   case object Perspective
    31   case object Assignment
    32   case class Commands_Changed(set: Set[Command])
    33 
    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
    40 }
    41 
    42 
    43 class Session(val file_store: Session.File_Store)
    44 {
    45   /* real time parameters */  // FIXME properties or settings (!?)
    46 
    47   // user input (e.g. text edits, cursor movement)
    48   val input_delay = Time.seconds(0.3)
    49 
    50   // prover output (markup, common messages)
    51   val output_delay = Time.seconds(0.1)
    52 
    53   // GUI layout updates
    54   val update_delay = Time.seconds(0.5)
    55 
    56 
    57   /* pervasive event buses */
    58 
    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]
    65 
    66 
    67   /** buffered command changes (delay_first discipline) **/
    68 
    69   private case object Stop
    70 
    71   private val (_, command_change_buffer) =
    72     Simple_Thread.actor("command_change_buffer", daemon = true)
    73   //{{{
    74   {
    75     var changed: Set[Command] = Set()
    76     var flush_time: Option[Long] = None
    77 
    78     def flush_timeout: Long =
    79       flush_time match {
    80         case None => 5000L
    81         case Some(time) => (time - System.currentTimeMillis()) max 0
    82       }
    83 
    84     def flush()
    85     {
    86       if (!changed.isEmpty) commands_changed.event(Session.Commands_Changed(changed))
    87       changed = Set()
    88       flush_time = None
    89     }
    90 
    91     def invoke()
    92     {
    93       val now = System.currentTimeMillis()
    94       flush_time match {
    95         case None => flush_time = Some(now + output_delay.ms)
    96         case Some(time) => if (now >= time) flush()
    97       }
    98     }
    99 
   100     var finished = false
   101     while (!finished) {
   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)
   107       }
   108     }
   109   }
   110   //}}}
   111 
   112 
   113 
   114   /** main protocol actor **/
   115 
   116   /* global state */
   117 
   118   @volatile var loaded_theories: Set[String] = Set()
   119 
   120   @volatile private var syntax = new Outer_Syntax
   121   def current_syntax(): Outer_Syntax = syntax
   122 
   123   @volatile private var reverse_syslog = List[XML.Elem]()
   124   def syslog(): String = reverse_syslog.reverse.map(msg => XML.content(msg).mkString).mkString("\n")
   125 
   126   @volatile private var _phase: Session.Phase = Session.Inactive
   127   def phase = _phase
   128   private def phase_=(new_phase: Session.Phase)
   129   {
   130     _phase = new_phase
   131     phase_changed.event(new_phase)
   132   }
   133   def is_ready: Boolean = phase == Session.Ready
   134 
   135   private val global_state = new Volatile(Document.State.init)
   136   def current_state(): Document.State = global_state.peek()
   137 
   138 
   139   /* theory files */
   140 
   141   val thy_load = new Thy_Load
   142   {
   143     override def is_loaded(name: String): Boolean =
   144       loaded_theories.contains(name)
   145 
   146     override def check_thy(dir: Path, name: String): (String, Thy_Header.Header) =
   147     {
   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)
   152       (text, header)
   153     }
   154   }
   155 
   156   val thy_info = new Thy_Info(thy_load)
   157 
   158 
   159   /* actor messages */
   160 
   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])
   167 
   168   var verbose: Boolean = false
   169 
   170   private val (_, session_actor) = Simple_Thread.actor("session_actor", daemon = true)
   171   {
   172     val this_actor = self
   173     var prover: Option[Isabelle_Process with Isar_Document] = None
   174 
   175 
   176     /* document changes */
   177 
   178     def handle_change(change: Document.Change)
   179     //{{{
   180     {
   181       val previous = change.previous.get_finished
   182       val (node_edits, version) = change.result.get_finished
   183 
   184       var former_assignment = global_state.peek().the_assignment(previous).get_finished
   185       for {
   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
   190 
   191       val id_edits =
   192         node_edits map {
   193           case (name, None) => (name, None)
   194           case (name, Some(cmd_edits)) =>
   195             val ids =
   196               cmd_edits map {
   197                 case (c1, c2) =>
   198                   val id1 = c1.map(_.id)
   199                   val id2 =
   200                     c2 match {
   201                       case None => None
   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))
   206                         }
   207                         Some(command.id)
   208                     }
   209                   (id1, id2)
   210               }
   211             (name -> Some(ids))
   212         }
   213       global_state.change(_.define_version(version, former_assignment))
   214       prover.get.edit_version(previous.id, version.id, id_edits)
   215     }
   216     //}}}
   217 
   218 
   219     /* prover results */
   220 
   221     def bad_result(result: Isabelle_Process.Result)
   222     {
   223       if (verbose)
   224         System.err.println("Ignoring prover result: " + result.message.toString)
   225     }
   226 
   227     def handle_result(result: Isabelle_Process.Result)
   228     //{{{
   229     {
   230       result.properties match {
   231         case Position.Id(state_id) =>
   232           try {
   233             val st = global_state.change_yield(_.accumulate(state_id, result.message))
   234             command_change_buffer ! st.command
   235           }
   236           catch { case _: Document.State.Fail => bad_result(result) }
   237         case _ =>
   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
   245             }
   246             else if (result.is_exit && phase == Session.Startup) phase = Session.Failed
   247             else if (result.is_exit) phase = Session.Inactive
   248           }
   249           else if (result.is_stdout) { }
   250           else if (result.is_status) {
   251             result.body match {
   252               case List(Isar_Document.Assign(id, edits)) =>
   253                 try {
   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)
   257                 }
   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)
   263             }
   264           }
   265           else bad_result(result)
   266         }
   267 
   268       raw_messages.event(result)
   269     }
   270     //}}}
   271 
   272 
   273     def edit_version(edits: List[Document.Edit_Text])
   274     //{{{
   275     {
   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))
   280 
   281       change.version.map(_ => {
   282         assignments.await { global_state.peek().is_assigned(previous.get_finished) }
   283         this_actor ! change })
   284     }
   285     //}}}
   286 
   287 
   288     /* main loop */
   289 
   290     var finished = false
   291     while (!finished) {
   292       receive {
   293         case Interrupt_Prover =>
   294           prover.map(_.interrupt)
   295 
   296         case Edit_Node(thy_name, header, text_edits) if prover.isDefined =>
   297           edit_version(List((thy_name, Some(text_edits))))
   298           reply(())
   299 
   300         case Init_Node(thy_name, header, text) if prover.isDefined =>
   301           // FIXME compare with existing node
   302           edit_version(List(
   303             (thy_name, None),
   304             (thy_name, Some(List(Text.Edit.insert(0, text))))))
   305           reply(())
   306 
   307         case change: Document.Change if prover.isDefined =>
   308           handle_change(change)
   309 
   310         case result: Isabelle_Process.Result => handle_result(result)
   311 
   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)
   316           }
   317 
   318         case Stop =>
   319           if (phase == Session.Ready) {
   320             global_state.change(_ => Document.State.init)  // FIXME event bus!?
   321             phase = Session.Shutdown
   322             prover.get.terminate
   323             phase = Session.Inactive
   324           }
   325           finished = true
   326           reply(())
   327 
   328         case bad if prover.isDefined =>
   329           System.err.println("session_actor: ignoring bad message " + bad)
   330       }
   331     }
   332   }
   333 
   334 
   335 
   336   /** main methods **/
   337 
   338   def start(timeout: Time, args: List[String]) { session_actor ! Start(timeout, args) }
   339 
   340   def stop() { command_change_buffer !? Stop; session_actor !? Stop }
   341 
   342   def interrupt() { session_actor ! Interrupt_Prover }
   343 
   344   def edit_node(thy_name: String, header: Exn.Result[Thy_Header.Header], edits: List[Text.Edit])
   345   {
   346     session_actor !? Edit_Node(thy_name, header, edits)
   347   }
   348 
   349   def init_node(thy_name: String, header: Exn.Result[Thy_Header.Header], text: String)
   350   {
   351     session_actor !? Init_Node(thy_name, header, text)
   352   }
   353 
   354   def snapshot(name: String, pending_edits: List[Text.Edit]): Document.Snapshot =
   355     global_state.peek().snapshot(name, pending_edits)
   356 }