| author | wenzelm | 
| Fri, 04 Jun 2021 22:50:32 +0200 | |
| changeset 73803 | 2141d6c83511 | 
| parent 73702 | 7202e12cb324 | 
| child 73987 | fc363a3b690a | 
| permissions | -rw-r--r-- | 
| 71849 | 1 | /* Title: Pure/System/scala.scala | 
| 43744 
2c7e1565b4a3
some support to invoke Scala methods under program control;
 wenzelm parents: diff
changeset | 2 | Author: Makarius | 
| 
2c7e1565b4a3
some support to invoke Scala methods under program control;
 wenzelm parents: diff
changeset | 3 | |
| 71849 | 4 | Support for Scala at runtime. | 
| 43744 
2c7e1565b4a3
some support to invoke Scala methods under program control;
 wenzelm parents: diff
changeset | 5 | */ | 
| 
2c7e1565b4a3
some support to invoke Scala methods under program control;
 wenzelm parents: diff
changeset | 6 | |
| 
2c7e1565b4a3
some support to invoke Scala methods under program control;
 wenzelm parents: diff
changeset | 7 | package isabelle | 
| 
2c7e1565b4a3
some support to invoke Scala methods under program control;
 wenzelm parents: diff
changeset | 8 | |
| 
2c7e1565b4a3
some support to invoke Scala methods under program control;
 wenzelm parents: diff
changeset | 9 | |
| 71864 | 10 | import java.io.{File => JFile, StringWriter, PrintWriter}
 | 
| 56730 
e723f041b6d0
tuned signature -- separate pool for JFuture tasks, which can be canceled;
 wenzelm parents: 
56667diff
changeset | 11 | |
| 71868 | 12 | import scala.tools.nsc.{GenericRunnerSettings, ConsoleWriter, NewLinePrintWriter}
 | 
| 72294 
25c6423ec538
clarified signature: proper eval/print via interpret;
 wenzelm parents: 
72213diff
changeset | 13 | import scala.tools.nsc.interpreter.{IMain, Results}
 | 
| 73136 | 14 | import scala.tools.nsc.interpreter.shell.ReplReporterImpl | 
| 43744 
2c7e1565b4a3
some support to invoke Scala methods under program control;
 wenzelm parents: diff
changeset | 15 | |
| 71849 | 16 | object Scala | 
| 43744 
2c7e1565b4a3
some support to invoke Scala methods under program control;
 wenzelm parents: diff
