src/Tools/jEdit/src/prover/Prover.scala
author wenzelm
Tue, 27 Jan 2009 16:16:55 +0100
changeset 34497 184fda8cce04
parent 34494 47f9303db81d
child 34498 f97b764f956f
permissions -rw-r--r--
more explicit indication of mutable collections;

/*
 * Higher-level prover communication
 *
 * @author Johannes Hölzl, TU Munich
 * @author Fabian Immler, TU Munich
 * @author Makarius
 */

package isabelle.prover


import scala.collection.mutable
import scala.collection.immutable.{TreeSet}

import org.gjt.sp.util.Log

import isabelle.proofdocument.{ProofDocument, Text, Token}
import isabelle.IsarDocument


class Prover(isabelle_system: IsabelleSystem)
{
  private var _logic = isabelle_system.getenv_strict("ISABELLE_LOGIC")
  private var process: Isar = null

  private val commands = new mutable.HashMap[IsarDocument.Command_ID, Command]


  /* outer syntax keywords */

  val decl_info = new EventBus[(String, String)]

  private val keyword_decls = new mutable.HashSet[String] {
    override def +=(name: String) = {
      decl_info.event(name, OuterKeyword.MINOR)
      super.+=(name)
    }
  }
  private val command_decls = new mutable.HashMap[String, String] {
    override def +=(entry: (String, String)) = {
      decl_info.event(entry)
      super.+=(entry)
    }
  }
  def is_command_keyword(s: String): Boolean = command_decls.contains(s)


  /* completions */

  var _completions = new TreeSet[String]
  def completions = _completions
  /* // TODO: ask Makarius to make Interpretation.symbols public (here: read-only as 'symbol_map')
  val map = isabelle.jedit.Isabelle.symbols.symbol_map
  for(xsymb <- map.keys) {
    _completions += xsymb
    if(map(xsymb).get("abbrev").isDefined) _completions += map(xsymb)("abbrev")
  }
  */
  decl_info += (k_v => _completions += k_v._1)


  /* event handling */

  private var initialized = false

  val activated = new EventBus[Unit]
  val command_info = new EventBus[Command]
  val output_info = new EventBus[String]
  var document: ProofDocument = null


  def command_change(c: Command) = Swing.now { command_info.event(c) }

  private def handle_result(r: IsabelleProcess.Result)
  {
    val (id, st) = r.props.find(p => p._1 == Markup.ID) match
      { case None => (null, null)
        case Some((_, i)) => (i, commands.getOrElse(i, null)) }

    if (r.kind == IsabelleProcess.Kind.STDOUT || r.kind == IsabelleProcess.Kind.STDIN)
      Swing.now { output_info.event(r.result) }
    else if (r.kind == IsabelleProcess.Kind.WRITELN && !initialized) {
      initialized = true
      Swing.now {
        if (document != null) {
          document.activate()
          activated.event(())
        }
      }
    }
    else {
      if (st == null || (st.status != Command.Status.REMOVED && st.status != Command.Status.REMOVE)) {
        r.kind match {

          case IsabelleProcess.Kind.STATUS =>
            //{{{ handle all kinds of status messages here
            val tree = process.parse_message(r)
            tree match {
              case XML.Elem(Markup.MESSAGE, _, elems) =>
                for (elem <- elems) {
                  elem match {
                    //{{{
                    // command status
                    case XML.Elem(Markup.FINISHED, _, _) =>
                      st.status = Command.Status.FINISHED
                      command_change(st)
                    case XML.Elem(Markup.UNPROCESSED, _, _) =>
                      st.status = Command.Status.UNPROCESSED
                      command_change(st)
                    case XML.Elem(Markup.FAILED, _, _) =>
                      st.status = Command.Status.FAILED
                      command_change(st)
                    case XML.Elem(Markup.DISPOSED, _, _) =>
                      commands.removeKey(st.id)
                      st.status = Command.Status.REMOVED
                      command_change(st)

                    // command and keyword declarations
                    case XML.Elem(Markup.COMMAND_DECL,
                        (Markup.NAME, name) :: (Markup.KIND, kind) :: _, _) =>
                      command_decls += (name -> kind)
                    case XML.Elem(Markup.KEYWORD_DECL, (Markup.NAME, name) :: _, _) =>
                      keyword_decls += name

                    // other markup
                    case XML.Elem(kind,
                        (Markup.OFFSET, offset) :: (Markup.END_OFFSET, end_offset) ::
                             (Markup.ID, markup_id) :: _, _) =>
                      val begin = Int.unbox(java.lang.Integer.valueOf(offset)) - 1
                      val end = Int.unbox(java.lang.Integer.valueOf(end_offset)) - 1

                      val command =
                        // outer syntax: no id in props
                        if (st == null) commands.getOrElse(markup_id, null)
                        // inner syntax: id from props
                        else st
                      command.root_node.insert(command.node_from(kind, begin, end))

                    case _ =>
                    //}}}
                  }
                }
              case _ =>
            }
            //}}}

          case IsabelleProcess.Kind.ERROR if st != null =>
            val tree = process.parse_message(r)
            if (st.status != Command.Status.REMOVED && st.status != Command.Status.REMOVE)
              st.status = Command.Status.FAILED
            st.add_result(tree)
            command_change(st)

          case IsabelleProcess.Kind.WRITELN | IsabelleProcess.Kind.PRIORITY
            | IsabelleProcess.Kind.WARNING if st != null =>
            val tree = process.parse_message(r)
            st.add_result(tree)
            command_change(st)

          case _ =>
        }
      }
    }
  }


  def start(logic: String) {
    if (logic != null) _logic = logic

    val results = new EventBus[IsabelleProcess.Result]
    results += handle_result
    results.logger = Log.log(Log.ERROR, null, _)

    process = new Isar(isabelle_system, results, "-m", "xsymbols", _logic)
  }

  def stop() {
    process.kill
  }

  def set_document(text: Text, path: String) {
    this.document = new ProofDocument(text, this)
    process.ML("ThyLoad.add_path " + IsabelleSyntax.encode_string(path))

    document.structural_changes += (changes => {
      for (cmd <- changes.removed_commands) remove(cmd)
      for (cmd <- changes.added_commands) send(cmd)
    })
    if (initialized) {
      document.activate()
      activated.event(())
    }
  }

  private def send(cmd: Command) {
    cmd.status = Command.Status.UNPROCESSED
    commands.put(cmd.id, cmd)

    val content = isabelle_system.symbols.encode(cmd.content)
    process.create_command(cmd.id, content)
    process.insert_command(if (cmd.prev == null) "" else cmd.prev.id, cmd.id)
  }

  def remove(cmd: Command) {
    cmd.status = Command.Status.REMOVE
    process.remove_command(cmd.id)
  }
}