more realistic kodkod invocation, imitating command-line tool;
authorwenzelm
Wed, 19 Aug 2020 20:14:41 +0200
changeset 72178 2481cdb84832
parent 72177 fbaa6b40b439
child 72179 0841895ca438
more realistic kodkod invocation, imitating command-line tool;
src/HOL/Tools/Nitpick/kodkod.scala
--- a/src/HOL/Tools/Nitpick/kodkod.scala	Wed Aug 19 13:29:53 2020 +0200
+++ b/src/HOL/Tools/Nitpick/kodkod.scala	Wed Aug 19 20:14:41 2020 +0200
@@ -10,51 +10,85 @@
 
 import java.util.concurrent.Executors
 
-import org.antlr.runtime.{ANTLRInputStream, Lexer, RecognitionException}
+import org.antlr.runtime.{ANTLRInputStream, RecognitionException}
 import de.tum.in.isabelle.Kodkodi.{Context, KodkodiLexer, KodkodiParser}
 
 
 object Kodkod
 {
+  sealed case class Result(rc: Int, out: String, err: String)
+
   def kodkod(source: String,
     solve_all: Boolean = false,
     prove: Boolean = false,
     max_solutions: Int = Integer.MAX_VALUE,
     cleanup_inst: Boolean = false,
-    max_threads: Int = 1): String =
+    timeout: Time = Time.zero,
+    max_threads: Int = 1): Result =
   {
+    val executor = Executors.newFixedThreadPool(max_threads)
+
+    def executor_kill(): Unit =
+      if (!executor.isShutdown) Isabelle_Thread.fork() { executor.shutdownNow() }
+
+    class Exit extends Exception
+
     val context =
       new Context {
+        private var rc = 0
         private val out = new StringBuilder
         private val err = new StringBuilder
-        def result: (String, String) = synchronized { (out.toString, err.toString) }
+
+        def return_code(i: Int): Unit = synchronized { rc = rc max i}
         override def output(s: String): Unit = synchronized { out ++= s; out += '\n' }
         override def error(s: String): Unit = synchronized { err ++= s; err += '\n' }
-        override def exit(rc: Int) { if (rc == 0) () else error("exit " + rc) }
+        override def exit(i: Int): Unit =
+          synchronized {
+            return_code(i)
+            executor_kill()
+            throw new Exit
+          }
+
+        def result(): Result = synchronized { Result(rc, out.toString, err.toString) }
       }
-    val executor = Executors.newFixedThreadPool(max_threads)
-    val lexer = new KodkodiLexer(new ANTLRInputStream(Bytes(source).stream))
-    val parser =
-      KodkodiParser.create(context, executor,
-        false, solve_all, prove, max_solutions, cleanup_inst, lexer)
+
+    try {
+      val lexer = new KodkodiLexer(new ANTLRInputStream(Bytes(source).stream))
+      val parser =
+        KodkodiParser.create(context, executor,
+          false, solve_all, prove, max_solutions, cleanup_inst, lexer)
 
-    val solution =
-        try { parser.problems() }
-        catch {
-          case exn: RecognitionException =>
-            parser.reportError(exn)
-            error("Parser error")
+      val timeout_request =
+        if (timeout.is_zero) None
+        else {
+          Some(Event_Timer.request(Time.now() + timeout) {
+            context.error("Ran out of time")
+            context.return_code(2)
+            executor_kill()
+          })
         }
-    if (parser.getTokenStream.LA(1) != KodkodiParser.EOF) {
-      error("Trailing tokens")
-    }
-    if (lexer.getNumberOfSyntaxErrors > 0) error("Lexical error")
-    if (parser.getNumberOfSyntaxErrors > 0) error("Syntax error")
+
+      try { parser.problems() }
+      catch { case exn: RecognitionException => parser.reportError(exn) }
+
+      timeout_request.foreach(_.cancel)
 
-    val (out, err) = context.result
-    Output.writeln(out)
-    Output.warning(err)
+      if (parser.getTokenStream.LA(1) != KodkodiParser.EOF) {
+        context.error("Error: trailing tokens")
+        context.exit(1)
+      }
+      if (lexer.getNumberOfSyntaxErrors + parser.getNumberOfSyntaxErrors > 0) {
+        context.exit(1)
+      }
+    }
+    catch {
+      case _: Exit =>
+      case exn: Throwable =>
+        val message = exn.getMessage
+        context.error(if (message.isEmpty) exn.toString else "Error: " + message)
+        context.return_code(1)
+    }
 
-    solution
+    context.result()
   }
 }