changeset | 17 | {
 | 
| 72193 | 18 | /** registered functions **/ | 
| 19 | ||
| 73419 
22f3f2117ed7
clarified signature: function_thread is determined in Isabelle/Scala, not Isabelle/ML;
 wenzelm parents: 
73418diff
changeset | 20 | abstract class Fun(val name: String, val thread: Boolean = false) | 
| 72193 | 21 |   {
 | 
| 22 | override def toString: String = name | |
| 73565 | 23 | def multi: Boolean = true | 
| 72756 | 24 | def position: Properties.T = here.position | 
| 25 | def here: Scala_Project.Here | |
| 73565 | 26 | def invoke(args: List[Bytes]): List[Bytes] | 
| 27 | } | |
| 28 | ||
| 29 | abstract class Fun_Strings(name: String, thread: Boolean = false) | |
| 30 | extends Fun(name, thread = thread) | |
| 31 |   {
 | |
| 32 | override def invoke(args: List[Bytes]): List[Bytes] = | |
| 33 | apply(args.map(_.text)).map(Bytes.apply) | |
| 34 | def apply(args: List[String]): List[String] | |
| 35 | } | |
| 36 | ||
| 37 | abstract class Fun_String(name: String, thread: Boolean = false) | |
| 38 | extends Fun_Strings(name, thread = thread) | |
| 39 |   {
 | |
| 40 | override def multi: Boolean = false | |
| 41 | override def apply(args: List[String]): List[String] = | |
| 73571 | 42 | List(apply(Library.the_single(args))) | 
| 72193 | 43 | def apply(arg: String): String | 
| 44 | } | |
| 45 | ||
| 46 | class Functions(val functions: Fun*) extends Isabelle_System.Service | |
| 47 | ||
| 48 | lazy val functions: List[Fun] = | |
| 49 | Isabelle_System.make_services(classOf[Functions]).flatMap(_.functions) | |
| 50 | ||
| 51 | ||
| 52 | ||
| 72152 | 53 | /** demo functions **/ | 
| 54 | ||
| 73565 | 55 |   object Echo extends Fun_String("echo")
 | 
| 72193 | 56 |   {
 | 
| 72756 | 57 | val here = Scala_Project.here | 
| 72193 | 58 | def apply(arg: String): String = arg | 
| 59 | } | |
| 72152 | 60 | |
| 73565 | 61 |   object Sleep extends Fun_String("sleep")
 | 
| 72152 | 62 |   {
 | 
| 72756 | 63 | val here = Scala_Project.here | 
| 72193 | 64 | def apply(seconds: String): String = | 
| 65 |     {
 | |
| 66 | val t = | |
| 67 |         seconds match {
 | |
| 68 | case Value.Double(s) => Time.seconds(s) | |
| 69 |           case _ => error("Malformed argument: " + quote(seconds))
 | |
| 70 | } | |
| 71 | val t0 = Time.now() | |
| 73702 
7202e12cb324
tuned signature --- following hints by IntelliJ IDEA;
 wenzelm parents: 
73576diff
changeset | 72 | t.sleep() | 
| 72193 | 73 | val t1 = Time.now() | 
| 74 | (t1 - t0).toString | |
| 75 | } | |
| 72152 | 76 | } | 
| 77 | ||
| 78 | ||
| 79 | ||
| 71870 | 80 | /** compiler **/ | 
| 71850 | 81 | |
| 71864 | 82 | object Compiler | 
| 71850 | 83 |   {
 | 
| 71868 | 84 | def context( | 
| 85 | error: String => Unit = Exn.error, | |
| 86 | jar_dirs: List[JFile] = Nil): Context = | |
| 71864 | 87 |     {
 | 
| 88 | def find_jars(dir: JFile): List[String] = | |
| 89 |         File.find_files(dir, file => file.getName.endsWith(".jar")).
 | |
| 90 | map(File.absolute_name) | |
| 91 | ||
| 92 | val class_path = | |
| 93 |         for {
 | |
| 71882 | 94 |           prop <- List("isabelle.scala.classpath", "java.class.path")
 | 
| 71864 | 95 | path = System.getProperty(prop, "") if path != "\"\"" | 
| 96 | elem <- space_explode(JFile.pathSeparatorChar, path) | |
| 97 | } yield elem | |
| 98 | ||
| 71868 | 99 | val settings = new GenericRunnerSettings(error) | 
| 100 | settings.classpath.value = | |
| 101 | (class_path ::: jar_dirs.flatMap(find_jars)).mkString(JFile.pathSeparator) | |
| 71850 | 102 | |
| 71868 | 103 | new Context(settings) | 
| 71864 | 104 | } | 
| 71850 | 105 | |
| 71868 | 106 | def default_print_writer: PrintWriter = | 
| 107 | new NewLinePrintWriter(new ConsoleWriter, true) | |
| 108 | ||
| 109 | class Context private [Compiler](val settings: GenericRunnerSettings) | |
| 71864 | 110 |     {
 | 
| 71869 | 111 | override def toString: String = settings.toString | 
| 112 | ||
| 71868 | 113 | def interpreter( | 
| 114 | print_writer: PrintWriter = default_print_writer, | |
| 115 | class_loader: ClassLoader = null): IMain = | |
| 116 |       {
 | |
| 73136 | 117 | new IMain(settings, new ReplReporterImpl(settings, print_writer)) | 
| 71868 | 118 |         {
 | 
| 119 | override def parentClassLoader: ClassLoader = | |
| 120 | if (class_loader == null) super.parentClassLoader | |
| 121 | else class_loader | |
| 122 | } | |
| 123 | } | |
| 71850 | 124 | |
| 72294 
25c6423ec538
clarified signature: proper eval/print via interpret;
 wenzelm parents: 
72213diff
changeset | 125 | def toplevel(interpret: Boolean, source: String): List[String] = | 
| 71868 | 126 |       {
 | 
| 127 | val out = new StringWriter | |
| 128 | val interp = interpreter(new PrintWriter(out)) | |
| 73356 | 129 | val marker = '\u000b' | 
| 72294 
25c6423ec538
clarified signature: proper eval/print via interpret;
 wenzelm parents: 
72213diff
changeset | 130 | val ok = | 
| 73356 | 131 |           interp.withLabel(marker.toString) {
 | 
| 72294 
25c6423ec538
clarified signature: proper eval/print via interpret;
 wenzelm parents: 
72213diff
changeset | 132 | if (interpret) interp.interpret(source) == Results.Success | 
| 
25c6423ec538
clarified signature: proper eval/print via interpret;
 wenzelm parents: 
72213diff
changeset | 133 | else (new interp.ReadEvalPrint).compile(source) | 
| 
25c6423ec538
clarified signature: proper eval/print via interpret;
 wenzelm parents: 
72213diff
changeset | 134 | } | 
| 73367 | 135 | out.close() | 
| 71864 | 136 | |
| 71868 | 137 | val Error = """(?s)^\S* error: (.*)$""".r | 
| 138 | val errors = | |
| 73356 | 139 | space_explode(marker, Library.strip_ansi_color(out.toString)). | 
| 71868 | 140 |             collect({ case Error(msg) => "Scala error: " + Library.trim_line(msg) })
 | 
| 141 | ||
| 142 |         if (!ok && errors.isEmpty) List("Error") else errors
 | |
| 143 | } | |
| 71864 | 144 | } | 
| 72194 | 145 | } | 
| 71850 | 146 | |
| 73565 | 147 |   object Toplevel extends Fun_String("scala_toplevel")
 | 
| 72194 | 148 |   {
 | 
| 72756 | 149 | val here = Scala_Project.here | 
| 72294 
25c6423ec538
clarified signature: proper eval/print via interpret;
 wenzelm parents: 
72213diff
changeset | 150 | def apply(arg: String): String = | 
| 72193 | 151 |     {
 | 
| 72294 
25c6423ec538
clarified signature: proper eval/print via interpret;
 wenzelm parents: 
72213diff
changeset | 152 | val (interpret, source) = | 
| 
25c6423ec538
clarified signature: proper eval/print via interpret;
 wenzelm parents: 
72213diff
changeset | 153 |         YXML.parse_body(arg) match {
 | 
| 
25c6423ec538
clarified signature: proper eval/print via interpret;
 wenzelm parents: 
72213diff
changeset | 154 | case Nil => (false, "") | 
| 
25c6423ec538
clarified signature: proper eval/print via interpret;
 wenzelm parents: 
72213diff
changeset | 155 | case List(XML.Text(source)) => (false, source) | 
| 
25c6423ec538
clarified signature: proper eval/print via interpret;
 wenzelm parents: 
72213diff
changeset | 156 | case body => import XML.Decode._; pair(bool, string)(body) | 
| 
25c6423ec538
clarified signature: proper eval/print via interpret;
 wenzelm parents: 
72213diff
changeset | 157 | } | 
| 72194 | 158 | val errors = | 
| 72294 
25c6423ec538
clarified signature: proper eval/print via interpret;
 wenzelm parents: 
72213diff
changeset | 159 |         try { Compiler.context().toplevel(interpret, source) }
 | 
| 72194 | 160 |         catch { case ERROR(msg) => List(msg) }
 | 
| 161 |       locally { import XML.Encode._; YXML.string_of_body(list(string)(errors)) }
 | |
| 72193 | 162 | } | 
| 71872 | 163 | } | 
| 164 | ||
| 71850 | 165 | |
| 166 | ||
| 71871 
28def00726ca
more robust isabelle.Functions --- avoid Java reflection with unclear class/object treatment;
 wenzelm parents: 
71870diff
changeset | 167 | /** invoke Scala functions from ML **/ | 
| 
28def00726ca
more robust isabelle.Functions --- avoid Java reflection with unclear class/object treatment;
 wenzelm parents: 
71870diff
changeset | 168 | |
| 
28def00726ca
more robust isabelle.Functions --- avoid Java reflection with unclear class/object treatment;
 wenzelm parents: 
71870diff
changeset | 169 | /* invoke function */ | 
| 43748 | 170 | |
| 171 | object Tag extends Enumeration | |
| 43744 
2c7e1565b4a3
some support to invoke Scala methods under program control;
 wenzelm parents: diff
changeset | 172 |   {
 | 
| 65638 | 173 | val NULL, OK, ERROR, FAIL, INTERRUPT = Value | 
| 43748 | 174 | } | 
| 43744 
2c7e1565b4a3
some support to invoke Scala methods under program control;
 wenzelm parents: diff
changeset | 175 | |
| 73419 
22f3f2117ed7
clarified signature: function_thread is determined in Isabelle/Scala, not Isabelle/ML;
 wenzelm parents: 
73418diff
changeset | 176 | def function_thread(name: String): Boolean = | 
| 
22f3f2117ed7
clarified signature: function_thread is determined in Isabelle/Scala, not Isabelle/ML;
 wenzelm parents: 
73418diff
changeset | 177 |     functions.find(fun => fun.name == name) match {
 | 
| 
22f3f2117ed7
clarified signature: function_thread is determined in Isabelle/Scala, not Isabelle/ML;
 wenzelm parents: 
73418diff
changeset | 178 | case Some(fun) => fun.thread | 
| 
22f3f2117ed7
clarified signature: function_thread is determined in Isabelle/Scala, not Isabelle/ML;
 wenzelm parents: 
73418diff
changeset | 179 | case None => false | 
| 
22f3f2117ed7
clarified signature: function_thread is determined in Isabelle/Scala, not Isabelle/ML;
 wenzelm parents: 
73418diff
changeset | 180 | } | 
| 
22f3f2117ed7
clarified signature: function_thread is determined in Isabelle/Scala, not Isabelle/ML;
 wenzelm parents: 
73418diff
changeset | 181 | |
| 73565 | 182 | def function_body(name: String, args: List[Bytes]): (Tag.Value, List[Bytes]) = | 
| 71873 | 183 |     functions.find(fun => fun.name == name) match {
 | 
| 184 | case Some(fun) => | |
| 73565 | 185 |         Exn.capture { fun.invoke(args) } match {
 | 
| 186 | case Exn.Res(null) => (Tag.NULL, Nil) | |
| 43748 | 187 | case Exn.Res(res) => (Tag.OK, res) | 
| 73565 | 188 | case Exn.Exn(Exn.Interrupt()) => (Tag.INTERRUPT, Nil) | 
| 189 | case Exn.Exn(e) => (Tag.ERROR, List(Bytes(Exn.message(e)))) | |
| 43748 | 190 | } | 
| 73565 | 191 |       case None => (Tag.FAIL, List(Bytes("Unknown Isabelle/Scala function: " + quote(name))))
 | 
| 43748 | 192 | } | 
| 52111 
1fd184eaa310
explicit management of Session.Protocol_Handlers, with protocol state and functions;
 wenzelm parents: 
49470diff
changeset | 193 | |
| 
1fd184eaa310
explicit management of Session.Protocol_Handlers, with protocol state and functions;
 wenzelm parents: 
49470diff
changeset | 194 | |
| 72157 | 195 | /* protocol handler */ | 
| 196 | ||
| 197 | class Handler extends Session.Protocol_Handler | |
| 198 |   {
 | |
| 199 | private var session: Session = null | |
| 200 | private var futures = Map.empty[String, Future[Unit]] | |
| 52111 
1fd184eaa310
explicit management of Session.Protocol_Handlers, with protocol state and functions;
 wenzelm parents: 
49470diff
changeset | 201 | |
| 72213 | 202 | override def init(session: Session): Unit = | 
| 203 |       synchronized { this.session = session }
 | |
| 52111 
1fd184eaa310
explicit management of Session.Protocol_Handlers, with protocol state and functions;
 wenzelm parents: 
49470diff
changeset | 204 | |
| 72157 | 205 | override def exit(): Unit = synchronized | 
| 206 |     {
 | |
| 207 | for ((id, future) <- futures) cancel(id, future) | |
| 208 | futures = Map.empty | |
| 209 | } | |
| 65219 | 210 | |
| 73565 | 211 | private def result(id: String, tag: Scala.Tag.Value, res: List[Bytes]): Unit = | 
| 72157 | 212 | synchronized | 
| 213 |       {
 | |
| 214 |         if (futures.isDefinedAt(id)) {
 | |
| 73565 | 215 |           session.protocol_command_raw("Scala.result", Bytes(id) :: Bytes(tag.id.toString) :: res)
 | 
| 72157 | 216 | futures -= id | 
| 217 | } | |
| 218 | } | |
| 65220 | 219 | |
| 73340 | 220 | private def cancel(id: String, future: Future[Unit]): Unit = | 
| 72157 | 221 |     {
 | 
| 73367 | 222 | future.cancel() | 
| 73565 | 223 | result(id, Scala.Tag.INTERRUPT, Nil) | 
| 72157 | 224 | } | 
| 225 | ||
| 226 | private def invoke_scala(msg: Prover.Protocol_Output): Boolean = synchronized | |
| 56387 | 227 |     {
 | 
| 72157 | 228 |       msg.properties match {
 | 
| 73419 
22f3f2117ed7
clarified signature: function_thread is determined in Isabelle/Scala, not Isabelle/ML;
 wenzelm parents: 
73418diff
changeset | 229 | case Markup.Invoke_Scala(name, id) => | 
| 73340 | 230 | def body: Unit = | 
| 72332 
319dd5c618a5
allow Scala function execution on separate thread: better reactivity, but potential overloading of the JVM;
 wenzelm parents: 
72294diff
changeset | 231 |           {
 | 
| 73565 | 232 | val (tag, res) = Scala.function_body(name, msg.chunks) | 
| 72332 
319dd5c618a5
allow Scala function execution on separate thread: better reactivity, but potential overloading of the JVM;
 wenzelm parents: 
72294diff
changeset | 233 | result(id, tag, res) | 
| 
319dd5c618a5
allow Scala function execution on separate thread: better reactivity, but potential overloading of the JVM;
 wenzelm parents: 
72294diff
changeset | 234 | } | 
| 
319dd5c618a5
allow Scala function execution on separate thread: better reactivity, but potential overloading of the JVM;
 wenzelm parents: 
72294diff
changeset | 235 | val future = | 
| 73419 
22f3f2117ed7
clarified signature: function_thread is determined in Isabelle/Scala, not Isabelle/ML;
 wenzelm parents: 
73418diff
changeset | 236 |             if (Scala.function_thread(name)) {
 | 
| 72332 
319dd5c618a5
allow Scala function execution on separate thread: better reactivity, but potential overloading of the JVM;
 wenzelm parents: 
72294diff
changeset | 237 | Future.thread(name = Isabelle_Thread.make_name(base = "invoke_scala"))(body) | 
| 
319dd5c618a5
allow Scala function execution on separate thread: better reactivity, but potential overloading of the JVM;
 wenzelm parents: 
72294diff
changeset | 238 | } | 
| 
319dd5c618a5
allow Scala function execution on separate thread: better reactivity, but potential overloading of the JVM;
 wenzelm parents: 
72294diff
changeset | 239 | else Future.fork(body) | 
| 
319dd5c618a5
allow Scala function execution on separate thread: better reactivity, but potential overloading of the JVM;
 wenzelm parents: 
72294diff
changeset | 240 | futures += (id -> future) | 
| 72157 | 241 | true | 
| 242 | case _ => false | |
| 56387 | 243 | } | 
| 52111 
1fd184eaa310
explicit management of Session.Protocol_Handlers, with protocol state and functions;
 wenzelm parents: 
49470diff
changeset | 244 | } | 
| 
1fd184eaa310
explicit management of Session.Protocol_Handlers, with protocol state and functions;
 wenzelm parents: 
49470diff
changeset | 245 | |
| 72157 | 246 | private def cancel_scala(msg: Prover.Protocol_Output): Boolean = synchronized | 
| 247 |     {
 | |
| 248 |       msg.properties match {
 | |
| 249 | case Markup.Cancel_Scala(id) => | |
| 250 |           futures.get(id) match {
 | |
| 251 | case Some(future) => cancel(id, future) | |
| 252 | case None => | |
| 253 | } | |
| 254 | true | |
| 255 | case _ => false | |
| 256 | } | |
| 257 | } | |
| 52111 
1fd184eaa310
explicit management of Session.Protocol_Handlers, with protocol state and functions;
 wenzelm parents: 
49470diff
changeset | 258 | |
| 72212 | 259 | override val functions = | 
| 72157 | 260 | List( | 
| 261 | Markup.Invoke_Scala.name -> invoke_scala, | |
| 262 | Markup.Cancel_Scala.name -> cancel_scala) | |
| 52111 
1fd184eaa310
explicit management of Session.Protocol_Handlers, with protocol state and functions;
 wenzelm parents: 
49470diff
changeset | 263 | } | 
| 
1fd184eaa310
explicit management of Session.Protocol_Handlers, with protocol state and functions;
 wenzelm parents: 
49470diff
changeset | 264 | } | 
| 71871 
28def00726ca
more robust isabelle.Functions --- avoid Java reflection with unclear class/object treatment;
 wenzelm parents: 
