diff --git a/alchemist-incarnation-scafi3/src/main/scala/it/unibo/alchemist/Scafi3Incarnation.scala b/alchemist-incarnation-scafi3/src/main/scala/it/unibo/alchemist/Scafi3Incarnation.scala index d33ada4b..42a83411 100644 --- a/alchemist-incarnation-scafi3/src/main/scala/it/unibo/alchemist/Scafi3Incarnation.scala +++ b/alchemist-incarnation-scafi3/src/main/scala/it/unibo/alchemist/Scafi3Incarnation.scala @@ -3,7 +3,6 @@ package it.unibo.alchemist import java.io.File import java.net.URLClassLoader import java.nio.file.Files -import javax.script.ScriptEngineManager import it.unibo.alchemist.Scafi3Incarnation.CACHE_SIZE import it.unibo.alchemist.actions.RunScafi3Program @@ -171,17 +170,40 @@ class Scafi3Incarnation[T, Position <: AlchemistPosition[Position]] extends Inca (reporter.hasErrors, List(reporter.summary)) private object ScalaScriptEngine: - private val engine = ScriptEngineManager().getEngineByName("scala").nn + private var idCounter = 0 + + @SuppressWarnings(Array("scalafix:DisableSyntax.asInstanceOf")) + private def eval[A](code: String): A = + code match + case "true" => true.asInstanceOf[A] + case "false" => false.asInstanceOf[A] + case s if s.toDoubleOption.isDefined => + val d = s.toDouble + if d.isValidInt then d.toInt.asInstanceOf[A] else d.asInstanceOf[A] + case _ => + idCounter += 1 + val className = s"Eval_$idCounter" + val src = s"""class $className { def eval = $code }""" + val inputFolder = Files.createTempDirectory("scafi3-eval") + val outputFolder = Files.createTempDirectory("scafi3-eval-out") + val sourceFilePath = Files.writeString(inputFolder.resolve(s"$className.scala"), src) + val (hasErrors, errors) = + compileWithOptions(sourceFilePath.toAbsolutePath.toString, outputFolder.toAbsolutePath.toString) + if hasErrors then throw new IllegalArgumentException(s"Could not compile $code: $errors") + val url = outputFolder.toFile.toURI.toURL + val cl = new URLClassLoader(Array(url), Thread.currentThread().getContextClassLoader) + val cls = cl.loadClass(className) + val instance = cls.getDeclaredConstructor().newInstance() + cls.getMethod("eval").invoke(instance).asInstanceOf[A] val concentrationCache: LoadingCache[String, Any] = - Caffeine.newBuilder().nn.build[String, Any](engine.eval).nn + Caffeine.newBuilder().nn.build[String, Any](eval[Any]).nn - @SuppressWarnings(Array("scalafix:DisableSyntax.asInstanceOf")) val propertyCache: LoadingCache[String, Any => Double] = Caffeine .newBuilder() .nn .build[String, Any => Double] { property => - engine.eval(property).asInstanceOf[Any => Double] + eval[Any => Double](property) } .nn end ScalaScriptEngine diff --git a/build.sbt b/build.sbt index 80fde92b..c3683d59 100644 --- a/build.sbt +++ b/build.sbt @@ -7,7 +7,7 @@ import sbtcrossproject.CrossProject import scala.scalanative.build.{ BuildTarget, GC, LTO, Mode } val projectName = "scafi3" -val scala3Version = "3.7.3" +val scala3Version = "3.8.1" ThisBuild / scalaVersion := scala3Version ThisBuild / organization := "it.unibo.scafi" diff --git a/example/src/main/scala/it/unibo/scafi/GradientInline.scala b/example/src/main/scala/it/unibo/scafi/GradientInline.scala index 4dcd789d..dd93e5b2 100644 --- a/example/src/main/scala/it/unibo/scafi/GradientInline.scala +++ b/example/src/main/scala/it/unibo/scafi/GradientInline.scala @@ -2,8 +2,10 @@ package it.unibo.scafi import it.unibo.alchemist.boundary.LoadAlchemist -object GradientInline extends App: - val simulationFile = getClass.getResource("/it/unibo/scafi/inline-gradient.yml").getPath - val loader = LoadAlchemist.from(simulationFile) - val simulation = loader.getDefault[Any, Nothing]() - simulation.run() +object GradientInline: + @main + def runInline(): Unit = + val simulationFile = getClass.getResource("/it/unibo/scafi/inline-gradient.yml").getPath + val loader = LoadAlchemist.from(simulationFile) + val simulation = loader.getDefault[Any, Nothing]() + simulation.run() diff --git a/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/FieldBasedSharedData.scala b/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/FieldBasedSharedData.scala index 65ed0d45..dd81bc32 100644 --- a/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/FieldBasedSharedData.scala +++ b/scafi3-core/src/main/scala/it/unibo/scafi/language/xc/FieldBasedSharedData.scala @@ -117,7 +117,6 @@ trait FieldBasedSharedData: override given sharedDataApplicative: Applicative[Field] = new Applicative[Field]: override def pure[A](x: A): Field[A] = Field(x) override def ap[A, B](ff: Field[A => B])(fa: Field[A]): Field[B] = - given [BB] => CanEqual[BB, BB] = CanEqual.derived val default = ff.defaultValue(fa.defaultValue) val allDevices = ff.devices ++ fa.devices val overrides = allDevices @@ -132,7 +131,6 @@ trait FieldBasedSharedData: override given sharedDataOps: SharedDataOps[Field] = new SharedDataOps[Field]: extension [A](field: Field[A]) override def withoutSelf: SafeIterable[A] = - given CanEqual[A, A] = CanEqual.derived SafeIterable(field.devices.toList.filterNot(_ == localId).map(field.apply)) override def onlySelf: A = field(localId) diff --git a/scafi3-core/src/main/scala/it/unibo/scafi/libraries/FoldhoodLibrary.scala b/scafi3-core/src/main/scala/it/unibo/scafi/libraries/FoldhoodLibrary.scala index 1cbf0823..3d669f4a 100644 --- a/scafi3-core/src/main/scala/it/unibo/scafi/libraries/FoldhoodLibrary.scala +++ b/scafi3-core/src/main/scala/it/unibo/scafi/libraries/FoldhoodLibrary.scala @@ -119,7 +119,7 @@ object FoldhoodLibrary: zippedNeighbouringValues.foldWithoutSelf(if withSelf then f(base, selfExprValue) else base): (acc, values) => val iterator = values.iterator val context: FoldhoodContext[L] = new FoldhoodContext[L]: - override def current[X](expr: (lang: L) ?=> lang.SharedData[X]): X = iterator.next match + override def current[X](expr: (lang: L) ?=> lang.SharedData[X]): X = iterator.next() match case x: X @unchecked => x case _ => throw new ClassCastException("Type mismatch") val result = expr(using context) diff --git a/scafi3-core/src/main/scala/it/unibo/scafi/message/ValueTree.scala b/scafi3-core/src/main/scala/it/unibo/scafi/message/ValueTree.scala index 1d2acaee..51aa5bb1 100644 --- a/scafi3-core/src/main/scala/it/unibo/scafi/message/ValueTree.scala +++ b/scafi3-core/src/main/scala/it/unibo/scafi/message/ValueTree.scala @@ -38,7 +38,7 @@ trait ValueTree: * an Option containing the value associated to the given [[Path]] if it exists, otherwise None. */ def get[Value](path: Path): Option[Value] = - try Some(apply(path)) + try Some(apply[Value](path)) catch case _: NoPathFoundException => None /** @@ -85,7 +85,12 @@ object ValueTree: override def equals(obj: Any): Boolean = obj match case that: ValueTree => - try this.paths == that.paths && this.paths.forall(path => this(path) == that(path)) + try + this.paths == that.paths && this.paths.forall(path => + val a = this.apply[Any](path) + val b = that.apply[Any](path) + a.equals(b), + ) catch case _: NoPathFoundException => false case _ => false diff --git a/scafi3-core/src/test/scala/it/unibo/scafi/context/xc/ExchangeAggregateContextTest.scala b/scafi3-core/src/test/scala/it/unibo/scafi/context/xc/ExchangeAggregateContextTest.scala index ae94fa8b..2c393567 100644 --- a/scafi3-core/src/test/scala/it/unibo/scafi/context/xc/ExchangeAggregateContextTest.scala +++ b/scafi3-core/src/test/scala/it/unibo/scafi/context/xc/ExchangeAggregateContextTest.scala @@ -34,7 +34,6 @@ class ExchangeAggregateContextTest private val lang = exchangeContextFactory(NeighborsNetworkManager[Int](0, Set(1, 2, 4, 6)), ValueTree.empty) private given [A: Arbitrary] => Arbitrary[lang.SharedData[A]] = Arbitrary: - given CanEqual[A, A] = CanEqual.derived for default <- Arbitrary.arbitrary[A] // Generate a random set (non empty) of device IDs between 0 and 10