71870diff
changeset | 265 | |
| 72193 | 266 | class Scala_Functions extends Scala.Functions( | 
| 267 | Scala.Echo, | |
| 268 | Scala.Sleep, | |
| 72194 | 269 | Scala.Toplevel, | 
| 73576 | 270 | Bytes.Decode_Base64, | 
| 271 | Bytes.Encode_Base64, | |
| 72760 
042180540068
clarified protocol: Doc.check at run-time via Scala function;
 wenzelm parents: 
72756diff
changeset | 272 | Doc.Doc_Names, | 
| 73228 
0575cfd2ecfc
support multi-threaded Bash.process invocation on Apple Silicon, where Poly/ML is running on Rosetta 2;
 wenzelm parents: 
73136diff
changeset | 273 | Bash.Process, | 
| 72763 | 274 | Bibtex.Check_Database, | 
| 73314 
87403fde8cc3
more direct make_directory in ML and Scala, but ssh still requires perl for Windows UNC paths (see a5dbad753552);
 wenzelm parents: 
73228diff
changeset | 275 | Isabelle_System.Make_Directory, | 
| 73322 | 276 | Isabelle_System.Copy_Dir, | 
| 277 | Isabelle_System.Copy_File, | |
| 278 | Isabelle_System.Copy_File_Base, | |
| 73324 | 279 | Isabelle_System.Rm_Tree, | 
| 73323 | 280 | Isabelle_System.Download, | 
| 73523 
2cd23d587db9
further clarification of Isabelle distribution identification -- avoid odd patching of sources;
 wenzelm parents: 
73431diff
changeset | 281 | Isabelle_System.Isabelle_Id, | 
| 73418 
7d7d959547a1
support for SystemOnTPTP in Isabelle/ML and Isabelle/Scala (without perl);
 wenzelm parents: 
73367diff
changeset | 282 | Isabelle_Tool.Isabelle_Tools, | 
| 73431 
f27d7b12e8a4
support for SystemOnTPTP.run_system, with strict error following scripts/remote_atp;
 wenzelm parents: 
73419diff
changeset | 283 | isabelle.atp.SystemOnTPTP.List_Systems, | 
| 
f27d7b12e8a4
support for SystemOnTPTP.run_system, with strict error following scripts/remote_atp;
 wenzelm parents: 
73419diff
changeset | 284 | isabelle.atp.SystemOnTPTP.Run_System) |