From 9a9e7e3e5e44b530e739a21a0414702f045cff09 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Sat, 25 Nov 2023 11:02:55 +0100 Subject: [PATCH 01/97] fix: Scala doesn't find `AnsiColor` --- code/shared/src/main/scala/maf/util/ColouredFormatting.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/shared/src/main/scala/maf/util/ColouredFormatting.scala b/code/shared/src/main/scala/maf/util/ColouredFormatting.scala index 8dd2577cd..983c381a8 100644 --- a/code/shared/src/main/scala/maf/util/ColouredFormatting.scala +++ b/code/shared/src/main/scala/maf/util/ColouredFormatting.scala @@ -1,5 +1,5 @@ package maf.util -import io.AnsiColor.* +import scala.io.AnsiColor.* object ColouredFormatting { // Wrapper for easily accessing https://www.scala-lang.org/api/2.13.5/scala/io/AnsiColor.html From 006478ad18ab12dbee836374f4f234203ac35115 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Sat, 25 Nov 2023 11:06:47 +0100 Subject: [PATCH 02/97] feat(save): Save name of analysis --- .gitignore | 1 + build.sbt | 2 ++ .../maf/cli/experiments/SchemeAnalyses.scala | 1 + .../main/scala/maf/cli/runnables/Repl.scala | 3 +- .../main/scala/maf/modular/ModAnalysis.scala | 15 +++++++- .../scheme/modf/SchemeModFSemantics.scala | 5 ++- .../main/scala/maf/save/SaveAnalysis.scala | 34 +++++++++++++++++++ 7 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 code/shared/src/main/scala/maf/save/SaveAnalysis.scala diff --git a/.gitignore b/.gitignore index c0bae7176..dfc643419 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,4 @@ out/ *.zo __pycache__ stats/ +res.json diff --git a/build.sbt b/build.sbt index 6d10e4b5d..ffcbce34f 100644 --- a/build.sbt +++ b/build.sbt @@ -23,6 +23,8 @@ lazy val maf = crossProject(JVMPlatform, JSPlatform) libraryDependencies += ("com.typesafe.akka" %% "akka-actor-typed" % "2.6.18").cross(CrossVersion.for3Use2_13), libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.2.10", libraryDependencies += "com.typesafe" % "config" % "1.4.1", + libraryDependencies += "io.bullet" %% "borer-core" % "1.10.0", + libraryDependencies += "io.bullet" %% "borer-derivation" % "1.10.0", /** Compilation options */ maxErrors := 5, /** Configuration for running the tests */ diff --git a/code/jvm/src/main/scala/maf/cli/experiments/SchemeAnalyses.scala b/code/jvm/src/main/scala/maf/cli/experiments/SchemeAnalyses.scala index b83db9afb..e7549867a 100644 --- a/code/jvm/src/main/scala/maf/cli/experiments/SchemeAnalyses.scala +++ b/code/jvm/src/main/scala/maf/cli/experiments/SchemeAnalyses.scala @@ -57,6 +57,7 @@ object SchemeAnalyses: prg: SchemeExp ) = new SimpleSchemeModFAnalysis(prg) with SchemeModFNoSensitivity with SchemeConstantPropagationDomain with FIFOWorklistAlgorithm[SchemeExp] { override def toString = "no-sensitivity" + override val analysisName: String = "modf" } //def contextInsensitiveAnalysisRacket( diff --git a/code/jvm/src/main/scala/maf/cli/runnables/Repl.scala b/code/jvm/src/main/scala/maf/cli/runnables/Repl.scala index e27c67c41..38914458f 100644 --- a/code/jvm/src/main/scala/maf/cli/runnables/Repl.scala +++ b/code/jvm/src/main/scala/maf/cli/runnables/Repl.scala @@ -178,12 +178,13 @@ object Repl: val exp = loader(filename) def runSingle(): Long = val anl = makeAnalysis(exp) - val (elapsed, _) = Timer.time { anl.analyzeWithTimeout(Timeout.start(timeout.seconds)) } + val (elapsed, _) = Timer.time { anl.analyze() } //anl.analyzeWithTimeout(Timeout.start(timeout.seconds)) } // Do not print results if we are in perfomance testing mode if !performance then if !anl.finished then println("Analysis timed out") anl.printResult println(s"Analysis took ${elapsed / (1000 * 1000)} ms") + anl.save("res.json") // Print a dot graph if the dot option has been enabled if dot then anl.toDot(filename.replace("/", "_").nn + ".dot") elapsed diff --git a/code/shared/src/main/scala/maf/modular/ModAnalysis.scala b/code/shared/src/main/scala/maf/modular/ModAnalysis.scala index 7ba883c5a..649929160 100644 --- a/code/shared/src/main/scala/maf/modular/ModAnalysis.scala +++ b/code/shared/src/main/scala/maf/modular/ModAnalysis.scala @@ -18,6 +18,9 @@ case class Metric(name: String, result: Double) /** Super type of all analyses in MAF, provides basic entry points to the analysis */ trait AnalysisEntry[Exp <: Expression]: + /** The name of the analysis */ + val analysisName: String = "None" + /** Returns a boolean indicating whether the analysis has finished. Implementation should be provided by the work list algorithm. */ def finished: Boolean @@ -40,6 +43,17 @@ trait AnalysisEntry[Exp <: Expression]: /** Method that defines how to print the result of the analysis */ def printResult: Unit = println(result) + /** + * This saves the current analysis to a file + * + * @note + * This method is not currently implemented and should be overridden, this method so the save implementation can gradually be added + * + * @param filename + * The file to save to + */ + def save(filename: String): Unit = System.err.nn.println("Save functionallity is not implemented for this analysis") + /** * Method that renders a Dot graph of the components and the dependencies between them and writes it to a file * @@ -167,5 +181,4 @@ abstract class ModAnalysis[Expr <: Expression](val program: Expr) extends Clonea // Print analysis information. def configString(): String = "Modular analysis" - } diff --git a/code/shared/src/main/scala/maf/modular/scheme/modf/SchemeModFSemantics.scala b/code/shared/src/main/scala/maf/modular/scheme/modf/SchemeModFSemantics.scala index 28e03fd08..e048d1205 100644 --- a/code/shared/src/main/scala/maf/modular/scheme/modf/SchemeModFSemantics.scala +++ b/code/shared/src/main/scala/maf/modular/scheme/modf/SchemeModFSemantics.scala @@ -14,6 +14,8 @@ import maf.util._ import maf.core.IdentityMonad.given import maf.core.Monad.MonadIterableOps import maf.core.Monad.MonadSyntaxOps +import maf.save.SaveModF +import maf.save.Savable trait BaseEvalM[M[_]] extends Monad[M] with MonadError[M, Error] with MonadJoin[M] @@ -413,5 +415,6 @@ abstract class SimpleSchemeModFAnalysis(prg: SchemeExp, override val name: Optio extends ModAnalysis[SchemeExp](prg) with StandardSchemeModFComponents with SchemeModFSemanticsM - with BigStepModFSemantics: + with BigStepModFSemantics + with SaveModF: override def intraAnalysis(cmp: Component) = new IntraAnalysis(cmp) with BigStepModFIntra diff --git a/code/shared/src/main/scala/maf/save/SaveAnalysis.scala b/code/shared/src/main/scala/maf/save/SaveAnalysis.scala new file mode 100644 index 000000000..1e081f56f --- /dev/null +++ b/code/shared/src/main/scala/maf/save/SaveAnalysis.scala @@ -0,0 +1,34 @@ +package maf.save + +import io.bullet.borer.{Decoder, Encoder, Writer} +import io.bullet.borer.Json +import maf.util.Writer.write +import maf.core.Expression +import maf.modular.ModAnalysis +import java.nio.file.Paths +import java.nio.file.Files +import maf.language.scheme.SchemeExp + +class Savable[T](val value: T)(using val encoder: Encoder[T]) + +trait Save[Expr <: Expression] extends ModAnalysis[Expr]: + given Encoder[Save[Expr]] with + override def write(writer: Writer, value: Save[Expr]): Writer = + writer.writeMapStart() + for (key, value) <- saveInfo do writer.writeMapMember(key, value.value)(using summon[Encoder[String]], value.encoder) + writer.writeMapClose() + + /** + * This saves the current analysis to a file + * + * @param filename + * The file to save to + */ + override def save(filename: String): Unit = + val res = Json.encode(this).toByteArray + Files.write(Paths.get(filename), res) + + def saveInfo: Map[String, Savable[_]] = + Map("name" -> Savable(analysisName)) + +trait SaveModF extends Save[SchemeExp] From 7119c9c221d4f12e04e966efbd33ca9d7ddc19ca Mon Sep 17 00:00:00 2001 From: Merlijn Date: Sat, 25 Nov 2023 11:15:15 +0100 Subject: [PATCH 03/97] feat(save): Map and Array based encoders These encoders automatically open the array/map for you. They also add a function to `Writer` that allows you to write one or two values, these values wile be written differently based on what encoder you use. --- .../main/scala/maf/save/SaveAnalysis.scala | 9 ++-- .../scala/maf/save/SaveEncapsulated.scala | 47 +++++++++++++++++++ 2 files changed, 51 insertions(+), 5 deletions(-) create mode 100644 code/shared/src/main/scala/maf/save/SaveEncapsulated.scala diff --git a/code/shared/src/main/scala/maf/save/SaveAnalysis.scala b/code/shared/src/main/scala/maf/save/SaveAnalysis.scala index 1e081f56f..c6f2aaad2 100644 --- a/code/shared/src/main/scala/maf/save/SaveAnalysis.scala +++ b/code/shared/src/main/scala/maf/save/SaveAnalysis.scala @@ -12,11 +12,10 @@ import maf.language.scheme.SchemeExp class Savable[T](val value: T)(using val encoder: Encoder[T]) trait Save[Expr <: Expression] extends ModAnalysis[Expr]: - given Encoder[Save[Expr]] with - override def write(writer: Writer, value: Save[Expr]): Writer = - writer.writeMapStart() - for (key, value) <- saveInfo do writer.writeMapMember(key, value.value)(using summon[Encoder[String]], value.encoder) - writer.writeMapClose() + given MapEncoder[Save[Expr]] with + override def writeEncapsulated(writer: Writer, value: Save[Expr]): Writer = + for (key, value) <- saveInfo do writer.writeMember(key, value.value)(using summon[Encoder[String]], value.encoder) + writer /** * This saves the current analysis to a file diff --git a/code/shared/src/main/scala/maf/save/SaveEncapsulated.scala b/code/shared/src/main/scala/maf/save/SaveEncapsulated.scala new file mode 100644 index 000000000..0ef0a6621 --- /dev/null +++ b/code/shared/src/main/scala/maf/save/SaveEncapsulated.scala @@ -0,0 +1,47 @@ +package maf.save + +import io.bullet.borer.Encoder +import io.bullet.borer.Writer + +trait EncapsulatedEncoder[T] extends Encoder[T]: + override def write(writer: Writer, value: T): Writer = writeEncapsulated(writer, value) + def writeEncapsulated(writer: Writer, value: T): Writer + def writeMembe[T: Encoder, U: Encoder](writer: Writer, key: T, value: U): Writer = writer.writeMember(key, value) + extension (writer: Writer) + def writeMember[T: Encoder, U: Encoder](key: T, value: U): Writer + def writeMember[T: Encoder](value: T): Writer + +trait MapEncoder[T] extends EncapsulatedEncoder[T]: + final override def write(writer: Writer, value: T): Writer = + writer.writeMapStart() + super.write(writer, value) + writer.writeMapClose() + + private var id = -1 + extension (writer: Writer) + override def writeMember[A: Encoder, B: Encoder](key: A, value: B): Writer = + writer.writeMapMember(key, value) + + override def writeMember[T: Encoder](value: T): Writer = + id += 1 + writer.writeMember(id.toString(), value) + +trait ArrayEncoder[T] extends EncapsulatedEncoder[T]: + final override def write(writer: Writer, value: T): Writer = + writer.writeArrayStart() + super.write(writer, value) + writer.writeArrayClose() + + extension (writer: Writer) + override def writeMember[A: Encoder, B: Encoder](key: A, value: B): Writer = + writer.write(value) + + override def writeMember[T: Encoder](value: T): Writer = + writer.write(value) + +trait ArrayKeyEncoder[T] extends ArrayEncoder[T]: + extension (writer: Writer) + override def writeMember[A: Encoder, B: Encoder](key: A, value: B): Writer = + writer.writeArrayOpen(2) + writer.write(key).write(value) + writer.writeArrayClose() From 4ca2629b53859d23513c6606ba90b17b48574056 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Sat, 25 Nov 2023 11:18:41 +0100 Subject: [PATCH 04/97] feat(save): Save components --- .../main/scala/maf/save/SaveAnalysis.scala | 2 +- .../main/scala/maf/save/SaveComponent.scala | 57 +++++++++++++++++++ .../src/main/scala/maf/save/SaveStore.scala | 19 +++++++ 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 code/shared/src/main/scala/maf/save/SaveComponent.scala create mode 100644 code/shared/src/main/scala/maf/save/SaveStore.scala diff --git a/code/shared/src/main/scala/maf/save/SaveAnalysis.scala b/code/shared/src/main/scala/maf/save/SaveAnalysis.scala index c6f2aaad2..59089f811 100644 --- a/code/shared/src/main/scala/maf/save/SaveAnalysis.scala +++ b/code/shared/src/main/scala/maf/save/SaveAnalysis.scala @@ -30,4 +30,4 @@ trait Save[Expr <: Expression] extends ModAnalysis[Expr]: def saveInfo: Map[String, Savable[_]] = Map("name" -> Savable(analysisName)) -trait SaveModF extends Save[SchemeExp] +trait SaveModF extends Save[SchemeExp] with SaveStandardSchemeComponents with SaveModularDomain diff --git a/code/shared/src/main/scala/maf/save/SaveComponent.scala b/code/shared/src/main/scala/maf/save/SaveComponent.scala new file mode 100644 index 000000000..999995e2b --- /dev/null +++ b/code/shared/src/main/scala/maf/save/SaveComponent.scala @@ -0,0 +1,57 @@ +package maf.save + +import io.bullet.borer.Encoder +import io.bullet.borer.Encoder.forIterableOnce +import io.bullet.borer.Writer +import maf.core.Address +import maf.core.Expression +import maf.core.Position.Position +import maf.language.scheme.SchemeExp +import maf.modular.AddrDependency +import maf.modular.AnalysisResults +import maf.modular.Dependency +import maf.modular.scheme.modf.SchemeModFComponent +import maf.modular.scheme.modf.StandardSchemeModFComponents + +trait SavePosition[Expr <: Expression] extends Save[Expr]: + given MapEncoder[Position] with + override def writeEncapsulated(writer: Writer, pos: Position): Writer = + writer.writeMember("line", pos.line) + writer.writeMember("col", pos.line) + if !pos.tag.show.isEmpty() then writer.writeMember("tag", pos.tag.show) + writer + +trait SaveComponents[Expr <: Expression] extends Save[Expr]: + override def saveInfo: Map[String, Savable[_]] = super.saveInfo + ("components" -> Savable(visited)) + + given componentEncoder: Encoder[Component] + +trait SaveStandardSchemeComponentID extends StandardSchemeModFComponents with SavePosition[SchemeExp]: + given componentIDEncoder: Encoder[Component] with + def write(writer: Writer, component: Component): Writer = + if component.equals(initialComponent) then writer.write("main") + else writer.write(component.asInstanceOf[SchemeModFComponent.Call[ComponentContext]])(schemeComponentIDEncoder) + + given schemeComponentIDEncoder[T]: Encoder[SchemeModFComponent.Call[T]] with + def write(writer: Writer, component: SchemeModFComponent.Call[T]): Writer = + val (lambda, _) = component.clo + writer.write(lambda.idn.pos) + +trait SaveStandardSchemeComponents + extends SaveComponents[SchemeExp] + with StandardSchemeModFComponents + with AnalysisResults[SchemeExp] + with SaveValue[SchemeExp] + with SavePosition[SchemeExp]: + override given componentEncoder: Encoder[Component] with + def write(writer: Writer, component: Component): Writer = + if component.equals(initialComponent) then writer.write("main") + else writer.write(component.asInstanceOf[SchemeModFComponent.Call[ComponentContext]]) + + given [T]: MapEncoder[SchemeModFComponent.Call[T]] with + override def writeEncapsulated(writer: Writer, component: SchemeModFComponent.Call[T]): Writer = + val (lambda, env) = component.clo + val context = component.ctx + if lambda.name.isDefined then writer.writeMapMember("name", lambda.name.get) + writer.writeMapMember("position", lambda.idn.pos) + writer.writeMapMember("result", returnValue(component)) diff --git a/code/shared/src/main/scala/maf/save/SaveStore.scala b/code/shared/src/main/scala/maf/save/SaveStore.scala new file mode 100644 index 000000000..80eacbd16 --- /dev/null +++ b/code/shared/src/main/scala/maf/save/SaveStore.scala @@ -0,0 +1,19 @@ +package maf.save + +import maf.core.Expression +import maf.modular.GlobalStore +import io.bullet.borer.Encoder +import maf.modular.AbstractDomain +import maf.language.scheme.SchemeExp +import maf.modular.scheme.ModularSchemeDomain +import maf.lattice.HMap +import io.bullet.borer.Writer + +trait SaveValue[Expr <: Expression] extends Save[Expr] with AbstractDomain[Expr]: + given valueEncoder: Encoder[Value] + +trait SaveModularDomain extends SaveValue[SchemeExp] with ModularSchemeDomain: + override given valueEncoder: ArrayEncoder[HMap] with + override def writeEncapsulated(writer: Writer, value: HMap): Writer = + value.contents.foreach((key, value) => writer.writeMember(value.toString())) + writer From 98c0db12be3a24f64da40f08310be1ed89da4ec2 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Sat, 25 Nov 2023 11:25:38 +0100 Subject: [PATCH 05/97] feat(save): Save dependencies --- .../main/scala/maf/save/SaveAnalysis.scala | 2 +- .../scala/maf/save/SaveDependencies.scala | 55 +++++++++++++++++++ .../shared/src/main/scala/maf/save/Util.scala | 10 ++++ 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 code/shared/src/main/scala/maf/save/SaveDependencies.scala create mode 100644 code/shared/src/main/scala/maf/save/Util.scala diff --git a/code/shared/src/main/scala/maf/save/SaveAnalysis.scala b/code/shared/src/main/scala/maf/save/SaveAnalysis.scala index 59089f811..7ab29326e 100644 --- a/code/shared/src/main/scala/maf/save/SaveAnalysis.scala +++ b/code/shared/src/main/scala/maf/save/SaveAnalysis.scala @@ -30,4 +30,4 @@ trait Save[Expr <: Expression] extends ModAnalysis[Expr]: def saveInfo: Map[String, Savable[_]] = Map("name" -> Savable(analysisName)) -trait SaveModF extends Save[SchemeExp] with SaveStandardSchemeComponents with SaveModularDomain +trait SaveModF extends Save[SchemeExp] with SaveStandardSchemeComponents with SaveModularDomain with SaveAddrDep diff --git a/code/shared/src/main/scala/maf/save/SaveDependencies.scala b/code/shared/src/main/scala/maf/save/SaveDependencies.scala new file mode 100644 index 000000000..886de4e78 --- /dev/null +++ b/code/shared/src/main/scala/maf/save/SaveDependencies.scala @@ -0,0 +1,55 @@ +package maf.save + +import maf.language.scheme.SchemeExp +import maf.modular.Dependency +import io.bullet.borer.Writer +import maf.modular.AddrDependency +import maf.core.Expression +import maf.core.Address +import maf.modular.scheme.VarAddr +import maf.modular.ReturnAddr +import io.bullet.borer.Encoder + +trait SaveAddrDep extends SaveDependency with SavePosition[SchemeExp] with SaveSchemeAddr: + override def encodeDependency(writer: Writer, dependency: Dependency): Writer = + dependency match { + case AddrDependency(_) => writer.write(dependency.asInstanceOf[AddrDependency].addr) + case _ => super.encodeDependency(writer, dependency) + } + +trait SaveDependency extends SaveMapToArray with SaveStandardSchemeComponentID: + override def saveInfo: Map[String, Savable[_]] = + import componentIDEncoder.given + super.saveInfo + ("dependencies" -> Savable(deps)) + + def encodeDependency(writer: Writer, dependency: Dependency): Writer = + System.err.nn.println("The dependency with type `" + dependency.getClass + "` could not be encoded") + writer + given dependencyEncoder: Encoder[Dependency] = encodeDependency _ + +trait SaveAddr[Expr <: Expression] extends Save[Expr] with SavePosition[Expr]: + def encodeAddress(writer: Writer, address: Address): Writer = + System.err.nn.println("The address with type `" + address.getClass + "` could not be encoded") + writer + + trait AddrEncoder extends EncapsulatedEncoder[Address]: + override def write(writer: Writer, address: Address): Writer = + writer.writeMember("position", address.idn.pos) + super.write(writer, address) + + trait AddrMapEncoder extends AddrEncoder with MapEncoder[Address] + given addressEncoder: AddrMapEncoder with + override def writeEncapsulated(writer: Writer, value: Address): Writer = encodeAddress(writer, value) + +trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveStandardSchemeComponentID: + override def encodeAddress(writer: Writer, address: Address): Writer = + import componentIDEncoder.given + address match { + case VarAddr(id, ctx) => + writer.writeMember("type", "varAddr") + writer.writeMember("name", id.name) + case ReturnAddr(cmp, idn) => + writer.writeMember("type", "returnAddr") + writer.writeMember("component", cmp.asInstanceOf[Component]) + case _ => super.encodeAddress(writer, address) + } diff --git a/code/shared/src/main/scala/maf/save/Util.scala b/code/shared/src/main/scala/maf/save/Util.scala new file mode 100644 index 000000000..ee7da136f --- /dev/null +++ b/code/shared/src/main/scala/maf/save/Util.scala @@ -0,0 +1,10 @@ +package maf.save + +import io.bullet.borer.Encoder +import io.bullet.borer.Writer + +trait SaveMapToArray: + given mapKeyEncoder[K, V](using keyEncoder: Encoder[K], valueEncoder: Encoder[V]): ArrayKeyEncoder[Map[K, V]] with + override def writeEncapsulated(writer: Writer, map: Map[K, V]): Writer = + for (key, value) <- map do writer.writeMember(key, value)(using keyEncoder, valueEncoder) + writer From a3e3e1a74b44bfaf068fccc0f854ecc3f2e5ca92 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Sat, 25 Nov 2023 11:26:59 +0100 Subject: [PATCH 06/97] refactor(save): Rename files --- .../main/scala/maf/save/{SaveAnalysis.scala => Analysis.scala} | 0 .../main/scala/maf/save/{SaveComponent.scala => Component.scala} | 0 .../scala/maf/save/{SaveDependencies.scala => Dependency.scala} | 0 .../scala/maf/save/{SaveEncapsulated.scala => Encapsulated.scala} | 0 .../src/main/scala/maf/save/{SaveStore.scala => Store.scala} | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename code/shared/src/main/scala/maf/save/{SaveAnalysis.scala => Analysis.scala} (100%) rename code/shared/src/main/scala/maf/save/{SaveComponent.scala => Component.scala} (100%) rename code/shared/src/main/scala/maf/save/{SaveDependencies.scala => Dependency.scala} (100%) rename code/shared/src/main/scala/maf/save/{SaveEncapsulated.scala => Encapsulated.scala} (100%) rename code/shared/src/main/scala/maf/save/{SaveStore.scala => Store.scala} (100%) diff --git a/code/shared/src/main/scala/maf/save/SaveAnalysis.scala b/code/shared/src/main/scala/maf/save/Analysis.scala similarity index 100% rename from code/shared/src/main/scala/maf/save/SaveAnalysis.scala rename to code/shared/src/main/scala/maf/save/Analysis.scala diff --git a/code/shared/src/main/scala/maf/save/SaveComponent.scala b/code/shared/src/main/scala/maf/save/Component.scala similarity index 100% rename from code/shared/src/main/scala/maf/save/SaveComponent.scala rename to code/shared/src/main/scala/maf/save/Component.scala diff --git a/code/shared/src/main/scala/maf/save/SaveDependencies.scala b/code/shared/src/main/scala/maf/save/Dependency.scala similarity index 100% rename from code/shared/src/main/scala/maf/save/SaveDependencies.scala rename to code/shared/src/main/scala/maf/save/Dependency.scala diff --git a/code/shared/src/main/scala/maf/save/SaveEncapsulated.scala b/code/shared/src/main/scala/maf/save/Encapsulated.scala similarity index 100% rename from code/shared/src/main/scala/maf/save/SaveEncapsulated.scala rename to code/shared/src/main/scala/maf/save/Encapsulated.scala diff --git a/code/shared/src/main/scala/maf/save/SaveStore.scala b/code/shared/src/main/scala/maf/save/Store.scala similarity index 100% rename from code/shared/src/main/scala/maf/save/SaveStore.scala rename to code/shared/src/main/scala/maf/save/Store.scala From 8437bd310dd84cb65a256bd8d4eade79bf61561a Mon Sep 17 00:00:00 2001 From: Merlijn Date: Sun, 3 Dec 2023 12:16:03 +0100 Subject: [PATCH 07/97] refactor(save): Extension methods in `EncapsulatedEncoder` use normal function You no longer need to overwrite the extension methods, but can override the normal functions that are used by the extension methods. --- .../main/scala/maf/save/Encapsulated.scala | 73 +++++++++++++------ 1 file changed, 49 insertions(+), 24 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/Encapsulated.scala b/code/shared/src/main/scala/maf/save/Encapsulated.scala index 0ef0a6621..a6c5dd9ba 100644 --- a/code/shared/src/main/scala/maf/save/Encapsulated.scala +++ b/code/shared/src/main/scala/maf/save/Encapsulated.scala @@ -5,43 +5,68 @@ import io.bullet.borer.Writer trait EncapsulatedEncoder[T] extends Encoder[T]: override def write(writer: Writer, value: T): Writer = writeEncapsulated(writer, value) - def writeEncapsulated(writer: Writer, value: T): Writer - def writeMembe[T: Encoder, U: Encoder](writer: Writer, key: T, value: U): Writer = writer.writeMember(key, value) + protected val writeOpenKey = true + protected def writeEncapsulated(writer: Writer, value: T): Writer + protected def writeKey(writer: Writer): Writer + protected def writeKey[T: Encoder](writer: Writer, key: T): Writer + protected def writeValue[T: Encoder](writer: Writer, value: T): Writer + protected def openEncapsulation(writer: Writer): Writer + protected def openEncapsulation(writer: Writer, amount: Int): Writer + protected def closeEncapsulation(writer: Writer): Writer + extension (writer: Writer) - def writeMember[T: Encoder, U: Encoder](key: T, value: U): Writer - def writeMember[T: Encoder](value: T): Writer + def close(): Writer = closeEncapsulation(writer) + def writeMember[T: Encoder, U: Encoder](key: T, value: U): Writer = + writeKey(writer, key) + writeValue(writer, value) + def writeMember[T: Encoder](value: T): Writer = + writeKey(writer) + writeValue(writer, value) + def open(): Writer = + if writeOpenKey then writeKey(writer) + openEncapsulation(writer) + def open(amount: Int): Writer = + if writeOpenKey then writeKey(writer) + openEncapsulation(writer, amount) -trait MapEncoder[T] extends EncapsulatedEncoder[T]: +trait MapEncapsulatedEncoder[T] extends EncapsulatedEncoder[T]: final override def write(writer: Writer, value: T): Writer = writer.writeMapStart() super.write(writer, value) writer.writeMapClose() +trait MapMemberEncoder[T] extends EncapsulatedEncoder[T]: private var id = -1 - extension (writer: Writer) - override def writeMember[A: Encoder, B: Encoder](key: A, value: B): Writer = - writer.writeMapMember(key, value) + override protected def writeKey(writer: Writer): Writer = + id += 1 + writeKey(writer, id.toString()) + override protected def writeKey[T: Encoder](writer: Writer, key: T): Writer = writer.write(key) + override protected def writeValue[T: Encoder](writer: Writer, value: T): Writer = writer.write(value) + override protected def openEncapsulation(writer: Writer): Writer = writer.writeMapStart() + override protected def openEncapsulation(writer: Writer, amount: Int): Writer = writer.writeMapOpen(amount) + override protected def closeEncapsulation(writer: Writer): Writer = writer.writeMapClose() +trait MapEncoder[T] extends MapEncapsulatedEncoder[T] with MapMemberEncoder[T] - override def writeMember[T: Encoder](value: T): Writer = - id += 1 - writer.writeMember(id.toString(), value) - -trait ArrayEncoder[T] extends EncapsulatedEncoder[T]: +trait ArrayEncapsulatedEncoder[T] extends EncapsulatedEncoder[T]: final override def write(writer: Writer, value: T): Writer = writer.writeArrayStart() super.write(writer, value) writer.writeArrayClose() - extension (writer: Writer) - override def writeMember[A: Encoder, B: Encoder](key: A, value: B): Writer = - writer.write(value) - - override def writeMember[T: Encoder](value: T): Writer = - writer.write(value) +trait ArrayMemberEncoder[T] extends EncapsulatedEncoder[T]: + override protected def writeKey(writer: Writer): Writer = writer + override protected def writeKey[T: Encoder](writer: Writer, key: T): Writer = writer + override protected def writeValue[T: Encoder](writer: Writer, value: T): Writer = writer.write(value) + override protected def openEncapsulation(writer: Writer): Writer = writer.writeArrayStart() + override protected def openEncapsulation(writer: Writer, amount: Int): Writer = writer.writeArrayOpen(amount) + override protected def closeEncapsulation(writer: Writer): Writer = writer.writeArrayClose() +trait ArrayEncoder[T] extends ArrayEncapsulatedEncoder[T] with ArrayMemberEncoder[T] trait ArrayKeyEncoder[T] extends ArrayEncoder[T]: - extension (writer: Writer) - override def writeMember[A: Encoder, B: Encoder](key: A, value: B): Writer = - writer.writeArrayOpen(2) - writer.write(key).write(value) - writer.writeArrayClose() + private var id = -1 + override protected val writeOpenKey: Boolean = false + override protected def writeKey(writer: Writer): Writer = + id += 1 + writeKey(writer, id.toString()) + override protected def writeKey[T: Encoder](writer: Writer, key: T): Writer = openEncapsulation(writer, 2).write(key) + override protected def writeValue[T: Encoder](writer: Writer, value: T): Writer = closeEncapsulation(writer.write(value)) From 78ec55111b6b0035e6f35d614ad4079c2b169d36 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Sun, 3 Dec 2023 12:24:19 +0100 Subject: [PATCH 08/97] refactor(save): Put extension methods of `EncapsulatedEncoder` in object This is to avoid ambiguity when importing form multiply places --- .../src/main/scala/maf/save/Analysis.scala | 3 ++- .../src/main/scala/maf/save/Component.scala | 1 + .../src/main/scala/maf/save/Dependency.scala | 1 + .../main/scala/maf/save/Encapsulated.scala | 27 ++++++++++--------- .../src/main/scala/maf/save/Store.scala | 1 + .../shared/src/main/scala/maf/save/Util.scala | 3 ++- 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/Analysis.scala b/code/shared/src/main/scala/maf/save/Analysis.scala index 7ab29326e..13d2c99ec 100644 --- a/code/shared/src/main/scala/maf/save/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/Analysis.scala @@ -8,13 +8,14 @@ import maf.modular.ModAnalysis import java.nio.file.Paths import java.nio.file.Files import maf.language.scheme.SchemeExp +import EncapsulatedEncoder.* class Savable[T](val value: T)(using val encoder: Encoder[T]) trait Save[Expr <: Expression] extends ModAnalysis[Expr]: given MapEncoder[Save[Expr]] with override def writeEncapsulated(writer: Writer, value: Save[Expr]): Writer = - for (key, value) <- saveInfo do writer.writeMember(key, value.value)(using summon[Encoder[String]], value.encoder) + for (key, value) <- saveInfo do writer.writeMember(key, value.value)(using summon[Encoder[String]], value.encoder, this) writer /** diff --git a/code/shared/src/main/scala/maf/save/Component.scala b/code/shared/src/main/scala/maf/save/Component.scala index 999995e2b..718c4802f 100644 --- a/code/shared/src/main/scala/maf/save/Component.scala +++ b/code/shared/src/main/scala/maf/save/Component.scala @@ -12,6 +12,7 @@ import maf.modular.AnalysisResults import maf.modular.Dependency import maf.modular.scheme.modf.SchemeModFComponent import maf.modular.scheme.modf.StandardSchemeModFComponents +import EncapsulatedEncoder.* trait SavePosition[Expr <: Expression] extends Save[Expr]: given MapEncoder[Position] with diff --git a/code/shared/src/main/scala/maf/save/Dependency.scala b/code/shared/src/main/scala/maf/save/Dependency.scala index 886de4e78..c1a3ab9a9 100644 --- a/code/shared/src/main/scala/maf/save/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/Dependency.scala @@ -9,6 +9,7 @@ import maf.core.Address import maf.modular.scheme.VarAddr import maf.modular.ReturnAddr import io.bullet.borer.Encoder +import EncapsulatedEncoder.* trait SaveAddrDep extends SaveDependency with SavePosition[SchemeExp] with SaveSchemeAddr: override def encodeDependency(writer: Writer, dependency: Dependency): Writer = diff --git a/code/shared/src/main/scala/maf/save/Encapsulated.scala b/code/shared/src/main/scala/maf/save/Encapsulated.scala index a6c5dd9ba..992a2f2b8 100644 --- a/code/shared/src/main/scala/maf/save/Encapsulated.scala +++ b/code/shared/src/main/scala/maf/save/Encapsulated.scala @@ -14,20 +14,21 @@ trait EncapsulatedEncoder[T] extends Encoder[T]: protected def openEncapsulation(writer: Writer, amount: Int): Writer protected def closeEncapsulation(writer: Writer): Writer +object EncapsulatedEncoder: extension (writer: Writer) - def close(): Writer = closeEncapsulation(writer) - def writeMember[T: Encoder, U: Encoder](key: T, value: U): Writer = - writeKey(writer, key) - writeValue(writer, value) - def writeMember[T: Encoder](value: T): Writer = - writeKey(writer) - writeValue(writer, value) - def open(): Writer = - if writeOpenKey then writeKey(writer) - openEncapsulation(writer) - def open(amount: Int): Writer = - if writeOpenKey then writeKey(writer) - openEncapsulation(writer, amount) + def close()(using encoder: EncapsulatedEncoder[_]): Writer = encoder.closeEncapsulation(writer) + def writeMember[T: Encoder, U: Encoder](key: T, value: U)(using encoder: EncapsulatedEncoder[_]): Writer = + encoder.writeKey(writer, key) + encoder.writeValue(writer, value) + def writeMember[T: Encoder](value: T)(using encoder: EncapsulatedEncoder[_]): Writer = + encoder.writeKey(writer) + encoder.writeValue(writer, value) + def open()(using encoder: EncapsulatedEncoder[_]): Writer = + if encoder.writeOpenKey then encoder.writeKey(writer) + encoder.openEncapsulation(writer) + def open(amount: Int)(using encoder: EncapsulatedEncoder[_]): Writer = + if encoder.writeOpenKey then encoder.writeKey(writer) + encoder.openEncapsulation(writer, amount) trait MapEncapsulatedEncoder[T] extends EncapsulatedEncoder[T]: final override def write(writer: Writer, value: T): Writer = diff --git a/code/shared/src/main/scala/maf/save/Store.scala b/code/shared/src/main/scala/maf/save/Store.scala index 80eacbd16..a7cbfad17 100644 --- a/code/shared/src/main/scala/maf/save/Store.scala +++ b/code/shared/src/main/scala/maf/save/Store.scala @@ -8,6 +8,7 @@ import maf.language.scheme.SchemeExp import maf.modular.scheme.ModularSchemeDomain import maf.lattice.HMap import io.bullet.borer.Writer +import EncapsulatedEncoder.* trait SaveValue[Expr <: Expression] extends Save[Expr] with AbstractDomain[Expr]: given valueEncoder: Encoder[Value] diff --git a/code/shared/src/main/scala/maf/save/Util.scala b/code/shared/src/main/scala/maf/save/Util.scala index ee7da136f..5b2ec4c58 100644 --- a/code/shared/src/main/scala/maf/save/Util.scala +++ b/code/shared/src/main/scala/maf/save/Util.scala @@ -2,9 +2,10 @@ package maf.save import io.bullet.borer.Encoder import io.bullet.borer.Writer +import EncapsulatedEncoder.* trait SaveMapToArray: given mapKeyEncoder[K, V](using keyEncoder: Encoder[K], valueEncoder: Encoder[V]): ArrayKeyEncoder[Map[K, V]] with override def writeEncapsulated(writer: Writer, map: Map[K, V]): Writer = - for (key, value) <- map do writer.writeMember(key, value)(using keyEncoder, valueEncoder) + for (key, value) <- map do writer.writeMember(key, value)(using keyEncoder, valueEncoder, this) writer From 9212aaec858df344412f09c144d4b5b1cfc5bda9 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Sun, 3 Dec 2023 12:28:11 +0100 Subject: [PATCH 09/97] feat(save): Save `PrmAddr` and `PtrAddr` --- code/shared/src/main/scala/maf/save/Dependency.scala | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/code/shared/src/main/scala/maf/save/Dependency.scala b/code/shared/src/main/scala/maf/save/Dependency.scala index c1a3ab9a9..1f57102c2 100644 --- a/code/shared/src/main/scala/maf/save/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/Dependency.scala @@ -9,6 +9,8 @@ import maf.core.Address import maf.modular.scheme.VarAddr import maf.modular.ReturnAddr import io.bullet.borer.Encoder +import maf.modular.scheme.PrmAddr +import maf.modular.scheme.PtrAddr import EncapsulatedEncoder.* trait SaveAddrDep extends SaveDependency with SavePosition[SchemeExp] with SaveSchemeAddr: @@ -52,5 +54,10 @@ trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveStandardSchemeComponen case ReturnAddr(cmp, idn) => writer.writeMember("type", "returnAddr") writer.writeMember("component", cmp.asInstanceOf[Component]) + case PrmAddr(nam) => + writer.writeMember("name", nam) + writer.writeMember("type", "prmAddr") + case PtrAddr(exp, ctx) => + writer.writeMember("type", "ptrAddr") case _ => super.encodeAddress(writer, address) } From a35c6bddb098b81cad59d035336f6c8df01751fe Mon Sep 17 00:00:00 2001 From: Merlijn Date: Sun, 3 Dec 2023 12:29:14 +0100 Subject: [PATCH 10/97] feat(save): Save the store --- .../src/main/scala/maf/save/Analysis.scala | 9 +++- .../src/main/scala/maf/save/Component.scala | 1 - .../src/main/scala/maf/save/Store.scala | 46 +++++++++++++++++-- 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/Analysis.scala b/code/shared/src/main/scala/maf/save/Analysis.scala index 13d2c99ec..b8b5a853a 100644 --- a/code/shared/src/main/scala/maf/save/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/Analysis.scala @@ -31,4 +31,11 @@ trait Save[Expr <: Expression] extends ModAnalysis[Expr]: def saveInfo: Map[String, Savable[_]] = Map("name" -> Savable(analysisName)) -trait SaveModF extends Save[SchemeExp] with SaveStandardSchemeComponents with SaveModularDomain with SaveAddrDep +trait SaveModF + extends Save[SchemeExp] + with SaveStandardSchemeComponents + with SaveModularDomain + with SaveAddrDep + with SaveSchemeAddr + with SaveGlobalStore[SchemeExp] + with SaveModularSchemeLattices diff --git a/code/shared/src/main/scala/maf/save/Component.scala b/code/shared/src/main/scala/maf/save/Component.scala index 718c4802f..420a1060a 100644 --- a/code/shared/src/main/scala/maf/save/Component.scala +++ b/code/shared/src/main/scala/maf/save/Component.scala @@ -55,4 +55,3 @@ trait SaveStandardSchemeComponents val context = component.ctx if lambda.name.isDefined then writer.writeMapMember("name", lambda.name.get) writer.writeMapMember("position", lambda.idn.pos) - writer.writeMapMember("result", returnValue(component)) diff --git a/code/shared/src/main/scala/maf/save/Store.scala b/code/shared/src/main/scala/maf/save/Store.scala index a7cbfad17..5a6627ff4 100644 --- a/code/shared/src/main/scala/maf/save/Store.scala +++ b/code/shared/src/main/scala/maf/save/Store.scala @@ -8,13 +8,53 @@ import maf.language.scheme.SchemeExp import maf.modular.scheme.ModularSchemeDomain import maf.lattice.HMap import io.bullet.borer.Writer +import maf.lattice.HMapKey +import maf.language.scheme.lattices.ModularSchemeLattice +import scala.reflect.ClassTag +import maf.lattice.AbstractWrapType +import maf.lattice.AbstractType +import maf.lattice.AbstractSetType +import io.bullet.borer.LowPrioEncoders +import maf.core.Address import EncapsulatedEncoder.* trait SaveValue[Expr <: Expression] extends Save[Expr] with AbstractDomain[Expr]: given valueEncoder: Encoder[Value] +trait SaveModularSchemeLattices extends SaveModularDomain with SaveAddr[SchemeExp]: + override def encodeHMapPair(writer: Writer, key: HMapKey, value: Any)(using encoder: EncapsulatedEncoder[_]): Writer = + type SchemeLattice = ModularSchemeLattice[?, ?, ?, ?, ?, ?, ?] + if !value.isInstanceOf[SchemeLattice#Value] then return super.encodeHMapPair(writer, key, value) + writer.open() + def writeValue[T: Encoder](value: T) = writer.writeMember("value", value) + def openValueArray(amount: Int) = writer.write("value").writeArrayOpen(amount) + + writer.writeMember("type", value.asInstanceOf[ModularSchemeLattice[?, ?, ?, ?, ?, ?, ?]#Value].typeName) + value match { + case int: SchemeLattice#Int => writeValue(int.i.toString()) + case bool: SchemeLattice#Bool => writeValue(bool.b.toString()) + case str: SchemeLattice#Str => writeValue(str.s.toString()) + case prim: SchemeLattice#Prim => writeValue(prim.prims) + case clo: SchemeLattice#Clo => clo.closures.foreach((clo) => writeValue(clo.toString)) + case pointer: SchemeLattice#Pointer => + openValueArray(pointer.ptrs.size) + pointer.ptrs.foreach(writer.write(_)) + writer.writeArrayClose() + case _ => super.encodeHMapPair(writer, key, value) + } + writer.close() + trait SaveModularDomain extends SaveValue[SchemeExp] with ModularSchemeDomain: - override given valueEncoder: ArrayEncoder[HMap] with - override def writeEncapsulated(writer: Writer, value: HMap): Writer = - value.contents.foreach((key, value) => writer.writeMember(value.toString())) + def encodeHMapPair(writer: Writer, key: HMapKey, value: Any)(using encoder: EncapsulatedEncoder[_]): Writer = + System.err.nn.println("The lattice with type `" + key.getClass + "` could not be encoded") + writer + + override given valueEncoder: ArrayEncapsulatedEncoder[HMap] with MapMemberEncoder[HMap] with + override protected val writeOpenKey = false + override def writeEncapsulated(writer: Writer, hmap: HMap): Writer = + hmap.contents.foreach((key, value) => encodeHMapPair(writer, key, value)) writer + +trait SaveGlobalStore[Expr <: Expression] extends SaveValue[Expr] with SaveAddr[Expr] with SaveMapToArray with GlobalStore[Expr]: + override def saveInfo: Map[String, Savable[_]] = + super.saveInfo + ("store" -> Savable(store)) From d30211219fdec885080cc587a5bd2921ce677fa9 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Sat, 3 Feb 2024 15:03:59 +0100 Subject: [PATCH 11/97] feat(save): Save Scheme expressions --- .../src/main/scala/maf/save/Component.scala | 48 ++++++++++++++++++- .../src/main/scala/maf/save/Store.scala | 11 ++++- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/Component.scala b/code/shared/src/main/scala/maf/save/Component.scala index 420a1060a..bd06c8bd3 100644 --- a/code/shared/src/main/scala/maf/save/Component.scala +++ b/code/shared/src/main/scala/maf/save/Component.scala @@ -13,6 +13,18 @@ import maf.modular.Dependency import maf.modular.scheme.modf.SchemeModFComponent import maf.modular.scheme.modf.StandardSchemeModFComponents import EncapsulatedEncoder.* +import io.bullet.borer.derivation.MapBasedCodecs +import maf.language.scheme.SchemeLambdaExp +import maf.core.Identifier +import maf.core.Identity +import maf.core.IdentityData +import maf.language.scheme.SchemeFuncall +import maf.language.scheme.SchemeLambda +import io.bullet.borer.derivation.ArrayBasedCodecs +import maf.language.scheme.SchemeVarArgLambda +import maf.language.scheme.SchemeIf +import maf.language.scheme.SchemeLet +import maf.language.scheme.SchemeVar trait SavePosition[Expr <: Expression] extends Save[Expr]: given MapEncoder[Position] with @@ -44,6 +56,39 @@ trait SaveStandardSchemeComponents with AnalysisResults[SchemeExp] with SaveValue[SchemeExp] with SavePosition[SchemeExp]: + + given MapEncoder[SchemeExp] with + def writeEncapsulated(writer: Writer, exp: SchemeExp): Writer = + val stringEncoder = summon[Encoder[String]] + exp match + case funcall: SchemeFuncall => + writer.writeMember("type", "funcall")(using stringEncoder, stringEncoder, this) + writer.writeMember("expression", funcall)(using summon[Encoder[String]], summon[Encoder[SchemeFuncall]], this) + case variable: SchemeVar => + writer.writeMember("type", "var")(using stringEncoder, stringEncoder, this) + writer.writeMember("expression", variable)(using summon[Encoder[String]], summon[Encoder[SchemeVar]], this) + case lambda: SchemeLambda => + writer.writeMember("type", "lambda")(using stringEncoder, stringEncoder, this) + writer.writeMember("expression", lambda)(using summon[Encoder[String]], summon[Encoder[SchemeLambda]], this) + case argLambda: SchemeVarArgLambda => + writer.writeMember("type", "argLambda")(using stringEncoder, stringEncoder, this) + writer.writeMember("expression", argLambda)(using summon[Encoder[String]], summon[Encoder[SchemeVarArgLambda]], this) + case _ => + System.err.nn.println("The schemeexpression with type `" + exp.getClass + "` could not be encoded") + writer + + given Encoder[SchemeFuncall] = MapBasedCodecs.deriveEncoder[SchemeFuncall] + given Encoder[SchemeVar] = MapBasedCodecs.deriveEncoder[SchemeVar] + given Encoder[SchemeLambda] = MapBasedCodecs.deriveEncoder[SchemeLambda] + given Encoder[SchemeVarArgLambda] = MapBasedCodecs.deriveEncoder[SchemeVarArgLambda] + given Encoder[SchemeLambdaExp] = MapBasedCodecs.deriveEncoder[SchemeLambdaExp] + given Encoder[Identifier] = MapBasedCodecs.deriveEncoder[Identifier] + given Encoder[Identity] = MapBasedCodecs.deriveAllEncoders[Identity] + given Encoder[IdentityData] with + def write(writer: Writer, value: IdentityData): Writer = + System.err.nn.println("IdentityData could not be encoded") + writer + override given componentEncoder: Encoder[Component] with def write(writer: Writer, component: Component): Writer = if component.equals(initialComponent) then writer.write("main") @@ -53,5 +98,4 @@ trait SaveStandardSchemeComponents override def writeEncapsulated(writer: Writer, component: SchemeModFComponent.Call[T]): Writer = val (lambda, env) = component.clo val context = component.ctx - if lambda.name.isDefined then writer.writeMapMember("name", lambda.name.get) - writer.writeMapMember("position", lambda.idn.pos) + writer.writeMember("lambda", lambda)(using summon[Encoder[String]], summon[Encoder[SchemeLambdaExp]], this) diff --git a/code/shared/src/main/scala/maf/save/Store.scala b/code/shared/src/main/scala/maf/save/Store.scala index 5a6627ff4..6d80e89cf 100644 --- a/code/shared/src/main/scala/maf/save/Store.scala +++ b/code/shared/src/main/scala/maf/save/Store.scala @@ -21,7 +21,7 @@ import EncapsulatedEncoder.* trait SaveValue[Expr <: Expression] extends Save[Expr] with AbstractDomain[Expr]: given valueEncoder: Encoder[Value] -trait SaveModularSchemeLattices extends SaveModularDomain with SaveAddr[SchemeExp]: +trait SaveModularSchemeLattices extends SaveModularDomain with SaveAddr[SchemeExp] with SaveStandardSchemeComponents: override def encodeHMapPair(writer: Writer, key: HMapKey, value: Any)(using encoder: EncapsulatedEncoder[_]): Writer = type SchemeLattice = ModularSchemeLattice[?, ?, ?, ?, ?, ?, ?] if !value.isInstanceOf[SchemeLattice#Value] then return super.encodeHMapPair(writer, key, value) @@ -35,7 +35,14 @@ trait SaveModularSchemeLattices extends SaveModularDomain with SaveAddr[SchemeEx case bool: SchemeLattice#Bool => writeValue(bool.b.toString()) case str: SchemeLattice#Str => writeValue(str.s.toString()) case prim: SchemeLattice#Prim => writeValue(prim.prims) - case clo: SchemeLattice#Clo => clo.closures.foreach((clo) => writeValue(clo.toString)) + case clo: SchemeLattice#Clo => + writer.write("value") + writer.open(2) + clo.closures.foreach((clo) => + writer.writeMember("expression", clo._1) + writer.writeMember("address", clo._2.toString()) + ) + writer.close() case pointer: SchemeLattice#Pointer => openValueArray(pointer.ptrs.size) pointer.ptrs.foreach(writer.write(_)) From eb94b3770a7c744e4a15f40027867debc81c9e26 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 5 Feb 2024 16:58:38 +0100 Subject: [PATCH 12/97] refactor(save): Put the encoder in a variable --- .../src/main/scala/maf/save/Analysis.scala | 9 +- .../src/main/scala/maf/save/Component.scala | 44 ++++---- .../src/main/scala/maf/save/Dependency.scala | 19 ++-- .../main/scala/maf/save/Encapsulated.scala | 102 ++++++++++-------- .../src/main/scala/maf/save/Store.scala | 67 ++++++------ .../shared/src/main/scala/maf/save/Util.scala | 5 +- 6 files changed, 136 insertions(+), 110 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/Analysis.scala b/code/shared/src/main/scala/maf/save/Analysis.scala index b8b5a853a..93ff3eb29 100644 --- a/code/shared/src/main/scala/maf/save/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/Analysis.scala @@ -13,9 +13,11 @@ import EncapsulatedEncoder.* class Savable[T](val value: T)(using val encoder: Encoder[T]) trait Save[Expr <: Expression] extends ModAnalysis[Expr]: - given MapEncoder[Save[Expr]] with + def encoder[T]: AbstractEncoder[T] + given EncapsulatedEncoder[Save[Expr]] with + override val encoder = Save.this.encoder[Save[Expr]] override def writeEncapsulated(writer: Writer, value: Save[Expr]): Writer = - for (key, value) <- saveInfo do writer.writeMember(key, value.value)(using summon[Encoder[String]], value.encoder, this) + for (key, value) <- saveInfo do writer.writeMember(key, value.value)(using summon[Encoder[String]], value.encoder, encoder) writer /** @@ -38,4 +40,5 @@ trait SaveModF with SaveAddrDep with SaveSchemeAddr with SaveGlobalStore[SchemeExp] - with SaveModularSchemeLattices + with SaveModularSchemeLattices: + override def encoder[T]: AbstractEncoder[T] = new MapEncoder[T] diff --git a/code/shared/src/main/scala/maf/save/Component.scala b/code/shared/src/main/scala/maf/save/Component.scala index bd06c8bd3..51161d146 100644 --- a/code/shared/src/main/scala/maf/save/Component.scala +++ b/code/shared/src/main/scala/maf/save/Component.scala @@ -27,7 +27,9 @@ import maf.language.scheme.SchemeLet import maf.language.scheme.SchemeVar trait SavePosition[Expr <: Expression] extends Save[Expr]: - given MapEncoder[Position] with + def positionEncoder[T]: AbstractEncoder[T] = encoder + given EncapsulatedEncoder[Position] with + override val encoder = positionEncoder[Position] override def writeEncapsulated(writer: Writer, pos: Position): Writer = writer.writeMember("line", pos.line) writer.writeMember("col", pos.line) @@ -35,6 +37,7 @@ trait SavePosition[Expr <: Expression] extends Save[Expr]: writer trait SaveComponents[Expr <: Expression] extends Save[Expr]: + def componentEncoder[T]: AbstractEncoder[T] = encoder override def saveInfo: Map[String, Savable[_]] = super.saveInfo + ("components" -> Savable(visited)) given componentEncoder: Encoder[Component] @@ -57,33 +60,35 @@ trait SaveStandardSchemeComponents with SaveValue[SchemeExp] with SavePosition[SchemeExp]: - given MapEncoder[SchemeExp] with + given EncapsulatedEncoder[SchemeExp] with + override val encoder = componentEncoder[SchemeExp] def writeEncapsulated(writer: Writer, exp: SchemeExp): Writer = val stringEncoder = summon[Encoder[String]] exp match case funcall: SchemeFuncall => - writer.writeMember("type", "funcall")(using stringEncoder, stringEncoder, this) - writer.writeMember("expression", funcall)(using summon[Encoder[String]], summon[Encoder[SchemeFuncall]], this) + writer.writeMember("type", "funcall") + writer.writeMember("expression", funcall) case variable: SchemeVar => - writer.writeMember("type", "var")(using stringEncoder, stringEncoder, this) - writer.writeMember("expression", variable)(using summon[Encoder[String]], summon[Encoder[SchemeVar]], this) + writer.writeMember("type", "var") + writer.writeMember("expression", variable) case lambda: SchemeLambda => - writer.writeMember("type", "lambda")(using stringEncoder, stringEncoder, this) - writer.writeMember("expression", lambda)(using summon[Encoder[String]], summon[Encoder[SchemeLambda]], this) + writer.writeMember("type", "lambda") + writer.writeMember("expression", lambda) case argLambda: SchemeVarArgLambda => - writer.writeMember("type", "argLambda")(using stringEncoder, stringEncoder, this) - writer.writeMember("expression", argLambda)(using summon[Encoder[String]], summon[Encoder[SchemeVarArgLambda]], this) + writer.writeMember("type", "argLambda") + writer.writeMember("expression", argLambda) case _ => System.err.nn.println("The schemeexpression with type `" + exp.getClass + "` could not be encoded") writer - given Encoder[SchemeFuncall] = MapBasedCodecs.deriveEncoder[SchemeFuncall] - given Encoder[SchemeVar] = MapBasedCodecs.deriveEncoder[SchemeVar] - given Encoder[SchemeLambda] = MapBasedCodecs.deriveEncoder[SchemeLambda] - given Encoder[SchemeVarArgLambda] = MapBasedCodecs.deriveEncoder[SchemeVarArgLambda] - given Encoder[SchemeLambdaExp] = MapBasedCodecs.deriveEncoder[SchemeLambdaExp] - given Encoder[Identifier] = MapBasedCodecs.deriveEncoder[Identifier] - given Encoder[Identity] = MapBasedCodecs.deriveAllEncoders[Identity] + private val compEncoder = componentEncoder[SchemeExp] + given Encoder[SchemeFuncall] = AbstractEncoder.deriveEncoder[SchemeFuncall](compEncoder) + given Encoder[SchemeVar] = AbstractEncoder.deriveEncoder[SchemeVar](compEncoder) + given Encoder[SchemeLambda] = AbstractEncoder.deriveEncoder[SchemeLambda](compEncoder) + given Encoder[SchemeVarArgLambda] = AbstractEncoder.deriveEncoder[SchemeVarArgLambda](compEncoder) + given Encoder[SchemeLambdaExp] = AbstractEncoder.deriveEncoder[SchemeLambdaExp](compEncoder) + given Encoder[Identifier] = AbstractEncoder.deriveEncoder[Identifier](compEncoder) + given Encoder[Identity] = AbstractEncoder.deriveAllEncoders[Identity](compEncoder) given Encoder[IdentityData] with def write(writer: Writer, value: IdentityData): Writer = System.err.nn.println("IdentityData could not be encoded") @@ -94,8 +99,9 @@ trait SaveStandardSchemeComponents if component.equals(initialComponent) then writer.write("main") else writer.write(component.asInstanceOf[SchemeModFComponent.Call[ComponentContext]]) - given [T]: MapEncoder[SchemeModFComponent.Call[T]] with + given [T]: EncapsulatedEncoder[SchemeModFComponent.Call[T]] with + override val encoder = componentEncoder[SchemeModFComponent.Call[T]] override def writeEncapsulated(writer: Writer, component: SchemeModFComponent.Call[T]): Writer = val (lambda, env) = component.clo val context = component.ctx - writer.writeMember("lambda", lambda)(using summon[Encoder[String]], summon[Encoder[SchemeLambdaExp]], this) + writer.writeMember("lambda", lambda) diff --git a/code/shared/src/main/scala/maf/save/Dependency.scala b/code/shared/src/main/scala/maf/save/Dependency.scala index 1f57102c2..81c50b6fb 100644 --- a/code/shared/src/main/scala/maf/save/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/Dependency.scala @@ -21,6 +21,7 @@ trait SaveAddrDep extends SaveDependency with SavePosition[SchemeExp] with SaveS } trait SaveDependency extends SaveMapToArray with SaveStandardSchemeComponentID: + def dependencyEncoder[T]: AbstractEncoder[T] = encoder override def saveInfo: Map[String, Savable[_]] = import componentIDEncoder.given super.saveInfo + ("dependencies" -> Savable(deps)) @@ -31,21 +32,19 @@ trait SaveDependency extends SaveMapToArray with SaveStandardSchemeComponentID: given dependencyEncoder: Encoder[Dependency] = encodeDependency _ trait SaveAddr[Expr <: Expression] extends Save[Expr] with SavePosition[Expr]: - def encodeAddress(writer: Writer, address: Address): Writer = + def addressEncoder[T]: AbstractEncoder[T] = encoder + def encodeAddress(writer: Writer, address: Address)(using encoder: AbstractEncoder[Address]): Writer = System.err.nn.println("The address with type `" + address.getClass + "` could not be encoded") writer - trait AddrEncoder extends EncapsulatedEncoder[Address]: - override def write(writer: Writer, address: Address): Writer = - writer.writeMember("position", address.idn.pos) - super.write(writer, address) - - trait AddrMapEncoder extends AddrEncoder with MapEncoder[Address] - given addressEncoder: AddrMapEncoder with - override def writeEncapsulated(writer: Writer, value: Address): Writer = encodeAddress(writer, value) + given EncapsulatedEncoder[Address] with + override val encoder = addressEncoder[Address] + override def writeEncapsulated(writer: Writer, value: Address): Writer = + writer.writeMember("position", value.idn.pos) + encodeAddress(writer, value) trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveStandardSchemeComponentID: - override def encodeAddress(writer: Writer, address: Address): Writer = + override def encodeAddress(writer: Writer, address: Address)(using encoder: AbstractEncoder[Address]): Writer = import componentIDEncoder.given address match { case VarAddr(id, ctx) => diff --git a/code/shared/src/main/scala/maf/save/Encapsulated.scala b/code/shared/src/main/scala/maf/save/Encapsulated.scala index 992a2f2b8..f76dcd234 100644 --- a/code/shared/src/main/scala/maf/save/Encapsulated.scala +++ b/code/shared/src/main/scala/maf/save/Encapsulated.scala @@ -2,72 +2,82 @@ package maf.save import io.bullet.borer.Encoder import io.bullet.borer.Writer +import io.bullet.borer.derivation.MapBasedCodecs +import io.bullet.borer.derivation.ArrayBasedCodecs + +trait AbstractEncoder[T]: + val mapBasedEncoder: Boolean + val writeOpenKey = true + def writeKey(writer: Writer): Writer + def writeKey[T: Encoder](writer: Writer, key: T): Writer + def writeValue[T: Encoder](writer: Writer, value: T): Writer + def openEncapsulation(writer: Writer): Writer + def openEncapsulation(writer: Writer, amount: Int): Writer + def closeEncapsulation(writer: Writer): Writer + +object AbstractEncoder: + inline def deriveEncoder[T](encoder: AbstractEncoder[_]): Encoder[T] = + if encoder.mapBasedEncoder then MapBasedCodecs.deriveEncoder[T] else ArrayBasedCodecs.deriveEncoder[T] + inline def deriveAllEncoders[T](encoder: AbstractEncoder[_]): Encoder[T] = + if encoder.mapBasedEncoder then MapBasedCodecs.deriveAllEncoders[T] else ArrayBasedCodecs.deriveAllEncoders[T] trait EncapsulatedEncoder[T] extends Encoder[T]: - override def write(writer: Writer, value: T): Writer = writeEncapsulated(writer, value) - protected val writeOpenKey = true + val encoder: AbstractEncoder[T] + protected given AbstractEncoder[T] = encoder + override def write(writer: Writer, value: T): Writer = + encoder.openEncapsulation(writer) + writeEncapsulated(writer, value) + encoder.closeEncapsulation(writer) protected def writeEncapsulated(writer: Writer, value: T): Writer - protected def writeKey(writer: Writer): Writer - protected def writeKey[T: Encoder](writer: Writer, key: T): Writer - protected def writeValue[T: Encoder](writer: Writer, value: T): Writer - protected def openEncapsulation(writer: Writer): Writer - protected def openEncapsulation(writer: Writer, amount: Int): Writer - protected def closeEncapsulation(writer: Writer): Writer object EncapsulatedEncoder: extension (writer: Writer) - def close()(using encoder: EncapsulatedEncoder[_]): Writer = encoder.closeEncapsulation(writer) - def writeMember[T: Encoder, U: Encoder](key: T, value: U)(using encoder: EncapsulatedEncoder[_]): Writer = + def close()(using encoder: AbstractEncoder[_]): Writer = encoder.closeEncapsulation(writer) + def writeMember[T: Encoder, U: Encoder](key: T, value: U)(using encoder: AbstractEncoder[_]): Writer = encoder.writeKey(writer, key) encoder.writeValue(writer, value) - def writeMember[T: Encoder](value: T)(using encoder: EncapsulatedEncoder[_]): Writer = + def writeMember[T: Encoder](value: T)(using encoder: AbstractEncoder[_]): Writer = encoder.writeKey(writer) encoder.writeValue(writer, value) - def open()(using encoder: EncapsulatedEncoder[_]): Writer = + def open()(using encoder: AbstractEncoder[_]): Writer = if encoder.writeOpenKey then encoder.writeKey(writer) encoder.openEncapsulation(writer) - def open(amount: Int)(using encoder: EncapsulatedEncoder[_]): Writer = + def open[T: Encoder](key: T)(using encoder: AbstractEncoder[_]): Writer = + encoder.writeKey(writer, key) + encoder.openEncapsulation(writer) + def open(amount: Int)(using encoder: AbstractEncoder[_]): Writer = if encoder.writeOpenKey then encoder.writeKey(writer) encoder.openEncapsulation(writer, amount) + def open[T: Encoder](key: T, amount: Int)(using encoder: AbstractEncoder[_]): Writer = + encoder.writeKey(writer, key) + encoder.openEncapsulation(writer) -trait MapEncapsulatedEncoder[T] extends EncapsulatedEncoder[T]: - final override def write(writer: Writer, value: T): Writer = - writer.writeMapStart() - super.write(writer, value) - writer.writeMapClose() - -trait MapMemberEncoder[T] extends EncapsulatedEncoder[T]: +class MapEncoder[T] extends AbstractEncoder[T]: + val mapBasedEncoder = true private var id = -1 - override protected def writeKey(writer: Writer): Writer = + override def writeKey(writer: Writer): Writer = id += 1 writeKey(writer, id.toString()) - override protected def writeKey[T: Encoder](writer: Writer, key: T): Writer = writer.write(key) - override protected def writeValue[T: Encoder](writer: Writer, value: T): Writer = writer.write(value) - override protected def openEncapsulation(writer: Writer): Writer = writer.writeMapStart() - override protected def openEncapsulation(writer: Writer, amount: Int): Writer = writer.writeMapOpen(amount) - override protected def closeEncapsulation(writer: Writer): Writer = writer.writeMapClose() -trait MapEncoder[T] extends MapEncapsulatedEncoder[T] with MapMemberEncoder[T] - -trait ArrayEncapsulatedEncoder[T] extends EncapsulatedEncoder[T]: - final override def write(writer: Writer, value: T): Writer = - writer.writeArrayStart() - super.write(writer, value) - writer.writeArrayClose() + override def writeKey[T: Encoder](writer: Writer, key: T): Writer = writer.write(key) + override def writeValue[T: Encoder](writer: Writer, value: T): Writer = writer.write(value) + override def openEncapsulation(writer: Writer): Writer = writer.writeMapStart() + override def openEncapsulation(writer: Writer, amount: Int): Writer = writer.writeMapOpen(amount) + override def closeEncapsulation(writer: Writer): Writer = writer.writeMapClose() -trait ArrayMemberEncoder[T] extends EncapsulatedEncoder[T]: - override protected def writeKey(writer: Writer): Writer = writer - override protected def writeKey[T: Encoder](writer: Writer, key: T): Writer = writer - override protected def writeValue[T: Encoder](writer: Writer, value: T): Writer = writer.write(value) - override protected def openEncapsulation(writer: Writer): Writer = writer.writeArrayStart() - override protected def openEncapsulation(writer: Writer, amount: Int): Writer = writer.writeArrayOpen(amount) - override protected def closeEncapsulation(writer: Writer): Writer = writer.writeArrayClose() -trait ArrayEncoder[T] extends ArrayEncapsulatedEncoder[T] with ArrayMemberEncoder[T] +class ArrayEncoder[T] extends AbstractEncoder[T]: + val mapBasedEncoder = false + override def writeKey(writer: Writer): Writer = writer + override def writeKey[T: Encoder](writer: Writer, key: T): Writer = writer + override def writeValue[T: Encoder](writer: Writer, value: T): Writer = writer.write(value) + override def openEncapsulation(writer: Writer): Writer = writer.writeArrayStart() + override def openEncapsulation(writer: Writer, amount: Int): Writer = writer.writeArrayOpen(amount) + override def closeEncapsulation(writer: Writer): Writer = writer.writeArrayClose() -trait ArrayKeyEncoder[T] extends ArrayEncoder[T]: +class ArrayKeyEncoder[T] extends ArrayEncoder[T]: private var id = -1 - override protected val writeOpenKey: Boolean = false - override protected def writeKey(writer: Writer): Writer = + override val writeOpenKey: Boolean = false + override def writeKey(writer: Writer): Writer = id += 1 writeKey(writer, id.toString()) - override protected def writeKey[T: Encoder](writer: Writer, key: T): Writer = openEncapsulation(writer, 2).write(key) - override protected def writeValue[T: Encoder](writer: Writer, value: T): Writer = closeEncapsulation(writer.write(value)) + override def writeKey[T: Encoder](writer: Writer, key: T): Writer = openEncapsulation(writer, 2).write(key) + override def writeValue[T: Encoder](writer: Writer, value: T): Writer = closeEncapsulation(writer.write(value)) diff --git a/code/shared/src/main/scala/maf/save/Store.scala b/code/shared/src/main/scala/maf/save/Store.scala index 6d80e89cf..c9e020922 100644 --- a/code/shared/src/main/scala/maf/save/Store.scala +++ b/code/shared/src/main/scala/maf/save/Store.scala @@ -19,45 +19,52 @@ import maf.core.Address import EncapsulatedEncoder.* trait SaveValue[Expr <: Expression] extends Save[Expr] with AbstractDomain[Expr]: + def valueEncoder[T]: AbstractEncoder[T] = encoder given valueEncoder: Encoder[Value] trait SaveModularSchemeLattices extends SaveModularDomain with SaveAddr[SchemeExp] with SaveStandardSchemeComponents: - override def encodeHMapPair(writer: Writer, key: HMapKey, value: Any)(using encoder: EncapsulatedEncoder[_]): Writer = - type SchemeLattice = ModularSchemeLattice[?, ?, ?, ?, ?, ?, ?] - if !value.isInstanceOf[SchemeLattice#Value] then return super.encodeHMapPair(writer, key, value) - writer.open() - def writeValue[T: Encoder](value: T) = writer.writeMember("value", value) - def openValueArray(amount: Int) = writer.write("value").writeArrayOpen(amount) + type SchemeLattice = ModularSchemeLattice[?, ?, ?, ?, ?, ?, ?] + given EncapsulatedEncoder[(HMapKey, SchemeLattice#Value)] with + override val encoder = valueEncoder[(HMapKey, SchemeLattice#Value)] + override protected def writeEncapsulated(writer: Writer, hMapPair: (HMapKey, SchemeLattice#Value)): Writer = + val (key, value) = hMapPair + def writeValue[T: Encoder](value: T) = writer.writeMember("value", value) + def openValueArray(amount: Int) = writer.write("value").writeArrayOpen(amount) - writer.writeMember("type", value.asInstanceOf[ModularSchemeLattice[?, ?, ?, ?, ?, ?, ?]#Value].typeName) - value match { - case int: SchemeLattice#Int => writeValue(int.i.toString()) - case bool: SchemeLattice#Bool => writeValue(bool.b.toString()) - case str: SchemeLattice#Str => writeValue(str.s.toString()) - case prim: SchemeLattice#Prim => writeValue(prim.prims) - case clo: SchemeLattice#Clo => - writer.write("value") - writer.open(2) - clo.closures.foreach((clo) => - writer.writeMember("expression", clo._1) - writer.writeMember("address", clo._2.toString()) - ) - writer.close() - case pointer: SchemeLattice#Pointer => - openValueArray(pointer.ptrs.size) - pointer.ptrs.foreach(writer.write(_)) - writer.writeArrayClose() - case _ => super.encodeHMapPair(writer, key, value) - } - writer.close() + writer.writeMember("type", value.typeName) + value match { + case int: SchemeLattice#Int => writeValue(int.i.toString()) + case bool: SchemeLattice#Bool => writeValue(bool.b.toString()) + case str: SchemeLattice#Str => writeValue(str.s.toString()) + case prim: SchemeLattice#Prim => writeValue(prim.prims) + case clo: SchemeLattice#Clo => + writer.open("value", 2) + clo.closures.foreach((clo) => + writer.writeMember("expression", clo._1) + writer.writeMember("address", clo._2.toString()) + ) + writer.close() + case pointer: SchemeLattice#Pointer => + openValueArray(pointer.ptrs.size) + pointer.ptrs.foreach(writer.write(_)) + writer.writeArrayClose() + case _ => + System.err.nn.println("The lattice with type `" + key.getClass + "` could not be encoded") + writer + } + + override def encodeHMapPair(writer: Writer, key: HMapKey, value: Any)(using encoder: AbstractEncoder[_]): Writer = + if value.isInstanceOf[SchemeLattice#Value] then writer.writeMember((key, value.asInstanceOf[SchemeLattice#Value])) + else return super.encodeHMapPair(writer, key, value) trait SaveModularDomain extends SaveValue[SchemeExp] with ModularSchemeDomain: - def encodeHMapPair(writer: Writer, key: HMapKey, value: Any)(using encoder: EncapsulatedEncoder[_]): Writer = + def hMapEncoder[T]: AbstractEncoder[T] = new ArrayEncoder[T] + def encodeHMapPair(writer: Writer, key: HMapKey, value: Any)(using encoder: AbstractEncoder[_]): Writer = System.err.nn.println("The lattice with type `" + key.getClass + "` could not be encoded") writer - override given valueEncoder: ArrayEncapsulatedEncoder[HMap] with MapMemberEncoder[HMap] with - override protected val writeOpenKey = false + override given valueEncoder: EncapsulatedEncoder[HMap] with + override val encoder = hMapEncoder[HMap] override def writeEncapsulated(writer: Writer, hmap: HMap): Writer = hmap.contents.foreach((key, value) => encodeHMapPair(writer, key, value)) writer diff --git a/code/shared/src/main/scala/maf/save/Util.scala b/code/shared/src/main/scala/maf/save/Util.scala index 5b2ec4c58..790528724 100644 --- a/code/shared/src/main/scala/maf/save/Util.scala +++ b/code/shared/src/main/scala/maf/save/Util.scala @@ -5,7 +5,8 @@ import io.bullet.borer.Writer import EncapsulatedEncoder.* trait SaveMapToArray: - given mapKeyEncoder[K, V](using keyEncoder: Encoder[K], valueEncoder: Encoder[V]): ArrayKeyEncoder[Map[K, V]] with + given mapKeyEncoder[K, V](using keyEncoder: Encoder[K], valueEncoder: Encoder[V]): EncapsulatedEncoder[Map[K, V]] with + override val encoder = new ArrayKeyEncoder[Map[K, V]] override def writeEncapsulated(writer: Writer, map: Map[K, V]): Writer = - for (key, value) <- map do writer.writeMember(key, value)(using keyEncoder, valueEncoder, this) + for (key, value) <- map do writer.writeMember(key, value) writer From 9476f180e8f3a59a1ff28b0a72b2054495b9fe8f Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 5 Feb 2024 18:12:41 +0100 Subject: [PATCH 13/97] feat(save): Save environment --- .../src/main/scala/maf/save/Component.scala | 25 ++++++++++++++++++- .../src/main/scala/maf/save/Store.scala | 5 ++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/Component.scala b/code/shared/src/main/scala/maf/save/Component.scala index 51161d146..1f87fd452 100644 --- a/code/shared/src/main/scala/maf/save/Component.scala +++ b/code/shared/src/main/scala/maf/save/Component.scala @@ -25,6 +25,10 @@ import maf.language.scheme.SchemeVarArgLambda import maf.language.scheme.SchemeIf import maf.language.scheme.SchemeLet import maf.language.scheme.SchemeVar +import maf.core.BasicEnvironment +import maf.core.Environment +import maf.core.WrappedEnv +import maf.core.NestedEnv trait SavePosition[Expr <: Expression] extends Save[Expr]: def positionEncoder[T]: AbstractEncoder[T] = encoder @@ -53,12 +57,30 @@ trait SaveStandardSchemeComponentID extends StandardSchemeModFComponents with Sa val (lambda, _) = component.clo writer.write(lambda.idn.pos) +trait SaveEnvironment[Expr <: Expression] extends Save[Expr] with SaveAddr[Expr]: + def environmentEncoder[T]: AbstractEncoder[T] = encoder + given [T <: Address]: EncapsulatedEncoder[Environment[T]] with + override val encoder: AbstractEncoder[Environment[T]] = environmentEncoder + override protected def writeEncapsulated(writer: Writer, env: Environment[T]): Writer = + env match { + case BasicEnvironment(content) => + writer.writeMember("content", content.asInstanceOf[Map[String, Address]]) + case NestedEnv(content, rst) => + writer.writeMember("content", content.asInstanceOf[Map[String, Address]]) + if rst.isDefined then writer.writeMember("rst", rst.get.asInstanceOf[Address]) + writer + case _ => + System.err.nn.println("The environemnt with type `" + env.getClass + "` could not be encoded") + writer + } + trait SaveStandardSchemeComponents extends SaveComponents[SchemeExp] with StandardSchemeModFComponents with AnalysisResults[SchemeExp] with SaveValue[SchemeExp] - with SavePosition[SchemeExp]: + with SavePosition[SchemeExp] + with SaveEnvironment[SchemeExp]: given EncapsulatedEncoder[SchemeExp] with override val encoder = componentEncoder[SchemeExp] @@ -105,3 +127,4 @@ trait SaveStandardSchemeComponents val (lambda, env) = component.clo val context = component.ctx writer.writeMember("lambda", lambda) + writer.writeMember("environment", env) diff --git a/code/shared/src/main/scala/maf/save/Store.scala b/code/shared/src/main/scala/maf/save/Store.scala index c9e020922..a215389b5 100644 --- a/code/shared/src/main/scala/maf/save/Store.scala +++ b/code/shared/src/main/scala/maf/save/Store.scala @@ -17,12 +17,13 @@ import maf.lattice.AbstractSetType import io.bullet.borer.LowPrioEncoders import maf.core.Address import EncapsulatedEncoder.* +import maf.core.Environment trait SaveValue[Expr <: Expression] extends Save[Expr] with AbstractDomain[Expr]: def valueEncoder[T]: AbstractEncoder[T] = encoder given valueEncoder: Encoder[Value] -trait SaveModularSchemeLattices extends SaveModularDomain with SaveAddr[SchemeExp] with SaveStandardSchemeComponents: +trait SaveModularSchemeLattices extends SaveModularDomain with SaveAddr[SchemeExp] with SaveStandardSchemeComponents with SaveEnvironment[SchemeExp]: type SchemeLattice = ModularSchemeLattice[?, ?, ?, ?, ?, ?, ?] given EncapsulatedEncoder[(HMapKey, SchemeLattice#Value)] with override val encoder = valueEncoder[(HMapKey, SchemeLattice#Value)] @@ -41,7 +42,7 @@ trait SaveModularSchemeLattices extends SaveModularDomain with SaveAddr[SchemeEx writer.open("value", 2) clo.closures.foreach((clo) => writer.writeMember("expression", clo._1) - writer.writeMember("address", clo._2.toString()) + writer.writeMember("address", clo._2.asInstanceOf[Environment[Address]]) ) writer.close() case pointer: SchemeLattice#Pointer => From b7a1b9c628061067c386d5fbf2500eafe9eba66d Mon Sep 17 00:00:00 2001 From: Merlijn Date: Tue, 6 Feb 2024 07:06:55 +0100 Subject: [PATCH 14/97] feat(save): Save environment Only noEnvironment is currently implemented --- .../src/main/scala/maf/save/Analysis.scala | 3 ++- .../src/main/scala/maf/save/Component.scala | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/Analysis.scala b/code/shared/src/main/scala/maf/save/Analysis.scala index 93ff3eb29..65e58f333 100644 --- a/code/shared/src/main/scala/maf/save/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/Analysis.scala @@ -40,5 +40,6 @@ trait SaveModF with SaveAddrDep with SaveSchemeAddr with SaveGlobalStore[SchemeExp] - with SaveModularSchemeLattices: + with SaveModularSchemeLattices + with SaveNoContext[SchemeExp]: override def encoder[T]: AbstractEncoder[T] = new MapEncoder[T] diff --git a/code/shared/src/main/scala/maf/save/Component.scala b/code/shared/src/main/scala/maf/save/Component.scala index 1f87fd452..b12e580e0 100644 --- a/code/shared/src/main/scala/maf/save/Component.scala +++ b/code/shared/src/main/scala/maf/save/Component.scala @@ -29,6 +29,8 @@ import maf.core.BasicEnvironment import maf.core.Environment import maf.core.WrappedEnv import maf.core.NestedEnv +import maf.modular.scv.ScvContextSensitivity +import maf.modular.scheme.modf.NoContext trait SavePosition[Expr <: Expression] extends Save[Expr]: def positionEncoder[T]: AbstractEncoder[T] = encoder @@ -74,13 +76,24 @@ trait SaveEnvironment[Expr <: Expression] extends Save[Expr] with SaveAddr[Expr] writer } +trait SaveContext[Expr <: Expression] extends Save[Expr]: + type Context + def contextEncoder[T]: AbstractEncoder[T] = encoder + given contextEncoder: Encoder[Context] + +trait SaveNoContext[Expr <: Expression] extends SaveContext[Expr]: + type Context = NoContext.type + override given contextEncoder: Encoder[Context] with + override def write(writer: Writer, context: Context): Writer = writer.write("ε") + trait SaveStandardSchemeComponents extends SaveComponents[SchemeExp] with StandardSchemeModFComponents with AnalysisResults[SchemeExp] with SaveValue[SchemeExp] with SavePosition[SchemeExp] - with SaveEnvironment[SchemeExp]: + with SaveEnvironment[SchemeExp] + with SaveContext[SchemeExp]: given EncapsulatedEncoder[SchemeExp] with override val encoder = componentEncoder[SchemeExp] @@ -128,3 +141,4 @@ trait SaveStandardSchemeComponents val context = component.ctx writer.writeMember("lambda", lambda) writer.writeMember("environment", env) + writer.writeMember("context", context.asInstanceOf[Context]) From 34a35bd9fa5d01f262bb4936b603ab1d688cf7d8 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Tue, 6 Feb 2024 14:20:08 +0100 Subject: [PATCH 15/97] refactor(save): Remove type parameter from `AbstractEncoder` --- .../src/main/scala/maf/save/Analysis.scala | 6 ++-- .../src/main/scala/maf/save/Component.scala | 18 +++++------ .../src/main/scala/maf/save/Dependency.scala | 10 +++---- .../main/scala/maf/save/Encapsulated.scala | 30 +++++++++---------- .../src/main/scala/maf/save/Store.scala | 12 ++++---- .../shared/src/main/scala/maf/save/Util.scala | 2 +- 6 files changed, 39 insertions(+), 39 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/Analysis.scala b/code/shared/src/main/scala/maf/save/Analysis.scala index 65e58f333..3056ba188 100644 --- a/code/shared/src/main/scala/maf/save/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/Analysis.scala @@ -13,9 +13,9 @@ import EncapsulatedEncoder.* class Savable[T](val value: T)(using val encoder: Encoder[T]) trait Save[Expr <: Expression] extends ModAnalysis[Expr]: - def encoder[T]: AbstractEncoder[T] + def getEncoder: AbstractEncoder given EncapsulatedEncoder[Save[Expr]] with - override val encoder = Save.this.encoder[Save[Expr]] + override val encoder = Save.this.getEncoder override def writeEncapsulated(writer: Writer, value: Save[Expr]): Writer = for (key, value) <- saveInfo do writer.writeMember(key, value.value)(using summon[Encoder[String]], value.encoder, encoder) writer @@ -42,4 +42,4 @@ trait SaveModF with SaveGlobalStore[SchemeExp] with SaveModularSchemeLattices with SaveNoContext[SchemeExp]: - override def encoder[T]: AbstractEncoder[T] = new MapEncoder[T] + override def getEncoder: AbstractEncoder = new MapEncoder diff --git a/code/shared/src/main/scala/maf/save/Component.scala b/code/shared/src/main/scala/maf/save/Component.scala index b12e580e0..20b86f629 100644 --- a/code/shared/src/main/scala/maf/save/Component.scala +++ b/code/shared/src/main/scala/maf/save/Component.scala @@ -33,9 +33,9 @@ import maf.modular.scv.ScvContextSensitivity import maf.modular.scheme.modf.NoContext trait SavePosition[Expr <: Expression] extends Save[Expr]: - def positionEncoder[T]: AbstractEncoder[T] = encoder + def getPositionEncoder: AbstractEncoder = getEncoder given EncapsulatedEncoder[Position] with - override val encoder = positionEncoder[Position] + override val encoder = getPositionEncoder override def writeEncapsulated(writer: Writer, pos: Position): Writer = writer.writeMember("line", pos.line) writer.writeMember("col", pos.line) @@ -43,7 +43,7 @@ trait SavePosition[Expr <: Expression] extends Save[Expr]: writer trait SaveComponents[Expr <: Expression] extends Save[Expr]: - def componentEncoder[T]: AbstractEncoder[T] = encoder + def getComponentEncoder: AbstractEncoder = getEncoder override def saveInfo: Map[String, Savable[_]] = super.saveInfo + ("components" -> Savable(visited)) given componentEncoder: Encoder[Component] @@ -60,9 +60,9 @@ trait SaveStandardSchemeComponentID extends StandardSchemeModFComponents with Sa writer.write(lambda.idn.pos) trait SaveEnvironment[Expr <: Expression] extends Save[Expr] with SaveAddr[Expr]: - def environmentEncoder[T]: AbstractEncoder[T] = encoder + def getEnvironmentEncoder: AbstractEncoder = getEncoder given [T <: Address]: EncapsulatedEncoder[Environment[T]] with - override val encoder: AbstractEncoder[Environment[T]] = environmentEncoder + override val encoder: AbstractEncoder = getEnvironmentEncoder override protected def writeEncapsulated(writer: Writer, env: Environment[T]): Writer = env match { case BasicEnvironment(content) => @@ -78,7 +78,7 @@ trait SaveEnvironment[Expr <: Expression] extends Save[Expr] with SaveAddr[Expr] trait SaveContext[Expr <: Expression] extends Save[Expr]: type Context - def contextEncoder[T]: AbstractEncoder[T] = encoder + def getContextEncoder: AbstractEncoder = getEncoder given contextEncoder: Encoder[Context] trait SaveNoContext[Expr <: Expression] extends SaveContext[Expr]: @@ -96,7 +96,7 @@ trait SaveStandardSchemeComponents with SaveContext[SchemeExp]: given EncapsulatedEncoder[SchemeExp] with - override val encoder = componentEncoder[SchemeExp] + override val encoder = getComponentEncoder def writeEncapsulated(writer: Writer, exp: SchemeExp): Writer = val stringEncoder = summon[Encoder[String]] exp match @@ -116,7 +116,7 @@ trait SaveStandardSchemeComponents System.err.nn.println("The schemeexpression with type `" + exp.getClass + "` could not be encoded") writer - private val compEncoder = componentEncoder[SchemeExp] + private val compEncoder = getComponentEncoder given Encoder[SchemeFuncall] = AbstractEncoder.deriveEncoder[SchemeFuncall](compEncoder) given Encoder[SchemeVar] = AbstractEncoder.deriveEncoder[SchemeVar](compEncoder) given Encoder[SchemeLambda] = AbstractEncoder.deriveEncoder[SchemeLambda](compEncoder) @@ -135,7 +135,7 @@ trait SaveStandardSchemeComponents else writer.write(component.asInstanceOf[SchemeModFComponent.Call[ComponentContext]]) given [T]: EncapsulatedEncoder[SchemeModFComponent.Call[T]] with - override val encoder = componentEncoder[SchemeModFComponent.Call[T]] + override val encoder = getComponentEncoder override def writeEncapsulated(writer: Writer, component: SchemeModFComponent.Call[T]): Writer = val (lambda, env) = component.clo val context = component.ctx diff --git a/code/shared/src/main/scala/maf/save/Dependency.scala b/code/shared/src/main/scala/maf/save/Dependency.scala index 81c50b6fb..f7e660ea5 100644 --- a/code/shared/src/main/scala/maf/save/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/Dependency.scala @@ -21,7 +21,7 @@ trait SaveAddrDep extends SaveDependency with SavePosition[SchemeExp] with SaveS } trait SaveDependency extends SaveMapToArray with SaveStandardSchemeComponentID: - def dependencyEncoder[T]: AbstractEncoder[T] = encoder + def getDependencyEncoder: AbstractEncoder = getEncoder override def saveInfo: Map[String, Savable[_]] = import componentIDEncoder.given super.saveInfo + ("dependencies" -> Savable(deps)) @@ -32,19 +32,19 @@ trait SaveDependency extends SaveMapToArray with SaveStandardSchemeComponentID: given dependencyEncoder: Encoder[Dependency] = encodeDependency _ trait SaveAddr[Expr <: Expression] extends Save[Expr] with SavePosition[Expr]: - def addressEncoder[T]: AbstractEncoder[T] = encoder - def encodeAddress(writer: Writer, address: Address)(using encoder: AbstractEncoder[Address]): Writer = + def getAddressEncoder: AbstractEncoder = getEncoder + def encodeAddress(writer: Writer, address: Address)(using encoder: AbstractEncoder): Writer = System.err.nn.println("The address with type `" + address.getClass + "` could not be encoded") writer given EncapsulatedEncoder[Address] with - override val encoder = addressEncoder[Address] + override val encoder = getAddressEncoder override def writeEncapsulated(writer: Writer, value: Address): Writer = writer.writeMember("position", value.idn.pos) encodeAddress(writer, value) trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveStandardSchemeComponentID: - override def encodeAddress(writer: Writer, address: Address)(using encoder: AbstractEncoder[Address]): Writer = + override def encodeAddress(writer: Writer, address: Address)(using encoder: AbstractEncoder): Writer = import componentIDEncoder.given address match { case VarAddr(id, ctx) => diff --git a/code/shared/src/main/scala/maf/save/Encapsulated.scala b/code/shared/src/main/scala/maf/save/Encapsulated.scala index f76dcd234..e08506501 100644 --- a/code/shared/src/main/scala/maf/save/Encapsulated.scala +++ b/code/shared/src/main/scala/maf/save/Encapsulated.scala @@ -5,7 +5,7 @@ import io.bullet.borer.Writer import io.bullet.borer.derivation.MapBasedCodecs import io.bullet.borer.derivation.ArrayBasedCodecs -trait AbstractEncoder[T]: +trait AbstractEncoder: val mapBasedEncoder: Boolean val writeOpenKey = true def writeKey(writer: Writer): Writer @@ -16,14 +16,14 @@ trait AbstractEncoder[T]: def closeEncapsulation(writer: Writer): Writer object AbstractEncoder: - inline def deriveEncoder[T](encoder: AbstractEncoder[_]): Encoder[T] = + inline def deriveEncoder[T](encoder: AbstractEncoder): Encoder[T] = if encoder.mapBasedEncoder then MapBasedCodecs.deriveEncoder[T] else ArrayBasedCodecs.deriveEncoder[T] - inline def deriveAllEncoders[T](encoder: AbstractEncoder[_]): Encoder[T] = + inline def deriveAllEncoders[T](encoder: AbstractEncoder): Encoder[T] = if encoder.mapBasedEncoder then MapBasedCodecs.deriveAllEncoders[T] else ArrayBasedCodecs.deriveAllEncoders[T] trait EncapsulatedEncoder[T] extends Encoder[T]: - val encoder: AbstractEncoder[T] - protected given AbstractEncoder[T] = encoder + val encoder: AbstractEncoder + protected given AbstractEncoder = encoder override def write(writer: Writer, value: T): Writer = encoder.openEncapsulation(writer) writeEncapsulated(writer, value) @@ -32,27 +32,27 @@ trait EncapsulatedEncoder[T] extends Encoder[T]: object EncapsulatedEncoder: extension (writer: Writer) - def close()(using encoder: AbstractEncoder[_]): Writer = encoder.closeEncapsulation(writer) - def writeMember[T: Encoder, U: Encoder](key: T, value: U)(using encoder: AbstractEncoder[_]): Writer = + def close()(using encoder: AbstractEncoder): Writer = encoder.closeEncapsulation(writer) + def writeMember[T: Encoder, U: Encoder](key: T, value: U)(using encoder: AbstractEncoder): Writer = encoder.writeKey(writer, key) encoder.writeValue(writer, value) - def writeMember[T: Encoder](value: T)(using encoder: AbstractEncoder[_]): Writer = + def writeMember[T: Encoder](value: T)(using encoder: AbstractEncoder): Writer = encoder.writeKey(writer) encoder.writeValue(writer, value) - def open()(using encoder: AbstractEncoder[_]): Writer = + def open()(using encoder: AbstractEncoder): Writer = if encoder.writeOpenKey then encoder.writeKey(writer) encoder.openEncapsulation(writer) - def open[T: Encoder](key: T)(using encoder: AbstractEncoder[_]): Writer = + def open[T: Encoder](key: T)(using encoder: AbstractEncoder): Writer = encoder.writeKey(writer, key) encoder.openEncapsulation(writer) - def open(amount: Int)(using encoder: AbstractEncoder[_]): Writer = + def open(amount: Int)(using encoder: AbstractEncoder): Writer = if encoder.writeOpenKey then encoder.writeKey(writer) encoder.openEncapsulation(writer, amount) - def open[T: Encoder](key: T, amount: Int)(using encoder: AbstractEncoder[_]): Writer = + def open[T: Encoder](key: T, amount: Int)(using encoder: AbstractEncoder): Writer = encoder.writeKey(writer, key) encoder.openEncapsulation(writer) -class MapEncoder[T] extends AbstractEncoder[T]: +class MapEncoder extends AbstractEncoder: val mapBasedEncoder = true private var id = -1 override def writeKey(writer: Writer): Writer = @@ -64,7 +64,7 @@ class MapEncoder[T] extends AbstractEncoder[T]: override def openEncapsulation(writer: Writer, amount: Int): Writer = writer.writeMapOpen(amount) override def closeEncapsulation(writer: Writer): Writer = writer.writeMapClose() -class ArrayEncoder[T] extends AbstractEncoder[T]: +class ArrayEncoder extends AbstractEncoder: val mapBasedEncoder = false override def writeKey(writer: Writer): Writer = writer override def writeKey[T: Encoder](writer: Writer, key: T): Writer = writer @@ -73,7 +73,7 @@ class ArrayEncoder[T] extends AbstractEncoder[T]: override def openEncapsulation(writer: Writer, amount: Int): Writer = writer.writeArrayOpen(amount) override def closeEncapsulation(writer: Writer): Writer = writer.writeArrayClose() -class ArrayKeyEncoder[T] extends ArrayEncoder[T]: +class ArrayKeyEncoder extends ArrayEncoder: private var id = -1 override val writeOpenKey: Boolean = false override def writeKey(writer: Writer): Writer = diff --git a/code/shared/src/main/scala/maf/save/Store.scala b/code/shared/src/main/scala/maf/save/Store.scala index a215389b5..775d61243 100644 --- a/code/shared/src/main/scala/maf/save/Store.scala +++ b/code/shared/src/main/scala/maf/save/Store.scala @@ -20,13 +20,13 @@ import EncapsulatedEncoder.* import maf.core.Environment trait SaveValue[Expr <: Expression] extends Save[Expr] with AbstractDomain[Expr]: - def valueEncoder[T]: AbstractEncoder[T] = encoder + def getValueEncoder: AbstractEncoder = getEncoder given valueEncoder: Encoder[Value] trait SaveModularSchemeLattices extends SaveModularDomain with SaveAddr[SchemeExp] with SaveStandardSchemeComponents with SaveEnvironment[SchemeExp]: type SchemeLattice = ModularSchemeLattice[?, ?, ?, ?, ?, ?, ?] given EncapsulatedEncoder[(HMapKey, SchemeLattice#Value)] with - override val encoder = valueEncoder[(HMapKey, SchemeLattice#Value)] + override val encoder = getValueEncoder override protected def writeEncapsulated(writer: Writer, hMapPair: (HMapKey, SchemeLattice#Value)): Writer = val (key, value) = hMapPair def writeValue[T: Encoder](value: T) = writer.writeMember("value", value) @@ -54,18 +54,18 @@ trait SaveModularSchemeLattices extends SaveModularDomain with SaveAddr[SchemeEx writer } - override def encodeHMapPair(writer: Writer, key: HMapKey, value: Any)(using encoder: AbstractEncoder[_]): Writer = + override def encodeHMapPair(writer: Writer, key: HMapKey, value: Any)(using encoder: AbstractEncoder): Writer = if value.isInstanceOf[SchemeLattice#Value] then writer.writeMember((key, value.asInstanceOf[SchemeLattice#Value])) else return super.encodeHMapPair(writer, key, value) trait SaveModularDomain extends SaveValue[SchemeExp] with ModularSchemeDomain: - def hMapEncoder[T]: AbstractEncoder[T] = new ArrayEncoder[T] - def encodeHMapPair(writer: Writer, key: HMapKey, value: Any)(using encoder: AbstractEncoder[_]): Writer = + def getHMapEncoder: AbstractEncoder = new ArrayEncoder + def encodeHMapPair(writer: Writer, key: HMapKey, value: Any)(using encoder: AbstractEncoder): Writer = System.err.nn.println("The lattice with type `" + key.getClass + "` could not be encoded") writer override given valueEncoder: EncapsulatedEncoder[HMap] with - override val encoder = hMapEncoder[HMap] + override val encoder = getHMapEncoder override def writeEncapsulated(writer: Writer, hmap: HMap): Writer = hmap.contents.foreach((key, value) => encodeHMapPair(writer, key, value)) writer diff --git a/code/shared/src/main/scala/maf/save/Util.scala b/code/shared/src/main/scala/maf/save/Util.scala index 790528724..5e5f1bb8b 100644 --- a/code/shared/src/main/scala/maf/save/Util.scala +++ b/code/shared/src/main/scala/maf/save/Util.scala @@ -6,7 +6,7 @@ import EncapsulatedEncoder.* trait SaveMapToArray: given mapKeyEncoder[K, V](using keyEncoder: Encoder[K], valueEncoder: Encoder[V]): EncapsulatedEncoder[Map[K, V]] with - override val encoder = new ArrayKeyEncoder[Map[K, V]] + override val encoder = new ArrayKeyEncoder override def writeEncapsulated(writer: Writer, map: Map[K, V]): Writer = for (key, value) <- map do writer.writeMember(key, value) writer From 3a9622658a731d92976ad94023d41a03317d2611 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Tue, 6 Feb 2024 16:05:52 +0100 Subject: [PATCH 16/97] feat(load): Add decoder and load name of analysis --- .../main/scala/maf/cli/runnables/Repl.scala | 2 +- .../main/scala/maf/modular/ModAnalysis.scala | 13 ++- .../scheme/modf/SchemeModFSemantics.scala | 4 +- .../src/main/scala/maf/save/Analysis.scala | 24 +++++- .../main/scala/maf/save/Encapsulated.scala | 82 +++++++++++++++++++ 5 files changed, 118 insertions(+), 7 deletions(-) diff --git a/code/jvm/src/main/scala/maf/cli/runnables/Repl.scala b/code/jvm/src/main/scala/maf/cli/runnables/Repl.scala index 38914458f..3e605676f 100644 --- a/code/jvm/src/main/scala/maf/cli/runnables/Repl.scala +++ b/code/jvm/src/main/scala/maf/cli/runnables/Repl.scala @@ -184,7 +184,7 @@ object Repl: if !anl.finished then println("Analysis timed out") anl.printResult println(s"Analysis took ${elapsed / (1000 * 1000)} ms") - anl.save("res.json") + anl.load("res.json") // Print a dot graph if the dot option has been enabled if dot then anl.toDot(filename.replace("/", "_").nn + ".dot") elapsed diff --git a/code/shared/src/main/scala/maf/modular/ModAnalysis.scala b/code/shared/src/main/scala/maf/modular/ModAnalysis.scala index 649929160..ad477359e 100644 --- a/code/shared/src/main/scala/maf/modular/ModAnalysis.scala +++ b/code/shared/src/main/scala/maf/modular/ModAnalysis.scala @@ -46,13 +46,18 @@ trait AnalysisEntry[Exp <: Expression]: /** * This saves the current analysis to a file * - * @note - * This method is not currently implemented and should be overridden, this method so the save implementation can gradually be added - * * @param filename * The file to save to */ - def save(filename: String): Unit = System.err.nn.println("Save functionallity is not implemented for this analysis") + def save(filename: String): Unit = System.err.nn.println("Save functionality is not implemented for this analysis") + + /** + * Load an analysis from a given file + * + * @param filename + * The file to load the analysis from + */ + def load(filename: String): Unit = System.err.nn.println("Load functionality is not implemented for this analysis") /** * Method that renders a Dot graph of the components and the dependencies between them and writes it to a file diff --git a/code/shared/src/main/scala/maf/modular/scheme/modf/SchemeModFSemantics.scala b/code/shared/src/main/scala/maf/modular/scheme/modf/SchemeModFSemantics.scala index e048d1205..c59be1ce2 100644 --- a/code/shared/src/main/scala/maf/modular/scheme/modf/SchemeModFSemantics.scala +++ b/code/shared/src/main/scala/maf/modular/scheme/modf/SchemeModFSemantics.scala @@ -16,6 +16,7 @@ import maf.core.Monad.MonadIterableOps import maf.core.Monad.MonadSyntaxOps import maf.save.SaveModF import maf.save.Savable +import maf.save.LoadModF trait BaseEvalM[M[_]] extends Monad[M] with MonadError[M, Error] with MonadJoin[M] @@ -416,5 +417,6 @@ abstract class SimpleSchemeModFAnalysis(prg: SchemeExp, override val name: Optio with StandardSchemeModFComponents with SchemeModFSemanticsM with BigStepModFSemantics - with SaveModF: + with SaveModF + with LoadModF: override def intraAnalysis(cmp: Component) = new IntraAnalysis(cmp) with BigStepModFIntra diff --git a/code/shared/src/main/scala/maf/save/Analysis.scala b/code/shared/src/main/scala/maf/save/Analysis.scala index 3056ba188..b588e2260 100644 --- a/code/shared/src/main/scala/maf/save/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/Analysis.scala @@ -9,8 +9,28 @@ import java.nio.file.Paths import java.nio.file.Files import maf.language.scheme.SchemeExp import EncapsulatedEncoder.* +import io.bullet.borer.Reader +import maf.save.EncapsulatedDecoder.* -class Savable[T](val value: T)(using val encoder: Encoder[T]) +case class Savable[T](val value: T)(using val encoder: Encoder[T]) +case class Loadable[T](val load: (T) => Unit)(using val decoder: Decoder[T]) + +trait Load[Expr <: Expression] extends ModAnalysis[Expr]: + def getDecoder: AbstractDecoder = new MapDecoder + + given EncapsulatedDecoder[Load[Expr]] with + override val decoder: AbstractDecoder = Load.this.getDecoder + override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Load[Expr] = + for (key, value) <- loadInfo do reader.readMember(key)(using value.decoder, decoder) + for (key, value) <- loadInfo do value.load(reader.getMember(key)) + return Load.this + + override def load(filename: String): Unit = + val bytes = Files.readAllBytes(Paths.get(filename)) + if bytes != null then Json.decode(bytes).to[Load[Expr]].value + + def loadInfo: Map[String, Loadable[_]] = + Map("name" -> Loadable((name: String) => println(name))) trait Save[Expr <: Expression] extends ModAnalysis[Expr]: def getEncoder: AbstractEncoder @@ -43,3 +63,5 @@ trait SaveModF with SaveModularSchemeLattices with SaveNoContext[SchemeExp]: override def getEncoder: AbstractEncoder = new MapEncoder + +trait LoadModF extends Load[SchemeExp] diff --git a/code/shared/src/main/scala/maf/save/Encapsulated.scala b/code/shared/src/main/scala/maf/save/Encapsulated.scala index e08506501..831a3ef5e 100644 --- a/code/shared/src/main/scala/maf/save/Encapsulated.scala +++ b/code/shared/src/main/scala/maf/save/Encapsulated.scala @@ -4,6 +4,9 @@ import io.bullet.borer.Encoder import io.bullet.borer.Writer import io.bullet.borer.derivation.MapBasedCodecs import io.bullet.borer.derivation.ArrayBasedCodecs +import io.bullet.borer.Decoder +import io.bullet.borer.Reader +import scala.collection.mutable.HashMap trait AbstractEncoder: val mapBasedEncoder: Boolean @@ -81,3 +84,82 @@ class ArrayKeyEncoder extends ArrayEncoder: writeKey(writer, id.toString()) override def writeKey[T: Encoder](writer: Writer, key: T): Writer = openEncapsulation(writer, 2).write(key) override def writeValue[T: Encoder](writer: Writer, value: T): Writer = closeEncapsulation(writer.write(value)) + +trait AbstractDecoder: + val mapBasedDecoder: Boolean + val writeOpenKey = true + def readKey(reader: Reader): Unit + def readKey(reader: Reader, key: String): Unit + def readValue[T: Decoder](reader: Reader): () => T + def getValue[T](key: String): T + def openEncapsulation(reader: Reader): Unit + def openEncapsulation(reader: Reader, amount: Int): Unit + def closeEncapsulation[T](reader: Reader, unbounded: Boolean, res: T): Unit + +object AbstractDecoder: + inline def deriveDecoder[T](decoder: AbstractDecoder): Decoder[T] = + if decoder.mapBasedDecoder then MapBasedCodecs.deriveDecoder[T] else ArrayBasedCodecs.deriveDecoder[T] + inline def deriveAllDecoders[T](decoder: AbstractDecoder): Decoder[T] = + if decoder.mapBasedDecoder then MapBasedCodecs.deriveAllDecoders[T] else ArrayBasedCodecs.deriveAllDecoders[T] + +trait EncapsulatedDecoder[T] extends Decoder[T]: + def decoder: AbstractDecoder + protected given AbstractDecoder = decoder + override def read(reader: Reader): T = + decoder.openEncapsulation(reader) + val res = readEncapsulated(reader)(using decoder) + decoder.closeEncapsulation(reader, true, res) + res + protected def readEncapsulated(reader: Reader)(using AbstractDecoder): T + +object EncapsulatedDecoder: + extension (reader: Reader) + def readMember[T: Decoder](key: String)(using decoder: AbstractDecoder): () => T = + decoder.readKey(reader, key) + decoder.readValue[T](reader) + def getMember[T](key: String)(using decoder: AbstractDecoder): T = decoder.getValue[T](key) + +class MapDecoder extends AbstractDecoder: + protected val values = new HashMap[String, Any]() + protected val keys = new HashMap[String, Decoder[_]]() + protected var decodeKey: Option[String] = None + protected var currentKey: Option[String] = None + + override val mapBasedDecoder: Boolean = true + protected def readDecodeKey(reader: Reader): Unit = + if decodeKey.isDefined then return + decodeKey = Some(reader.readString()) + override def readKey(reader: Reader): Unit = + readDecodeKey(reader) + this.currentKey = decodeKey + override def readKey(reader: Reader, key: String): Unit = + readDecodeKey(reader) + this.currentKey = Some(key) + override def readValue[T: Decoder](reader: Reader): () => T = + if decodeKey.isEmpty then throw new KeyException("Trying to read a value before reading a key.") + + if decodeKey == currentKey then + val res = reader.read[T]() + values.addOne((currentKey.get, res)) + if reader.hasString then decodeKey = Some(reader.readString()) + if decodeKey.isDefined && keys.contains(decodeKey.get) then + currentKey = decodeKey + val decoder = keys.remove(decodeKey.get).get + readValue(reader)(using decoder) + currentKey = None + return () => res + else + keys.addOne((currentKey.get, summon[Decoder[T]])) + val key = currentKey.get + currentKey = None + return () => + if !values.contains(key) then throw new KeyException("Key '" + key + "' is not found.") + values.get(key).get.asInstanceOf[T] + override def getValue[T](key: String): T = + if !values.contains(key) then throw new KeyException("Key '" + key + "' is not found.") + values.get(key).get.asInstanceOf[T] + override def openEncapsulation(reader: Reader): Unit = reader.readMapStart() + override def openEncapsulation(reader: Reader, amount: Int): Unit = reader.readMapOpen(amount) + override def closeEncapsulation[T](reader: Reader, unbounded: Boolean, res: T): Unit = reader.readMapClose(unbounded, res) + + case class KeyException(msg: String) extends Exception(msg) From 513422e761dfdd98647d77d009703b2cdf6a4aad Mon Sep 17 00:00:00 2001 From: Merlijn Date: Tue, 6 Feb 2024 16:27:18 +0100 Subject: [PATCH 17/97] feat(repl): Add `-s` and `-l` as CLI arguments `-s` is used to select the file you want to save your analysis to `-l` is used to select the file you want to load your analysis from --- .../main/scala/maf/cli/runnables/Repl.scala | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/code/jvm/src/main/scala/maf/cli/runnables/Repl.scala b/code/jvm/src/main/scala/maf/cli/runnables/Repl.scala index 3e605676f..5d963d9c7 100644 --- a/code/jvm/src/main/scala/maf/cli/runnables/Repl.scala +++ b/code/jvm/src/main/scala/maf/cli/runnables/Repl.scala @@ -32,6 +32,8 @@ object Repl: | * -t TIMEOUT: run the analysis with the given timeout (in seconds). Defaults to 10. | * -dot set flag to enable outputting a FILENAME.dot file that contains a visualisation of results of the analysis, only works with "-f" | * -m load the given Racket module, use instead of "-f" + | * -s FILENAME: the name of the file where the analysis should be saved to when finished + | * -l FILENAME: the name of the file where the analysis should be loaded from """.stripMargin private val configurationsHelp: Map[String, String] = Map( @@ -79,7 +81,9 @@ object Repl: interactive: Boolean = false, performance: Boolean = false, dot: Boolean = false, - timeout: Long = 10): + timeout: Long = 10, + saveFile: Option[String] = None, + loadFile: Option[String] = None): def isEmpty: Boolean = remaining.isEmpty def continue(remaining: List[String]): ArgParser = this.copy(remaining = remaining) def setAnalysis(analysis: String): ArgParser = @@ -90,6 +94,14 @@ object Repl: ensureNotSet(this.filename, "filename") this.copy(filename = Some(filename)) + def setSaveFilename(filename: String): ArgParser = + ensureNotSet(this.saveFile, "saveFile") + this.copy(saveFile = Some(filename)) + + def setLoadFilename(filename: String): ArgParser = + ensureNotSet(this.loadFile, "loadFile") + this.copy(loadFile = Some(filename)) + def setParser(parser: String): ArgParser = ensureNotSet(this.parser, "parser") this.copy(parser = Some(parser)) @@ -132,6 +144,12 @@ object Repl: case "-dot" :: rest => parse(parser.copy(dot = true).continue(rest)) + case "-s" :: filename :: rest => + parse(parser.setSaveFilename(filename).continue(rest)) + + case "-l" :: filename :: rest => + parse(parser.setLoadFilename(filename).continue(rest)) + case arg => throw new Exception(s"invalid arguments $arg") @@ -171,6 +189,8 @@ object Repl: performance: Boolean, timeout: Long, dot: Boolean, + saveFile: Option[String] = None, + loadFile: Option[String] = None, someLoader: Option[String => SchemeExp] = None ): Unit = val loader: String => SchemeExp = someLoader.getOrElse(Reader.loadFile andThen parser.parse) @@ -178,13 +198,14 @@ object Repl: val exp = loader(filename) def runSingle(): Long = val anl = makeAnalysis(exp) + if loadFile.isDefined then anl.load(loadFile.get) val (elapsed, _) = Timer.time { anl.analyze() } //anl.analyzeWithTimeout(Timeout.start(timeout.seconds)) } // Do not print results if we are in perfomance testing mode if !performance then if !anl.finished then println("Analysis timed out") anl.printResult println(s"Analysis took ${elapsed / (1000 * 1000)} ms") - anl.load("res.json") + if saveFile.isDefined then anl.save(saveFile.get) // Print a dot graph if the dot option has been enabled if dot then anl.toDot(filename.replace("/", "_").nn + ".dot") elapsed @@ -222,7 +243,7 @@ object Repl: else runSingle() /** Runs a REPL that can be used to interactively test the abstract interpreter */ - private def runRepl(parser: P, makeAnalysis: A): Unit = + private def runRepl(parser: P, makeAnalysis: A, saveFile: Option[String]): Unit = def repl(): Unit = print(">") val program = readLine().trim().nn @@ -233,6 +254,7 @@ object Repl: anl.printResult println(s"Analysis took ${elapsed / (1000 * 1000)} ms") + if saveFile.isDefined then anl.save(saveFile.get) repl() repl() @@ -252,6 +274,8 @@ object Repl: assert(if options.dot then options.filename.isDefined else true, "-dot can only be combined with -f") // ensure that "-m" is not combined with "-f" assert(if options.module.isDefined then !options.filename.isDefined else true, "-m can not be combined with -f") + // ensure that "-i" is not combined with "-l" + assert(!(options.interactive && options.loadFile.isDefined), "-i can not be combined with -l") // setup the parser val parser = setupParser(options.parser) // setup the loader @@ -262,8 +286,8 @@ object Repl: val analysisFactory = setupAnalysis(options.analysis.get) // setup the loader of the file/module // either run the file or the repl - if options.interactive then runRepl(parser, analysisFactory) + if options.interactive then runRepl(parser, analysisFactory, options.saveFile) else // retrieve the file or module name val path = options.filename.getOrElse(options.module.get) - runFile(path, parser, analysisFactory, options.performance, options.timeout, options.dot, loader) + runFile(path, parser, analysisFactory, options.performance, options.timeout, options.dot, options.saveFile, options.loadFile, loader) From cea7b0bc4bfbf2f6b7476499f008cf02a14a9ad7 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Tue, 6 Feb 2024 18:42:53 +0100 Subject: [PATCH 18/97] fix(save): Scheme address is not saving everything --- .../shared/src/main/scala/maf/save/Component.scala | 14 ++++++++------ .../src/main/scala/maf/save/Dependency.scala | 11 ++++++++++- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/Component.scala b/code/shared/src/main/scala/maf/save/Component.scala index 20b86f629..a51977be7 100644 --- a/code/shared/src/main/scala/maf/save/Component.scala +++ b/code/shared/src/main/scala/maf/save/Component.scala @@ -42,6 +42,14 @@ trait SavePosition[Expr <: Expression] extends Save[Expr]: if !pos.tag.show.isEmpty() then writer.writeMember("tag", pos.tag.show) writer + val posEncoder = getPositionEncoder + given Encoder[Identifier] = AbstractEncoder.deriveEncoder[Identifier](posEncoder) + given Encoder[Identity] = AbstractEncoder.deriveAllEncoders[Identity](posEncoder) + given Encoder[IdentityData] with + def write(writer: Writer, value: IdentityData): Writer = + System.err.nn.println("IdentityData could not be encoded") + writer + trait SaveComponents[Expr <: Expression] extends Save[Expr]: def getComponentEncoder: AbstractEncoder = getEncoder override def saveInfo: Map[String, Savable[_]] = super.saveInfo + ("components" -> Savable(visited)) @@ -122,12 +130,6 @@ trait SaveStandardSchemeComponents given Encoder[SchemeLambda] = AbstractEncoder.deriveEncoder[SchemeLambda](compEncoder) given Encoder[SchemeVarArgLambda] = AbstractEncoder.deriveEncoder[SchemeVarArgLambda](compEncoder) given Encoder[SchemeLambdaExp] = AbstractEncoder.deriveEncoder[SchemeLambdaExp](compEncoder) - given Encoder[Identifier] = AbstractEncoder.deriveEncoder[Identifier](compEncoder) - given Encoder[Identity] = AbstractEncoder.deriveAllEncoders[Identity](compEncoder) - given Encoder[IdentityData] with - def write(writer: Writer, value: IdentityData): Writer = - System.err.nn.println("IdentityData could not be encoded") - writer override given componentEncoder: Encoder[Component] with def write(writer: Writer, component: Component): Writer = diff --git a/code/shared/src/main/scala/maf/save/Dependency.scala b/code/shared/src/main/scala/maf/save/Dependency.scala index f7e660ea5..77885cf1e 100644 --- a/code/shared/src/main/scala/maf/save/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/Dependency.scala @@ -12,6 +12,7 @@ import io.bullet.borer.Encoder import maf.modular.scheme.PrmAddr import maf.modular.scheme.PtrAddr import EncapsulatedEncoder.* +import maf.language.scheme.SchemeValue trait SaveAddrDep extends SaveDependency with SavePosition[SchemeExp] with SaveSchemeAddr: override def encodeDependency(writer: Writer, dependency: Dependency): Writer = @@ -43,20 +44,28 @@ trait SaveAddr[Expr <: Expression] extends Save[Expr] with SavePosition[Expr]: writer.writeMember("position", value.idn.pos) encodeAddress(writer, value) -trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveStandardSchemeComponentID: +trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveStandardSchemeComponentID with SaveContext[SchemeExp]: + given Encoder[SchemeValue] = AbstractEncoder.deriveEncoder(getAddressEncoder) + given Encoder[maf.language.sexp.Value] = AbstractEncoder.deriveAllEncoders(getAddressEncoder) override def encodeAddress(writer: Writer, address: Address)(using encoder: AbstractEncoder): Writer = import componentIDEncoder.given address match { case VarAddr(id, ctx) => writer.writeMember("type", "varAddr") writer.writeMember("name", id.name) + if ctx.asInstanceOf[Option[Context]].isDefined then writer.writeMember("context", ctx.asInstanceOf[Option[Context]].get) + writer case ReturnAddr(cmp, idn) => writer.writeMember("type", "returnAddr") writer.writeMember("component", cmp.asInstanceOf[Component]) + writer.writeMember("identity", idn) case PrmAddr(nam) => writer.writeMember("name", nam) writer.writeMember("type", "prmAddr") case PtrAddr(exp, ctx) => writer.writeMember("type", "ptrAddr") + writer.writeMember("expression", exp.asInstanceOf[SchemeValue]) + if ctx.asInstanceOf[Option[Context]].isDefined then writer.writeMember("context", ctx.asInstanceOf[Option[Context]].get) + writer case _ => super.encodeAddress(writer, address) } From 860f688cde566bc5a3135da9859c5dccd801553f Mon Sep 17 00:00:00 2001 From: Merlijn Date: Thu, 8 Feb 2024 09:11:21 +0100 Subject: [PATCH 19/97] feat(load): Load components --- .../src/main/scala/maf/save/Analysis.scala | 10 +- .../src/main/scala/maf/save/Component.scala | 140 +++++++++++++++--- .../src/main/scala/maf/save/Dependency.scala | 64 ++++++-- .../main/scala/maf/save/Encapsulated.scala | 57 ++++--- 4 files changed, 216 insertions(+), 55 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/Analysis.scala b/code/shared/src/main/scala/maf/save/Analysis.scala index b588e2260..ac98ced46 100644 --- a/code/shared/src/main/scala/maf/save/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/Analysis.scala @@ -16,7 +16,7 @@ case class Savable[T](val value: T)(using val encoder: Encoder[T]) case class Loadable[T](val load: (T) => Unit)(using val decoder: Decoder[T]) trait Load[Expr <: Expression] extends ModAnalysis[Expr]: - def getDecoder: AbstractDecoder = new MapDecoder + def getDecoder: AbstractDecoder given EncapsulatedDecoder[Load[Expr]] with override val decoder: AbstractDecoder = Load.this.getDecoder @@ -64,4 +64,10 @@ trait SaveModF with SaveNoContext[SchemeExp]: override def getEncoder: AbstractEncoder = new MapEncoder -trait LoadModF extends Load[SchemeExp] +trait LoadModF + extends Load[SchemeExp] + with LoadComponents[SchemeExp] + with LoadStandardSchemeComponents + with LoadNoContext[SchemeExp] + with LoadSchemeAddr: + def getDecoder: AbstractDecoder = new MapDecoder diff --git a/code/shared/src/main/scala/maf/save/Component.scala b/code/shared/src/main/scala/maf/save/Component.scala index a51977be7..712b2d07f 100644 --- a/code/shared/src/main/scala/maf/save/Component.scala +++ b/code/shared/src/main/scala/maf/save/Component.scala @@ -31,18 +31,18 @@ import maf.core.WrappedEnv import maf.core.NestedEnv import maf.modular.scv.ScvContextSensitivity import maf.modular.scheme.modf.NoContext +import io.bullet.borer.Decoder +import io.bullet.borer.Reader +import maf.save.EncapsulatedDecoder.* +import maf.core.Position +import maf.core.Position.PTag trait SavePosition[Expr <: Expression] extends Save[Expr]: def getPositionEncoder: AbstractEncoder = getEncoder - given EncapsulatedEncoder[Position] with - override val encoder = getPositionEncoder - override def writeEncapsulated(writer: Writer, pos: Position): Writer = - writer.writeMember("line", pos.line) - writer.writeMember("col", pos.line) - if !pos.tag.show.isEmpty() then writer.writeMember("tag", pos.tag.show) - writer val posEncoder = getPositionEncoder + given Encoder[Position] = AbstractEncoder.deriveAllEncoders[Position](posEncoder) + given Encoder[PTag] = AbstractEncoder.deriveAllEncoders[PTag](posEncoder) given Encoder[Identifier] = AbstractEncoder.deriveEncoder[Identifier](posEncoder) given Encoder[Identity] = AbstractEncoder.deriveAllEncoders[Identity](posEncoder) given Encoder[IdentityData] with @@ -74,11 +74,12 @@ trait SaveEnvironment[Expr <: Expression] extends Save[Expr] with SaveAddr[Expr] override protected def writeEncapsulated(writer: Writer, env: Environment[T]): Writer = env match { case BasicEnvironment(content) => - writer.writeMember("content", content.asInstanceOf[Map[String, Address]]) + writer.writeMember("BasicEnvironment", content.asInstanceOf[Map[String, Address]]) case NestedEnv(content, rst) => + writer.open("NestedEnvironment") writer.writeMember("content", content.asInstanceOf[Map[String, Address]]) if rst.isDefined then writer.writeMember("rst", rst.get.asInstanceOf[Address]) - writer + writer.close() case _ => System.err.nn.println("The environemnt with type `" + env.getClass + "` could not be encoded") writer @@ -106,20 +107,11 @@ trait SaveStandardSchemeComponents given EncapsulatedEncoder[SchemeExp] with override val encoder = getComponentEncoder def writeEncapsulated(writer: Writer, exp: SchemeExp): Writer = - val stringEncoder = summon[Encoder[String]] exp match - case funcall: SchemeFuncall => - writer.writeMember("type", "funcall") - writer.writeMember("expression", funcall) - case variable: SchemeVar => - writer.writeMember("type", "var") - writer.writeMember("expression", variable) - case lambda: SchemeLambda => - writer.writeMember("type", "lambda") - writer.writeMember("expression", lambda) - case argLambda: SchemeVarArgLambda => - writer.writeMember("type", "argLambda") - writer.writeMember("expression", argLambda) + case funcall: SchemeFuncall => writer.writeMember("funcall", funcall) + case variable: SchemeVar => writer.writeMember("var", variable) + case lambda: SchemeLambda => writer.writeMember("lambda", lambda) + case argLambda: SchemeVarArgLambda => writer.writeMember("argLambda", argLambda) case _ => System.err.nn.println("The schemeexpression with type `" + exp.getClass + "` could not be encoded") writer @@ -144,3 +136,107 @@ trait SaveStandardSchemeComponents writer.writeMember("lambda", lambda) writer.writeMember("environment", env) writer.writeMember("context", context.asInstanceOf[Context]) + +trait LoadComponents[Expr <: Expression] extends Load[Expr]: + def getComponentDecoder: AbstractDecoder = getDecoder + override def loadInfo: Map[String, Loadable[_]] = + super.loadInfo + ("components" -> Loadable((visited: Set[Component]) => + visited.foreach((component) => if component != initialComponent then println(component.asInstanceOf[SchemeModFComponent.Call[_]].clo)) + )) + + given componentDecoder: Decoder[Component] + +trait LoadStandardSchemeComponents + extends LoadComponents[SchemeExp] + with StandardSchemeModFComponents + with LoadContext[SchemeExp] + with LoadPosition[SchemeExp] + with LoadEnvironment[SchemeExp]: + override given componentDecoder: Decoder[Component] with + override def read(reader: Reader): Component = + if reader.tryReadString("main") then return initialComponent + else reader.read[SchemeModFComponent.Call[Context]]() + + given EncapsulatedDecoder[SchemeModFComponent.Call[Context]] with + override val decoder = getComponentDecoder + override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): SchemeModFComponent.Call[Context] = + val lambda = reader.readMember[SchemeLambdaExp]("lambda") + val environment = reader.readMember[Environment[Address]]("environment") + val context = reader.readMember[Context]("context") + return new SchemeModFComponent.Call[Context]((lambda.value.get.get, environment.value.get.get), context.value.get.get) + private val compDecoder = getComponentDecoder + given Decoder[SchemeFuncall] = AbstractDecoder.deriveDecoder[SchemeFuncall](compDecoder) + given Decoder[SchemeVar] = AbstractDecoder.deriveDecoder[SchemeVar](compDecoder) + given Decoder[SchemeLambda] = AbstractDecoder.deriveDecoder[SchemeLambda](compDecoder) + given Decoder[SchemeVarArgLambda] = AbstractDecoder.deriveDecoder[SchemeVarArgLambda](compDecoder) + given Decoder[SchemeLambdaExp] = AbstractDecoder.deriveDecoder[SchemeLambdaExp](compDecoder) + + given EncapsulatedDecoder[SchemeExp] with + override val decoder = getComponentDecoder + override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): SchemeExp = + val expression = reader.readMembers[SchemeExp]( + Array( + ("funcall", summon[Decoder[SchemeFuncall]]), + ("var", summon[Decoder[SchemeVar]]), + ("lambda", summon[Decoder[SchemeLambda]]), + ("argLambda", summon[Decoder[SchemeVarArgLambda]]) + ) + ) + expression.value.get.get + +trait LoadContext[Expr <: Expression] extends Load[Expr]: + type Context + def getContextDecoder: AbstractDecoder = getDecoder + given contextDecoder: Decoder[Context] + +trait LoadNoContext[Expr <: Expression] extends LoadContext[Expr]: + type Context = NoContext.type + override given contextDecoder: Decoder[Context] with + override def read(reader: Reader): Context = + if !reader.tryReadString("ε") then return reader.unexpectedDataItem("ε") + NoContext + +trait LoadPosition[Expr <: Expression] extends Load[Expr]: + def getPositionDecoder: AbstractDecoder = getDecoder + given Decoder[Position] = AbstractDecoder.deriveAllDecoders[Position](getPositionDecoder) + given Decoder[PTag] = AbstractDecoder.deriveAllDecoders[PTag](getPositionDecoder) + + val posDecoder = getPositionDecoder + given Decoder[Identifier] = AbstractDecoder.deriveDecoder[Identifier](posDecoder) + given Decoder[Identity] = AbstractDecoder.deriveAllDecoders[Identity](posDecoder) + given Decoder[IdentityData] with + private object IdnData extends IdentityData { + // TODO: + def canEqual(that: Any): Boolean = ??? + def productArity: Int = ??? + def productElement(n: Int): Any = ??? + } + override def read(reader: Reader): IdentityData = + System.err.nn.println("IdentityData could not be decoded") + return IdnData + +trait LoadEnvironment[Expr <: Expression] extends Load[Expr] with LoadAddr[Expr]: + def getEnvironmentDecoder: AbstractDecoder = getDecoder + given [T <: Address]: EncapsulatedDecoder[Environment[T]] with + override def decoder: AbstractDecoder = getEnvironmentDecoder + override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Environment[T] = + return reader + .readMembers[Environment[T]]( + Array(("BasicEnvironment", summon[Decoder[BasicEnvironment[T]]]), ("NestedEnv", summon[Decoder[NestedEnv[T, T]]])) + ) + .value + .get + .get + + given [T <: Address]: Decoder[BasicEnvironment[T]] with + override def read(reader: Reader): BasicEnvironment[T] = return new BasicEnvironment( + reader.read[Map[String, Address]]().asInstanceOf[Map[String, T]] + ) + given [T <: Address, K <: Address]: EncapsulatedDecoder[NestedEnv[T, K]] with + override def decoder: AbstractDecoder = getEnvironmentDecoder + override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): NestedEnv[T, K] = + val content = reader.readMember[Map[String, Address]]("content") + val rst = reader.readMember[Address]("rst") + return new NestedEnv(content.value.get.get.asInstanceOf[Map[String, T]], + if rst.isCompleted then Some(rst.value.get.get.asInstanceOf[K]) else None + ) diff --git a/code/shared/src/main/scala/maf/save/Dependency.scala b/code/shared/src/main/scala/maf/save/Dependency.scala index 77885cf1e..9e6c5ab78 100644 --- a/code/shared/src/main/scala/maf/save/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/Dependency.scala @@ -13,6 +13,11 @@ import maf.modular.scheme.PrmAddr import maf.modular.scheme.PtrAddr import EncapsulatedEncoder.* import maf.language.scheme.SchemeValue +import io.bullet.borer.Reader +import io.bullet.borer.Decoder +import maf.save.EncapsulatedDecoder.* +import maf.core.Identifier +import maf.util.Writer.write trait SaveAddrDep extends SaveDependency with SavePosition[SchemeExp] with SaveSchemeAddr: override def encodeDependency(writer: Writer, dependency: Dependency): Writer = @@ -40,9 +45,7 @@ trait SaveAddr[Expr <: Expression] extends Save[Expr] with SavePosition[Expr]: given EncapsulatedEncoder[Address] with override val encoder = getAddressEncoder - override def writeEncapsulated(writer: Writer, value: Address): Writer = - writer.writeMember("position", value.idn.pos) - encodeAddress(writer, value) + override def writeEncapsulated(writer: Writer, value: Address): Writer = encodeAddress(writer, value) trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveStandardSchemeComponentID with SaveContext[SchemeExp]: given Encoder[SchemeValue] = AbstractEncoder.deriveEncoder(getAddressEncoder) @@ -51,21 +54,60 @@ trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveStandardSchemeComponen import componentIDEncoder.given address match { case VarAddr(id, ctx) => - writer.writeMember("type", "varAddr") - writer.writeMember("name", id.name) + writer.open("varAddr") + writer.writeMember("id", id) if ctx.asInstanceOf[Option[Context]].isDefined then writer.writeMember("context", ctx.asInstanceOf[Option[Context]].get) - writer + writer.close() case ReturnAddr(cmp, idn) => - writer.writeMember("type", "returnAddr") + writer.open("returnAddr") writer.writeMember("component", cmp.asInstanceOf[Component]) writer.writeMember("identity", idn) + writer.close() case PrmAddr(nam) => - writer.writeMember("name", nam) - writer.writeMember("type", "prmAddr") + writer.writeMember("prmAddr", nam) case PtrAddr(exp, ctx) => - writer.writeMember("type", "ptrAddr") + writer.open("ptrAddr") writer.writeMember("expression", exp.asInstanceOf[SchemeValue]) if ctx.asInstanceOf[Option[Context]].isDefined then writer.writeMember("context", ctx.asInstanceOf[Option[Context]].get) - writer + writer.close() case _ => super.encodeAddress(writer, address) } + +trait LoadAddr[Expr <: Expression] extends Load[Expr] with LoadPosition[Expr]: + def getAddressDecoder: AbstractDecoder = getDecoder + def addressDecoders = List[(String, Decoder[_ <: Address])]() + + given EncapsulatedDecoder[Address] with + override def decoder: AbstractDecoder = getAddressDecoder + override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Address = + reader.readMembers(addressDecoders.toArray).value.get.get + +trait LoadSchemeAddr extends LoadAddr[SchemeExp] with LoadContext[SchemeExp]: + given Decoder[SchemeValue] = AbstractDecoder.deriveDecoder(getAddressDecoder) + given Decoder[maf.language.sexp.Value] = AbstractDecoder.deriveAllDecoders(getAddressDecoder) + override def addressDecoders = + super.addressDecoders ++ List(("varAddr", summon[Decoder[VarAddr[Context]]]), + ("prmAddr", summon[Decoder[PrmAddr]]), + ("ptrAddr", summon[Decoder[PtrAddr[Context]]]) + ) + + given EncapsulatedDecoder[VarAddr[Context]] with + override def decoder: AbstractDecoder = getAddressDecoder + override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): VarAddr[Context] = + val name = reader.readMember[Identifier]("id") + val context = reader.readMember[Context]("context") + return new VarAddr[Context](name.value.get.get, + if context.isCompleted then Some(context.value.get.get).asInstanceOf[Context] else None.asInstanceOf[Context] + ) + + given Decoder[PrmAddr] with + override def read(reader: Reader): PrmAddr = new PrmAddr(reader.read[String]()) + + given EncapsulatedDecoder[PtrAddr[Context]] with + override def decoder: AbstractDecoder = getAddressDecoder + override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): PtrAddr[Context] = + val expression = reader.readMember[SchemeValue]("expression") + val context = reader.readMember[Context]("context") + return new PtrAddr[Context](expression.value.get.get, + if context.isCompleted then Some(context.value.get.get).asInstanceOf[Context] else None.asInstanceOf[Context] + ) diff --git a/code/shared/src/main/scala/maf/save/Encapsulated.scala b/code/shared/src/main/scala/maf/save/Encapsulated.scala index 831a3ef5e..28172040c 100644 --- a/code/shared/src/main/scala/maf/save/Encapsulated.scala +++ b/code/shared/src/main/scala/maf/save/Encapsulated.scala @@ -7,6 +7,11 @@ import io.bullet.borer.derivation.ArrayBasedCodecs import io.bullet.borer.Decoder import io.bullet.borer.Reader import scala.collection.mutable.HashMap +import io.bullet.borer.derivation.CompactMapBasedCodecs +import scala.concurrent.Future +import scala.concurrent.Promise +import scala.util.{Failure, Success} +import scala.concurrent.ExecutionContext.Implicits.global trait AbstractEncoder: val mapBasedEncoder: Boolean @@ -20,9 +25,9 @@ trait AbstractEncoder: object AbstractEncoder: inline def deriveEncoder[T](encoder: AbstractEncoder): Encoder[T] = - if encoder.mapBasedEncoder then MapBasedCodecs.deriveEncoder[T] else ArrayBasedCodecs.deriveEncoder[T] + if encoder.mapBasedEncoder then CompactMapBasedCodecs.deriveEncoder[T] else ArrayBasedCodecs.deriveEncoder[T] inline def deriveAllEncoders[T](encoder: AbstractEncoder): Encoder[T] = - if encoder.mapBasedEncoder then MapBasedCodecs.deriveAllEncoders[T] else ArrayBasedCodecs.deriveAllEncoders[T] + if encoder.mapBasedEncoder then CompactMapBasedCodecs.deriveAllEncoders[T] else ArrayBasedCodecs.deriveAllEncoders[T] trait EncapsulatedEncoder[T] extends Encoder[T]: val encoder: AbstractEncoder @@ -90,7 +95,7 @@ trait AbstractDecoder: val writeOpenKey = true def readKey(reader: Reader): Unit def readKey(reader: Reader, key: String): Unit - def readValue[T: Decoder](reader: Reader): () => T + def readValue[T: Decoder](reader: Reader): Future[T] def getValue[T](key: String): T def openEncapsulation(reader: Reader): Unit def openEncapsulation(reader: Reader, amount: Int): Unit @@ -98,9 +103,9 @@ trait AbstractDecoder: object AbstractDecoder: inline def deriveDecoder[T](decoder: AbstractDecoder): Decoder[T] = - if decoder.mapBasedDecoder then MapBasedCodecs.deriveDecoder[T] else ArrayBasedCodecs.deriveDecoder[T] + if decoder.mapBasedDecoder then CompactMapBasedCodecs.deriveDecoder[T] else ArrayBasedCodecs.deriveDecoder[T] inline def deriveAllDecoders[T](decoder: AbstractDecoder): Decoder[T] = - if decoder.mapBasedDecoder then MapBasedCodecs.deriveAllDecoders[T] else ArrayBasedCodecs.deriveAllDecoders[T] + if decoder.mapBasedDecoder then CompactMapBasedCodecs.deriveAllDecoders[T] else ArrayBasedCodecs.deriveAllDecoders[T] trait EncapsulatedDecoder[T] extends Decoder[T]: def decoder: AbstractDecoder @@ -114,9 +119,23 @@ trait EncapsulatedDecoder[T] extends Decoder[T]: object EncapsulatedDecoder: extension (reader: Reader) - def readMember[T: Decoder](key: String)(using decoder: AbstractDecoder): () => T = + def readMember[T: Decoder](key: String)(using decoder: AbstractDecoder): Future[T] = decoder.readKey(reader, key) decoder.readValue[T](reader) + def readMembers[T](keys: Array[(String, Decoder[_ <: T])])(using decoder: AbstractDecoder): Future[T] = + val promise = Promise[T]() + for (key, valueDecoder) <- keys do + decoder.readKey(reader, key) + val value = decoder.readValue(reader)(using valueDecoder) + if value.isCompleted then + promise.complete(value.value.get) + return promise.future + value.onComplete { + case Success(value) => promise.success(value.asInstanceOf[T]) + case Failure(e) => promise.failure(e.asInstanceOf[Throwable]) + } + return promise.future + def getMember[T](key: String)(using decoder: AbstractDecoder): T = decoder.getValue[T](key) class MapDecoder extends AbstractDecoder: @@ -127,34 +146,32 @@ class MapDecoder extends AbstractDecoder: override val mapBasedDecoder: Boolean = true protected def readDecodeKey(reader: Reader): Unit = - if decodeKey.isDefined then return - decodeKey = Some(reader.readString()) + if decodeKey.isEmpty && reader.hasString then decodeKey = Some(reader.readString()) override def readKey(reader: Reader): Unit = readDecodeKey(reader) this.currentKey = decodeKey override def readKey(reader: Reader, key: String): Unit = readDecodeKey(reader) this.currentKey = Some(key) - override def readValue[T: Decoder](reader: Reader): () => T = - if decodeKey.isEmpty then throw new KeyException("Trying to read a value before reading a key.") + override def readValue[T: Decoder](reader: Reader): Future[T] = + if currentKey.isEmpty then throw new KeyException("Trying to read a value before reading a key.") + val promise = Promise[T]() if decodeKey == currentKey then + val key = decodeKey.get + decodeKey = None + currentKey = None val res = reader.read[T]() - values.addOne((currentKey.get, res)) + values.addOne((key, res)) if reader.hasString then decodeKey = Some(reader.readString()) + promise.success(res) if decodeKey.isDefined && keys.contains(decodeKey.get) then currentKey = decodeKey val decoder = keys.remove(decodeKey.get).get readValue(reader)(using decoder) - currentKey = None - return () => res - else - keys.addOne((currentKey.get, summon[Decoder[T]])) - val key = currentKey.get - currentKey = None - return () => - if !values.contains(key) then throw new KeyException("Key '" + key + "' is not found.") - values.get(key).get.asInstanceOf[T] + else keys.addOne((currentKey.get, summon[Decoder[T]])) + currentKey = None + promise.future override def getValue[T](key: String): T = if !values.contains(key) then throw new KeyException("Key '" + key + "' is not found.") values.get(key).get.asInstanceOf[T] From a59ed78a5822a1531858bf7fcc5f464bed4d128e Mon Sep 17 00:00:00 2001 From: Merlijn Date: Thu, 8 Feb 2024 09:26:59 +0100 Subject: [PATCH 20/97] refactor(save): Move save logic and load logic to separate folders --- .../main/scala/maf/save/load/Analysis.scala | 38 +++++ .../scala/maf/save/{ => load}/Component.scala | 104 -------------- .../main/scala/maf/save/load/Dependency.scala | 55 +++++++ .../maf/save/{ => load}/Encapsulated.scala | 80 ----------- .../src/main/scala/maf/save/load/Store.scala | 6 + .../src/main/scala/maf/save/load/Util.scala | 6 + .../scala/maf/save/{ => save}/Analysis.scala | 30 +--- .../main/scala/maf/save/save/Component.scala | 135 ++++++++++++++++++ .../maf/save/{ => save}/Dependency.scala | 42 ------ .../scala/maf/save/save/Encapsulated.scala | 89 ++++++++++++ .../scala/maf/save/{ => save}/Store.scala | 0 .../main/scala/maf/save/{ => save}/Util.scala | 0 12 files changed, 330 insertions(+), 255 deletions(-) create mode 100644 code/shared/src/main/scala/maf/save/load/Analysis.scala rename code/shared/src/main/scala/maf/save/{ => load}/Component.scala (54%) create mode 100644 code/shared/src/main/scala/maf/save/load/Dependency.scala rename code/shared/src/main/scala/maf/save/{ => load}/Encapsulated.scala (53%) create mode 100644 code/shared/src/main/scala/maf/save/load/Store.scala create mode 100644 code/shared/src/main/scala/maf/save/load/Util.scala rename code/shared/src/main/scala/maf/save/{ => save}/Analysis.scala (53%) create mode 100644 code/shared/src/main/scala/maf/save/save/Component.scala rename code/shared/src/main/scala/maf/save/{ => save}/Dependency.scala (58%) create mode 100644 code/shared/src/main/scala/maf/save/save/Encapsulated.scala rename code/shared/src/main/scala/maf/save/{ => save}/Store.scala (100%) rename code/shared/src/main/scala/maf/save/{ => save}/Util.scala (100%) diff --git a/code/shared/src/main/scala/maf/save/load/Analysis.scala b/code/shared/src/main/scala/maf/save/load/Analysis.scala new file mode 100644 index 000000000..4d973000f --- /dev/null +++ b/code/shared/src/main/scala/maf/save/load/Analysis.scala @@ -0,0 +1,38 @@ +package maf.save + +import io.bullet.borer.Decoder +import io.bullet.borer.Json +import maf.core.Expression +import maf.modular.ModAnalysis +import java.nio.file.Paths +import java.nio.file.Files +import maf.language.scheme.SchemeExp +import io.bullet.borer.Reader +import maf.save.EncapsulatedDecoder.* + +case class Loadable[T](val load: (T) => Unit)(using val decoder: Decoder[T]) + +trait Load[Expr <: Expression] extends ModAnalysis[Expr]: + def getDecoder: AbstractDecoder + + given EncapsulatedDecoder[Load[Expr]] with + override val decoder: AbstractDecoder = Load.this.getDecoder + override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Load[Expr] = + for (key, value) <- loadInfo do reader.readMember(key)(using value.decoder, decoder) + for (key, value) <- loadInfo do value.load(reader.getMember(key)) + return Load.this + + override def load(filename: String): Unit = + val bytes = Files.readAllBytes(Paths.get(filename)) + if bytes != null then Json.decode(bytes).to[Load[Expr]].value + + def loadInfo: Map[String, Loadable[_]] = + Map("name" -> Loadable((name: String) => println(name))) + +trait LoadModF + extends Load[SchemeExp] + with LoadComponents[SchemeExp] + with LoadStandardSchemeComponents + with LoadNoContext[SchemeExp] + with LoadSchemeAddr: + def getDecoder: AbstractDecoder = new MapDecoder diff --git a/code/shared/src/main/scala/maf/save/Component.scala b/code/shared/src/main/scala/maf/save/load/Component.scala similarity index 54% rename from code/shared/src/main/scala/maf/save/Component.scala rename to code/shared/src/main/scala/maf/save/load/Component.scala index 712b2d07f..5cc243325 100644 --- a/code/shared/src/main/scala/maf/save/Component.scala +++ b/code/shared/src/main/scala/maf/save/load/Component.scala @@ -1,8 +1,5 @@ package maf.save -import io.bullet.borer.Encoder -import io.bullet.borer.Encoder.forIterableOnce -import io.bullet.borer.Writer import maf.core.Address import maf.core.Expression import maf.core.Position.Position @@ -12,7 +9,6 @@ import maf.modular.AnalysisResults import maf.modular.Dependency import maf.modular.scheme.modf.SchemeModFComponent import maf.modular.scheme.modf.StandardSchemeModFComponents -import EncapsulatedEncoder.* import io.bullet.borer.derivation.MapBasedCodecs import maf.language.scheme.SchemeLambdaExp import maf.core.Identifier @@ -37,106 +33,6 @@ import maf.save.EncapsulatedDecoder.* import maf.core.Position import maf.core.Position.PTag -trait SavePosition[Expr <: Expression] extends Save[Expr]: - def getPositionEncoder: AbstractEncoder = getEncoder - - val posEncoder = getPositionEncoder - given Encoder[Position] = AbstractEncoder.deriveAllEncoders[Position](posEncoder) - given Encoder[PTag] = AbstractEncoder.deriveAllEncoders[PTag](posEncoder) - given Encoder[Identifier] = AbstractEncoder.deriveEncoder[Identifier](posEncoder) - given Encoder[Identity] = AbstractEncoder.deriveAllEncoders[Identity](posEncoder) - given Encoder[IdentityData] with - def write(writer: Writer, value: IdentityData): Writer = - System.err.nn.println("IdentityData could not be encoded") - writer - -trait SaveComponents[Expr <: Expression] extends Save[Expr]: - def getComponentEncoder: AbstractEncoder = getEncoder - override def saveInfo: Map[String, Savable[_]] = super.saveInfo + ("components" -> Savable(visited)) - - given componentEncoder: Encoder[Component] - -trait SaveStandardSchemeComponentID extends StandardSchemeModFComponents with SavePosition[SchemeExp]: - given componentIDEncoder: Encoder[Component] with - def write(writer: Writer, component: Component): Writer = - if component.equals(initialComponent) then writer.write("main") - else writer.write(component.asInstanceOf[SchemeModFComponent.Call[ComponentContext]])(schemeComponentIDEncoder) - - given schemeComponentIDEncoder[T]: Encoder[SchemeModFComponent.Call[T]] with - def write(writer: Writer, component: SchemeModFComponent.Call[T]): Writer = - val (lambda, _) = component.clo - writer.write(lambda.idn.pos) - -trait SaveEnvironment[Expr <: Expression] extends Save[Expr] with SaveAddr[Expr]: - def getEnvironmentEncoder: AbstractEncoder = getEncoder - given [T <: Address]: EncapsulatedEncoder[Environment[T]] with - override val encoder: AbstractEncoder = getEnvironmentEncoder - override protected def writeEncapsulated(writer: Writer, env: Environment[T]): Writer = - env match { - case BasicEnvironment(content) => - writer.writeMember("BasicEnvironment", content.asInstanceOf[Map[String, Address]]) - case NestedEnv(content, rst) => - writer.open("NestedEnvironment") - writer.writeMember("content", content.asInstanceOf[Map[String, Address]]) - if rst.isDefined then writer.writeMember("rst", rst.get.asInstanceOf[Address]) - writer.close() - case _ => - System.err.nn.println("The environemnt with type `" + env.getClass + "` could not be encoded") - writer - } - -trait SaveContext[Expr <: Expression] extends Save[Expr]: - type Context - def getContextEncoder: AbstractEncoder = getEncoder - given contextEncoder: Encoder[Context] - -trait SaveNoContext[Expr <: Expression] extends SaveContext[Expr]: - type Context = NoContext.type - override given contextEncoder: Encoder[Context] with - override def write(writer: Writer, context: Context): Writer = writer.write("ε") - -trait SaveStandardSchemeComponents - extends SaveComponents[SchemeExp] - with StandardSchemeModFComponents - with AnalysisResults[SchemeExp] - with SaveValue[SchemeExp] - with SavePosition[SchemeExp] - with SaveEnvironment[SchemeExp] - with SaveContext[SchemeExp]: - - given EncapsulatedEncoder[SchemeExp] with - override val encoder = getComponentEncoder - def writeEncapsulated(writer: Writer, exp: SchemeExp): Writer = - exp match - case funcall: SchemeFuncall => writer.writeMember("funcall", funcall) - case variable: SchemeVar => writer.writeMember("var", variable) - case lambda: SchemeLambda => writer.writeMember("lambda", lambda) - case argLambda: SchemeVarArgLambda => writer.writeMember("argLambda", argLambda) - case _ => - System.err.nn.println("The schemeexpression with type `" + exp.getClass + "` could not be encoded") - writer - - private val compEncoder = getComponentEncoder - given Encoder[SchemeFuncall] = AbstractEncoder.deriveEncoder[SchemeFuncall](compEncoder) - given Encoder[SchemeVar] = AbstractEncoder.deriveEncoder[SchemeVar](compEncoder) - given Encoder[SchemeLambda] = AbstractEncoder.deriveEncoder[SchemeLambda](compEncoder) - given Encoder[SchemeVarArgLambda] = AbstractEncoder.deriveEncoder[SchemeVarArgLambda](compEncoder) - given Encoder[SchemeLambdaExp] = AbstractEncoder.deriveEncoder[SchemeLambdaExp](compEncoder) - - override given componentEncoder: Encoder[Component] with - def write(writer: Writer, component: Component): Writer = - if component.equals(initialComponent) then writer.write("main") - else writer.write(component.asInstanceOf[SchemeModFComponent.Call[ComponentContext]]) - - given [T]: EncapsulatedEncoder[SchemeModFComponent.Call[T]] with - override val encoder = getComponentEncoder - override def writeEncapsulated(writer: Writer, component: SchemeModFComponent.Call[T]): Writer = - val (lambda, env) = component.clo - val context = component.ctx - writer.writeMember("lambda", lambda) - writer.writeMember("environment", env) - writer.writeMember("context", context.asInstanceOf[Context]) - trait LoadComponents[Expr <: Expression] extends Load[Expr]: def getComponentDecoder: AbstractDecoder = getDecoder override def loadInfo: Map[String, Loadable[_]] = diff --git a/code/shared/src/main/scala/maf/save/load/Dependency.scala b/code/shared/src/main/scala/maf/save/load/Dependency.scala new file mode 100644 index 000000000..cd9ff2656 --- /dev/null +++ b/code/shared/src/main/scala/maf/save/load/Dependency.scala @@ -0,0 +1,55 @@ +package maf.save + +import maf.language.scheme.SchemeExp +import maf.modular.Dependency +import maf.modular.AddrDependency +import maf.core.Expression +import maf.core.Address +import maf.modular.scheme.VarAddr +import maf.modular.ReturnAddr +import maf.modular.scheme.PrmAddr +import maf.modular.scheme.PtrAddr +import maf.language.scheme.SchemeValue +import io.bullet.borer.Reader +import io.bullet.borer.Decoder +import maf.save.EncapsulatedDecoder.* +import maf.core.Identifier + +trait LoadAddr[Expr <: Expression] extends Load[Expr] with LoadPosition[Expr]: + def getAddressDecoder: AbstractDecoder = getDecoder + def addressDecoders = List[(String, Decoder[_ <: Address])]() + + given EncapsulatedDecoder[Address] with + override def decoder: AbstractDecoder = getAddressDecoder + override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Address = + reader.readMembers(addressDecoders.toArray).value.get.get + +trait LoadSchemeAddr extends LoadAddr[SchemeExp] with LoadContext[SchemeExp]: + given Decoder[SchemeValue] = AbstractDecoder.deriveDecoder(getAddressDecoder) + given Decoder[maf.language.sexp.Value] = AbstractDecoder.deriveAllDecoders(getAddressDecoder) + override def addressDecoders = + super.addressDecoders ++ List(("varAddr", summon[Decoder[VarAddr[Context]]]), + ("prmAddr", summon[Decoder[PrmAddr]]), + ("ptrAddr", summon[Decoder[PtrAddr[Context]]]) + ) + + given EncapsulatedDecoder[VarAddr[Context]] with + override def decoder: AbstractDecoder = getAddressDecoder + override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): VarAddr[Context] = + val name = reader.readMember[Identifier]("id") + val context = reader.readMember[Context]("context") + return new VarAddr[Context](name.value.get.get, + if context.isCompleted then Some(context.value.get.get).asInstanceOf[Context] else None.asInstanceOf[Context] + ) + + given Decoder[PrmAddr] with + override def read(reader: Reader): PrmAddr = new PrmAddr(reader.read[String]()) + + given EncapsulatedDecoder[PtrAddr[Context]] with + override def decoder: AbstractDecoder = getAddressDecoder + override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): PtrAddr[Context] = + val expression = reader.readMember[SchemeValue]("expression") + val context = reader.readMember[Context]("context") + return new PtrAddr[Context](expression.value.get.get, + if context.isCompleted then Some(context.value.get.get).asInstanceOf[Context] else None.asInstanceOf[Context] + ) diff --git a/code/shared/src/main/scala/maf/save/Encapsulated.scala b/code/shared/src/main/scala/maf/save/load/Encapsulated.scala similarity index 53% rename from code/shared/src/main/scala/maf/save/Encapsulated.scala rename to code/shared/src/main/scala/maf/save/load/Encapsulated.scala index 28172040c..cde970b4d 100644 --- a/code/shared/src/main/scala/maf/save/Encapsulated.scala +++ b/code/shared/src/main/scala/maf/save/load/Encapsulated.scala @@ -1,7 +1,5 @@ package maf.save -import io.bullet.borer.Encoder -import io.bullet.borer.Writer import io.bullet.borer.derivation.MapBasedCodecs import io.bullet.borer.derivation.ArrayBasedCodecs import io.bullet.borer.Decoder @@ -13,86 +11,8 @@ import scala.concurrent.Promise import scala.util.{Failure, Success} import scala.concurrent.ExecutionContext.Implicits.global -trait AbstractEncoder: - val mapBasedEncoder: Boolean - val writeOpenKey = true - def writeKey(writer: Writer): Writer - def writeKey[T: Encoder](writer: Writer, key: T): Writer - def writeValue[T: Encoder](writer: Writer, value: T): Writer - def openEncapsulation(writer: Writer): Writer - def openEncapsulation(writer: Writer, amount: Int): Writer - def closeEncapsulation(writer: Writer): Writer - -object AbstractEncoder: - inline def deriveEncoder[T](encoder: AbstractEncoder): Encoder[T] = - if encoder.mapBasedEncoder then CompactMapBasedCodecs.deriveEncoder[T] else ArrayBasedCodecs.deriveEncoder[T] - inline def deriveAllEncoders[T](encoder: AbstractEncoder): Encoder[T] = - if encoder.mapBasedEncoder then CompactMapBasedCodecs.deriveAllEncoders[T] else ArrayBasedCodecs.deriveAllEncoders[T] - -trait EncapsulatedEncoder[T] extends Encoder[T]: - val encoder: AbstractEncoder - protected given AbstractEncoder = encoder - override def write(writer: Writer, value: T): Writer = - encoder.openEncapsulation(writer) - writeEncapsulated(writer, value) - encoder.closeEncapsulation(writer) - protected def writeEncapsulated(writer: Writer, value: T): Writer - -object EncapsulatedEncoder: - extension (writer: Writer) - def close()(using encoder: AbstractEncoder): Writer = encoder.closeEncapsulation(writer) - def writeMember[T: Encoder, U: Encoder](key: T, value: U)(using encoder: AbstractEncoder): Writer = - encoder.writeKey(writer, key) - encoder.writeValue(writer, value) - def writeMember[T: Encoder](value: T)(using encoder: AbstractEncoder): Writer = - encoder.writeKey(writer) - encoder.writeValue(writer, value) - def open()(using encoder: AbstractEncoder): Writer = - if encoder.writeOpenKey then encoder.writeKey(writer) - encoder.openEncapsulation(writer) - def open[T: Encoder](key: T)(using encoder: AbstractEncoder): Writer = - encoder.writeKey(writer, key) - encoder.openEncapsulation(writer) - def open(amount: Int)(using encoder: AbstractEncoder): Writer = - if encoder.writeOpenKey then encoder.writeKey(writer) - encoder.openEncapsulation(writer, amount) - def open[T: Encoder](key: T, amount: Int)(using encoder: AbstractEncoder): Writer = - encoder.writeKey(writer, key) - encoder.openEncapsulation(writer) - -class MapEncoder extends AbstractEncoder: - val mapBasedEncoder = true - private var id = -1 - override def writeKey(writer: Writer): Writer = - id += 1 - writeKey(writer, id.toString()) - override def writeKey[T: Encoder](writer: Writer, key: T): Writer = writer.write(key) - override def writeValue[T: Encoder](writer: Writer, value: T): Writer = writer.write(value) - override def openEncapsulation(writer: Writer): Writer = writer.writeMapStart() - override def openEncapsulation(writer: Writer, amount: Int): Writer = writer.writeMapOpen(amount) - override def closeEncapsulation(writer: Writer): Writer = writer.writeMapClose() - -class ArrayEncoder extends AbstractEncoder: - val mapBasedEncoder = false - override def writeKey(writer: Writer): Writer = writer - override def writeKey[T: Encoder](writer: Writer, key: T): Writer = writer - override def writeValue[T: Encoder](writer: Writer, value: T): Writer = writer.write(value) - override def openEncapsulation(writer: Writer): Writer = writer.writeArrayStart() - override def openEncapsulation(writer: Writer, amount: Int): Writer = writer.writeArrayOpen(amount) - override def closeEncapsulation(writer: Writer): Writer = writer.writeArrayClose() - -class ArrayKeyEncoder extends ArrayEncoder: - private var id = -1 - override val writeOpenKey: Boolean = false - override def writeKey(writer: Writer): Writer = - id += 1 - writeKey(writer, id.toString()) - override def writeKey[T: Encoder](writer: Writer, key: T): Writer = openEncapsulation(writer, 2).write(key) - override def writeValue[T: Encoder](writer: Writer, value: T): Writer = closeEncapsulation(writer.write(value)) - trait AbstractDecoder: val mapBasedDecoder: Boolean - val writeOpenKey = true def readKey(reader: Reader): Unit def readKey(reader: Reader, key: String): Unit def readValue[T: Decoder](reader: Reader): Future[T] diff --git a/code/shared/src/main/scala/maf/save/load/Store.scala b/code/shared/src/main/scala/maf/save/load/Store.scala new file mode 100644 index 000000000..7803fc329 --- /dev/null +++ b/code/shared/src/main/scala/maf/save/load/Store.scala @@ -0,0 +1,6 @@ +package maf.save + +import maf.core.Expression +import maf.modular.AbstractDomain + +trait LoadValue[Expr <: Expression] extends Save[Expr] with AbstractDomain[Expr] diff --git a/code/shared/src/main/scala/maf/save/load/Util.scala b/code/shared/src/main/scala/maf/save/load/Util.scala new file mode 100644 index 000000000..1c372f885 --- /dev/null +++ b/code/shared/src/main/scala/maf/save/load/Util.scala @@ -0,0 +1,6 @@ +package maf.save + +import io.bullet.borer.Decoder + +trait LoadMapToArray: + given mapKeyDecoder[K, V](using keyDecoder: Decoder[K], valueDecoder: Decoder[V]): EncapsulatedDecoder[Map[K, V]] diff --git a/code/shared/src/main/scala/maf/save/Analysis.scala b/code/shared/src/main/scala/maf/save/save/Analysis.scala similarity index 53% rename from code/shared/src/main/scala/maf/save/Analysis.scala rename to code/shared/src/main/scala/maf/save/save/Analysis.scala index ac98ced46..762d5c0e4 100644 --- a/code/shared/src/main/scala/maf/save/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/save/Analysis.scala @@ -1,6 +1,6 @@ package maf.save -import io.bullet.borer.{Decoder, Encoder, Writer} +import io.bullet.borer.{Encoder, Writer} import io.bullet.borer.Json import maf.util.Writer.write import maf.core.Expression @@ -9,28 +9,8 @@ import java.nio.file.Paths import java.nio.file.Files import maf.language.scheme.SchemeExp import EncapsulatedEncoder.* -import io.bullet.borer.Reader -import maf.save.EncapsulatedDecoder.* case class Savable[T](val value: T)(using val encoder: Encoder[T]) -case class Loadable[T](val load: (T) => Unit)(using val decoder: Decoder[T]) - -trait Load[Expr <: Expression] extends ModAnalysis[Expr]: - def getDecoder: AbstractDecoder - - given EncapsulatedDecoder[Load[Expr]] with - override val decoder: AbstractDecoder = Load.this.getDecoder - override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Load[Expr] = - for (key, value) <- loadInfo do reader.readMember(key)(using value.decoder, decoder) - for (key, value) <- loadInfo do value.load(reader.getMember(key)) - return Load.this - - override def load(filename: String): Unit = - val bytes = Files.readAllBytes(Paths.get(filename)) - if bytes != null then Json.decode(bytes).to[Load[Expr]].value - - def loadInfo: Map[String, Loadable[_]] = - Map("name" -> Loadable((name: String) => println(name))) trait Save[Expr <: Expression] extends ModAnalysis[Expr]: def getEncoder: AbstractEncoder @@ -63,11 +43,3 @@ trait SaveModF with SaveModularSchemeLattices with SaveNoContext[SchemeExp]: override def getEncoder: AbstractEncoder = new MapEncoder - -trait LoadModF - extends Load[SchemeExp] - with LoadComponents[SchemeExp] - with LoadStandardSchemeComponents - with LoadNoContext[SchemeExp] - with LoadSchemeAddr: - def getDecoder: AbstractDecoder = new MapDecoder diff --git a/code/shared/src/main/scala/maf/save/save/Component.scala b/code/shared/src/main/scala/maf/save/save/Component.scala new file mode 100644 index 000000000..ace0a6e72 --- /dev/null +++ b/code/shared/src/main/scala/maf/save/save/Component.scala @@ -0,0 +1,135 @@ +package maf.save + +import io.bullet.borer.Encoder +import io.bullet.borer.Encoder.forIterableOnce +import io.bullet.borer.Writer +import maf.core.Address +import maf.core.Expression +import maf.core.Position.Position +import maf.language.scheme.SchemeExp +import maf.modular.AddrDependency +import maf.modular.AnalysisResults +import maf.modular.Dependency +import maf.modular.scheme.modf.SchemeModFComponent +import maf.modular.scheme.modf.StandardSchemeModFComponents +import EncapsulatedEncoder.* +import io.bullet.borer.derivation.MapBasedCodecs +import maf.language.scheme.SchemeLambdaExp +import maf.core.Identifier +import maf.core.Identity +import maf.core.IdentityData +import maf.language.scheme.SchemeFuncall +import maf.language.scheme.SchemeLambda +import io.bullet.borer.derivation.ArrayBasedCodecs +import maf.language.scheme.SchemeVarArgLambda +import maf.language.scheme.SchemeIf +import maf.language.scheme.SchemeLet +import maf.language.scheme.SchemeVar +import maf.core.BasicEnvironment +import maf.core.Environment +import maf.core.WrappedEnv +import maf.core.NestedEnv +import maf.modular.scv.ScvContextSensitivity +import maf.modular.scheme.modf.NoContext +import maf.core.Position +import maf.core.Position.PTag + +trait SavePosition[Expr <: Expression] extends Save[Expr]: + def getPositionEncoder: AbstractEncoder = getEncoder + + val posEncoder = getPositionEncoder + given Encoder[Position] = AbstractEncoder.deriveAllEncoders[Position](posEncoder) + given Encoder[PTag] = AbstractEncoder.deriveAllEncoders[PTag](posEncoder) + given Encoder[Identifier] = AbstractEncoder.deriveEncoder[Identifier](posEncoder) + given Encoder[Identity] = AbstractEncoder.deriveAllEncoders[Identity](posEncoder) + given Encoder[IdentityData] with + def write(writer: Writer, value: IdentityData): Writer = + System.err.nn.println("IdentityData could not be encoded") + writer + +trait SaveComponents[Expr <: Expression] extends Save[Expr]: + def getComponentEncoder: AbstractEncoder = getEncoder + override def saveInfo: Map[String, Savable[_]] = super.saveInfo + ("components" -> Savable(visited)) + + given componentEncoder: Encoder[Component] + +trait SaveStandardSchemeComponentID extends StandardSchemeModFComponents with SavePosition[SchemeExp]: + given componentIDEncoder: Encoder[Component] with + def write(writer: Writer, component: Component): Writer = + if component.equals(initialComponent) then writer.write("main") + else writer.write(component.asInstanceOf[SchemeModFComponent.Call[ComponentContext]])(schemeComponentIDEncoder) + + given schemeComponentIDEncoder[T]: Encoder[SchemeModFComponent.Call[T]] with + def write(writer: Writer, component: SchemeModFComponent.Call[T]): Writer = + val (lambda, _) = component.clo + writer.write(lambda.idn.pos) + +trait SaveEnvironment[Expr <: Expression] extends Save[Expr] with SaveAddr[Expr]: + def getEnvironmentEncoder: AbstractEncoder = getEncoder + given [T <: Address]: EncapsulatedEncoder[Environment[T]] with + override val encoder: AbstractEncoder = getEnvironmentEncoder + override protected def writeEncapsulated(writer: Writer, env: Environment[T]): Writer = + env match { + case BasicEnvironment(content) => + writer.writeMember("BasicEnvironment", content.asInstanceOf[Map[String, Address]]) + case NestedEnv(content, rst) => + writer.open("NestedEnvironment") + writer.writeMember("content", content.asInstanceOf[Map[String, Address]]) + if rst.isDefined then writer.writeMember("rst", rst.get.asInstanceOf[Address]) + writer.close() + case _ => + System.err.nn.println("The environemnt with type `" + env.getClass + "` could not be encoded") + writer + } + +trait SaveContext[Expr <: Expression] extends Save[Expr]: + type Context + def getContextEncoder: AbstractEncoder = getEncoder + given contextEncoder: Encoder[Context] + +trait SaveNoContext[Expr <: Expression] extends SaveContext[Expr]: + type Context = NoContext.type + override given contextEncoder: Encoder[Context] with + override def write(writer: Writer, context: Context): Writer = writer.write("ε") + +trait SaveStandardSchemeComponents + extends SaveComponents[SchemeExp] + with StandardSchemeModFComponents + with AnalysisResults[SchemeExp] + with SaveValue[SchemeExp] + with SavePosition[SchemeExp] + with SaveEnvironment[SchemeExp] + with SaveContext[SchemeExp]: + + given EncapsulatedEncoder[SchemeExp] with + override val encoder = getComponentEncoder + def writeEncapsulated(writer: Writer, exp: SchemeExp): Writer = + exp match + case funcall: SchemeFuncall => writer.writeMember("funcall", funcall) + case variable: SchemeVar => writer.writeMember("var", variable) + case lambda: SchemeLambda => writer.writeMember("lambda", lambda) + case argLambda: SchemeVarArgLambda => writer.writeMember("argLambda", argLambda) + case _ => + System.err.nn.println("The schemeexpression with type `" + exp.getClass + "` could not be encoded") + writer + + private val compEncoder = getComponentEncoder + given Encoder[SchemeFuncall] = AbstractEncoder.deriveEncoder[SchemeFuncall](compEncoder) + given Encoder[SchemeVar] = AbstractEncoder.deriveEncoder[SchemeVar](compEncoder) + given Encoder[SchemeLambda] = AbstractEncoder.deriveEncoder[SchemeLambda](compEncoder) + given Encoder[SchemeVarArgLambda] = AbstractEncoder.deriveEncoder[SchemeVarArgLambda](compEncoder) + given Encoder[SchemeLambdaExp] = AbstractEncoder.deriveEncoder[SchemeLambdaExp](compEncoder) + + override given componentEncoder: Encoder[Component] with + def write(writer: Writer, component: Component): Writer = + if component.equals(initialComponent) then writer.write("main") + else writer.write(component.asInstanceOf[SchemeModFComponent.Call[ComponentContext]]) + + given [T]: EncapsulatedEncoder[SchemeModFComponent.Call[T]] with + override val encoder = getComponentEncoder + override def writeEncapsulated(writer: Writer, component: SchemeModFComponent.Call[T]): Writer = + val (lambda, env) = component.clo + val context = component.ctx + writer.writeMember("lambda", lambda) + writer.writeMember("environment", env) + writer.writeMember("context", context.asInstanceOf[Context]) diff --git a/code/shared/src/main/scala/maf/save/Dependency.scala b/code/shared/src/main/scala/maf/save/save/Dependency.scala similarity index 58% rename from code/shared/src/main/scala/maf/save/Dependency.scala rename to code/shared/src/main/scala/maf/save/save/Dependency.scala index 9e6c5ab78..42a46aa0b 100644 --- a/code/shared/src/main/scala/maf/save/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/save/Dependency.scala @@ -13,9 +13,6 @@ import maf.modular.scheme.PrmAddr import maf.modular.scheme.PtrAddr import EncapsulatedEncoder.* import maf.language.scheme.SchemeValue -import io.bullet.borer.Reader -import io.bullet.borer.Decoder -import maf.save.EncapsulatedDecoder.* import maf.core.Identifier import maf.util.Writer.write @@ -72,42 +69,3 @@ trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveStandardSchemeComponen writer.close() case _ => super.encodeAddress(writer, address) } - -trait LoadAddr[Expr <: Expression] extends Load[Expr] with LoadPosition[Expr]: - def getAddressDecoder: AbstractDecoder = getDecoder - def addressDecoders = List[(String, Decoder[_ <: Address])]() - - given EncapsulatedDecoder[Address] with - override def decoder: AbstractDecoder = getAddressDecoder - override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Address = - reader.readMembers(addressDecoders.toArray).value.get.get - -trait LoadSchemeAddr extends LoadAddr[SchemeExp] with LoadContext[SchemeExp]: - given Decoder[SchemeValue] = AbstractDecoder.deriveDecoder(getAddressDecoder) - given Decoder[maf.language.sexp.Value] = AbstractDecoder.deriveAllDecoders(getAddressDecoder) - override def addressDecoders = - super.addressDecoders ++ List(("varAddr", summon[Decoder[VarAddr[Context]]]), - ("prmAddr", summon[Decoder[PrmAddr]]), - ("ptrAddr", summon[Decoder[PtrAddr[Context]]]) - ) - - given EncapsulatedDecoder[VarAddr[Context]] with - override def decoder: AbstractDecoder = getAddressDecoder - override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): VarAddr[Context] = - val name = reader.readMember[Identifier]("id") - val context = reader.readMember[Context]("context") - return new VarAddr[Context](name.value.get.get, - if context.isCompleted then Some(context.value.get.get).asInstanceOf[Context] else None.asInstanceOf[Context] - ) - - given Decoder[PrmAddr] with - override def read(reader: Reader): PrmAddr = new PrmAddr(reader.read[String]()) - - given EncapsulatedDecoder[PtrAddr[Context]] with - override def decoder: AbstractDecoder = getAddressDecoder - override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): PtrAddr[Context] = - val expression = reader.readMember[SchemeValue]("expression") - val context = reader.readMember[Context]("context") - return new PtrAddr[Context](expression.value.get.get, - if context.isCompleted then Some(context.value.get.get).asInstanceOf[Context] else None.asInstanceOf[Context] - ) diff --git a/code/shared/src/main/scala/maf/save/save/Encapsulated.scala b/code/shared/src/main/scala/maf/save/save/Encapsulated.scala new file mode 100644 index 000000000..791ad5070 --- /dev/null +++ b/code/shared/src/main/scala/maf/save/save/Encapsulated.scala @@ -0,0 +1,89 @@ +package maf.save + +import io.bullet.borer.Encoder +import io.bullet.borer.Writer +import io.bullet.borer.derivation.MapBasedCodecs +import io.bullet.borer.derivation.ArrayBasedCodecs +import scala.collection.mutable.HashMap +import io.bullet.borer.derivation.CompactMapBasedCodecs +import scala.concurrent.Future +import scala.concurrent.Promise +import scala.util.{Failure, Success} +import scala.concurrent.ExecutionContext.Implicits.global + +trait AbstractEncoder: + val mapBasedEncoder: Boolean + val writeOpenKey = true + def writeKey(writer: Writer): Writer + def writeKey[T: Encoder](writer: Writer, key: T): Writer + def writeValue[T: Encoder](writer: Writer, value: T): Writer + def openEncapsulation(writer: Writer): Writer + def openEncapsulation(writer: Writer, amount: Int): Writer + def closeEncapsulation(writer: Writer): Writer + +object AbstractEncoder: + inline def deriveEncoder[T](encoder: AbstractEncoder): Encoder[T] = + if encoder.mapBasedEncoder then CompactMapBasedCodecs.deriveEncoder[T] else ArrayBasedCodecs.deriveEncoder[T] + inline def deriveAllEncoders[T](encoder: AbstractEncoder): Encoder[T] = + if encoder.mapBasedEncoder then CompactMapBasedCodecs.deriveAllEncoders[T] else ArrayBasedCodecs.deriveAllEncoders[T] + +trait EncapsulatedEncoder[T] extends Encoder[T]: + val encoder: AbstractEncoder + protected given AbstractEncoder = encoder + override def write(writer: Writer, value: T): Writer = + encoder.openEncapsulation(writer) + writeEncapsulated(writer, value) + encoder.closeEncapsulation(writer) + protected def writeEncapsulated(writer: Writer, value: T): Writer + +object EncapsulatedEncoder: + extension (writer: Writer) + def close()(using encoder: AbstractEncoder): Writer = encoder.closeEncapsulation(writer) + def writeMember[T: Encoder, U: Encoder](key: T, value: U)(using encoder: AbstractEncoder): Writer = + encoder.writeKey(writer, key) + encoder.writeValue(writer, value) + def writeMember[T: Encoder](value: T)(using encoder: AbstractEncoder): Writer = + encoder.writeKey(writer) + encoder.writeValue(writer, value) + def open()(using encoder: AbstractEncoder): Writer = + if encoder.writeOpenKey then encoder.writeKey(writer) + encoder.openEncapsulation(writer) + def open[T: Encoder](key: T)(using encoder: AbstractEncoder): Writer = + encoder.writeKey(writer, key) + encoder.openEncapsulation(writer) + def open(amount: Int)(using encoder: AbstractEncoder): Writer = + if encoder.writeOpenKey then encoder.writeKey(writer) + encoder.openEncapsulation(writer, amount) + def open[T: Encoder](key: T, amount: Int)(using encoder: AbstractEncoder): Writer = + encoder.writeKey(writer, key) + encoder.openEncapsulation(writer) + +class MapEncoder extends AbstractEncoder: + val mapBasedEncoder = true + private var id = -1 + override def writeKey(writer: Writer): Writer = + id += 1 + writeKey(writer, id.toString()) + override def writeKey[T: Encoder](writer: Writer, key: T): Writer = writer.write(key) + override def writeValue[T: Encoder](writer: Writer, value: T): Writer = writer.write(value) + override def openEncapsulation(writer: Writer): Writer = writer.writeMapStart() + override def openEncapsulation(writer: Writer, amount: Int): Writer = writer.writeMapOpen(amount) + override def closeEncapsulation(writer: Writer): Writer = writer.writeMapClose() + +class ArrayEncoder extends AbstractEncoder: + val mapBasedEncoder = false + override def writeKey(writer: Writer): Writer = writer + override def writeKey[T: Encoder](writer: Writer, key: T): Writer = writer + override def writeValue[T: Encoder](writer: Writer, value: T): Writer = writer.write(value) + override def openEncapsulation(writer: Writer): Writer = writer.writeArrayStart() + override def openEncapsulation(writer: Writer, amount: Int): Writer = writer.writeArrayOpen(amount) + override def closeEncapsulation(writer: Writer): Writer = writer.writeArrayClose() + +class ArrayKeyEncoder extends ArrayEncoder: + private var id = -1 + override val writeOpenKey: Boolean = false + override def writeKey(writer: Writer): Writer = + id += 1 + writeKey(writer, id.toString()) + override def writeKey[T: Encoder](writer: Writer, key: T): Writer = openEncapsulation(writer, 2).write(key) + override def writeValue[T: Encoder](writer: Writer, value: T): Writer = closeEncapsulation(writer.write(value)) diff --git a/code/shared/src/main/scala/maf/save/Store.scala b/code/shared/src/main/scala/maf/save/save/Store.scala similarity index 100% rename from code/shared/src/main/scala/maf/save/Store.scala rename to code/shared/src/main/scala/maf/save/save/Store.scala diff --git a/code/shared/src/main/scala/maf/save/Util.scala b/code/shared/src/main/scala/maf/save/save/Util.scala similarity index 100% rename from code/shared/src/main/scala/maf/save/Util.scala rename to code/shared/src/main/scala/maf/save/save/Util.scala From de748464f9324032df9e4725c36630bb4b27f094 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Thu, 8 Feb 2024 11:52:45 +0100 Subject: [PATCH 21/97] refactor(save): Move shared encoders into separate encoders Encoders for super-types (e.g. Address) need to distinguish their sub-types, then encoding of these sub-types was sometimes done inside of the encoder of the super-type. This is now changed. --- .../main/scala/maf/save/load/Component.scala | 2 +- .../main/scala/maf/save/save/Component.scala | 26 +++++++---- .../main/scala/maf/save/save/Dependency.scala | 44 ++++++++++++------- .../scala/maf/save/save/Encapsulated.scala | 5 +++ .../src/main/scala/maf/save/save/Store.scala | 42 ++++++++++-------- 5 files changed, 76 insertions(+), 43 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/load/Component.scala b/code/shared/src/main/scala/maf/save/load/Component.scala index 5cc243325..3c8275b63 100644 --- a/code/shared/src/main/scala/maf/save/load/Component.scala +++ b/code/shared/src/main/scala/maf/save/load/Component.scala @@ -118,7 +118,7 @@ trait LoadEnvironment[Expr <: Expression] extends Load[Expr] with LoadAddr[Expr] override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Environment[T] = return reader .readMembers[Environment[T]]( - Array(("BasicEnvironment", summon[Decoder[BasicEnvironment[T]]]), ("NestedEnv", summon[Decoder[NestedEnv[T, T]]])) + Array(("basicEnvironment", summon[Decoder[BasicEnvironment[T]]]), ("nestedEnv", summon[Decoder[NestedEnv[T, T]]])) ) .value .get diff --git a/code/shared/src/main/scala/maf/save/save/Component.scala b/code/shared/src/main/scala/maf/save/save/Component.scala index ace0a6e72..063d60328 100644 --- a/code/shared/src/main/scala/maf/save/save/Component.scala +++ b/code/shared/src/main/scala/maf/save/save/Component.scala @@ -33,6 +33,7 @@ import maf.modular.scv.ScvContextSensitivity import maf.modular.scheme.modf.NoContext import maf.core.Position import maf.core.Position.PTag +import scala.collection.immutable.HashMap trait SavePosition[Expr <: Expression] extends Save[Expr]: def getPositionEncoder: AbstractEncoder = getEncoder @@ -66,17 +67,24 @@ trait SaveStandardSchemeComponentID extends StandardSchemeModFComponents with Sa trait SaveEnvironment[Expr <: Expression] extends Save[Expr] with SaveAddr[Expr]: def getEnvironmentEncoder: AbstractEncoder = getEncoder - given [T <: Address]: EncapsulatedEncoder[Environment[T]] with + given Encoder[BasicEnvironment[Address]] with + override def write(writer: Writer, env: BasicEnvironment[Address]): Writer = writer.write(env.content) + + given EncapsulatedEncoder[NestedEnv[Address, Address]] with + override val encoder: AbstractEncoder = getEnvironmentEncoder + override protected def writeEncapsulated(writer: Writer, env: NestedEnv[Address, Address]): Writer = + writer.writeMember("content", env.content) + if env.rst.isDefined then writer.writeMember("rst", env.rst.get) + writer + + given EncapsulatedEncoder[Environment[Address]] with override val encoder: AbstractEncoder = getEnvironmentEncoder - override protected def writeEncapsulated(writer: Writer, env: Environment[T]): Writer = + override protected def writeEncapsulated(writer: Writer, env: Environment[Address]): Writer = env match { - case BasicEnvironment(content) => - writer.writeMember("BasicEnvironment", content.asInstanceOf[Map[String, Address]]) - case NestedEnv(content, rst) => - writer.open("NestedEnvironment") - writer.writeMember("content", content.asInstanceOf[Map[String, Address]]) - if rst.isDefined then writer.writeMember("rst", rst.get.asInstanceOf[Address]) - writer.close() + case basicEnv @ BasicEnvironment(_) => + writer.writeMember("basicEnvironment", basicEnv) + case nestedEnv @ NestedEnv(_, _) => + writer.writeMember("nestedEnvironment", nestedEnv.asInstanceOf[NestedEnv[Address, Address]]) case _ => System.err.nn.println("The environemnt with type `" + env.getClass + "` could not be encoded") writer diff --git a/code/shared/src/main/scala/maf/save/save/Dependency.scala b/code/shared/src/main/scala/maf/save/save/Dependency.scala index 42a46aa0b..0cb34b18d 100644 --- a/code/shared/src/main/scala/maf/save/save/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/save/Dependency.scala @@ -47,25 +47,39 @@ trait SaveAddr[Expr <: Expression] extends Save[Expr] with SavePosition[Expr]: trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveStandardSchemeComponentID with SaveContext[SchemeExp]: given Encoder[SchemeValue] = AbstractEncoder.deriveEncoder(getAddressEncoder) given Encoder[maf.language.sexp.Value] = AbstractEncoder.deriveAllEncoders(getAddressEncoder) + + given EncapsulatedEncoder[VarAddr[Context]] with + override val encoder = getAddressEncoder + override def writeEncapsulated(writer: Writer, address: VarAddr[Context]): Writer = + import componentIDEncoder.given + writer.writeMember("id", address.id) + if address.ctx.asInstanceOf[Option[Context]].isDefined then writer.writeMember("context", address.ctx.asInstanceOf[Option[Context]].get) + writer + + given EncapsulatedEncoder[ReturnAddr[Context]] with + override val encoder = getAddressEncoder + override def writeEncapsulated(writer: Writer, address: ReturnAddr[Context]): Writer = + import componentIDEncoder.given + writer.writeMember("component", address.cmp.asInstanceOf[Component]) + writer.writeMember("identity", address.idn) + + given EncapsulatedEncoder[PtrAddr[Context]] with + override val encoder = getAddressEncoder + override def writeEncapsulated(writer: Writer, address: PtrAddr[Context]): Writer = + writer.writeMember("expression", address.exp.asInstanceOf[SchemeValue]) + if address.ctx.asInstanceOf[Option[Context]].isDefined then writer.writeMember("context", address.ctx.asInstanceOf[Option[Context]].get) + writer + override def encodeAddress(writer: Writer, address: Address)(using encoder: AbstractEncoder): Writer = import componentIDEncoder.given address match { - case VarAddr(id, ctx) => - writer.open("varAddr") - writer.writeMember("id", id) - if ctx.asInstanceOf[Option[Context]].isDefined then writer.writeMember("context", ctx.asInstanceOf[Option[Context]].get) - writer.close() - case ReturnAddr(cmp, idn) => - writer.open("returnAddr") - writer.writeMember("component", cmp.asInstanceOf[Component]) - writer.writeMember("identity", idn) - writer.close() + case varAddr @ VarAddr(_, _) => + writer.writeMember("varAddr", varAddr.asInstanceOf[VarAddr[Context]]) + case returnAddr @ ReturnAddr(_, _) => + writer.writeMember("returnAddr", returnAddr.asInstanceOf[ReturnAddr[Context]]) case PrmAddr(nam) => writer.writeMember("prmAddr", nam) - case PtrAddr(exp, ctx) => - writer.open("ptrAddr") - writer.writeMember("expression", exp.asInstanceOf[SchemeValue]) - if ctx.asInstanceOf[Option[Context]].isDefined then writer.writeMember("context", ctx.asInstanceOf[Option[Context]].get) - writer.close() + case ptrAddr @ PtrAddr(_, _) => + writer.writeMember("ptrAddr", ptrAddr.asInstanceOf[PtrAddr[Context]]) case _ => super.encodeAddress(writer, address) } diff --git a/code/shared/src/main/scala/maf/save/save/Encapsulated.scala b/code/shared/src/main/scala/maf/save/save/Encapsulated.scala index 791ad5070..01b477d9f 100644 --- a/code/shared/src/main/scala/maf/save/save/Encapsulated.scala +++ b/code/shared/src/main/scala/maf/save/save/Encapsulated.scala @@ -35,6 +35,11 @@ trait EncapsulatedEncoder[T] extends Encoder[T]: writeEncapsulated(writer, value) encoder.closeEncapsulation(writer) protected def writeEncapsulated(writer: Writer, value: T): Writer +trait EncapsulatedArrayEncoder[T](length: Int = 0) extends EncapsulatedEncoder[T]: + override def write(writer: Writer, value: T): Writer = + if length == 0 then writer.writeArrayStart() else writer.writeArrayOpen(length) + writeEncapsulated(writer, value) + writer.writeArrayClose() object EncapsulatedEncoder: extension (writer: Writer) diff --git a/code/shared/src/main/scala/maf/save/save/Store.scala b/code/shared/src/main/scala/maf/save/save/Store.scala index 775d61243..64e8c48c9 100644 --- a/code/shared/src/main/scala/maf/save/save/Store.scala +++ b/code/shared/src/main/scala/maf/save/save/Store.scala @@ -25,30 +25,36 @@ trait SaveValue[Expr <: Expression] extends Save[Expr] with AbstractDomain[Expr] trait SaveModularSchemeLattices extends SaveModularDomain with SaveAddr[SchemeExp] with SaveStandardSchemeComponents with SaveEnvironment[SchemeExp]: type SchemeLattice = ModularSchemeLattice[?, ?, ?, ?, ?, ?, ?] + + given EncapsulatedArrayEncoder[SchemeLattice#Clo]() with + override val encoder = getValueEncoder + override protected def writeEncapsulated(writer: Writer, closure: SchemeLattice#Clo): Writer = + closure.closures.foreach((clo) => + encoder.openEncapsulation(writer) + writer.writeMember("expression", clo._1) + writer.writeMember("address", clo._2.asInstanceOf[Environment[Address]]) + encoder.closeEncapsulation(writer) + ) + writer + + given EncapsulatedArrayEncoder[SchemeLattice#Pointer]() with + override val encoder = getValueEncoder + override protected def writeEncapsulated(writer: Writer, pointer: SchemeLattice#Pointer): Writer = + pointer.ptrs.foreach(writer.write(_)) + writer + given EncapsulatedEncoder[(HMapKey, SchemeLattice#Value)] with override val encoder = getValueEncoder override protected def writeEncapsulated(writer: Writer, hMapPair: (HMapKey, SchemeLattice#Value)): Writer = val (key, value) = hMapPair - def writeValue[T: Encoder](value: T) = writer.writeMember("value", value) - def openValueArray(amount: Int) = writer.write("value").writeArrayOpen(amount) - writer.writeMember("type", value.typeName) value match { - case int: SchemeLattice#Int => writeValue(int.i.toString()) - case bool: SchemeLattice#Bool => writeValue(bool.b.toString()) - case str: SchemeLattice#Str => writeValue(str.s.toString()) - case prim: SchemeLattice#Prim => writeValue(prim.prims) - case clo: SchemeLattice#Clo => - writer.open("value", 2) - clo.closures.foreach((clo) => - writer.writeMember("expression", clo._1) - writer.writeMember("address", clo._2.asInstanceOf[Environment[Address]]) - ) - writer.close() - case pointer: SchemeLattice#Pointer => - openValueArray(pointer.ptrs.size) - pointer.ptrs.foreach(writer.write(_)) - writer.writeArrayClose() + case int: SchemeLattice#Int => writer.writeMember("int", int.i.toString()) + case bool: SchemeLattice#Bool => writer.writeMember("boolean", bool.b.toString()) + case str: SchemeLattice#Str => writer.writeMember("string", str.s.toString()) + case prim: SchemeLattice#Prim => writer.writeMember("primitive", prim.prims) + case clo: SchemeLattice#Clo => writer.writeMember("closure", clo) + case pointer: SchemeLattice#Pointer => writer.writeMember("pointer", pointer) case _ => System.err.nn.println("The lattice with type `" + key.getClass + "` could not be encoded") writer From 1f9a2ecb63beec26035cc302a2c3f3b8530f6d5c Mon Sep 17 00:00:00 2001 From: Merlijn Date: Thu, 8 Feb 2024 12:17:21 +0100 Subject: [PATCH 22/97] refactor(save): Require keys to be strings JSON keys can only be strings, so you should not be able to use any other type as key. If you are using ArrayKeyEncoder, you can use different types, because keys are stored as normal array elements, and not as keys. --- .../main/scala/maf/save/save/Analysis.scala | 2 +- .../scala/maf/save/save/Encapsulated.scala | 24 ++++++++++++------- .../src/main/scala/maf/save/save/Util.scala | 6 ++--- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/save/Analysis.scala b/code/shared/src/main/scala/maf/save/save/Analysis.scala index 762d5c0e4..357788fd5 100644 --- a/code/shared/src/main/scala/maf/save/save/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/save/Analysis.scala @@ -17,7 +17,7 @@ trait Save[Expr <: Expression] extends ModAnalysis[Expr]: given EncapsulatedEncoder[Save[Expr]] with override val encoder = Save.this.getEncoder override def writeEncapsulated(writer: Writer, value: Save[Expr]): Writer = - for (key, value) <- saveInfo do writer.writeMember(key, value.value)(using summon[Encoder[String]], value.encoder, encoder) + for (key, value) <- saveInfo do writer.writeMember(key, value.value)(using value.encoder, encoder) writer /** diff --git a/code/shared/src/main/scala/maf/save/save/Encapsulated.scala b/code/shared/src/main/scala/maf/save/save/Encapsulated.scala index 01b477d9f..dc0653e6c 100644 --- a/code/shared/src/main/scala/maf/save/save/Encapsulated.scala +++ b/code/shared/src/main/scala/maf/save/save/Encapsulated.scala @@ -15,7 +15,7 @@ trait AbstractEncoder: val mapBasedEncoder: Boolean val writeOpenKey = true def writeKey(writer: Writer): Writer - def writeKey[T: Encoder](writer: Writer, key: T): Writer + def writeKey(writer: Writer, key: String): Writer def writeValue[T: Encoder](writer: Writer, value: T): Writer def openEncapsulation(writer: Writer): Writer def openEncapsulation(writer: Writer, amount: Int): Writer @@ -35,6 +35,7 @@ trait EncapsulatedEncoder[T] extends Encoder[T]: writeEncapsulated(writer, value) encoder.closeEncapsulation(writer) protected def writeEncapsulated(writer: Writer, value: T): Writer + trait EncapsulatedArrayEncoder[T](length: Int = 0) extends EncapsulatedEncoder[T]: override def write(writer: Writer, value: T): Writer = if length == 0 then writer.writeArrayStart() else writer.writeArrayOpen(length) @@ -44,7 +45,7 @@ trait EncapsulatedArrayEncoder[T](length: Int = 0) extends EncapsulatedEncoder[T object EncapsulatedEncoder: extension (writer: Writer) def close()(using encoder: AbstractEncoder): Writer = encoder.closeEncapsulation(writer) - def writeMember[T: Encoder, U: Encoder](key: T, value: U)(using encoder: AbstractEncoder): Writer = + def writeMember[T: Encoder](key: String, value: T)(using encoder: AbstractEncoder): Writer = encoder.writeKey(writer, key) encoder.writeValue(writer, value) def writeMember[T: Encoder](value: T)(using encoder: AbstractEncoder): Writer = @@ -53,13 +54,13 @@ object EncapsulatedEncoder: def open()(using encoder: AbstractEncoder): Writer = if encoder.writeOpenKey then encoder.writeKey(writer) encoder.openEncapsulation(writer) - def open[T: Encoder](key: T)(using encoder: AbstractEncoder): Writer = + def open(key: String)(using encoder: AbstractEncoder): Writer = encoder.writeKey(writer, key) encoder.openEncapsulation(writer) def open(amount: Int)(using encoder: AbstractEncoder): Writer = if encoder.writeOpenKey then encoder.writeKey(writer) encoder.openEncapsulation(writer, amount) - def open[T: Encoder](key: T, amount: Int)(using encoder: AbstractEncoder): Writer = + def open(key: String, amount: Int)(using encoder: AbstractEncoder): Writer = encoder.writeKey(writer, key) encoder.openEncapsulation(writer) @@ -69,7 +70,7 @@ class MapEncoder extends AbstractEncoder: override def writeKey(writer: Writer): Writer = id += 1 writeKey(writer, id.toString()) - override def writeKey[T: Encoder](writer: Writer, key: T): Writer = writer.write(key) + override def writeKey(writer: Writer, key: String): Writer = writer.write(key) override def writeValue[T: Encoder](writer: Writer, value: T): Writer = writer.write(value) override def openEncapsulation(writer: Writer): Writer = writer.writeMapStart() override def openEncapsulation(writer: Writer, amount: Int): Writer = writer.writeMapOpen(amount) @@ -78,7 +79,7 @@ class MapEncoder extends AbstractEncoder: class ArrayEncoder extends AbstractEncoder: val mapBasedEncoder = false override def writeKey(writer: Writer): Writer = writer - override def writeKey[T: Encoder](writer: Writer, key: T): Writer = writer + override def writeKey(writer: Writer, key: String): Writer = writer override def writeValue[T: Encoder](writer: Writer, value: T): Writer = writer.write(value) override def openEncapsulation(writer: Writer): Writer = writer.writeArrayStart() override def openEncapsulation(writer: Writer, amount: Int): Writer = writer.writeArrayOpen(amount) @@ -90,5 +91,12 @@ class ArrayKeyEncoder extends ArrayEncoder: override def writeKey(writer: Writer): Writer = id += 1 writeKey(writer, id.toString()) - override def writeKey[T: Encoder](writer: Writer, key: T): Writer = openEncapsulation(writer, 2).write(key) - override def writeValue[T: Encoder](writer: Writer, value: T): Writer = closeEncapsulation(writer.write(value)) + override def writeKey(writer: Writer, key: String): Writer = writer.write(key) + def writeKey[T: Encoder](writer: Writer, key: T): Writer = writer.write(key) + override def writeValue[T: Encoder](writer: Writer, value: T): Writer = writer.write(value) + +object ArrayKeyEncoder: + extension (writer: Writer) + def writeMember[T: Encoder, U: Encoder](key: T, value: U)(using encoder: ArrayKeyEncoder): Writer = + encoder.writeKey(writer, key) + encoder.writeValue(writer, value) diff --git a/code/shared/src/main/scala/maf/save/save/Util.scala b/code/shared/src/main/scala/maf/save/save/Util.scala index 5e5f1bb8b..8adc1269e 100644 --- a/code/shared/src/main/scala/maf/save/save/Util.scala +++ b/code/shared/src/main/scala/maf/save/save/Util.scala @@ -2,11 +2,11 @@ package maf.save import io.bullet.borer.Encoder import io.bullet.borer.Writer -import EncapsulatedEncoder.* +import ArrayKeyEncoder.* trait SaveMapToArray: given mapKeyEncoder[K, V](using keyEncoder: Encoder[K], valueEncoder: Encoder[V]): EncapsulatedEncoder[Map[K, V]] with - override val encoder = new ArrayKeyEncoder + override val encoder: ArrayKeyEncoder = new ArrayKeyEncoder override def writeEncapsulated(writer: Writer, map: Map[K, V]): Writer = - for (key, value) <- map do writer.writeMember(key, value) + for (key, value) <- map do writer.writeMember(key, value)(using keyEncoder, valueEncoder, encoder) writer From 7f5113fa9aae5880104b6031518e74c898177a14 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Thu, 8 Feb 2024 13:28:28 +0100 Subject: [PATCH 23/97] feat(load): Add ArrayDecoder and ArrayKeyDecoder --- .../main/scala/maf/save/load/Analysis.scala | 2 + .../main/scala/maf/save/load/Component.scala | 6 +- .../main/scala/maf/save/load/Dependency.scala | 3 +- .../scala/maf/save/load/Encapsulated.scala | 57 ++++++++++++++++--- .../main/scala/maf/save/save/Analysis.scala | 2 + .../main/scala/maf/save/save/Component.scala | 6 +- .../main/scala/maf/save/save/Dependency.scala | 3 +- .../scala/maf/save/save/Encapsulated.scala | 4 -- .../src/main/scala/maf/save/save/Store.scala | 3 +- 9 files changed, 67 insertions(+), 19 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/load/Analysis.scala b/code/shared/src/main/scala/maf/save/load/Analysis.scala index 4d973000f..9926b0baa 100644 --- a/code/shared/src/main/scala/maf/save/load/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/load/Analysis.scala @@ -14,6 +14,7 @@ case class Loadable[T](val load: (T) => Unit)(using val decoder: Decoder[T]) trait Load[Expr <: Expression] extends ModAnalysis[Expr]: def getDecoder: AbstractDecoder + def getKeyDecoder: AbstractDecoder given EncapsulatedDecoder[Load[Expr]] with override val decoder: AbstractDecoder = Load.this.getDecoder @@ -36,3 +37,4 @@ trait LoadModF with LoadNoContext[SchemeExp] with LoadSchemeAddr: def getDecoder: AbstractDecoder = new MapDecoder + def getKeyDecoder: AbstractDecoder = new MapDecoder diff --git a/code/shared/src/main/scala/maf/save/load/Component.scala b/code/shared/src/main/scala/maf/save/load/Component.scala index 3c8275b63..88b5ffc79 100644 --- a/code/shared/src/main/scala/maf/save/load/Component.scala +++ b/code/shared/src/main/scala/maf/save/load/Component.scala @@ -35,6 +35,7 @@ import maf.core.Position.PTag trait LoadComponents[Expr <: Expression] extends Load[Expr]: def getComponentDecoder: AbstractDecoder = getDecoder + def getComponentKeyDecoder: AbstractDecoder = getKeyDecoder override def loadInfo: Map[String, Loadable[_]] = super.loadInfo + ("components" -> Loadable((visited: Set[Component]) => visited.foreach((component) => if component != initialComponent then println(component.asInstanceOf[SchemeModFComponent.Call[_]].clo)) @@ -68,7 +69,7 @@ trait LoadStandardSchemeComponents given Decoder[SchemeLambdaExp] = AbstractDecoder.deriveDecoder[SchemeLambdaExp](compDecoder) given EncapsulatedDecoder[SchemeExp] with - override val decoder = getComponentDecoder + override val decoder = getComponentKeyDecoder override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): SchemeExp = val expression = reader.readMembers[SchemeExp]( Array( @@ -113,8 +114,9 @@ trait LoadPosition[Expr <: Expression] extends Load[Expr]: trait LoadEnvironment[Expr <: Expression] extends Load[Expr] with LoadAddr[Expr]: def getEnvironmentDecoder: AbstractDecoder = getDecoder + def getEnvironmentKeyDecoder: AbstractDecoder = getKeyDecoder given [T <: Address]: EncapsulatedDecoder[Environment[T]] with - override def decoder: AbstractDecoder = getEnvironmentDecoder + override def decoder: AbstractDecoder = getEnvironmentKeyDecoder override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Environment[T] = return reader .readMembers[Environment[T]]( diff --git a/code/shared/src/main/scala/maf/save/load/Dependency.scala b/code/shared/src/main/scala/maf/save/load/Dependency.scala index cd9ff2656..df03d74de 100644 --- a/code/shared/src/main/scala/maf/save/load/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/load/Dependency.scala @@ -17,10 +17,11 @@ import maf.core.Identifier trait LoadAddr[Expr <: Expression] extends Load[Expr] with LoadPosition[Expr]: def getAddressDecoder: AbstractDecoder = getDecoder + def getAddressKeyDecoder: AbstractDecoder = getKeyDecoder def addressDecoders = List[(String, Decoder[_ <: Address])]() given EncapsulatedDecoder[Address] with - override def decoder: AbstractDecoder = getAddressDecoder + override def decoder: AbstractDecoder = getAddressKeyDecoder override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Address = reader.readMembers(addressDecoders.toArray).value.get.get diff --git a/code/shared/src/main/scala/maf/save/load/Encapsulated.scala b/code/shared/src/main/scala/maf/save/load/Encapsulated.scala index cde970b4d..2d70c10d1 100644 --- a/code/shared/src/main/scala/maf/save/load/Encapsulated.scala +++ b/code/shared/src/main/scala/maf/save/load/Encapsulated.scala @@ -12,11 +12,15 @@ import scala.util.{Failure, Success} import scala.concurrent.ExecutionContext.Implicits.global trait AbstractDecoder: + protected val values = new HashMap[String, Any]() + protected var currentKey: Option[String] = None val mapBasedDecoder: Boolean def readKey(reader: Reader): Unit def readKey(reader: Reader, key: String): Unit def readValue[T: Decoder](reader: Reader): Future[T] - def getValue[T](key: String): T + def getValue[T](key: String): T = + if !values.contains(key) then throw new AbstractDecoder.KeyException("Key '" + key + "' is not found.") + values.get(key).get.asInstanceOf[T] def openEncapsulation(reader: Reader): Unit def openEncapsulation(reader: Reader, amount: Int): Unit def closeEncapsulation[T](reader: Reader, unbounded: Boolean, res: T): Unit @@ -26,6 +30,7 @@ object AbstractDecoder: if decoder.mapBasedDecoder then CompactMapBasedCodecs.deriveDecoder[T] else ArrayBasedCodecs.deriveDecoder[T] inline def deriveAllDecoders[T](decoder: AbstractDecoder): Decoder[T] = if decoder.mapBasedDecoder then CompactMapBasedCodecs.deriveAllDecoders[T] else ArrayBasedCodecs.deriveAllDecoders[T] + case class KeyException(msg: String) extends Exception(msg) trait EncapsulatedDecoder[T] extends Decoder[T]: def decoder: AbstractDecoder @@ -59,10 +64,8 @@ object EncapsulatedDecoder: def getMember[T](key: String)(using decoder: AbstractDecoder): T = decoder.getValue[T](key) class MapDecoder extends AbstractDecoder: - protected val values = new HashMap[String, Any]() protected val keys = new HashMap[String, Decoder[_]]() protected var decodeKey: Option[String] = None - protected var currentKey: Option[String] = None override val mapBasedDecoder: Boolean = true protected def readDecodeKey(reader: Reader): Unit = @@ -74,7 +77,7 @@ class MapDecoder extends AbstractDecoder: readDecodeKey(reader) this.currentKey = Some(key) override def readValue[T: Decoder](reader: Reader): Future[T] = - if currentKey.isEmpty then throw new KeyException("Trying to read a value before reading a key.") + if currentKey.isEmpty then throw new AbstractDecoder.KeyException("Trying to read a value before reading a key.") val promise = Promise[T]() if decodeKey == currentKey then @@ -92,11 +95,49 @@ class MapDecoder extends AbstractDecoder: else keys.addOne((currentKey.get, summon[Decoder[T]])) currentKey = None promise.future - override def getValue[T](key: String): T = - if !values.contains(key) then throw new KeyException("Key '" + key + "' is not found.") - values.get(key).get.asInstanceOf[T] override def openEncapsulation(reader: Reader): Unit = reader.readMapStart() override def openEncapsulation(reader: Reader, amount: Int): Unit = reader.readMapOpen(amount) override def closeEncapsulation[T](reader: Reader, unbounded: Boolean, res: T): Unit = reader.readMapClose(unbounded, res) - case class KeyException(msg: String) extends Exception(msg) +class ArrayDecoder extends AbstractDecoder: + protected var id = -1 + + override val mapBasedDecoder: Boolean = false + override def readKey(reader: Reader): Unit = return + override def readKey(reader: Reader, key: String): Unit = + currentKey = Some(key) + return + override def readValue[T: Decoder](reader: Reader): Future[T] = + val key = if currentKey.isDefined then currentKey.get else id.toString() + currentKey = None + val promise = Promise[T]() + if reader.hasBreak then return promise.future + id += 1 + val res = reader.read[T]() + values.addOne(key, res) + promise.success(res) + promise.future + override def openEncapsulation(reader: Reader): Unit = reader.readArrayStart() + override def openEncapsulation(reader: Reader, amount: Int): Unit = reader.readArrayOpen(amount) + override def closeEncapsulation[T](reader: Reader, unbounded: Boolean, res: T): Unit = reader.readMapClose(unbounded, res) + +class ArrayKeyDecoder extends MapDecoder: + protected var key: Option[Any] = None + def readKey[T: Decoder](reader: Reader): Unit = + this.key = Some(reader.read[T]()) + def readKeyValue[T: Decoder](reader: Reader): Future[(Any, T)] = + if key.isEmpty then throw new AbstractDecoder.KeyException("Trying to read a value before reading a key.") + val promise = Promise[(Any, T)]() + val res = reader.read[T]() + key = None + promise.success((key, res)) + promise.future + override def openEncapsulation(reader: Reader): Unit = reader.readArrayStart() + override def openEncapsulation(reader: Reader, amount: Int): Unit = reader.readArrayOpen(amount) + override def closeEncapsulation[T](reader: Reader, unbounded: Boolean, res: T): Unit = reader.readMapClose(unbounded, res) + +object ArrayKeyDecoder: + extension (reader: Reader) + def readMember[K: Decoder, V: Decoder]()(using decoder: ArrayKeyDecoder): Future[(K, V)] = + decoder.readKey[K](reader) + decoder.readKeyValue[V](reader).asInstanceOf[Future[(K, V)]] diff --git a/code/shared/src/main/scala/maf/save/save/Analysis.scala b/code/shared/src/main/scala/maf/save/save/Analysis.scala index 357788fd5..837b88d25 100644 --- a/code/shared/src/main/scala/maf/save/save/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/save/Analysis.scala @@ -14,6 +14,7 @@ case class Savable[T](val value: T)(using val encoder: Encoder[T]) trait Save[Expr <: Expression] extends ModAnalysis[Expr]: def getEncoder: AbstractEncoder + def getKeyEncoder: AbstractEncoder given EncapsulatedEncoder[Save[Expr]] with override val encoder = Save.this.getEncoder override def writeEncapsulated(writer: Writer, value: Save[Expr]): Writer = @@ -43,3 +44,4 @@ trait SaveModF with SaveModularSchemeLattices with SaveNoContext[SchemeExp]: override def getEncoder: AbstractEncoder = new MapEncoder + override def getKeyEncoder: AbstractEncoder = new MapEncoder diff --git a/code/shared/src/main/scala/maf/save/save/Component.scala b/code/shared/src/main/scala/maf/save/save/Component.scala index 063d60328..73b10a6e6 100644 --- a/code/shared/src/main/scala/maf/save/save/Component.scala +++ b/code/shared/src/main/scala/maf/save/save/Component.scala @@ -50,6 +50,7 @@ trait SavePosition[Expr <: Expression] extends Save[Expr]: trait SaveComponents[Expr <: Expression] extends Save[Expr]: def getComponentEncoder: AbstractEncoder = getEncoder + def getComponentKeyEncoder: AbstractEncoder = getKeyEncoder override def saveInfo: Map[String, Savable[_]] = super.saveInfo + ("components" -> Savable(visited)) given componentEncoder: Encoder[Component] @@ -67,6 +68,7 @@ trait SaveStandardSchemeComponentID extends StandardSchemeModFComponents with Sa trait SaveEnvironment[Expr <: Expression] extends Save[Expr] with SaveAddr[Expr]: def getEnvironmentEncoder: AbstractEncoder = getEncoder + def getEnvironmentKeyEncoder: AbstractEncoder = getKeyEncoder given Encoder[BasicEnvironment[Address]] with override def write(writer: Writer, env: BasicEnvironment[Address]): Writer = writer.write(env.content) @@ -78,7 +80,7 @@ trait SaveEnvironment[Expr <: Expression] extends Save[Expr] with SaveAddr[Expr] writer given EncapsulatedEncoder[Environment[Address]] with - override val encoder: AbstractEncoder = getEnvironmentEncoder + override val encoder: AbstractEncoder = getEnvironmentKeyEncoder override protected def writeEncapsulated(writer: Writer, env: Environment[Address]): Writer = env match { case basicEnv @ BasicEnvironment(_) => @@ -110,7 +112,7 @@ trait SaveStandardSchemeComponents with SaveContext[SchemeExp]: given EncapsulatedEncoder[SchemeExp] with - override val encoder = getComponentEncoder + override val encoder = getComponentKeyEncoder def writeEncapsulated(writer: Writer, exp: SchemeExp): Writer = exp match case funcall: SchemeFuncall => writer.writeMember("funcall", funcall) diff --git a/code/shared/src/main/scala/maf/save/save/Dependency.scala b/code/shared/src/main/scala/maf/save/save/Dependency.scala index 0cb34b18d..e789b2a24 100644 --- a/code/shared/src/main/scala/maf/save/save/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/save/Dependency.scala @@ -36,12 +36,13 @@ trait SaveDependency extends SaveMapToArray with SaveStandardSchemeComponentID: trait SaveAddr[Expr <: Expression] extends Save[Expr] with SavePosition[Expr]: def getAddressEncoder: AbstractEncoder = getEncoder + def getAddressKeyEncoder: AbstractEncoder = getKeyEncoder def encodeAddress(writer: Writer, address: Address)(using encoder: AbstractEncoder): Writer = System.err.nn.println("The address with type `" + address.getClass + "` could not be encoded") writer given EncapsulatedEncoder[Address] with - override val encoder = getAddressEncoder + override val encoder = getAddressKeyEncoder override def writeEncapsulated(writer: Writer, value: Address): Writer = encodeAddress(writer, value) trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveStandardSchemeComponentID with SaveContext[SchemeExp]: diff --git a/code/shared/src/main/scala/maf/save/save/Encapsulated.scala b/code/shared/src/main/scala/maf/save/save/Encapsulated.scala index dc0653e6c..a0a4f8bdf 100644 --- a/code/shared/src/main/scala/maf/save/save/Encapsulated.scala +++ b/code/shared/src/main/scala/maf/save/save/Encapsulated.scala @@ -86,11 +86,7 @@ class ArrayEncoder extends AbstractEncoder: override def closeEncapsulation(writer: Writer): Writer = writer.writeArrayClose() class ArrayKeyEncoder extends ArrayEncoder: - private var id = -1 override val writeOpenKey: Boolean = false - override def writeKey(writer: Writer): Writer = - id += 1 - writeKey(writer, id.toString()) override def writeKey(writer: Writer, key: String): Writer = writer.write(key) def writeKey[T: Encoder](writer: Writer, key: T): Writer = writer.write(key) override def writeValue[T: Encoder](writer: Writer, value: T): Writer = writer.write(value) diff --git a/code/shared/src/main/scala/maf/save/save/Store.scala b/code/shared/src/main/scala/maf/save/save/Store.scala index 64e8c48c9..80fff71f4 100644 --- a/code/shared/src/main/scala/maf/save/save/Store.scala +++ b/code/shared/src/main/scala/maf/save/save/Store.scala @@ -21,6 +21,7 @@ import maf.core.Environment trait SaveValue[Expr <: Expression] extends Save[Expr] with AbstractDomain[Expr]: def getValueEncoder: AbstractEncoder = getEncoder + def getValueKeyEncoder: AbstractEncoder = getKeyEncoder given valueEncoder: Encoder[Value] trait SaveModularSchemeLattices extends SaveModularDomain with SaveAddr[SchemeExp] with SaveStandardSchemeComponents with SaveEnvironment[SchemeExp]: @@ -44,7 +45,7 @@ trait SaveModularSchemeLattices extends SaveModularDomain with SaveAddr[SchemeEx writer given EncapsulatedEncoder[(HMapKey, SchemeLattice#Value)] with - override val encoder = getValueEncoder + override val encoder = getValueKeyEncoder override protected def writeEncapsulated(writer: Writer, hMapPair: (HMapKey, SchemeLattice#Value)): Writer = val (key, value) = hMapPair From c80e47d8fefcbb09560b57711059c23375e5924a Mon Sep 17 00:00:00 2001 From: Merlijn Date: Thu, 8 Feb 2024 15:41:52 +0100 Subject: [PATCH 24/97] feat(load): Load dependencies --- .../main/scala/maf/save/load/Analysis.scala | 8 ++-- .../main/scala/maf/save/load/Component.scala | 27 +++++++++++++- .../main/scala/maf/save/load/Dependency.scala | 37 +++++++++++++++++-- .../scala/maf/save/load/Encapsulated.scala | 4 +- .../src/main/scala/maf/save/load/Util.scala | 10 ++++- .../main/scala/maf/save/save/Dependency.scala | 12 +++--- 6 files changed, 81 insertions(+), 17 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/load/Analysis.scala b/code/shared/src/main/scala/maf/save/load/Analysis.scala index 9926b0baa..9aa93ba90 100644 --- a/code/shared/src/main/scala/maf/save/load/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/load/Analysis.scala @@ -19,8 +19,7 @@ trait Load[Expr <: Expression] extends ModAnalysis[Expr]: given EncapsulatedDecoder[Load[Expr]] with override val decoder: AbstractDecoder = Load.this.getDecoder override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Load[Expr] = - for (key, value) <- loadInfo do reader.readMember(key)(using value.decoder, decoder) - for (key, value) <- loadInfo do value.load(reader.getMember(key)) + for (key, value) <- loadInfo do value.load(reader.readMember(key)(using value.decoder, decoder).value.get.get) return Load.this override def load(filename: String): Unit = @@ -35,6 +34,9 @@ trait LoadModF with LoadComponents[SchemeExp] with LoadStandardSchemeComponents with LoadNoContext[SchemeExp] - with LoadSchemeAddr: + with LoadSchemeAddr + with LoadDependency[SchemeExp] + with LoadAddrDependency[SchemeExp] + with LoadStandardSchemeComponentID: def getDecoder: AbstractDecoder = new MapDecoder def getKeyDecoder: AbstractDecoder = new MapDecoder diff --git a/code/shared/src/main/scala/maf/save/load/Component.scala b/code/shared/src/main/scala/maf/save/load/Component.scala index 88b5ffc79..25f5e7730 100644 --- a/code/shared/src/main/scala/maf/save/load/Component.scala +++ b/code/shared/src/main/scala/maf/save/load/Component.scala @@ -32,13 +32,18 @@ import io.bullet.borer.Reader import maf.save.EncapsulatedDecoder.* import maf.core.Position import maf.core.Position.PTag +import scala.collection.mutable.HashMap -trait LoadComponents[Expr <: Expression] extends Load[Expr]: +trait LoadComponents[Expr <: Expression] extends Load[Expr] with LoadComponentID[Expr]: def getComponentDecoder: AbstractDecoder = getDecoder def getComponentKeyDecoder: AbstractDecoder = getKeyDecoder + def addComponent(component: Component): Unit override def loadInfo: Map[String, Loadable[_]] = super.loadInfo + ("components" -> Loadable((visited: Set[Component]) => - visited.foreach((component) => if component != initialComponent then println(component.asInstanceOf[SchemeModFComponent.Call[_]].clo)) + visited.foreach((component) => + addComponent(component) + if component != initialComponent then println(component.asInstanceOf[SchemeModFComponent.Call[_]].clo) + ) )) given componentDecoder: Decoder[Component] @@ -49,6 +54,9 @@ trait LoadStandardSchemeComponents with LoadContext[SchemeExp] with LoadPosition[SchemeExp] with LoadEnvironment[SchemeExp]: + override def addComponent(component: SchemeModFComponent): Unit = + if component != initialComponent then components.addOne(component.asInstanceOf[SchemeModFComponent.Call[Context]].clo._1.idn.pos, component) + override given componentDecoder: Decoder[Component] with override def read(reader: Reader): Component = if reader.tryReadString("main") then return initialComponent @@ -138,3 +146,18 @@ trait LoadEnvironment[Expr <: Expression] extends Load[Expr] with LoadAddr[Expr] return new NestedEnv(content.value.get.get.asInstanceOf[Map[String, T]], if rst.isCompleted then Some(rst.value.get.get.asInstanceOf[K]) else None ) + +trait LoadComponentID[Expr <: Expression] extends LoadPosition[Expr]: + var components = HashMap[Position, Component]() + given componentIDDecoder: Decoder[Component] + +trait LoadStandardSchemeComponentID extends LoadComponentID[SchemeExp] with LoadContext[SchemeExp]: + given componentIDDecoder: Decoder[Component] with + override def read(reader: Reader): Component = + if reader.tryReadString("main") then return initialComponent + else reader.read[SchemeModFComponent.Call[Context]]().asInstanceOf[Component] + + given schemeComponentIDDecoder[T]: Decoder[SchemeModFComponent.Call[Context]] with + override def read(reader: Reader): SchemeModFComponent.Call[Context] = + val pos = reader.read[Position]() + return components(pos).asInstanceOf[SchemeModFComponent.Call[Context]] diff --git a/code/shared/src/main/scala/maf/save/load/Dependency.scala b/code/shared/src/main/scala/maf/save/load/Dependency.scala index df03d74de..a637496b1 100644 --- a/code/shared/src/main/scala/maf/save/load/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/load/Dependency.scala @@ -14,6 +14,26 @@ import io.bullet.borer.Reader import io.bullet.borer.Decoder import maf.save.EncapsulatedDecoder.* import maf.core.Identifier +import maf.core.Identity + +trait LoadAddrDependency[Expr <: Expression] extends LoadDependency[Expr] with LoadPosition[Expr] with LoadAddr[Expr]: + given Decoder[AddrDependency] with + override def read(reader: Reader): AddrDependency = + val addr = reader.read[Address]() + return new AddrDependency(addr) + override def dependencyDecoders = super.dependencyDecoders ++ Set(("addrDependency", summon[Decoder[AddrDependency]])) + +trait LoadDependency[Expr <: Expression] extends LoadMapToArray with LoadComponentID[Expr]: + def getDependencyDecoder: AbstractDecoder = getDecoder + def getDependencyKeyDecoder: AbstractDecoder = getKeyDecoder + override def loadInfo: Map[String, Loadable[_]] = + super.loadInfo + ("dependencies" -> Loadable((deps: Map[Dependency, Set[Component]]) => deps.foreach(println(_)))) + def dependencyDecoders = Set[(String, Decoder[_ <: Dependency])]() + + given EncapsulatedDecoder[Dependency] with + override def decoder: AbstractDecoder = getDependencyKeyDecoder + override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Dependency = + reader.readMembers(dependencyDecoders.toArray).value.get.get trait LoadAddr[Expr <: Expression] extends Load[Expr] with LoadPosition[Expr]: def getAddressDecoder: AbstractDecoder = getDecoder @@ -25,15 +45,24 @@ trait LoadAddr[Expr <: Expression] extends Load[Expr] with LoadPosition[Expr]: override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Address = reader.readMembers(addressDecoders.toArray).value.get.get -trait LoadSchemeAddr extends LoadAddr[SchemeExp] with LoadContext[SchemeExp]: +trait LoadSchemeAddr extends LoadAddr[SchemeExp] with LoadContext[SchemeExp] with LoadComponentID[SchemeExp]: given Decoder[SchemeValue] = AbstractDecoder.deriveDecoder(getAddressDecoder) given Decoder[maf.language.sexp.Value] = AbstractDecoder.deriveAllDecoders(getAddressDecoder) override def addressDecoders = - super.addressDecoders ++ List(("varAddr", summon[Decoder[VarAddr[Context]]]), - ("prmAddr", summon[Decoder[PrmAddr]]), - ("ptrAddr", summon[Decoder[PtrAddr[Context]]]) + super.addressDecoders ++ List( + ("varAddr", summon[Decoder[VarAddr[Context]]]), + ("prmAddr", summon[Decoder[PrmAddr]]), + ("returnAddr", summon[Decoder[ReturnAddr[Component]]]), + ("ptrAddr", summon[Decoder[PtrAddr[Context]]]) ) + given EncapsulatedDecoder[ReturnAddr[Component]] with + override def decoder: AbstractDecoder = getAddressDecoder + override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): ReturnAddr[Component] = + val component = reader.readMember[Component]("component") + val identity = reader.readMember[Identity]("identity") + return new ReturnAddr[Component](component.value.get.get, identity.value.get.get) + given EncapsulatedDecoder[VarAddr[Context]] with override def decoder: AbstractDecoder = getAddressDecoder override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): VarAddr[Context] = diff --git a/code/shared/src/main/scala/maf/save/load/Encapsulated.scala b/code/shared/src/main/scala/maf/save/load/Encapsulated.scala index 2d70c10d1..a7634d33a 100644 --- a/code/shared/src/main/scala/maf/save/load/Encapsulated.scala +++ b/code/shared/src/main/scala/maf/save/load/Encapsulated.scala @@ -122,15 +122,15 @@ class ArrayDecoder extends AbstractDecoder: override def closeEncapsulation[T](reader: Reader, unbounded: Boolean, res: T): Unit = reader.readMapClose(unbounded, res) class ArrayKeyDecoder extends MapDecoder: - protected var key: Option[Any] = None + var key: Option[Any] = None def readKey[T: Decoder](reader: Reader): Unit = this.key = Some(reader.read[T]()) def readKeyValue[T: Decoder](reader: Reader): Future[(Any, T)] = if key.isEmpty then throw new AbstractDecoder.KeyException("Trying to read a value before reading a key.") val promise = Promise[(Any, T)]() val res = reader.read[T]() + promise.success((key.get, res)) key = None - promise.success((key, res)) promise.future override def openEncapsulation(reader: Reader): Unit = reader.readArrayStart() override def openEncapsulation(reader: Reader, amount: Int): Unit = reader.readArrayOpen(amount) diff --git a/code/shared/src/main/scala/maf/save/load/Util.scala b/code/shared/src/main/scala/maf/save/load/Util.scala index 1c372f885..105b3a73e 100644 --- a/code/shared/src/main/scala/maf/save/load/Util.scala +++ b/code/shared/src/main/scala/maf/save/load/Util.scala @@ -1,6 +1,14 @@ package maf.save import io.bullet.borer.Decoder +import io.bullet.borer.Reader +import maf.save.ArrayKeyDecoder.readMember +import scala.collection.mutable trait LoadMapToArray: - given mapKeyDecoder[K, V](using keyDecoder: Decoder[K], valueDecoder: Decoder[V]): EncapsulatedDecoder[Map[K, V]] + given mapKeyDecoder[K, V](using keyDecoder: Decoder[K], valueDecoder: Decoder[V]): EncapsulatedDecoder[Map[K, V]] with + override val decoder: ArrayKeyDecoder = new ArrayKeyDecoder + override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Map[K, V] = + val elements = mutable.Set[(K, V)]() + while !reader.hasBreak do elements.add(reader.readMember[K, V]()(using keyDecoder, valueDecoder, decoder).value.get.get) + return elements.toMap diff --git a/code/shared/src/main/scala/maf/save/save/Dependency.scala b/code/shared/src/main/scala/maf/save/save/Dependency.scala index e789b2a24..2bfc2cec8 100644 --- a/code/shared/src/main/scala/maf/save/save/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/save/Dependency.scala @@ -17,10 +17,10 @@ import maf.core.Identifier import maf.util.Writer.write trait SaveAddrDep extends SaveDependency with SavePosition[SchemeExp] with SaveSchemeAddr: - override def encodeDependency(writer: Writer, dependency: Dependency): Writer = + override def encodeDependency(writer: Writer, dependency: Dependency)(using AbstractEncoder): Writer = dependency match { - case AddrDependency(_) => writer.write(dependency.asInstanceOf[AddrDependency].addr) - case _ => super.encodeDependency(writer, dependency) + case AddrDependency(addr) => writer.writeMember("addrDependency", addr) + case _ => super.encodeDependency(writer, dependency) } trait SaveDependency extends SaveMapToArray with SaveStandardSchemeComponentID: @@ -29,10 +29,12 @@ trait SaveDependency extends SaveMapToArray with SaveStandardSchemeComponentID: import componentIDEncoder.given super.saveInfo + ("dependencies" -> Savable(deps)) - def encodeDependency(writer: Writer, dependency: Dependency): Writer = + def encodeDependency(writer: Writer, dependency: Dependency)(using AbstractEncoder): Writer = System.err.nn.println("The dependency with type `" + dependency.getClass + "` could not be encoded") writer - given dependencyEncoder: Encoder[Dependency] = encodeDependency _ + given EncapsulatedEncoder[Dependency] with + override val encoder: AbstractEncoder = getDependencyEncoder + override protected def writeEncapsulated(writer: Writer, value: Dependency): Writer = encodeDependency(writer, value) trait SaveAddr[Expr <: Expression] extends Save[Expr] with SavePosition[Expr]: def getAddressEncoder: AbstractEncoder = getEncoder From 0c35ecb36f87f7552b4ed3abc4e39e6d0108215e Mon Sep 17 00:00:00 2001 From: Merlijn Date: Thu, 8 Feb 2024 20:51:48 +0100 Subject: [PATCH 25/97] feat(save): Save lattices correctly The `Int`, `Boolean` and `String` were using `toString`, they are now correctly encoded. --- .../src/main/scala/maf/save/save/Store.scala | 37 +++++++++++++++++-- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/save/Store.scala b/code/shared/src/main/scala/maf/save/save/Store.scala index 80fff71f4..82483a381 100644 --- a/code/shared/src/main/scala/maf/save/save/Store.scala +++ b/code/shared/src/main/scala/maf/save/save/Store.scala @@ -18,13 +18,42 @@ import io.bullet.borer.LowPrioEncoders import maf.core.Address import EncapsulatedEncoder.* import maf.core.Environment +import maf.lattice.{ConcreteLattice, ConstantPropagation} +import maf.lattice.Concrete trait SaveValue[Expr <: Expression] extends Save[Expr] with AbstractDomain[Expr]: def getValueEncoder: AbstractEncoder = getEncoder def getValueKeyEncoder: AbstractEncoder = getKeyEncoder given valueEncoder: Encoder[Value] -trait SaveModularSchemeLattices extends SaveModularDomain with SaveAddr[SchemeExp] with SaveStandardSchemeComponents with SaveEnvironment[SchemeExp]: +trait SaveLattice[Expr <: Expression] extends Save[Expr]: + def getLatticeEncoder: AbstractEncoder = getEncoder + def getLatticeKeyEncoder: AbstractEncoder = getKeyEncoder + + type Lattice[T] = ConstantPropagation.L[T] | Concrete.L[T] + given latticeEncoder[P[T] <: Lattice[T], T: Encoder]: EncapsulatedEncoder[P[T]] with + override val encoder: AbstractEncoder = getLatticeKeyEncoder + override protected def writeEncapsulated(writer: Writer, lattice: P[T]): Writer = + lattice match + case constant: ConstantPropagation.L[T] => + writer.writeMember[ConstantPropagation.L[T]]("constant", constant)(using constantLatticeEncoder, encoder) + case _ => System.err.nn.println("The lattice of type `" + lattice.getClass + "` could not be encoded") + writer + + given constantLatticeEncoder[T: Encoder]: Encoder[ConstantPropagation.L[T]] with + override def write(writer: Writer, lattice: ConstantPropagation.L[T]): Writer = + lattice match + case ConstantPropagation.Top => writer.write("top") + case ConstantPropagation.Constant(a) => writer.write[T](a) + case ConstantPropagation.Bottom => writer.write("bottom") + writer + +trait SaveModularSchemeLattices + extends SaveModularDomain + with SaveAddr[SchemeExp] + with SaveStandardSchemeComponents + with SaveEnvironment[SchemeExp] + with SaveLattice[SchemeExp]: type SchemeLattice = ModularSchemeLattice[?, ?, ?, ?, ?, ?, ?] given EncapsulatedArrayEncoder[SchemeLattice#Clo]() with @@ -50,9 +79,9 @@ trait SaveModularSchemeLattices extends SaveModularDomain with SaveAddr[SchemeEx val (key, value) = hMapPair value match { - case int: SchemeLattice#Int => writer.writeMember("int", int.i.toString()) - case bool: SchemeLattice#Bool => writer.writeMember("boolean", bool.b.toString()) - case str: SchemeLattice#Str => writer.writeMember("string", str.s.toString()) + case int: SchemeLattice#Int => writer.writeMember("int", int.i.asInstanceOf[Lattice[BigInt]]) + case bool: SchemeLattice#Bool => writer.writeMember("boolean", bool.b.asInstanceOf[Lattice[Boolean]]) + case str: SchemeLattice#Str => writer.writeMember("string", str.s.asInstanceOf[Lattice[String]]) case prim: SchemeLattice#Prim => writer.writeMember("primitive", prim.prims) case clo: SchemeLattice#Clo => writer.writeMember("closure", clo) case pointer: SchemeLattice#Pointer => writer.writeMember("pointer", pointer) From 77aaf8f47d95735fffa7ce8ed843cfbbda5619da Mon Sep 17 00:00:00 2001 From: Merlijn Date: Fri, 9 Feb 2024 09:43:15 +0100 Subject: [PATCH 26/97] fix(load): Using wrong decoder when decoding dependencies --- code/shared/src/main/scala/maf/save/load/Dependency.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/shared/src/main/scala/maf/save/load/Dependency.scala b/code/shared/src/main/scala/maf/save/load/Dependency.scala index a637496b1..098ffc835 100644 --- a/code/shared/src/main/scala/maf/save/load/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/load/Dependency.scala @@ -31,7 +31,7 @@ trait LoadDependency[Expr <: Expression] extends LoadMapToArray with LoadCompone def dependencyDecoders = Set[(String, Decoder[_ <: Dependency])]() given EncapsulatedDecoder[Dependency] with - override def decoder: AbstractDecoder = getDependencyKeyDecoder + override def decoder: AbstractDecoder = getDependencyDecoder override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Dependency = reader.readMembers(dependencyDecoders.toArray).value.get.get From 1ae25ad6f124e4098e08ae280c65734e4f951ac0 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Fri, 9 Feb 2024 09:44:43 +0100 Subject: [PATCH 27/97] feat(load): Load store --- .../main/scala/maf/save/load/Analysis.scala | 4 +- .../scala/maf/save/load/Encapsulated.scala | 13 +- .../src/main/scala/maf/save/load/Store.scala | 114 +++++++++++++++++- 3 files changed, 128 insertions(+), 3 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/load/Analysis.scala b/code/shared/src/main/scala/maf/save/load/Analysis.scala index 9aa93ba90..f3f14503d 100644 --- a/code/shared/src/main/scala/maf/save/load/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/load/Analysis.scala @@ -37,6 +37,8 @@ trait LoadModF with LoadSchemeAddr with LoadDependency[SchemeExp] with LoadAddrDependency[SchemeExp] - with LoadStandardSchemeComponentID: + with LoadStandardSchemeComponentID + with LoadGlobalStore[SchemeExp] + with LoadModularSchemeLattices: def getDecoder: AbstractDecoder = new MapDecoder def getKeyDecoder: AbstractDecoder = new MapDecoder diff --git a/code/shared/src/main/scala/maf/save/load/Encapsulated.scala b/code/shared/src/main/scala/maf/save/load/Encapsulated.scala index a7634d33a..450331022 100644 --- a/code/shared/src/main/scala/maf/save/load/Encapsulated.scala +++ b/code/shared/src/main/scala/maf/save/load/Encapsulated.scala @@ -41,12 +41,20 @@ trait EncapsulatedDecoder[T] extends Decoder[T]: decoder.closeEncapsulation(reader, true, res) res protected def readEncapsulated(reader: Reader)(using AbstractDecoder): T +trait EncapsulatedArrayDecoder[T](length: Int = 0) extends EncapsulatedDecoder[T]: + override def read(reader: Reader): T = + if length == 0 then reader.readArrayStart() else reader.readArrayOpen(length) + val res = readEncapsulated(reader)(using decoder) + reader.readArrayClose(true, res) object EncapsulatedDecoder: extension (reader: Reader) def readMember[T: Decoder](key: String)(using decoder: AbstractDecoder): Future[T] = decoder.readKey(reader, key) decoder.readValue[T](reader) + def readMember[T: Decoder]()(using decoder: AbstractDecoder): Future[T] = + decoder.readKey(reader) + decoder.readValue[T](reader) def readMembers[T](keys: Array[(String, Decoder[_ <: T])])(using decoder: AbstractDecoder): Future[T] = val promise = Promise[T]() for (key, valueDecoder) <- keys do @@ -60,7 +68,10 @@ object EncapsulatedDecoder: case Failure(e) => promise.failure(e.asInstanceOf[Throwable]) } return promise.future - + def readUntilBeforeBreak[T](zero: T)(f: T => T): T = + var res = zero + while !reader.hasBreak do res = f(res) + return res def getMember[T](key: String)(using decoder: AbstractDecoder): T = decoder.getValue[T](key) class MapDecoder extends AbstractDecoder: diff --git a/code/shared/src/main/scala/maf/save/load/Store.scala b/code/shared/src/main/scala/maf/save/load/Store.scala index 7803fc329..5c0710e36 100644 --- a/code/shared/src/main/scala/maf/save/load/Store.scala +++ b/code/shared/src/main/scala/maf/save/load/Store.scala @@ -2,5 +2,117 @@ package maf.save import maf.core.Expression import maf.modular.AbstractDomain +import maf.modular.GlobalStore +import io.bullet.borer.Decoder +import maf.modular.scheme.ModularSchemeDomain +import maf.language.scheme.SchemeExp +import maf.lattice.HMapKey +import maf.lattice.HMap +import io.bullet.borer.Reader +import maf.save.EncapsulatedDecoder.* +import maf.language.scheme.lattices.ModularSchemeLattice +import maf.lattice.Concrete +import maf.lattice.ConstantPropagation +import maf.language.scheme.lattices.SchemeLattice +import maf.language.scheme.SchemeLambdaExp +import maf.core.Address -trait LoadValue[Expr <: Expression] extends Save[Expr] with AbstractDomain[Expr] +trait LoadValue[Expr <: Expression] extends Load[Expr] with AbstractDomain[Expr]: + def getValueDecoder: AbstractDecoder = getDecoder + def getValueKeyDecoder: AbstractDecoder = getKeyDecoder + given valueDecoder: Decoder[Value] + +trait LoadLattice[Expr <: Expression] extends Load[Expr]: + type Lattice[T] = ConstantPropagation.L[T] | Concrete.L[T] + + def getLatticeDecoder: AbstractDecoder = getDecoder + def getLatticeKeyDecoder: AbstractDecoder = getKeyDecoder + def latticeDecoders[T: Decoder] = Set[(String, Decoder[_ <: Lattice[T]])](("constant", constantLatticeDecoder[T])) + + given latticeDecoder[P[T] <: Lattice[T], T: Decoder]: EncapsulatedDecoder[P[T]] with + override def decoder: AbstractDecoder = getLatticeKeyDecoder + override protected def readEncapsulated(reader: Reader)(using d: AbstractDecoder): P[T] = + reader.readMembers[P[T]](latticeDecoders.toArray.asInstanceOf[Array[(String, io.bullet.borer.Decoder[? <: P[T]])]]).value.get.get + + given constantLatticeDecoder[T: Decoder]: Decoder[ConstantPropagation.L[T]] with + override def read(reader: Reader): ConstantPropagation.L[T] = + if reader.tryReadString("top") then return ConstantPropagation.Top + else if reader.tryReadString("bottom") then ConstantPropagation.Bottom + else return new ConstantPropagation.Constant[T](reader.read[T]()) + +trait LoadModularSchemeLattices + extends LoadModularDomain + with LoadAddr[SchemeExp] + with LoadStandardSchemeComponents + with LoadEnvironment[SchemeExp] + with LoadLattice[SchemeExp]: + type SchemeLattice = ModularSchemeLattice[?, ?, ?, ?, ?, ?, ?] + override def hMapDecoders = super.hMapDecoders + ( + ("int", summon[Decoder[(HMapKey, SchemeLattice#Int)]]), + ("boolean", summon[Decoder[(HMapKey, SchemeLattice#Bool)]]), + ("string", summon[Decoder[(HMapKey, SchemeLattice#Str)]]), + ("primitive", summon[Decoder[(HMapKey, SchemeLattice#Prim)]]), + ("closure", summon[Decoder[(HMapKey, SchemeLattice#Clo)]]), + ("pointer", summon[Decoder[(HMapKey, SchemeLattice#Pointer)]]) + ) + + given Decoder[(HMapKey, SchemeLattice#Int)] with + override def read(reader: Reader): (HMapKey, SchemeLattice#Int) = + val lattice = reader.read[Lattice[BigInt]]() + return (modularLattice.IntT, new modularLattice.Int(lattice.asInstanceOf[LoadModularSchemeLattices.this.modularLatticeWrapper.I])) + + given Decoder[(HMapKey, SchemeLattice#Bool)] with + override def read(reader: Reader): (HMapKey, SchemeLattice#Bool) = + val lattice = reader.read[Lattice[Boolean]]() + return (modularLattice.BoolT, new modularLattice.Bool(lattice.asInstanceOf[LoadModularSchemeLattices.this.modularLatticeWrapper.B])) + + given Decoder[(HMapKey, SchemeLattice#Str)] with + override def read(reader: Reader): (HMapKey, SchemeLattice#Str) = + val lattice = reader.read[Lattice[String]]() + return (modularLattice.StrT, new modularLattice.Str(lattice.asInstanceOf[LoadModularSchemeLattices.this.modularLatticeWrapper.S])) + + given Decoder[(HMapKey, SchemeLattice#Prim)] with + override def read(reader: Reader): (HMapKey, SchemeLattice#Prim) = + return (modularLattice.PrimT, new modularLattice.Prim(reader.read[Set[String]]())) + + given EncapsulatedDecoder[(SchemeLambdaExp, Env)] with + override def decoder: AbstractDecoder = getValueDecoder + override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): (SchemeLambdaExp, Env) = + val expression = reader.readMember[SchemeLambdaExp]("expression") + val address = reader.readMember[Env]("address") + return (expression.value.get.get, address.value.get.get) + + given EncapsulatedArrayDecoder[(HMapKey, SchemeLattice#Clo)]() with + override def decoder: AbstractDecoder = getValueDecoder + override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): (HMapKey, SchemeLattice#Clo) = + return (modularLattice.CloT, + new modularLattice.Clo( + reader.readUntilBeforeBreak[Set[(SchemeLambdaExp, Env)]](Set[(SchemeLambdaExp, Env)]())((closures) => + closures + (reader.read[(SchemeLambdaExp, Env)]()) + ) + ) + ) + + given EncapsulatedArrayDecoder[(HMapKey, SchemeLattice#Pointer)]() with + override def decoder: AbstractDecoder = getValueDecoder + override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): (HMapKey, SchemeLattice#Pointer) = + return (modularLattice.PointerT, + new modularLattice.Pointer(reader.readUntilBeforeBreak[Set[Address]](Set())((pointers) => pointers + (reader.read[Address]()))) + ) + +trait LoadModularDomain extends LoadValue[SchemeExp] with ModularSchemeDomain: + def getHMapDecoder: AbstractDecoder = new ArrayDecoder + def hMapDecoders = Set[(String, Decoder[_ <: (HMapKey, Any)])]() + + given EncapsulatedDecoder[(HMapKey, Any)] with + override def decoder: AbstractDecoder = getValueKeyDecoder + override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): (HMapKey, Any) = + reader.readMembers(hMapDecoders.toArray).value.get.get + + override given valueDecoder: EncapsulatedDecoder[HMap] with + override def decoder: AbstractDecoder = getHMapDecoder + override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): HMap = + return new HMap(reader.readUntilBeforeBreak[Map[HMapKey, Any]](Map())((hMap) => hMap + reader.readMember[(HMapKey, Any)]().value.get.get)) + +trait LoadGlobalStore[Expr <: Expression] extends LoadValue[Expr] with LoadAddr[Expr] with LoadMapToArray with GlobalStore[Expr]: + override def loadInfo: Map[String, Loadable[?]] = super.loadInfo + ("store" -> Loadable((store: Map[Addr, Value]) => println(store))) From f9dc35915dae613ce4963da846be339f2e89165e Mon Sep 17 00:00:00 2001 From: Merlijn Date: Fri, 9 Feb 2024 12:29:36 +0100 Subject: [PATCH 28/97] feat(load): Load everything into an analysis --- code/jvm/src/main/scala/maf/cli/runnables/Repl.scala | 5 ++++- code/shared/src/main/scala/maf/save/load/Component.scala | 6 ++---- code/shared/src/main/scala/maf/save/load/Dependency.scala | 2 +- code/shared/src/main/scala/maf/save/load/Store.scala | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/code/jvm/src/main/scala/maf/cli/runnables/Repl.scala b/code/jvm/src/main/scala/maf/cli/runnables/Repl.scala index 5d963d9c7..0504b27d2 100644 --- a/code/jvm/src/main/scala/maf/cli/runnables/Repl.scala +++ b/code/jvm/src/main/scala/maf/cli/runnables/Repl.scala @@ -198,7 +198,10 @@ object Repl: val exp = loader(filename) def runSingle(): Long = val anl = makeAnalysis(exp) - if loadFile.isDefined then anl.load(loadFile.get) + if loadFile.isDefined then + anl.load(loadFile.get) + print("loaded result: ") + anl.printResult val (elapsed, _) = Timer.time { anl.analyze() } //anl.analyzeWithTimeout(Timeout.start(timeout.seconds)) } // Do not print results if we are in perfomance testing mode if !performance then diff --git a/code/shared/src/main/scala/maf/save/load/Component.scala b/code/shared/src/main/scala/maf/save/load/Component.scala index 25f5e7730..85150113e 100644 --- a/code/shared/src/main/scala/maf/save/load/Component.scala +++ b/code/shared/src/main/scala/maf/save/load/Component.scala @@ -40,10 +40,8 @@ trait LoadComponents[Expr <: Expression] extends Load[Expr] with LoadComponentID def addComponent(component: Component): Unit override def loadInfo: Map[String, Loadable[_]] = super.loadInfo + ("components" -> Loadable((visited: Set[Component]) => - visited.foreach((component) => - addComponent(component) - if component != initialComponent then println(component.asInstanceOf[SchemeModFComponent.Call[_]].clo) - ) + visited.foreach((component) => addComponent(component)) + this.visited = visited )) given componentDecoder: Decoder[Component] diff --git a/code/shared/src/main/scala/maf/save/load/Dependency.scala b/code/shared/src/main/scala/maf/save/load/Dependency.scala index 098ffc835..b77825cc7 100644 --- a/code/shared/src/main/scala/maf/save/load/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/load/Dependency.scala @@ -27,7 +27,7 @@ trait LoadDependency[Expr <: Expression] extends LoadMapToArray with LoadCompone def getDependencyDecoder: AbstractDecoder = getDecoder def getDependencyKeyDecoder: AbstractDecoder = getKeyDecoder override def loadInfo: Map[String, Loadable[_]] = - super.loadInfo + ("dependencies" -> Loadable((deps: Map[Dependency, Set[Component]]) => deps.foreach(println(_)))) + super.loadInfo + ("dependencies" -> Loadable((deps: Map[Dependency, Set[Component]]) => this.deps = deps)) def dependencyDecoders = Set[(String, Decoder[_ <: Dependency])]() given EncapsulatedDecoder[Dependency] with diff --git a/code/shared/src/main/scala/maf/save/load/Store.scala b/code/shared/src/main/scala/maf/save/load/Store.scala index 5c0710e36..de04ed41e 100644 --- a/code/shared/src/main/scala/maf/save/load/Store.scala +++ b/code/shared/src/main/scala/maf/save/load/Store.scala @@ -115,4 +115,4 @@ trait LoadModularDomain extends LoadValue[SchemeExp] with ModularSchemeDomain: return new HMap(reader.readUntilBeforeBreak[Map[HMapKey, Any]](Map())((hMap) => hMap + reader.readMember[(HMapKey, Any)]().value.get.get)) trait LoadGlobalStore[Expr <: Expression] extends LoadValue[Expr] with LoadAddr[Expr] with LoadMapToArray with GlobalStore[Expr]: - override def loadInfo: Map[String, Loadable[?]] = super.loadInfo + ("store" -> Loadable((store: Map[Addr, Value]) => println(store))) + override def loadInfo = super.loadInfo + ("store" -> Loadable((store: Map[Addr, Value]) => this.store = store)) From f926be6e1eda94f4460887a634bf013112f7a471 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Fri, 9 Feb 2024 12:45:06 +0100 Subject: [PATCH 29/97] refactor(save): Add `SaveComponentID` This allows you to save the ID of components without specifying what kind of component you want to save. --- code/shared/src/main/scala/maf/save/save/Analysis.scala | 3 ++- code/shared/src/main/scala/maf/save/save/Component.scala | 5 ++++- code/shared/src/main/scala/maf/save/save/Dependency.scala | 5 ++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/save/Analysis.scala b/code/shared/src/main/scala/maf/save/save/Analysis.scala index 837b88d25..7fffeb8b5 100644 --- a/code/shared/src/main/scala/maf/save/save/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/save/Analysis.scala @@ -42,6 +42,7 @@ trait SaveModF with SaveSchemeAddr with SaveGlobalStore[SchemeExp] with SaveModularSchemeLattices - with SaveNoContext[SchemeExp]: + with SaveNoContext[SchemeExp] + with SaveStandardSchemeComponentID: override def getEncoder: AbstractEncoder = new MapEncoder override def getKeyEncoder: AbstractEncoder = new MapEncoder diff --git a/code/shared/src/main/scala/maf/save/save/Component.scala b/code/shared/src/main/scala/maf/save/save/Component.scala index 73b10a6e6..60af52556 100644 --- a/code/shared/src/main/scala/maf/save/save/Component.scala +++ b/code/shared/src/main/scala/maf/save/save/Component.scala @@ -55,7 +55,10 @@ trait SaveComponents[Expr <: Expression] extends Save[Expr]: given componentEncoder: Encoder[Component] -trait SaveStandardSchemeComponentID extends StandardSchemeModFComponents with SavePosition[SchemeExp]: +trait SaveComponentID[Expr <: Expression] extends SavePosition[Expr]: + given componentIDEncoder: Encoder[Component] + +trait SaveStandardSchemeComponentID extends SaveComponentID[SchemeExp] with StandardSchemeModFComponents: given componentIDEncoder: Encoder[Component] with def write(writer: Writer, component: Component): Writer = if component.equals(initialComponent) then writer.write("main") diff --git a/code/shared/src/main/scala/maf/save/save/Dependency.scala b/code/shared/src/main/scala/maf/save/save/Dependency.scala index 2bfc2cec8..8bd0f1dd7 100644 --- a/code/shared/src/main/scala/maf/save/save/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/save/Dependency.scala @@ -16,17 +16,16 @@ import maf.language.scheme.SchemeValue import maf.core.Identifier import maf.util.Writer.write -trait SaveAddrDep extends SaveDependency with SavePosition[SchemeExp] with SaveSchemeAddr: +trait SaveAddrDep extends SaveDependency[SchemeExp] with SavePosition[SchemeExp] with SaveSchemeAddr: override def encodeDependency(writer: Writer, dependency: Dependency)(using AbstractEncoder): Writer = dependency match { case AddrDependency(addr) => writer.writeMember("addrDependency", addr) case _ => super.encodeDependency(writer, dependency) } -trait SaveDependency extends SaveMapToArray with SaveStandardSchemeComponentID: +trait SaveDependency[Expr <: Expression] extends SaveMapToArray with SaveComponentID[Expr]: def getDependencyEncoder: AbstractEncoder = getEncoder override def saveInfo: Map[String, Savable[_]] = - import componentIDEncoder.given super.saveInfo + ("dependencies" -> Savable(deps)) def encodeDependency(writer: Writer, dependency: Dependency)(using AbstractEncoder): Writer = From e979a58af3230c1d4e12a295971c7b187d25e4b3 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Fri, 9 Feb 2024 18:11:42 +0100 Subject: [PATCH 30/97] refactor(load): Use class to store result instead of future --- .../main/scala/maf/save/load/Analysis.scala | 2 +- .../main/scala/maf/save/load/Component.scala | 10 +- .../main/scala/maf/save/load/Dependency.scala | 14 +- .../scala/maf/save/load/Encapsulated.scala | 126 +++++++++++------- .../src/main/scala/maf/save/load/Store.scala | 8 +- .../src/main/scala/maf/save/load/Util.scala | 4 +- 6 files changed, 96 insertions(+), 68 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/load/Analysis.scala b/code/shared/src/main/scala/maf/save/load/Analysis.scala index f3f14503d..cf02e7da3 100644 --- a/code/shared/src/main/scala/maf/save/load/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/load/Analysis.scala @@ -19,7 +19,7 @@ trait Load[Expr <: Expression] extends ModAnalysis[Expr]: given EncapsulatedDecoder[Load[Expr]] with override val decoder: AbstractDecoder = Load.this.getDecoder override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Load[Expr] = - for (key, value) <- loadInfo do value.load(reader.readMember(key)(using value.decoder, decoder).value.get.get) + for (key, value) <- loadInfo do value.load(reader.readMember(key)(using value.decoder, decoder).value) return Load.this override def load(filename: String): Unit = diff --git a/code/shared/src/main/scala/maf/save/load/Component.scala b/code/shared/src/main/scala/maf/save/load/Component.scala index 85150113e..30829c36c 100644 --- a/code/shared/src/main/scala/maf/save/load/Component.scala +++ b/code/shared/src/main/scala/maf/save/load/Component.scala @@ -66,7 +66,7 @@ trait LoadStandardSchemeComponents val lambda = reader.readMember[SchemeLambdaExp]("lambda") val environment = reader.readMember[Environment[Address]]("environment") val context = reader.readMember[Context]("context") - return new SchemeModFComponent.Call[Context]((lambda.value.get.get, environment.value.get.get), context.value.get.get) + return new SchemeModFComponent.Call[Context]((lambda.value, environment.value), context.value) private val compDecoder = getComponentDecoder given Decoder[SchemeFuncall] = AbstractDecoder.deriveDecoder[SchemeFuncall](compDecoder) given Decoder[SchemeVar] = AbstractDecoder.deriveDecoder[SchemeVar](compDecoder) @@ -85,7 +85,7 @@ trait LoadStandardSchemeComponents ("argLambda", summon[Decoder[SchemeVarArgLambda]]) ) ) - expression.value.get.get + expression.value trait LoadContext[Expr <: Expression] extends Load[Expr]: type Context @@ -129,8 +129,6 @@ trait LoadEnvironment[Expr <: Expression] extends Load[Expr] with LoadAddr[Expr] Array(("basicEnvironment", summon[Decoder[BasicEnvironment[T]]]), ("nestedEnv", summon[Decoder[NestedEnv[T, T]]])) ) .value - .get - .get given [T <: Address]: Decoder[BasicEnvironment[T]] with override def read(reader: Reader): BasicEnvironment[T] = return new BasicEnvironment( @@ -141,9 +139,7 @@ trait LoadEnvironment[Expr <: Expression] extends Load[Expr] with LoadAddr[Expr] override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): NestedEnv[T, K] = val content = reader.readMember[Map[String, Address]]("content") val rst = reader.readMember[Address]("rst") - return new NestedEnv(content.value.get.get.asInstanceOf[Map[String, T]], - if rst.isCompleted then Some(rst.value.get.get.asInstanceOf[K]) else None - ) + return new NestedEnv(content.value.asInstanceOf[Map[String, T]], if rst.hasValue then Some(rst.value.asInstanceOf[K]) else None) trait LoadComponentID[Expr <: Expression] extends LoadPosition[Expr]: var components = HashMap[Position, Component]() diff --git a/code/shared/src/main/scala/maf/save/load/Dependency.scala b/code/shared/src/main/scala/maf/save/load/Dependency.scala index b77825cc7..ecea03995 100644 --- a/code/shared/src/main/scala/maf/save/load/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/load/Dependency.scala @@ -33,7 +33,7 @@ trait LoadDependency[Expr <: Expression] extends LoadMapToArray with LoadCompone given EncapsulatedDecoder[Dependency] with override def decoder: AbstractDecoder = getDependencyDecoder override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Dependency = - reader.readMembers(dependencyDecoders.toArray).value.get.get + return reader.readMembers(dependencyDecoders.toArray).value trait LoadAddr[Expr <: Expression] extends Load[Expr] with LoadPosition[Expr]: def getAddressDecoder: AbstractDecoder = getDecoder @@ -43,7 +43,7 @@ trait LoadAddr[Expr <: Expression] extends Load[Expr] with LoadPosition[Expr]: given EncapsulatedDecoder[Address] with override def decoder: AbstractDecoder = getAddressKeyDecoder override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Address = - reader.readMembers(addressDecoders.toArray).value.get.get + reader.readMembers(addressDecoders.toArray).value trait LoadSchemeAddr extends LoadAddr[SchemeExp] with LoadContext[SchemeExp] with LoadComponentID[SchemeExp]: given Decoder[SchemeValue] = AbstractDecoder.deriveDecoder(getAddressDecoder) @@ -61,16 +61,14 @@ trait LoadSchemeAddr extends LoadAddr[SchemeExp] with LoadContext[SchemeExp] wit override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): ReturnAddr[Component] = val component = reader.readMember[Component]("component") val identity = reader.readMember[Identity]("identity") - return new ReturnAddr[Component](component.value.get.get, identity.value.get.get) + return new ReturnAddr[Component](component.value, identity.value) given EncapsulatedDecoder[VarAddr[Context]] with override def decoder: AbstractDecoder = getAddressDecoder override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): VarAddr[Context] = val name = reader.readMember[Identifier]("id") val context = reader.readMember[Context]("context") - return new VarAddr[Context](name.value.get.get, - if context.isCompleted then Some(context.value.get.get).asInstanceOf[Context] else None.asInstanceOf[Context] - ) + return new VarAddr[Context](name.value, if context.hasValue then Some(context.value).asInstanceOf[Context] else None.asInstanceOf[Context]) given Decoder[PrmAddr] with override def read(reader: Reader): PrmAddr = new PrmAddr(reader.read[String]()) @@ -80,6 +78,6 @@ trait LoadSchemeAddr extends LoadAddr[SchemeExp] with LoadContext[SchemeExp] wit override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): PtrAddr[Context] = val expression = reader.readMember[SchemeValue]("expression") val context = reader.readMember[Context]("context") - return new PtrAddr[Context](expression.value.get.get, - if context.isCompleted then Some(context.value.get.get).asInstanceOf[Context] else None.asInstanceOf[Context] + return new PtrAddr[Context](expression.value, + if context.hasValue then Some(context.value).asInstanceOf[Context] else None.asInstanceOf[Context] ) diff --git a/code/shared/src/main/scala/maf/save/load/Encapsulated.scala b/code/shared/src/main/scala/maf/save/load/Encapsulated.scala index 450331022..fbc919046 100644 --- a/code/shared/src/main/scala/maf/save/load/Encapsulated.scala +++ b/code/shared/src/main/scala/maf/save/load/Encapsulated.scala @@ -10,16 +10,50 @@ import scala.concurrent.Future import scala.concurrent.Promise import scala.util.{Failure, Success} import scala.concurrent.ExecutionContext.Implicits.global +import scala.reflect.ClassTag + +class ReadValue[K, V](): + protected var _key: Option[K] = None + protected var _value: Option[V] = None + protected var _updateValue: Option[ReadValue[K, V]] = None + + def this(_key: K, _value: V) = + this() + this._key = Some(_key) + this._value = Some(_value) + + def this(_key: K) = + this() + this._key = Some(_key) + + def hasValue: Boolean = _value.isDefined + def value: V = + if _value.isEmpty then throw new NoSuchElementException(s"The key '${key}' does not have a value.") + _value.get + def value_=(newValue: V): Unit = + _value = Some(newValue) + if _updateValue.isDefined then _updateValue.get.value = newValue + def hasKey: Boolean = _key.isDefined + + /** @throws t */ + def key: K = + if _key.isEmpty then throw new NoSuchElementException("This element does not have a key.") + _key.get + def key_=(newKey: K): Unit = + _key = Some(newKey) + if _updateValue.isDefined then _updateValue.get.key = newKey + def updateValue_=(newUpdateValue: ReadValue[K, V]) = _updateValue = Some(newUpdateValue) + def updateValue = _updateValue trait AbstractDecoder: protected val values = new HashMap[String, Any]() protected var currentKey: Option[String] = None val mapBasedDecoder: Boolean - def readKey(reader: Reader): Unit - def readKey(reader: Reader, key: String): Unit - def readValue[T: Decoder](reader: Reader): Future[T] + def readKey(reader: Reader): String + def readKey(reader: Reader, key: String): String + def readValue[T: Decoder](reader: Reader): ReadValue[String, T] def getValue[T](key: String): T = - if !values.contains(key) then throw new AbstractDecoder.KeyException("Key '" + key + "' is not found.") + if !values.contains(key) then throw new NoSuchElementException(s"The key '${key}' does not have a value.") values.get(key).get.asInstanceOf[T] def openEncapsulation(reader: Reader): Unit def openEncapsulation(reader: Reader, amount: Int): Unit @@ -30,7 +64,6 @@ object AbstractDecoder: if decoder.mapBasedDecoder then CompactMapBasedCodecs.deriveDecoder[T] else ArrayBasedCodecs.deriveDecoder[T] inline def deriveAllDecoders[T](decoder: AbstractDecoder): Decoder[T] = if decoder.mapBasedDecoder then CompactMapBasedCodecs.deriveAllDecoders[T] else ArrayBasedCodecs.deriveAllDecoders[T] - case class KeyException(msg: String) extends Exception(msg) trait EncapsulatedDecoder[T] extends Decoder[T]: def decoder: AbstractDecoder @@ -49,25 +82,20 @@ trait EncapsulatedArrayDecoder[T](length: Int = 0) extends EncapsulatedDecoder[T object EncapsulatedDecoder: extension (reader: Reader) - def readMember[T: Decoder](key: String)(using decoder: AbstractDecoder): Future[T] = + def readMember[T: Decoder](key: String)(using decoder: AbstractDecoder): ReadValue[String, T] = decoder.readKey(reader, key) decoder.readValue[T](reader) - def readMember[T: Decoder]()(using decoder: AbstractDecoder): Future[T] = + def readMember[T: Decoder]()(using decoder: AbstractDecoder): ReadValue[String, T] = decoder.readKey(reader) decoder.readValue[T](reader) - def readMembers[T](keys: Array[(String, Decoder[_ <: T])])(using decoder: AbstractDecoder): Future[T] = - val promise = Promise[T]() + def readMembers[T](keys: Array[(String, Decoder[_ <: T])])(using decoder: AbstractDecoder): ReadValue[String, T] = + val res = new ReadValue[String, T]() for (key, valueDecoder) <- keys do decoder.readKey(reader, key) - val value = decoder.readValue(reader)(using valueDecoder) - if value.isCompleted then - promise.complete(value.value.get) - return promise.future - value.onComplete { - case Success(value) => promise.success(value.asInstanceOf[T]) - case Failure(e) => promise.failure(e.asInstanceOf[Throwable]) - } - return promise.future + val value = decoder.readValue[T](reader)(using valueDecoder.asInstanceOf[Decoder[T]]) + if value.hasValue then return value + value.updateValue = res + return res def readUntilBeforeBreak[T](zero: T)(f: T => T): T = var res = zero while !reader.hasBreak do res = f(res) @@ -75,21 +103,23 @@ object EncapsulatedDecoder: def getMember[T](key: String)(using decoder: AbstractDecoder): T = decoder.getValue[T](key) class MapDecoder extends AbstractDecoder: - protected val keys = new HashMap[String, Decoder[_]]() + protected case class ValueDecoder[T](value: ReadValue[String, T], decoder: Decoder[T]) + protected val keys = new HashMap[String, ValueDecoder[_]]() protected var decodeKey: Option[String] = None override val mapBasedDecoder: Boolean = true protected def readDecodeKey(reader: Reader): Unit = if decodeKey.isEmpty && reader.hasString then decodeKey = Some(reader.readString()) - override def readKey(reader: Reader): Unit = + override def readKey(reader: Reader): String = readDecodeKey(reader) this.currentKey = decodeKey - override def readKey(reader: Reader, key: String): Unit = + return if decodeKey.isDefined then decodeKey.get else "" + override def readKey(reader: Reader, key: String): String = readDecodeKey(reader) this.currentKey = Some(key) - override def readValue[T: Decoder](reader: Reader): Future[T] = - if currentKey.isEmpty then throw new AbstractDecoder.KeyException("Trying to read a value before reading a key.") - val promise = Promise[T]() + return key + override def readValue[T: Decoder](reader: Reader): ReadValue[String, T] = + if currentKey.isEmpty then throw new IllegalStateException("Trying to read a value before reading a key.") if decodeKey == currentKey then val key = decodeKey.get @@ -98,14 +128,18 @@ class MapDecoder extends AbstractDecoder: val res = reader.read[T]() values.addOne((key, res)) if reader.hasString then decodeKey = Some(reader.readString()) - promise.success(res) if decodeKey.isDefined && keys.contains(decodeKey.get) then currentKey = decodeKey - val decoder = keys.remove(decodeKey.get).get - readValue(reader)(using decoder) - else keys.addOne((currentKey.get, summon[Decoder[T]])) - currentKey = None - promise.future + val valueDecoder = keys.remove(decodeKey.get).get + val res = readValue(reader)(using valueDecoder.decoder) + valueDecoder.value.value = res.value + currentKey = None + return new ReadValue[String, T](key, res) + else + val readValue = new ReadValue[String, T](currentKey.get) + keys.addOne((currentKey.get, ValueDecoder(readValue, summon[Decoder[T]]))) + currentKey = None + return readValue override def openEncapsulation(reader: Reader): Unit = reader.readMapStart() override def openEncapsulation(reader: Reader, amount: Int): Unit = reader.readMapOpen(amount) override def closeEncapsulation[T](reader: Reader, unbounded: Boolean, res: T): Unit = reader.readMapClose(unbounded, res) @@ -114,41 +148,39 @@ class ArrayDecoder extends AbstractDecoder: protected var id = -1 override val mapBasedDecoder: Boolean = false - override def readKey(reader: Reader): Unit = return - override def readKey(reader: Reader, key: String): Unit = + override def readKey(reader: Reader): String = return "" + override def readKey(reader: Reader, key: String): String = currentKey = Some(key) - return - override def readValue[T: Decoder](reader: Reader): Future[T] = + return key + override def readValue[T: Decoder](reader: Reader): ReadValue[String, T] = val key = if currentKey.isDefined then currentKey.get else id.toString() currentKey = None - val promise = Promise[T]() - if reader.hasBreak then return promise.future + if reader.hasBreak then throw reader.unexpectedDataItem(key) id += 1 val res = reader.read[T]() values.addOne(key, res) - promise.success(res) - promise.future + return ReadValue[String, T](key, res) override def openEncapsulation(reader: Reader): Unit = reader.readArrayStart() override def openEncapsulation(reader: Reader, amount: Int): Unit = reader.readArrayOpen(amount) override def closeEncapsulation[T](reader: Reader, unbounded: Boolean, res: T): Unit = reader.readMapClose(unbounded, res) class ArrayKeyDecoder extends MapDecoder: var key: Option[Any] = None - def readKey[T: Decoder](reader: Reader): Unit = - this.key = Some(reader.read[T]()) - def readKeyValue[T: Decoder](reader: Reader): Future[(Any, T)] = - if key.isEmpty then throw new AbstractDecoder.KeyException("Trying to read a value before reading a key.") - val promise = Promise[(Any, T)]() + def readKey[T: Decoder](reader: Reader): Any = + key = Some(reader.read[T]()) + key.get + def readKeyValue[V, T: Decoder](reader: Reader): ReadValue[V, T] = + if key.isEmpty then throw new IllegalStateException(s"Trying to read a value before reading a key.") val res = reader.read[T]() - promise.success((key.get, res)) + val tmpKey = key key = None - promise.future + return ReadValue(tmpKey.get.asInstanceOf[V], res) override def openEncapsulation(reader: Reader): Unit = reader.readArrayStart() override def openEncapsulation(reader: Reader, amount: Int): Unit = reader.readArrayOpen(amount) override def closeEncapsulation[T](reader: Reader, unbounded: Boolean, res: T): Unit = reader.readMapClose(unbounded, res) object ArrayKeyDecoder: extension (reader: Reader) - def readMember[K: Decoder, V: Decoder]()(using decoder: ArrayKeyDecoder): Future[(K, V)] = + def readMember[K: Decoder, V: Decoder]()(using decoder: ArrayKeyDecoder): ReadValue[K, V] = decoder.readKey[K](reader) - decoder.readKeyValue[V](reader).asInstanceOf[Future[(K, V)]] + decoder.readKeyValue[K, V](reader) diff --git a/code/shared/src/main/scala/maf/save/load/Store.scala b/code/shared/src/main/scala/maf/save/load/Store.scala index de04ed41e..b55b1c48f 100644 --- a/code/shared/src/main/scala/maf/save/load/Store.scala +++ b/code/shared/src/main/scala/maf/save/load/Store.scala @@ -32,7 +32,7 @@ trait LoadLattice[Expr <: Expression] extends Load[Expr]: given latticeDecoder[P[T] <: Lattice[T], T: Decoder]: EncapsulatedDecoder[P[T]] with override def decoder: AbstractDecoder = getLatticeKeyDecoder override protected def readEncapsulated(reader: Reader)(using d: AbstractDecoder): P[T] = - reader.readMembers[P[T]](latticeDecoders.toArray.asInstanceOf[Array[(String, io.bullet.borer.Decoder[? <: P[T]])]]).value.get.get + reader.readMembers[P[T]](latticeDecoders.toArray.asInstanceOf[Array[(String, io.bullet.borer.Decoder[? <: P[T]])]]).value given constantLatticeDecoder[T: Decoder]: Decoder[ConstantPropagation.L[T]] with override def read(reader: Reader): ConstantPropagation.L[T] = @@ -80,7 +80,7 @@ trait LoadModularSchemeLattices override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): (SchemeLambdaExp, Env) = val expression = reader.readMember[SchemeLambdaExp]("expression") val address = reader.readMember[Env]("address") - return (expression.value.get.get, address.value.get.get) + return (expression.value, address.value) given EncapsulatedArrayDecoder[(HMapKey, SchemeLattice#Clo)]() with override def decoder: AbstractDecoder = getValueDecoder @@ -107,12 +107,12 @@ trait LoadModularDomain extends LoadValue[SchemeExp] with ModularSchemeDomain: given EncapsulatedDecoder[(HMapKey, Any)] with override def decoder: AbstractDecoder = getValueKeyDecoder override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): (HMapKey, Any) = - reader.readMembers(hMapDecoders.toArray).value.get.get + reader.readMembers(hMapDecoders.toArray).value override given valueDecoder: EncapsulatedDecoder[HMap] with override def decoder: AbstractDecoder = getHMapDecoder override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): HMap = - return new HMap(reader.readUntilBeforeBreak[Map[HMapKey, Any]](Map())((hMap) => hMap + reader.readMember[(HMapKey, Any)]().value.get.get)) + return new HMap(reader.readUntilBeforeBreak[Map[HMapKey, Any]](Map())((hMap) => hMap + reader.readMember[(HMapKey, Any)]().value)) trait LoadGlobalStore[Expr <: Expression] extends LoadValue[Expr] with LoadAddr[Expr] with LoadMapToArray with GlobalStore[Expr]: override def loadInfo = super.loadInfo + ("store" -> Loadable((store: Map[Addr, Value]) => this.store = store)) diff --git a/code/shared/src/main/scala/maf/save/load/Util.scala b/code/shared/src/main/scala/maf/save/load/Util.scala index 105b3a73e..873c4e133 100644 --- a/code/shared/src/main/scala/maf/save/load/Util.scala +++ b/code/shared/src/main/scala/maf/save/load/Util.scala @@ -10,5 +10,7 @@ trait LoadMapToArray: override val decoder: ArrayKeyDecoder = new ArrayKeyDecoder override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Map[K, V] = val elements = mutable.Set[(K, V)]() - while !reader.hasBreak do elements.add(reader.readMember[K, V]()(using keyDecoder, valueDecoder, decoder).value.get.get) + while !reader.hasBreak do + val res = reader.readMember[K, V]()(using keyDecoder, valueDecoder, decoder) + elements.add(res.key, res.value) return elements.toMap From f13c632d5ad922023da7d738f24adb66ffbe8bbc Mon Sep 17 00:00:00 2001 From: Merlijn Date: Sat, 10 Feb 2024 14:11:11 +0100 Subject: [PATCH 31/97] docs(save): Add code documentation --- .../main/scala/maf/save/save/Analysis.scala | 59 ++- .../main/scala/maf/save/save/Component.scala | 128 +++++- .../main/scala/maf/save/save/Dependency.scala | 121 +++++- .../scala/maf/save/save/Encapsulated.scala | 390 +++++++++++++++++- .../src/main/scala/maf/save/save/Store.scala | 117 +++++- .../src/main/scala/maf/save/save/Util.scala | 30 ++ 6 files changed, 819 insertions(+), 26 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/save/Analysis.scala b/code/shared/src/main/scala/maf/save/save/Analysis.scala index 7fffeb8b5..881d673dc 100644 --- a/code/shared/src/main/scala/maf/save/save/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/save/Analysis.scala @@ -10,39 +10,82 @@ import java.nio.file.Files import maf.language.scheme.SchemeExp import EncapsulatedEncoder.* +/** + * Contains info about the top-level objects that need to be saved. + * + * @param value + * The value that needs to be saved + * @param encoder + * Encodes the value + * @tparam T + * The type of the value the needs to be saved + */ case class Savable[T](val value: T)(using val encoder: Encoder[T]) +/** + * The base trait for saving an analysis. + * + * Implementing this allows you to save your analysis, by default it will only save the name of your analysis and you should mixin other traits like + * [[SaveComponents]] to also save components. + * + * @tparam Expr + * The type of expression used in the analysis + */ trait Save[Expr <: Expression] extends ModAnalysis[Expr]: + /** + * Get the encoder that will be used to encode your analysis. + * + * This will influence how the analysis will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] + * encoder. + */ def getEncoder: AbstractEncoder + + /** + * Get the encoder that will be used to encode your analysis. + * + * This encoder is used to encode objects where the key is important, when you e.g. encode a type in the key, some encoders might remove this key, + * and should therefore not be used here. + * + * This will influence how the analysis will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] + * encoder. + */ def getKeyEncoder: AbstractEncoder + + /** Encode an analysis. */ given EncapsulatedEncoder[Save[Expr]] with override val encoder = Save.this.getEncoder override def writeEncapsulated(writer: Writer, value: Save[Expr]): Writer = for (key, value) <- saveInfo do writer.writeMember(key, value.value)(using value.encoder, encoder) writer - /** - * This saves the current analysis to a file - * - * @param filename - * The file to save to - */ override def save(filename: String): Unit = val res = Json.encode(this).toByteArray Files.write(Paths.get(filename), res) + /** + * Returns a map strings and [[Savable]] s. + * + * This map defines all top-level objects that should be saved in your analysis, and the key with which they should be saved. If you want to save + * something else, you can override this method and add something to it. + * + * {{{ + * override def saveInfo: Map[String, Savable[_]] = + * super.saveInfo + ("< key >" -> Savable(< saveValue >)) + * }}} + */ def saveInfo: Map[String, Savable[_]] = Map("name" -> Savable(analysisName)) +/** The trait used to save the modF analysis. */ trait SaveModF extends Save[SchemeExp] with SaveStandardSchemeComponents with SaveModularDomain - with SaveAddrDep + with SaveAddrDep[SchemeExp] with SaveSchemeAddr with SaveGlobalStore[SchemeExp] with SaveModularSchemeLattices with SaveNoContext[SchemeExp] - with SaveStandardSchemeComponentID: + with SaveStandardSchemeComponentPosition: override def getEncoder: AbstractEncoder = new MapEncoder override def getKeyEncoder: AbstractEncoder = new MapEncoder diff --git a/code/shared/src/main/scala/maf/save/save/Component.scala b/code/shared/src/main/scala/maf/save/save/Component.scala index 60af52556..82de5d8af 100644 --- a/code/shared/src/main/scala/maf/save/save/Component.scala +++ b/code/shared/src/main/scala/maf/save/save/Component.scala @@ -35,10 +35,22 @@ import maf.core.Position import maf.core.Position.PTag import scala.collection.immutable.HashMap +/** + * Trait to encode positions. + * + * @tparam Expr + * The type of expression used in the analysis + */ trait SavePosition[Expr <: Expression] extends Save[Expr]: + /** + * Get the encoder that will be used to encode positions. + * + * This will influence how positions will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] + * encoder. + */ def getPositionEncoder: AbstractEncoder = getEncoder - val posEncoder = getPositionEncoder + private val posEncoder = getPositionEncoder given Encoder[Position] = AbstractEncoder.deriveAllEncoders[Position](posEncoder) given Encoder[PTag] = AbstractEncoder.deriveAllEncoders[PTag](posEncoder) given Encoder[Identifier] = AbstractEncoder.deriveEncoder[Identifier](posEncoder) @@ -48,29 +60,104 @@ trait SavePosition[Expr <: Expression] extends Save[Expr]: System.err.nn.println("IdentityData could not be encoded") writer +/** + * The base trait for encoding components. + * + * @note + * This trait gives the methods needed to encode components, but does not implement them yet, other traits like [[SaveStandardSchemeComponents]] or + * [[SaveStandardSchemeComponentID]] should be mixed in for the implementation. The trait that should be mixed in depends on the kind of components + * are used in your analysis. + * + * @tparam Expr + * The type of expression used in the analysis + */ trait SaveComponents[Expr <: Expression] extends Save[Expr]: + /** + * Get the encoder that will be used to encode components. + * + * This will influence how components will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] + * encoder. + */ def getComponentEncoder: AbstractEncoder = getEncoder + + /** + * Get the encoder that will be used to encode components. + * + * This encoder is used to encode objects where the key is important, when you e.g. encode a type in the key, some encoders might remove this key, + * and should therefore not be used here. + * + * This will influence how components will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] + * encoder. + */ def getComponentKeyEncoder: AbstractEncoder = getKeyEncoder override def saveInfo: Map[String, Savable[_]] = super.saveInfo + ("components" -> Savable(visited)) + /** Encodes a component. */ given componentEncoder: Encoder[Component] +/** + * Base trait for encoding components only by their ID, and not in their entirety. + * + * @note + * This trait gives the methods needed to encode components using their ID, instead of saving it entirely, but no implementation. Other traits like + * [[SaveStandardSchemeComponentID]] should be mixed in to give the implementation based on what ID you want to use and what components your + * analysis uses. + * + * @note + * Because this trait only encodes the component IDs, the entire component should be encoded somewhere else if you want to decode this again. + * + * @tparam T + * The type of the value the needs to be saved + */ trait SaveComponentID[Expr <: Expression] extends SavePosition[Expr]: + /** Encodes a component by their ID */ given componentIDEncoder: Encoder[Component] -trait SaveStandardSchemeComponentID extends SaveComponentID[SchemeExp] with StandardSchemeModFComponents: - given componentIDEncoder: Encoder[Component] with +/** + * Trait that encodes components using their position. + * + * Implementation of [[SaveComponentID]] + * + * @note + * Because this trait only encodes the component position, the entire component should be encoded somewhere else if you want to decode this again. + */ +trait SaveStandardSchemeComponentPosition extends SaveComponentID[SchemeExp] with StandardSchemeModFComponents: + /** Encodes a component by their position */ + override given componentIDEncoder: Encoder[Component] with def write(writer: Writer, component: Component): Writer = if component.equals(initialComponent) then writer.write("main") else writer.write(component.asInstanceOf[SchemeModFComponent.Call[ComponentContext]])(schemeComponentIDEncoder) + /** Encodes a scheme component using their position */ given schemeComponentIDEncoder[T]: Encoder[SchemeModFComponent.Call[T]] with def write(writer: Writer, component: SchemeModFComponent.Call[T]): Writer = val (lambda, _) = component.clo writer.write(lambda.idn.pos) +/** + * Trait to encode environments. + * + * @tparam T + * The type of the value the needs to be saved + */ trait SaveEnvironment[Expr <: Expression] extends Save[Expr] with SaveAddr[Expr]: + /** + * Get the encoder that will be used to encode environments. + * + * This will influence how environments will be encodes, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] + * encoder. + */ def getEnvironmentEncoder: AbstractEncoder = getEncoder + + /** + * Get the encoder that will be used to encode environments. + * + * This encoder is used to encode objects where the key is important, when you e.g. encode a type in the key, some encoders might remove this key, + * and should therefore not be used here. + * + * This will influence how environments will be encodes, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] + * encoder. + */ def getEnvironmentKeyEncoder: AbstractEncoder = getKeyEncoder given Encoder[BasicEnvironment[Address]] with override def write(writer: Writer, env: BasicEnvironment[Address]): Writer = writer.write(env.content) @@ -95,16 +182,49 @@ trait SaveEnvironment[Expr <: Expression] extends Save[Expr] with SaveAddr[Expr] writer } +/** + * Base trait for saving context. + * + * @note + * This trait gives the methods needed to encode context, but not the implementation. Other traits like [[SaveNoContext]] should be mixed in. The + * exact trait that is mixed in depends on the Context that you are using in your analysis. + * + * @tparam T + * The type of the value the needs to be saved + */ trait SaveContext[Expr <: Expression] extends Save[Expr]: + /** The type of context that should be encoded. */ type Context + + /** + * Get the encoder that will be used to encode context. + * + * This will influence how context will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] + * encoder. + */ def getContextEncoder: AbstractEncoder = getEncoder + + /** Encodes context */ given contextEncoder: Encoder[Context] +/** + * Trait to encode the context for an analysis with no context. + * + * This will just write 'ε' when asked to write the context. + * + * @tparam T + * The type of the value the needs to be saved + */ trait SaveNoContext[Expr <: Expression] extends SaveContext[Expr]: - type Context = NoContext.type + override type Context = NoContext.type override given contextEncoder: Encoder[Context] with override def write(writer: Writer, context: Context): Writer = writer.write("ε") +/** + * Trait to encode standard scheme components. + * + * This is an implementation of [[SaveComponents]]. + */ trait SaveStandardSchemeComponents extends SaveComponents[SchemeExp] with StandardSchemeModFComponents diff --git a/code/shared/src/main/scala/maf/save/save/Dependency.scala b/code/shared/src/main/scala/maf/save/save/Dependency.scala index 8bd0f1dd7..29754245a 100644 --- a/code/shared/src/main/scala/maf/save/save/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/save/Dependency.scala @@ -16,29 +16,133 @@ import maf.language.scheme.SchemeValue import maf.core.Identifier import maf.util.Writer.write -trait SaveAddrDep extends SaveDependency[SchemeExp] with SavePosition[SchemeExp] with SaveSchemeAddr: - override def encodeDependency(writer: Writer, dependency: Dependency)(using AbstractEncoder): Writer = +/** + * Trait to encode address dependencies. + * + * This is an implementation of [[SaveDependency]]. + * + * @tparam Expr + * The type of expression used in the analysis + */ +trait SaveAddrDep[Expr <: Expression] extends SaveDependency[Expr] with SavePosition[Expr] with SaveAddr[Expr]: + override protected def encodeDependency(writer: Writer, dependency: Dependency)(using AbstractEncoder): Writer = dependency match { case AddrDependency(addr) => writer.writeMember("addrDependency", addr) case _ => super.encodeDependency(writer, dependency) } +/** + * Base trait for encoding dependencies. + * + * @note + * This trait gives the methods needed to encode dependencies, but not the implementation. Other traits like [[SaveAddrDep]] should be mixed in. The + * exact trait that is mixed in depends on the dependencies that you are using in your analysis. + * + * @tparam Expr + * The type of expression used in the analysis + */ trait SaveDependency[Expr <: Expression] extends SaveMapToArray with SaveComponentID[Expr]: + /** + * Get the encoder that will be used to encode dependencies. + * + * This will influence how dependencies will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] + * encoder. + */ def getDependencyEncoder: AbstractEncoder = getEncoder override def saveInfo: Map[String, Savable[_]] = super.saveInfo + ("dependencies" -> Savable(deps)) - def encodeDependency(writer: Writer, dependency: Dependency)(using AbstractEncoder): Writer = + /** + * Encodes a dependency. + * + * This method allows for expanding the dependencies that can be encoded by overriding it, and allowing you to add new dependencies by simply + * mixin in another trait that overrides this method. If you want to add a new encodable dependency you can override this method like this: + * {{{ + * override def encodeDependency(writer: Writer, dependency: Dependency)(using AbstractEncoder): Writer = + * dependency match { + * case < Dependency >(...) => < encode dependency > + * case _ => super.encodeDependency(writer, dependency) + * } + * }}} + * This is just an example and the actual implementation can also be done differently. + * + * @note + * This method should not be called directly, but should instead only be called from an encoder. + * + * @param writer + * The writer to write to + * @param dependency + * The dependency to encode + * @param encoder + * Implicit argument that encodes the dependency + * @return + * The used writer + */ + protected def encodeDependency(writer: Writer, dependency: Dependency)(using AbstractEncoder): Writer = System.err.nn.println("The dependency with type `" + dependency.getClass + "` could not be encoded") writer + given EncapsulatedEncoder[Dependency] with override val encoder: AbstractEncoder = getDependencyEncoder override protected def writeEncapsulated(writer: Writer, value: Dependency): Writer = encodeDependency(writer, value) +/** + * Base trait for encoding addresses. + * + * @note + * This trait gives the methods needed to encode addresses, but not the implementation. Other traits like [[SaveSchemeAddr]] should be mixed in. The + * exact trait that is mixed in depends on the addresses that you are using in your analysis. + * + * @tparam Expr + * The type of expression used in the analysis + */ trait SaveAddr[Expr <: Expression] extends Save[Expr] with SavePosition[Expr]: + /** + * Get the encoder that will be used to encode addresses. + * + * This will influence how addresses will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] + * encoder. + */ def getAddressEncoder: AbstractEncoder = getEncoder + + /** + * Get the encoder that will be used to encode addresses. + * + * This encoder is used to encode objects where the key is important, when you e.g. encode a type in the key, some encoders might remove this key, + * and should therefore not be used here. + * + * This will influence how addresses will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] + * encoder. + */ def getAddressKeyEncoder: AbstractEncoder = getKeyEncoder - def encodeAddress(writer: Writer, address: Address)(using encoder: AbstractEncoder): Writer = + + /** + * Encodes an address. + * + * This method allows for expanding the addresses that can be encoded by overriding it, and allowing you to add new addresses by simply mixin in + * another trait that overrides this method. If you want to add a new encodable address you can override this method like this: + * {{{ + * override def encodeAddress(writer: Writer, address: Address)(using AbstractEncoder): Writer = + * address match { + * case < Address >(...) => < encode address > + * case _ => super.encodeDependency(writer, address) + * } + * }}} + * This is just an example and the actual implementation can also be done differently. + * + * @note + * This method should not be called directly, but should instead only be called from an encoder. + * + * @param writer + * The writer to write to + * @param address + * The address to encode + * @param encoder + * Implicit argument that encodes the address + * @return + * The used writer + */ + protected def encodeAddress(writer: Writer, address: Address)(using encoder: AbstractEncoder): Writer = System.err.nn.println("The address with type `" + address.getClass + "` could not be encoded") writer @@ -46,7 +150,12 @@ trait SaveAddr[Expr <: Expression] extends Save[Expr] with SavePosition[Expr]: override val encoder = getAddressKeyEncoder override def writeEncapsulated(writer: Writer, value: Address): Writer = encodeAddress(writer, value) -trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveStandardSchemeComponentID with SaveContext[SchemeExp]: +/** + * Trait to encode scheme addresses. + * + * This is an implementation of [[SaveAddr]]. + */ +trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveStandardSchemeComponentPosition with SaveContext[SchemeExp]: given Encoder[SchemeValue] = AbstractEncoder.deriveEncoder(getAddressEncoder) given Encoder[maf.language.sexp.Value] = AbstractEncoder.deriveAllEncoders(getAddressEncoder) @@ -72,7 +181,7 @@ trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveStandardSchemeComponen if address.ctx.asInstanceOf[Option[Context]].isDefined then writer.writeMember("context", address.ctx.asInstanceOf[Option[Context]].get) writer - override def encodeAddress(writer: Writer, address: Address)(using encoder: AbstractEncoder): Writer = + override protected def encodeAddress(writer: Writer, address: Address)(using encoder: AbstractEncoder): Writer = import componentIDEncoder.given address match { case varAddr @ VarAddr(_, _) => diff --git a/code/shared/src/main/scala/maf/save/save/Encapsulated.scala b/code/shared/src/main/scala/maf/save/save/Encapsulated.scala index a0a4f8bdf..5c04ddd19 100644 --- a/code/shared/src/main/scala/maf/save/save/Encapsulated.scala +++ b/code/shared/src/main/scala/maf/save/save/Encapsulated.scala @@ -11,62 +11,375 @@ import scala.concurrent.Promise import scala.util.{Failure, Success} import scala.concurrent.ExecutionContext.Implicits.global +/** + * Base trait for an encoder. + * + * This trait has methods for writing keys and values with a encoder to a given writer and depending on the implementation off this trait, this can + * either write your values to a map or an array. This allows you to encode your values in a certain way, but without knowing exactly how it will be + * stored (e.g. in a map or in an array). + * + * This trait also has method for opening and closing an encapsulation, this will open/close either a map or an array based on the implementation of + * this trait. + * + * @note + * This trait gives the methods needed to write values, but not the implementation. Other traits like [[MapEncoder]] or [[ArrayEncoder]] should be + * used depending on how you want your value to be encoded. + * + * @note + * Some encoders like [[ArrayEncoder]] will also not encode your key, because arrays do not require keys like maps do, if the key does need to be + * stored, you should use an encoder that writes them down like [[MapEncoder]] or [[ArrayKeyEncoder]] if you want to use an array. + */ trait AbstractEncoder: - val mapBasedEncoder: Boolean - val writeOpenKey = true + /** Does this encoder use maps or array to encode the data. */ + protected val mapBasedEncoder: Boolean + + /** + * Write a generated key to the given writer. + * + * @note + * This only writes a key and should therefore be followed up by either writing a value using [[writeValue]] or opening an map or array using + * [[openEncapsulation]]. + * + * @note + * In some encoders like [[ArrayEncoder]] this will not write anything as this encoder does not use keys. + * + * @note + * The key that is generated depends on the encoder that is used. + * + * @param writer + * The writer used write the key + * @return + * The used writer + */ def writeKey(writer: Writer): Writer + + /** + * Write the given key to the given writer. + * + * @note + * This only writes a key and should therefore be followed up by either writing a value using [[writeValue]] or opening an map or array using + * [[openEncapsulation]]. + * + * @note + * In some encoders like [[ArrayEncoder]] this will not write anything as this encoder does not use keys. + * + * @param writer + * The writer used write the key + * @param key + * The key to write + * @return + * The used writer + */ def writeKey(writer: Writer, key: String): Writer + + /** + * Write a given value to the given writer. + * + * @note + * This only writes a value and should therefore only be called after writing a key using [[writeKey]]. + * + * @param writer + * The writer used write the value + * @param value + * The value to write + * @return + * The used writer + */ def writeValue[T: Encoder](writer: Writer, value: T): Writer + + /** + * Opens either a new map or a new array, based on the encoder that is used. + * + * @note + * This only opens a new encapsulation and should therefore only be called after writing a key using [[writeKey]]. + * + * @param writer + * The writer used write the array/map + * @return + * The used writer + */ def openEncapsulation(writer: Writer): Writer + + /** + * Opens either a new map or a new array, based on the encoder that is used. + * + * @note + * This only opens a new encapsulation and should therefore only be called after writing a key using [[writeKey]]. + * + * @param writer + * The writer used write the array/map + * @param amount + * The amount of elements the map/array, this is only useful when using CBOR the length of maps and arrays is used in the encoding + * @return + * The used writer + */ def openEncapsulation(writer: Writer, amount: Int): Writer + + /** + * Closes the map/array, based on the encoder that is used. + * + * @note + * This only closes the encapsulation and should therefore only be called after having already opened an encapsulation using + * [[openEncapsulation]]. + * + * @param writer + * The writer used write the array/map + * @return + * The used writer + */ def closeEncapsulation(writer: Writer): Writer object AbstractEncoder: + /** + * Automatically derive an encoder for type T. + * + * This will derive an encoder for type T, this encoder will either be [[CompactMapBasedCodecs map-based]] or [[ArrayBasedCodecs array-based]] + * based on the type of the provided encoder. + * + * @note + * T must be a case class, enum, sealed abstract class or sealed trait + * + * @param encoder + * The type of encoder that should be used to encode T + * @tparam T + * The type to derive encoders for + */ inline def deriveEncoder[T](encoder: AbstractEncoder): Encoder[T] = if encoder.mapBasedEncoder then CompactMapBasedCodecs.deriveEncoder[T] else ArrayBasedCodecs.deriveEncoder[T] + + /** + * Automatically derive all encoders for type T. + * + * This will derive an encoder for the type T and all subtypes of type T, these encoders will either be [[CompactMapBasedCodecs map-based]] or + * [[ArrayBasedCodecs array-based]] based on the type of the provided encoder. + * + * @note + * T must be a case object, case class, sealed trait or sealed abstract class. + * + * @param encoder + * The type of encoder that should be used to encode T + * @tparam T + * The type to derive encoders for + */ inline def deriveAllEncoders[T](encoder: AbstractEncoder): Encoder[T] = if encoder.mapBasedEncoder then CompactMapBasedCodecs.deriveAllEncoders[T] else ArrayBasedCodecs.deriveAllEncoders[T] +/** + * Trait used to encode an instance of type T. + * + * This trait will write a value of type T but encapsulate it either in an array or a map, based on the encoder that is given. In order to implement + * this class, you should overwrite the [[encoder]] variable to decide on the type of encoder you want to use, and the [[writeEncapsulated]] method + * which will implement the actual writing of the value. + * + * {{{ + * given EncapsulatedEncoder[< T >] with + * override val encoder: AbstractEncoder = < getEncoder > + * override protected def writeEncapsulated(writer: Writer, value: < T >): Writer = < encode value > + * }}} + * + * @note + * Since this class already opens a map/array, you should not do this anymore unless you are writing a nested map/array. + * + * @example + * If you implement it as a given, you can use it implicitly in your code + * {{{ + * override protected def writeEncapsulated(writer: Writer, value: < ... >): Writer = + * ... + * // Will use the implicit Encoder[T] + * writer.writeMember[T]("< key >", < value >) + * ... + * }}} + * + * @tparam T + * The type to encode + */ trait EncapsulatedEncoder[T] extends Encoder[T]: + /** The encoder used to write this value, this will specify how this value will be written (e.g. in a map or in an array). */ val encoder: AbstractEncoder + + /** + * The encoder used to write this value, this will specify how this value will be written (e.g. in a map or in an array). + * + * This is an given because a lot of method require this encoder to be added implicitly, adding the implicit here, makes it that you don't have to + * specify this anymore. + */ protected given AbstractEncoder = encoder override def write(writer: Writer, value: T): Writer = encoder.openEncapsulation(writer) writeEncapsulated(writer, value) encoder.closeEncapsulation(writer) + + /** + * Write a given value encapsulated in a map/array based on the [[encoder]]. + * + * @note + * This should not be called directly, but only through the [[write]] method. + * + * @param writer + * The writer used to write the value + * @param value + * The value that should be written + */ protected def writeEncapsulated(writer: Writer, value: T): Writer +/** + * Trait used to encode an instance of type T. + * + * This trait will write a value of type T but encapsulate it either in an array. In order to implement this class, you should overwrite the + * [[encoder]] variable to decide on the type of encoder you want to use, and the [[writeEncapsulated]] method which will implement the actual writing + * of the value. + * + * {{{ + * given EncapsulatedEncoder[< T >] with + * override val encoder: AbstractEncoder = < getEncoder > + * override protected def writeEncapsulated(writer: Writer, value: < T >): Writer = < encode value > + * }}} + * + * @note + * Since this class already opens an array, you should not do this anymore unless you are writing a nested map/array. + * + * @note + * Using `writer.writeMember`, `writer.openEncapsulation`, ... can still use either a map or an array, it is only the top-level encapsulation that + * is an array. + * + * @example + * If you implement it as a given, you can use it implicitly in your code + * {{{ + * override protected def writeEncapsulated(writer: Writer, value: < ... >): Writer = + * ... + * // Will use the implicit Encoder[T] + * writer.writeMember[T]("< key >", < value >) + * ... + * }}} + * + * @tparam T + * The type to encode + */ trait EncapsulatedArrayEncoder[T](length: Int = 0) extends EncapsulatedEncoder[T]: override def write(writer: Writer, value: T): Writer = if length == 0 then writer.writeArrayStart() else writer.writeArrayOpen(length) writeEncapsulated(writer, value) writer.writeArrayClose() +/** + * Object with extension methods for [[borer.Writer]]. + * + * These extension methods should be used when using an [[EncapsulatedEncoder]]. + */ object EncapsulatedEncoder: extension (writer: Writer) + /** + * Close the encapsulation. + * + * This will close either a map or an array based on the encoder that is given. + * + * @param encoder + * Implicit argument that decides how to close the encapsulation + */ def close()(using encoder: AbstractEncoder): Writer = encoder.closeEncapsulation(writer) + + /** + * Write a key-value pair. + * + * This will write a key-value pair in either a map or an array, based on the encoder that is given. + * + * @param key + * The key to write + * @param value + * The value to write + * @param encoder + * Implicit argument that decides how to write the key-value pair + * @tparam T + * The type of the value that should be written, this type should have an encoder + */ def writeMember[T: Encoder](key: String, value: T)(using encoder: AbstractEncoder): Writer = encoder.writeKey(writer, key) encoder.writeValue(writer, value) + + /** + * Write a value. + * + * This will write a value in either a map or an array, based on the encoder that is given. Certain encoders like map-based encoders will + * create a key and write this first if this method is called. + * + * @param value + * The value to write + * @param encoder + * Implicit argument that decides how to write the value + * @tparam T + * The type of the value that should be written, this type should have an encoder + */ def writeMember[T: Encoder](value: T)(using encoder: AbstractEncoder): Writer = encoder.writeKey(writer) encoder.writeValue(writer, value) + + /** + * Open the encapsulation. + * + * This will open either a map or an array based on the encoder that is given. Certain encoders like map-based encoders will create a key and + * write this first if this method is called. + * + * @param encoder + * Implicit argument that decides how to open the encapsulation + */ def open()(using encoder: AbstractEncoder): Writer = - if encoder.writeOpenKey then encoder.writeKey(writer) + encoder.writeKey(writer) encoder.openEncapsulation(writer) + + /** + * Open the encapsulation with a key. + * + * This will open either a map or an array based on the encoder that is given with a given key. + * + * @param key + * The key to write + * @param encoder + * Implicit argument that decides how to open the encapsulation + */ def open(key: String)(using encoder: AbstractEncoder): Writer = encoder.writeKey(writer, key) encoder.openEncapsulation(writer) + + /** + * Open the encapsulation. + * + * This will open either a map or an array based on the encoder that is given. Certain encoders like map-based encoders will create a key and + * write this first if this method is called. + * + * @param amount + * The amount of elements the map/array, this is only useful when using CBOR the length of maps and arrays is used in the encoding + * @param encoder + * Implicit argument that decides how to open the encapsulation + */ def open(amount: Int)(using encoder: AbstractEncoder): Writer = - if encoder.writeOpenKey then encoder.writeKey(writer) + encoder.writeKey(writer) encoder.openEncapsulation(writer, amount) + + /** + * Open the encapsulation with a key. + * + * This will open either a map or an array based on the encoder that is given with a given key. + * + * @param key + * The key to write + * @param amount + * The amount of elements the map/array, this is only useful when using CBOR the length of maps and arrays is used in the encoding + * @param encoder + * Implicit argument that decides how to open the encapsulation + */ def open(key: String, amount: Int)(using encoder: AbstractEncoder): Writer = encoder.writeKey(writer, key) encoder.openEncapsulation(writer) +/** + * Encoder that uses maps to encode values. + * + * This encoder uses maps to encode values and will therefore always use keys, if no key is provided, an auto-increasing ID will be used instead. + */ class MapEncoder extends AbstractEncoder: - val mapBasedEncoder = true + /** Used to generate IDs if no key is provided */ private var id = -1 + val mapBasedEncoder = true override def writeKey(writer: Writer): Writer = id += 1 writeKey(writer, id.toString()) @@ -76,8 +389,14 @@ class MapEncoder extends AbstractEncoder: override def openEncapsulation(writer: Writer, amount: Int): Writer = writer.writeMapOpen(amount) override def closeEncapsulation(writer: Writer): Writer = writer.writeMapClose() +/** + * Encoder that uses arrays to encode values. + * + * This encoder uses arrays to encode values and will not write any keys, if you want an array-based encoder that saves keys, you should use + * [[ArrayKeyEncoder]]. + */ class ArrayEncoder extends AbstractEncoder: - val mapBasedEncoder = false + override val mapBasedEncoder = false override def writeKey(writer: Writer): Writer = writer override def writeKey(writer: Writer, key: String): Writer = writer override def writeValue[T: Encoder](writer: Writer, value: T): Writer = writer.write(value) @@ -85,14 +404,71 @@ class ArrayEncoder extends AbstractEncoder: override def openEncapsulation(writer: Writer, amount: Int): Writer = writer.writeArrayOpen(amount) override def closeEncapsulation(writer: Writer): Writer = writer.writeArrayClose() +/** + * Encoder that uses arrays to encode values, but preserves keys. + * + * This encoder uses arrays to encode values but will save key-value pairs by first writing the key and then the value. This can be used, for example, + * if your keys are not strings. Since non-string keys are not supported in JSON, you cannot use a map for this, but this class does allow you to save + * your key-value pair in an intuitive way. + * + * This is how your key-value pair would be saved: + * {{{ + * [ + * < key1 >, + * < value1 >, + * < key2 >, + * < value2> , + * ... + * ] + * }}} + */ class ArrayKeyEncoder extends ArrayEncoder: - override val writeOpenKey: Boolean = false override def writeKey(writer: Writer, key: String): Writer = writer.write(key) + + /** + * Write the given key to the given writer. + * + * @note + * This only writes a key and should therefore be followed up by either writing a value using [[writeValue]] or opening an map or array using + * [[openEncapsulation]]. + * + * @note + * In some encoders like [[ArrayEncoder]] this will not write anything as this encoder does not use keys. + * + * @param writer + * The writer used write the key + * @param key + * The key to write + * @return + * The used writer + */ def writeKey[T: Encoder](writer: Writer, key: T): Writer = writer.write(key) override def writeValue[T: Encoder](writer: Writer, value: T): Writer = writer.write(value) +/** + * Object with an extension method for [[borer.Writer]]. + * + * This extension method allows you to write a key-value pair where the key is not a String, and should only be used when using an + * [[ArrayKeyEncoder]]. + */ object ArrayKeyEncoder: extension (writer: Writer) + /** + * Write a key-value pair. + * + * This will write a key-value pair in either a map or an array, based on the encoder that is given. + * + * @param key + * The key to write + * @param value + * The value to write + * @param encoder + * Implicit argument that decides how to write the key-value pair + * @tparam T + * The type of the key that should be written, this type should have an encoder + * @tparam U + * The type of the value that should be written, this type should have an encoder + */ def writeMember[T: Encoder, U: Encoder](key: T, value: U)(using encoder: ArrayKeyEncoder): Writer = encoder.writeKey(writer, key) encoder.writeValue(writer, value) diff --git a/code/shared/src/main/scala/maf/save/save/Store.scala b/code/shared/src/main/scala/maf/save/save/Store.scala index 82483a381..356f494f3 100644 --- a/code/shared/src/main/scala/maf/save/save/Store.scala +++ b/code/shared/src/main/scala/maf/save/save/Store.scala @@ -21,25 +21,80 @@ import maf.core.Environment import maf.lattice.{ConcreteLattice, ConstantPropagation} import maf.lattice.Concrete +/** + * Base trait for encoding [[AbstractDomain.Value values]]. + * + * @note + * This trait gives the methods needed to encode values, but not the implementation. Other traits like [[SaveModularDomain]] should be mixed in. The + * exact trait that is mixed in depends on the values that you are using in your analysis. + * + * @tparam Expr + * The type of expression used in the analysis + */ trait SaveValue[Expr <: Expression] extends Save[Expr] with AbstractDomain[Expr]: + /** + * Get the encoder that will be used to encode values. + * + * This will influence how values will be encoded, this can be e.g. a [[maf.save.MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] + * encoder. + */ def getValueEncoder: AbstractEncoder = getEncoder + + /** + * Get the encoder that will be used to encode values. + * + * This encoder is used to encode objects where the key is important, when you e.g. encode a type in the key, some encoders might remove this key, + * and should therefore not be used here. + * + * This will influence how values will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] encoder. + */ def getValueKeyEncoder: AbstractEncoder = getKeyEncoder + + /** Encodes a value */ given valueEncoder: Encoder[Value] +/** + * Trait to encode lattices. + * + * @tparam Expr + * The type of expression used in the analysis + */ trait SaveLattice[Expr <: Expression] extends Save[Expr]: + /** + * Get the encoder that will be used to encode lattices. + * + * This will influence how lattices will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] + * encoder. + */ def getLatticeEncoder: AbstractEncoder = getEncoder + + /** + * Get the encoder that will be used to encode lattices. + * + * This encoder is used to encode objects where the key is important, when you e.g. encode a type in the key, some encoders might remove this key, + * and should therefore not be used here. + * + * This will influence how lattices will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] + * encoder. + */ def getLatticeKeyEncoder: AbstractEncoder = getKeyEncoder + /** + * The types of lattices that can be encoded by this trait. + * + * This is used to specify the givens, if this was not used, this given could be used for every class with a single abstract type. + */ type Lattice[T] = ConstantPropagation.L[T] | Concrete.L[T] given latticeEncoder[P[T] <: Lattice[T], T: Encoder]: EncapsulatedEncoder[P[T]] with override val encoder: AbstractEncoder = getLatticeKeyEncoder override protected def writeEncapsulated(writer: Writer, lattice: P[T]): Writer = lattice match case constant: ConstantPropagation.L[T] => - writer.writeMember[ConstantPropagation.L[T]]("constant", constant)(using constantLatticeEncoder, encoder) + writer.writeMember("constant", constant)(using constantLatticeEncoder, encoder) case _ => System.err.nn.println("The lattice of type `" + lattice.getClass + "` could not be encoded") writer + /** Encodes [[ConstantPropagation.L constant lattices]]. */ given constantLatticeEncoder[T: Encoder]: Encoder[ConstantPropagation.L[T]] with override def write(writer: Writer, lattice: ConstantPropagation.L[T]): Writer = lattice match @@ -48,12 +103,18 @@ trait SaveLattice[Expr <: Expression] extends Save[Expr]: case ConstantPropagation.Bottom => writer.write("bottom") writer +/** + * Trait to encode [[ModularSchemeLattice modular scheme lattices]]. + * + * Implementation of [[SaveModularDomain]] + */ trait SaveModularSchemeLattices extends SaveModularDomain with SaveAddr[SchemeExp] with SaveStandardSchemeComponents with SaveEnvironment[SchemeExp] with SaveLattice[SchemeExp]: + /** Generic modular scheme lattice that is used for typechecking of nested class inside of this. */ type SchemeLattice = ModularSchemeLattice[?, ?, ?, ?, ?, ?, ?] given EncapsulatedArrayEncoder[SchemeLattice#Clo]() with @@ -94,8 +155,55 @@ trait SaveModularSchemeLattices if value.isInstanceOf[SchemeLattice#Value] then writer.writeMember((key, value.asInstanceOf[SchemeLattice#Value])) else return super.encodeHMapPair(writer, key, value) +/** + * Base trait for encoding values as [[ModularSchemeLattice modular scheme lattices]], as defined in [[ModularSchemeDomain]]. + * + * Implementation of [[SaveValue]]. + * + * @note + * This trait gives the methods needed to encode values, but not the implementation. Other traits like [[SaveModularSchemeLattices]] should be mixed + * in. The exact trait that is mixed in depends on the values that you are using in your analysis. + */ trait SaveModularDomain extends SaveValue[SchemeExp] with ModularSchemeDomain: + /** + * Get the encoder that will be used to encode an hMap. + * + * This will influence how an hMap will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] + * encoder. + * + * @note + * This is an [[ArrayEncoder array encoder]] by default + */ def getHMapEncoder: AbstractEncoder = new ArrayEncoder + + /** + * Encodes an hMap pair. + * + * This method allows for expanding the hMap pairs that can be encoded by overriding it, and allowing you to add new hMap pairs by simply mixin in + * another trait that overrides this method. If you want to add a new encodable hMap pair you can override this method like this: + * {{{ + * override def encodeHMapPair(writer: Writer, key: HMapKey, value: Any)(using AbstractEncoder): Writer = + * address match { + * case < ... >(...) => < encode hMap pair > + * case _ => super.encodeHMapPair(writer, key, value) + * } + * }}} + * This is just an example and the actual implementation can also be done differently. + * + * @note + * This method should not be called directly, but should instead only be called from an encoder. + * + * @param writer + * The writer to write to + * @param key + * The key used in the hMap + * @param value + * The value that is associated to the key + * @param encoder + * Implicit argument that encodes the hMap pair + * @return + * The used writer + */ def encodeHMapPair(writer: Writer, key: HMapKey, value: Any)(using encoder: AbstractEncoder): Writer = System.err.nn.println("The lattice with type `" + key.getClass + "` could not be encoded") writer @@ -106,6 +214,13 @@ trait SaveModularDomain extends SaveValue[SchemeExp] with ModularSchemeDomain: hmap.contents.foreach((key, value) => encodeHMapPair(writer, key, value)) writer +/** + * Trait to encode the global store. + * + * This adds the global store to the objects that should be saved, but does not have an implementation that can be used to encode the + * [[AbstractDomain.Value values]] inside of the store, for this an implementation of [[SaveValue]] like [[SaveModularDomain]] should be included + * depending on the values that are used in your analysis. + */ trait SaveGlobalStore[Expr <: Expression] extends SaveValue[Expr] with SaveAddr[Expr] with SaveMapToArray with GlobalStore[Expr]: override def saveInfo: Map[String, Savable[_]] = super.saveInfo + ("store" -> Savable(store)) diff --git a/code/shared/src/main/scala/maf/save/save/Util.scala b/code/shared/src/main/scala/maf/save/save/Util.scala index 8adc1269e..4c2d39606 100644 --- a/code/shared/src/main/scala/maf/save/save/Util.scala +++ b/code/shared/src/main/scala/maf/save/save/Util.scala @@ -4,7 +4,37 @@ import io.bullet.borer.Encoder import io.bullet.borer.Writer import ArrayKeyEncoder.* +/** + * Trait to encode a map using an array. + * + * This will save your map in an array with alternating keys and values: + * {{{ + * [ + * < key1 >, + * < value1 >, + * < key2 >, + * < value2 >, + * ... + * ] + * }}} + * This can, for example be used if the key is not a string, and can therefore not be used as a key of a JSON map. + */ trait SaveMapToArray: + /** + * Encodes a map using an array. + * + * This will save your map in an array with alternating keys and values: + * {{{ + * [ + * < key1 >, + * < value1 >, + * < key2 >, + * < value2 >, + * ... + * ] + * }}} + * This can, for example be used if the key is not a string, and can therefore not be used as a key of a JSON map. + */ given mapKeyEncoder[K, V](using keyEncoder: Encoder[K], valueEncoder: Encoder[V]): EncapsulatedEncoder[Map[K, V]] with override val encoder: ArrayKeyEncoder = new ArrayKeyEncoder override def writeEncapsulated(writer: Writer, map: Map[K, V]): Writer = From ed196049ba920c7b12cddce14c8fd0bb4838110f Mon Sep 17 00:00:00 2001 From: Merlijn Date: Sun, 11 Feb 2024 21:08:44 +0100 Subject: [PATCH 32/97] refactor(save, load): Change name of Context type `Context` was used in both `SaveContext` and `LoadContext`, this caused an issue when overriding them when both traits where mixed into the same class. --- .../main/scala/maf/save/load/Component.scala | 33 ++++++++++--------- .../main/scala/maf/save/load/Dependency.scala | 26 ++++++++------- .../main/scala/maf/save/save/Component.scala | 12 +++---- .../main/scala/maf/save/save/Dependency.scala | 24 +++++++------- 4 files changed, 51 insertions(+), 44 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/load/Component.scala b/code/shared/src/main/scala/maf/save/load/Component.scala index 30829c36c..ea7ff19d6 100644 --- a/code/shared/src/main/scala/maf/save/load/Component.scala +++ b/code/shared/src/main/scala/maf/save/load/Component.scala @@ -53,20 +53,21 @@ trait LoadStandardSchemeComponents with LoadPosition[SchemeExp] with LoadEnvironment[SchemeExp]: override def addComponent(component: SchemeModFComponent): Unit = - if component != initialComponent then components.addOne(component.asInstanceOf[SchemeModFComponent.Call[Context]].clo._1.idn.pos, component) + if component != initialComponent then + components.addOne(component.asInstanceOf[SchemeModFComponent.Call[DecodeContext]].clo._1.idn.pos, component) override given componentDecoder: Decoder[Component] with override def read(reader: Reader): Component = if reader.tryReadString("main") then return initialComponent - else reader.read[SchemeModFComponent.Call[Context]]() + else reader.read[SchemeModFComponent.Call[DecodeContext]]() - given EncapsulatedDecoder[SchemeModFComponent.Call[Context]] with + given EncapsulatedDecoder[SchemeModFComponent.Call[DecodeContext]] with override val decoder = getComponentDecoder - override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): SchemeModFComponent.Call[Context] = + override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): SchemeModFComponent.Call[DecodeContext] = val lambda = reader.readMember[SchemeLambdaExp]("lambda") val environment = reader.readMember[Environment[Address]]("environment") - val context = reader.readMember[Context]("context") - return new SchemeModFComponent.Call[Context]((lambda.value, environment.value), context.value) + val context = reader.readMember[DecodeContext]("context") + return new SchemeModFComponent.Call[DecodeContext]((lambda.value, environment.value), context.value) private val compDecoder = getComponentDecoder given Decoder[SchemeFuncall] = AbstractDecoder.deriveDecoder[SchemeFuncall](compDecoder) given Decoder[SchemeVar] = AbstractDecoder.deriveDecoder[SchemeVar](compDecoder) @@ -88,14 +89,14 @@ trait LoadStandardSchemeComponents expression.value trait LoadContext[Expr <: Expression] extends Load[Expr]: - type Context + type DecodeContext def getContextDecoder: AbstractDecoder = getDecoder - given contextDecoder: Decoder[Context] + given contextDecoder: Decoder[DecodeContext] trait LoadNoContext[Expr <: Expression] extends LoadContext[Expr]: - type Context = NoContext.type - override given contextDecoder: Decoder[Context] with - override def read(reader: Reader): Context = + override type DecodeContext = NoContext.type + override given contextDecoder: Decoder[DecodeContext] with + override def read(reader: Reader): DecodeContext = if !reader.tryReadString("ε") then return reader.unexpectedDataItem("ε") NoContext @@ -104,7 +105,7 @@ trait LoadPosition[Expr <: Expression] extends Load[Expr]: given Decoder[Position] = AbstractDecoder.deriveAllDecoders[Position](getPositionDecoder) given Decoder[PTag] = AbstractDecoder.deriveAllDecoders[PTag](getPositionDecoder) - val posDecoder = getPositionDecoder + private val posDecoder = getPositionDecoder given Decoder[Identifier] = AbstractDecoder.deriveDecoder[Identifier](posDecoder) given Decoder[Identity] = AbstractDecoder.deriveAllDecoders[Identity](posDecoder) given Decoder[IdentityData] with @@ -149,9 +150,9 @@ trait LoadStandardSchemeComponentID extends LoadComponentID[SchemeExp] with Load given componentIDDecoder: Decoder[Component] with override def read(reader: Reader): Component = if reader.tryReadString("main") then return initialComponent - else reader.read[SchemeModFComponent.Call[Context]]().asInstanceOf[Component] + else reader.read[SchemeModFComponent.Call[DecodeContext]]().asInstanceOf[Component] - given schemeComponentIDDecoder[T]: Decoder[SchemeModFComponent.Call[Context]] with - override def read(reader: Reader): SchemeModFComponent.Call[Context] = + given schemeComponentIDDecoder[T]: Decoder[SchemeModFComponent.Call[DecodeContext]] with + override def read(reader: Reader): SchemeModFComponent.Call[DecodeContext] = val pos = reader.read[Position]() - return components(pos).asInstanceOf[SchemeModFComponent.Call[Context]] + return components(pos).asInstanceOf[SchemeModFComponent.Call[DecodeContext]] diff --git a/code/shared/src/main/scala/maf/save/load/Dependency.scala b/code/shared/src/main/scala/maf/save/load/Dependency.scala index ecea03995..5614d2129 100644 --- a/code/shared/src/main/scala/maf/save/load/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/load/Dependency.scala @@ -50,10 +50,10 @@ trait LoadSchemeAddr extends LoadAddr[SchemeExp] with LoadContext[SchemeExp] wit given Decoder[maf.language.sexp.Value] = AbstractDecoder.deriveAllDecoders(getAddressDecoder) override def addressDecoders = super.addressDecoders ++ List( - ("varAddr", summon[Decoder[VarAddr[Context]]]), + ("varAddr", summon[Decoder[VarAddr[DecodeContext]]]), ("prmAddr", summon[Decoder[PrmAddr]]), ("returnAddr", summon[Decoder[ReturnAddr[Component]]]), - ("ptrAddr", summon[Decoder[PtrAddr[Context]]]) + ("ptrAddr", summon[Decoder[PtrAddr[DecodeContext]]]) ) given EncapsulatedDecoder[ReturnAddr[Component]] with @@ -63,21 +63,25 @@ trait LoadSchemeAddr extends LoadAddr[SchemeExp] with LoadContext[SchemeExp] wit val identity = reader.readMember[Identity]("identity") return new ReturnAddr[Component](component.value, identity.value) - given EncapsulatedDecoder[VarAddr[Context]] with + given EncapsulatedDecoder[VarAddr[DecodeContext]] with override def decoder: AbstractDecoder = getAddressDecoder - override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): VarAddr[Context] = + override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): VarAddr[DecodeContext] = val name = reader.readMember[Identifier]("id") - val context = reader.readMember[Context]("context") - return new VarAddr[Context](name.value, if context.hasValue then Some(context.value).asInstanceOf[Context] else None.asInstanceOf[Context]) + val context = reader.readMember[DecodeContext]("context") + return new VarAddr[DecodeContext]( + name.value, + if context.hasValue then Some(context.value).asInstanceOf[DecodeContext] else None.asInstanceOf[DecodeContext] + ) given Decoder[PrmAddr] with override def read(reader: Reader): PrmAddr = new PrmAddr(reader.read[String]()) - given EncapsulatedDecoder[PtrAddr[Context]] with + given EncapsulatedDecoder[PtrAddr[DecodeContext]] with override def decoder: AbstractDecoder = getAddressDecoder - override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): PtrAddr[Context] = + override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): PtrAddr[DecodeContext] = val expression = reader.readMember[SchemeValue]("expression") - val context = reader.readMember[Context]("context") - return new PtrAddr[Context](expression.value, - if context.hasValue then Some(context.value).asInstanceOf[Context] else None.asInstanceOf[Context] + val context = reader.readMember[DecodeContext]("context") + return new PtrAddr[DecodeContext]( + expression.value, + if context.hasValue then Some(context.value).asInstanceOf[DecodeContext] else None.asInstanceOf[DecodeContext] ) diff --git a/code/shared/src/main/scala/maf/save/save/Component.scala b/code/shared/src/main/scala/maf/save/save/Component.scala index 82de5d8af..66ab91105 100644 --- a/code/shared/src/main/scala/maf/save/save/Component.scala +++ b/code/shared/src/main/scala/maf/save/save/Component.scala @@ -194,7 +194,7 @@ trait SaveEnvironment[Expr <: Expression] extends Save[Expr] with SaveAddr[Expr] */ trait SaveContext[Expr <: Expression] extends Save[Expr]: /** The type of context that should be encoded. */ - type Context + type EncodeContext /** * Get the encoder that will be used to encode context. @@ -205,7 +205,7 @@ trait SaveContext[Expr <: Expression] extends Save[Expr]: def getContextEncoder: AbstractEncoder = getEncoder /** Encodes context */ - given contextEncoder: Encoder[Context] + given contextEncoder: Encoder[EncodeContext] /** * Trait to encode the context for an analysis with no context. @@ -216,9 +216,9 @@ trait SaveContext[Expr <: Expression] extends Save[Expr]: * The type of the value the needs to be saved */ trait SaveNoContext[Expr <: Expression] extends SaveContext[Expr]: - override type Context = NoContext.type - override given contextEncoder: Encoder[Context] with - override def write(writer: Writer, context: Context): Writer = writer.write("ε") + override type EncodeContext = NoContext.type + override given contextEncoder: Encoder[EncodeContext] with + override def write(writer: Writer, context: EncodeContext): Writer = writer.write("ε") /** * Trait to encode standard scheme components. @@ -265,4 +265,4 @@ trait SaveStandardSchemeComponents val context = component.ctx writer.writeMember("lambda", lambda) writer.writeMember("environment", env) - writer.writeMember("context", context.asInstanceOf[Context]) + writer.writeMember("context", context.asInstanceOf[EncodeContext]) diff --git a/code/shared/src/main/scala/maf/save/save/Dependency.scala b/code/shared/src/main/scala/maf/save/save/Dependency.scala index 29754245a..322792ffa 100644 --- a/code/shared/src/main/scala/maf/save/save/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/save/Dependency.scala @@ -159,38 +159,40 @@ trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveStandardSchemeComponen given Encoder[SchemeValue] = AbstractEncoder.deriveEncoder(getAddressEncoder) given Encoder[maf.language.sexp.Value] = AbstractEncoder.deriveAllEncoders(getAddressEncoder) - given EncapsulatedEncoder[VarAddr[Context]] with + given EncapsulatedEncoder[VarAddr[EncodeContext]] with override val encoder = getAddressEncoder - override def writeEncapsulated(writer: Writer, address: VarAddr[Context]): Writer = + override def writeEncapsulated(writer: Writer, address: VarAddr[EncodeContext]): Writer = import componentIDEncoder.given writer.writeMember("id", address.id) - if address.ctx.asInstanceOf[Option[Context]].isDefined then writer.writeMember("context", address.ctx.asInstanceOf[Option[Context]].get) + if address.ctx.asInstanceOf[Option[EncodeContext]].isDefined then + writer.writeMember("context", address.ctx.asInstanceOf[Option[EncodeContext]].get) writer - given EncapsulatedEncoder[ReturnAddr[Context]] with + given EncapsulatedEncoder[ReturnAddr[EncodeContext]] with override val encoder = getAddressEncoder - override def writeEncapsulated(writer: Writer, address: ReturnAddr[Context]): Writer = + override def writeEncapsulated(writer: Writer, address: ReturnAddr[EncodeContext]): Writer = import componentIDEncoder.given writer.writeMember("component", address.cmp.asInstanceOf[Component]) writer.writeMember("identity", address.idn) - given EncapsulatedEncoder[PtrAddr[Context]] with + given EncapsulatedEncoder[PtrAddr[EncodeContext]] with override val encoder = getAddressEncoder - override def writeEncapsulated(writer: Writer, address: PtrAddr[Context]): Writer = + override def writeEncapsulated(writer: Writer, address: PtrAddr[EncodeContext]): Writer = writer.writeMember("expression", address.exp.asInstanceOf[SchemeValue]) - if address.ctx.asInstanceOf[Option[Context]].isDefined then writer.writeMember("context", address.ctx.asInstanceOf[Option[Context]].get) + if address.ctx.asInstanceOf[Option[EncodeContext]].isDefined then + writer.writeMember("context", address.ctx.asInstanceOf[Option[EncodeContext]].get) writer override protected def encodeAddress(writer: Writer, address: Address)(using encoder: AbstractEncoder): Writer = import componentIDEncoder.given address match { case varAddr @ VarAddr(_, _) => - writer.writeMember("varAddr", varAddr.asInstanceOf[VarAddr[Context]]) + writer.writeMember("varAddr", varAddr.asInstanceOf[VarAddr[EncodeContext]]) case returnAddr @ ReturnAddr(_, _) => - writer.writeMember("returnAddr", returnAddr.asInstanceOf[ReturnAddr[Context]]) + writer.writeMember("returnAddr", returnAddr.asInstanceOf[ReturnAddr[EncodeContext]]) case PrmAddr(nam) => writer.writeMember("prmAddr", nam) case ptrAddr @ PtrAddr(_, _) => - writer.writeMember("ptrAddr", ptrAddr.asInstanceOf[PtrAddr[Context]]) + writer.writeMember("ptrAddr", ptrAddr.asInstanceOf[PtrAddr[EncodeContext]]) case _ => super.encodeAddress(writer, address) } From 922ba7345686986439d63dab1de1846637403c4e Mon Sep 17 00:00:00 2001 From: Merlijn Date: Sun, 11 Feb 2024 21:12:01 +0100 Subject: [PATCH 33/97] docs(load): Add code documentation --- .../main/scala/maf/save/load/Analysis.scala | 48 ++ .../main/scala/maf/save/load/Component.scala | 128 +++++ .../main/scala/maf/save/load/Dependency.scala | 69 +++ .../scala/maf/save/load/Encapsulated.scala | 495 +++++++++++++++++- .../src/main/scala/maf/save/load/Store.scala | 92 +++- .../src/main/scala/maf/save/load/Util.scala | 30 ++ 6 files changed, 850 insertions(+), 12 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/load/Analysis.scala b/code/shared/src/main/scala/maf/save/load/Analysis.scala index cf02e7da3..68beedfe9 100644 --- a/code/shared/src/main/scala/maf/save/load/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/load/Analysis.scala @@ -10,12 +10,48 @@ import maf.language.scheme.SchemeExp import io.bullet.borer.Reader import maf.save.EncapsulatedDecoder.* +/** + * Contains info about the top-level objects that need to be loaded. + * + * @param load + * Function that should be called with the value after it was decoded + * @param decoder + * Decodes the value + * @tparam T + * The type of the value the needs to be loaded + */ case class Loadable[T](val load: (T) => Unit)(using val decoder: Decoder[T]) +/** + * The base trait for saving an analysis. + * + * Implementing this allows you to load your analysis, by default it will only load the name of your analysis and you should mixin other traits like + * [[LoadComponents]] to also load components. + * + * @tparam Expr + * The type of expression used in the analysis + */ trait Load[Expr <: Expression] extends ModAnalysis[Expr]: + /** + * Get the decoder that will be used to decode your analysis. + * + * This will influence how the analysis will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] + * decoder. + */ def getDecoder: AbstractDecoder + + /** + * Get the decoder that will be used to decode your analysis. + * + * This decoder is used to decode objects where the key is important, when you want to e.g. decode a type from the key, some decoders might ignore + * this key, and should therefore not be used here. + * + * This will influence how the analysis will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] + * decoder. + */ def getKeyDecoder: AbstractDecoder + /** Decode an analysis. */ given EncapsulatedDecoder[Load[Expr]] with override val decoder: AbstractDecoder = Load.this.getDecoder override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Load[Expr] = @@ -26,9 +62,21 @@ trait Load[Expr <: Expression] extends ModAnalysis[Expr]: val bytes = Files.readAllBytes(Paths.get(filename)) if bytes != null then Json.decode(bytes).to[Load[Expr]].value + /** + * Returns a map strings and [[Loadable]] s. + * + * This map defines all top-level objects that should be loaded in your analysis, and the key with which they should be loaded. If you want to + * load something else, you can override this method and add something to it. + * + * {{{ + * override def loadInfo: Map[String, Loadable[_]] = + * super.loadInfo + ("< key >" -> Loadable[< loadType >]((< loaded object >: < SaveType >) => < put loaded object into analysis >)) + * }}} + */ def loadInfo: Map[String, Loadable[_]] = Map("name" -> Loadable((name: String) => println(name))) +/** The trait used to load the modF analysis. */ trait LoadModF extends Load[SchemeExp] with LoadComponents[SchemeExp] diff --git a/code/shared/src/main/scala/maf/save/load/Component.scala b/code/shared/src/main/scala/maf/save/load/Component.scala index ea7ff19d6..0d113099a 100644 --- a/code/shared/src/main/scala/maf/save/load/Component.scala +++ b/code/shared/src/main/scala/maf/save/load/Component.scala @@ -34,9 +34,43 @@ import maf.core.Position import maf.core.Position.PTag import scala.collection.mutable.HashMap +/** + * The base trait for decoding components. + * + * @note + * This trait gives the methods needed to decode components, but does not implement them yet, other traits like [[LoadStandardSchemeComponents]] or + * [[LoadStandardSchemeComponentID]] should be mixed in for the implementation. The trait that should be mixed in depends on the kind of components + * are used in your analysis. + * + * @tparam Expr + * The type of expression used in the analysis + */ trait LoadComponents[Expr <: Expression] extends Load[Expr] with LoadComponentID[Expr]: + /** + * Get the decoder that will be used to decode components. + * + * This will influence how components will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] + * decoder. + */ def getComponentDecoder: AbstractDecoder = getDecoder + + /** + * Get the decoder that will be used to decode components. + * + * This decoder is used to decode objects where the key is important, when you want to e.g. decode a type from the key, some decoders might ignore + * this key, and should therefore not be used here. + * + * This will influence how components will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] + * decoder. + */ def getComponentKeyDecoder: AbstractDecoder = getKeyDecoder + + /** + * Register a loaded component, this is necessary if you also want to load components by their ID. + * + * @param component + * The component to register + */ def addComponent(component: Component): Unit override def loadInfo: Map[String, Loadable[_]] = super.loadInfo + ("components" -> Loadable((visited: Set[Component]) => @@ -88,11 +122,47 @@ trait LoadStandardSchemeComponents ) expression.value +/** + * The base trait for decoding context. + * + * @note + * This trait gives the methods needed to decode context, but does not implement them yet, other traits like [[LoadNoContext]] should be mixed in + * for the implementation. The trait that should be mixed in depends on the kind of context that is used in your analysis. + * + * @tparam Expr + * The type of expression used in the analysis + */ trait LoadContext[Expr <: Expression] extends Load[Expr]: + /** The type of context that should be decoded. */ type DecodeContext + + /** + * Get the decoder that will be used to decode context. + * + * This will influence how context will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] + * decoder. + */ def getContextDecoder: AbstractDecoder = getDecoder + /** + * Get the decoder that will be used to decode context. + * + * This decoder is used to decode objects where the key is important, when you want to e.g. decode a type from the key, some decoders might ignore + * this key, and should therefore not be used here. + * + * This will influence how context will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] + * decoder. + */ given contextDecoder: Decoder[DecodeContext] + +/** + * Trait to decode the context for an analysis with no context. + * + * This will expect 'ε' when reading context. + * + * @tparam Expr + * The type of expression used in the analysis + */ trait LoadNoContext[Expr <: Expression] extends LoadContext[Expr]: override type DecodeContext = NoContext.type override given contextDecoder: Decoder[DecodeContext] with @@ -100,7 +170,19 @@ trait LoadNoContext[Expr <: Expression] extends LoadContext[Expr]: if !reader.tryReadString("ε") then return reader.unexpectedDataItem("ε") NoContext +/** + * Trait to decode positions. + * + * @tparam Expr + * The type of expression used in the analysis + */ trait LoadPosition[Expr <: Expression] extends Load[Expr]: + /** + * Get the decoder that will be used to decode positions. + * + * This will influence how positions will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] + * decoder. + */ def getPositionDecoder: AbstractDecoder = getDecoder given Decoder[Position] = AbstractDecoder.deriveAllDecoders[Position](getPositionDecoder) given Decoder[PTag] = AbstractDecoder.deriveAllDecoders[PTag](getPositionDecoder) @@ -119,8 +201,30 @@ trait LoadPosition[Expr <: Expression] extends Load[Expr]: System.err.nn.println("IdentityData could not be decoded") return IdnData +/** + * Trait to decode environments. + * + * @tparam Expr + * The type of expression used in the analysis + */ trait LoadEnvironment[Expr <: Expression] extends Load[Expr] with LoadAddr[Expr]: + /** + * Get the decoder that will be used to decode environments. + * + * This will influence how environments will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] + * decoder. + */ def getEnvironmentDecoder: AbstractDecoder = getDecoder + + /** + * Get the decoder that will be used to decode environments. + * + * This decoder is used to decode objects where the key is important, when you want to e.g. decode a type from the key, some decoders might ignore + * this key, and should therefore not be used here. + * + * This will influence how environments will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] + * decoder. + */ def getEnvironmentKeyDecoder: AbstractDecoder = getKeyDecoder given [T <: Address]: EncapsulatedDecoder[Environment[T]] with override def decoder: AbstractDecoder = getEnvironmentKeyDecoder @@ -142,10 +246,34 @@ trait LoadEnvironment[Expr <: Expression] extends Load[Expr] with LoadAddr[Expr] val rst = reader.readMember[Address]("rst") return new NestedEnv(content.value.asInstanceOf[Map[String, T]], if rst.hasValue then Some(rst.value.asInstanceOf[K]) else None) +/** + * The base trait for decoding components only by their ID. + * + * @note + * This trait gives the methods needed to decode context, but does not implement them yet, other traits like [[LoadStandardSchemeComponentID]] + * should be mixed in for the implementation. The trait that should be mixed in depends on the kind of context that is used in your analysis. + * + * @note + * Because this trait only decodes the component IDs, the entire component should have already been decoded and placed in [[components]], so the ID + * can be mapped to an actual component. + * + * @tparam Expr + * The type of expression used in the analysis + */ trait LoadComponentID[Expr <: Expression] extends LoadPosition[Expr]: + /** Map that connects component IDs to the component. */ var components = HashMap[Position, Component]() given componentIDDecoder: Decoder[Component] +/** + * Trait that decodes components using their position. + * + * Implementation of [[LoadComponentID]]. + * + * @note + * Because this trait only decodes the component positions, the entire component should have already been decoded and placed in [[components]], so + * the position can be mapped to an actual component. + */ trait LoadStandardSchemeComponentID extends LoadComponentID[SchemeExp] with LoadContext[SchemeExp]: given componentIDDecoder: Decoder[Component] with override def read(reader: Reader): Component = diff --git a/code/shared/src/main/scala/maf/save/load/Dependency.scala b/code/shared/src/main/scala/maf/save/load/Dependency.scala index 5614d2129..1adc0e0aa 100644 --- a/code/shared/src/main/scala/maf/save/load/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/load/Dependency.scala @@ -16,6 +16,14 @@ import maf.save.EncapsulatedDecoder.* import maf.core.Identifier import maf.core.Identity +/** + * Trait to decode address dependencies. + * + * Implementation of [[LoadDependency]]. + * + * @tparam Expr + * The type of expression used in the analysis + */ trait LoadAddrDependency[Expr <: Expression] extends LoadDependency[Expr] with LoadPosition[Expr] with LoadAddr[Expr]: given Decoder[AddrDependency] with override def read(reader: Reader): AddrDependency = @@ -23,11 +31,39 @@ trait LoadAddrDependency[Expr <: Expression] extends LoadDependency[Expr] with L return new AddrDependency(addr) override def dependencyDecoders = super.dependencyDecoders ++ Set(("addrDependency", summon[Decoder[AddrDependency]])) +/** + * The base trait for decoding dependencies. + * + * @note + * This trait gives the methods needed to decode dependencies, but does not implement them yet, other traits like [[LoadAddrDependency]] should be + * mixed in for the implementation. The trait that should be mixed in depends on the kind of dependencies that is used in your analysis. + * + * @tparam Expr + * The type of expression used in the analysis + */ trait LoadDependency[Expr <: Expression] extends LoadMapToArray with LoadComponentID[Expr]: + /** + * Get the decoder that will be used to decode dependencies. + * + * This will influence how dependencies will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] + * decoder. + */ def getDependencyDecoder: AbstractDecoder = getDecoder + + /** + * Get the decoder that will be used to decode dependencies. + * + * This decoder is used to decode objects where the key is important, when you want to e.g. decode a type from the key, some decoders might ignore + * this key, and should therefore not be used here. + * + * This will influence how dependencies will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] + * decoder. + */ def getDependencyKeyDecoder: AbstractDecoder = getKeyDecoder override def loadInfo: Map[String, Loadable[_]] = super.loadInfo + ("dependencies" -> Loadable((deps: Map[Dependency, Set[Component]]) => this.deps = deps)) + + /** Returns a map that links a key to a specific decoder. */ def dependencyDecoders = Set[(String, Decoder[_ <: Dependency])]() given EncapsulatedDecoder[Dependency] with @@ -35,9 +71,37 @@ trait LoadDependency[Expr <: Expression] extends LoadMapToArray with LoadCompone override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Dependency = return reader.readMembers(dependencyDecoders.toArray).value +/** + * The base trait for decoding addresses. + * + * @note + * This trait gives the methods needed to decode addresses, but does not implement them yet, other traits like [[LoadAddrDependency]] should be + * mixed in for the implementation. The trait that should be mixed in depends on the kind of addresses that is used in your analysis. + * + * @tparam Expr + * The type of expression used in the analysis + */ trait LoadAddr[Expr <: Expression] extends Load[Expr] with LoadPosition[Expr]: + /** + * Get the decoder that will be used to decode addresses. + * + * This will influence how addresses will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] + * decoder. + */ def getAddressDecoder: AbstractDecoder = getDecoder + + /** + * Get the decoder that will be used to decode addresses. + * + * This decoder is used to decode objects where the key is important, when you want to e.g. decode a type from the key, some decoders might ignore + * this key, and should therefore not be used here. + * + * This will influence how addresses will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] + * decoder. + */ def getAddressKeyDecoder: AbstractDecoder = getKeyDecoder + + /** Returns a map that links a key to a specific decoder. */ def addressDecoders = List[(String, Decoder[_ <: Address])]() given EncapsulatedDecoder[Address] with @@ -45,6 +109,11 @@ trait LoadAddr[Expr <: Expression] extends Load[Expr] with LoadPosition[Expr]: override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Address = reader.readMembers(addressDecoders.toArray).value +/** + * Trait to decode scheme addresses. + * + * This is an implementation of [[LoadAddr]]. + */ trait LoadSchemeAddr extends LoadAddr[SchemeExp] with LoadContext[SchemeExp] with LoadComponentID[SchemeExp]: given Decoder[SchemeValue] = AbstractDecoder.deriveDecoder(getAddressDecoder) given Decoder[maf.language.sexp.Value] = AbstractDecoder.deriveAllDecoders(getAddressDecoder) diff --git a/code/shared/src/main/scala/maf/save/load/Encapsulated.scala b/code/shared/src/main/scala/maf/save/load/Encapsulated.scala index fbc919046..9d60d5552 100644 --- a/code/shared/src/main/scala/maf/save/load/Encapsulated.scala +++ b/code/shared/src/main/scala/maf/save/load/Encapsulated.scala @@ -12,82 +12,439 @@ import scala.util.{Failure, Success} import scala.concurrent.ExecutionContext.Implicits.global import scala.reflect.ClassTag +/** + * A class representing a decoded key-value pair. + * + * This is a class that holds a key-value pair after decoding it, but because it it possible that the key that is should be decoded hasn't been + * encountered yet, it is possible that either the key or the value don't have a value yet. To check if they have a value you can use [[hasKey]] and + * [[hasValue]], if you try to retrieve the key or the value without one being present, an error will be thrown. + * + * @constructor + * Create a new key-value pair that doesn't have a key nor a value yet. + * + * @tparam K + * The type of the key + * @tparam V + * The type of the value + */ class ReadValue[K, V](): protected var _key: Option[K] = None protected var _value: Option[V] = None protected var _updateValue: Option[ReadValue[K, V]] = None + /** + * Create a new key-value pair with a key and a value. + * + * @param _key + * The key of the key-value pair + * @param _value + * The value of the key-value pair + */ def this(_key: K, _value: V) = this() this._key = Some(_key) this._value = Some(_value) + /** + * Create a new key-value pair with a key. + * + * @param _key + * The key of the key-value pair + */ def this(_key: K) = this() this._key = Some(_key) + /** Check if this has a value. */ def hasValue: Boolean = _value.isDefined + + /** + * Returns the value. + * + * @throws NoSuchElementException + * If there is no value + */ def value: V = - if _value.isEmpty then throw new NoSuchElementException(s"The key '${key}' does not have a value.") + if _value.isEmpty then + if _key.isDefined then throw new NoSuchElementException(s"The key '${key}' does not have a value.") + else throw new NoSuchElementException("This element does not have a value.") _value.get + + /** Set the value. */ def value_=(newValue: V): Unit = _value = Some(newValue) if _updateValue.isDefined then _updateValue.get.value = newValue + + /** Check if this has a key. */ def hasKey: Boolean = _key.isDefined - /** @throws t */ + /** + * Returns the key. + * + * @throws NoSuchElementException + * If there is no key + */ def key: K = if _key.isEmpty then throw new NoSuchElementException("This element does not have a key.") _key.get + + /** Set the key. */ def key_=(newKey: K): Unit = _key = Some(newKey) if _updateValue.isDefined then _updateValue.get.key = newKey + + /** Set the [[ReadValue]] that should have be updated if this [[ReadValue]] gets a key or a value */ def updateValue_=(newUpdateValue: ReadValue[K, V]) = _updateValue = Some(newUpdateValue) + + /** The [[ReadValue]] that will be updated when this [[ReadValue]] gets a key or a value. */ def updateValue = _updateValue +/** + * Base trait for an decoder. + * + * This trait has methods for reading keys and values with a decoder from a given reader and depending on the implementation off this trait, this can + * either read your values from a map or an array. This allows you to decode your values in a certain way, but without knowing exactly how it was + * stored (e.g. in a map or in an array). + * + * This trait also has method for opening and closing an encapsulation, this will open/close either a map or an array based on the implementation of + * this trait. + * + * @note + * This trait gives the methods needed to read values, but not the implementation. Other traits like [[MapDecoder]] or [[ArrayDecoder]] should be + * used depending on how you want your value to be decoded. + * + * @note + * Some decoders like [[ArrayDecoder]] will also not decode your key, because arrays do not require keys like maps do, if the key does need to be + * stored, you should use an decoder that writes them down like [[MapDecoder]] or [[ArrayKeyDecoder]] if you want to use an array. + */ trait AbstractDecoder: + /** The values that have already been decoded, with their key. */ protected val values = new HashMap[String, Any]() + + /** + * The key that should be read next. + * + * This is the key that is given through [[readKey]]. + */ protected var currentKey: Option[String] = None - val mapBasedDecoder: Boolean + + /** Does this decoder use maps or array to decode the data. */ + protected val mapBasedDecoder: Boolean + + /** + * Read a key from the given reader. + * + * @note + * This only reads a key and should therefore be followed up by reading a value using [[readValue]]. + * + * @note + * In some decoders like [[ArrayDecoder]] this will not read anything as this decoder does not use keys. + * + * @param reader + * The reader used read the key + * @return + * The key that was read. + */ def readKey(reader: Reader): String + + /** + * Read a given key from the given reader. + * + * This will read a given key, if this key is not yet readable because it only appears later in the file, the reading of this key will be delayed + * until it becomes available. + * + * @note + * This only reads a key and should therefore be followed up by reading a value using [[readValue]]. + * + * @note + * In some decoders like [[ArrayDecoder]] this will not read anything as this decoder does not use keys. + * + * @param reader + * The reader used read the key + * @param key + * The key that should be read + * @return + * The key that was read. + */ def readKey(reader: Reader, key: String): String + + /** + * Read a given value from the given reader. + * + * This will read a value of type T using the previously read key, if this key is not yet available because it appears later in the file, this + * will be delayed until it becomes available. In this case the returned object will not have a value yet, which will be filled in when the key + * becomes available. + * + * @note + * This only reads a value and should therefore only be called after reading a key using [[readKey]]. + * + * @note + * In some decoders like [[ArrayDecoder]] the value will always be read immediately because there are no keys. + * + * @param reader + * The reader used read the value + * @tparam T + * The type of value that should be decoded + * @returns + * The key-value pair that is read, if the key cannot be read yet because it appears later in the file, the value will be empty and will be + * filled in later. + * + * @throws IllegalStateException + * When you call this before calling [[readKey]] + */ def readValue[T: Decoder](reader: Reader): ReadValue[String, T] + + /** + * Returns an already read value using a given key. + * + * @throws NoSuchElementException + * If this key hasn't been read yet, and therefore doesn't have a value. + */ def getValue[T](key: String): T = if !values.contains(key) then throw new NoSuchElementException(s"The key '${key}' does not have a value.") values.get(key).get.asInstanceOf[T] + + /** + * Opens either a new map or a new array, based on the decoder that is used. + * + * @note + * This only opens a new encapsulation and should therefore only be called after reading a key using [[readKey]]. + * + * @param reader + * The reader used read the array/map + */ def openEncapsulation(reader: Reader): Unit + + /** + * Opens either a new map or a new array, based on the decoder that is used. + * + * @note + * This only opens a new encapsulation and should therefore only be called after reading a key using [[readKey]]. + * + * @param reader + * The reader used read the array/map + * @param amount + * The amount of elements the map/array, this is only useful when using CBOR the length of maps and arrays is used in the encoding + */ def openEncapsulation(reader: Reader, amount: Int): Unit + + /** + * Closes the map/array, based on the decoder that is used. + * + * @note + * This only closes the encapsulation and should therefore only be called after having already opened an encapsulation using + * [[openEncapsulation]]. + * + * @param reader + * The reader used read the array/map + * @param unbounded + * Wether the array/map had a fixed length or not + * @param res + * The object that was encoded inside of the array/map + * @tparam T + * The type of the object that was encoded inside of the array/map + */ def closeEncapsulation[T](reader: Reader, unbounded: Boolean, res: T): Unit object AbstractDecoder: + /** + * Automatically derive an decoder for type T. + * + * This will derive an decoder for type T, this decoder will either be [[CompactMapBasedCodecs map-based]] or [[ArrayBasedCodecs array-based]] + * based on the type of the provided decoder. + * + * @note + * T must be a case class, enum, sealed abstract class or sealed trait + * + * @param decoder + * The type of decoder that should be used to decode T + * @tparam T + * The type to derive decoders for + */ inline def deriveDecoder[T](decoder: AbstractDecoder): Decoder[T] = if decoder.mapBasedDecoder then CompactMapBasedCodecs.deriveDecoder[T] else ArrayBasedCodecs.deriveDecoder[T] + + /** + * Automatically derive all decoders for type T. + * + * This will derive an decoder for the type T and all subtypes of type T, these decoders will either be [[CompactMapBasedCodecs map-based]] or + * [[ArrayBasedCodecs array-based]] based on the type of the provided decoder. + * + * @note + * T must be a case object, case class, sealed trait or sealed abstract class. + * + * @param decoder + * The type of decoder that should be used to decode T + * @tparam T + * The type to derive decoders for + */ inline def deriveAllDecoders[T](decoder: AbstractDecoder): Decoder[T] = if decoder.mapBasedDecoder then CompactMapBasedCodecs.deriveAllDecoders[T] else ArrayBasedCodecs.deriveAllDecoders[T] +/** + * Trait used to decode an instance of type T. + * + * This trait will read a value of type T encapsulated in either an array or a map, based on the decoder that is given. In order to implement this + * class, you should overwrite the [[decoder]] variable to decide on the type of decoder you want to use, and the [[readEncapsulated]] method which + * will implement the actual reading of the value. + * + * {{{ + * given EncapsulatedDecoder[< T >] with + * override def decoder: AbstractDecoder = < getDecoder > + * override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): T = < decode value > + * }}} + * + * @note + * Since this class already opens a map/array, you should not do this anymore unless you are reading a nested map/array. + * + * @example + * If you implement it as a given, you can use it implicitly in your code + * {{{ + * override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): T = + * ... + * // Will use the implicit Decoder[T] + * reader.readMember[T]("< key >", < value >) + * ... + * }}} + * + * @tparam T + * The type to decode + */ trait EncapsulatedDecoder[T] extends Decoder[T]: + /** The decoder used to read this value, this will specify how this value will be written (e.g. in a map or in an array). */ def decoder: AbstractDecoder + + /** + * The decoder used to read this value, this will specify how this value will be written (e.g. in a map or in an array). + * + * This is an given because a lot of method require this decoder to be added implicitly, adding the implicit here, makes it that you don't have to + * specify this anymore. + */ protected given AbstractDecoder = decoder override def read(reader: Reader): T = decoder.openEncapsulation(reader) val res = readEncapsulated(reader)(using decoder) decoder.closeEncapsulation(reader, true, res) res + + /** + * Read a value encapsulated in a map/array based on the [[decoder]]. + * + * @note + * This should not be called directly, but only through the [[read]] method. + * + * @param reader + * The reader used to read the value + * @param decoder + * Implicit argument used to decode the value + * @returns + * The returned value + */ protected def readEncapsulated(reader: Reader)(using AbstractDecoder): T + +/** + * Trait used to decode an instance of type T. + * + * This trait will read a value of type T encapsulated in an array. In order to implement this class, you should overwrite the [[decoder]] variable to + * decide on the type of decoder you want to use, and the [[readEncapsulated]] method which will implement the actual reading of the value. + * + * {{{ + * given EncapsulatedDecoder[< T >] with + * override def decoder: AbstractDecoder = < getDecoder > + * override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): T = < decode value > + * }}} + * + * @note + * Since this class already opens an array, you should not do this anymore unless you are reading a nested map/array. + * + * @note + * Using `reader.readMember`, `reader.openEncapsulation`, ... can still use either a map or an array, it is only the top-level encapsulation that is + * an array. + * + * @example + * If you implement it as a given, you can use it implicitly in your code + * {{{ + * override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): T = + * ... + * // Will use the implicit Decoder[T] + * reader.readMember[T]("< key >", < value >) + * ... + * }}} + * + * @tparam T + * The type to decode + */ trait EncapsulatedArrayDecoder[T](length: Int = 0) extends EncapsulatedDecoder[T]: override def read(reader: Reader): T = if length == 0 then reader.readArrayStart() else reader.readArrayOpen(length) val res = readEncapsulated(reader)(using decoder) reader.readArrayClose(true, res) +/** + * Object with extension methods for [[borer.Reader]]. + * + * These extension methods should be used when using an [[EncapsulatedDecoder]]. + */ object EncapsulatedDecoder: extension (reader: Reader) + /** + * Read a key-value pair. + * + * This will read a key-value pair in either a map or an array, based on the decoder that is given. If the key cannot be read yet because it + * appears later in the file, the resulting value will not have a value yet, this value will be filled in when the key becomes readable. + * + * @param key + * The key to read + * @param decoder + * Implicit argument that decides how to read the key-value pair + * @tparam T + * The type of the value that should be read, this type should have an decoder + * @returns + * The key-value pair that is read, if the key cannot be read yet because it appears later in the file, the value will be empty and will be + * filled in later. + */ def readMember[T: Decoder](key: String)(using decoder: AbstractDecoder): ReadValue[String, T] = decoder.readKey(reader, key) decoder.readValue[T](reader) + + /** + * Read a key-value pair. + * + * This will read a key-value pair in either a map or an array, based on the decoder that is given. The key will be the first available key or + * a autogenerated key if you are using a decoder that doesn't use keys like [[ArrayDecoder]] + * + * @param key + * The key to read + * @param decoder + * Implicit argument that decides how to read the key-value pair + * @tparam T + * The type of the value that should be read, this type should have an decoder + * @returns + * The key-value pair that is read. + */ def readMember[T: Decoder]()(using decoder: AbstractDecoder): ReadValue[String, T] = decoder.readKey(reader) decoder.readValue[T](reader) + + /** + * Read one of the given key-value pairs. + * + * This will read the first key that is found and return the value associated with it, if non of the keys can currently be read, the returned + * value will not have a key or a value. These will be filled in when a key matching one of the given keys is encountered. + * + * @note + * When using a decoder that doesn't use keys like [[ArrayDecoder]] the first decoder given will be used. + * + * @param keys + * The possible keys that could be read + * @param decoder + * Implicit argument that decides how to read the key-value pair + * @tparam T + * The type of the value that should be read, this type should have an decoder + * @returns + * The key-value pair that is read, if none of the keys can be read because it appears later in the file, the key and the value will be + * empty and will be filled in later. + */ def readMembers[T](keys: Array[(String, Decoder[_ <: T])])(using decoder: AbstractDecoder): ReadValue[String, T] = val res = new ReadValue[String, T]() for (key, valueDecoder) <- keys do @@ -96,18 +453,63 @@ object EncapsulatedDecoder: if value.hasValue then return value value.updateValue = res return res - def readUntilBeforeBreak[T](zero: T)(f: T => T): T = + + /** + * Read until a map/array end is found and accumulate the result without reading the actual map/array end. + * + * @note + * The given function should also read the values, this method will just continuously call the given function with the returned value until + * a break is reached. + * + * @param zero + * The initial alue + * @param f + * The function transforming the previous value into the next value, this should include reading the value + * @return + * The final value + */ + def readUntilBeforeBreak[T](zero: T, f: T => T): T = var res = zero while !reader.hasBreak do res = f(res) return res + + /** + * Returns an already read value using a given key. + * + * @param decoder + * The decoder used to decode the value + * @throws NoSuchElementException + * If this key hasn't been read yet, and therefore doesn't have a value. + */ def getMember[T](key: String)(using decoder: AbstractDecoder): T = decoder.getValue[T](key) +/** + * Decoder that uses maps to decode values. + * + * This decoder uses maps to decode values and therefore requires keys to be present. + */ class MapDecoder extends AbstractDecoder: + /** + * Stores a key-value pair and a decoder that can be used to decode this value. + * + * This is used to store an decoder for later if the key cannot be read yet and keep the same type for the decoder and the value. + * + * @param value + * The key-value pair + * @param decoder + * The decoder + */ protected case class ValueDecoder[T](value: ReadValue[String, T], decoder: Decoder[T]) + + /** Stores keys that haven't been read yet combined with a decoder to decode the value, and the key-value pair that was returned. */ protected val keys = new HashMap[String, ValueDecoder[_]]() + + /** The key that was read, and which value should now be read. */ protected var decodeKey: Option[String] = None - override val mapBasedDecoder: Boolean = true + override protected val mapBasedDecoder: Boolean = true + + /** Read the next key for which the value should be decoded, if there isn't currently a value being decoded */ protected def readDecodeKey(reader: Reader): Unit = if decodeKey.isEmpty && reader.hasString then decodeKey = Some(reader.readString()) override def readKey(reader: Reader): String = @@ -144,10 +546,17 @@ class MapDecoder extends AbstractDecoder: override def openEncapsulation(reader: Reader, amount: Int): Unit = reader.readMapOpen(amount) override def closeEncapsulation[T](reader: Reader, unbounded: Boolean, res: T): Unit = reader.readMapClose(unbounded, res) +/** + * Decoder that uses arrays to decode values. + * + * This decoder uses arrays to decode values and does not require keys, if you want an array-based decoder that saves keys, you should use + * [[ArrayKeyDecoder]]. + */ class ArrayDecoder extends AbstractDecoder: + /** Used to generate IDs if no key is provided, this is used to store the values. */ protected var id = -1 - override val mapBasedDecoder: Boolean = false + override protected val mapBasedDecoder: Boolean = false override def readKey(reader: Reader): String = return "" override def readKey(reader: Reader, key: String): String = currentKey = Some(key) @@ -164,11 +573,66 @@ class ArrayDecoder extends AbstractDecoder: override def openEncapsulation(reader: Reader, amount: Int): Unit = reader.readArrayOpen(amount) override def closeEncapsulation[T](reader: Reader, unbounded: Boolean, res: T): Unit = reader.readMapClose(unbounded, res) +/** + * Decoder that uses arrays to decode values, but preserves keys. + * + * This decoder uses arrays to decode values but requires values to be directly preceded by a key. This can be used, for example, if your keys are not + * strings. Since non-string keys are not supported in JSON, you cannot use a map for this, but this class does allow you to load your key-value pair + * in an intuitive way. + * + * This is how your key-value pair should be saved: + * {{{ + * [ + * < key1 >, + * < value1 >, + * < key2 >, + * < value2> , + * ... + * ] + * }}} + */ class ArrayKeyDecoder extends MapDecoder: - var key: Option[Any] = None + /** The key that was read, and which value should now be read. */ + protected var key: Option[Any] = None + + /** + * Read a key from the given reader. + * + * @note + * This only reads a key and should therefore be followed up by reading a value using [[readValue]]. + * + * @param reader + * The reader used read the key + * @tparam T + * The type of the key that should be read + * @return + * The key that was read. + */ def readKey[T: Decoder](reader: Reader): Any = key = Some(reader.read[T]()) key.get + + /** + * Read a given value from the given reader. + * + * This will read a value of type T using the previously read key. + * + * @note + * This only reads a value and should therefore only be called after reading a key using [[readKey]]. + * + * @param reader + * The reader used read the value + * @tparam V + * The type of key that was decoded by [[readKey]] + * @tparam T + * The type of value that should be decoded + * @returns + * The key-value pair that is read, if the key cannot be read yet because it appears later in the file, the value will be empty and will be + * filled in later. + * + * @throws IllegalStateException + * When you call this before calling [[readKey]] + */ def readKeyValue[V, T: Decoder](reader: Reader): ReadValue[V, T] = if key.isEmpty then throw new IllegalStateException(s"Trying to read a value before reading a key.") val res = reader.read[T]() @@ -179,8 +643,25 @@ class ArrayKeyDecoder extends MapDecoder: override def openEncapsulation(reader: Reader, amount: Int): Unit = reader.readArrayOpen(amount) override def closeEncapsulation[T](reader: Reader, unbounded: Boolean, res: T): Unit = reader.readMapClose(unbounded, res) +/** + * Object with an extension method for [[borer.Reader]]. + * + * This extension method allows you to read a key-value pair where the key is not a String, and should only be used when using an [[ArrayKeyDecoder]]. + */ object ArrayKeyDecoder: extension (reader: Reader) + /** + * Reads a key-value pair. + * + * This will read a key-value pair in either a map or an array, based on the decoder that is given. + * + * @param decoder + * Implicit argument that decides how to read the key-value pair + * @tparam T + * The type of the key that should be read, this type should have an decoder + * @tparam U + * The type of the value that should be read, this type should have an decoder + */ def readMember[K: Decoder, V: Decoder]()(using decoder: ArrayKeyDecoder): ReadValue[K, V] = decoder.readKey[K](reader) decoder.readKeyValue[K, V](reader) diff --git a/code/shared/src/main/scala/maf/save/load/Store.scala b/code/shared/src/main/scala/maf/save/load/Store.scala index b55b1c48f..87cb9776b 100644 --- a/code/shared/src/main/scala/maf/save/load/Store.scala +++ b/code/shared/src/main/scala/maf/save/load/Store.scala @@ -17,16 +17,68 @@ import maf.language.scheme.lattices.SchemeLattice import maf.language.scheme.SchemeLambdaExp import maf.core.Address +/** + * The base trait for decoding [[AbstractDomain.Value values]]. + * + * @note + * This trait gives the methods needed to decode values, but does not implement them yet, other traits like [[LoaNoContext]] should be mixed in for + * the implementation. The trait that should be mixed in depends on the kind of values that is used in your analysis. + * + * @tparam Expr + * The type of expression used in the analysis + */ trait LoadValue[Expr <: Expression] extends Load[Expr] with AbstractDomain[Expr]: + /** + * Get the decoder that will be used to decode values. + * + * This will influence how values will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] decoder. + */ def getValueDecoder: AbstractDecoder = getDecoder + + /** + * Get the decoder that will be used to decode values. + * + * This decoder is used to decode objects where the key is important, when you want to e.g. decode a type from the key, some decoders might ignore + * this key, and should therefore not be used here. + * + * This will influence how values will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] decoder. + */ def getValueKeyDecoder: AbstractDecoder = getKeyDecoder given valueDecoder: Decoder[Value] +/* Trait to decode lattices. + * + * @tparam Expr + * The type of expression used in the analysis + */ trait LoadLattice[Expr <: Expression] extends Load[Expr]: + /** + * The types of lattices that can be decoded by this trait. + * + * This is used to specify the givens, if this was not used, this given could be used for every class with a single abstract type. + */ type Lattice[T] = ConstantPropagation.L[T] | Concrete.L[T] + /** + * Get the decoder that will be used to decode lattices. + * + * This will influence how lattices will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] + * decoder. + */ def getLatticeDecoder: AbstractDecoder = getDecoder + + /** + * Get the decoder that will be used to decode lattices. + * + * This decoder is used to decode objects where the key is important, when you want to e.g. decode a type from the key, some decoders might ignore + * this key, and should therefore not be used here. + * + * This will influence how lattices will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] + * decoder. + */ def getLatticeKeyDecoder: AbstractDecoder = getKeyDecoder + + /** Returns a map that links a key to a specific decoder. */ def latticeDecoders[T: Decoder] = Set[(String, Decoder[_ <: Lattice[T]])](("constant", constantLatticeDecoder[T])) given latticeDecoder[P[T] <: Lattice[T], T: Decoder]: EncapsulatedDecoder[P[T]] with @@ -40,6 +92,11 @@ trait LoadLattice[Expr <: Expression] extends Load[Expr]: else if reader.tryReadString("bottom") then ConstantPropagation.Bottom else return new ConstantPropagation.Constant[T](reader.read[T]()) +/** + * Trait to decode [[ModularSchemeLattice modular scheme lattices]]. + * + * Implementation of [[LoadModularDomain]]. + */ trait LoadModularSchemeLattices extends LoadModularDomain with LoadAddr[SchemeExp] @@ -87,8 +144,8 @@ trait LoadModularSchemeLattices override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): (HMapKey, SchemeLattice#Clo) = return (modularLattice.CloT, new modularLattice.Clo( - reader.readUntilBeforeBreak[Set[(SchemeLambdaExp, Env)]](Set[(SchemeLambdaExp, Env)]())((closures) => - closures + (reader.read[(SchemeLambdaExp, Env)]()) + reader.readUntilBeforeBreak[Set[(SchemeLambdaExp, Env)]](Set[(SchemeLambdaExp, Env)](), + (closures) => closures + (reader.read[(SchemeLambdaExp, Env)]()) ) ) ) @@ -97,11 +154,29 @@ trait LoadModularSchemeLattices override def decoder: AbstractDecoder = getValueDecoder override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): (HMapKey, SchemeLattice#Pointer) = return (modularLattice.PointerT, - new modularLattice.Pointer(reader.readUntilBeforeBreak[Set[Address]](Set())((pointers) => pointers + (reader.read[Address]()))) + new modularLattice.Pointer(reader.readUntilBeforeBreak[Set[Address]](Set(), (pointers) => pointers + (reader.read[Address]()))) ) +/** + * Base trait for decoding values as [[ModularSchemeLattice modular scheme lattices]], as defined in [[ModularSchemeDomain]]. + * + * @note + * This trait gives the methods needed to decode values, but does not implement them yet, other traits like [[LoaNoContext]] should be mixed in for + * the implementation. The trait that should be mixed in depends on the kind of values that is used in your analysis. + * + * @tparam Expr + * The type of expression used in the analysis + */ trait LoadModularDomain extends LoadValue[SchemeExp] with ModularSchemeDomain: + /** + * Get the decoder that will be used to decode an hMap. + * + * This will influence how an hMap will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] + * decoder. + */ def getHMapDecoder: AbstractDecoder = new ArrayDecoder + + /** Returns a map that links a key to a specific decoder. */ def hMapDecoders = Set[(String, Decoder[_ <: (HMapKey, Any)])]() given EncapsulatedDecoder[(HMapKey, Any)] with @@ -112,7 +187,14 @@ trait LoadModularDomain extends LoadValue[SchemeExp] with ModularSchemeDomain: override given valueDecoder: EncapsulatedDecoder[HMap] with override def decoder: AbstractDecoder = getHMapDecoder override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): HMap = - return new HMap(reader.readUntilBeforeBreak[Map[HMapKey, Any]](Map())((hMap) => hMap + reader.readMember[(HMapKey, Any)]().value)) - + return new HMap(reader.readUntilBeforeBreak[Map[HMapKey, Any]](Map(), (hMap) => hMap + reader.readMember[(HMapKey, Any)]().value)) + +/** + * Trait to decode the global store. + * + * This adds the global store to the objects that should be loaded, but does not have an implementation that can be used to decode the + * [[AbstractDomain.Value values]] inside of the store, for this an implementation of [[LoadValue]] like [[LoadModularDomain]] should be included + * depending on the values that are used in your analysis. + */ trait LoadGlobalStore[Expr <: Expression] extends LoadValue[Expr] with LoadAddr[Expr] with LoadMapToArray with GlobalStore[Expr]: override def loadInfo = super.loadInfo + ("store" -> Loadable((store: Map[Addr, Value]) => this.store = store)) diff --git a/code/shared/src/main/scala/maf/save/load/Util.scala b/code/shared/src/main/scala/maf/save/load/Util.scala index 873c4e133..852826ac5 100644 --- a/code/shared/src/main/scala/maf/save/load/Util.scala +++ b/code/shared/src/main/scala/maf/save/load/Util.scala @@ -5,7 +5,37 @@ import io.bullet.borer.Reader import maf.save.ArrayKeyDecoder.readMember import scala.collection.mutable +/** + * Trait to decode a map using an array. + * + * This will load a map from an array with alternating keys and values: + * {{{ + * [ + * < key1 >, + * < value1 >, + * < key2 >, + * < value2 >, + * ... + * ] + * }}} + * This can, for example be used if the key is not a string, and can therefore not be used as a key of a JSON map. + */ trait LoadMapToArray: + /** + * Decodes a map using an array. + * + * This will load a map from an array with alternating keys and values: + * {{{ + * [ + * < key1 >, + * < value1 >, + * < key2 >, + * < value2 >, + * ... + * ] + * }}} + * This can, for example be used if the key is not a string, and can therefore not be used as a key of a JSON map. + */ given mapKeyDecoder[K, V](using keyDecoder: Decoder[K], valueDecoder: Decoder[V]): EncapsulatedDecoder[Map[K, V]] with override val decoder: ArrayKeyDecoder = new ArrayKeyDecoder override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Map[K, V] = From 8d4a89a85ae8ff302017662c9f1951e79f25ccc7 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 12 Feb 2024 08:47:46 +0100 Subject: [PATCH 34/97] refactor(load): Remove LoadComponentID from LoadComponents You can now use LoadComponents with LoadComponentID, the other way is no longer possible since you first need to lead the components before being able to load them using their ID. --- .../main/scala/maf/save/load/Component.scala | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/load/Component.scala b/code/shared/src/main/scala/maf/save/load/Component.scala index 0d113099a..da96b26e1 100644 --- a/code/shared/src/main/scala/maf/save/load/Component.scala +++ b/code/shared/src/main/scala/maf/save/load/Component.scala @@ -45,7 +45,7 @@ import scala.collection.mutable.HashMap * @tparam Expr * The type of expression used in the analysis */ -trait LoadComponents[Expr <: Expression] extends Load[Expr] with LoadComponentID[Expr]: +trait LoadComponents[Expr <: Expression] extends Load[Expr]: /** * Get the decoder that will be used to decode components. * @@ -65,18 +65,8 @@ trait LoadComponents[Expr <: Expression] extends Load[Expr] with LoadComponentID */ def getComponentKeyDecoder: AbstractDecoder = getKeyDecoder - /** - * Register a loaded component, this is necessary if you also want to load components by their ID. - * - * @param component - * The component to register - */ - def addComponent(component: Component): Unit override def loadInfo: Map[String, Loadable[_]] = - super.loadInfo + ("components" -> Loadable((visited: Set[Component]) => - visited.foreach((component) => addComponent(component)) - this.visited = visited - )) + super.loadInfo + ("components" -> Loadable((visited: Set[Component]) => this.visited = visited)) given componentDecoder: Decoder[Component] @@ -86,10 +76,6 @@ trait LoadStandardSchemeComponents with LoadContext[SchemeExp] with LoadPosition[SchemeExp] with LoadEnvironment[SchemeExp]: - override def addComponent(component: SchemeModFComponent): Unit = - if component != initialComponent then - components.addOne(component.asInstanceOf[SchemeModFComponent.Call[DecodeContext]].clo._1.idn.pos, component) - override given componentDecoder: Decoder[Component] with override def read(reader: Reader): Component = if reader.tryReadString("main") then return initialComponent @@ -260,13 +246,29 @@ trait LoadEnvironment[Expr <: Expression] extends Load[Expr] with LoadAddr[Expr] * @tparam Expr * The type of expression used in the analysis */ -trait LoadComponentID[Expr <: Expression] extends LoadPosition[Expr]: +trait LoadComponentID[Expr <: Expression] extends LoadComponents[Expr] with LoadPosition[Expr]: /** Map that connects component IDs to the component. */ var components = HashMap[Position, Component]() + + /** + * Register a loaded component, this allows you to also reference components using their ID using the [[components]] map. + * + * @param component + * The component to register + */ + def addComponent(component: Component): Unit given componentIDDecoder: Decoder[Component] + override def loadInfo: Map[String, Loadable[_]] = + val loadInfoMap = super.loadInfo + val components = loadInfoMap("components").asInstanceOf[Loadable[Set[Component]]] + loadInfoMap + ("components" -> Loadable((visited: Set[Component]) => + visited.foreach((component) => addComponent(component)) + components.load(visited) + )(using components.decoder)) + /** - * Trait that decodes components using their position. + * Trait that decodes standard scheme components using their position. * * Implementation of [[LoadComponentID]]. * @@ -275,6 +277,10 @@ trait LoadComponentID[Expr <: Expression] extends LoadPosition[Expr]: * the position can be mapped to an actual component. */ trait LoadStandardSchemeComponentID extends LoadComponentID[SchemeExp] with LoadContext[SchemeExp]: + override def addComponent(component: Component): Unit = + if component != initialComponent then + components.addOne(component.asInstanceOf[SchemeModFComponent.Call[DecodeContext]].clo._1.idn.pos, component) + given componentIDDecoder: Decoder[Component] with override def read(reader: Reader): Component = if reader.tryReadString("main") then return initialComponent From da209872752662eecc30b958a6975c8a6fc3974c Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 12 Feb 2024 08:51:56 +0100 Subject: [PATCH 35/97] refactor(load): Use abstract type for component ID This allows you to use different kinds of IDs based on the analysis and components that you are using. --- code/shared/src/main/scala/maf/save/load/Analysis.scala | 2 +- code/shared/src/main/scala/maf/save/load/Component.scala | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/load/Analysis.scala b/code/shared/src/main/scala/maf/save/load/Analysis.scala index 68beedfe9..1a63a7c4c 100644 --- a/code/shared/src/main/scala/maf/save/load/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/load/Analysis.scala @@ -85,7 +85,7 @@ trait LoadModF with LoadSchemeAddr with LoadDependency[SchemeExp] with LoadAddrDependency[SchemeExp] - with LoadStandardSchemeComponentID + with LoadStandardSchemeComponentPosition with LoadGlobalStore[SchemeExp] with LoadModularSchemeLattices: def getDecoder: AbstractDecoder = new MapDecoder diff --git a/code/shared/src/main/scala/maf/save/load/Component.scala b/code/shared/src/main/scala/maf/save/load/Component.scala index da96b26e1..157efb74e 100644 --- a/code/shared/src/main/scala/maf/save/load/Component.scala +++ b/code/shared/src/main/scala/maf/save/load/Component.scala @@ -247,8 +247,11 @@ trait LoadEnvironment[Expr <: Expression] extends Load[Expr] with LoadAddr[Expr] * The type of expression used in the analysis */ trait LoadComponentID[Expr <: Expression] extends LoadComponents[Expr] with LoadPosition[Expr]: + /* The type of ID that will be used to load components */ + type ID + /** Map that connects component IDs to the component. */ - var components = HashMap[Position, Component]() + var components = HashMap[ID, Component]() /** * Register a loaded component, this allows you to also reference components using their ID using the [[components]] map. @@ -276,7 +279,8 @@ trait LoadComponentID[Expr <: Expression] extends LoadComponents[Expr] with Load * Because this trait only decodes the component positions, the entire component should have already been decoded and placed in [[components]], so * the position can be mapped to an actual component. */ -trait LoadStandardSchemeComponentID extends LoadComponentID[SchemeExp] with LoadContext[SchemeExp]: +trait LoadStandardSchemeComponentPosition extends LoadComponentID[SchemeExp] with LoadContext[SchemeExp]: + override type ID = Position override def addComponent(component: Component): Unit = if component != initialComponent then components.addOne(component.asInstanceOf[SchemeModFComponent.Call[DecodeContext]].clo._1.idn.pos, component) From 0806e6e340ff35830280d702e3674dcf8ec66164 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 12 Feb 2024 08:57:33 +0100 Subject: [PATCH 36/97] fix(load): `ArrayKeyDecoder` isn't setting `mapBasedDecoder` to false --- code/shared/src/main/scala/maf/save/load/Encapsulated.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/shared/src/main/scala/maf/save/load/Encapsulated.scala b/code/shared/src/main/scala/maf/save/load/Encapsulated.scala index 9d60d5552..e4a3c95ea 100644 --- a/code/shared/src/main/scala/maf/save/load/Encapsulated.scala +++ b/code/shared/src/main/scala/maf/save/load/Encapsulated.scala @@ -592,6 +592,8 @@ class ArrayDecoder extends AbstractDecoder: * }}} */ class ArrayKeyDecoder extends MapDecoder: + override protected val mapBasedDecoder: Boolean = false + /** The key that was read, and which value should now be read. */ protected var key: Option[Any] = None From 747126ef2a8705672ec96126fefe28485c790858 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 12 Feb 2024 09:56:22 +0100 Subject: [PATCH 37/97] feat(save): Add componentEncoder that uses integer IDs This allows you to save every component using a single number instead of having to use the position, which is much longer. --- .../main/scala/maf/save/save/Component.scala | 30 ++++++++++++++++++- .../main/scala/maf/save/save/Dependency.scala | 5 +--- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/save/Component.scala b/code/shared/src/main/scala/maf/save/save/Component.scala index 66ab91105..2feb97909 100644 --- a/code/shared/src/main/scala/maf/save/save/Component.scala +++ b/code/shared/src/main/scala/maf/save/save/Component.scala @@ -33,7 +33,7 @@ import maf.modular.scv.ScvContextSensitivity import maf.modular.scheme.modf.NoContext import maf.core.Position import maf.core.Position.PTag -import scala.collection.immutable.HashMap +import scala.collection.mutable.HashMap /** * Trait to encode positions. @@ -113,6 +113,34 @@ trait SaveComponentID[Expr <: Expression] extends SavePosition[Expr]: /** Encodes a component by their ID */ given componentIDEncoder: Encoder[Component] +/** + * Trait that encodes components using an autoincreasing integer ID. + * + * Implementation of [[SaveComponentID]] + * + * @tparam Expr + * The type of expression used in the analysis + */ +trait SaveComponentIntID[Expr <: Expression] extends SaveComponents[Expr] with SaveComponentID[Expr]: + private val components = HashMap[Component, Int]() + private var id = 0 + override def saveInfo: Map[String, Savable[?]] = + val saveInfoMap = super.saveInfo + val component = saveInfoMap("components").asInstanceOf[Savable[Set[Component]]] + return saveInfoMap + ("components" -> Savable(component.value)) + + private given EncapsulatedEncoder[Set[Component]] with + override val encoder: AbstractEncoder = getComponentKeyEncoder + override protected def writeEncapsulated(writer: Writer, components: Set[Component]): Writer = + for (component <- components) do + SaveComponentIntID.this.components.addOne((component, id)) + writer.writeMember(id.toString(), component)(using componentEncoder, encoder) + id += 1 + writer + + override given componentIDEncoder: Encoder[Component] with + override def write(writer: Writer, component: Component): Writer = writer.write(components(component)) + /** * Trait that encodes components using their position. * diff --git a/code/shared/src/main/scala/maf/save/save/Dependency.scala b/code/shared/src/main/scala/maf/save/save/Dependency.scala index 322792ffa..28eddc690 100644 --- a/code/shared/src/main/scala/maf/save/save/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/save/Dependency.scala @@ -155,14 +155,13 @@ trait SaveAddr[Expr <: Expression] extends Save[Expr] with SavePosition[Expr]: * * This is an implementation of [[SaveAddr]]. */ -trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveStandardSchemeComponentPosition with SaveContext[SchemeExp]: +trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveComponentID[SchemeExp] with SaveContext[SchemeExp]: given Encoder[SchemeValue] = AbstractEncoder.deriveEncoder(getAddressEncoder) given Encoder[maf.language.sexp.Value] = AbstractEncoder.deriveAllEncoders(getAddressEncoder) given EncapsulatedEncoder[VarAddr[EncodeContext]] with override val encoder = getAddressEncoder override def writeEncapsulated(writer: Writer, address: VarAddr[EncodeContext]): Writer = - import componentIDEncoder.given writer.writeMember("id", address.id) if address.ctx.asInstanceOf[Option[EncodeContext]].isDefined then writer.writeMember("context", address.ctx.asInstanceOf[Option[EncodeContext]].get) @@ -171,7 +170,6 @@ trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveStandardSchemeComponen given EncapsulatedEncoder[ReturnAddr[EncodeContext]] with override val encoder = getAddressEncoder override def writeEncapsulated(writer: Writer, address: ReturnAddr[EncodeContext]): Writer = - import componentIDEncoder.given writer.writeMember("component", address.cmp.asInstanceOf[Component]) writer.writeMember("identity", address.idn) @@ -184,7 +182,6 @@ trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveStandardSchemeComponen writer override protected def encodeAddress(writer: Writer, address: Address)(using encoder: AbstractEncoder): Writer = - import componentIDEncoder.given address match { case varAddr @ VarAddr(_, _) => writer.writeMember("varAddr", varAddr.asInstanceOf[VarAddr[EncodeContext]]) From ef12608ebb9a64d82e143554ab27953ae95817b4 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 12 Feb 2024 11:09:56 +0100 Subject: [PATCH 38/97] feat(load): Add componentDecoder that uses integer IDs --- .../main/scala/maf/save/load/Analysis.scala | 2 +- .../main/scala/maf/save/load/Component.scala | 37 ++++++++++++++++++- .../main/scala/maf/save/save/Analysis.scala | 2 +- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/load/Analysis.scala b/code/shared/src/main/scala/maf/save/load/Analysis.scala index 1a63a7c4c..b3a559f3b 100644 --- a/code/shared/src/main/scala/maf/save/load/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/load/Analysis.scala @@ -85,7 +85,7 @@ trait LoadModF with LoadSchemeAddr with LoadDependency[SchemeExp] with LoadAddrDependency[SchemeExp] - with LoadStandardSchemeComponentPosition + with LoadComponentIntID[SchemeExp] with LoadGlobalStore[SchemeExp] with LoadModularSchemeLattices: def getDecoder: AbstractDecoder = new MapDecoder diff --git a/code/shared/src/main/scala/maf/save/load/Component.scala b/code/shared/src/main/scala/maf/save/load/Component.scala index 157efb74e..c318d4218 100644 --- a/code/shared/src/main/scala/maf/save/load/Component.scala +++ b/code/shared/src/main/scala/maf/save/load/Component.scala @@ -285,7 +285,7 @@ trait LoadStandardSchemeComponentPosition extends LoadComponentID[SchemeExp] wit if component != initialComponent then components.addOne(component.asInstanceOf[SchemeModFComponent.Call[DecodeContext]].clo._1.idn.pos, component) - given componentIDDecoder: Decoder[Component] with + override given componentIDDecoder: Decoder[Component] with override def read(reader: Reader): Component = if reader.tryReadString("main") then return initialComponent else reader.read[SchemeModFComponent.Call[DecodeContext]]().asInstanceOf[Component] @@ -294,3 +294,38 @@ trait LoadStandardSchemeComponentPosition extends LoadComponentID[SchemeExp] wit override def read(reader: Reader): SchemeModFComponent.Call[DecodeContext] = val pos = reader.read[Position]() return components(pos).asInstanceOf[SchemeModFComponent.Call[DecodeContext]] + +/** + * Trait that decodes standard scheme components using an integer ID. + * + * Implementation of [[LoadComponentID]]. + * + * @note + * Because this trait only decodes the component ID, the entire component should have already been decoded and placed in [[components]], so the ID + * can be mapped to an actual component. + */ +trait LoadComponentIntID[Expr <: Expression] extends LoadComponentID[Expr]: + override type ID = Int + override def addComponent(component: Component): Unit = return + + override given componentIDDecoder: Decoder[Component] with + override def read(reader: Reader): Component = + val id = reader.readInt() + return components(id) + + private given EncapsulatedDecoder[Set[Component]] with + override def decoder: AbstractDecoder = getComponentKeyDecoder + override protected def readEncapsulated(reader: Reader)(using decoder: AbstractDecoder): Set[Component] = + reader.readUntilBeforeBreak( + Set[Component](), + (components: Set[Component]) => + val component = reader.readMember[Component]()(using componentDecoder, decoder) + val key = component.key.toInt + LoadComponentIntID.this.components.addOne((key, component.value)) + components + (component.value) + ) + + override def loadInfo: Map[String, Loadable[?]] = + val loadInfoMap = super.loadInfo + val component = loadInfoMap("components").asInstanceOf[Loadable[Set[Component]]] + return loadInfoMap + ("components" -> Loadable(component.load)) diff --git a/code/shared/src/main/scala/maf/save/save/Analysis.scala b/code/shared/src/main/scala/maf/save/save/Analysis.scala index 881d673dc..81022994f 100644 --- a/code/shared/src/main/scala/maf/save/save/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/save/Analysis.scala @@ -86,6 +86,6 @@ trait SaveModF with SaveGlobalStore[SchemeExp] with SaveModularSchemeLattices with SaveNoContext[SchemeExp] - with SaveStandardSchemeComponentPosition: + with SaveComponentIntID[SchemeExp]: override def getEncoder: AbstractEncoder = new MapEncoder override def getKeyEncoder: AbstractEncoder = new MapEncoder From b905bca3e11d658fd59751be400afdadbe16651c Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 12 Feb 2024 13:11:59 +0100 Subject: [PATCH 39/97] feat(save): Add support for more scheme expressions --- .../main/scala/maf/save/save/Component.scala | 25 ++++++++++++++++++- .../main/scala/maf/save/save/Dependency.scala | 9 +++---- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/save/Component.scala b/code/shared/src/main/scala/maf/save/save/Component.scala index 2feb97909..a8970b342 100644 --- a/code/shared/src/main/scala/maf/save/save/Component.scala +++ b/code/shared/src/main/scala/maf/save/save/Component.scala @@ -34,6 +34,12 @@ import maf.modular.scheme.modf.NoContext import maf.core.Position import maf.core.Position.PTag import scala.collection.mutable.HashMap +import maf.language.scheme.SchemeLetrec +import maf.language.scheme.SchemeAssert +import maf.language.scheme.SchemeValue +import maf.language.scheme.SchemeSet +import maf.language.scheme.SchemeBegin +import maf.language.scheme.SchemeLetStar /** * Trait to encode positions. @@ -270,16 +276,33 @@ trait SaveStandardSchemeComponents case variable: SchemeVar => writer.writeMember("var", variable) case lambda: SchemeLambda => writer.writeMember("lambda", lambda) case argLambda: SchemeVarArgLambda => writer.writeMember("argLambda", argLambda) + case value: SchemeValue => writer.writeMember("value", value) + case letrec: SchemeLetrec => writer.writeMember("letrec", letrec) + case assert: SchemeAssert => writer.writeMember("assert", assert) + case let: SchemeLet => writer.writeMember("let", let) + case schemeIf: SchemeIf => writer.writeMember("schemeIf", schemeIf) + case set: SchemeSet => writer.writeMember("set", set) + case begin: SchemeBegin => writer.writeMember("begin", begin) + case letStar: SchemeLetStar => writer.writeMember("letStar", letStar) case _ => - System.err.nn.println("The schemeexpression with type `" + exp.getClass + "` could not be encoded") + System.err.nn.println("The scheme expression with type `" + exp.getClass + "` could not be encoded") writer private val compEncoder = getComponentEncoder + given Encoder[SchemeValue] = AbstractEncoder.deriveEncoder(compEncoder) + given Encoder[maf.language.sexp.Value] = AbstractEncoder.deriveAllEncoders(compEncoder) given Encoder[SchemeFuncall] = AbstractEncoder.deriveEncoder[SchemeFuncall](compEncoder) given Encoder[SchemeVar] = AbstractEncoder.deriveEncoder[SchemeVar](compEncoder) given Encoder[SchemeLambda] = AbstractEncoder.deriveEncoder[SchemeLambda](compEncoder) given Encoder[SchemeVarArgLambda] = AbstractEncoder.deriveEncoder[SchemeVarArgLambda](compEncoder) given Encoder[SchemeLambdaExp] = AbstractEncoder.deriveEncoder[SchemeLambdaExp](compEncoder) + given Encoder[SchemeLetrec] = AbstractEncoder.deriveEncoder[SchemeLetrec](compEncoder) + given Encoder[SchemeAssert] = AbstractEncoder.deriveEncoder[SchemeAssert](compEncoder) + given Encoder[SchemeLet] = AbstractEncoder.deriveEncoder[SchemeLet](compEncoder) + given Encoder[SchemeIf] = AbstractEncoder.deriveEncoder[SchemeIf](compEncoder) + given Encoder[SchemeSet] = AbstractEncoder.deriveEncoder[SchemeSet](compEncoder) + given Encoder[SchemeBegin] = AbstractEncoder.deriveEncoder[SchemeBegin](compEncoder) + given Encoder[SchemeLetStar] = AbstractEncoder.deriveEncoder[SchemeLetStar](compEncoder) override given componentEncoder: Encoder[Component] with def write(writer: Writer, component: Component): Writer = diff --git a/code/shared/src/main/scala/maf/save/save/Dependency.scala b/code/shared/src/main/scala/maf/save/save/Dependency.scala index 28eddc690..1d7c291d9 100644 --- a/code/shared/src/main/scala/maf/save/save/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/save/Dependency.scala @@ -155,10 +155,7 @@ trait SaveAddr[Expr <: Expression] extends Save[Expr] with SavePosition[Expr]: * * This is an implementation of [[SaveAddr]]. */ -trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveComponentID[SchemeExp] with SaveContext[SchemeExp]: - given Encoder[SchemeValue] = AbstractEncoder.deriveEncoder(getAddressEncoder) - given Encoder[maf.language.sexp.Value] = AbstractEncoder.deriveAllEncoders(getAddressEncoder) - +trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveComponentID[SchemeExp] with SaveContext[SchemeExp] with SaveStandardSchemeComponents: given EncapsulatedEncoder[VarAddr[EncodeContext]] with override val encoder = getAddressEncoder override def writeEncapsulated(writer: Writer, address: VarAddr[EncodeContext]): Writer = @@ -170,13 +167,13 @@ trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveComponentID[SchemeExp] given EncapsulatedEncoder[ReturnAddr[EncodeContext]] with override val encoder = getAddressEncoder override def writeEncapsulated(writer: Writer, address: ReturnAddr[EncodeContext]): Writer = - writer.writeMember("component", address.cmp.asInstanceOf[Component]) + writer.writeMember("component", address.cmp.asInstanceOf[Component])(using componentIDEncoder, encoder) writer.writeMember("identity", address.idn) given EncapsulatedEncoder[PtrAddr[EncodeContext]] with override val encoder = getAddressEncoder override def writeEncapsulated(writer: Writer, address: PtrAddr[EncodeContext]): Writer = - writer.writeMember("expression", address.exp.asInstanceOf[SchemeValue]) + writer.writeMember("expression", address.exp.asInstanceOf[SchemeExp]) if address.ctx.asInstanceOf[Option[EncodeContext]].isDefined then writer.writeMember("context", address.ctx.asInstanceOf[Option[EncodeContext]].get) writer From 22bbe1ad9798e690934f281b613b347e8ddbf598 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 12 Feb 2024 13:33:02 +0100 Subject: [PATCH 40/97] feat(save): Add support for more scheme lattices --- code/shared/src/main/scala/maf/save/save/Store.scala | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/code/shared/src/main/scala/maf/save/save/Store.scala b/code/shared/src/main/scala/maf/save/save/Store.scala index 356f494f3..cafa072c3 100644 --- a/code/shared/src/main/scala/maf/save/save/Store.scala +++ b/code/shared/src/main/scala/maf/save/save/Store.scala @@ -134,6 +134,12 @@ trait SaveModularSchemeLattices pointer.ptrs.foreach(writer.write(_)) writer + given EncapsulatedEncoder[SchemeLattice#Cons]() with + override val encoder = getValueEncoder + override protected def writeEncapsulated(writer: Writer, cons: SchemeLattice#Cons): Writer = + writer.writeMember("car", cons.car) + writer.writeMember("cdr", cons.cdr) + given EncapsulatedEncoder[(HMapKey, SchemeLattice#Value)] with override val encoder = getValueKeyEncoder override protected def writeEncapsulated(writer: Writer, hMapPair: (HMapKey, SchemeLattice#Value)): Writer = @@ -143,9 +149,13 @@ trait SaveModularSchemeLattices case int: SchemeLattice#Int => writer.writeMember("int", int.i.asInstanceOf[Lattice[BigInt]]) case bool: SchemeLattice#Bool => writer.writeMember("boolean", bool.b.asInstanceOf[Lattice[Boolean]]) case str: SchemeLattice#Str => writer.writeMember("string", str.s.asInstanceOf[Lattice[String]]) + case symbol: SchemeLattice#Symbol => writer.writeMember("symbol", symbol.s.asInstanceOf[Lattice[String]]) case prim: SchemeLattice#Prim => writer.writeMember("primitive", prim.prims) case clo: SchemeLattice#Clo => writer.writeMember("closure", clo) case pointer: SchemeLattice#Pointer => writer.writeMember("pointer", pointer) + case cons: SchemeLattice#Cons => writer.writeMember("cons", cons) + case modularLattice.Nil => writer.writeMember("nil", "") + case modularLattice.Void => writer.writeMember("void", "") case _ => System.err.nn.println("The lattice with type `" + key.getClass + "` could not be encoded") writer From f9e2c1c408a55026f73d991e8e8d30165ed19bfb Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 12 Feb 2024 15:43:13 +0100 Subject: [PATCH 41/97] feat(load): Add support for more scheme expressions --- .../main/scala/maf/save/load/Component.scala | 25 ++++++++++++++++++- .../main/scala/maf/save/load/Dependency.scala | 10 +++----- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/load/Component.scala b/code/shared/src/main/scala/maf/save/load/Component.scala index c318d4218..d0fc8d3b4 100644 --- a/code/shared/src/main/scala/maf/save/load/Component.scala +++ b/code/shared/src/main/scala/maf/save/load/Component.scala @@ -33,6 +33,12 @@ import maf.save.EncapsulatedDecoder.* import maf.core.Position import maf.core.Position.PTag import scala.collection.mutable.HashMap +import maf.language.scheme.SchemeValue +import maf.language.scheme.SchemeLetrec +import maf.language.scheme.SchemeAssert +import maf.language.scheme.SchemeSet +import maf.language.scheme.SchemeBegin +import maf.language.scheme.SchemeLetStar /** * The base trait for decoding components. @@ -89,11 +95,20 @@ trait LoadStandardSchemeComponents val context = reader.readMember[DecodeContext]("context") return new SchemeModFComponent.Call[DecodeContext]((lambda.value, environment.value), context.value) private val compDecoder = getComponentDecoder + given Decoder[SchemeValue] = AbstractDecoder.deriveDecoder(compDecoder) + given Decoder[maf.language.sexp.Value] = AbstractDecoder.deriveAllDecoders(compDecoder) given Decoder[SchemeFuncall] = AbstractDecoder.deriveDecoder[SchemeFuncall](compDecoder) given Decoder[SchemeVar] = AbstractDecoder.deriveDecoder[SchemeVar](compDecoder) given Decoder[SchemeLambda] = AbstractDecoder.deriveDecoder[SchemeLambda](compDecoder) given Decoder[SchemeVarArgLambda] = AbstractDecoder.deriveDecoder[SchemeVarArgLambda](compDecoder) given Decoder[SchemeLambdaExp] = AbstractDecoder.deriveDecoder[SchemeLambdaExp](compDecoder) + given Decoder[SchemeLetrec] = AbstractDecoder.deriveDecoder[SchemeLetrec](compDecoder) + given Decoder[SchemeAssert] = AbstractDecoder.deriveDecoder[SchemeAssert](compDecoder) + given Decoder[SchemeLet] = AbstractDecoder.deriveDecoder[SchemeLet](compDecoder) + given Decoder[SchemeIf] = AbstractDecoder.deriveDecoder[SchemeIf](compDecoder) + given Decoder[SchemeSet] = AbstractDecoder.deriveDecoder[SchemeSet](compDecoder) + given Decoder[SchemeBegin] = AbstractDecoder.deriveDecoder[SchemeBegin](compDecoder) + given Decoder[SchemeLetStar] = AbstractDecoder.deriveDecoder[SchemeLetStar](compDecoder) given EncapsulatedDecoder[SchemeExp] with override val decoder = getComponentKeyDecoder @@ -103,7 +118,15 @@ trait LoadStandardSchemeComponents ("funcall", summon[Decoder[SchemeFuncall]]), ("var", summon[Decoder[SchemeVar]]), ("lambda", summon[Decoder[SchemeLambda]]), - ("argLambda", summon[Decoder[SchemeVarArgLambda]]) + ("argLambda", summon[Decoder[SchemeVarArgLambda]]), + ("value", summon[Decoder[SchemeValue]]), + ("letrec", summon[Decoder[SchemeLetrec]]), + ("assert", summon[Decoder[SchemeAssert]]), + ("let", summon[Decoder[SchemeLet]]), + ("schemeIf", summon[Decoder[SchemeIf]]), + ("set", summon[Decoder[SchemeSet]]), + ("begin", summon[Decoder[SchemeBegin]]), + ("letStar", summon[Decoder[SchemeLetStar]]), ) ) expression.value diff --git a/code/shared/src/main/scala/maf/save/load/Dependency.scala b/code/shared/src/main/scala/maf/save/load/Dependency.scala index 1adc0e0aa..ead958ed7 100644 --- a/code/shared/src/main/scala/maf/save/load/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/load/Dependency.scala @@ -114,9 +114,7 @@ trait LoadAddr[Expr <: Expression] extends Load[Expr] with LoadPosition[Expr]: * * This is an implementation of [[LoadAddr]]. */ -trait LoadSchemeAddr extends LoadAddr[SchemeExp] with LoadContext[SchemeExp] with LoadComponentID[SchemeExp]: - given Decoder[SchemeValue] = AbstractDecoder.deriveDecoder(getAddressDecoder) - given Decoder[maf.language.sexp.Value] = AbstractDecoder.deriveAllDecoders(getAddressDecoder) +trait LoadSchemeAddr extends LoadAddr[SchemeExp] with LoadContext[SchemeExp] with LoadComponentID[SchemeExp] with LoadStandardSchemeComponents: override def addressDecoders = super.addressDecoders ++ List( ("varAddr", summon[Decoder[VarAddr[DecodeContext]]]), @@ -127,8 +125,8 @@ trait LoadSchemeAddr extends LoadAddr[SchemeExp] with LoadContext[SchemeExp] wit given EncapsulatedDecoder[ReturnAddr[Component]] with override def decoder: AbstractDecoder = getAddressDecoder - override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): ReturnAddr[Component] = - val component = reader.readMember[Component]("component") + override protected def readEncapsulated(reader: Reader)(using decoder: AbstractDecoder): ReturnAddr[Component] = + val component = reader.readMember[Component]("component")(using componentIDDecoder, decoder) val identity = reader.readMember[Identity]("identity") return new ReturnAddr[Component](component.value, identity.value) @@ -148,7 +146,7 @@ trait LoadSchemeAddr extends LoadAddr[SchemeExp] with LoadContext[SchemeExp] wit given EncapsulatedDecoder[PtrAddr[DecodeContext]] with override def decoder: AbstractDecoder = getAddressDecoder override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): PtrAddr[DecodeContext] = - val expression = reader.readMember[SchemeValue]("expression") + val expression = reader.readMember[SchemeExp]("expression") val context = reader.readMember[DecodeContext]("context") return new PtrAddr[DecodeContext]( expression.value, From c210f9f1139a095f474652af2fb33124982f7c74 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 12 Feb 2024 15:44:20 +0100 Subject: [PATCH 42/97] feat(load): Add support for more scheme lattices --- .../src/main/scala/maf/save/load/Store.scala | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/code/shared/src/main/scala/maf/save/load/Store.scala b/code/shared/src/main/scala/maf/save/load/Store.scala index 87cb9776b..81918c837 100644 --- a/code/shared/src/main/scala/maf/save/load/Store.scala +++ b/code/shared/src/main/scala/maf/save/load/Store.scala @@ -110,7 +110,11 @@ trait LoadModularSchemeLattices ("string", summon[Decoder[(HMapKey, SchemeLattice#Str)]]), ("primitive", summon[Decoder[(HMapKey, SchemeLattice#Prim)]]), ("closure", summon[Decoder[(HMapKey, SchemeLattice#Clo)]]), - ("pointer", summon[Decoder[(HMapKey, SchemeLattice#Pointer)]]) + ("pointer", summon[Decoder[(HMapKey, SchemeLattice#Pointer)]]), + ("symbol", summon[Decoder[(HMapKey, SchemeLattice#Symbol)]]), + ("cons", summon[Decoder[(HMapKey, SchemeLattice#Cons)]]), + ("nil", summon[Decoder[(HMapKey, modularLattice.Nil.type)]]), + ("void", summon[Decoder[(HMapKey, modularLattice.Void.type)]]), ) given Decoder[(HMapKey, SchemeLattice#Int)] with @@ -128,6 +132,11 @@ trait LoadModularSchemeLattices val lattice = reader.read[Lattice[String]]() return (modularLattice.StrT, new modularLattice.Str(lattice.asInstanceOf[LoadModularSchemeLattices.this.modularLatticeWrapper.S])) + given Decoder[(HMapKey, SchemeLattice#Symbol)] with + override def read(reader: Reader): (HMapKey, SchemeLattice#Symbol) = + val lattice = reader.read[Lattice[String]]() + return (modularLattice.SymbolT, new modularLattice.Symbol(lattice.asInstanceOf[LoadModularSchemeLattices.this.modularLatticeWrapper.Sym])) + given Decoder[(HMapKey, SchemeLattice#Prim)] with override def read(reader: Reader): (HMapKey, SchemeLattice#Prim) = return (modularLattice.PrimT, new modularLattice.Prim(reader.read[Set[String]]())) @@ -157,6 +166,19 @@ trait LoadModularSchemeLattices new modularLattice.Pointer(reader.readUntilBeforeBreak[Set[Address]](Set(), (pointers) => pointers + (reader.read[Address]()))) ) + given EncapsulatedDecoder[(HMapKey, SchemeLattice#Cons)]() with + override def decoder: AbstractDecoder = getValueDecoder + override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): (HMapKey, SchemeLattice#Cons) = + val car = reader.readMember[SchemeLattice#L]("car") + val cdr = reader.readMember[SchemeLattice#L]("cdr") + return (modularLattice.ConsT, new modularLattice.Cons(car.value, cdr.value)) + + given Decoder[(HMapKey, modularLattice.Nil.type)] with + override def read(reader: Reader): (HMapKey, modularLattice.Nil.type) = return (modularLattice.NilT, modularLattice.Nil) + + given Decoder[(HMapKey, modularLattice.Void.type)] with + override def read(reader: Reader): (HMapKey, modularLattice.Void.type) = return (modularLattice.VoidT, modularLattice.Void) + /** * Base trait for decoding values as [[ModularSchemeLattice modular scheme lattices]], as defined in [[ModularSchemeDomain]]. * From 5840d4420ecfac5795e7ec793b8cfd2fcd17dfde Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 12 Feb 2024 15:45:08 +0100 Subject: [PATCH 43/97] fix(save): Encoder for `ReturnAddr` uses context instead of component --- code/shared/src/main/scala/maf/save/save/Dependency.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/save/Dependency.scala b/code/shared/src/main/scala/maf/save/save/Dependency.scala index 1d7c291d9..c4a22cc11 100644 --- a/code/shared/src/main/scala/maf/save/save/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/save/Dependency.scala @@ -164,11 +164,11 @@ trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveComponentID[SchemeExp] writer.writeMember("context", address.ctx.asInstanceOf[Option[EncodeContext]].get) writer - given EncapsulatedEncoder[ReturnAddr[EncodeContext]] with + given EncapsulatedEncoder[ReturnAddr[Component]] with override val encoder = getAddressEncoder - override def writeEncapsulated(writer: Writer, address: ReturnAddr[EncodeContext]): Writer = - writer.writeMember("component", address.cmp.asInstanceOf[Component])(using componentIDEncoder, encoder) + override def writeEncapsulated(writer: Writer, address: ReturnAddr[Component]): Writer = writer.writeMember("identity", address.idn) + writer.writeMember("component", address.cmp)(using componentIDEncoder, encoder) given EncapsulatedEncoder[PtrAddr[EncodeContext]] with override val encoder = getAddressEncoder @@ -183,7 +183,7 @@ trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveComponentID[SchemeExp] case varAddr @ VarAddr(_, _) => writer.writeMember("varAddr", varAddr.asInstanceOf[VarAddr[EncodeContext]]) case returnAddr @ ReturnAddr(_, _) => - writer.writeMember("returnAddr", returnAddr.asInstanceOf[ReturnAddr[EncodeContext]]) + writer.writeMember("returnAddr", returnAddr.asInstanceOf[ReturnAddr[Component]]) case PrmAddr(nam) => writer.writeMember("prmAddr", nam) case ptrAddr @ PtrAddr(_, _) => From 6b57209fea2af0da6b90f5896d4aa92a57c280ad Mon Sep 17 00:00:00 2001 From: Merlijn Date: Thu, 29 Feb 2024 16:03:53 +0100 Subject: [PATCH 44/97] fix(save): In CBOR, the arrays/maps where not closed correctly Because `writeMapClose` and `writeArrayClose` where used instead of `writeBreak` --- code/shared/src/main/scala/maf/save/save/Encapsulated.scala | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/save/Encapsulated.scala b/code/shared/src/main/scala/maf/save/save/Encapsulated.scala index 5c04ddd19..d97ea3112 100644 --- a/code/shared/src/main/scala/maf/save/save/Encapsulated.scala +++ b/code/shared/src/main/scala/maf/save/save/Encapsulated.scala @@ -127,7 +127,7 @@ trait AbstractEncoder: * @return * The used writer */ - def closeEncapsulation(writer: Writer): Writer + def closeEncapsulation(writer: Writer): Writer = writer.writeBreak() object AbstractEncoder: /** @@ -259,7 +259,7 @@ trait EncapsulatedArrayEncoder[T](length: Int = 0) extends EncapsulatedEncoder[T override def write(writer: Writer, value: T): Writer = if length == 0 then writer.writeArrayStart() else writer.writeArrayOpen(length) writeEncapsulated(writer, value) - writer.writeArrayClose() + writer.writeBreak() /** * Object with extension methods for [[borer.Writer]]. @@ -387,7 +387,6 @@ class MapEncoder extends AbstractEncoder: override def writeValue[T: Encoder](writer: Writer, value: T): Writer = writer.write(value) override def openEncapsulation(writer: Writer): Writer = writer.writeMapStart() override def openEncapsulation(writer: Writer, amount: Int): Writer = writer.writeMapOpen(amount) - override def closeEncapsulation(writer: Writer): Writer = writer.writeMapClose() /** * Encoder that uses arrays to encode values. @@ -402,7 +401,6 @@ class ArrayEncoder extends AbstractEncoder: override def writeValue[T: Encoder](writer: Writer, value: T): Writer = writer.write(value) override def openEncapsulation(writer: Writer): Writer = writer.writeArrayStart() override def openEncapsulation(writer: Writer, amount: Int): Writer = writer.writeArrayOpen(amount) - override def closeEncapsulation(writer: Writer): Writer = writer.writeArrayClose() /** * Encoder that uses arrays to encode values, but preserves keys. From 37ee5115d2efc8644d82a567506da4ebc4d0b705 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Thu, 7 Mar 2024 09:05:14 +0100 Subject: [PATCH 45/97] fix(save): Order of saved objects not consistent A list is now used instead of a map --- .../main/scala/maf/save/save/Analysis.scala | 3 +- .../main/scala/maf/save/save/Component.scala | 29 ++++++++++++++----- .../main/scala/maf/save/save/Dependency.scala | 4 +-- .../src/main/scala/maf/save/save/Store.scala | 4 +-- 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/save/Analysis.scala b/code/shared/src/main/scala/maf/save/save/Analysis.scala index 81022994f..ea2609b84 100644 --- a/code/shared/src/main/scala/maf/save/save/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/save/Analysis.scala @@ -73,8 +73,7 @@ trait Save[Expr <: Expression] extends ModAnalysis[Expr]: * super.saveInfo + ("< key >" -> Savable(< saveValue >)) * }}} */ - def saveInfo: Map[String, Savable[_]] = - Map("name" -> Savable(analysisName)) + def saveInfo: List[(String, Savable[_])] = List(("name", Savable(analysisName))) /** The trait used to save the modF analysis. */ trait SaveModF diff --git a/code/shared/src/main/scala/maf/save/save/Component.scala b/code/shared/src/main/scala/maf/save/save/Component.scala index a8970b342..781e0b5e5 100644 --- a/code/shared/src/main/scala/maf/save/save/Component.scala +++ b/code/shared/src/main/scala/maf/save/save/Component.scala @@ -96,11 +96,14 @@ trait SaveComponents[Expr <: Expression] extends Save[Expr]: * encoder. */ def getComponentKeyEncoder: AbstractEncoder = getKeyEncoder - override def saveInfo: Map[String, Savable[_]] = super.saveInfo + ("components" -> Savable(visited)) + override def saveInfo: List[(String, Savable[_])] = super.saveInfo ++ List(("components", Savable(visited))) /** Encodes a component. */ given componentEncoder: Encoder[Component] + /** Encodes a set of components */ + protected given componentSetEncoder: Encoder[Set[Component]] + /** * Base trait for encoding components only by their ID, and not in their entirety. * @@ -130,12 +133,8 @@ trait SaveComponentID[Expr <: Expression] extends SavePosition[Expr]: trait SaveComponentIntID[Expr <: Expression] extends SaveComponents[Expr] with SaveComponentID[Expr]: private val components = HashMap[Component, Int]() private var id = 0 - override def saveInfo: Map[String, Savable[?]] = - val saveInfoMap = super.saveInfo - val component = saveInfoMap("components").asInstanceOf[Savable[Set[Component]]] - return saveInfoMap + ("components" -> Savable(component.value)) - private given EncapsulatedEncoder[Set[Component]] with + override protected given componentSetEncoder: EncapsulatedEncoder[Set[Component]] with override val encoder: AbstractEncoder = getComponentKeyEncoder override protected def writeEncapsulated(writer: Writer, components: Set[Component]): Writer = for (component <- components) do @@ -155,7 +154,17 @@ trait SaveComponentIntID[Expr <: Expression] extends SaveComponents[Expr] with S * @note * Because this trait only encodes the component position, the entire component should be encoded somewhere else if you want to decode this again. */ -trait SaveStandardSchemeComponentPosition extends SaveComponentID[SchemeExp] with StandardSchemeModFComponents: +trait SaveStandardSchemeComponentPosition extends SaveComponents[SchemeExp] with SaveComponentID[SchemeExp] with StandardSchemeModFComponents: + override type Component = SchemeModFComponent + + /** + * Get the encoder that will be used to encode a set of components. + * + * This will influence how a set of components will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an + * [[ArrayEncoder array-based]] encoder. + */ + def getComponentSetEncoder: AbstractEncoder = new ArrayEncoder + /** Encodes a component by their position */ override given componentIDEncoder: Encoder[Component] with def write(writer: Writer, component: Component): Writer = @@ -168,6 +177,12 @@ trait SaveStandardSchemeComponentPosition extends SaveComponentID[SchemeExp] wit val (lambda, _) = component.clo writer.write(lambda.idn.pos) + override protected given componentSetEncoder: EncapsulatedEncoder[Set[Component]] with + override val encoder: AbstractEncoder = getComponentSetEncoder + override protected def writeEncapsulated(writer: Writer, components: Set[Component]): Writer = + for (component <- components) do writer.writeMember(component) + writer + /** * Trait to encode environments. * diff --git a/code/shared/src/main/scala/maf/save/save/Dependency.scala b/code/shared/src/main/scala/maf/save/save/Dependency.scala index c4a22cc11..cb9d3436b 100644 --- a/code/shared/src/main/scala/maf/save/save/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/save/Dependency.scala @@ -49,8 +49,8 @@ trait SaveDependency[Expr <: Expression] extends SaveMapToArray with SaveCompone * encoder. */ def getDependencyEncoder: AbstractEncoder = getEncoder - override def saveInfo: Map[String, Savable[_]] = - super.saveInfo + ("dependencies" -> Savable(deps)) + override def saveInfo: List[(String, Savable[_])] = + super.saveInfo ++ List(("dependencies" -> Savable(deps))) /** * Encodes a dependency. diff --git a/code/shared/src/main/scala/maf/save/save/Store.scala b/code/shared/src/main/scala/maf/save/save/Store.scala index cafa072c3..de43f875b 100644 --- a/code/shared/src/main/scala/maf/save/save/Store.scala +++ b/code/shared/src/main/scala/maf/save/save/Store.scala @@ -232,5 +232,5 @@ trait SaveModularDomain extends SaveValue[SchemeExp] with ModularSchemeDomain: * depending on the values that are used in your analysis. */ trait SaveGlobalStore[Expr <: Expression] extends SaveValue[Expr] with SaveAddr[Expr] with SaveMapToArray with GlobalStore[Expr]: - override def saveInfo: Map[String, Savable[_]] = - super.saveInfo + ("store" -> Savable(store)) + override def saveInfo: List[(String, Savable[_])] = + return super.saveInfo ++ List(("store" -> Savable(store))) From 013754747f285d4fac4005c57c3e4b88607c159f Mon Sep 17 00:00:00 2001 From: Merlijn Date: Thu, 7 Mar 2024 14:02:24 +0100 Subject: [PATCH 46/97] feat(save): Save expressions separately Solves the issue of reaching max JSON depth when doing this recursively --- .../main/scala/maf/save/save/Analysis.scala | 4 + .../main/scala/maf/save/save/Component.scala | 43 +-- .../main/scala/maf/save/save/Expression.scala | 252 ++++++++++++++++++ .../src/main/scala/maf/save/save/Store.scala | 2 +- 4 files changed, 262 insertions(+), 39 deletions(-) create mode 100644 code/shared/src/main/scala/maf/save/save/Expression.scala diff --git a/code/shared/src/main/scala/maf/save/save/Analysis.scala b/code/shared/src/main/scala/maf/save/save/Analysis.scala index ea2609b84..ae40513b5 100644 --- a/code/shared/src/main/scala/maf/save/save/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/save/Analysis.scala @@ -9,6 +9,8 @@ import java.nio.file.Paths import java.nio.file.Files import maf.language.scheme.SchemeExp import EncapsulatedEncoder.* +import maf.save.save.SaveSchemeExpressions +import maf.save.save.SaveRecursiveSchemeExpressionsIntID /** * Contains info about the top-level objects that need to be saved. @@ -78,6 +80,8 @@ trait Save[Expr <: Expression] extends ModAnalysis[Expr]: /** The trait used to save the modF analysis. */ trait SaveModF extends Save[SchemeExp] + with SaveSchemeExpressions + with SaveRecursiveSchemeExpressionsIntID with SaveStandardSchemeComponents with SaveModularDomain with SaveAddrDep[SchemeExp] diff --git a/code/shared/src/main/scala/maf/save/save/Component.scala b/code/shared/src/main/scala/maf/save/save/Component.scala index 781e0b5e5..1bfac6891 100644 --- a/code/shared/src/main/scala/maf/save/save/Component.scala +++ b/code/shared/src/main/scala/maf/save/save/Component.scala @@ -40,6 +40,7 @@ import maf.language.scheme.SchemeValue import maf.language.scheme.SchemeSet import maf.language.scheme.SchemeBegin import maf.language.scheme.SchemeLetStar +import maf.save.save.SaveExpressions /** * Trait to encode positions. @@ -281,43 +282,9 @@ trait SaveStandardSchemeComponents with SaveValue[SchemeExp] with SavePosition[SchemeExp] with SaveEnvironment[SchemeExp] - with SaveContext[SchemeExp]: - - given EncapsulatedEncoder[SchemeExp] with - override val encoder = getComponentKeyEncoder - def writeEncapsulated(writer: Writer, exp: SchemeExp): Writer = - exp match - case funcall: SchemeFuncall => writer.writeMember("funcall", funcall) - case variable: SchemeVar => writer.writeMember("var", variable) - case lambda: SchemeLambda => writer.writeMember("lambda", lambda) - case argLambda: SchemeVarArgLambda => writer.writeMember("argLambda", argLambda) - case value: SchemeValue => writer.writeMember("value", value) - case letrec: SchemeLetrec => writer.writeMember("letrec", letrec) - case assert: SchemeAssert => writer.writeMember("assert", assert) - case let: SchemeLet => writer.writeMember("let", let) - case schemeIf: SchemeIf => writer.writeMember("schemeIf", schemeIf) - case set: SchemeSet => writer.writeMember("set", set) - case begin: SchemeBegin => writer.writeMember("begin", begin) - case letStar: SchemeLetStar => writer.writeMember("letStar", letStar) - case _ => - System.err.nn.println("The scheme expression with type `" + exp.getClass + "` could not be encoded") - writer - - private val compEncoder = getComponentEncoder - given Encoder[SchemeValue] = AbstractEncoder.deriveEncoder(compEncoder) - given Encoder[maf.language.sexp.Value] = AbstractEncoder.deriveAllEncoders(compEncoder) - given Encoder[SchemeFuncall] = AbstractEncoder.deriveEncoder[SchemeFuncall](compEncoder) - given Encoder[SchemeVar] = AbstractEncoder.deriveEncoder[SchemeVar](compEncoder) - given Encoder[SchemeLambda] = AbstractEncoder.deriveEncoder[SchemeLambda](compEncoder) - given Encoder[SchemeVarArgLambda] = AbstractEncoder.deriveEncoder[SchemeVarArgLambda](compEncoder) - given Encoder[SchemeLambdaExp] = AbstractEncoder.deriveEncoder[SchemeLambdaExp](compEncoder) - given Encoder[SchemeLetrec] = AbstractEncoder.deriveEncoder[SchemeLetrec](compEncoder) - given Encoder[SchemeAssert] = AbstractEncoder.deriveEncoder[SchemeAssert](compEncoder) - given Encoder[SchemeLet] = AbstractEncoder.deriveEncoder[SchemeLet](compEncoder) - given Encoder[SchemeIf] = AbstractEncoder.deriveEncoder[SchemeIf](compEncoder) - given Encoder[SchemeSet] = AbstractEncoder.deriveEncoder[SchemeSet](compEncoder) - given Encoder[SchemeBegin] = AbstractEncoder.deriveEncoder[SchemeBegin](compEncoder) - given Encoder[SchemeLetStar] = AbstractEncoder.deriveEncoder[SchemeLetStar](compEncoder) + with SaveContext[SchemeExp] + with SaveExpressions[SchemeExp]: + override type Component = SchemeModFComponent override given componentEncoder: Encoder[Component] with def write(writer: Writer, component: Component): Writer = @@ -329,6 +296,6 @@ trait SaveStandardSchemeComponents override def writeEncapsulated(writer: Writer, component: SchemeModFComponent.Call[T]): Writer = val (lambda, env) = component.clo val context = component.ctx - writer.writeMember("lambda", lambda) + writer.writeMember("lambda", lambda.asInstanceOf[SchemeExp]) writer.writeMember("environment", env) writer.writeMember("context", context.asInstanceOf[EncodeContext]) diff --git a/code/shared/src/main/scala/maf/save/save/Expression.scala b/code/shared/src/main/scala/maf/save/save/Expression.scala new file mode 100644 index 000000000..b5a9d234e --- /dev/null +++ b/code/shared/src/main/scala/maf/save/save/Expression.scala @@ -0,0 +1,252 @@ +package maf.save.save + +import maf.core.Expression +import maf.save.Save +import maf.save.AbstractEncoder +import io.bullet.borer.Encoder +import maf.save.Savable +import scala.collection.mutable.HashMap +import maf.save.EncapsulatedEncoder +import io.bullet.borer.Writer +import EncapsulatedEncoder.* +import maf.language.scheme.SchemeExp +import maf.language.scheme.SchemeFuncall +import maf.language.scheme.SchemeLambda +import maf.language.scheme.SchemeVarArgLambda +import maf.language.scheme.SchemeLetrec +import maf.language.scheme.SchemeAssert +import maf.language.scheme.SchemeLet +import maf.language.scheme.SchemeIf +import maf.language.scheme.SchemeSet +import maf.language.scheme.SchemeBegin +import maf.language.scheme.SchemeLetStar +import maf.save.SavePosition +import maf.save.ArrayEncoder +import maf.language.scheme.SchemeVar +import maf.language.scheme.SchemeValue +import maf.language.scheme.SchemeLambdaExp + +/** + * The base trait for encoding expressions. + * + * @note + * This trait gives the methods needed to encode expressions, but does not implement them yet, other traits like [[SaveSchemeExpressions]] or + * [[SaveExpressionIntID]] should be mixed in for the implementation. The trait that should be mixed in depends on the kind of components are used + * in your analysis. + * + * @tparam Expr + * The type of expression used in the analysis + */ +trait SaveExpressions[Expr <: Expression] extends Save[Expr]: + /** + * Get the encoder that will be used to encode expressions. + * + * This will influence how context will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] + * encoder. + */ + def getExpressionEncoder: AbstractEncoder = getEncoder + + /** + * Get the encoder that will be used to encode expression. + * + * This encoder is used to encode objects where the key is important, when you e.g. encode a type in the key, some encoders might remove this key, + * and should therefore not be used here. + * + * This will influence how components will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] + * encoder. + */ + def getExpressionKeyEncoder: AbstractEncoder = getKeyEncoder + + /** Encodes an expression */ + given expressionEncoder: Encoder[Expr] + +/** + * The base trait for encoding expressions. + * + * This trait is used to add [[actualExpressionEncoder]], this given cannot be added into [[SaveExpressions]] because this would cause an ambigious + * implicit with [[expressionEncoder]]. + * + * @note + * This trait gives the methods needed to encode expressions, but does not implement them yet, other traits like [[SaveSchemeExpressions]] or + * [[SaveExpressionIntID]] should be mixed in for the implementation. The trait that should be mixed in depends on the kind of components are used + * in your analysis. + * @note + * This trait should not be extended, rather, [[SaveExpressions]] should be extended. + * + * @tparam Expr + * The type of expression used in the analysis + */ +trait SaveActualExprs[Expr <: Expression] extends SaveExpressions[Expr]: + /** Encodes the actual expression, and doesn't encode it using IDs. */ + protected given actualExpressionEncoder: Encoder[Expr] + +/** + * The base trait for encoding expressions as IDs. + * + * This is an implementation of [[SaveExpressions]] + * + * @note + * This trait gives the methods needed to encode expression IDs, but does not implement them yet, other traits like + * [[SaveRecursiveSchemeExpressionsIntID]] or [[SaveExpressionIntID]] should be mixed in for the implementation. The trait that should be mixed in + * depends on the kind of components are used in your analysis. + * @note + * This trait will first save all necessary expressions separately and use IDs to save them to following times. + * + * @tparam Expr + * The type of expression used in the analysis + */ +trait SaveExpressionID[Expr <: Expression] extends Save[Expr] with SaveActualExprs[Expr]: + override given expressionEncoder: Encoder[Expr] = expressionIDEncoder + override def saveInfo: List[(String, Savable[_])] = super.saveInfo ++ List(("expressions" -> Savable(visited.map(expr(_))))) + + /** Encodes a set of expressions, this is used to e.g. add ID info to the expressions. */ + protected given expressionSetEncoder: Encoder[Set[Expr]] + + /** Encodes an expression using an ID */ + protected given expressionIDEncoder: Encoder[Expr] + +/** + * Trait to encode expressions using integer IDs. + * + * Implementation of [[SaveExpressionID]] + * + * @tparam Expr + * The type of expression used in the analysis + */ +trait SaveExpressionIntID[Expr <: Expression] extends SaveExpressionID[Expr] with SaveExpressions[Expr]: + private val expressions = HashMap[Expr, Int]() + private var id = 0 + + override protected given expressionSetEncoder: EncapsulatedEncoder[Set[Expr]] with + override val encoder = getExpressionKeyEncoder + def writeEncapsulated(writer: Writer, exprs: Set[Expr]): Writer = + for (expr <- exprs) do + println(id) + writer.writeMember(id.toString, expr)(using actualExpressionEncoder, encoder) + expressions.addOne((expr, id)) + id += 1 + writer + + override protected given expressionIDEncoder: Encoder[Expr] with + override def write(writer: Writer, expr: Expr): Writer = + if expressions.contains(expr) then writer.write(expressions(expr)) + else writer.write(expr)(using actualExpressionEncoder) + +/** + * Trait to encode scheme expressions recursively using integer IDs. + * + * This will recursively save every expression, this means that if you are e.g. encoding an `if` statement, this will first encode the condition, the + * consequence and the alternative, and only then save the actual `if` statement. + * + * Implementation of [[SaveExpressionID]] + */ +trait SaveRecursiveSchemeExpressionsIntID extends SaveExpressionID[SchemeExp] with SaveExpressions[SchemeExp]: + private val expressions = HashMap[SchemeExp, Int]() + private var id = 0 + + override protected given expressionSetEncoder: EncapsulatedEncoder[Set[SchemeExp]] with + override val encoder = getExpressionKeyEncoder + + given Encoder[List[SchemeExp]] with + override def write(writer: Writer, exprs: List[SchemeExp]): Writer = + for (expr <- exprs) writer.write(expr)(using recursiveExpressionEncoder) + writer + + given recursiveExpressionEncoder: Encoder[SchemeExp] with + override def write(writer: Writer, expr: SchemeExp): Writer = + if expressions.contains(expr) then return writer + expr match + case funcall: SchemeFuncall => + writer.write(funcall.args) + writer.write(funcall.f)(using recursiveExpressionEncoder) + case lambda: SchemeLambda => writer.write(lambda.body) + case argLambda: SchemeVarArgLambda => writer.write(argLambda.body) + case letrec: SchemeLetrec => + for (binding <- letrec.bindings) writer.write(binding._2) + writer.write(letrec.body) + case assert: SchemeAssert => writer.write(assert.exp) + case let: SchemeLet => + for (binding <- let.bindings) writer.write(binding._2) + writer.write(let.body) + case schemeIf: SchemeIf => + writer.write(schemeIf.cond) + writer.write(schemeIf.cons) + writer.write(schemeIf.alt) + case set: SchemeSet => + writer.write(set.value) + case begin: SchemeBegin => + writer.write(begin.exps) + case letStar: SchemeLetStar => + for (binding <- letStar.bindings) writer.write(binding._2) + writer.write(letStar.body) + case _ => writer + + writer.writeMember(id.toString(), expr)(using actualExpressionEncoder, encoder) + expressions.addOne(expr, id) + id += 1 + writer + + def writeEncapsulated(writer: Writer, exprs: Set[SchemeExp]): Writer = + for (expr <- exprs) do writer.write(expr)(using recursiveExpressionEncoder) + writer + + override protected given expressionIDEncoder: Encoder[SchemeExp] with + override def write(writer: Writer, expr: SchemeExp): Writer = + if expressions.contains(expr) then writer.write(expressions(expr)) + else writer.write(expr)(using actualExpressionEncoder) + +/** + * Save the expressions normally. + * + * Implementation of [[SaveExpressions]]. + */ +trait SaveActualExpressions[Expr <: Expression] extends SaveActualExprs[Expr]: + override given expressionEncoder: Encoder[Expr] = actualExpressionEncoder + +/** + * Save Scheme expressions. + * + * Implementation of [[SaveExpressions]]. + */ +trait SaveSchemeExpressions extends SaveActualExprs[SchemeExp] with SaveSchemeSubExpressions with SavePosition[SchemeExp]: + override protected given actualExpressionEncoder: EncapsulatedEncoder[SchemeExp] with + override val encoder = getExpressionKeyEncoder + def writeEncapsulated(writer: Writer, exp: SchemeExp): Writer = + exp match + case funcall: SchemeFuncall => writer.writeMember("funcall", funcall) + case variable: SchemeVar => writer.writeMember("var", variable) + case lambda: SchemeLambda => writer.writeMember("lambda", lambda) + case argLambda: SchemeVarArgLambda => writer.writeMember("argLambda", argLambda) + case value: SchemeValue => writer.writeMember("value", value) + case letrec: SchemeLetrec => writer.writeMember("letrec", letrec) + case assert: SchemeAssert => writer.writeMember("assert", assert) + case let: SchemeLet => writer.writeMember("let", let) + case schemeIf: SchemeIf => writer.writeMember("schemeIf", schemeIf) + case set: SchemeSet => writer.writeMember("set", set) + case begin: SchemeBegin => writer.writeMember("begin", begin) + case letStar: SchemeLetStar => writer.writeMember("letStar", letStar) + case _ => + System.err.nn.println("The scheme expression with type `" + exp.getClass + "` could not be encoded") + writer + +/** + * Save Scheme subexpressions. + * + * Implementation of [[SaveExpressions]]. + */ +trait SaveSchemeSubExpressions extends SaveExpressions[SchemeExp] with SavePosition[SchemeExp]: + private val compEncoder = getExpressionEncoder + given Encoder[SchemeValue] = AbstractEncoder.deriveEncoder(compEncoder) + given Encoder[maf.language.sexp.Value] = AbstractEncoder.deriveAllEncoders(compEncoder) + given Encoder[SchemeFuncall] = AbstractEncoder.deriveEncoder[SchemeFuncall](compEncoder) + given Encoder[SchemeVar] = AbstractEncoder.deriveEncoder[SchemeVar](compEncoder) + given Encoder[SchemeLambda] = AbstractEncoder.deriveEncoder[SchemeLambda](compEncoder) + given Encoder[SchemeVarArgLambda] = AbstractEncoder.deriveEncoder[SchemeVarArgLambda](compEncoder) + given Encoder[SchemeLambdaExp] = AbstractEncoder.deriveEncoder[SchemeLambdaExp](compEncoder) + given Encoder[SchemeLetrec] = AbstractEncoder.deriveEncoder[SchemeLetrec](compEncoder) + given Encoder[SchemeAssert] = AbstractEncoder.deriveEncoder[SchemeAssert](compEncoder) + given Encoder[SchemeLet] = AbstractEncoder.deriveEncoder[SchemeLet](compEncoder) + given Encoder[SchemeIf] = AbstractEncoder.deriveEncoder[SchemeIf](compEncoder) + given Encoder[SchemeSet] = AbstractEncoder.deriveEncoder[SchemeSet](compEncoder) + given Encoder[SchemeBegin] = AbstractEncoder.deriveEncoder[SchemeBegin](compEncoder) + given Encoder[SchemeLetStar] = AbstractEncoder.deriveEncoder[SchemeLetStar](compEncoder) diff --git a/code/shared/src/main/scala/maf/save/save/Store.scala b/code/shared/src/main/scala/maf/save/save/Store.scala index de43f875b..6f38e7747 100644 --- a/code/shared/src/main/scala/maf/save/save/Store.scala +++ b/code/shared/src/main/scala/maf/save/save/Store.scala @@ -122,7 +122,7 @@ trait SaveModularSchemeLattices override protected def writeEncapsulated(writer: Writer, closure: SchemeLattice#Clo): Writer = closure.closures.foreach((clo) => encoder.openEncapsulation(writer) - writer.writeMember("expression", clo._1) + writer.writeMember("expression", clo._1.asInstanceOf[SchemeExp]) writer.writeMember("address", clo._2.asInstanceOf[Environment[Address]]) encoder.closeEncapsulation(writer) ) From 71b63c344425857495689f8d1c70d8f9b4f8363d Mon Sep 17 00:00:00 2001 From: Merlijn Date: Fri, 8 Mar 2024 16:53:37 +0100 Subject: [PATCH 47/97] feat(load): Make `LoadComponents` more generic You should now extend `LoadComponents`, when decoding a component this can either decode an ID or an actual component based on the traits used. --- .../main/scala/maf/save/load/Analysis.scala | 2 +- .../main/scala/maf/save/load/Component.scala | 83 +++++++++++++------ .../main/scala/maf/save/load/Dependency.scala | 6 +- .../src/main/scala/maf/save/load/Store.scala | 8 +- 4 files changed, 67 insertions(+), 32 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/load/Analysis.scala b/code/shared/src/main/scala/maf/save/load/Analysis.scala index b3a559f3b..804431d1c 100644 --- a/code/shared/src/main/scala/maf/save/load/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/load/Analysis.scala @@ -80,12 +80,12 @@ trait Load[Expr <: Expression] extends ModAnalysis[Expr]: trait LoadModF extends Load[SchemeExp] with LoadComponents[SchemeExp] + with LoadComponentIntID[SchemeExp] with LoadStandardSchemeComponents with LoadNoContext[SchemeExp] with LoadSchemeAddr with LoadDependency[SchemeExp] with LoadAddrDependency[SchemeExp] - with LoadComponentIntID[SchemeExp] with LoadGlobalStore[SchemeExp] with LoadModularSchemeLattices: def getDecoder: AbstractDecoder = new MapDecoder diff --git a/code/shared/src/main/scala/maf/save/load/Component.scala b/code/shared/src/main/scala/maf/save/load/Component.scala index d0fc8d3b4..7c771b1d5 100644 --- a/code/shared/src/main/scala/maf/save/load/Component.scala +++ b/code/shared/src/main/scala/maf/save/load/Component.scala @@ -45,8 +45,8 @@ import maf.language.scheme.SchemeLetStar * * @note * This trait gives the methods needed to decode components, but does not implement them yet, other traits like [[LoadStandardSchemeComponents]] or - * [[LoadStandardSchemeComponentID]] should be mixed in for the implementation. The trait that should be mixed in depends on the kind of components - * are used in your analysis. + * [[LoadComponentIntID]] should be mixed in for the implementation. The trait that should be mixed in depends on the kind of components are used in + * your analysis. * * @tparam Expr * The type of expression used in the analysis @@ -71,18 +71,49 @@ trait LoadComponents[Expr <: Expression] extends Load[Expr]: */ def getComponentKeyDecoder: AbstractDecoder = getKeyDecoder - override def loadInfo: Map[String, Loadable[_]] = - super.loadInfo + ("components" -> Loadable((visited: Set[Component]) => this.visited = visited)) - given componentDecoder: Decoder[Component] +/** + * The base trait for decoding components. + * + * This trait is used to add [[actualComponentDecoder]], this given cannot be added into [[LoadComponents]] because this would cause an ambigious + * implicit with [[componentEncoder]]. + * + * @note + * This trait gives the methods needed to decode components, but does not implement them yet, other traits like [[LoadStandardSchemeComponents]] or + * [[LoadComponentIntID]] should be mixed in for the implementation. The trait that should be mixed in depends on the kind of components are used in + * your analysis. + * @note + * This trait should not be used, rather, [[LoadExpressions]] should be extended. + * + * @tparam Expr + * The type of expression used in the analysis + */ +trait LoadActualComps[Expr <: Expression] extends LoadComponents[Expr]: + /** Encodes the actual component. */ + given actualComponentDecoder: Decoder[Component] + +/** + * Load components normally. + * + * Implementation of [[LoadComponents]] + */ +trait LoadActualComponents[Expr <: Expression] extends LoadActualComps[Expr]: + override given componentDecoder: Decoder[Component] = actualComponentDecoder + +/** + * Load standard scheme components + * + * Implementation of [[LoadComponents]] + */ trait LoadStandardSchemeComponents - extends LoadComponents[SchemeExp] + extends LoadActualComps[SchemeExp] with StandardSchemeModFComponents with LoadContext[SchemeExp] with LoadPosition[SchemeExp] - with LoadEnvironment[SchemeExp]: - override given componentDecoder: Decoder[Component] with + with LoadEnvironment[SchemeExp] + with LoadExpressions[SchemeExp]: + override given actualComponentDecoder: Decoder[Component] with override def read(reader: Reader): Component = if reader.tryReadString("main") then return initialComponent else reader.read[SchemeModFComponent.Call[DecodeContext]]() @@ -259,8 +290,8 @@ trait LoadEnvironment[Expr <: Expression] extends Load[Expr] with LoadAddr[Expr] * The base trait for decoding components only by their ID. * * @note - * This trait gives the methods needed to decode context, but does not implement them yet, other traits like [[LoadStandardSchemeComponentID]] - * should be mixed in for the implementation. The trait that should be mixed in depends on the kind of context that is used in your analysis. + * This trait gives the methods needed to decode context, but does not implement them yet, other traits like [[LoadComponentIntID]] should be mixed + * in for the implementation. The trait that should be mixed in depends on the kind of context that is used in your analysis. * * @note * Because this trait only decodes the component IDs, the entire component should have already been decoded and placed in [[components]], so the ID @@ -269,7 +300,7 @@ trait LoadEnvironment[Expr <: Expression] extends Load[Expr] with LoadAddr[Expr] * @tparam Expr * The type of expression used in the analysis */ -trait LoadComponentID[Expr <: Expression] extends LoadComponents[Expr] with LoadPosition[Expr]: +trait LoadComponentID[Expr <: Expression] extends LoadActualComps[Expr] with LoadPosition[Expr]: /* The type of ID that will be used to load components */ type ID @@ -283,15 +314,15 @@ trait LoadComponentID[Expr <: Expression] extends LoadComponents[Expr] with Load * The component to register */ def addComponent(component: Component): Unit + override given componentDecoder: Decoder[Component] = componentIDDecoder given componentIDDecoder: Decoder[Component] + protected given componentSetDecoder: Decoder[Set[Component]] override def loadInfo: Map[String, Loadable[_]] = - val loadInfoMap = super.loadInfo - val components = loadInfoMap("components").asInstanceOf[Loadable[Set[Component]]] - loadInfoMap + ("components" -> Loadable((visited: Set[Component]) => + super.loadInfo + ("components" -> Loadable((visited: Set[Component]) => visited.foreach((component) => addComponent(component)) - components.load(visited) - )(using components.decoder)) + this.visited = visited + )) /** * Trait that decodes standard scheme components using their position. @@ -308,6 +339,11 @@ trait LoadStandardSchemeComponentPosition extends LoadComponentID[SchemeExp] wit if component != initialComponent then components.addOne(component.asInstanceOf[SchemeModFComponent.Call[DecodeContext]].clo._1.idn.pos, component) + override protected given componentSetDecoder: EncapsulatedDecoder[Set[Component]] with + override def decoder: AbstractDecoder = getComponentDecoder + override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Set[Component] = + return reader.readUntilBeforeBreak(Set[Component](), (components: Set[Component]) => components + reader.readMember[Component]().value) + override given componentIDDecoder: Decoder[Component] with override def read(reader: Reader): Component = if reader.tryReadString("main") then return initialComponent @@ -333,13 +369,15 @@ trait LoadComponentIntID[Expr <: Expression] extends LoadComponentID[Expr]: override given componentIDDecoder: Decoder[Component] with override def read(reader: Reader): Component = - val id = reader.readInt() - return components(id) + if reader.hasInt then + val id = reader.readInt() + return components(id) + else return reader.read[Component]()(using actualComponentDecoder) - private given EncapsulatedDecoder[Set[Component]] with + override protected given componentSetDecoder: EncapsulatedDecoder[Set[Component]] with override def decoder: AbstractDecoder = getComponentKeyDecoder override protected def readEncapsulated(reader: Reader)(using decoder: AbstractDecoder): Set[Component] = - reader.readUntilBeforeBreak( + return reader.readUntilBeforeBreak( Set[Component](), (components: Set[Component]) => val component = reader.readMember[Component]()(using componentDecoder, decoder) @@ -347,8 +385,3 @@ trait LoadComponentIntID[Expr <: Expression] extends LoadComponentID[Expr]: LoadComponentIntID.this.components.addOne((key, component.value)) components + (component.value) ) - - override def loadInfo: Map[String, Loadable[?]] = - val loadInfoMap = super.loadInfo - val component = loadInfoMap("components").asInstanceOf[Loadable[Set[Component]]] - return loadInfoMap + ("components" -> Loadable(component.load)) diff --git a/code/shared/src/main/scala/maf/save/load/Dependency.scala b/code/shared/src/main/scala/maf/save/load/Dependency.scala index ead958ed7..0a8e48386 100644 --- a/code/shared/src/main/scala/maf/save/load/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/load/Dependency.scala @@ -41,7 +41,7 @@ trait LoadAddrDependency[Expr <: Expression] extends LoadDependency[Expr] with L * @tparam Expr * The type of expression used in the analysis */ -trait LoadDependency[Expr <: Expression] extends LoadMapToArray with LoadComponentID[Expr]: +trait LoadDependency[Expr <: Expression] extends LoadMapToArray with LoadComponents[Expr]: /** * Get the decoder that will be used to decode dependencies. * @@ -114,7 +114,7 @@ trait LoadAddr[Expr <: Expression] extends Load[Expr] with LoadPosition[Expr]: * * This is an implementation of [[LoadAddr]]. */ -trait LoadSchemeAddr extends LoadAddr[SchemeExp] with LoadContext[SchemeExp] with LoadComponentID[SchemeExp] with LoadStandardSchemeComponents: +trait LoadSchemeAddr extends LoadAddr[SchemeExp] with LoadContext[SchemeExp] with LoadComponents[SchemeExp] with LoadExpressions[SchemeExp]: override def addressDecoders = super.addressDecoders ++ List( ("varAddr", summon[Decoder[VarAddr[DecodeContext]]]), @@ -126,7 +126,7 @@ trait LoadSchemeAddr extends LoadAddr[SchemeExp] with LoadContext[SchemeExp] wit given EncapsulatedDecoder[ReturnAddr[Component]] with override def decoder: AbstractDecoder = getAddressDecoder override protected def readEncapsulated(reader: Reader)(using decoder: AbstractDecoder): ReturnAddr[Component] = - val component = reader.readMember[Component]("component")(using componentIDDecoder, decoder) + val component = reader.readMember[Component]("component") val identity = reader.readMember[Identity]("identity") return new ReturnAddr[Component](component.value, identity.value) diff --git a/code/shared/src/main/scala/maf/save/load/Store.scala b/code/shared/src/main/scala/maf/save/load/Store.scala index 81918c837..ec0ad54b7 100644 --- a/code/shared/src/main/scala/maf/save/load/Store.scala +++ b/code/shared/src/main/scala/maf/save/load/Store.scala @@ -16,6 +16,7 @@ import maf.lattice.ConstantPropagation import maf.language.scheme.lattices.SchemeLattice import maf.language.scheme.SchemeLambdaExp import maf.core.Address +import maf.modular.scheme.modf.BaseSchemeModFSemanticsM /** * The base trait for decoding [[AbstractDomain.Value values]]. @@ -100,11 +101,12 @@ trait LoadLattice[Expr <: Expression] extends Load[Expr]: trait LoadModularSchemeLattices extends LoadModularDomain with LoadAddr[SchemeExp] - with LoadStandardSchemeComponents + with LoadExpressions[SchemeExp] + with BaseSchemeModFSemanticsM with LoadEnvironment[SchemeExp] with LoadLattice[SchemeExp]: type SchemeLattice = ModularSchemeLattice[?, ?, ?, ?, ?, ?, ?] - override def hMapDecoders = super.hMapDecoders + ( + override def hMapDecoders = super.hMapDecoders ++ Set( ("int", summon[Decoder[(HMapKey, SchemeLattice#Int)]]), ("boolean", summon[Decoder[(HMapKey, SchemeLattice#Bool)]]), ("string", summon[Decoder[(HMapKey, SchemeLattice#Str)]]), @@ -144,7 +146,7 @@ trait LoadModularSchemeLattices given EncapsulatedDecoder[(SchemeLambdaExp, Env)] with override def decoder: AbstractDecoder = getValueDecoder override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): (SchemeLambdaExp, Env) = - val expression = reader.readMember[SchemeLambdaExp]("expression") + val expression = reader.readMember[SchemeExp]("expression").asInstanceOf[ReadValue[String, SchemeLambdaExp]] val address = reader.readMember[Env]("address") return (expression.value, address.value) From 339425fd5b6bcac5f60ced9e3015c5d3bc67a9df Mon Sep 17 00:00:00 2001 From: Merlijn Date: Fri, 8 Mar 2024 16:59:28 +0100 Subject: [PATCH 48/97] fix(load): Order of loaded objects not consistent A list is now used to determine the order that the objects are loaded instead of a map. --- code/shared/src/main/scala/maf/save/load/Analysis.scala | 3 +-- code/shared/src/main/scala/maf/save/load/Component.scala | 6 +++--- code/shared/src/main/scala/maf/save/load/Dependency.scala | 4 ++-- code/shared/src/main/scala/maf/save/load/Store.scala | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/load/Analysis.scala b/code/shared/src/main/scala/maf/save/load/Analysis.scala index 804431d1c..cff301dea 100644 --- a/code/shared/src/main/scala/maf/save/load/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/load/Analysis.scala @@ -73,8 +73,7 @@ trait Load[Expr <: Expression] extends ModAnalysis[Expr]: * super.loadInfo + ("< key >" -> Loadable[< loadType >]((< loaded object >: < SaveType >) => < put loaded object into analysis >)) * }}} */ - def loadInfo: Map[String, Loadable[_]] = - Map("name" -> Loadable((name: String) => println(name))) + def loadInfo: List[(String, Loadable[_])] = List(("name", Loadable((name: String) => ()))) /** The trait used to load the modF analysis. */ trait LoadModF diff --git a/code/shared/src/main/scala/maf/save/load/Component.scala b/code/shared/src/main/scala/maf/save/load/Component.scala index 7c771b1d5..9c7c19685 100644 --- a/code/shared/src/main/scala/maf/save/load/Component.scala +++ b/code/shared/src/main/scala/maf/save/load/Component.scala @@ -318,11 +318,11 @@ trait LoadComponentID[Expr <: Expression] extends LoadActualComps[Expr] with Loa given componentIDDecoder: Decoder[Component] protected given componentSetDecoder: Decoder[Set[Component]] - override def loadInfo: Map[String, Loadable[_]] = - super.loadInfo + ("components" -> Loadable((visited: Set[Component]) => + override def loadInfo: List[(String, Loadable[_])] = + super.loadInfo ++ List(("components" -> Loadable((visited: Set[Component]) => visited.foreach((component) => addComponent(component)) this.visited = visited - )) + ))) /** * Trait that decodes standard scheme components using their position. diff --git a/code/shared/src/main/scala/maf/save/load/Dependency.scala b/code/shared/src/main/scala/maf/save/load/Dependency.scala index 0a8e48386..d22cdd51e 100644 --- a/code/shared/src/main/scala/maf/save/load/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/load/Dependency.scala @@ -60,8 +60,8 @@ trait LoadDependency[Expr <: Expression] extends LoadMapToArray with LoadCompone * decoder. */ def getDependencyKeyDecoder: AbstractDecoder = getKeyDecoder - override def loadInfo: Map[String, Loadable[_]] = - super.loadInfo + ("dependencies" -> Loadable((deps: Map[Dependency, Set[Component]]) => this.deps = deps)) + override def loadInfo: List[(String, Loadable[_])] = + super.loadInfo ++ List(("dependencies", Loadable((deps: Map[Dependency, Set[Component]]) => this.deps = deps))) /** Returns a map that links a key to a specific decoder. */ def dependencyDecoders = Set[(String, Decoder[_ <: Dependency])]() diff --git a/code/shared/src/main/scala/maf/save/load/Store.scala b/code/shared/src/main/scala/maf/save/load/Store.scala index ec0ad54b7..19af02036 100644 --- a/code/shared/src/main/scala/maf/save/load/Store.scala +++ b/code/shared/src/main/scala/maf/save/load/Store.scala @@ -221,4 +221,4 @@ trait LoadModularDomain extends LoadValue[SchemeExp] with ModularSchemeDomain: * depending on the values that are used in your analysis. */ trait LoadGlobalStore[Expr <: Expression] extends LoadValue[Expr] with LoadAddr[Expr] with LoadMapToArray with GlobalStore[Expr]: - override def loadInfo = super.loadInfo + ("store" -> Loadable((store: Map[Addr, Value]) => this.store = store)) + override def loadInfo = super.loadInfo ++ List(("store", Loadable((store: Map[Addr, Value]) => this.store = store))) From c33e327f529f1c9f94a8bac1be07c446eb788d29 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Fri, 8 Mar 2024 17:01:50 +0100 Subject: [PATCH 49/97] fix(load): Can't lattices that where encoded correctly If a lattice can't be encoded, an error message is added. When this error message is encountered during decoding, a `nil` value is used instead of the missing value. --- code/shared/src/main/scala/maf/save/load/Store.scala | 11 +++++++++-- code/shared/src/main/scala/maf/save/save/Store.scala | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/load/Store.scala b/code/shared/src/main/scala/maf/save/load/Store.scala index 19af02036..35830b861 100644 --- a/code/shared/src/main/scala/maf/save/load/Store.scala +++ b/code/shared/src/main/scala/maf/save/load/Store.scala @@ -115,10 +115,17 @@ trait LoadModularSchemeLattices ("pointer", summon[Decoder[(HMapKey, SchemeLattice#Pointer)]]), ("symbol", summon[Decoder[(HMapKey, SchemeLattice#Symbol)]]), ("cons", summon[Decoder[(HMapKey, SchemeLattice#Cons)]]), - ("nil", summon[Decoder[(HMapKey, modularLattice.Nil.type)]]), + ("nil", nilLatticeDecoder), ("void", summon[Decoder[(HMapKey, modularLattice.Void.type)]]), + ("ERROR", errorDecoder) ) + private given errorDecoder: Decoder[(HMapKey, modularLattice.Nil.type)] with + override def read(reader: Reader): (HMapKey, modularLattice.Nil.type) = + val error = reader.read[String]() + System.err.nn.println("The lattice was not correctly encoded and had error: `" + error + "`, using `nil` instead.") + return (modularLattice.NilT, modularLattice.Nil) + given Decoder[(HMapKey, SchemeLattice#Int)] with override def read(reader: Reader): (HMapKey, SchemeLattice#Int) = val lattice = reader.read[Lattice[BigInt]]() @@ -175,7 +182,7 @@ trait LoadModularSchemeLattices val cdr = reader.readMember[SchemeLattice#L]("cdr") return (modularLattice.ConsT, new modularLattice.Cons(car.value, cdr.value)) - given Decoder[(HMapKey, modularLattice.Nil.type)] with + given nilLatticeDecoder: Decoder[(HMapKey, modularLattice.Nil.type)] with override def read(reader: Reader): (HMapKey, modularLattice.Nil.type) = return (modularLattice.NilT, modularLattice.Nil) given Decoder[(HMapKey, modularLattice.Void.type)] with diff --git a/code/shared/src/main/scala/maf/save/save/Store.scala b/code/shared/src/main/scala/maf/save/save/Store.scala index 6f38e7747..55e8f2013 100644 --- a/code/shared/src/main/scala/maf/save/save/Store.scala +++ b/code/shared/src/main/scala/maf/save/save/Store.scala @@ -158,7 +158,7 @@ trait SaveModularSchemeLattices case modularLattice.Void => writer.writeMember("void", "") case _ => System.err.nn.println("The lattice with type `" + key.getClass + "` could not be encoded") - writer + writer.writeMember("ERROR", "Unknown type: " + key.getClass.toString()) } override def encodeHMapPair(writer: Writer, key: HMapKey, value: Any)(using encoder: AbstractEncoder): Writer = From 8e0169bc91621cc03f5f2b373a661a952c8e702b Mon Sep 17 00:00:00 2001 From: Merlijn Date: Fri, 8 Mar 2024 17:04:40 +0100 Subject: [PATCH 50/97] feat(load): Decode expressions separately Add a generic `LoadExpressions` that will either decode the expression normally, or place all expressions in a list, and then decode them using their ID. --- .../main/scala/maf/save/load/Analysis.scala | 2 + .../main/scala/maf/save/load/Component.scala | 38 +---- .../main/scala/maf/save/load/Expression.scala | 146 ++++++++++++++++++ .../main/scala/maf/save/save/Expression.scala | 2 +- 4 files changed, 150 insertions(+), 38 deletions(-) create mode 100644 code/shared/src/main/scala/maf/save/load/Expression.scala diff --git a/code/shared/src/main/scala/maf/save/load/Analysis.scala b/code/shared/src/main/scala/maf/save/load/Analysis.scala index cff301dea..dc6a436f8 100644 --- a/code/shared/src/main/scala/maf/save/load/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/load/Analysis.scala @@ -78,6 +78,8 @@ trait Load[Expr <: Expression] extends ModAnalysis[Expr]: /** The trait used to load the modF analysis. */ trait LoadModF extends Load[SchemeExp] + with LoadExpressionIntID[SchemeExp] + with LoadSchemeExpressions with LoadComponents[SchemeExp] with LoadComponentIntID[SchemeExp] with LoadStandardSchemeComponents diff --git a/code/shared/src/main/scala/maf/save/load/Component.scala b/code/shared/src/main/scala/maf/save/load/Component.scala index 9c7c19685..49649d726 100644 --- a/code/shared/src/main/scala/maf/save/load/Component.scala +++ b/code/shared/src/main/scala/maf/save/load/Component.scala @@ -121,46 +121,10 @@ trait LoadStandardSchemeComponents given EncapsulatedDecoder[SchemeModFComponent.Call[DecodeContext]] with override val decoder = getComponentDecoder override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): SchemeModFComponent.Call[DecodeContext] = - val lambda = reader.readMember[SchemeLambdaExp]("lambda") + val lambda = reader.readMember[SchemeExp]("lambda").asInstanceOf[ReadValue[String, SchemeLambdaExp]] val environment = reader.readMember[Environment[Address]]("environment") val context = reader.readMember[DecodeContext]("context") return new SchemeModFComponent.Call[DecodeContext]((lambda.value, environment.value), context.value) - private val compDecoder = getComponentDecoder - given Decoder[SchemeValue] = AbstractDecoder.deriveDecoder(compDecoder) - given Decoder[maf.language.sexp.Value] = AbstractDecoder.deriveAllDecoders(compDecoder) - given Decoder[SchemeFuncall] = AbstractDecoder.deriveDecoder[SchemeFuncall](compDecoder) - given Decoder[SchemeVar] = AbstractDecoder.deriveDecoder[SchemeVar](compDecoder) - given Decoder[SchemeLambda] = AbstractDecoder.deriveDecoder[SchemeLambda](compDecoder) - given Decoder[SchemeVarArgLambda] = AbstractDecoder.deriveDecoder[SchemeVarArgLambda](compDecoder) - given Decoder[SchemeLambdaExp] = AbstractDecoder.deriveDecoder[SchemeLambdaExp](compDecoder) - given Decoder[SchemeLetrec] = AbstractDecoder.deriveDecoder[SchemeLetrec](compDecoder) - given Decoder[SchemeAssert] = AbstractDecoder.deriveDecoder[SchemeAssert](compDecoder) - given Decoder[SchemeLet] = AbstractDecoder.deriveDecoder[SchemeLet](compDecoder) - given Decoder[SchemeIf] = AbstractDecoder.deriveDecoder[SchemeIf](compDecoder) - given Decoder[SchemeSet] = AbstractDecoder.deriveDecoder[SchemeSet](compDecoder) - given Decoder[SchemeBegin] = AbstractDecoder.deriveDecoder[SchemeBegin](compDecoder) - given Decoder[SchemeLetStar] = AbstractDecoder.deriveDecoder[SchemeLetStar](compDecoder) - - given EncapsulatedDecoder[SchemeExp] with - override val decoder = getComponentKeyDecoder - override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): SchemeExp = - val expression = reader.readMembers[SchemeExp]( - Array( - ("funcall", summon[Decoder[SchemeFuncall]]), - ("var", summon[Decoder[SchemeVar]]), - ("lambda", summon[Decoder[SchemeLambda]]), - ("argLambda", summon[Decoder[SchemeVarArgLambda]]), - ("value", summon[Decoder[SchemeValue]]), - ("letrec", summon[Decoder[SchemeLetrec]]), - ("assert", summon[Decoder[SchemeAssert]]), - ("let", summon[Decoder[SchemeLet]]), - ("schemeIf", summon[Decoder[SchemeIf]]), - ("set", summon[Decoder[SchemeSet]]), - ("begin", summon[Decoder[SchemeBegin]]), - ("letStar", summon[Decoder[SchemeLetStar]]), - ) - ) - expression.value /** * The base trait for decoding context. diff --git a/code/shared/src/main/scala/maf/save/load/Expression.scala b/code/shared/src/main/scala/maf/save/load/Expression.scala new file mode 100644 index 000000000..cbeefb5ea --- /dev/null +++ b/code/shared/src/main/scala/maf/save/load/Expression.scala @@ -0,0 +1,146 @@ +package maf.save + +import maf.save.EncapsulatedDecoder.* + +import maf.core.Expression +import io.bullet.borer.Decoder +import maf.language.scheme.SchemeExp +import maf.language.scheme.SchemeValue +import maf.language.scheme.SchemeFuncall +import maf.language.scheme.SchemeVar +import maf.language.scheme.SchemeLambda +import maf.language.scheme.SchemeVarArgLambda +import maf.language.scheme.SchemeLambdaExp +import maf.language.scheme.SchemeLetrec +import maf.language.scheme.SchemeLet +import maf.language.scheme.SchemeIf +import maf.language.scheme.SchemeSet +import maf.language.scheme.SchemeBegin +import maf.language.scheme.SchemeLetStar +import maf.language.scheme.SchemeAssert +import io.bullet.borer.Reader +import scala.collection.mutable.HashMap + +/** + * The base trait for decoding expressions. + * + * @note + * This trait gives the methods needed to decode expressions, but does not implement them yet, other traits like [[LoadSchemeExpressions]] or + * [[LoadExpressionIntID]] should be mixed in for the implementation. The trait that should be mixed in depends on the kind of expressions are used + * in your analysis. + * + * @tparam Expr + * The type of expression used in the analysis + */ +trait LoadExpressions[Expr <: Expression] extends Load[Expr]: + /** + * Get the decoder that will be used to decode expressions. + * + * This will influence how expressions will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] + * decoder. + */ + def getExpressionDecoder: AbstractDecoder = getDecoder + + /** + * Get the decoder that will be used to decode expressions. + * + * This decoder is used to decode objects where the key is important, when you want to e.g. decode a type from the key, some decoders might ignore + * this key, and should therefore not be used here. + * + * This will influence how expressions will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] + * decoder. + */ + def getExpressionKeyDecoder: AbstractDecoder = getKeyDecoder + + given expressionDecoder: Decoder[Expr] + +/** + * The base trait for decoding expressions. + * + * This trait is used to add [[actualExpressionDecoder]], this given cannot be added into [[LoadExpressions]] because this would cause an ambigious + * implicit with [[expressionEncoder]]. + * + * @note + * This trait gives the methods needed to decode expressions, but does not implement them yet, other traits like [[LoadSchemeExpressions]] or + * [[LoadExpressionIntID]] should be mixed in for the implementation. The trait that should be mixed in depends on the kind of expressions are used + * in your analysis. + * @note + * This trait should not be used, rather, [[LoadExpressions]] should be extended. + * + * @tparam Expr + * The type of expression used in the analysis + */ +trait LoadActualExprs[Expr <: Expression] extends LoadExpressions[Expr]: + /** Decodes the actual expression, and doesn't decode it using IDs. */ + protected given actualExpressionDecoder: Decoder[Expr] + +trait LoadActualExpressions[Expr <: Expression] extends LoadActualExprs[Expr]: + override given expressionDecoder: Decoder[Expr] = actualExpressionDecoder + +trait LoadExpressionID[Expr <: Expression] extends Load[Expr] with LoadActualExprs[Expr]: + override given expressionDecoder: Decoder[Expr] = expressionIDDecoder + override def loadInfo: List[(String, Loadable[?])] = + super.loadInfo ++ List(("expressions", Loadable((expressions: Set[Expr]) => ()))) + + /** Decodes a set of expressions, this is used to e.g. get ID info to the expressions. */ + protected given expressionSetDecoder: Decoder[Set[Expr]] + + /** Decodes an expression using an ID */ + protected given expressionIDDecoder: Decoder[Expr] + +trait LoadExpressionIntID[Expr <: Expression] extends LoadExpressionID[Expr]: + private val expressions: HashMap[Int, Expr] = HashMap[Int, Expr]() + override protected given expressionSetDecoder: EncapsulatedDecoder[Set[Expr]] with + override def decoder: AbstractDecoder = getExpressionKeyDecoder + override protected def readEncapsulated(reader: Reader)(using decoder: AbstractDecoder): Set[Expr] = + return reader.readUntilBeforeBreak( + Set[Expr](), + (expressions: Set[Expr]) => + val expression = reader.readMember[Expr]()(using actualExpressionDecoder, decoder) + val key = expression.key.toInt + LoadExpressionIntID.this.expressions.addOne((key, expression.value)) + expressions + (expression.value) + ) + override protected given expressionIDDecoder: Decoder[Expr] with + override def read(reader: Reader): Expr = + if reader.hasInt then return expressions(reader.readInt()) + else reader.read[Expr]()(using actualExpressionDecoder) + +trait LoadSchemeSubExpressions extends LoadExpressions[SchemeExp] with LoadPosition[SchemeExp]: + private val compDecoder = getExpressionDecoder + given Decoder[SchemeValue] = AbstractDecoder.deriveDecoder(compDecoder) + given Decoder[maf.language.sexp.Value] = AbstractDecoder.deriveAllDecoders(compDecoder) + given Decoder[SchemeFuncall] = AbstractDecoder.deriveDecoder[SchemeFuncall](compDecoder) + given Decoder[SchemeVar] = AbstractDecoder.deriveDecoder[SchemeVar](compDecoder) + given Decoder[SchemeLambda] = AbstractDecoder.deriveDecoder[SchemeLambda](compDecoder) + given Decoder[SchemeVarArgLambda] = AbstractDecoder.deriveDecoder[SchemeVarArgLambda](compDecoder) + given Decoder[SchemeLambdaExp] = AbstractDecoder.deriveDecoder[SchemeLambdaExp](compDecoder) + given Decoder[SchemeLetrec] = AbstractDecoder.deriveDecoder[SchemeLetrec](compDecoder) + given Decoder[SchemeAssert] = AbstractDecoder.deriveDecoder[SchemeAssert](compDecoder) + given Decoder[SchemeLet] = AbstractDecoder.deriveDecoder[SchemeLet](compDecoder) + given Decoder[SchemeIf] = AbstractDecoder.deriveDecoder[SchemeIf](compDecoder) + given Decoder[SchemeSet] = AbstractDecoder.deriveDecoder[SchemeSet](compDecoder) + given Decoder[SchemeBegin] = AbstractDecoder.deriveDecoder[SchemeBegin](compDecoder) + given Decoder[SchemeLetStar] = AbstractDecoder.deriveDecoder[SchemeLetStar](compDecoder) + +trait LoadSchemeExpressions extends LoadActualExprs[SchemeExp] with LoadSchemeSubExpressions: + override protected given actualExpressionDecoder: EncapsulatedDecoder[SchemeExp] with + override val decoder = getExpressionKeyDecoder + override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): SchemeExp = + val expression = reader.readMembers[SchemeExp]( + Array( + ("funcall", summon[Decoder[SchemeFuncall]]), + ("var", summon[Decoder[SchemeVar]]), + ("lambda", summon[Decoder[SchemeLambda]]), + ("argLambda", summon[Decoder[SchemeVarArgLambda]]), + ("value", summon[Decoder[SchemeValue]]), + ("letrec", summon[Decoder[SchemeLetrec]]), + ("assert", summon[Decoder[SchemeAssert]]), + ("let", summon[Decoder[SchemeLet]]), + ("schemeIf", summon[Decoder[SchemeIf]]), + ("set", summon[Decoder[SchemeSet]]), + ("begin", summon[Decoder[SchemeBegin]]), + ("letStar", summon[Decoder[SchemeLetStar]]), + ) + ) + expression.value diff --git a/code/shared/src/main/scala/maf/save/save/Expression.scala b/code/shared/src/main/scala/maf/save/save/Expression.scala index b5a9d234e..aa8d9804f 100644 --- a/code/shared/src/main/scala/maf/save/save/Expression.scala +++ b/code/shared/src/main/scala/maf/save/save/Expression.scala @@ -71,7 +71,7 @@ trait SaveExpressions[Expr <: Expression] extends Save[Expr]: * [[SaveExpressionIntID]] should be mixed in for the implementation. The trait that should be mixed in depends on the kind of components are used * in your analysis. * @note - * This trait should not be extended, rather, [[SaveExpressions]] should be extended. + * This trait should not be used, rather, [[SaveExpressions]] should be extended. * * @tparam Expr * The type of expression used in the analysis From ce23d18cf73d56e0e432696af2ad2a3ed8c76cbb Mon Sep 17 00:00:00 2001 From: Merlijn Date: Sat, 9 Mar 2024 07:50:55 +0100 Subject: [PATCH 51/97] feat(save): Make 'SaveComponents' save either IDs or components When extending 'SaveComponents', it will now either encode IDs or components based on the traits that are mixed in, when IDs are used, the components are also saved separately. --- .../main/scala/maf/save/save/Analysis.scala | 4 +-- .../main/scala/maf/save/save/Component.scala | 25 ++++++++++++------- .../main/scala/maf/save/save/Dependency.scala | 7 +++--- .../src/main/scala/maf/save/save/Store.scala | 3 ++- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/save/Analysis.scala b/code/shared/src/main/scala/maf/save/save/Analysis.scala index ae40513b5..cfd15a35b 100644 --- a/code/shared/src/main/scala/maf/save/save/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/save/Analysis.scala @@ -82,13 +82,13 @@ trait SaveModF extends Save[SchemeExp] with SaveSchemeExpressions with SaveRecursiveSchemeExpressionsIntID + with SaveComponentIntID[SchemeExp] with SaveStandardSchemeComponents with SaveModularDomain with SaveAddrDep[SchemeExp] with SaveSchemeAddr with SaveGlobalStore[SchemeExp] with SaveModularSchemeLattices - with SaveNoContext[SchemeExp] - with SaveComponentIntID[SchemeExp]: + with SaveNoContext[SchemeExp]: override def getEncoder: AbstractEncoder = new MapEncoder override def getKeyEncoder: AbstractEncoder = new MapEncoder diff --git a/code/shared/src/main/scala/maf/save/save/Component.scala b/code/shared/src/main/scala/maf/save/save/Component.scala index 1bfac6891..579b8e741 100644 --- a/code/shared/src/main/scala/maf/save/save/Component.scala +++ b/code/shared/src/main/scala/maf/save/save/Component.scala @@ -97,13 +97,15 @@ trait SaveComponents[Expr <: Expression] extends Save[Expr]: * encoder. */ def getComponentKeyEncoder: AbstractEncoder = getKeyEncoder - override def saveInfo: List[(String, Savable[_])] = super.saveInfo ++ List(("components", Savable(visited))) /** Encodes a component. */ given componentEncoder: Encoder[Component] - /** Encodes a set of components */ - protected given componentSetEncoder: Encoder[Set[Component]] +protected trait SaveActualComps[Expr <: Expression] extends SaveComponents[Expr]: + given actualComponentEncoder: Encoder[Component] + +trait SaveActualComponents[Expr <: Expression] extends SaveActualComps[Expr]: + override given componentEncoder: Encoder[Component] = actualComponentEncoder /** * Base trait for encoding components only by their ID, and not in their entirety. @@ -119,9 +121,14 @@ trait SaveComponents[Expr <: Expression] extends Save[Expr]: * @tparam T * The type of the value the needs to be saved */ -trait SaveComponentID[Expr <: Expression] extends SavePosition[Expr]: +trait SaveComponentID[Expr <: Expression] extends SaveComponents[Expr] with SavePosition[Expr]: /** Encodes a component by their ID */ given componentIDEncoder: Encoder[Component] + override given componentEncoder: Encoder[Component] = componentIDEncoder + + /** Encodes a set of components */ + protected given componentSetEncoder: Encoder[Set[Component]] + override def saveInfo: List[(String, Savable[_])] = super.saveInfo ++ List(("components", Savable(visited))) /** * Trait that encodes components using an autoincreasing integer ID. @@ -131,7 +138,7 @@ trait SaveComponentID[Expr <: Expression] extends SavePosition[Expr]: * @tparam Expr * The type of expression used in the analysis */ -trait SaveComponentIntID[Expr <: Expression] extends SaveComponents[Expr] with SaveComponentID[Expr]: +trait SaveComponentIntID[Expr <: Expression] extends SaveActualComps[Expr] with SaveComponentID[Expr]: private val components = HashMap[Component, Int]() private var id = 0 @@ -140,7 +147,7 @@ trait SaveComponentIntID[Expr <: Expression] extends SaveComponents[Expr] with S override protected def writeEncapsulated(writer: Writer, components: Set[Component]): Writer = for (component <- components) do SaveComponentIntID.this.components.addOne((component, id)) - writer.writeMember(id.toString(), component)(using componentEncoder, encoder) + writer.writeMember(id.toString(), component)(using actualComponentEncoder, encoder) id += 1 writer @@ -155,7 +162,7 @@ trait SaveComponentIntID[Expr <: Expression] extends SaveComponents[Expr] with S * @note * Because this trait only encodes the component position, the entire component should be encoded somewhere else if you want to decode this again. */ -trait SaveStandardSchemeComponentPosition extends SaveComponents[SchemeExp] with SaveComponentID[SchemeExp] with StandardSchemeModFComponents: +trait SaveStandardSchemeComponentPosition extends SaveComponentID[SchemeExp] with StandardSchemeModFComponents: override type Component = SchemeModFComponent /** @@ -276,7 +283,7 @@ trait SaveNoContext[Expr <: Expression] extends SaveContext[Expr]: * This is an implementation of [[SaveComponents]]. */ trait SaveStandardSchemeComponents - extends SaveComponents[SchemeExp] + extends SaveActualComps[SchemeExp] with StandardSchemeModFComponents with AnalysisResults[SchemeExp] with SaveValue[SchemeExp] @@ -286,7 +293,7 @@ trait SaveStandardSchemeComponents with SaveExpressions[SchemeExp]: override type Component = SchemeModFComponent - override given componentEncoder: Encoder[Component] with + override given actualComponentEncoder: Encoder[Component] with def write(writer: Writer, component: Component): Writer = if component.equals(initialComponent) then writer.write("main") else writer.write(component.asInstanceOf[SchemeModFComponent.Call[ComponentContext]]) diff --git a/code/shared/src/main/scala/maf/save/save/Dependency.scala b/code/shared/src/main/scala/maf/save/save/Dependency.scala index cb9d3436b..49c47762f 100644 --- a/code/shared/src/main/scala/maf/save/save/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/save/Dependency.scala @@ -15,6 +15,7 @@ import EncapsulatedEncoder.* import maf.language.scheme.SchemeValue import maf.core.Identifier import maf.util.Writer.write +import maf.save.save.SaveExpressions /** * Trait to encode address dependencies. @@ -41,7 +42,7 @@ trait SaveAddrDep[Expr <: Expression] extends SaveDependency[Expr] with SavePosi * @tparam Expr * The type of expression used in the analysis */ -trait SaveDependency[Expr <: Expression] extends SaveMapToArray with SaveComponentID[Expr]: +trait SaveDependency[Expr <: Expression] extends SaveMapToArray with SaveComponents[Expr]: /** * Get the encoder that will be used to encode dependencies. * @@ -155,7 +156,7 @@ trait SaveAddr[Expr <: Expression] extends Save[Expr] with SavePosition[Expr]: * * This is an implementation of [[SaveAddr]]. */ -trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveComponentID[SchemeExp] with SaveContext[SchemeExp] with SaveStandardSchemeComponents: +trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveComponents[SchemeExp] with SaveContext[SchemeExp] with SaveExpressions[SchemeExp]: given EncapsulatedEncoder[VarAddr[EncodeContext]] with override val encoder = getAddressEncoder override def writeEncapsulated(writer: Writer, address: VarAddr[EncodeContext]): Writer = @@ -168,7 +169,7 @@ trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveComponentID[SchemeExp] override val encoder = getAddressEncoder override def writeEncapsulated(writer: Writer, address: ReturnAddr[Component]): Writer = writer.writeMember("identity", address.idn) - writer.writeMember("component", address.cmp)(using componentIDEncoder, encoder) + writer.writeMember("component", address.cmp) given EncapsulatedEncoder[PtrAddr[EncodeContext]] with override val encoder = getAddressEncoder diff --git a/code/shared/src/main/scala/maf/save/save/Store.scala b/code/shared/src/main/scala/maf/save/save/Store.scala index 55e8f2013..74ede252e 100644 --- a/code/shared/src/main/scala/maf/save/save/Store.scala +++ b/code/shared/src/main/scala/maf/save/save/Store.scala @@ -20,6 +20,7 @@ import EncapsulatedEncoder.* import maf.core.Environment import maf.lattice.{ConcreteLattice, ConstantPropagation} import maf.lattice.Concrete +import maf.save.save.SaveExpressions /** * Base trait for encoding [[AbstractDomain.Value values]]. @@ -111,7 +112,7 @@ trait SaveLattice[Expr <: Expression] extends Save[Expr]: trait SaveModularSchemeLattices extends SaveModularDomain with SaveAddr[SchemeExp] - with SaveStandardSchemeComponents + with SaveExpressions[SchemeExp] with SaveEnvironment[SchemeExp] with SaveLattice[SchemeExp]: /** Generic modular scheme lattice that is used for typechecking of nested class inside of this. */ From c356cb2f90888f2bcfeb79103e2d2809d7a512ca Mon Sep 17 00:00:00 2001 From: Merlijn Date: Tue, 12 Mar 2024 13:22:50 +0100 Subject: [PATCH 52/97] feat(save): Save worklist --- .../main/scala/maf/save/save/Analysis.scala | 1 + .../main/scala/maf/save/save/Component.scala | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/code/shared/src/main/scala/maf/save/save/Analysis.scala b/code/shared/src/main/scala/maf/save/save/Analysis.scala index cfd15a35b..93ce2b926 100644 --- a/code/shared/src/main/scala/maf/save/save/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/save/Analysis.scala @@ -89,6 +89,7 @@ trait SaveModF with SaveSchemeAddr with SaveGlobalStore[SchemeExp] with SaveModularSchemeLattices + with SaveSequentialWorklist[SchemeExp] with SaveNoContext[SchemeExp]: override def getEncoder: AbstractEncoder = new MapEncoder override def getKeyEncoder: AbstractEncoder = new MapEncoder diff --git a/code/shared/src/main/scala/maf/save/save/Component.scala b/code/shared/src/main/scala/maf/save/save/Component.scala index 579b8e741..d49c41050 100644 --- a/code/shared/src/main/scala/maf/save/save/Component.scala +++ b/code/shared/src/main/scala/maf/save/save/Component.scala @@ -41,6 +41,9 @@ import maf.language.scheme.SchemeSet import maf.language.scheme.SchemeBegin import maf.language.scheme.SchemeLetStar import maf.save.save.SaveExpressions +import maf.core.worklist.WorkList +import maf.modular.worklist.SequentialWorklistAlgorithm +import maf.modular.worklist.FIFOWorklistAlgorithm /** * Trait to encode positions. @@ -306,3 +309,27 @@ trait SaveStandardSchemeComponents writer.writeMember("lambda", lambda.asInstanceOf[SchemeExp]) writer.writeMember("environment", env) writer.writeMember("context", context.asInstanceOf[EncodeContext]) + +trait SaveWorklist[Expr <: Expression] extends Save[Expr]: + /** + * Get the encoder that will be used to encode the worklist. + * + * This will influence how the worklist will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] + * encoder. + */ + def getWorklistEncoder: AbstractEncoder = new ArrayEncoder + + type WorklistComponent + given worklistEncoder: Encoder[WorkList[WorklistComponent]] + def getWorklist: WorkList[WorklistComponent] + override def saveInfo: List[(String, Savable[_])] = super.saveInfo ++ List(("worklist", Savable(getWorklist))) + +trait SaveSequentialWorklist[Expr <: Expression] extends SaveWorklist[Expr] with SequentialWorklistAlgorithm[Expr] with SaveComponents[Expr]: + type WorklistComponent = Component + override def getWorklist: WorkList[Component] = workList + override given worklistEncoder: EncapsulatedEncoder[WorkList[Component]] with + override val encoder: AbstractEncoder = getWorklistEncoder + override protected def writeEncapsulated(writer: Writer, worklist: WorkList[Component]): Writer = + val worklistList = workList.toList + worklistList.foreach(writer.writeMember(_)) + writer From d911fba78a21f65b8c4326e592fbfca221c71950 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Tue, 12 Mar 2024 16:31:06 +0100 Subject: [PATCH 53/97] feat(load): Load worklist --- .../maf/cli/experiments/SchemeAnalyses.scala | 3 +- .../main/scala/maf/save/load/Component.scala | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/code/jvm/src/main/scala/maf/cli/experiments/SchemeAnalyses.scala b/code/jvm/src/main/scala/maf/cli/experiments/SchemeAnalyses.scala index e7549867a..43480ff5d 100644 --- a/code/jvm/src/main/scala/maf/cli/experiments/SchemeAnalyses.scala +++ b/code/jvm/src/main/scala/maf/cli/experiments/SchemeAnalyses.scala @@ -21,6 +21,7 @@ import maf.modular.scheme.modactor.mirrors.ModActorWithMirrors import maf.modular.scheme.modactor.mirrors.SimpleModActorWithMirrors import maf.language.racket.RacketLoaderSemantics import maf.language.racket.RacketLoader +import maf.save.LoadFIFOWorklist object SchemeAnalysesBoundedDomain: object NoSensitivity: @@ -55,7 +56,7 @@ object SchemeAnalyses: def contextInsensitiveAnalysis( prg: SchemeExp - ) = new SimpleSchemeModFAnalysis(prg) with SchemeModFNoSensitivity with SchemeConstantPropagationDomain with FIFOWorklistAlgorithm[SchemeExp] { + ) = new SimpleSchemeModFAnalysis(prg) with SchemeModFNoSensitivity with SchemeConstantPropagationDomain with FIFOWorklistAlgorithm[SchemeExp] with LoadFIFOWorklist[SchemeExp] { override def toString = "no-sensitivity" override val analysisName: String = "modf" } diff --git a/code/shared/src/main/scala/maf/save/load/Component.scala b/code/shared/src/main/scala/maf/save/load/Component.scala index 49649d726..3ad4a2036 100644 --- a/code/shared/src/main/scala/maf/save/load/Component.scala +++ b/code/shared/src/main/scala/maf/save/load/Component.scala @@ -39,6 +39,11 @@ import maf.language.scheme.SchemeAssert import maf.language.scheme.SchemeSet import maf.language.scheme.SchemeBegin import maf.language.scheme.SchemeLetStar +import maf.core.worklist.WorkList +import maf.modular.worklist.SequentialWorklistAlgorithm +import maf.modular.worklist.FIFOWorklistAlgorithm +import scala.collection.immutable.Queue +import maf.core.worklist.FIFOWorkList /** * The base trait for decoding components. @@ -349,3 +354,28 @@ trait LoadComponentIntID[Expr <: Expression] extends LoadComponentID[Expr]: LoadComponentIntID.this.components.addOne((key, component.value)) components + (component.value) ) + +trait LoadWorklist[Expr <: Expression] extends Load[Expr]: + /** + * Get the decoder that will be used to decode the worklist. + * + * This will influence how the worklist will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] + * decoder. + */ + def getWorklistDecoder: AbstractDecoder = new ArrayDecoder + type WorklistComponent + given worklistDecoder: Decoder[List[WorklistComponent]] + def setWorklist(worklist: List[WorklistComponent]): Unit + override def loadInfo: List[(String, Loadable[_])] = + super.loadInfo ++ List(("worklist" -> Loadable((worklist: List[WorklistComponent]) => setWorklist(worklist)))) + +trait LoadSequentialWorklist[Expr <: Expression] extends SequentialWorklistAlgorithm[Expr] with LoadWorklist[Expr] with LoadComponents[Expr]: + type WorklistComponent = Component + given worklistDecoder: EncapsulatedDecoder[List[WorklistComponent]] with + override def decoder: AbstractDecoder = getWorklistDecoder + override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): List[WorklistComponent] = + reader.readUntilBeforeBreak(List[Component](), (lst: List[Component]) => lst ++ List(reader.read[Component]())) + +trait LoadFIFOWorklist[Expr <: Expression] extends LoadSequentialWorklist[Expr] with FIFOWorklistAlgorithm[Expr]: + def setWorklist(worklist: List[WorklistComponent]): Unit = + workList = new FIFOWorkList[WorklistComponent](Queue(), Set()).addAll(worklist) From 6f1588798f2a5e80d21a37b3904846ef743c9bc5 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Tue, 12 Mar 2024 16:42:48 +0100 Subject: [PATCH 54/97] feat(save): Add ability to only save some elements --- .../main/scala/maf/modular/ModAnalysis.scala | 10 ++++++++++ .../src/main/scala/maf/save/save/Analysis.scala | 17 +++++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/code/shared/src/main/scala/maf/modular/ModAnalysis.scala b/code/shared/src/main/scala/maf/modular/ModAnalysis.scala index ad477359e..b4ccb9e94 100644 --- a/code/shared/src/main/scala/maf/modular/ModAnalysis.scala +++ b/code/shared/src/main/scala/maf/modular/ModAnalysis.scala @@ -51,6 +51,16 @@ trait AnalysisEntry[Exp <: Expression]: */ def save(filename: String): Unit = System.err.nn.println("Save functionality is not implemented for this analysis") + /** + * This saves the current analysis to a file, but only the elements that are in save + * + * @param filename + * The file to save to + * @param save + * The elements to save + */ + def save(filename: String, save: Set[String]): Unit = System.err.nn.println("Save functionality is not implemented for this analysis") + /** * Load an analysis from a given file * diff --git a/code/shared/src/main/scala/maf/save/save/Analysis.scala b/code/shared/src/main/scala/maf/save/save/Analysis.scala index 93ce2b926..767aaad66 100644 --- a/code/shared/src/main/scala/maf/save/save/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/save/Analysis.scala @@ -54,14 +54,27 @@ trait Save[Expr <: Expression] extends ModAnalysis[Expr]: def getKeyEncoder: AbstractEncoder /** Encode an analysis. */ - given EncapsulatedEncoder[Save[Expr]] with + given analysisEncoder: EncapsulatedEncoder[Save[Expr]] with override val encoder = Save.this.getEncoder override def writeEncapsulated(writer: Writer, value: Save[Expr]): Writer = for (key, value) <- saveInfo do writer.writeMember(key, value.value)(using value.encoder, encoder) writer + private var save = Set[String]() + private given excludedAnalysisEncoder: EncapsulatedEncoder[Save[Expr]] with + override val encoder = Save.this.getEncoder + override def writeEncapsulated(writer: Writer, value: Save[Expr]): Writer = + for (key, value) <- saveInfo do if save.contains(key) then writer.writeMember(key, value.value)(using value.encoder, encoder) + writer + override def save(filename: String): Unit = - val res = Json.encode(this).toByteArray + val res = Json.encode(this)(using analysisEncoder).toByteArray + Files.write(Paths.get(filename), res) + + override def save(filename: String, save: Set[String]): Unit = + this.save = save + val res = Json.encode(this)(using excludedAnalysisEncoder).toByteArray + this.save = Set[String]() Files.write(Paths.get(filename), res) /** From 4e4a697d3d63bc4fbb4cdfc51cc2bf8993d52858 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Tue, 12 Mar 2024 17:14:22 +0100 Subject: [PATCH 55/97] feat(load): Add ability to only load some elements --- .../main/scala/maf/modular/ModAnalysis.scala | 10 +++++++ .../main/scala/maf/save/load/Analysis.scala | 27 ++++++++++++++++--- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/code/shared/src/main/scala/maf/modular/ModAnalysis.scala b/code/shared/src/main/scala/maf/modular/ModAnalysis.scala index b4ccb9e94..765956828 100644 --- a/code/shared/src/main/scala/maf/modular/ModAnalysis.scala +++ b/code/shared/src/main/scala/maf/modular/ModAnalysis.scala @@ -69,6 +69,16 @@ trait AnalysisEntry[Exp <: Expression]: */ def load(filename: String): Unit = System.err.nn.println("Load functionality is not implemented for this analysis") + /** + * Load the given elements of an analysis from a given file + * + * @param filename + * The file to load the analysis from + * @param load + * The elements to load + */ + def load(filename: String, load: Set[String]): Unit = System.err.nn.println("Load functionality is not implemented for this analysis") + /** * Method that renders a Dot graph of the components and the dependencies between them and writes it to a file * diff --git a/code/shared/src/main/scala/maf/save/load/Analysis.scala b/code/shared/src/main/scala/maf/save/load/Analysis.scala index dc6a436f8..5ba0b2ba1 100644 --- a/code/shared/src/main/scala/maf/save/load/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/load/Analysis.scala @@ -9,6 +9,7 @@ import java.nio.file.Files import maf.language.scheme.SchemeExp import io.bullet.borer.Reader import maf.save.EncapsulatedDecoder.* +import scala.collection.mutable.HashMap /** * Contains info about the top-level objects that need to be loaded. @@ -52,15 +53,35 @@ trait Load[Expr <: Expression] extends ModAnalysis[Expr]: def getKeyDecoder: AbstractDecoder /** Decode an analysis. */ - given EncapsulatedDecoder[Load[Expr]] with + given analysisDecoder: Decoder[Load[Expr]] with + override def read(reader: Reader): Load[Expr] = + val loadInfo = Load.this.loadInfo + load = loadInfo.map(_._1).toSet + reader.read[Load[Expr]]() + load = Set[String]() + return Load.this + + private var load = Set[String]() + private given excludedAnalysisDecoder: EncapsulatedDecoder[Load[Expr]] with override val decoder: AbstractDecoder = Load.this.getDecoder override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Load[Expr] = - for (key, value) <- loadInfo do value.load(reader.readMember(key)(using value.decoder, decoder).value) + val loaded = HashMap[String, Boolean]() + for (key, value) <- loadInfo do + if load.contains(key) then + val result = reader.readMember(key)(using value.decoder, decoder) + if result.hasValue then + value.load(result.value) + loaded.addOne((key, true)) + for (key, value) <- loadInfo do if load.contains(key) && !loaded.contains(key) then value.load(reader.getMember(key)) return Load.this override def load(filename: String): Unit = val bytes = Files.readAllBytes(Paths.get(filename)) - if bytes != null then Json.decode(bytes).to[Load[Expr]].value + if bytes != null then Json.decode(bytes).to[Load[Expr]](using analysisDecoder).value + + override def load(filename: String, load: Set[String]): Unit = + val bytes = Files.readAllBytes(Paths.get(filename)) + if bytes != null then Json.decode(bytes).to[Load[Expr]](using excludedAnalysisDecoder).value /** * Returns a map strings and [[Loadable]] s. From b22bb1f2ef5a1b03f3f8a2cfeaf7524b326aa1b1 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Wed, 13 Mar 2024 06:51:53 +0100 Subject: [PATCH 56/97] refactor(load, save): Use 'AnalysisEntry' as a basis instead of 'ModAnalysis' --- code/shared/src/main/scala/maf/save/load/Analysis.scala | 4 ++-- code/shared/src/main/scala/maf/save/load/Component.scala | 3 ++- code/shared/src/main/scala/maf/save/save/Analysis.scala | 4 ++-- code/shared/src/main/scala/maf/save/save/Component.scala | 3 ++- code/shared/src/main/scala/maf/save/save/Expression.scala | 3 ++- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/load/Analysis.scala b/code/shared/src/main/scala/maf/save/load/Analysis.scala index 5ba0b2ba1..0911195d4 100644 --- a/code/shared/src/main/scala/maf/save/load/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/load/Analysis.scala @@ -3,13 +3,13 @@ package maf.save import io.bullet.borer.Decoder import io.bullet.borer.Json import maf.core.Expression -import maf.modular.ModAnalysis import java.nio.file.Paths import java.nio.file.Files import maf.language.scheme.SchemeExp import io.bullet.borer.Reader import maf.save.EncapsulatedDecoder.* import scala.collection.mutable.HashMap +import maf.modular.AnalysisEntry /** * Contains info about the top-level objects that need to be loaded. @@ -32,7 +32,7 @@ case class Loadable[T](val load: (T) => Unit)(using val decoder: Decoder[T]) * @tparam Expr * The type of expression used in the analysis */ -trait Load[Expr <: Expression] extends ModAnalysis[Expr]: +trait Load[Expr <: Expression] extends AnalysisEntry[Expr]: /** * Get the decoder that will be used to decode your analysis. * diff --git a/code/shared/src/main/scala/maf/save/load/Component.scala b/code/shared/src/main/scala/maf/save/load/Component.scala index 3ad4a2036..a7c46eba2 100644 --- a/code/shared/src/main/scala/maf/save/load/Component.scala +++ b/code/shared/src/main/scala/maf/save/load/Component.scala @@ -44,6 +44,7 @@ import maf.modular.worklist.SequentialWorklistAlgorithm import maf.modular.worklist.FIFOWorklistAlgorithm import scala.collection.immutable.Queue import maf.core.worklist.FIFOWorkList +import maf.modular.ModAnalysis /** * The base trait for decoding components. @@ -56,7 +57,7 @@ import maf.core.worklist.FIFOWorkList * @tparam Expr * The type of expression used in the analysis */ -trait LoadComponents[Expr <: Expression] extends Load[Expr]: +trait LoadComponents[Expr <: Expression] extends ModAnalysis[Expr] with Load[Expr]: /** * Get the decoder that will be used to decode components. * diff --git a/code/shared/src/main/scala/maf/save/save/Analysis.scala b/code/shared/src/main/scala/maf/save/save/Analysis.scala index 767aaad66..d212c5496 100644 --- a/code/shared/src/main/scala/maf/save/save/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/save/Analysis.scala @@ -4,13 +4,13 @@ import io.bullet.borer.{Encoder, Writer} import io.bullet.borer.Json import maf.util.Writer.write import maf.core.Expression -import maf.modular.ModAnalysis import java.nio.file.Paths import java.nio.file.Files import maf.language.scheme.SchemeExp import EncapsulatedEncoder.* import maf.save.save.SaveSchemeExpressions import maf.save.save.SaveRecursiveSchemeExpressionsIntID +import maf.modular.AnalysisEntry /** * Contains info about the top-level objects that need to be saved. @@ -33,7 +33,7 @@ case class Savable[T](val value: T)(using val encoder: Encoder[T]) * @tparam Expr * The type of expression used in the analysis */ -trait Save[Expr <: Expression] extends ModAnalysis[Expr]: +trait Save[Expr <: Expression] extends AnalysisEntry[Expr]: /** * Get the encoder that will be used to encode your analysis. * diff --git a/code/shared/src/main/scala/maf/save/save/Component.scala b/code/shared/src/main/scala/maf/save/save/Component.scala index d49c41050..d66954220 100644 --- a/code/shared/src/main/scala/maf/save/save/Component.scala +++ b/code/shared/src/main/scala/maf/save/save/Component.scala @@ -44,6 +44,7 @@ import maf.save.save.SaveExpressions import maf.core.worklist.WorkList import maf.modular.worklist.SequentialWorklistAlgorithm import maf.modular.worklist.FIFOWorklistAlgorithm +import maf.modular.ModAnalysis /** * Trait to encode positions. @@ -81,7 +82,7 @@ trait SavePosition[Expr <: Expression] extends Save[Expr]: * @tparam Expr * The type of expression used in the analysis */ -trait SaveComponents[Expr <: Expression] extends Save[Expr]: +trait SaveComponents[Expr <: Expression] extends ModAnalysis[Expr] with Save[Expr]: /** * Get the encoder that will be used to encode components. * diff --git a/code/shared/src/main/scala/maf/save/save/Expression.scala b/code/shared/src/main/scala/maf/save/save/Expression.scala index aa8d9804f..1d75015b7 100644 --- a/code/shared/src/main/scala/maf/save/save/Expression.scala +++ b/code/shared/src/main/scala/maf/save/save/Expression.scala @@ -25,6 +25,7 @@ import maf.save.ArrayEncoder import maf.language.scheme.SchemeVar import maf.language.scheme.SchemeValue import maf.language.scheme.SchemeLambdaExp +import maf.modular.ModAnalysis /** * The base trait for encoding expressions. @@ -95,7 +96,7 @@ trait SaveActualExprs[Expr <: Expression] extends SaveExpressions[Expr]: * @tparam Expr * The type of expression used in the analysis */ -trait SaveExpressionID[Expr <: Expression] extends Save[Expr] with SaveActualExprs[Expr]: +trait SaveExpressionID[Expr <: Expression] extends ModAnalysis[Expr] with Save[Expr] with SaveActualExprs[Expr]: override given expressionEncoder: Encoder[Expr] = expressionIDEncoder override def saveInfo: List[(String, Savable[_])] = super.saveInfo ++ List(("expressions" -> Savable(visited.map(expr(_))))) From e1d90f340b68ad8c60fbdb721eb7bdb27a4e9b54 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Wed, 13 Mar 2024 10:38:38 +0100 Subject: [PATCH 57/97] feat(load): Allow for extra map elements When decoding, any map elements with an unknown key will be discarded. --- .../scala/maf/save/load/Encapsulated.scala | 122 ++++++++++++++++-- 1 file changed, 109 insertions(+), 13 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/load/Encapsulated.scala b/code/shared/src/main/scala/maf/save/load/Encapsulated.scala index e4a3c95ea..0ff11173e 100644 --- a/code/shared/src/main/scala/maf/save/load/Encapsulated.scala +++ b/code/shared/src/main/scala/maf/save/load/Encapsulated.scala @@ -27,10 +27,11 @@ import scala.reflect.ClassTag * @tparam V * The type of the value */ -class ReadValue[K, V](): +class ReadValue[K, V](private val forceRead: (key: Option[K]) => Unit): protected var _key: Option[K] = None protected var _value: Option[V] = None protected var _updateValue: Option[ReadValue[K, V]] = None + protected var _forceRead: Option[(key: Option[K]) => Unit] = Some(forceRead) /** * Create a new key-value pair with a key and a value. @@ -41,7 +42,7 @@ class ReadValue[K, V](): * The value of the key-value pair */ def this(_key: K, _value: V) = - this() + this((key: Option[K]) => ()) this._key = Some(_key) this._value = Some(_value) @@ -51,20 +52,27 @@ class ReadValue[K, V](): * @param _key * The key of the key-value pair */ - def this(_key: K) = - this() + def this(_key: K, forceRead: (key: Option[K]) => Unit) = + this(forceRead) this._key = Some(_key) /** Check if this has a value. */ def hasValue: Boolean = _value.isDefined /** - * Returns the value. + * Returns the value, or tries to find if if there is none yet. + * + * @note + * If there is no value yet, it will first try to find it anyway, discarding any other values that haven't been read yet. If a value can still + * not be found, an error will be thrown. * * @throws NoSuchElementException * If there is no value */ def value: V = + if _value.isEmpty && _forceRead.isDefined then + if _key.isDefined then _forceRead.get(Some(_key.get)) + else _forceRead.get(None) if _value.isEmpty then if _key.isDefined then throw new NoSuchElementException(s"The key '${key}' does not have a value.") else throw new NoSuchElementException("This element does not have a value.") @@ -79,12 +87,17 @@ class ReadValue[K, V](): def hasKey: Boolean = _key.isDefined /** - * Returns the key. + * Returns the key, or tries to find it if there is no key yet. + * + * @note + * If there is no key yet, it will first try to find it anyway, discarding any other key-value pairs that haven't been read yet. If a key can + * still not be found, an error will be thrown. * * @throws NoSuchElementException * If there is no key */ def key: K = + if _key.isEmpty && _forceRead.isDefined then _forceRead.get(None) if _key.isEmpty then throw new NoSuchElementException("This element does not have a key.") _key.get @@ -197,6 +210,9 @@ trait AbstractDecoder: /** * Returns an already read value using a given key. * + * @param key + * The key for which to get the value + * * @throws NoSuchElementException * If this key hasn't been read yet, and therefore doesn't have a value. */ @@ -204,6 +220,14 @@ trait AbstractDecoder: if !values.contains(key) then throw new NoSuchElementException(s"The key '${key}' does not have a value.") values.get(key).get.asInstanceOf[T] + /** + * Returns whether or not a given key has a value yet. + * + * @param key + * The key to check if it has a value + */ + def hasValue(key: String): Boolean = values.contains(key) + /** * Opens either a new map or a new array, based on the decoder that is used. * @@ -246,6 +270,26 @@ trait AbstractDecoder: */ def closeEncapsulation[T](reader: Reader, unbounded: Boolean, res: T): Unit + /** + * Read a key-value pair. + * + * This will read until it finds the specified key, discarding any other elements that it cannot decode. + * + * @param reader + * The reader used read + * @param key + * The key to find + */ + def forceReadValue(reader: Reader, key: String): Unit + + /** + * Read the first element that you can, discarding any other elements. + * + * @param reader + * The reader used read + */ + def forceReadValue(reader: Reader): Unit + object AbstractDecoder: /** * Automatically derive an decoder for type T. @@ -280,6 +324,9 @@ object AbstractDecoder: */ inline def deriveAllDecoders[T](decoder: AbstractDecoder): Decoder[T] = if decoder.mapBasedDecoder then CompactMapBasedCodecs.deriveAllDecoders[T] else ArrayBasedCodecs.deriveAllDecoders[T] + def forceRead(reader: Reader, decoder: AbstractDecoder, key: Option[String]) = + if key.isDefined then decoder.forceReadValue(reader, key.get) + else decoder.forceReadValue(reader) /** * Trait used to decode an instance of type T. @@ -324,6 +371,12 @@ trait EncapsulatedDecoder[T] extends Decoder[T]: override def read(reader: Reader): T = decoder.openEncapsulation(reader) val res = readEncapsulated(reader)(using decoder) + // Read all remaining elements, since these where not read they will be ignored + EncapsulatedDecoder.readUntilBeforeBreak(reader)(None, + (none: None.type) => + reader.skipElement() + None + ) decoder.closeEncapsulation(reader, true, res) res @@ -446,7 +499,7 @@ object EncapsulatedDecoder: * empty and will be filled in later. */ def readMembers[T](keys: Array[(String, Decoder[_ <: T])])(using decoder: AbstractDecoder): ReadValue[String, T] = - val res = new ReadValue[String, T]() + val res = new ReadValue[String, T](AbstractDecoder.forceRead(reader, decoder, _)) for (key, valueDecoder) <- keys do decoder.readKey(reader, key) val value = decoder.readValue[T](reader)(using valueDecoder.asInstanceOf[Decoder[T]]) @@ -481,7 +534,9 @@ object EncapsulatedDecoder: * @throws NoSuchElementException * If this key hasn't been read yet, and therefore doesn't have a value. */ - def getMember[T](key: String)(using decoder: AbstractDecoder): T = decoder.getValue[T](key) + def getMember[T](key: String)(using decoder: AbstractDecoder): T = + if !decoder.hasValue(key) then decoder.forceReadValue(reader, key) + decoder.getValue[T](key) /** * Decoder that uses maps to decode values. @@ -532,19 +587,36 @@ class MapDecoder extends AbstractDecoder: if reader.hasString then decodeKey = Some(reader.readString()) if decodeKey.isDefined && keys.contains(decodeKey.get) then currentKey = decodeKey - val valueDecoder = keys.remove(decodeKey.get).get - val res = readValue(reader)(using valueDecoder.decoder) - valueDecoder.value.value = res.value + val valueDecoder = keys.get(currentKey.get).get + readValue(reader)(using valueDecoder.decoder) currentKey = None - return new ReadValue[String, T](key, res) + if keys.contains(key) then + val valueDecoder = keys.remove(key).get.asInstanceOf[ValueDecoder[T]] + valueDecoder.value.value = res + return valueDecoder.value + else return new ReadValue[String, T](key, res) else - val readValue = new ReadValue[String, T](currentKey.get) + val readValue = new ReadValue[String, T](currentKey.get, AbstractDecoder.forceRead(reader, this, _)) keys.addOne((currentKey.get, ValueDecoder(readValue, summon[Decoder[T]]))) currentKey = None return readValue override def openEncapsulation(reader: Reader): Unit = reader.readMapStart() override def openEncapsulation(reader: Reader, amount: Int): Unit = reader.readMapOpen(amount) override def closeEncapsulation[T](reader: Reader, unbounded: Boolean, res: T): Unit = reader.readMapClose(unbounded, res) + override def forceReadValue(reader: Reader, key: String): Unit = + while decodeKey.isDefined && !values.contains(key.asInstanceOf[String]) do forceReadValue(reader) + override def forceReadValue(reader: Reader): Unit = + val tmpCurrentKey = currentKey + readDecodeKey(reader) + while decodeKey.isDefined && !keys.contains(decodeKey.get) do + reader.skipElement() + decodeKey = None + readDecodeKey(reader) + if !decodeKey.isDefined then return + val valueDecoder = keys.get(decodeKey.get).get + currentKey = decodeKey + readValue(reader)(using valueDecoder.decoder) + currentKey = tmpCurrentKey /** * Decoder that uses arrays to decode values. @@ -572,6 +644,8 @@ class ArrayDecoder extends AbstractDecoder: override def openEncapsulation(reader: Reader): Unit = reader.readArrayStart() override def openEncapsulation(reader: Reader, amount: Int): Unit = reader.readArrayOpen(amount) override def closeEncapsulation[T](reader: Reader, unbounded: Boolean, res: T): Unit = reader.readMapClose(unbounded, res) + override def forceReadValue(reader: Reader, key: String): Unit = return + override def forceReadValue(reader: Reader): Unit = reader.skipElement() /** * Decoder that uses arrays to decode values, but preserves keys. @@ -596,6 +670,7 @@ class ArrayKeyDecoder extends MapDecoder: /** The key that was read, and which value should now be read. */ protected var key: Option[Any] = None + protected val keyValues = new HashMap[Any, Any]() /** * Read a key from the given reader. @@ -640,11 +715,32 @@ class ArrayKeyDecoder extends MapDecoder: val res = reader.read[T]() val tmpKey = key key = None + keyValues.addOne((tmpKey.get, res)) return ReadValue(tmpKey.get.asInstanceOf[V], res) override def openEncapsulation(reader: Reader): Unit = reader.readArrayStart() override def openEncapsulation(reader: Reader, amount: Int): Unit = reader.readArrayOpen(amount) override def closeEncapsulation[T](reader: Reader, unbounded: Boolean, res: T): Unit = reader.readMapClose(unbounded, res) + /** + * Returns an already read value using a given key. + * + * @throws NoSuchElementException + * If this key hasn't been read yet, and therefore doesn't have a value. + */ + def getValue[T](key: Any): T = + if !keyValues.contains(key) then + if key.isInstanceOf[String] then return super.getValue(key.asInstanceOf[String]) + else throw new NoSuchElementException(s"The key '${key}' does not have a value.") + return keyValues.get(key).get.asInstanceOf[T] + + /** + * Returns whether or not a given key has a value yet. + * + * @param key + * The key to check if it has a value + */ + def hasValue(key: Any): Boolean = keyValues.contains(key) || (key.isInstanceOf[String] && super.hasValue(key.asInstanceOf[String])) + /** * Object with an extension method for [[borer.Reader]]. * From 275b4400f39f762d4aa44b79658297a3bae76345 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Wed, 13 Mar 2024 15:37:01 +0100 Subject: [PATCH 58/97] feat(save): Save expressions normally for certain AST heights In `SaveRecursiveSchemeExpressionsIntID` when saving expressions using their ID, save it normally when the AST depth becomes less than `maxASTHeight`. --- .../main/scala/maf/save/save/Analysis.scala | 1 + .../main/scala/maf/save/save/Expression.scala | 55 ++++++++++--------- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/save/Analysis.scala b/code/shared/src/main/scala/maf/save/save/Analysis.scala index d212c5496..238f2cb9b 100644 --- a/code/shared/src/main/scala/maf/save/save/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/save/Analysis.scala @@ -106,3 +106,4 @@ trait SaveModF with SaveNoContext[SchemeExp]: override def getEncoder: AbstractEncoder = new MapEncoder override def getKeyEncoder: AbstractEncoder = new MapEncoder + override val maxASTHeight = 3 diff --git a/code/shared/src/main/scala/maf/save/save/Expression.scala b/code/shared/src/main/scala/maf/save/save/Expression.scala index 1d75015b7..e0e666b39 100644 --- a/code/shared/src/main/scala/maf/save/save/Expression.scala +++ b/code/shared/src/main/scala/maf/save/save/Expression.scala @@ -122,7 +122,6 @@ trait SaveExpressionIntID[Expr <: Expression] extends SaveExpressionID[Expr] wit override val encoder = getExpressionKeyEncoder def writeEncapsulated(writer: Writer, exprs: Set[Expr]): Writer = for (expr <- exprs) do - println(id) writer.writeMember(id.toString, expr)(using actualExpressionEncoder, encoder) expressions.addOne((expr, id)) id += 1 @@ -145,6 +144,9 @@ trait SaveRecursiveSchemeExpressionsIntID extends SaveExpressionID[SchemeExp] wi private val expressions = HashMap[SchemeExp, Int]() private var id = 0 + /** The max height of the AST before you encode it normally. */ + val maxASTHeight: Int + override protected given expressionSetEncoder: EncapsulatedEncoder[Set[SchemeExp]] with override val encoder = getExpressionKeyEncoder @@ -156,31 +158,32 @@ trait SaveRecursiveSchemeExpressionsIntID extends SaveExpressionID[SchemeExp] wi given recursiveExpressionEncoder: Encoder[SchemeExp] with override def write(writer: Writer, expr: SchemeExp): Writer = if expressions.contains(expr) then return writer - expr match - case funcall: SchemeFuncall => - writer.write(funcall.args) - writer.write(funcall.f)(using recursiveExpressionEncoder) - case lambda: SchemeLambda => writer.write(lambda.body) - case argLambda: SchemeVarArgLambda => writer.write(argLambda.body) - case letrec: SchemeLetrec => - for (binding <- letrec.bindings) writer.write(binding._2) - writer.write(letrec.body) - case assert: SchemeAssert => writer.write(assert.exp) - case let: SchemeLet => - for (binding <- let.bindings) writer.write(binding._2) - writer.write(let.body) - case schemeIf: SchemeIf => - writer.write(schemeIf.cond) - writer.write(schemeIf.cons) - writer.write(schemeIf.alt) - case set: SchemeSet => - writer.write(set.value) - case begin: SchemeBegin => - writer.write(begin.exps) - case letStar: SchemeLetStar => - for (binding <- letStar.bindings) writer.write(binding._2) - writer.write(letStar.body) - case _ => writer + if expr.height > maxASTHeight then + expr match + case funcall: SchemeFuncall => + writer.write(funcall.args) + writer.write(funcall.f)(using recursiveExpressionEncoder) + case lambda: SchemeLambda => writer.write(lambda.body) + case argLambda: SchemeVarArgLambda => writer.write(argLambda.body) + case letrec: SchemeLetrec => + for (binding <- letrec.bindings) writer.write(binding._2) + writer.write(letrec.body) + case assert: SchemeAssert => writer.write(assert.exp) + case let: SchemeLet => + for (binding <- let.bindings) writer.write(binding._2) + writer.write(let.body) + case schemeIf: SchemeIf => + writer.write(schemeIf.cond) + writer.write(schemeIf.cons) + writer.write(schemeIf.alt) + case set: SchemeSet => + writer.write(set.value) + case begin: SchemeBegin => + writer.write(begin.exps) + case letStar: SchemeLetStar => + for (binding <- letStar.bindings) writer.write(binding._2) + writer.write(letStar.body) + case _ => () writer.writeMember(id.toString(), expr)(using actualExpressionEncoder, encoder) expressions.addOne(expr, id) From 3f25287ac002b19e288157ca3270601515e6d777 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Wed, 13 Mar 2024 15:49:23 +0100 Subject: [PATCH 59/97] feat(save): Save initialized state Save whether or not the ModAnalysis is initialized yet --- code/shared/src/main/scala/maf/modular/ModAnalysis.scala | 2 ++ code/shared/src/main/scala/maf/save/save/Analysis.scala | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/code/shared/src/main/scala/maf/modular/ModAnalysis.scala b/code/shared/src/main/scala/maf/modular/ModAnalysis.scala index 765956828..4785ee2b4 100644 --- a/code/shared/src/main/scala/maf/modular/ModAnalysis.scala +++ b/code/shared/src/main/scala/maf/modular/ModAnalysis.scala @@ -173,6 +173,8 @@ abstract class ModAnalysis[Expr <: Expression](val program: Expr) extends Clonea // flag to indicate if the analysis has already been initialized (see method `init`) private var initialized: Boolean = false + def analysisInitialized = initialized + def analysisInitialized_=(init: Boolean) = initialized = init /* Runs the analysis with a timeout. Implementation should be provided by the worklist algorithm. */ protected def run(timeout: Timeout.T): Unit diff --git a/code/shared/src/main/scala/maf/save/save/Analysis.scala b/code/shared/src/main/scala/maf/save/save/Analysis.scala index 238f2cb9b..2b6d5da02 100644 --- a/code/shared/src/main/scala/maf/save/save/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/save/Analysis.scala @@ -11,6 +11,7 @@ import EncapsulatedEncoder.* import maf.save.save.SaveSchemeExpressions import maf.save.save.SaveRecursiveSchemeExpressionsIntID import maf.modular.AnalysisEntry +import maf.modular.ModAnalysis /** * Contains info about the top-level objects that need to be saved. @@ -90,9 +91,13 @@ trait Save[Expr <: Expression] extends AnalysisEntry[Expr]: */ def saveInfo: List[(String, Savable[_])] = List(("name", Savable(analysisName))) +trait SaveInitialized[Expr <: Expression] extends ModAnalysis[Expr] with Save[Expr]: + override def saveInfo: List[(String, Savable[_])] = super.saveInfo ++ List(("initialized", Savable(analysisInitialized))) + /** The trait used to save the modF analysis. */ trait SaveModF extends Save[SchemeExp] + with SaveInitialized[SchemeExp] with SaveSchemeExpressions with SaveRecursiveSchemeExpressionsIntID with SaveComponentIntID[SchemeExp] From 8f49c32684a7ccdfa17b6ceb59185c3210e34e27 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Wed, 13 Mar 2024 15:58:58 +0100 Subject: [PATCH 60/97] feat(load): Load initialized state Load whether or not the ModAnalysis was initialized when it was saved, this ensures that the analysis is not ran again. --- code/shared/src/main/scala/maf/save/load/Analysis.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/code/shared/src/main/scala/maf/save/load/Analysis.scala b/code/shared/src/main/scala/maf/save/load/Analysis.scala index 0911195d4..7be575a67 100644 --- a/code/shared/src/main/scala/maf/save/load/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/load/Analysis.scala @@ -10,6 +10,7 @@ import io.bullet.borer.Reader import maf.save.EncapsulatedDecoder.* import scala.collection.mutable.HashMap import maf.modular.AnalysisEntry +import maf.modular.ModAnalysis /** * Contains info about the top-level objects that need to be loaded. @@ -96,9 +97,14 @@ trait Load[Expr <: Expression] extends AnalysisEntry[Expr]: */ def loadInfo: List[(String, Loadable[_])] = List(("name", Loadable((name: String) => ()))) +trait LoadInitialized[Expr <: Expression] extends ModAnalysis[Expr] with Load[Expr]: + override def loadInfo: List[(String, Loadable[?])] = + super.loadInfo ++ List(("initialized", Loadable((initialized: Boolean) => analysisInitialized = initialized))) + /** The trait used to load the modF analysis. */ trait LoadModF extends Load[SchemeExp] + with LoadInitialized[SchemeExp] with LoadExpressionIntID[SchemeExp] with LoadSchemeExpressions with LoadComponents[SchemeExp] From 487811b2e6fdfdefc966e51863f7901e45068846 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Wed, 27 Mar 2024 12:05:37 +0100 Subject: [PATCH 61/97] feat(save): Encode vector lattices --- code/shared/src/main/scala/maf/save/save/Store.scala | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/code/shared/src/main/scala/maf/save/save/Store.scala b/code/shared/src/main/scala/maf/save/save/Store.scala index 74ede252e..32fabd8f4 100644 --- a/code/shared/src/main/scala/maf/save/save/Store.scala +++ b/code/shared/src/main/scala/maf/save/save/Store.scala @@ -141,6 +141,12 @@ trait SaveModularSchemeLattices writer.writeMember("car", cons.car) writer.writeMember("cdr", cons.cdr) + given EncapsulatedEncoder[SchemeLattice#Vec]() with SaveMapToArray with + override val encoder = getValueEncoder + override protected def writeEncapsulated(writer: Writer, vec: SchemeLattice#Vec): Writer = + writer.writeMember("size", vec.size.asInstanceOf[Lattice[BigInt]]) + writer.writeMember("elements", vec.elements.asInstanceOf[Map[Lattice[BigInt], SchemeLattice#L]]) + given EncapsulatedEncoder[(HMapKey, SchemeLattice#Value)] with override val encoder = getValueKeyEncoder override protected def writeEncapsulated(writer: Writer, hMapPair: (HMapKey, SchemeLattice#Value)): Writer = @@ -155,6 +161,7 @@ trait SaveModularSchemeLattices case clo: SchemeLattice#Clo => writer.writeMember("closure", clo) case pointer: SchemeLattice#Pointer => writer.writeMember("pointer", pointer) case cons: SchemeLattice#Cons => writer.writeMember("cons", cons) + case vec: SchemeLattice#Vec => writer.writeMember("vector", vec) case modularLattice.Nil => writer.writeMember("nil", "") case modularLattice.Void => writer.writeMember("void", "") case _ => From 315debb4827ca3dda76c51c9cba9df61c8e317c3 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Wed, 27 Mar 2024 18:32:18 +0100 Subject: [PATCH 62/97] feat(load): Decode vector lattice --- code/shared/src/main/scala/maf/save/load/Store.scala | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/code/shared/src/main/scala/maf/save/load/Store.scala b/code/shared/src/main/scala/maf/save/load/Store.scala index 35830b861..4d64ab1e3 100644 --- a/code/shared/src/main/scala/maf/save/load/Store.scala +++ b/code/shared/src/main/scala/maf/save/load/Store.scala @@ -115,6 +115,7 @@ trait LoadModularSchemeLattices ("pointer", summon[Decoder[(HMapKey, SchemeLattice#Pointer)]]), ("symbol", summon[Decoder[(HMapKey, SchemeLattice#Symbol)]]), ("cons", summon[Decoder[(HMapKey, SchemeLattice#Cons)]]), + ("vector", summon[Decoder[(HMapKey, SchemeLattice#Vec)]]), ("nil", nilLatticeDecoder), ("void", summon[Decoder[(HMapKey, modularLattice.Void.type)]]), ("ERROR", errorDecoder) @@ -182,6 +183,17 @@ trait LoadModularSchemeLattices val cdr = reader.readMember[SchemeLattice#L]("cdr") return (modularLattice.ConsT, new modularLattice.Cons(car.value, cdr.value)) + given EncapsulatedDecoder[(HMapKey, SchemeLattice#Vec)] with LoadMapToArray with + override def decoder: AbstractDecoder = getValueDecoder + override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): (HMapKey, SchemeLattice#Vec) = + val size = reader.readMember[Lattice[BigInt]]("size").value.asInstanceOf[LoadModularSchemeLattices.this.modularLatticeWrapper.I] + val elements = + reader + .readMember[Map[Lattice[BigInt], SchemeLattice#L]]("elements") + .value + .asInstanceOf[Map[LoadModularSchemeLattices.this.modularLatticeWrapper.I, SchemeLattice#L]] + return (modularLattice.VecT, new modularLattice.Vec(size, elements)) + given nilLatticeDecoder: Decoder[(HMapKey, modularLattice.Nil.type)] with override def read(reader: Reader): (HMapKey, modularLattice.Nil.type) = return (modularLattice.NilT, modularLattice.Nil) From 37c56896e3adcd34d715025e50ebf513fe86e32d Mon Sep 17 00:00:00 2001 From: Merlijn Date: Fri, 29 Mar 2024 21:43:36 +0100 Subject: [PATCH 63/97] fix(load): `nilLattice` and `voidLattice` aren't decoded correctly --- code/shared/src/main/scala/maf/save/load/Store.scala | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/code/shared/src/main/scala/maf/save/load/Store.scala b/code/shared/src/main/scala/maf/save/load/Store.scala index 4d64ab1e3..dbf168b03 100644 --- a/code/shared/src/main/scala/maf/save/load/Store.scala +++ b/code/shared/src/main/scala/maf/save/load/Store.scala @@ -123,7 +123,7 @@ trait LoadModularSchemeLattices private given errorDecoder: Decoder[(HMapKey, modularLattice.Nil.type)] with override def read(reader: Reader): (HMapKey, modularLattice.Nil.type) = - val error = reader.read[String]() + val error = reader.readString() System.err.nn.println("The lattice was not correctly encoded and had error: `" + error + "`, using `nil` instead.") return (modularLattice.NilT, modularLattice.Nil) @@ -195,10 +195,14 @@ trait LoadModularSchemeLattices return (modularLattice.VecT, new modularLattice.Vec(size, elements)) given nilLatticeDecoder: Decoder[(HMapKey, modularLattice.Nil.type)] with - override def read(reader: Reader): (HMapKey, modularLattice.Nil.type) = return (modularLattice.NilT, modularLattice.Nil) + override def read(reader: Reader): (HMapKey, modularLattice.Nil.type) = + reader.readString() + return (modularLattice.NilT, modularLattice.Nil) given Decoder[(HMapKey, modularLattice.Void.type)] with - override def read(reader: Reader): (HMapKey, modularLattice.Void.type) = return (modularLattice.VoidT, modularLattice.Void) + override def read(reader: Reader): (HMapKey, modularLattice.Void.type) = + reader.readString() + return (modularLattice.VoidT, modularLattice.Void) /** * Base trait for decoding values as [[ModularSchemeLattice modular scheme lattices]], as defined in [[ModularSchemeDomain]]. From cab4f8a4f412171571b656e960d7d3bf592b78df Mon Sep 17 00:00:00 2001 From: Merlijn Date: Fri, 19 Apr 2024 18:14:40 +0200 Subject: [PATCH 64/97] chore: Only add `Save` and `Load` traits to contextInsensitiveAnalysis Not to every scheme analysis by using SimpleSchemeModFAnalysis. --- .../scala/maf/cli/experiments/SchemeAnalyses.scala | 10 +++++++++- .../maf/modular/scheme/modf/SchemeModFSemantics.scala | 4 +--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/code/jvm/src/main/scala/maf/cli/experiments/SchemeAnalyses.scala b/code/jvm/src/main/scala/maf/cli/experiments/SchemeAnalyses.scala index 43480ff5d..486d41702 100644 --- a/code/jvm/src/main/scala/maf/cli/experiments/SchemeAnalyses.scala +++ b/code/jvm/src/main/scala/maf/cli/experiments/SchemeAnalyses.scala @@ -22,6 +22,8 @@ import maf.modular.scheme.modactor.mirrors.SimpleModActorWithMirrors import maf.language.racket.RacketLoaderSemantics import maf.language.racket.RacketLoader import maf.save.LoadFIFOWorklist +import maf.save.LoadModF +import maf.save.SaveModF object SchemeAnalysesBoundedDomain: object NoSensitivity: @@ -56,7 +58,13 @@ object SchemeAnalyses: def contextInsensitiveAnalysis( prg: SchemeExp - ) = new SimpleSchemeModFAnalysis(prg) with SchemeModFNoSensitivity with SchemeConstantPropagationDomain with FIFOWorklistAlgorithm[SchemeExp] with LoadFIFOWorklist[SchemeExp] { + ) = new SimpleSchemeModFAnalysis(prg) + with SchemeModFNoSensitivity + with SchemeConstantPropagationDomain + with FIFOWorklistAlgorithm[SchemeExp] + with SaveModF + with LoadModF + with LoadFIFOWorklist[SchemeExp] { override def toString = "no-sensitivity" override val analysisName: String = "modf" } diff --git a/code/shared/src/main/scala/maf/modular/scheme/modf/SchemeModFSemantics.scala b/code/shared/src/main/scala/maf/modular/scheme/modf/SchemeModFSemantics.scala index c59be1ce2..0e022b62a 100644 --- a/code/shared/src/main/scala/maf/modular/scheme/modf/SchemeModFSemantics.scala +++ b/code/shared/src/main/scala/maf/modular/scheme/modf/SchemeModFSemantics.scala @@ -416,7 +416,5 @@ abstract class SimpleSchemeModFAnalysis(prg: SchemeExp, override val name: Optio extends ModAnalysis[SchemeExp](prg) with StandardSchemeModFComponents with SchemeModFSemanticsM - with BigStepModFSemantics - with SaveModF - with LoadModF: + with BigStepModFSemantics: override def intraAnalysis(cmp: Component) = new IntraAnalysis(cmp) with BigStepModFIntra From 59005e9e9da6c13df09448f84ec2bb462a8718fd Mon Sep 17 00:00:00 2001 From: Merlijn Date: Fri, 19 Apr 2024 18:16:41 +0200 Subject: [PATCH 65/97] chore(Repl): Time the time it takes to load in the result --- code/jvm/src/main/scala/maf/cli/runnables/Repl.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/code/jvm/src/main/scala/maf/cli/runnables/Repl.scala b/code/jvm/src/main/scala/maf/cli/runnables/Repl.scala index 0504b27d2..d261c441d 100644 --- a/code/jvm/src/main/scala/maf/cli/runnables/Repl.scala +++ b/code/jvm/src/main/scala/maf/cli/runnables/Repl.scala @@ -199,9 +199,8 @@ object Repl: def runSingle(): Long = val anl = makeAnalysis(exp) if loadFile.isDefined then - anl.load(loadFile.get) - print("loaded result: ") - anl.printResult + val (elapsed, _) = Timer.time { anl.load(loadFile.get) } //anl.analyzeWithTimeout(Timeout.start(timeout.seconds)) } + println(s"load time: ${elapsed / 1000000}") val (elapsed, _) = Timer.time { anl.analyze() } //anl.analyzeWithTimeout(Timeout.start(timeout.seconds)) } // Do not print results if we are in perfomance testing mode if !performance then From ee9cc5d520b69599d47334c70ec12b34b3920fad Mon Sep 17 00:00:00 2001 From: Merlijn Date: Fri, 19 Apr 2024 18:27:39 +0200 Subject: [PATCH 66/97] refactor(Save): Use actual encoder type instead of abstract encoder You now just say that you want to use a `MapEncoder` instead of abstracting this away, this is to reduce unnecessary complexity of the program. --- .../main/scala/maf/save/save/Analysis.scala | 43 +--- .../main/scala/maf/save/save/Component.scala | 134 +++------- .../main/scala/maf/save/save/Dependency.scala | 126 +++------ .../{Encapsulated.scala => Encoder.scala} | 242 +++++------------- .../main/scala/maf/save/save/Expression.scala | 82 +++--- .../src/main/scala/maf/save/save/Store.scala | 168 +++--------- .../src/main/scala/maf/save/save/Util.scala | 11 +- 7 files changed, 222 insertions(+), 584 deletions(-) rename code/shared/src/main/scala/maf/save/save/{Encapsulated.scala => Encoder.scala} (61%) diff --git a/code/shared/src/main/scala/maf/save/save/Analysis.scala b/code/shared/src/main/scala/maf/save/save/Analysis.scala index 2b6d5da02..801ea9d6f 100644 --- a/code/shared/src/main/scala/maf/save/save/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/save/Analysis.scala @@ -7,7 +7,6 @@ import maf.core.Expression import java.nio.file.Paths import java.nio.file.Files import maf.language.scheme.SchemeExp -import EncapsulatedEncoder.* import maf.save.save.SaveSchemeExpressions import maf.save.save.SaveRecursiveSchemeExpressionsIntID import maf.modular.AnalysisEntry @@ -35,38 +34,19 @@ case class Savable[T](val value: T)(using val encoder: Encoder[T]) * The type of expression used in the analysis */ trait Save[Expr <: Expression] extends AnalysisEntry[Expr]: - /** - * Get the encoder that will be used to encode your analysis. - * - * This will influence how the analysis will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] - * encoder. - */ - def getEncoder: AbstractEncoder - - /** - * Get the encoder that will be used to encode your analysis. - * - * This encoder is used to encode objects where the key is important, when you e.g. encode a type in the key, some encoders might remove this key, - * and should therefore not be used here. - * - * This will influence how the analysis will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] - * encoder. - */ - def getKeyEncoder: AbstractEncoder - /** Encode an analysis. */ - given analysisEncoder: EncapsulatedEncoder[Save[Expr]] with - override val encoder = Save.this.getEncoder - override def writeEncapsulated(writer: Writer, value: Save[Expr]): Writer = - for (key, value) <- saveInfo do writer.writeMember(key, value.value)(using value.encoder, encoder) - writer + given analysisEncoder: MapEncoder[Save[Expr]] with + override def write(writer: Writer, value: Save[Expr]): Writer = + writer.start() + for (key, value) <- saveInfo do writer.writeMember(key, value.value)(using value.encoder) + writer.close() private var save = Set[String]() - private given excludedAnalysisEncoder: EncapsulatedEncoder[Save[Expr]] with - override val encoder = Save.this.getEncoder - override def writeEncapsulated(writer: Writer, value: Save[Expr]): Writer = - for (key, value) <- saveInfo do if save.contains(key) then writer.writeMember(key, value.value)(using value.encoder, encoder) - writer + private given excludedAnalysisEncoder: MapEncoder[Save[Expr]] with + override def write(writer: Writer, value: Save[Expr]): Writer = + writer.start() + for (key, value) <- saveInfo do if save.contains(key) then writer.writeMember(key, value.value)(using value.encoder) + writer.close() override def save(filename: String): Unit = val res = Json.encode(this)(using analysisEncoder).toByteArray @@ -106,9 +86,6 @@ trait SaveModF with SaveAddrDep[SchemeExp] with SaveSchemeAddr with SaveGlobalStore[SchemeExp] - with SaveModularSchemeLattices with SaveSequentialWorklist[SchemeExp] with SaveNoContext[SchemeExp]: - override def getEncoder: AbstractEncoder = new MapEncoder - override def getKeyEncoder: AbstractEncoder = new MapEncoder override val maxASTHeight = 3 diff --git a/code/shared/src/main/scala/maf/save/save/Component.scala b/code/shared/src/main/scala/maf/save/save/Component.scala index d66954220..685e51f0e 100644 --- a/code/shared/src/main/scala/maf/save/save/Component.scala +++ b/code/shared/src/main/scala/maf/save/save/Component.scala @@ -12,7 +12,6 @@ import maf.modular.AnalysisResults import maf.modular.Dependency import maf.modular.scheme.modf.SchemeModFComponent import maf.modular.scheme.modf.StandardSchemeModFComponents -import EncapsulatedEncoder.* import io.bullet.borer.derivation.MapBasedCodecs import maf.language.scheme.SchemeLambdaExp import maf.core.Identifier @@ -45,6 +44,7 @@ import maf.core.worklist.WorkList import maf.modular.worklist.SequentialWorklistAlgorithm import maf.modular.worklist.FIFOWorklistAlgorithm import maf.modular.ModAnalysis +import io.bullet.borer.derivation.CompactMapBasedCodecs /** * Trait to encode positions. @@ -53,19 +53,10 @@ import maf.modular.ModAnalysis * The type of expression used in the analysis */ trait SavePosition[Expr <: Expression] extends Save[Expr]: - /** - * Get the encoder that will be used to encode positions. - * - * This will influence how positions will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] - * encoder. - */ - def getPositionEncoder: AbstractEncoder = getEncoder - - private val posEncoder = getPositionEncoder - given Encoder[Position] = AbstractEncoder.deriveAllEncoders[Position](posEncoder) - given Encoder[PTag] = AbstractEncoder.deriveAllEncoders[PTag](posEncoder) - given Encoder[Identifier] = AbstractEncoder.deriveEncoder[Identifier](posEncoder) - given Encoder[Identity] = AbstractEncoder.deriveAllEncoders[Identity](posEncoder) + given Encoder[Position] = CompactMapBasedCodecs.deriveAllEncoders[Position] + given Encoder[PTag] = CompactMapBasedCodecs.deriveAllEncoders[PTag] + given Encoder[Identifier] = CompactMapBasedCodecs.deriveEncoder[Identifier] + given Encoder[Identity] = CompactMapBasedCodecs.deriveAllEncoders[Identity] given Encoder[IdentityData] with def write(writer: Writer, value: IdentityData): Writer = System.err.nn.println("IdentityData could not be encoded") @@ -83,25 +74,6 @@ trait SavePosition[Expr <: Expression] extends Save[Expr]: * The type of expression used in the analysis */ trait SaveComponents[Expr <: Expression] extends ModAnalysis[Expr] with Save[Expr]: - /** - * Get the encoder that will be used to encode components. - * - * This will influence how components will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] - * encoder. - */ - def getComponentEncoder: AbstractEncoder = getEncoder - - /** - * Get the encoder that will be used to encode components. - * - * This encoder is used to encode objects where the key is important, when you e.g. encode a type in the key, some encoders might remove this key, - * and should therefore not be used here. - * - * This will influence how components will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] - * encoder. - */ - def getComponentKeyEncoder: AbstractEncoder = getKeyEncoder - /** Encodes a component. */ given componentEncoder: Encoder[Component] @@ -146,14 +118,14 @@ trait SaveComponentIntID[Expr <: Expression] extends SaveActualComps[Expr] with private val components = HashMap[Component, Int]() private var id = 0 - override protected given componentSetEncoder: EncapsulatedEncoder[Set[Component]] with - override val encoder: AbstractEncoder = getComponentKeyEncoder - override protected def writeEncapsulated(writer: Writer, components: Set[Component]): Writer = + override protected given componentSetEncoder: MapEncoder[Set[Component]] with + override def write(writer: Writer, components: Set[Component]): Writer = + writer.start() for (component <- components) do SaveComponentIntID.this.components.addOne((component, id)) - writer.writeMember(id.toString(), component)(using actualComponentEncoder, encoder) + writer.writeMember(id.toString(), component)(using actualComponentEncoder) id += 1 - writer + writer.close() override given componentIDEncoder: Encoder[Component] with override def write(writer: Writer, component: Component): Writer = writer.write(components(component)) @@ -167,33 +139,27 @@ trait SaveComponentIntID[Expr <: Expression] extends SaveActualComps[Expr] with * Because this trait only encodes the component position, the entire component should be encoded somewhere else if you want to decode this again. */ trait SaveStandardSchemeComponentPosition extends SaveComponentID[SchemeExp] with StandardSchemeModFComponents: - override type Component = SchemeModFComponent - - /** - * Get the encoder that will be used to encode a set of components. - * - * This will influence how a set of components will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an - * [[ArrayEncoder array-based]] encoder. - */ - def getComponentSetEncoder: AbstractEncoder = new ArrayEncoder - /** Encodes a component by their position */ override given componentIDEncoder: Encoder[Component] with def write(writer: Writer, component: Component): Writer = + writer.start() if component.equals(initialComponent) then writer.write("main") else writer.write(component.asInstanceOf[SchemeModFComponent.Call[ComponentContext]])(schemeComponentIDEncoder) + writer.close() /** Encodes a scheme component using their position */ given schemeComponentIDEncoder[T]: Encoder[SchemeModFComponent.Call[T]] with def write(writer: Writer, component: SchemeModFComponent.Call[T]): Writer = + writer.start() val (lambda, _) = component.clo writer.write(lambda.idn.pos) + writer.close() - override protected given componentSetEncoder: EncapsulatedEncoder[Set[Component]] with - override val encoder: AbstractEncoder = getComponentSetEncoder - override protected def writeEncapsulated(writer: Writer, components: Set[Component]): Writer = + override protected given componentSetEncoder: ArrayEncoder[Set[Component]] with + override def write(writer: Writer, components: Set[Component]): Writer = + writer.start() for (component <- components) do writer.writeMember(component) - writer + writer.close() /** * Trait to encode environments. @@ -202,37 +168,19 @@ trait SaveStandardSchemeComponentPosition extends SaveComponentID[SchemeExp] wit * The type of the value the needs to be saved */ trait SaveEnvironment[Expr <: Expression] extends Save[Expr] with SaveAddr[Expr]: - /** - * Get the encoder that will be used to encode environments. - * - * This will influence how environments will be encodes, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] - * encoder. - */ - def getEnvironmentEncoder: AbstractEncoder = getEncoder - - /** - * Get the encoder that will be used to encode environments. - * - * This encoder is used to encode objects where the key is important, when you e.g. encode a type in the key, some encoders might remove this key, - * and should therefore not be used here. - * - * This will influence how environments will be encodes, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] - * encoder. - */ - def getEnvironmentKeyEncoder: AbstractEncoder = getKeyEncoder given Encoder[BasicEnvironment[Address]] with override def write(writer: Writer, env: BasicEnvironment[Address]): Writer = writer.write(env.content) - given EncapsulatedEncoder[NestedEnv[Address, Address]] with - override val encoder: AbstractEncoder = getEnvironmentEncoder - override protected def writeEncapsulated(writer: Writer, env: NestedEnv[Address, Address]): Writer = + given MapEncoder[NestedEnv[Address, Address]] with + override def write(writer: Writer, env: NestedEnv[Address, Address]): Writer = + writer.start() writer.writeMember("content", env.content) if env.rst.isDefined then writer.writeMember("rst", env.rst.get) - writer + writer.close() - given EncapsulatedEncoder[Environment[Address]] with - override val encoder: AbstractEncoder = getEnvironmentKeyEncoder - override protected def writeEncapsulated(writer: Writer, env: Environment[Address]): Writer = + given MapEncoder[Environment[Address]] with + override def write(writer: Writer, env: Environment[Address]): Writer = + writer.start() env match { case basicEnv @ BasicEnvironment(_) => writer.writeMember("basicEnvironment", basicEnv) @@ -242,6 +190,7 @@ trait SaveEnvironment[Expr <: Expression] extends Save[Expr] with SaveAddr[Expr] System.err.nn.println("The environemnt with type `" + env.getClass + "` could not be encoded") writer } + writer.close() /** * Base trait for saving context. @@ -257,14 +206,6 @@ trait SaveContext[Expr <: Expression] extends Save[Expr]: /** The type of context that should be encoded. */ type EncodeContext - /** - * Get the encoder that will be used to encode context. - * - * This will influence how context will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] - * encoder. - */ - def getContextEncoder: AbstractEncoder = getEncoder - /** Encodes context */ given contextEncoder: Encoder[EncodeContext] @@ -302,24 +243,17 @@ trait SaveStandardSchemeComponents if component.equals(initialComponent) then writer.write("main") else writer.write(component.asInstanceOf[SchemeModFComponent.Call[ComponentContext]]) - given [T]: EncapsulatedEncoder[SchemeModFComponent.Call[T]] with - override val encoder = getComponentEncoder - override def writeEncapsulated(writer: Writer, component: SchemeModFComponent.Call[T]): Writer = + given [T]: MapEncoder[SchemeModFComponent.Call[T]] with + override def write(writer: Writer, component: SchemeModFComponent.Call[T]): Writer = + writer.start() val (lambda, env) = component.clo val context = component.ctx writer.writeMember("lambda", lambda.asInstanceOf[SchemeExp]) writer.writeMember("environment", env) writer.writeMember("context", context.asInstanceOf[EncodeContext]) + writer.close() trait SaveWorklist[Expr <: Expression] extends Save[Expr]: - /** - * Get the encoder that will be used to encode the worklist. - * - * This will influence how the worklist will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] - * encoder. - */ - def getWorklistEncoder: AbstractEncoder = new ArrayEncoder - type WorklistComponent given worklistEncoder: Encoder[WorkList[WorklistComponent]] def getWorklist: WorkList[WorklistComponent] @@ -328,9 +262,9 @@ trait SaveWorklist[Expr <: Expression] extends Save[Expr]: trait SaveSequentialWorklist[Expr <: Expression] extends SaveWorklist[Expr] with SequentialWorklistAlgorithm[Expr] with SaveComponents[Expr]: type WorklistComponent = Component override def getWorklist: WorkList[Component] = workList - override given worklistEncoder: EncapsulatedEncoder[WorkList[Component]] with - override val encoder: AbstractEncoder = getWorklistEncoder - override protected def writeEncapsulated(writer: Writer, worklist: WorkList[Component]): Writer = + override given worklistEncoder: ArrayEncoder[WorkList[Component]] with + override def write(writer: Writer, worklist: WorkList[Component]): Writer = + writer.start() val worklistList = workList.toList worklistList.foreach(writer.writeMember(_)) - writer + writer.close() diff --git a/code/shared/src/main/scala/maf/save/save/Dependency.scala b/code/shared/src/main/scala/maf/save/save/Dependency.scala index 49c47762f..89afda73e 100644 --- a/code/shared/src/main/scala/maf/save/save/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/save/Dependency.scala @@ -11,11 +11,11 @@ import maf.modular.ReturnAddr import io.bullet.borer.Encoder import maf.modular.scheme.PrmAddr import maf.modular.scheme.PtrAddr -import EncapsulatedEncoder.* import maf.language.scheme.SchemeValue import maf.core.Identifier import maf.util.Writer.write import maf.save.save.SaveExpressions +import maf.modular.scheme.SchemeAddr /** * Trait to encode address dependencies. @@ -26,7 +26,7 @@ import maf.save.save.SaveExpressions * The type of expression used in the analysis */ trait SaveAddrDep[Expr <: Expression] extends SaveDependency[Expr] with SavePosition[Expr] with SaveAddr[Expr]: - override protected def encodeDependency(writer: Writer, dependency: Dependency)(using AbstractEncoder): Writer = + override protected def encodeDependency(writer: Writer, dependency: Dependency): Writer = dependency match { case AddrDependency(addr) => writer.writeMember("addrDependency", addr) case _ => super.encodeDependency(writer, dependency) @@ -43,13 +43,6 @@ trait SaveAddrDep[Expr <: Expression] extends SaveDependency[Expr] with SavePosi * The type of expression used in the analysis */ trait SaveDependency[Expr <: Expression] extends SaveMapToArray with SaveComponents[Expr]: - /** - * Get the encoder that will be used to encode dependencies. - * - * This will influence how dependencies will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] - * encoder. - */ - def getDependencyEncoder: AbstractEncoder = getEncoder override def saveInfo: List[(String, Savable[_])] = super.saveInfo ++ List(("dependencies" -> Savable(deps))) @@ -79,13 +72,15 @@ trait SaveDependency[Expr <: Expression] extends SaveMapToArray with SaveCompone * @return * The used writer */ - protected def encodeDependency(writer: Writer, dependency: Dependency)(using AbstractEncoder): Writer = + protected def encodeDependency(writer: Writer, dependency: Dependency): Writer = System.err.nn.println("The dependency with type `" + dependency.getClass + "` could not be encoded") writer - given EncapsulatedEncoder[Dependency] with - override val encoder: AbstractEncoder = getDependencyEncoder - override protected def writeEncapsulated(writer: Writer, value: Dependency): Writer = encodeDependency(writer, value) + given MapEncoder[Dependency] with + override def write(writer: Writer, value: Dependency): Writer = + writer.start() + encodeDependency(writer, value) + writer.close() /** * Base trait for encoding addresses. @@ -98,58 +93,7 @@ trait SaveDependency[Expr <: Expression] extends SaveMapToArray with SaveCompone * The type of expression used in the analysis */ trait SaveAddr[Expr <: Expression] extends Save[Expr] with SavePosition[Expr]: - /** - * Get the encoder that will be used to encode addresses. - * - * This will influence how addresses will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] - * encoder. - */ - def getAddressEncoder: AbstractEncoder = getEncoder - - /** - * Get the encoder that will be used to encode addresses. - * - * This encoder is used to encode objects where the key is important, when you e.g. encode a type in the key, some encoders might remove this key, - * and should therefore not be used here. - * - * This will influence how addresses will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] - * encoder. - */ - def getAddressKeyEncoder: AbstractEncoder = getKeyEncoder - - /** - * Encodes an address. - * - * This method allows for expanding the addresses that can be encoded by overriding it, and allowing you to add new addresses by simply mixin in - * another trait that overrides this method. If you want to add a new encodable address you can override this method like this: - * {{{ - * override def encodeAddress(writer: Writer, address: Address)(using AbstractEncoder): Writer = - * address match { - * case < Address >(...) => < encode address > - * case _ => super.encodeDependency(writer, address) - * } - * }}} - * This is just an example and the actual implementation can also be done differently. - * - * @note - * This method should not be called directly, but should instead only be called from an encoder. - * - * @param writer - * The writer to write to - * @param address - * The address to encode - * @param encoder - * Implicit argument that encodes the address - * @return - * The used writer - */ - protected def encodeAddress(writer: Writer, address: Address)(using encoder: AbstractEncoder): Writer = - System.err.nn.println("The address with type `" + address.getClass + "` could not be encoded") - writer - - given EncapsulatedEncoder[Address] with - override val encoder = getAddressKeyEncoder - override def writeEncapsulated(writer: Writer, value: Address): Writer = encodeAddress(writer, value) + given addressEncoder: Encoder[Address] /** * Trait to encode scheme addresses. @@ -157,37 +101,43 @@ trait SaveAddr[Expr <: Expression] extends Save[Expr] with SavePosition[Expr]: * This is an implementation of [[SaveAddr]]. */ trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveComponents[SchemeExp] with SaveContext[SchemeExp] with SaveExpressions[SchemeExp]: - given EncapsulatedEncoder[VarAddr[EncodeContext]] with - override val encoder = getAddressEncoder - override def writeEncapsulated(writer: Writer, address: VarAddr[EncodeContext]): Writer = + given MapEncoder[VarAddr[EncodeContext]] with + override def write(writer: Writer, address: VarAddr[EncodeContext]): Writer = + writer.start() writer.writeMember("id", address.id) if address.ctx.asInstanceOf[Option[EncodeContext]].isDefined then writer.writeMember("context", address.ctx.asInstanceOf[Option[EncodeContext]].get) - writer + writer.close() - given EncapsulatedEncoder[ReturnAddr[Component]] with - override val encoder = getAddressEncoder - override def writeEncapsulated(writer: Writer, address: ReturnAddr[Component]): Writer = + given MapEncoder[ReturnAddr[Component]] with + override def write(writer: Writer, address: ReturnAddr[Component]): Writer = + writer.start() writer.writeMember("identity", address.idn) writer.writeMember("component", address.cmp) + writer.close() - given EncapsulatedEncoder[PtrAddr[EncodeContext]] with - override val encoder = getAddressEncoder - override def writeEncapsulated(writer: Writer, address: PtrAddr[EncodeContext]): Writer = + given MapEncoder[PtrAddr[EncodeContext]] with + override def write(writer: Writer, address: PtrAddr[EncodeContext]): Writer = + writer.start() writer.writeMember("expression", address.exp.asInstanceOf[SchemeExp]) if address.ctx.asInstanceOf[Option[EncodeContext]].isDefined then writer.writeMember("context", address.ctx.asInstanceOf[Option[EncodeContext]].get) - writer + writer.close() - override protected def encodeAddress(writer: Writer, address: Address)(using encoder: AbstractEncoder): Writer = - address match { - case varAddr @ VarAddr(_, _) => - writer.writeMember("varAddr", varAddr.asInstanceOf[VarAddr[EncodeContext]]) - case returnAddr @ ReturnAddr(_, _) => - writer.writeMember("returnAddr", returnAddr.asInstanceOf[ReturnAddr[Component]]) - case PrmAddr(nam) => - writer.writeMember("prmAddr", nam) - case ptrAddr @ PtrAddr(_, _) => - writer.writeMember("ptrAddr", ptrAddr.asInstanceOf[PtrAddr[EncodeContext]]) - case _ => super.encodeAddress(writer, address) - } + override given addressEncoder: MapEncoder[Address] with + override def write(writer: Writer, address: Address) = + writer.start() + address match { + case varAddr @ VarAddr(_, _) => + writer.writeMember("varAddr", varAddr.asInstanceOf[VarAddr[EncodeContext]]) + case returnAddr @ ReturnAddr(_, _) => + writer.writeMember("returnAddr", returnAddr.asInstanceOf[ReturnAddr[Component]]) + case PrmAddr(nam) => + writer.writeMember("prmAddr", nam) + case ptrAddr @ PtrAddr(_, _) => + writer.writeMember("ptrAddr", ptrAddr.asInstanceOf[PtrAddr[EncodeContext]]) + case _ => + System.err.nn.println("The scheme address with type `" + address.getClass + "` could not be encoded") + writer + } + writer.close() diff --git a/code/shared/src/main/scala/maf/save/save/Encapsulated.scala b/code/shared/src/main/scala/maf/save/save/Encoder.scala similarity index 61% rename from code/shared/src/main/scala/maf/save/save/Encapsulated.scala rename to code/shared/src/main/scala/maf/save/save/Encoder.scala index d97ea3112..aa4a9d784 100644 --- a/code/shared/src/main/scala/maf/save/save/Encapsulated.scala +++ b/code/shared/src/main/scala/maf/save/save/Encoder.scala @@ -29,10 +29,7 @@ import scala.concurrent.ExecutionContext.Implicits.global * Some encoders like [[ArrayEncoder]] will also not encode your key, because arrays do not require keys like maps do, if the key does need to be * stored, you should use an encoder that writes them down like [[MapEncoder]] or [[ArrayKeyEncoder]] if you want to use an array. */ -trait AbstractEncoder: - /** Does this encoder use maps or array to encode the data. */ - protected val mapBasedEncoder: Boolean - +trait AbstractEncoder[T] extends Encoder[T]: /** * Write a generated key to the given writer. * @@ -129,144 +126,6 @@ trait AbstractEncoder: */ def closeEncapsulation(writer: Writer): Writer = writer.writeBreak() -object AbstractEncoder: - /** - * Automatically derive an encoder for type T. - * - * This will derive an encoder for type T, this encoder will either be [[CompactMapBasedCodecs map-based]] or [[ArrayBasedCodecs array-based]] - * based on the type of the provided encoder. - * - * @note - * T must be a case class, enum, sealed abstract class or sealed trait - * - * @param encoder - * The type of encoder that should be used to encode T - * @tparam T - * The type to derive encoders for - */ - inline def deriveEncoder[T](encoder: AbstractEncoder): Encoder[T] = - if encoder.mapBasedEncoder then CompactMapBasedCodecs.deriveEncoder[T] else ArrayBasedCodecs.deriveEncoder[T] - - /** - * Automatically derive all encoders for type T. - * - * This will derive an encoder for the type T and all subtypes of type T, these encoders will either be [[CompactMapBasedCodecs map-based]] or - * [[ArrayBasedCodecs array-based]] based on the type of the provided encoder. - * - * @note - * T must be a case object, case class, sealed trait or sealed abstract class. - * - * @param encoder - * The type of encoder that should be used to encode T - * @tparam T - * The type to derive encoders for - */ - inline def deriveAllEncoders[T](encoder: AbstractEncoder): Encoder[T] = - if encoder.mapBasedEncoder then CompactMapBasedCodecs.deriveAllEncoders[T] else ArrayBasedCodecs.deriveAllEncoders[T] - -/** - * Trait used to encode an instance of type T. - * - * This trait will write a value of type T but encapsulate it either in an array or a map, based on the encoder that is given. In order to implement - * this class, you should overwrite the [[encoder]] variable to decide on the type of encoder you want to use, and the [[writeEncapsulated]] method - * which will implement the actual writing of the value. - * - * {{{ - * given EncapsulatedEncoder[< T >] with - * override val encoder: AbstractEncoder = < getEncoder > - * override protected def writeEncapsulated(writer: Writer, value: < T >): Writer = < encode value > - * }}} - * - * @note - * Since this class already opens a map/array, you should not do this anymore unless you are writing a nested map/array. - * - * @example - * If you implement it as a given, you can use it implicitly in your code - * {{{ - * override protected def writeEncapsulated(writer: Writer, value: < ... >): Writer = - * ... - * // Will use the implicit Encoder[T] - * writer.writeMember[T]("< key >", < value >) - * ... - * }}} - * - * @tparam T - * The type to encode - */ -trait EncapsulatedEncoder[T] extends Encoder[T]: - /** The encoder used to write this value, this will specify how this value will be written (e.g. in a map or in an array). */ - val encoder: AbstractEncoder - - /** - * The encoder used to write this value, this will specify how this value will be written (e.g. in a map or in an array). - * - * This is an given because a lot of method require this encoder to be added implicitly, adding the implicit here, makes it that you don't have to - * specify this anymore. - */ - protected given AbstractEncoder = encoder - override def write(writer: Writer, value: T): Writer = - encoder.openEncapsulation(writer) - writeEncapsulated(writer, value) - encoder.closeEncapsulation(writer) - - /** - * Write a given value encapsulated in a map/array based on the [[encoder]]. - * - * @note - * This should not be called directly, but only through the [[write]] method. - * - * @param writer - * The writer used to write the value - * @param value - * The value that should be written - */ - protected def writeEncapsulated(writer: Writer, value: T): Writer - -/** - * Trait used to encode an instance of type T. - * - * This trait will write a value of type T but encapsulate it either in an array. In order to implement this class, you should overwrite the - * [[encoder]] variable to decide on the type of encoder you want to use, and the [[writeEncapsulated]] method which will implement the actual writing - * of the value. - * - * {{{ - * given EncapsulatedEncoder[< T >] with - * override val encoder: AbstractEncoder = < getEncoder > - * override protected def writeEncapsulated(writer: Writer, value: < T >): Writer = < encode value > - * }}} - * - * @note - * Since this class already opens an array, you should not do this anymore unless you are writing a nested map/array. - * - * @note - * Using `writer.writeMember`, `writer.openEncapsulation`, ... can still use either a map or an array, it is only the top-level encapsulation that - * is an array. - * - * @example - * If you implement it as a given, you can use it implicitly in your code - * {{{ - * override protected def writeEncapsulated(writer: Writer, value: < ... >): Writer = - * ... - * // Will use the implicit Encoder[T] - * writer.writeMember[T]("< key >", < value >) - * ... - * }}} - * - * @tparam T - * The type to encode - */ -trait EncapsulatedArrayEncoder[T](length: Int = 0) extends EncapsulatedEncoder[T]: - override def write(writer: Writer, value: T): Writer = - if length == 0 then writer.writeArrayStart() else writer.writeArrayOpen(length) - writeEncapsulated(writer, value) - writer.writeBreak() - -/** - * Object with extension methods for [[borer.Writer]]. - * - * These extension methods should be used when using an [[EncapsulatedEncoder]]. - */ -object EncapsulatedEncoder: extension (writer: Writer) /** * Close the encapsulation. @@ -276,7 +135,7 @@ object EncapsulatedEncoder: * @param encoder * Implicit argument that decides how to close the encapsulation */ - def close()(using encoder: AbstractEncoder): Writer = encoder.closeEncapsulation(writer) + def close(): Writer = closeEncapsulation(writer) /** * Write a key-value pair. @@ -292,9 +151,9 @@ object EncapsulatedEncoder: * @tparam T * The type of the value that should be written, this type should have an encoder */ - def writeMember[T: Encoder](key: String, value: T)(using encoder: AbstractEncoder): Writer = - encoder.writeKey(writer, key) - encoder.writeValue(writer, value) + def writeMember[T: Encoder](key: String, value: T): Writer = + writeKey(writer, key) + writeValue(writer, value) /** * Write a value. @@ -309,9 +168,13 @@ object EncapsulatedEncoder: * @tparam T * The type of the value that should be written, this type should have an encoder */ - def writeMember[T: Encoder](value: T)(using encoder: AbstractEncoder): Writer = - encoder.writeKey(writer) - encoder.writeValue(writer, value) + def writeMember[T: Encoder](value: T): Writer = + writeKey(writer) + writeValue(writer, value) + + /** [TODO: description] */ + def start() = + openEncapsulation(writer) /** * Open the encapsulation. @@ -322,9 +185,9 @@ object EncapsulatedEncoder: * @param encoder * Implicit argument that decides how to open the encapsulation */ - def open()(using encoder: AbstractEncoder): Writer = - encoder.writeKey(writer) - encoder.openEncapsulation(writer) + def open(): Writer = + writeKey(writer) + openEncapsulation(writer) /** * Open the encapsulation with a key. @@ -336,9 +199,9 @@ object EncapsulatedEncoder: * @param encoder * Implicit argument that decides how to open the encapsulation */ - def open(key: String)(using encoder: AbstractEncoder): Writer = - encoder.writeKey(writer, key) - encoder.openEncapsulation(writer) + def open(key: String): Writer = + writeKey(writer, key) + openEncapsulation(writer) /** * Open the encapsulation. @@ -351,9 +214,9 @@ object EncapsulatedEncoder: * @param encoder * Implicit argument that decides how to open the encapsulation */ - def open(amount: Int)(using encoder: AbstractEncoder): Writer = - encoder.writeKey(writer) - encoder.openEncapsulation(writer, amount) + def open(amount: Int): Writer = + writeKey(writer) + openEncapsulation(writer, amount) /** * Open the encapsulation with a key. @@ -367,19 +230,30 @@ object EncapsulatedEncoder: * @param encoder * Implicit argument that decides how to open the encapsulation */ - def open(key: String, amount: Int)(using encoder: AbstractEncoder): Writer = - encoder.writeKey(writer, key) - encoder.openEncapsulation(writer) + def open(key: String, amount: Int): Writer = + writeKey(writer, key) + openEncapsulation(writer) /** * Encoder that uses maps to encode values. * * This encoder uses maps to encode values and will therefore always use keys, if no key is provided, an auto-increasing ID will be used instead. + * + * @example + * {{{ + * given MapEncoder[T] + * override protected def write(writer: Writer, value: T): Writer = + * writer.start() + * < encode value > + * writer.close() + * }}} + * + * @tparam T + * The type to encode */ -class MapEncoder extends AbstractEncoder: +trait MapEncoder[T] extends AbstractEncoder[T]: /** Used to generate IDs if no key is provided */ private var id = -1 - val mapBasedEncoder = true override def writeKey(writer: Writer): Writer = id += 1 writeKey(writer, id.toString()) @@ -393,9 +267,20 @@ class MapEncoder extends AbstractEncoder: * * This encoder uses arrays to encode values and will not write any keys, if you want an array-based encoder that saves keys, you should use * [[ArrayKeyEncoder]]. + * + * @example + * {{{ + * given ArrayEncoder[T] + * override protected def write(writer: Writer, value: T): Writer = + * writer.start() + * < encode value > + * writer.close() + * }}} + * + * @tparam T + * The type to encode */ -class ArrayEncoder extends AbstractEncoder: - override val mapBasedEncoder = false +trait ArrayEncoder[T] extends AbstractEncoder[T]: override def writeKey(writer: Writer): Writer = writer override def writeKey(writer: Writer, key: String): Writer = writer override def writeValue[T: Encoder](writer: Writer, value: T): Writer = writer.write(value) @@ -419,8 +304,20 @@ class ArrayEncoder extends AbstractEncoder: * ... * ] * }}} + * + * @example + * {{{ + * given ArrayKeyEncoder[T] + * override protected def write(writer: Writer, value: T): Writer = + * writer.start() + * < encode value > + * writer.close() + * }}} + * + * @tparam T + * The type to encode */ -class ArrayKeyEncoder extends ArrayEncoder: +trait ArrayKeyEncoder[T] extends ArrayEncoder[T]: override def writeKey(writer: Writer, key: String): Writer = writer.write(key) /** @@ -443,13 +340,6 @@ class ArrayKeyEncoder extends ArrayEncoder: def writeKey[T: Encoder](writer: Writer, key: T): Writer = writer.write(key) override def writeValue[T: Encoder](writer: Writer, value: T): Writer = writer.write(value) -/** - * Object with an extension method for [[borer.Writer]]. - * - * This extension method allows you to write a key-value pair where the key is not a String, and should only be used when using an - * [[ArrayKeyEncoder]]. - */ -object ArrayKeyEncoder: extension (writer: Writer) /** * Write a key-value pair. @@ -467,6 +357,6 @@ object ArrayKeyEncoder: * @tparam U * The type of the value that should be written, this type should have an encoder */ - def writeMember[T: Encoder, U: Encoder](key: T, value: U)(using encoder: ArrayKeyEncoder): Writer = - encoder.writeKey(writer, key) - encoder.writeValue(writer, value) + def writeMember[T: Encoder, U: Encoder](key: T, value: U): Writer = + writeKey(writer, key) + writeValue(writer, value) diff --git a/code/shared/src/main/scala/maf/save/save/Expression.scala b/code/shared/src/main/scala/maf/save/save/Expression.scala index e0e666b39..ab89f396e 100644 --- a/code/shared/src/main/scala/maf/save/save/Expression.scala +++ b/code/shared/src/main/scala/maf/save/save/Expression.scala @@ -6,9 +6,7 @@ import maf.save.AbstractEncoder import io.bullet.borer.Encoder import maf.save.Savable import scala.collection.mutable.HashMap -import maf.save.EncapsulatedEncoder import io.bullet.borer.Writer -import EncapsulatedEncoder.* import maf.language.scheme.SchemeExp import maf.language.scheme.SchemeFuncall import maf.language.scheme.SchemeLambda @@ -26,6 +24,8 @@ import maf.language.scheme.SchemeVar import maf.language.scheme.SchemeValue import maf.language.scheme.SchemeLambdaExp import maf.modular.ModAnalysis +import maf.save.MapEncoder +import io.bullet.borer.derivation.CompactMapBasedCodecs /** * The base trait for encoding expressions. @@ -39,25 +39,6 @@ import maf.modular.ModAnalysis * The type of expression used in the analysis */ trait SaveExpressions[Expr <: Expression] extends Save[Expr]: - /** - * Get the encoder that will be used to encode expressions. - * - * This will influence how context will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] - * encoder. - */ - def getExpressionEncoder: AbstractEncoder = getEncoder - - /** - * Get the encoder that will be used to encode expression. - * - * This encoder is used to encode objects where the key is important, when you e.g. encode a type in the key, some encoders might remove this key, - * and should therefore not be used here. - * - * This will influence how components will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] - * encoder. - */ - def getExpressionKeyEncoder: AbstractEncoder = getKeyEncoder - /** Encodes an expression */ given expressionEncoder: Encoder[Expr] @@ -72,7 +53,7 @@ trait SaveExpressions[Expr <: Expression] extends Save[Expr]: * [[SaveExpressionIntID]] should be mixed in for the implementation. The trait that should be mixed in depends on the kind of components are used * in your analysis. * @note - * This trait should not be used, rather, [[SaveExpressions]] should be extended. + * This trait should not be used, rather, [[SaveExpressions]] or [[SaveActualExpressions]] should be extended. * * @tparam Expr * The type of expression used in the analysis @@ -118,14 +99,14 @@ trait SaveExpressionIntID[Expr <: Expression] extends SaveExpressionID[Expr] wit private val expressions = HashMap[Expr, Int]() private var id = 0 - override protected given expressionSetEncoder: EncapsulatedEncoder[Set[Expr]] with - override val encoder = getExpressionKeyEncoder - def writeEncapsulated(writer: Writer, exprs: Set[Expr]): Writer = + override protected given expressionSetEncoder: MapEncoder[Set[Expr]] with + override def write(writer: Writer, exprs: Set[Expr]): Writer = + writer.start() for (expr <- exprs) do - writer.writeMember(id.toString, expr)(using actualExpressionEncoder, encoder) + writer.writeMember(id.toString, expr)(using actualExpressionEncoder) expressions.addOne((expr, id)) id += 1 - writer + writer.close() override protected given expressionIDEncoder: Encoder[Expr] with override def write(writer: Writer, expr: Expr): Writer = @@ -147,9 +128,7 @@ trait SaveRecursiveSchemeExpressionsIntID extends SaveExpressionID[SchemeExp] wi /** The max height of the AST before you encode it normally. */ val maxASTHeight: Int - override protected given expressionSetEncoder: EncapsulatedEncoder[Set[SchemeExp]] with - override val encoder = getExpressionKeyEncoder - + override protected given expressionSetEncoder: MapEncoder[Set[SchemeExp]] with given Encoder[List[SchemeExp]] with override def write(writer: Writer, exprs: List[SchemeExp]): Writer = for (expr <- exprs) writer.write(expr)(using recursiveExpressionEncoder) @@ -185,14 +164,15 @@ trait SaveRecursiveSchemeExpressionsIntID extends SaveExpressionID[SchemeExp] wi writer.write(letStar.body) case _ => () - writer.writeMember(id.toString(), expr)(using actualExpressionEncoder, encoder) + writer.writeMember(id.toString(), expr)(using actualExpressionEncoder) expressions.addOne(expr, id) id += 1 writer - def writeEncapsulated(writer: Writer, exprs: Set[SchemeExp]): Writer = + override def write(writer: Writer, exprs: Set[SchemeExp]): Writer = + writer.start() for (expr <- exprs) do writer.write(expr)(using recursiveExpressionEncoder) - writer + writer.close() override protected given expressionIDEncoder: Encoder[SchemeExp] with override def write(writer: Writer, expr: SchemeExp): Writer = @@ -213,9 +193,9 @@ trait SaveActualExpressions[Expr <: Expression] extends SaveActualExprs[Expr]: * Implementation of [[SaveExpressions]]. */ trait SaveSchemeExpressions extends SaveActualExprs[SchemeExp] with SaveSchemeSubExpressions with SavePosition[SchemeExp]: - override protected given actualExpressionEncoder: EncapsulatedEncoder[SchemeExp] with - override val encoder = getExpressionKeyEncoder - def writeEncapsulated(writer: Writer, exp: SchemeExp): Writer = + override protected given actualExpressionEncoder: MapEncoder[SchemeExp] with + override def write(writer: Writer, exp: SchemeExp): Writer = + writer.start() exp match case funcall: SchemeFuncall => writer.writeMember("funcall", funcall) case variable: SchemeVar => writer.writeMember("var", variable) @@ -232,6 +212,7 @@ trait SaveSchemeExpressions extends SaveActualExprs[SchemeExp] with SaveSchemeSu case _ => System.err.nn.println("The scheme expression with type `" + exp.getClass + "` could not be encoded") writer + writer.close() /** * Save Scheme subexpressions. @@ -239,18 +220,17 @@ trait SaveSchemeExpressions extends SaveActualExprs[SchemeExp] with SaveSchemeSu * Implementation of [[SaveExpressions]]. */ trait SaveSchemeSubExpressions extends SaveExpressions[SchemeExp] with SavePosition[SchemeExp]: - private val compEncoder = getExpressionEncoder - given Encoder[SchemeValue] = AbstractEncoder.deriveEncoder(compEncoder) - given Encoder[maf.language.sexp.Value] = AbstractEncoder.deriveAllEncoders(compEncoder) - given Encoder[SchemeFuncall] = AbstractEncoder.deriveEncoder[SchemeFuncall](compEncoder) - given Encoder[SchemeVar] = AbstractEncoder.deriveEncoder[SchemeVar](compEncoder) - given Encoder[SchemeLambda] = AbstractEncoder.deriveEncoder[SchemeLambda](compEncoder) - given Encoder[SchemeVarArgLambda] = AbstractEncoder.deriveEncoder[SchemeVarArgLambda](compEncoder) - given Encoder[SchemeLambdaExp] = AbstractEncoder.deriveEncoder[SchemeLambdaExp](compEncoder) - given Encoder[SchemeLetrec] = AbstractEncoder.deriveEncoder[SchemeLetrec](compEncoder) - given Encoder[SchemeAssert] = AbstractEncoder.deriveEncoder[SchemeAssert](compEncoder) - given Encoder[SchemeLet] = AbstractEncoder.deriveEncoder[SchemeLet](compEncoder) - given Encoder[SchemeIf] = AbstractEncoder.deriveEncoder[SchemeIf](compEncoder) - given Encoder[SchemeSet] = AbstractEncoder.deriveEncoder[SchemeSet](compEncoder) - given Encoder[SchemeBegin] = AbstractEncoder.deriveEncoder[SchemeBegin](compEncoder) - given Encoder[SchemeLetStar] = AbstractEncoder.deriveEncoder[SchemeLetStar](compEncoder) + given Encoder[SchemeValue] = CompactMapBasedCodecs.deriveEncoder[SchemeValue] + given Encoder[maf.language.sexp.Value] = CompactMapBasedCodecs.deriveAllEncoders[maf.language.sexp.Value] + given Encoder[SchemeFuncall] = CompactMapBasedCodecs.deriveEncoder[SchemeFuncall] + given Encoder[SchemeVar] = CompactMapBasedCodecs.deriveEncoder[SchemeVar] + given Encoder[SchemeLambda] = CompactMapBasedCodecs.deriveEncoder[SchemeLambda] + given Encoder[SchemeVarArgLambda] = CompactMapBasedCodecs.deriveEncoder[SchemeVarArgLambda] + given Encoder[SchemeLambdaExp] = CompactMapBasedCodecs.deriveEncoder[SchemeLambdaExp] + given Encoder[SchemeLetrec] = CompactMapBasedCodecs.deriveEncoder[SchemeLetrec] + given Encoder[SchemeAssert] = CompactMapBasedCodecs.deriveEncoder[SchemeAssert] + given Encoder[SchemeLet] = CompactMapBasedCodecs.deriveEncoder[SchemeLet] + given Encoder[SchemeIf] = CompactMapBasedCodecs.deriveEncoder[SchemeIf] + given Encoder[SchemeSet] = CompactMapBasedCodecs.deriveEncoder[SchemeSet] + given Encoder[SchemeBegin] = CompactMapBasedCodecs.deriveEncoder[SchemeBegin] + given Encoder[SchemeLetStar] = CompactMapBasedCodecs.deriveEncoder[SchemeLetStar] diff --git a/code/shared/src/main/scala/maf/save/save/Store.scala b/code/shared/src/main/scala/maf/save/save/Store.scala index 32fabd8f4..de8e2ae51 100644 --- a/code/shared/src/main/scala/maf/save/save/Store.scala +++ b/code/shared/src/main/scala/maf/save/save/Store.scala @@ -16,7 +16,6 @@ import maf.lattice.AbstractType import maf.lattice.AbstractSetType import io.bullet.borer.LowPrioEncoders import maf.core.Address -import EncapsulatedEncoder.* import maf.core.Environment import maf.lattice.{ConcreteLattice, ConstantPropagation} import maf.lattice.Concrete @@ -33,24 +32,6 @@ import maf.save.save.SaveExpressions * The type of expression used in the analysis */ trait SaveValue[Expr <: Expression] extends Save[Expr] with AbstractDomain[Expr]: - /** - * Get the encoder that will be used to encode values. - * - * This will influence how values will be encoded, this can be e.g. a [[maf.save.MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] - * encoder. - */ - def getValueEncoder: AbstractEncoder = getEncoder - - /** - * Get the encoder that will be used to encode values. - * - * This encoder is used to encode objects where the key is important, when you e.g. encode a type in the key, some encoders might remove this key, - * and should therefore not be used here. - * - * This will influence how values will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] encoder. - */ - def getValueKeyEncoder: AbstractEncoder = getKeyEncoder - /** Encodes a value */ given valueEncoder: Encoder[Value] @@ -61,39 +42,20 @@ trait SaveValue[Expr <: Expression] extends Save[Expr] with AbstractDomain[Expr] * The type of expression used in the analysis */ trait SaveLattice[Expr <: Expression] extends Save[Expr]: - /** - * Get the encoder that will be used to encode lattices. - * - * This will influence how lattices will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] - * encoder. - */ - def getLatticeEncoder: AbstractEncoder = getEncoder - - /** - * Get the encoder that will be used to encode lattices. - * - * This encoder is used to encode objects where the key is important, when you e.g. encode a type in the key, some encoders might remove this key, - * and should therefore not be used here. - * - * This will influence how lattices will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] - * encoder. - */ - def getLatticeKeyEncoder: AbstractEncoder = getKeyEncoder - /** * The types of lattices that can be encoded by this trait. * * This is used to specify the givens, if this was not used, this given could be used for every class with a single abstract type. */ type Lattice[T] = ConstantPropagation.L[T] | Concrete.L[T] - given latticeEncoder[P[T] <: Lattice[T], T: Encoder]: EncapsulatedEncoder[P[T]] with - override val encoder: AbstractEncoder = getLatticeKeyEncoder - override protected def writeEncapsulated(writer: Writer, lattice: P[T]): Writer = + given latticeEncoder[P[T] <: Lattice[T], T: Encoder]: MapEncoder[P[T]] with + override def write(writer: Writer, lattice: P[T]): Writer = + writer.start() lattice match case constant: ConstantPropagation.L[T] => - writer.writeMember("constant", constant)(using constantLatticeEncoder, encoder) + writer.writeMember("constant", constant)(using constantLatticeEncoder) case _ => System.err.nn.println("The lattice of type `" + lattice.getClass + "` could not be encoded") - writer + writer.close() /** Encodes [[ConstantPropagation.L constant lattices]]. */ given constantLatticeEncoder[T: Encoder]: Encoder[ConstantPropagation.L[T]] with @@ -105,51 +67,59 @@ trait SaveLattice[Expr <: Expression] extends Save[Expr]: writer /** - * Trait to encode [[ModularSchemeLattice modular scheme lattices]]. + * Trait for encoding values as [[ModularSchemeLattice modular scheme lattices]], as defined in [[ModularSchemeDomain]]. * - * Implementation of [[SaveModularDomain]] + * Implementation of [[SaveValue]]. */ -trait SaveModularSchemeLattices - extends SaveModularDomain +trait SaveModularDomain + extends SaveValue[SchemeExp] + with ModularSchemeDomain with SaveAddr[SchemeExp] with SaveExpressions[SchemeExp] with SaveEnvironment[SchemeExp] with SaveLattice[SchemeExp]: /** Generic modular scheme lattice that is used for typechecking of nested class inside of this. */ type SchemeLattice = ModularSchemeLattice[?, ?, ?, ?, ?, ?, ?] - - given EncapsulatedArrayEncoder[SchemeLattice#Clo]() with - override val encoder = getValueEncoder - override protected def writeEncapsulated(writer: Writer, closure: SchemeLattice#Clo): Writer = + override given valueEncoder: ArrayEncoder[HMap] with + override def write(writer: Writer, hmap: HMap): Writer = + writer.start() + hmap.contents.foreach((key, value) => writer.writeMember((key, value))) + writer.close() + + given MapEncoder[SchemeLattice#Clo]() with + override def write(writer: Writer, closure: SchemeLattice#Clo): Writer = + writer.writeArrayOpen(closure.closures.size) closure.closures.foreach((clo) => - encoder.openEncapsulation(writer) + writer.start() writer.writeMember("expression", clo._1.asInstanceOf[SchemeExp]) writer.writeMember("address", clo._2.asInstanceOf[Environment[Address]]) - encoder.closeEncapsulation(writer) + writer.close() ) - writer + writer.writeArrayClose() - given EncapsulatedArrayEncoder[SchemeLattice#Pointer]() with - override val encoder = getValueEncoder - override protected def writeEncapsulated(writer: Writer, pointer: SchemeLattice#Pointer): Writer = + given ArrayEncoder[SchemeLattice#Pointer]() with + override def write(writer: Writer, pointer: SchemeLattice#Pointer): Writer = + writer.start() pointer.ptrs.foreach(writer.write(_)) - writer + writer.close() - given EncapsulatedEncoder[SchemeLattice#Cons]() with - override val encoder = getValueEncoder - override protected def writeEncapsulated(writer: Writer, cons: SchemeLattice#Cons): Writer = + given MapEncoder[SchemeLattice#Cons]() with + override def write(writer: Writer, cons: SchemeLattice#Cons): Writer = + writer.start() writer.writeMember("car", cons.car) writer.writeMember("cdr", cons.cdr) + writer.close() - given EncapsulatedEncoder[SchemeLattice#Vec]() with SaveMapToArray with - override val encoder = getValueEncoder - override protected def writeEncapsulated(writer: Writer, vec: SchemeLattice#Vec): Writer = + given MapEncoder[SchemeLattice#Vec]() with SaveMapToArray with + override def write(writer: Writer, vec: SchemeLattice#Vec): Writer = + writer.start() writer.writeMember("size", vec.size.asInstanceOf[Lattice[BigInt]]) writer.writeMember("elements", vec.elements.asInstanceOf[Map[Lattice[BigInt], SchemeLattice#L]]) + writer.close() - given EncapsulatedEncoder[(HMapKey, SchemeLattice#Value)] with - override val encoder = getValueKeyEncoder - override protected def writeEncapsulated(writer: Writer, hMapPair: (HMapKey, SchemeLattice#Value)): Writer = + given MapEncoder[(HMapKey, Any)] with + override def write(writer: Writer, hMapPair: (HMapKey, Any)): Writer = + writer.start() val (key, value) = hMapPair value match { @@ -168,69 +138,7 @@ trait SaveModularSchemeLattices System.err.nn.println("The lattice with type `" + key.getClass + "` could not be encoded") writer.writeMember("ERROR", "Unknown type: " + key.getClass.toString()) } - - override def encodeHMapPair(writer: Writer, key: HMapKey, value: Any)(using encoder: AbstractEncoder): Writer = - if value.isInstanceOf[SchemeLattice#Value] then writer.writeMember((key, value.asInstanceOf[SchemeLattice#Value])) - else return super.encodeHMapPair(writer, key, value) - -/** - * Base trait for encoding values as [[ModularSchemeLattice modular scheme lattices]], as defined in [[ModularSchemeDomain]]. - * - * Implementation of [[SaveValue]]. - * - * @note - * This trait gives the methods needed to encode values, but not the implementation. Other traits like [[SaveModularSchemeLattices]] should be mixed - * in. The exact trait that is mixed in depends on the values that you are using in your analysis. - */ -trait SaveModularDomain extends SaveValue[SchemeExp] with ModularSchemeDomain: - /** - * Get the encoder that will be used to encode an hMap. - * - * This will influence how an hMap will be encoded, this can be e.g. a [[MapEncoder map-based]] encoder or an [[ArrayEncoder array-based]] - * encoder. - * - * @note - * This is an [[ArrayEncoder array encoder]] by default - */ - def getHMapEncoder: AbstractEncoder = new ArrayEncoder - - /** - * Encodes an hMap pair. - * - * This method allows for expanding the hMap pairs that can be encoded by overriding it, and allowing you to add new hMap pairs by simply mixin in - * another trait that overrides this method. If you want to add a new encodable hMap pair you can override this method like this: - * {{{ - * override def encodeHMapPair(writer: Writer, key: HMapKey, value: Any)(using AbstractEncoder): Writer = - * address match { - * case < ... >(...) => < encode hMap pair > - * case _ => super.encodeHMapPair(writer, key, value) - * } - * }}} - * This is just an example and the actual implementation can also be done differently. - * - * @note - * This method should not be called directly, but should instead only be called from an encoder. - * - * @param writer - * The writer to write to - * @param key - * The key used in the hMap - * @param value - * The value that is associated to the key - * @param encoder - * Implicit argument that encodes the hMap pair - * @return - * The used writer - */ - def encodeHMapPair(writer: Writer, key: HMapKey, value: Any)(using encoder: AbstractEncoder): Writer = - System.err.nn.println("The lattice with type `" + key.getClass + "` could not be encoded") - writer - - override given valueEncoder: EncapsulatedEncoder[HMap] with - override val encoder = getHMapEncoder - override def writeEncapsulated(writer: Writer, hmap: HMap): Writer = - hmap.contents.foreach((key, value) => encodeHMapPair(writer, key, value)) - writer + writer.close() /** * Trait to encode the global store. diff --git a/code/shared/src/main/scala/maf/save/save/Util.scala b/code/shared/src/main/scala/maf/save/save/Util.scala index 4c2d39606..adc2b1e52 100644 --- a/code/shared/src/main/scala/maf/save/save/Util.scala +++ b/code/shared/src/main/scala/maf/save/save/Util.scala @@ -2,7 +2,6 @@ package maf.save import io.bullet.borer.Encoder import io.bullet.borer.Writer -import ArrayKeyEncoder.* /** * Trait to encode a map using an array. @@ -35,8 +34,8 @@ trait SaveMapToArray: * }}} * This can, for example be used if the key is not a string, and can therefore not be used as a key of a JSON map. */ - given mapKeyEncoder[K, V](using keyEncoder: Encoder[K], valueEncoder: Encoder[V]): EncapsulatedEncoder[Map[K, V]] with - override val encoder: ArrayKeyEncoder = new ArrayKeyEncoder - override def writeEncapsulated(writer: Writer, map: Map[K, V]): Writer = - for (key, value) <- map do writer.writeMember(key, value)(using keyEncoder, valueEncoder, encoder) - writer + given mapKeyEncoder[K, V](using keyEncoder: Encoder[K], valueEncoder: Encoder[V]): ArrayKeyEncoder[Map[K, V]] with + override def write(writer: Writer, map: Map[K, V]): Writer = + writer.start() + for (key, value) <- map do writer.writeMember(key, value)(using keyEncoder, valueEncoder) + writer.close() From ef0d2da1d9a7d9a6b415586ce153388f422cff75 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Fri, 19 Apr 2024 18:30:50 +0200 Subject: [PATCH 67/97] refactor(Load): Use actual decoder type instead of abstract decoder You now just say that you want to use a `MapDecoder` or `ArrayDecoder` instead of abstracting this away, this is to reduce unnecessary complexity of the program. --- .../main/scala/maf/save/load/Analysis.scala | 33 +-- .../main/scala/maf/save/load/Component.scala | 129 +++------ .../{Encapsulated.scala => Decoder.scala} | 269 +++++------------- .../main/scala/maf/save/load/Dependency.scala | 79 ++--- .../main/scala/maf/save/load/Expression.scala | 72 ++--- .../src/main/scala/maf/save/load/Store.scala | 129 +++------ .../src/main/scala/maf/save/load/Util.scala | 10 +- 7 files changed, 226 insertions(+), 495 deletions(-) rename code/shared/src/main/scala/maf/save/load/{Encapsulated.scala => Decoder.scala} (73%) diff --git a/code/shared/src/main/scala/maf/save/load/Analysis.scala b/code/shared/src/main/scala/maf/save/load/Analysis.scala index 7be575a67..1e3201d40 100644 --- a/code/shared/src/main/scala/maf/save/load/Analysis.scala +++ b/code/shared/src/main/scala/maf/save/load/Analysis.scala @@ -7,7 +7,6 @@ import java.nio.file.Paths import java.nio.file.Files import maf.language.scheme.SchemeExp import io.bullet.borer.Reader -import maf.save.EncapsulatedDecoder.* import scala.collection.mutable.HashMap import maf.modular.AnalysisEntry import maf.modular.ModAnalysis @@ -34,25 +33,6 @@ case class Loadable[T](val load: (T) => Unit)(using val decoder: Decoder[T]) * The type of expression used in the analysis */ trait Load[Expr <: Expression] extends AnalysisEntry[Expr]: - /** - * Get the decoder that will be used to decode your analysis. - * - * This will influence how the analysis will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] - * decoder. - */ - def getDecoder: AbstractDecoder - - /** - * Get the decoder that will be used to decode your analysis. - * - * This decoder is used to decode objects where the key is important, when you want to e.g. decode a type from the key, some decoders might ignore - * this key, and should therefore not be used here. - * - * This will influence how the analysis will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] - * decoder. - */ - def getKeyDecoder: AbstractDecoder - /** Decode an analysis. */ given analysisDecoder: Decoder[Load[Expr]] with override def read(reader: Reader): Load[Expr] = @@ -63,17 +43,18 @@ trait Load[Expr <: Expression] extends AnalysisEntry[Expr]: return Load.this private var load = Set[String]() - private given excludedAnalysisDecoder: EncapsulatedDecoder[Load[Expr]] with - override val decoder: AbstractDecoder = Load.this.getDecoder - override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Load[Expr] = + private given excludedAnalysisDecoder: MapDecoder[Load[Expr]] with + override def read(reader: Reader): Load[Expr] = + reader.start() val loaded = HashMap[String, Boolean]() for (key, value) <- loadInfo do if load.contains(key) then - val result = reader.readMember(key)(using value.decoder, decoder) + val result = reader.readMember(key)(using value.decoder) if result.hasValue then value.load(result.value) loaded.addOne((key, true)) for (key, value) <- loadInfo do if load.contains(key) && !loaded.contains(key) then value.load(reader.getMember(key)) + reader.close() return Load.this override def load(filename: String): Unit = @@ -115,6 +96,4 @@ trait LoadModF with LoadDependency[SchemeExp] with LoadAddrDependency[SchemeExp] with LoadGlobalStore[SchemeExp] - with LoadModularSchemeLattices: - def getDecoder: AbstractDecoder = new MapDecoder - def getKeyDecoder: AbstractDecoder = new MapDecoder + with LoadModularSchemeLattices diff --git a/code/shared/src/main/scala/maf/save/load/Component.scala b/code/shared/src/main/scala/maf/save/load/Component.scala index a7c46eba2..d3caeaf69 100644 --- a/code/shared/src/main/scala/maf/save/load/Component.scala +++ b/code/shared/src/main/scala/maf/save/load/Component.scala @@ -29,7 +29,6 @@ import maf.modular.scv.ScvContextSensitivity import maf.modular.scheme.modf.NoContext import io.bullet.borer.Decoder import io.bullet.borer.Reader -import maf.save.EncapsulatedDecoder.* import maf.core.Position import maf.core.Position.PTag import scala.collection.mutable.HashMap @@ -45,6 +44,7 @@ import maf.modular.worklist.FIFOWorklistAlgorithm import scala.collection.immutable.Queue import maf.core.worklist.FIFOWorkList import maf.modular.ModAnalysis +import io.bullet.borer.derivation.CompactMapBasedCodecs /** * The base trait for decoding components. @@ -58,25 +58,6 @@ import maf.modular.ModAnalysis * The type of expression used in the analysis */ trait LoadComponents[Expr <: Expression] extends ModAnalysis[Expr] with Load[Expr]: - /** - * Get the decoder that will be used to decode components. - * - * This will influence how components will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] - * decoder. - */ - def getComponentDecoder: AbstractDecoder = getDecoder - - /** - * Get the decoder that will be used to decode components. - * - * This decoder is used to decode objects where the key is important, when you want to e.g. decode a type from the key, some decoders might ignore - * this key, and should therefore not be used here. - * - * This will influence how components will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] - * decoder. - */ - def getComponentKeyDecoder: AbstractDecoder = getKeyDecoder - given componentDecoder: Decoder[Component] /** @@ -124,12 +105,13 @@ trait LoadStandardSchemeComponents if reader.tryReadString("main") then return initialComponent else reader.read[SchemeModFComponent.Call[DecodeContext]]() - given EncapsulatedDecoder[SchemeModFComponent.Call[DecodeContext]] with - override val decoder = getComponentDecoder - override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): SchemeModFComponent.Call[DecodeContext] = + given MapDecoder[SchemeModFComponent.Call[DecodeContext]] with + override def read(reader: Reader): SchemeModFComponent.Call[DecodeContext] = + reader.start() val lambda = reader.readMember[SchemeExp]("lambda").asInstanceOf[ReadValue[String, SchemeLambdaExp]] val environment = reader.readMember[Environment[Address]]("environment") val context = reader.readMember[DecodeContext]("context") + reader.close() return new SchemeModFComponent.Call[DecodeContext]((lambda.value, environment.value), context.value) /** @@ -146,14 +128,6 @@ trait LoadContext[Expr <: Expression] extends Load[Expr]: /** The type of context that should be decoded. */ type DecodeContext - /** - * Get the decoder that will be used to decode context. - * - * This will influence how context will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] - * decoder. - */ - def getContextDecoder: AbstractDecoder = getDecoder - /** * Get the decoder that will be used to decode context. * @@ -178,7 +152,7 @@ trait LoadNoContext[Expr <: Expression] extends LoadContext[Expr]: override given contextDecoder: Decoder[DecodeContext] with override def read(reader: Reader): DecodeContext = if !reader.tryReadString("ε") then return reader.unexpectedDataItem("ε") - NoContext + return NoContext /** * Trait to decode positions. @@ -187,19 +161,11 @@ trait LoadNoContext[Expr <: Expression] extends LoadContext[Expr]: * The type of expression used in the analysis */ trait LoadPosition[Expr <: Expression] extends Load[Expr]: - /** - * Get the decoder that will be used to decode positions. - * - * This will influence how positions will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] - * decoder. - */ - def getPositionDecoder: AbstractDecoder = getDecoder - given Decoder[Position] = AbstractDecoder.deriveAllDecoders[Position](getPositionDecoder) - given Decoder[PTag] = AbstractDecoder.deriveAllDecoders[PTag](getPositionDecoder) + given Decoder[Position] = CompactMapBasedCodecs.deriveAllDecoders[Position] + given Decoder[PTag] = CompactMapBasedCodecs.deriveAllDecoders[PTag] - private val posDecoder = getPositionDecoder - given Decoder[Identifier] = AbstractDecoder.deriveDecoder[Identifier](posDecoder) - given Decoder[Identity] = AbstractDecoder.deriveAllDecoders[Identity](posDecoder) + given Decoder[Identifier] = CompactMapBasedCodecs.deriveDecoder[Identifier] + given Decoder[Identity] = CompactMapBasedCodecs.deriveAllDecoders[Identity] given Decoder[IdentityData] with private object IdnData extends IdentityData { // TODO: @@ -218,42 +184,27 @@ trait LoadPosition[Expr <: Expression] extends Load[Expr]: * The type of expression used in the analysis */ trait LoadEnvironment[Expr <: Expression] extends Load[Expr] with LoadAddr[Expr]: - /** - * Get the decoder that will be used to decode environments. - * - * This will influence how environments will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] - * decoder. - */ - def getEnvironmentDecoder: AbstractDecoder = getDecoder - - /** - * Get the decoder that will be used to decode environments. - * - * This decoder is used to decode objects where the key is important, when you want to e.g. decode a type from the key, some decoders might ignore - * this key, and should therefore not be used here. - * - * This will influence how environments will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] - * decoder. - */ - def getEnvironmentKeyDecoder: AbstractDecoder = getKeyDecoder - given [T <: Address]: EncapsulatedDecoder[Environment[T]] with - override def decoder: AbstractDecoder = getEnvironmentKeyDecoder - override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Environment[T] = - return reader + given [T <: Address]: MapDecoder[Environment[T]] with + override def read(reader: Reader): Environment[T] = + reader.start() + val value = reader .readMembers[Environment[T]]( Array(("basicEnvironment", summon[Decoder[BasicEnvironment[T]]]), ("nestedEnv", summon[Decoder[NestedEnv[T, T]]])) ) .value + reader.close() + return value given [T <: Address]: Decoder[BasicEnvironment[T]] with override def read(reader: Reader): BasicEnvironment[T] = return new BasicEnvironment( reader.read[Map[String, Address]]().asInstanceOf[Map[String, T]] ) - given [T <: Address, K <: Address]: EncapsulatedDecoder[NestedEnv[T, K]] with - override def decoder: AbstractDecoder = getEnvironmentDecoder - override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): NestedEnv[T, K] = + given [T <: Address, K <: Address]: MapDecoder[NestedEnv[T, K]] with + override def read(reader: Reader): NestedEnv[T, K] = + reader.start() val content = reader.readMember[Map[String, Address]]("content") val rst = reader.readMember[Address]("rst") + reader.close() return new NestedEnv(content.value.asInstanceOf[Map[String, T]], if rst.hasValue then Some(rst.value.asInstanceOf[K]) else None) /** @@ -309,10 +260,13 @@ trait LoadStandardSchemeComponentPosition extends LoadComponentID[SchemeExp] wit if component != initialComponent then components.addOne(component.asInstanceOf[SchemeModFComponent.Call[DecodeContext]].clo._1.idn.pos, component) - override protected given componentSetDecoder: EncapsulatedDecoder[Set[Component]] with - override def decoder: AbstractDecoder = getComponentDecoder - override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Set[Component] = - return reader.readUntilBeforeBreak(Set[Component](), (components: Set[Component]) => components + reader.readMember[Component]().value) + override protected given componentSetDecoder: MapDecoder[Set[Component]] with + override def read(reader: Reader): Set[Component] = + reader.start() + val components = + reader.readUntilBeforeBreak(Set[Component](), (components: Set[Component]) => components + reader.readMember[Component]().value) + reader.close() + return components override given componentIDDecoder: Decoder[Component] with override def read(reader: Reader): Component = @@ -344,26 +298,21 @@ trait LoadComponentIntID[Expr <: Expression] extends LoadComponentID[Expr]: return components(id) else return reader.read[Component]()(using actualComponentDecoder) - override protected given componentSetDecoder: EncapsulatedDecoder[Set[Component]] with - override def decoder: AbstractDecoder = getComponentKeyDecoder - override protected def readEncapsulated(reader: Reader)(using decoder: AbstractDecoder): Set[Component] = - return reader.readUntilBeforeBreak( + override protected given componentSetDecoder: MapDecoder[Set[Component]] with + override def read(reader: Reader): Set[Component] = + reader.start() + val components = reader.readUntilBeforeBreak( Set[Component](), (components: Set[Component]) => - val component = reader.readMember[Component]()(using componentDecoder, decoder) + val component = reader.readMember[Component]()(using componentDecoder) val key = component.key.toInt LoadComponentIntID.this.components.addOne((key, component.value)) components + (component.value) ) + reader.close() + return components trait LoadWorklist[Expr <: Expression] extends Load[Expr]: - /** - * Get the decoder that will be used to decode the worklist. - * - * This will influence how the worklist will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] - * decoder. - */ - def getWorklistDecoder: AbstractDecoder = new ArrayDecoder type WorklistComponent given worklistDecoder: Decoder[List[WorklistComponent]] def setWorklist(worklist: List[WorklistComponent]): Unit @@ -372,10 +321,12 @@ trait LoadWorklist[Expr <: Expression] extends Load[Expr]: trait LoadSequentialWorklist[Expr <: Expression] extends SequentialWorklistAlgorithm[Expr] with LoadWorklist[Expr] with LoadComponents[Expr]: type WorklistComponent = Component - given worklistDecoder: EncapsulatedDecoder[List[WorklistComponent]] with - override def decoder: AbstractDecoder = getWorklistDecoder - override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): List[WorklistComponent] = - reader.readUntilBeforeBreak(List[Component](), (lst: List[Component]) => lst ++ List(reader.read[Component]())) + given worklistDecoder: ArrayDecoder[List[WorklistComponent]] with + override def read(reader: Reader): List[WorklistComponent] = + reader.start() + val worklistComponents = reader.readUntilBeforeBreak(List[Component](), (lst: List[Component]) => lst ++ List(reader.read[Component]())) + reader.close() + return worklistComponents trait LoadFIFOWorklist[Expr <: Expression] extends LoadSequentialWorklist[Expr] with FIFOWorklistAlgorithm[Expr]: def setWorklist(worklist: List[WorklistComponent]): Unit = diff --git a/code/shared/src/main/scala/maf/save/load/Encapsulated.scala b/code/shared/src/main/scala/maf/save/load/Decoder.scala similarity index 73% rename from code/shared/src/main/scala/maf/save/load/Encapsulated.scala rename to code/shared/src/main/scala/maf/save/load/Decoder.scala index 0ff11173e..f69ce5cd8 100644 --- a/code/shared/src/main/scala/maf/save/load/Encapsulated.scala +++ b/code/shared/src/main/scala/maf/save/load/Decoder.scala @@ -130,7 +130,7 @@ class ReadValue[K, V](private val forceRead: (key: Option[K]) => Unit): * Some decoders like [[ArrayDecoder]] will also not decode your key, because arrays do not require keys like maps do, if the key does need to be * stored, you should use an decoder that writes them down like [[MapDecoder]] or [[ArrayKeyDecoder]] if you want to use an array. */ -trait AbstractDecoder: +trait AbstractDecoder[T] extends Decoder[T]: /** The values that have already been decoded, with their key. */ protected val values = new HashMap[String, Any]() @@ -141,9 +141,6 @@ trait AbstractDecoder: */ protected var currentKey: Option[String] = None - /** Does this decoder use maps or array to decode the data. */ - protected val mapBasedDecoder: Boolean - /** * Read a key from the given reader. * @@ -268,7 +265,7 @@ trait AbstractDecoder: * @tparam T * The type of the object that was encoded inside of the array/map */ - def closeEncapsulation[T](reader: Reader, unbounded: Boolean, res: T): Unit + def closeEncapsulation[T](reader: Reader): Unit /** * Read a key-value pair. @@ -290,155 +287,9 @@ trait AbstractDecoder: */ def forceReadValue(reader: Reader): Unit -object AbstractDecoder: - /** - * Automatically derive an decoder for type T. - * - * This will derive an decoder for type T, this decoder will either be [[CompactMapBasedCodecs map-based]] or [[ArrayBasedCodecs array-based]] - * based on the type of the provided decoder. - * - * @note - * T must be a case class, enum, sealed abstract class or sealed trait - * - * @param decoder - * The type of decoder that should be used to decode T - * @tparam T - * The type to derive decoders for - */ - inline def deriveDecoder[T](decoder: AbstractDecoder): Decoder[T] = - if decoder.mapBasedDecoder then CompactMapBasedCodecs.deriveDecoder[T] else ArrayBasedCodecs.deriveDecoder[T] - - /** - * Automatically derive all decoders for type T. - * - * This will derive an decoder for the type T and all subtypes of type T, these decoders will either be [[CompactMapBasedCodecs map-based]] or - * [[ArrayBasedCodecs array-based]] based on the type of the provided decoder. - * - * @note - * T must be a case object, case class, sealed trait or sealed abstract class. - * - * @param decoder - * The type of decoder that should be used to decode T - * @tparam T - * The type to derive decoders for - */ - inline def deriveAllDecoders[T](decoder: AbstractDecoder): Decoder[T] = - if decoder.mapBasedDecoder then CompactMapBasedCodecs.deriveAllDecoders[T] else ArrayBasedCodecs.deriveAllDecoders[T] - def forceRead(reader: Reader, decoder: AbstractDecoder, key: Option[String]) = - if key.isDefined then decoder.forceReadValue(reader, key.get) - else decoder.forceReadValue(reader) - -/** - * Trait used to decode an instance of type T. - * - * This trait will read a value of type T encapsulated in either an array or a map, based on the decoder that is given. In order to implement this - * class, you should overwrite the [[decoder]] variable to decide on the type of decoder you want to use, and the [[readEncapsulated]] method which - * will implement the actual reading of the value. - * - * {{{ - * given EncapsulatedDecoder[< T >] with - * override def decoder: AbstractDecoder = < getDecoder > - * override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): T = < decode value > - * }}} - * - * @note - * Since this class already opens a map/array, you should not do this anymore unless you are reading a nested map/array. - * - * @example - * If you implement it as a given, you can use it implicitly in your code - * {{{ - * override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): T = - * ... - * // Will use the implicit Decoder[T] - * reader.readMember[T]("< key >", < value >) - * ... - * }}} - * - * @tparam T - * The type to decode - */ -trait EncapsulatedDecoder[T] extends Decoder[T]: - /** The decoder used to read this value, this will specify how this value will be written (e.g. in a map or in an array). */ - def decoder: AbstractDecoder - - /** - * The decoder used to read this value, this will specify how this value will be written (e.g. in a map or in an array). - * - * This is an given because a lot of method require this decoder to be added implicitly, adding the implicit here, makes it that you don't have to - * specify this anymore. - */ - protected given AbstractDecoder = decoder - override def read(reader: Reader): T = - decoder.openEncapsulation(reader) - val res = readEncapsulated(reader)(using decoder) - // Read all remaining elements, since these where not read they will be ignored - EncapsulatedDecoder.readUntilBeforeBreak(reader)(None, - (none: None.type) => - reader.skipElement() - None - ) - decoder.closeEncapsulation(reader, true, res) - res - - /** - * Read a value encapsulated in a map/array based on the [[decoder]]. - * - * @note - * This should not be called directly, but only through the [[read]] method. - * - * @param reader - * The reader used to read the value - * @param decoder - * Implicit argument used to decode the value - * @returns - * The returned value - */ - protected def readEncapsulated(reader: Reader)(using AbstractDecoder): T - -/** - * Trait used to decode an instance of type T. - * - * This trait will read a value of type T encapsulated in an array. In order to implement this class, you should overwrite the [[decoder]] variable to - * decide on the type of decoder you want to use, and the [[readEncapsulated]] method which will implement the actual reading of the value. - * - * {{{ - * given EncapsulatedDecoder[< T >] with - * override def decoder: AbstractDecoder = < getDecoder > - * override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): T = < decode value > - * }}} - * - * @note - * Since this class already opens an array, you should not do this anymore unless you are reading a nested map/array. - * - * @note - * Using `reader.readMember`, `reader.openEncapsulation`, ... can still use either a map or an array, it is only the top-level encapsulation that is - * an array. - * - * @example - * If you implement it as a given, you can use it implicitly in your code - * {{{ - * override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): T = - * ... - * // Will use the implicit Decoder[T] - * reader.readMember[T]("< key >", < value >) - * ... - * }}} - * - * @tparam T - * The type to decode - */ -trait EncapsulatedArrayDecoder[T](length: Int = 0) extends EncapsulatedDecoder[T]: - override def read(reader: Reader): T = - if length == 0 then reader.readArrayStart() else reader.readArrayOpen(length) - val res = readEncapsulated(reader)(using decoder) - reader.readArrayClose(true, res) - -/** - * Object with extension methods for [[borer.Reader]]. - * - * These extension methods should be used when using an [[EncapsulatedDecoder]]. - */ -object EncapsulatedDecoder: + def forceRead(reader: Reader, key: Option[String]) = + if key.isDefined then forceReadValue(reader, key.get) + else forceReadValue(reader) extension (reader: Reader) /** * Read a key-value pair. @@ -456,9 +307,9 @@ object EncapsulatedDecoder: * The key-value pair that is read, if the key cannot be read yet because it appears later in the file, the value will be empty and will be * filled in later. */ - def readMember[T: Decoder](key: String)(using decoder: AbstractDecoder): ReadValue[String, T] = - decoder.readKey(reader, key) - decoder.readValue[T](reader) + def readMember[T: Decoder](key: String): ReadValue[String, T] = + readKey(reader, key) + readValue[T](reader) /** * Read a key-value pair. @@ -475,9 +326,9 @@ object EncapsulatedDecoder: * @returns * The key-value pair that is read. */ - def readMember[T: Decoder]()(using decoder: AbstractDecoder): ReadValue[String, T] = - decoder.readKey(reader) - decoder.readValue[T](reader) + def readMember[T: Decoder](): ReadValue[String, T] = + readKey(reader) + readValue[T](reader) /** * Read one of the given key-value pairs. @@ -498,11 +349,11 @@ object EncapsulatedDecoder: * The key-value pair that is read, if none of the keys can be read because it appears later in the file, the key and the value will be * empty and will be filled in later. */ - def readMembers[T](keys: Array[(String, Decoder[_ <: T])])(using decoder: AbstractDecoder): ReadValue[String, T] = - val res = new ReadValue[String, T](AbstractDecoder.forceRead(reader, decoder, _)) + def readMembers[T](keys: Array[(String, Decoder[_ <: T])]): ReadValue[String, T] = + val res = new ReadValue[String, T](forceRead(reader, _)) for (key, valueDecoder) <- keys do - decoder.readKey(reader, key) - val value = decoder.readValue[T](reader)(using valueDecoder.asInstanceOf[Decoder[T]]) + readKey(reader, key) + val value = readValue[T](reader)(using valueDecoder.asInstanceOf[Decoder[T]]) if value.hasValue then return value value.updateValue = res return res @@ -534,16 +385,41 @@ object EncapsulatedDecoder: * @throws NoSuchElementException * If this key hasn't been read yet, and therefore doesn't have a value. */ - def getMember[T](key: String)(using decoder: AbstractDecoder): T = - if !decoder.hasValue(key) then decoder.forceReadValue(reader, key) - decoder.getValue[T](key) + def getMember[T](key: String): T = + if !hasValue(key) then forceReadValue(reader, key) + getValue[T](key) + + /** [TODO: description] */ + def start(): Unit = + openEncapsulation(reader) + + /** [TODO: description] */ + def close(): Unit = + readUntilBeforeBreak(None, + (none: None.type) => + forceReadValue(reader) + None + ) + closeEncapsulation(reader) /** * Decoder that uses maps to decode values. * * This decoder uses maps to decode values and therefore requires keys to be present. + * + * @example + * {{{ + * given MapDecoder[T] + * override protected def read(reader: Reader): T = + * reader.start() + * < decode value > + * reader.close() + * }}} + * + * @tparam T + * The type to encode */ -class MapDecoder extends AbstractDecoder: +trait MapDecoder[T] extends AbstractDecoder[T]: /** * Stores a key-value pair and a decoder that can be used to decode this value. * @@ -562,8 +438,6 @@ class MapDecoder extends AbstractDecoder: /** The key that was read, and which value should now be read. */ protected var decodeKey: Option[String] = None - override protected val mapBasedDecoder: Boolean = true - /** Read the next key for which the value should be decoded, if there isn't currently a value being decoded */ protected def readDecodeKey(reader: Reader): Unit = if decodeKey.isEmpty && reader.hasString then decodeKey = Some(reader.readString()) @@ -596,13 +470,13 @@ class MapDecoder extends AbstractDecoder: return valueDecoder.value else return new ReadValue[String, T](key, res) else - val readValue = new ReadValue[String, T](currentKey.get, AbstractDecoder.forceRead(reader, this, _)) + val readValue = new ReadValue[String, T](currentKey.get, forceRead(reader, _)) keys.addOne((currentKey.get, ValueDecoder(readValue, summon[Decoder[T]]))) currentKey = None return readValue override def openEncapsulation(reader: Reader): Unit = reader.readMapStart() override def openEncapsulation(reader: Reader, amount: Int): Unit = reader.readMapOpen(amount) - override def closeEncapsulation[T](reader: Reader, unbounded: Boolean, res: T): Unit = reader.readMapClose(unbounded, res) + override def closeEncapsulation[T](reader: Reader): Unit = reader.readBreak() override def forceReadValue(reader: Reader, key: String): Unit = while decodeKey.isDefined && !values.contains(key.asInstanceOf[String]) do forceReadValue(reader) override def forceReadValue(reader: Reader): Unit = @@ -623,12 +497,22 @@ class MapDecoder extends AbstractDecoder: * * This decoder uses arrays to decode values and does not require keys, if you want an array-based decoder that saves keys, you should use * [[ArrayKeyDecoder]]. + * + * @example + * {{{ + * given ArrayDecoder[T] + * override protected def read(reader: Reader): T = + * reader.start() + * < decode value > + * reader.close() + * }}} + * + * @tparam T + * The type to encode */ -class ArrayDecoder extends AbstractDecoder: +trait ArrayDecoder[T] extends AbstractDecoder[T]: /** Used to generate IDs if no key is provided, this is used to store the values. */ protected var id = -1 - - override protected val mapBasedDecoder: Boolean = false override def readKey(reader: Reader): String = return "" override def readKey(reader: Reader, key: String): String = currentKey = Some(key) @@ -643,8 +527,8 @@ class ArrayDecoder extends AbstractDecoder: return ReadValue[String, T](key, res) override def openEncapsulation(reader: Reader): Unit = reader.readArrayStart() override def openEncapsulation(reader: Reader, amount: Int): Unit = reader.readArrayOpen(amount) - override def closeEncapsulation[T](reader: Reader, unbounded: Boolean, res: T): Unit = reader.readMapClose(unbounded, res) - override def forceReadValue(reader: Reader, key: String): Unit = return + override def closeEncapsulation[T](reader: Reader): Unit = reader.readBreak() + override def forceReadValue(reader: Reader, key: String): Unit = reader.skipElement() override def forceReadValue(reader: Reader): Unit = reader.skipElement() /** @@ -664,10 +548,20 @@ class ArrayDecoder extends AbstractDecoder: * ... * ] * }}} + * + * @example + * {{{ + * given ArrayKeyDecoder[T] + * override protected def read(reader: Reader): T = + * reader.start() + * < decode value > + * reader.close() + * }}} + * + * @tparam T + * The type to encode */ -class ArrayKeyDecoder extends MapDecoder: - override protected val mapBasedDecoder: Boolean = false - +trait ArrayKeyDecoder[T] extends MapDecoder[T]: /** The key that was read, and which value should now be read. */ protected var key: Option[Any] = None protected val keyValues = new HashMap[Any, Any]() @@ -719,7 +613,7 @@ class ArrayKeyDecoder extends MapDecoder: return ReadValue(tmpKey.get.asInstanceOf[V], res) override def openEncapsulation(reader: Reader): Unit = reader.readArrayStart() override def openEncapsulation(reader: Reader, amount: Int): Unit = reader.readArrayOpen(amount) - override def closeEncapsulation[T](reader: Reader, unbounded: Boolean, res: T): Unit = reader.readMapClose(unbounded, res) + override def closeEncapsulation[T](reader: Reader): Unit = reader.readBreak() /** * Returns an already read value using a given key. @@ -740,13 +634,6 @@ class ArrayKeyDecoder extends MapDecoder: * The key to check if it has a value */ def hasValue(key: Any): Boolean = keyValues.contains(key) || (key.isInstanceOf[String] && super.hasValue(key.asInstanceOf[String])) - -/** - * Object with an extension method for [[borer.Reader]]. - * - * This extension method allows you to read a key-value pair where the key is not a String, and should only be used when using an [[ArrayKeyDecoder]]. - */ -object ArrayKeyDecoder: extension (reader: Reader) /** * Reads a key-value pair. @@ -760,6 +647,6 @@ object ArrayKeyDecoder: * @tparam U * The type of the value that should be read, this type should have an decoder */ - def readMember[K: Decoder, V: Decoder]()(using decoder: ArrayKeyDecoder): ReadValue[K, V] = - decoder.readKey[K](reader) - decoder.readKeyValue[K, V](reader) + def readMember[K: Decoder, V: Decoder](): ReadValue[K, V] = + readKey[K](reader) + readKeyValue[K, V](reader) diff --git a/code/shared/src/main/scala/maf/save/load/Dependency.scala b/code/shared/src/main/scala/maf/save/load/Dependency.scala index d22cdd51e..b6ce4063b 100644 --- a/code/shared/src/main/scala/maf/save/load/Dependency.scala +++ b/code/shared/src/main/scala/maf/save/load/Dependency.scala @@ -12,7 +12,6 @@ import maf.modular.scheme.PtrAddr import maf.language.scheme.SchemeValue import io.bullet.borer.Reader import io.bullet.borer.Decoder -import maf.save.EncapsulatedDecoder.* import maf.core.Identifier import maf.core.Identity @@ -42,34 +41,18 @@ trait LoadAddrDependency[Expr <: Expression] extends LoadDependency[Expr] with L * The type of expression used in the analysis */ trait LoadDependency[Expr <: Expression] extends LoadMapToArray with LoadComponents[Expr]: - /** - * Get the decoder that will be used to decode dependencies. - * - * This will influence how dependencies will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] - * decoder. - */ - def getDependencyDecoder: AbstractDecoder = getDecoder - - /** - * Get the decoder that will be used to decode dependencies. - * - * This decoder is used to decode objects where the key is important, when you want to e.g. decode a type from the key, some decoders might ignore - * this key, and should therefore not be used here. - * - * This will influence how dependencies will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] - * decoder. - */ - def getDependencyKeyDecoder: AbstractDecoder = getKeyDecoder override def loadInfo: List[(String, Loadable[_])] = super.loadInfo ++ List(("dependencies", Loadable((deps: Map[Dependency, Set[Component]]) => this.deps = deps))) /** Returns a map that links a key to a specific decoder. */ def dependencyDecoders = Set[(String, Decoder[_ <: Dependency])]() - given EncapsulatedDecoder[Dependency] with - override def decoder: AbstractDecoder = getDependencyDecoder - override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Dependency = - return reader.readMembers(dependencyDecoders.toArray).value + given MapDecoder[Dependency] with + override def read(reader: Reader): Dependency = + reader.start() + val dependency = reader.readMembers(dependencyDecoders.toArray).value + reader.close() + return dependency /** * The base trait for decoding addresses. @@ -82,32 +65,15 @@ trait LoadDependency[Expr <: Expression] extends LoadMapToArray with LoadCompone * The type of expression used in the analysis */ trait LoadAddr[Expr <: Expression] extends Load[Expr] with LoadPosition[Expr]: - /** - * Get the decoder that will be used to decode addresses. - * - * This will influence how addresses will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] - * decoder. - */ - def getAddressDecoder: AbstractDecoder = getDecoder - - /** - * Get the decoder that will be used to decode addresses. - * - * This decoder is used to decode objects where the key is important, when you want to e.g. decode a type from the key, some decoders might ignore - * this key, and should therefore not be used here. - * - * This will influence how addresses will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] - * decoder. - */ - def getAddressKeyDecoder: AbstractDecoder = getKeyDecoder - /** Returns a map that links a key to a specific decoder. */ def addressDecoders = List[(String, Decoder[_ <: Address])]() - given EncapsulatedDecoder[Address] with - override def decoder: AbstractDecoder = getAddressKeyDecoder - override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Address = - reader.readMembers(addressDecoders.toArray).value + given MapDecoder[Address] with + override def read(reader: Reader): Address = + reader.start() + val address = reader.readMembers(addressDecoders.toArray).value + reader.close() + return address /** * Trait to decode scheme addresses. @@ -123,18 +89,20 @@ trait LoadSchemeAddr extends LoadAddr[SchemeExp] with LoadContext[SchemeExp] wit ("ptrAddr", summon[Decoder[PtrAddr[DecodeContext]]]) ) - given EncapsulatedDecoder[ReturnAddr[Component]] with - override def decoder: AbstractDecoder = getAddressDecoder - override protected def readEncapsulated(reader: Reader)(using decoder: AbstractDecoder): ReturnAddr[Component] = + given MapDecoder[ReturnAddr[Component]] with + override def read(reader: Reader): ReturnAddr[Component] = + reader.start() val component = reader.readMember[Component]("component") val identity = reader.readMember[Identity]("identity") + reader.close() return new ReturnAddr[Component](component.value, identity.value) - given EncapsulatedDecoder[VarAddr[DecodeContext]] with - override def decoder: AbstractDecoder = getAddressDecoder - override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): VarAddr[DecodeContext] = + given MapDecoder[VarAddr[DecodeContext]] with + override def read(reader: Reader): VarAddr[DecodeContext] = + reader.start() val name = reader.readMember[Identifier]("id") val context = reader.readMember[DecodeContext]("context") + reader.close() return new VarAddr[DecodeContext]( name.value, if context.hasValue then Some(context.value).asInstanceOf[DecodeContext] else None.asInstanceOf[DecodeContext] @@ -143,11 +111,12 @@ trait LoadSchemeAddr extends LoadAddr[SchemeExp] with LoadContext[SchemeExp] wit given Decoder[PrmAddr] with override def read(reader: Reader): PrmAddr = new PrmAddr(reader.read[String]()) - given EncapsulatedDecoder[PtrAddr[DecodeContext]] with - override def decoder: AbstractDecoder = getAddressDecoder - override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): PtrAddr[DecodeContext] = + given MapDecoder[PtrAddr[DecodeContext]] with + override def read(reader: Reader): PtrAddr[DecodeContext] = + reader.start() val expression = reader.readMember[SchemeExp]("expression") val context = reader.readMember[DecodeContext]("context") + reader.close() return new PtrAddr[DecodeContext]( expression.value, if context.hasValue then Some(context.value).asInstanceOf[DecodeContext] else None.asInstanceOf[DecodeContext] diff --git a/code/shared/src/main/scala/maf/save/load/Expression.scala b/code/shared/src/main/scala/maf/save/load/Expression.scala index cbeefb5ea..dc8749cda 100644 --- a/code/shared/src/main/scala/maf/save/load/Expression.scala +++ b/code/shared/src/main/scala/maf/save/load/Expression.scala @@ -1,7 +1,5 @@ package maf.save -import maf.save.EncapsulatedDecoder.* - import maf.core.Expression import io.bullet.borer.Decoder import maf.language.scheme.SchemeExp @@ -20,6 +18,7 @@ import maf.language.scheme.SchemeLetStar import maf.language.scheme.SchemeAssert import io.bullet.borer.Reader import scala.collection.mutable.HashMap +import io.bullet.borer.derivation.CompactMapBasedCodecs /** * The base trait for decoding expressions. @@ -33,25 +32,6 @@ import scala.collection.mutable.HashMap * The type of expression used in the analysis */ trait LoadExpressions[Expr <: Expression] extends Load[Expr]: - /** - * Get the decoder that will be used to decode expressions. - * - * This will influence how expressions will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] - * decoder. - */ - def getExpressionDecoder: AbstractDecoder = getDecoder - - /** - * Get the decoder that will be used to decode expressions. - * - * This decoder is used to decode objects where the key is important, when you want to e.g. decode a type from the key, some decoders might ignore - * this key, and should therefore not be used here. - * - * This will influence how expressions will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] - * decoder. - */ - def getExpressionKeyDecoder: AbstractDecoder = getKeyDecoder - given expressionDecoder: Decoder[Expr] /** @@ -90,43 +70,44 @@ trait LoadExpressionID[Expr <: Expression] extends Load[Expr] with LoadActualExp trait LoadExpressionIntID[Expr <: Expression] extends LoadExpressionID[Expr]: private val expressions: HashMap[Int, Expr] = HashMap[Int, Expr]() - override protected given expressionSetDecoder: EncapsulatedDecoder[Set[Expr]] with - override def decoder: AbstractDecoder = getExpressionKeyDecoder - override protected def readEncapsulated(reader: Reader)(using decoder: AbstractDecoder): Set[Expr] = - return reader.readUntilBeforeBreak( + override protected given expressionSetDecoder: MapDecoder[Set[Expr]] with + override def read(reader: Reader): Set[Expr] = + reader.start() + val expressions = reader.readUntilBeforeBreak( Set[Expr](), (expressions: Set[Expr]) => - val expression = reader.readMember[Expr]()(using actualExpressionDecoder, decoder) + val expression = reader.readMember[Expr]()(using actualExpressionDecoder) val key = expression.key.toInt LoadExpressionIntID.this.expressions.addOne((key, expression.value)) expressions + (expression.value) ) + reader.close() + return expressions override protected given expressionIDDecoder: Decoder[Expr] with override def read(reader: Reader): Expr = if reader.hasInt then return expressions(reader.readInt()) else reader.read[Expr]()(using actualExpressionDecoder) trait LoadSchemeSubExpressions extends LoadExpressions[SchemeExp] with LoadPosition[SchemeExp]: - private val compDecoder = getExpressionDecoder - given Decoder[SchemeValue] = AbstractDecoder.deriveDecoder(compDecoder) - given Decoder[maf.language.sexp.Value] = AbstractDecoder.deriveAllDecoders(compDecoder) - given Decoder[SchemeFuncall] = AbstractDecoder.deriveDecoder[SchemeFuncall](compDecoder) - given Decoder[SchemeVar] = AbstractDecoder.deriveDecoder[SchemeVar](compDecoder) - given Decoder[SchemeLambda] = AbstractDecoder.deriveDecoder[SchemeLambda](compDecoder) - given Decoder[SchemeVarArgLambda] = AbstractDecoder.deriveDecoder[SchemeVarArgLambda](compDecoder) - given Decoder[SchemeLambdaExp] = AbstractDecoder.deriveDecoder[SchemeLambdaExp](compDecoder) - given Decoder[SchemeLetrec] = AbstractDecoder.deriveDecoder[SchemeLetrec](compDecoder) - given Decoder[SchemeAssert] = AbstractDecoder.deriveDecoder[SchemeAssert](compDecoder) - given Decoder[SchemeLet] = AbstractDecoder.deriveDecoder[SchemeLet](compDecoder) - given Decoder[SchemeIf] = AbstractDecoder.deriveDecoder[SchemeIf](compDecoder) - given Decoder[SchemeSet] = AbstractDecoder.deriveDecoder[SchemeSet](compDecoder) - given Decoder[SchemeBegin] = AbstractDecoder.deriveDecoder[SchemeBegin](compDecoder) - given Decoder[SchemeLetStar] = AbstractDecoder.deriveDecoder[SchemeLetStar](compDecoder) + given Decoder[SchemeValue] = CompactMapBasedCodecs.deriveDecoder + given Decoder[maf.language.sexp.Value] = CompactMapBasedCodecs.deriveAllDecoders + given Decoder[SchemeFuncall] = CompactMapBasedCodecs.deriveDecoder[SchemeFuncall] + given Decoder[SchemeVar] = CompactMapBasedCodecs.deriveDecoder[SchemeVar] + given Decoder[SchemeLambda] = CompactMapBasedCodecs.deriveDecoder[SchemeLambda] + given Decoder[SchemeVarArgLambda] = CompactMapBasedCodecs.deriveDecoder[SchemeVarArgLambda] + given Decoder[SchemeLambdaExp] = CompactMapBasedCodecs.deriveDecoder[SchemeLambdaExp] + given Decoder[SchemeLetrec] = CompactMapBasedCodecs.deriveDecoder[SchemeLetrec] + given Decoder[SchemeAssert] = CompactMapBasedCodecs.deriveDecoder[SchemeAssert] + given Decoder[SchemeLet] = CompactMapBasedCodecs.deriveDecoder[SchemeLet] + given Decoder[SchemeIf] = CompactMapBasedCodecs.deriveDecoder[SchemeIf] + given Decoder[SchemeSet] = CompactMapBasedCodecs.deriveDecoder[SchemeSet] + given Decoder[SchemeBegin] = CompactMapBasedCodecs.deriveDecoder[SchemeBegin] + given Decoder[SchemeLetStar] = CompactMapBasedCodecs.deriveDecoder[SchemeLetStar] trait LoadSchemeExpressions extends LoadActualExprs[SchemeExp] with LoadSchemeSubExpressions: - override protected given actualExpressionDecoder: EncapsulatedDecoder[SchemeExp] with - override val decoder = getExpressionKeyDecoder - override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): SchemeExp = + override protected given actualExpressionDecoder: MapDecoder[SchemeExp] with + override def read(reader: Reader): SchemeExp = + reader.start() val expression = reader.readMembers[SchemeExp]( Array( ("funcall", summon[Decoder[SchemeFuncall]]), @@ -143,4 +124,5 @@ trait LoadSchemeExpressions extends LoadActualExprs[SchemeExp] with LoadSchemeSu ("letStar", summon[Decoder[SchemeLetStar]]), ) ) - expression.value + reader.close() + return expression.value diff --git a/code/shared/src/main/scala/maf/save/load/Store.scala b/code/shared/src/main/scala/maf/save/load/Store.scala index dbf168b03..2dc69053a 100644 --- a/code/shared/src/main/scala/maf/save/load/Store.scala +++ b/code/shared/src/main/scala/maf/save/load/Store.scala @@ -9,7 +9,6 @@ import maf.language.scheme.SchemeExp import maf.lattice.HMapKey import maf.lattice.HMap import io.bullet.borer.Reader -import maf.save.EncapsulatedDecoder.* import maf.language.scheme.lattices.ModularSchemeLattice import maf.lattice.Concrete import maf.lattice.ConstantPropagation @@ -29,22 +28,6 @@ import maf.modular.scheme.modf.BaseSchemeModFSemanticsM * The type of expression used in the analysis */ trait LoadValue[Expr <: Expression] extends Load[Expr] with AbstractDomain[Expr]: - /** - * Get the decoder that will be used to decode values. - * - * This will influence how values will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] decoder. - */ - def getValueDecoder: AbstractDecoder = getDecoder - - /** - * Get the decoder that will be used to decode values. - * - * This decoder is used to decode objects where the key is important, when you want to e.g. decode a type from the key, some decoders might ignore - * this key, and should therefore not be used here. - * - * This will influence how values will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] decoder. - */ - def getValueKeyDecoder: AbstractDecoder = getKeyDecoder given valueDecoder: Decoder[Value] /* Trait to decode lattices. @@ -60,32 +43,15 @@ trait LoadLattice[Expr <: Expression] extends Load[Expr]: */ type Lattice[T] = ConstantPropagation.L[T] | Concrete.L[T] - /** - * Get the decoder that will be used to decode lattices. - * - * This will influence how lattices will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] - * decoder. - */ - def getLatticeDecoder: AbstractDecoder = getDecoder - - /** - * Get the decoder that will be used to decode lattices. - * - * This decoder is used to decode objects where the key is important, when you want to e.g. decode a type from the key, some decoders might ignore - * this key, and should therefore not be used here. - * - * This will influence how lattices will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] - * decoder. - */ - def getLatticeKeyDecoder: AbstractDecoder = getKeyDecoder - /** Returns a map that links a key to a specific decoder. */ def latticeDecoders[T: Decoder] = Set[(String, Decoder[_ <: Lattice[T]])](("constant", constantLatticeDecoder[T])) - given latticeDecoder[P[T] <: Lattice[T], T: Decoder]: EncapsulatedDecoder[P[T]] with - override def decoder: AbstractDecoder = getLatticeKeyDecoder - override protected def readEncapsulated(reader: Reader)(using d: AbstractDecoder): P[T] = - reader.readMembers[P[T]](latticeDecoders.toArray.asInstanceOf[Array[(String, io.bullet.borer.Decoder[? <: P[T]])]]).value + given latticeDecoder[P[T] <: Lattice[T], T: Decoder]: MapDecoder[P[T]] with + override def read(reader: Reader): P[T] = + reader.start() + val lattice = reader.readMembers[P[T]](latticeDecoders.toArray.asInstanceOf[Array[(String, io.bullet.borer.Decoder[? <: P[T]])]]) + reader.close() + return lattice.value given constantLatticeDecoder[T: Decoder]: Decoder[ConstantPropagation.L[T]] with override def read(reader: Reader): ConstantPropagation.L[T] = @@ -151,47 +117,48 @@ trait LoadModularSchemeLattices override def read(reader: Reader): (HMapKey, SchemeLattice#Prim) = return (modularLattice.PrimT, new modularLattice.Prim(reader.read[Set[String]]())) - given EncapsulatedDecoder[(SchemeLambdaExp, Env)] with - override def decoder: AbstractDecoder = getValueDecoder - override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): (SchemeLambdaExp, Env) = + given MapDecoder[(SchemeLambdaExp, Env)] with + override def read(reader: Reader): (SchemeLambdaExp, Env) = + reader.start() val expression = reader.readMember[SchemeExp]("expression").asInstanceOf[ReadValue[String, SchemeLambdaExp]] val address = reader.readMember[Env]("address") + reader.close() return (expression.value, address.value) - given EncapsulatedArrayDecoder[(HMapKey, SchemeLattice#Clo)]() with - override def decoder: AbstractDecoder = getValueDecoder - override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): (HMapKey, SchemeLattice#Clo) = - return (modularLattice.CloT, - new modularLattice.Clo( - reader.readUntilBeforeBreak[Set[(SchemeLambdaExp, Env)]](Set[(SchemeLambdaExp, Env)](), - (closures) => closures + (reader.read[(SchemeLambdaExp, Env)]()) - ) - ) - ) - - given EncapsulatedArrayDecoder[(HMapKey, SchemeLattice#Pointer)]() with - override def decoder: AbstractDecoder = getValueDecoder - override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): (HMapKey, SchemeLattice#Pointer) = - return (modularLattice.PointerT, - new modularLattice.Pointer(reader.readUntilBeforeBreak[Set[Address]](Set(), (pointers) => pointers + (reader.read[Address]()))) + given ArrayDecoder[(HMapKey, SchemeLattice#Clo)]() with + override def read(reader: Reader): (HMapKey, SchemeLattice#Clo) = + reader.start() + val closures = reader.readUntilBeforeBreak[Set[(SchemeLambdaExp, Env)]](Set[(SchemeLambdaExp, Env)](), + (closures) => closures + (reader.read[(SchemeLambdaExp, Env)]()) ) - - given EncapsulatedDecoder[(HMapKey, SchemeLattice#Cons)]() with - override def decoder: AbstractDecoder = getValueDecoder - override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): (HMapKey, SchemeLattice#Cons) = + reader.close() + return (modularLattice.CloT, new modularLattice.Clo(closures)) + + given ArrayDecoder[(HMapKey, SchemeLattice#Pointer)]() with + override def read(reader: Reader): (HMapKey, SchemeLattice#Pointer) = + reader.start() + val pointers = reader.readUntilBeforeBreak[Set[Address]](Set(), (pointers) => pointers + (reader.read[Address]())) + reader.close() + return (modularLattice.PointerT, new modularLattice.Pointer(pointers)) + + given MapDecoder[(HMapKey, SchemeLattice#Cons)]() with + override def read(reader: Reader): (HMapKey, SchemeLattice#Cons) = + reader.start() val car = reader.readMember[SchemeLattice#L]("car") val cdr = reader.readMember[SchemeLattice#L]("cdr") + reader.close() return (modularLattice.ConsT, new modularLattice.Cons(car.value, cdr.value)) - given EncapsulatedDecoder[(HMapKey, SchemeLattice#Vec)] with LoadMapToArray with - override def decoder: AbstractDecoder = getValueDecoder - override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): (HMapKey, SchemeLattice#Vec) = + given MapDecoder[(HMapKey, SchemeLattice#Vec)] with LoadMapToArray with + override def read(reader: Reader): (HMapKey, SchemeLattice#Vec) = + reader.start() val size = reader.readMember[Lattice[BigInt]]("size").value.asInstanceOf[LoadModularSchemeLattices.this.modularLatticeWrapper.I] val elements = reader .readMember[Map[Lattice[BigInt], SchemeLattice#L]]("elements") .value .asInstanceOf[Map[LoadModularSchemeLattices.this.modularLatticeWrapper.I, SchemeLattice#L]] + reader.close() return (modularLattice.VecT, new modularLattice.Vec(size, elements)) given nilLatticeDecoder: Decoder[(HMapKey, modularLattice.Nil.type)] with @@ -215,26 +182,22 @@ trait LoadModularSchemeLattices * The type of expression used in the analysis */ trait LoadModularDomain extends LoadValue[SchemeExp] with ModularSchemeDomain: - /** - * Get the decoder that will be used to decode an hMap. - * - * This will influence how an hMap will be decoded, this can be e.g. a [[MapDecoder map-based]] decoder or an [[ArrayDecoder array-based]] - * decoder. - */ - def getHMapDecoder: AbstractDecoder = new ArrayDecoder - /** Returns a map that links a key to a specific decoder. */ def hMapDecoders = Set[(String, Decoder[_ <: (HMapKey, Any)])]() - given EncapsulatedDecoder[(HMapKey, Any)] with - override def decoder: AbstractDecoder = getValueKeyDecoder - override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): (HMapKey, Any) = - reader.readMembers(hMapDecoders.toArray).value - - override given valueDecoder: EncapsulatedDecoder[HMap] with - override def decoder: AbstractDecoder = getHMapDecoder - override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): HMap = - return new HMap(reader.readUntilBeforeBreak[Map[HMapKey, Any]](Map(), (hMap) => hMap + reader.readMember[(HMapKey, Any)]().value)) + given MapDecoder[(HMapKey, Any)] with + override def read(reader: Reader): (HMapKey, Any) = + reader.start() + val hmap = reader.readMembers(hMapDecoders.toArray) + reader.close() + return hmap.value + + override given valueDecoder: ArrayDecoder[HMap] with + override def read(reader: Reader): HMap = + reader.start() + val hmap = reader.readUntilBeforeBreak[Map[HMapKey, Any]](Map(), (hMap) => hMap + reader.readMember[(HMapKey, Any)]().value) + reader.close() + return new HMap(hmap) /** * Trait to decode the global store. diff --git a/code/shared/src/main/scala/maf/save/load/Util.scala b/code/shared/src/main/scala/maf/save/load/Util.scala index 852826ac5..f9a8b5601 100644 --- a/code/shared/src/main/scala/maf/save/load/Util.scala +++ b/code/shared/src/main/scala/maf/save/load/Util.scala @@ -2,7 +2,6 @@ package maf.save import io.bullet.borer.Decoder import io.bullet.borer.Reader -import maf.save.ArrayKeyDecoder.readMember import scala.collection.mutable /** @@ -36,11 +35,12 @@ trait LoadMapToArray: * }}} * This can, for example be used if the key is not a string, and can therefore not be used as a key of a JSON map. */ - given mapKeyDecoder[K, V](using keyDecoder: Decoder[K], valueDecoder: Decoder[V]): EncapsulatedDecoder[Map[K, V]] with - override val decoder: ArrayKeyDecoder = new ArrayKeyDecoder - override protected def readEncapsulated(reader: Reader)(using AbstractDecoder): Map[K, V] = + given mapKeyDecoder[K, V](using keyDecoder: Decoder[K], valueDecoder: Decoder[V]): ArrayKeyDecoder[Map[K, V]] with + override def read(reader: Reader): Map[K, V] = + reader.start() val elements = mutable.Set[(K, V)]() while !reader.hasBreak do - val res = reader.readMember[K, V]()(using keyDecoder, valueDecoder, decoder) + val res = reader.readMember[K, V]()(using keyDecoder, valueDecoder) elements.add(res.key, res.value) + reader.close() return elements.toMap From bcdac9ec92bf9f9fb9f5e6e0d478bb0e11a23b2f Mon Sep 17 00:00:00 2001 From: Merlijn Date: Fri, 19 Apr 2024 18:34:13 +0200 Subject: [PATCH 68/97] refactor(persistence): Change name of folder Change name of parent folder for persistence code from `save` to `persistence` to increase clarity. --- .../src/main/scala/maf/{save => persistence}/load/Analysis.scala | 0 .../src/main/scala/maf/{save => persistence}/load/Component.scala | 0 .../src/main/scala/maf/{save => persistence}/load/Decoder.scala | 0 .../main/scala/maf/{save => persistence}/load/Dependency.scala | 0 .../main/scala/maf/{save => persistence}/load/Expression.scala | 0 .../src/main/scala/maf/{save => persistence}/load/Store.scala | 0 .../src/main/scala/maf/{save => persistence}/load/Util.scala | 0 .../src/main/scala/maf/{save => persistence}/save/Analysis.scala | 0 .../src/main/scala/maf/{save => persistence}/save/Component.scala | 0 .../main/scala/maf/{save => persistence}/save/Dependency.scala | 0 .../src/main/scala/maf/{save => persistence}/save/Encoder.scala | 0 .../main/scala/maf/{save => persistence}/save/Expression.scala | 0 .../src/main/scala/maf/{save => persistence}/save/Store.scala | 0 .../src/main/scala/maf/{save => persistence}/save/Util.scala | 0 14 files changed, 0 insertions(+), 0 deletions(-) rename code/shared/src/main/scala/maf/{save => persistence}/load/Analysis.scala (100%) rename code/shared/src/main/scala/maf/{save => persistence}/load/Component.scala (100%) rename code/shared/src/main/scala/maf/{save => persistence}/load/Decoder.scala (100%) rename code/shared/src/main/scala/maf/{save => persistence}/load/Dependency.scala (100%) rename code/shared/src/main/scala/maf/{save => persistence}/load/Expression.scala (100%) rename code/shared/src/main/scala/maf/{save => persistence}/load/Store.scala (100%) rename code/shared/src/main/scala/maf/{save => persistence}/load/Util.scala (100%) rename code/shared/src/main/scala/maf/{save => persistence}/save/Analysis.scala (100%) rename code/shared/src/main/scala/maf/{save => persistence}/save/Component.scala (100%) rename code/shared/src/main/scala/maf/{save => persistence}/save/Dependency.scala (100%) rename code/shared/src/main/scala/maf/{save => persistence}/save/Encoder.scala (100%) rename code/shared/src/main/scala/maf/{save => persistence}/save/Expression.scala (100%) rename code/shared/src/main/scala/maf/{save => persistence}/save/Store.scala (100%) rename code/shared/src/main/scala/maf/{save => persistence}/save/Util.scala (100%) diff --git a/code/shared/src/main/scala/maf/save/load/Analysis.scala b/code/shared/src/main/scala/maf/persistence/load/Analysis.scala similarity index 100% rename from code/shared/src/main/scala/maf/save/load/Analysis.scala rename to code/shared/src/main/scala/maf/persistence/load/Analysis.scala diff --git a/code/shared/src/main/scala/maf/save/load/Component.scala b/code/shared/src/main/scala/maf/persistence/load/Component.scala similarity index 100% rename from code/shared/src/main/scala/maf/save/load/Component.scala rename to code/shared/src/main/scala/maf/persistence/load/Component.scala diff --git a/code/shared/src/main/scala/maf/save/load/Decoder.scala b/code/shared/src/main/scala/maf/persistence/load/Decoder.scala similarity index 100% rename from code/shared/src/main/scala/maf/save/load/Decoder.scala rename to code/shared/src/main/scala/maf/persistence/load/Decoder.scala diff --git a/code/shared/src/main/scala/maf/save/load/Dependency.scala b/code/shared/src/main/scala/maf/persistence/load/Dependency.scala similarity index 100% rename from code/shared/src/main/scala/maf/save/load/Dependency.scala rename to code/shared/src/main/scala/maf/persistence/load/Dependency.scala diff --git a/code/shared/src/main/scala/maf/save/load/Expression.scala b/code/shared/src/main/scala/maf/persistence/load/Expression.scala similarity index 100% rename from code/shared/src/main/scala/maf/save/load/Expression.scala rename to code/shared/src/main/scala/maf/persistence/load/Expression.scala diff --git a/code/shared/src/main/scala/maf/save/load/Store.scala b/code/shared/src/main/scala/maf/persistence/load/Store.scala similarity index 100% rename from code/shared/src/main/scala/maf/save/load/Store.scala rename to code/shared/src/main/scala/maf/persistence/load/Store.scala diff --git a/code/shared/src/main/scala/maf/save/load/Util.scala b/code/shared/src/main/scala/maf/persistence/load/Util.scala similarity index 100% rename from code/shared/src/main/scala/maf/save/load/Util.scala rename to code/shared/src/main/scala/maf/persistence/load/Util.scala diff --git a/code/shared/src/main/scala/maf/save/save/Analysis.scala b/code/shared/src/main/scala/maf/persistence/save/Analysis.scala similarity index 100% rename from code/shared/src/main/scala/maf/save/save/Analysis.scala rename to code/shared/src/main/scala/maf/persistence/save/Analysis.scala diff --git a/code/shared/src/main/scala/maf/save/save/Component.scala b/code/shared/src/main/scala/maf/persistence/save/Component.scala similarity index 100% rename from code/shared/src/main/scala/maf/save/save/Component.scala rename to code/shared/src/main/scala/maf/persistence/save/Component.scala diff --git a/code/shared/src/main/scala/maf/save/save/Dependency.scala b/code/shared/src/main/scala/maf/persistence/save/Dependency.scala similarity index 100% rename from code/shared/src/main/scala/maf/save/save/Dependency.scala rename to code/shared/src/main/scala/maf/persistence/save/Dependency.scala diff --git a/code/shared/src/main/scala/maf/save/save/Encoder.scala b/code/shared/src/main/scala/maf/persistence/save/Encoder.scala similarity index 100% rename from code/shared/src/main/scala/maf/save/save/Encoder.scala rename to code/shared/src/main/scala/maf/persistence/save/Encoder.scala diff --git a/code/shared/src/main/scala/maf/save/save/Expression.scala b/code/shared/src/main/scala/maf/persistence/save/Expression.scala similarity index 100% rename from code/shared/src/main/scala/maf/save/save/Expression.scala rename to code/shared/src/main/scala/maf/persistence/save/Expression.scala diff --git a/code/shared/src/main/scala/maf/save/save/Store.scala b/code/shared/src/main/scala/maf/persistence/save/Store.scala similarity index 100% rename from code/shared/src/main/scala/maf/save/save/Store.scala rename to code/shared/src/main/scala/maf/persistence/save/Store.scala diff --git a/code/shared/src/main/scala/maf/save/save/Util.scala b/code/shared/src/main/scala/maf/persistence/save/Util.scala similarity index 100% rename from code/shared/src/main/scala/maf/save/save/Util.scala rename to code/shared/src/main/scala/maf/persistence/save/Util.scala From 0359f76d6c0655cc081ad7b50b3a62b70a65075f Mon Sep 17 00:00:00 2001 From: Merlijn Date: Sun, 21 Apr 2024 19:58:49 +0200 Subject: [PATCH 69/97] feat(save): Add way to encode option This allows for the non-inclusion of optional values inside of maps --- .../scala/maf/persistence/save/Dependency.scala | 6 ++---- .../scala/maf/persistence/save/Encoder.scala | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/code/shared/src/main/scala/maf/persistence/save/Dependency.scala b/code/shared/src/main/scala/maf/persistence/save/Dependency.scala index 89afda73e..54fe7d7d8 100644 --- a/code/shared/src/main/scala/maf/persistence/save/Dependency.scala +++ b/code/shared/src/main/scala/maf/persistence/save/Dependency.scala @@ -105,8 +105,7 @@ trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveComponents[SchemeExp] override def write(writer: Writer, address: VarAddr[EncodeContext]): Writer = writer.start() writer.writeMember("id", address.id) - if address.ctx.asInstanceOf[Option[EncodeContext]].isDefined then - writer.writeMember("context", address.ctx.asInstanceOf[Option[EncodeContext]].get) + writer.writeMember("context", address.ctx.asInstanceOf[Option[EncodeContext]]) writer.close() given MapEncoder[ReturnAddr[Component]] with @@ -120,8 +119,7 @@ trait SaveSchemeAddr extends SaveAddr[SchemeExp] with SaveComponents[SchemeExp] override def write(writer: Writer, address: PtrAddr[EncodeContext]): Writer = writer.start() writer.writeMember("expression", address.exp.asInstanceOf[SchemeExp]) - if address.ctx.asInstanceOf[Option[EncodeContext]].isDefined then - writer.writeMember("context", address.ctx.asInstanceOf[Option[EncodeContext]].get) + writer.writeMember("context", address.ctx.asInstanceOf[Option[EncodeContext]]) writer.close() override given addressEncoder: MapEncoder[Address] with diff --git a/code/shared/src/main/scala/maf/persistence/save/Encoder.scala b/code/shared/src/main/scala/maf/persistence/save/Encoder.scala index aa4a9d784..9ded0cbc7 100644 --- a/code/shared/src/main/scala/maf/persistence/save/Encoder.scala +++ b/code/shared/src/main/scala/maf/persistence/save/Encoder.scala @@ -126,6 +126,9 @@ trait AbstractEncoder[T] extends Encoder[T]: */ def closeEncapsulation(writer: Writer): Writer = writer.writeBreak() + /** [TODO: description] */ + def encodeOption[T: Encoder](writer: Writer, key: String, option: Option[T]): Writer + extension (writer: Writer) /** * Close the encapsulation. @@ -172,6 +175,9 @@ trait AbstractEncoder[T] extends Encoder[T]: writeKey(writer) writeValue(writer, value) + def writeMember[T: Encoder](key: String, value: Option[T]): Writer = + encodeOption(writer, key, value) + /** [TODO: description] */ def start() = openEncapsulation(writer) @@ -261,6 +267,9 @@ trait MapEncoder[T] extends AbstractEncoder[T]: override def writeValue[T: Encoder](writer: Writer, value: T): Writer = writer.write(value) override def openEncapsulation(writer: Writer): Writer = writer.writeMapStart() override def openEncapsulation(writer: Writer, amount: Int): Writer = writer.writeMapOpen(amount) + override def encodeOption[T: Encoder](writer: Writer, key: String, option: Option[T]): Writer = + if option.isDefined then writer.writeMember(key, option.get) + writer /** * Encoder that uses arrays to encode values. @@ -286,6 +295,10 @@ trait ArrayEncoder[T] extends AbstractEncoder[T]: override def writeValue[T: Encoder](writer: Writer, value: T): Writer = writer.write(value) override def openEncapsulation(writer: Writer): Writer = writer.writeArrayStart() override def openEncapsulation(writer: Writer, amount: Int): Writer = writer.writeArrayOpen(amount) + override def encodeOption[T: Encoder](writer: Writer, key: String, option: Option[T]): Writer = + writer.writeArrayOpen(if option.isDefined then 1 else 0) + if option.isDefined then writer.write(option.get) + writer.writeBreak() /** * Encoder that uses arrays to encode values, but preserves keys. @@ -339,6 +352,9 @@ trait ArrayKeyEncoder[T] extends ArrayEncoder[T]: */ def writeKey[T: Encoder](writer: Writer, key: T): Writer = writer.write(key) override def writeValue[T: Encoder](writer: Writer, value: T): Writer = writer.write(value) + override def encodeOption[T: Encoder](writer: Writer, key: String, option: Option[T]): Writer = + if option.isDefined then writer.writeMember(key, option.get) + writer extension (writer: Writer) /** From e9ab5c097103dbb5e78f3a1f1cf14cfb80ee5699 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Sun, 21 Apr 2024 19:59:45 +0200 Subject: [PATCH 70/97] feat(load): Add way to decode option This allows for the non-inclusion of optional values inside of maps --- .../scala/maf/persistence/load/Analysis.scala | 10 +- .../scala/maf/persistence/load/Decoder.scala | 201 +++++++++--------- .../maf/persistence/load/Dependency.scala | 9 +- .../scala/maf/persistence/load/Store.scala | 8 +- 4 files changed, 115 insertions(+), 113 deletions(-) diff --git a/code/shared/src/main/scala/maf/persistence/load/Analysis.scala b/code/shared/src/main/scala/maf/persistence/load/Analysis.scala index 1e3201d40..1a3af57e8 100644 --- a/code/shared/src/main/scala/maf/persistence/load/Analysis.scala +++ b/code/shared/src/main/scala/maf/persistence/load/Analysis.scala @@ -10,6 +10,7 @@ import io.bullet.borer.Reader import scala.collection.mutable.HashMap import maf.modular.AnalysisEntry import maf.modular.ModAnalysis +import scala.collection.mutable.ListBuffer /** * Contains info about the top-level objects that need to be loaded. @@ -46,14 +47,13 @@ trait Load[Expr <: Expression] extends AnalysisEntry[Expr]: private given excludedAnalysisDecoder: MapDecoder[Load[Expr]] with override def read(reader: Reader): Load[Expr] = reader.start() - val loaded = HashMap[String, Boolean]() + var loadResults = ListBuffer[() => Unit]() for (key, value) <- loadInfo do if load.contains(key) then val result = reader.readMember(key)(using value.decoder) - if result.hasValue then - value.load(result.value) - loaded.addOne((key, true)) - for (key, value) <- loadInfo do if load.contains(key) && !loaded.contains(key) then value.load(reader.getMember(key)) + if result.hasValue then value.load(result.value) + else loadResults = loadResults.addOne(() => value.load(result.value)) + for loadRes <- loadResults do loadRes() reader.close() return Load.this diff --git a/code/shared/src/main/scala/maf/persistence/load/Decoder.scala b/code/shared/src/main/scala/maf/persistence/load/Decoder.scala index f69ce5cd8..33114ca80 100644 --- a/code/shared/src/main/scala/maf/persistence/load/Decoder.scala +++ b/code/shared/src/main/scala/maf/persistence/load/Decoder.scala @@ -112,6 +112,35 @@ class ReadValue[K, V](private val forceRead: (key: Option[K]) => Unit): /** The [[ReadValue]] that will be updated when this [[ReadValue]] gets a key or a value. */ def updateValue = _updateValue +class ReadOptionValue[K, V](private val forceRead: (key: Option[K]) => Unit) extends ReadValue[K, Option[V]](forceRead): + /** + * Create a new key-value pair with a key and a value. + * + * @param _key + * The key of the key-value pair + * @param _value + * The value of the key-value pair + */ + def this(_key: K, _value: Option[V]) = + this((key: Option[K]) => ()) + this._key = Some(_key) + this._value = Some(_value) + + /** + * Create a new key-value pair with a key. + * + * @param _key + * The key of the key-value pair + */ + def this(_key: K, forceRead: (key: Option[K]) => Unit) = + this(forceRead) + this._key = Some(_key) + + override def value: Option[V] = + if _value.isEmpty then return None + return _value.get + override def hasValue: Boolean = true + /** * Base trait for an decoder. * @@ -131,16 +160,6 @@ class ReadValue[K, V](private val forceRead: (key: Option[K]) => Unit): * stored, you should use an decoder that writes them down like [[MapDecoder]] or [[ArrayKeyDecoder]] if you want to use an array. */ trait AbstractDecoder[T] extends Decoder[T]: - /** The values that have already been decoded, with their key. */ - protected val values = new HashMap[String, Any]() - - /** - * The key that should be read next. - * - * This is the key that is given through [[readKey]]. - */ - protected var currentKey: Option[String] = None - /** * Read a key from the given reader. * @@ -204,27 +223,6 @@ trait AbstractDecoder[T] extends Decoder[T]: */ def readValue[T: Decoder](reader: Reader): ReadValue[String, T] - /** - * Returns an already read value using a given key. - * - * @param key - * The key for which to get the value - * - * @throws NoSuchElementException - * If this key hasn't been read yet, and therefore doesn't have a value. - */ - def getValue[T](key: String): T = - if !values.contains(key) then throw new NoSuchElementException(s"The key '${key}' does not have a value.") - values.get(key).get.asInstanceOf[T] - - /** - * Returns whether or not a given key has a value yet. - * - * @param key - * The key to check if it has a value - */ - def hasValue(key: String): Boolean = values.contains(key) - /** * Opens either a new map or a new array, based on the decoder that is used. * @@ -287,6 +285,13 @@ trait AbstractDecoder[T] extends Decoder[T]: */ def forceReadValue(reader: Reader): Unit + /** [TODO: description] */ + def decodeOption[T: Decoder](reader: Reader): Option[T] + + given [T: Decoder]: Decoder[Option[T]] with + override def read(reader: Reader): Option[T] = + return decodeOption[T](reader) + def forceRead(reader: Reader, key: Option[String]) = if key.isDefined then forceReadValue(reader, key.get) else forceReadValue(reader) @@ -330,6 +335,15 @@ trait AbstractDecoder[T] extends Decoder[T]: readKey(reader) readValue[T](reader) + def readOptionMember[T](key: String)(using Decoder[T]): ReadValue[String, Option[T]] = + readKey(reader, key) + val value = readValue[Option[T]](reader) + val optionValue = + if value.hasValue then new ReadOptionValue[String, T](value.key, value.value) + else new ReadOptionValue[String, T](value.key, forceRead(reader, _)) + value.updateValue = optionValue; + return optionValue + /** * Read one of the given key-value pairs. * @@ -377,18 +391,6 @@ trait AbstractDecoder[T] extends Decoder[T]: while !reader.hasBreak do res = f(res) return res - /** - * Returns an already read value using a given key. - * - * @param decoder - * The decoder used to decode the value - * @throws NoSuchElementException - * If this key hasn't been read yet, and therefore doesn't have a value. - */ - def getMember[T](key: String): T = - if !hasValue(key) then forceReadValue(reader, key) - getValue[T](key) - /** [TODO: description] */ def start(): Unit = openEncapsulation(reader) @@ -432,65 +434,79 @@ trait MapDecoder[T] extends AbstractDecoder[T]: */ protected case class ValueDecoder[T](value: ReadValue[String, T], decoder: Decoder[T]) - /** Stores keys that haven't been read yet combined with a decoder to decode the value, and the key-value pair that was returned. */ - protected val keys = new HashMap[String, ValueDecoder[_]]() + /** [TODO: description] */ + protected class DecoderInfo: + val keys: HashMap[String, ValueDecoder[_]] = new HashMap[String, ValueDecoder[_]]() + var currentKey: Option[String] = None + var decodeKey: Option[String] = None + var previous: DecoderInfo = this - /** The key that was read, and which value should now be read. */ - protected var decodeKey: Option[String] = None + protected var decoderInfo = new DecoderInfo() /** Read the next key for which the value should be decoded, if there isn't currently a value being decoded */ protected def readDecodeKey(reader: Reader): Unit = - if decodeKey.isEmpty && reader.hasString then decodeKey = Some(reader.readString()) + if decoderInfo.decodeKey.isEmpty && reader.hasString then decoderInfo.decodeKey = Some(reader.readString()) override def readKey(reader: Reader): String = readDecodeKey(reader) - this.currentKey = decodeKey - return if decodeKey.isDefined then decodeKey.get else "" + decoderInfo.currentKey = decoderInfo.decodeKey + return if decoderInfo.decodeKey.isDefined then decoderInfo.decodeKey.get else "" override def readKey(reader: Reader, key: String): String = readDecodeKey(reader) - this.currentKey = Some(key) + decoderInfo.currentKey = Some(key) return key override def readValue[T: Decoder](reader: Reader): ReadValue[String, T] = - if currentKey.isEmpty then throw new IllegalStateException("Trying to read a value before reading a key.") + if decoderInfo.currentKey.isEmpty then throw new IllegalStateException("Trying to read a value before reading a key.") - if decodeKey == currentKey then - val key = decodeKey.get - decodeKey = None - currentKey = None + if decoderInfo.decodeKey == decoderInfo.currentKey then + val key = decoderInfo.decodeKey.get + decoderInfo.decodeKey = None + decoderInfo.currentKey = None val res = reader.read[T]() - values.addOne((key, res)) - if reader.hasString then decodeKey = Some(reader.readString()) - if decodeKey.isDefined && keys.contains(decodeKey.get) then - currentKey = decodeKey - val valueDecoder = keys.get(currentKey.get).get + if reader.hasString then decoderInfo.decodeKey = Some(reader.readString()) + if decoderInfo.decodeKey.isDefined && decoderInfo.keys.contains(decoderInfo.decodeKey.get) then + decoderInfo.currentKey = decoderInfo.decodeKey + val valueDecoder = decoderInfo.keys.get(decoderInfo.currentKey.get).get readValue(reader)(using valueDecoder.decoder) - currentKey = None - if keys.contains(key) then - val valueDecoder = keys.remove(key).get.asInstanceOf[ValueDecoder[T]] + decoderInfo.currentKey = None + if decoderInfo.keys.contains(key) then + val valueDecoder = decoderInfo.keys.remove(key).get.asInstanceOf[ValueDecoder[T]] valueDecoder.value.value = res return valueDecoder.value else return new ReadValue[String, T](key, res) else - val readValue = new ReadValue[String, T](currentKey.get, forceRead(reader, _)) - keys.addOne((currentKey.get, ValueDecoder(readValue, summon[Decoder[T]]))) - currentKey = None + val readValue = new ReadValue[String, T](decoderInfo.currentKey.get, forceRead(reader, _)) + decoderInfo.keys.addOne((decoderInfo.currentKey.get, ValueDecoder(readValue, summon[Decoder[T]]))) + decoderInfo.currentKey = None return readValue - override def openEncapsulation(reader: Reader): Unit = reader.readMapStart() - override def openEncapsulation(reader: Reader, amount: Int): Unit = reader.readMapOpen(amount) - override def closeEncapsulation[T](reader: Reader): Unit = reader.readBreak() + override def openEncapsulation(reader: Reader): Unit = + val info = new DecoderInfo() + info.previous = decoderInfo + decoderInfo = info + reader.readMapStart() + override def openEncapsulation(reader: Reader, amount: Int): Unit = + val info = new DecoderInfo() + info.previous = decoderInfo + decoderInfo = info + reader.readMapOpen(amount) + override def closeEncapsulation[T](reader: Reader): Unit = + decoderInfo = decoderInfo.previous + reader.readBreak() override def forceReadValue(reader: Reader, key: String): Unit = - while decodeKey.isDefined && !values.contains(key.asInstanceOf[String]) do forceReadValue(reader) + while decoderInfo.decodeKey.isDefined do forceReadValue(reader) override def forceReadValue(reader: Reader): Unit = - val tmpCurrentKey = currentKey + val tmpCurrentKey = decoderInfo.currentKey readDecodeKey(reader) - while decodeKey.isDefined && !keys.contains(decodeKey.get) do + while decoderInfo.decodeKey.isDefined && !decoderInfo.keys.contains(decoderInfo.decodeKey.get) do reader.skipElement() - decodeKey = None + decoderInfo.decodeKey = None readDecodeKey(reader) - if !decodeKey.isDefined then return - val valueDecoder = keys.get(decodeKey.get).get - currentKey = decodeKey + if !decoderInfo.decodeKey.isDefined then return + val valueDecoder = decoderInfo.keys.get(decoderInfo.decodeKey.get).get + decoderInfo.currentKey = decoderInfo.decodeKey readValue(reader)(using valueDecoder.decoder) - currentKey = tmpCurrentKey + decoderInfo.currentKey = tmpCurrentKey + override def decodeOption[T: Decoder](reader: Reader): Option[T] = + return Some(reader.read[T]()) /** * Decoder that uses arrays to decode values. @@ -513,6 +529,7 @@ trait MapDecoder[T] extends AbstractDecoder[T]: trait ArrayDecoder[T] extends AbstractDecoder[T]: /** Used to generate IDs if no key is provided, this is used to store the values. */ protected var id = -1 + protected var currentKey: Option[String] = None override def readKey(reader: Reader): String = return "" override def readKey(reader: Reader, key: String): String = currentKey = Some(key) @@ -523,13 +540,18 @@ trait ArrayDecoder[T] extends AbstractDecoder[T]: if reader.hasBreak then throw reader.unexpectedDataItem(key) id += 1 val res = reader.read[T]() - values.addOne(key, res) return ReadValue[String, T](key, res) override def openEncapsulation(reader: Reader): Unit = reader.readArrayStart() override def openEncapsulation(reader: Reader, amount: Int): Unit = reader.readArrayOpen(amount) override def closeEncapsulation[T](reader: Reader): Unit = reader.readBreak() override def forceReadValue(reader: Reader, key: String): Unit = reader.skipElement() override def forceReadValue(reader: Reader): Unit = reader.skipElement() + override def decodeOption[T: Decoder](reader: Reader): Option[T] = + var res: Option[T] = None + reader.readArrayOpen(1) + if !reader.hasBreak then res = Some(reader.read[T]()) + reader.readBreak() + return res /** * Decoder that uses arrays to decode values, but preserves keys. @@ -564,7 +586,6 @@ trait ArrayDecoder[T] extends AbstractDecoder[T]: trait ArrayKeyDecoder[T] extends MapDecoder[T]: /** The key that was read, and which value should now be read. */ protected var key: Option[Any] = None - protected val keyValues = new HashMap[Any, Any]() /** * Read a key from the given reader. @@ -609,31 +630,11 @@ trait ArrayKeyDecoder[T] extends MapDecoder[T]: val res = reader.read[T]() val tmpKey = key key = None - keyValues.addOne((tmpKey.get, res)) return ReadValue(tmpKey.get.asInstanceOf[V], res) override def openEncapsulation(reader: Reader): Unit = reader.readArrayStart() override def openEncapsulation(reader: Reader, amount: Int): Unit = reader.readArrayOpen(amount) override def closeEncapsulation[T](reader: Reader): Unit = reader.readBreak() - /** - * Returns an already read value using a given key. - * - * @throws NoSuchElementException - * If this key hasn't been read yet, and therefore doesn't have a value. - */ - def getValue[T](key: Any): T = - if !keyValues.contains(key) then - if key.isInstanceOf[String] then return super.getValue(key.asInstanceOf[String]) - else throw new NoSuchElementException(s"The key '${key}' does not have a value.") - return keyValues.get(key).get.asInstanceOf[T] - - /** - * Returns whether or not a given key has a value yet. - * - * @param key - * The key to check if it has a value - */ - def hasValue(key: Any): Boolean = keyValues.contains(key) || (key.isInstanceOf[String] && super.hasValue(key.asInstanceOf[String])) extension (reader: Reader) /** * Reads a key-value pair. diff --git a/code/shared/src/main/scala/maf/persistence/load/Dependency.scala b/code/shared/src/main/scala/maf/persistence/load/Dependency.scala index b6ce4063b..6dc35873e 100644 --- a/code/shared/src/main/scala/maf/persistence/load/Dependency.scala +++ b/code/shared/src/main/scala/maf/persistence/load/Dependency.scala @@ -14,6 +14,7 @@ import io.bullet.borer.Reader import io.bullet.borer.Decoder import maf.core.Identifier import maf.core.Identity +import scala.collection.mutable.HashMap /** * Trait to decode address dependencies. @@ -101,11 +102,11 @@ trait LoadSchemeAddr extends LoadAddr[SchemeExp] with LoadContext[SchemeExp] wit override def read(reader: Reader): VarAddr[DecodeContext] = reader.start() val name = reader.readMember[Identifier]("id") - val context = reader.readMember[DecodeContext]("context") + val context = reader.readOptionMember[DecodeContext]("context") reader.close() return new VarAddr[DecodeContext]( name.value, - if context.hasValue then Some(context.value).asInstanceOf[DecodeContext] else None.asInstanceOf[DecodeContext] + context.value.asInstanceOf[DecodeContext] ) given Decoder[PrmAddr] with @@ -115,9 +116,9 @@ trait LoadSchemeAddr extends LoadAddr[SchemeExp] with LoadContext[SchemeExp] wit override def read(reader: Reader): PtrAddr[DecodeContext] = reader.start() val expression = reader.readMember[SchemeExp]("expression") - val context = reader.readMember[DecodeContext]("context") + val context = reader.readOptionMember[DecodeContext]("context") reader.close() return new PtrAddr[DecodeContext]( expression.value, - if context.hasValue then Some(context.value).asInstanceOf[DecodeContext] else None.asInstanceOf[DecodeContext] + context.value.asInstanceOf[DecodeContext] ) diff --git a/code/shared/src/main/scala/maf/persistence/load/Store.scala b/code/shared/src/main/scala/maf/persistence/load/Store.scala index 2dc69053a..e0ab14c05 100644 --- a/code/shared/src/main/scala/maf/persistence/load/Store.scala +++ b/code/shared/src/main/scala/maf/persistence/load/Store.scala @@ -21,8 +21,8 @@ import maf.modular.scheme.modf.BaseSchemeModFSemanticsM * The base trait for decoding [[AbstractDomain.Value values]]. * * @note - * This trait gives the methods needed to decode values, but does not implement them yet, other traits like [[LoaNoContext]] should be mixed in for - * the implementation. The trait that should be mixed in depends on the kind of values that is used in your analysis. + * This trait gives the methods needed to decode values, but does not implement them yet, other traits like [[LoadModularDomain]] should be mixed in + * for the implementation. The trait that should be mixed in depends on the kind of values that is used in your analysis. * * @tparam Expr * The type of expression used in the analysis @@ -175,8 +175,8 @@ trait LoadModularSchemeLattices * Base trait for decoding values as [[ModularSchemeLattice modular scheme lattices]], as defined in [[ModularSchemeDomain]]. * * @note - * This trait gives the methods needed to decode values, but does not implement them yet, other traits like [[LoaNoContext]] should be mixed in for - * the implementation. The trait that should be mixed in depends on the kind of values that is used in your analysis. + * This trait gives the methods needed to decode values, but does not implement them yet, other traits like [[LoadModularSchemeLattices]] should be + * mixed in for the implementation. The trait that should be mixed in depends on the kind of values that is used in your analysis. * * @tparam Expr * The type of expression used in the analysis From 2ff267a0a0ac8ccea7db7f663e9efd72fa53e6d0 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 22 Apr 2024 08:57:58 +0200 Subject: [PATCH 71/97] fix(save): When saving the environment save the option and not the value --- code/shared/src/main/scala/maf/persistence/save/Component.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/shared/src/main/scala/maf/persistence/save/Component.scala b/code/shared/src/main/scala/maf/persistence/save/Component.scala index 685e51f0e..0848bbc61 100644 --- a/code/shared/src/main/scala/maf/persistence/save/Component.scala +++ b/code/shared/src/main/scala/maf/persistence/save/Component.scala @@ -175,7 +175,7 @@ trait SaveEnvironment[Expr <: Expression] extends Save[Expr] with SaveAddr[Expr] override def write(writer: Writer, env: NestedEnv[Address, Address]): Writer = writer.start() writer.writeMember("content", env.content) - if env.rst.isDefined then writer.writeMember("rst", env.rst.get) + writer.writeMember("rst", env.rst) writer.close() given MapEncoder[Environment[Address]] with From 24c2b63c866f0c46b7b66157de5941120b76bd53 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 22 Apr 2024 08:58:48 +0200 Subject: [PATCH 72/97] chore(save): Remove unnecessary traits in `saveComponents` --- .../shared/src/main/scala/maf/persistence/save/Component.scala | 3 --- 1 file changed, 3 deletions(-) diff --git a/code/shared/src/main/scala/maf/persistence/save/Component.scala b/code/shared/src/main/scala/maf/persistence/save/Component.scala index 0848bbc61..140df7043 100644 --- a/code/shared/src/main/scala/maf/persistence/save/Component.scala +++ b/code/shared/src/main/scala/maf/persistence/save/Component.scala @@ -230,9 +230,6 @@ trait SaveNoContext[Expr <: Expression] extends SaveContext[Expr]: trait SaveStandardSchemeComponents extends SaveActualComps[SchemeExp] with StandardSchemeModFComponents - with AnalysisResults[SchemeExp] - with SaveValue[SchemeExp] - with SavePosition[SchemeExp] with SaveEnvironment[SchemeExp] with SaveContext[SchemeExp] with SaveExpressions[SchemeExp]: From 1a4e6dfbdbb8d958c50109b227f23e3f9182daf7 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 22 Apr 2024 08:59:26 +0200 Subject: [PATCH 73/97] fix(save): Encode the correct worklist I was encoding the global worklist instead of the worklist that was given to the encoder --- code/shared/src/main/scala/maf/persistence/save/Component.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/shared/src/main/scala/maf/persistence/save/Component.scala b/code/shared/src/main/scala/maf/persistence/save/Component.scala index 140df7043..88bfc60a2 100644 --- a/code/shared/src/main/scala/maf/persistence/save/Component.scala +++ b/code/shared/src/main/scala/maf/persistence/save/Component.scala @@ -262,6 +262,6 @@ trait SaveSequentialWorklist[Expr <: Expression] extends SaveWorklist[Expr] with override given worklistEncoder: ArrayEncoder[WorkList[Component]] with override def write(writer: Writer, worklist: WorkList[Component]): Writer = writer.start() - val worklistList = workList.toList + val worklistList = worklist.toList worklistList.foreach(writer.writeMember(_)) writer.close() From 52a5424b0410c78bad0a7a107b2fcf1705e5498f Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 22 Apr 2024 09:00:56 +0200 Subject: [PATCH 74/97] chore(load): Remove unnecessary traits from `LoadComponent` --- .../shared/src/main/scala/maf/persistence/load/Component.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/code/shared/src/main/scala/maf/persistence/load/Component.scala b/code/shared/src/main/scala/maf/persistence/load/Component.scala index d3caeaf69..8abb77286 100644 --- a/code/shared/src/main/scala/maf/persistence/load/Component.scala +++ b/code/shared/src/main/scala/maf/persistence/load/Component.scala @@ -76,7 +76,7 @@ trait LoadComponents[Expr <: Expression] extends ModAnalysis[Expr] with Load[Exp * @tparam Expr * The type of expression used in the analysis */ -trait LoadActualComps[Expr <: Expression] extends LoadComponents[Expr]: +protected trait LoadActualComps[Expr <: Expression] extends LoadComponents[Expr]: /** Encodes the actual component. */ given actualComponentDecoder: Decoder[Component] @@ -97,7 +97,6 @@ trait LoadStandardSchemeComponents extends LoadActualComps[SchemeExp] with StandardSchemeModFComponents with LoadContext[SchemeExp] - with LoadPosition[SchemeExp] with LoadEnvironment[SchemeExp] with LoadExpressions[SchemeExp]: override given actualComponentDecoder: Decoder[Component] with From 1a09290a470e70beb9ef219c66832351464082a8 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 22 Apr 2024 09:01:37 +0200 Subject: [PATCH 75/97] fix(load): `NestedEnv` was not decoded correctly --- .../src/main/scala/maf/persistence/load/Component.scala | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/code/shared/src/main/scala/maf/persistence/load/Component.scala b/code/shared/src/main/scala/maf/persistence/load/Component.scala index 8abb77286..3ecda799f 100644 --- a/code/shared/src/main/scala/maf/persistence/load/Component.scala +++ b/code/shared/src/main/scala/maf/persistence/load/Component.scala @@ -188,7 +188,7 @@ trait LoadEnvironment[Expr <: Expression] extends Load[Expr] with LoadAddr[Expr] reader.start() val value = reader .readMembers[Environment[T]]( - Array(("basicEnvironment", summon[Decoder[BasicEnvironment[T]]]), ("nestedEnv", summon[Decoder[NestedEnv[T, T]]])) + Array(("basicEnvironment", summon[Decoder[BasicEnvironment[T]]]), ("nestedEnvironment", summon[Decoder[NestedEnv[T, T]]])) ) .value reader.close() @@ -198,13 +198,14 @@ trait LoadEnvironment[Expr <: Expression] extends Load[Expr] with LoadAddr[Expr] override def read(reader: Reader): BasicEnvironment[T] = return new BasicEnvironment( reader.read[Map[String, Address]]().asInstanceOf[Map[String, T]] ) + given [T <: Address, K <: Address]: MapDecoder[NestedEnv[T, K]] with override def read(reader: Reader): NestedEnv[T, K] = reader.start() val content = reader.readMember[Map[String, Address]]("content") - val rst = reader.readMember[Address]("rst") + val rst = reader.readOptionMember[Address]("rst") reader.close() - return new NestedEnv(content.value.asInstanceOf[Map[String, T]], if rst.hasValue then Some(rst.value.asInstanceOf[K]) else None) + return new NestedEnv(content.value.asInstanceOf[Map[String, T]], rst.value.asInstanceOf[Option[K]]) /** * The base trait for decoding components only by their ID. From 6d3f1a22ee8b8cab49d1f0b9c21bcfbda67a3213 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 22 Apr 2024 09:02:12 +0200 Subject: [PATCH 76/97] refactor(load): Use `Component` when decoding the worklist Instead of creating a new type `WorklistComponent` --- .../maf/persistence/load/Component.scala | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/code/shared/src/main/scala/maf/persistence/load/Component.scala b/code/shared/src/main/scala/maf/persistence/load/Component.scala index 3ecda799f..f1c0b4aa9 100644 --- a/code/shared/src/main/scala/maf/persistence/load/Component.scala +++ b/code/shared/src/main/scala/maf/persistence/load/Component.scala @@ -312,22 +312,22 @@ trait LoadComponentIntID[Expr <: Expression] extends LoadComponentID[Expr]: reader.close() return components -trait LoadWorklist[Expr <: Expression] extends Load[Expr]: - type WorklistComponent - given worklistDecoder: Decoder[List[WorklistComponent]] - def setWorklist(worklist: List[WorklistComponent]): Unit +trait LoadWorklist[Expr <: Expression] extends ModAnalysis[Expr] with Load[Expr]: + given worklistDecoder: Decoder[WorkList[Component]] + def setWorklist(worklist: WorkList[Component]): Unit + def newWorklist(components: List[Component]): WorkList[Component] override def loadInfo: List[(String, Loadable[_])] = - super.loadInfo ++ List(("worklist" -> Loadable((worklist: List[WorklistComponent]) => setWorklist(worklist)))) + super.loadInfo ++ List(("worklist" -> Loadable((worklist: WorkList[Component]) => setWorklist(worklist)))) trait LoadSequentialWorklist[Expr <: Expression] extends SequentialWorklistAlgorithm[Expr] with LoadWorklist[Expr] with LoadComponents[Expr]: - type WorklistComponent = Component - given worklistDecoder: ArrayDecoder[List[WorklistComponent]] with - override def read(reader: Reader): List[WorklistComponent] = + override def setWorklist(worklist: WorkList[Component]): Unit = workList = worklist + given worklistDecoder: ArrayDecoder[WorkList[Component]] with + override def read(reader: Reader): WorkList[Component] = reader.start() val worklistComponents = reader.readUntilBeforeBreak(List[Component](), (lst: List[Component]) => lst ++ List(reader.read[Component]())) reader.close() - return worklistComponents + return newWorklist(worklistComponents) trait LoadFIFOWorklist[Expr <: Expression] extends LoadSequentialWorklist[Expr] with FIFOWorklistAlgorithm[Expr]: - def setWorklist(worklist: List[WorklistComponent]): Unit = - workList = new FIFOWorkList[WorklistComponent](Queue(), Set()).addAll(worklist) + override def newWorklist(components: List[Component]): WorkList[Component] = + return FIFOWorkList[Component](components.iterator.to(Iterable)) From 226d058549bdc999ce6448f0aa639bdedb04454d Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 22 Apr 2024 09:03:39 +0200 Subject: [PATCH 77/97] test(persistence): Add tests --- .../scala/maf/test/persistence/Analysis.scala | 445 ++++++++++++++++++ .../maf/test/persistence/Component.scala | 199 ++++++++ .../maf/test/persistence/Dependency.scala | 108 +++++ .../maf/test/persistence/Expression.scala | 119 +++++ .../scala/maf/test/persistence/Store.scala | 135 ++++++ 5 files changed, 1006 insertions(+) create mode 100644 code/shared/src/test/scala/maf/test/persistence/Analysis.scala create mode 100644 code/shared/src/test/scala/maf/test/persistence/Component.scala create mode 100644 code/shared/src/test/scala/maf/test/persistence/Dependency.scala create mode 100644 code/shared/src/test/scala/maf/test/persistence/Expression.scala create mode 100644 code/shared/src/test/scala/maf/test/persistence/Store.scala diff --git a/code/shared/src/test/scala/maf/test/persistence/Analysis.scala b/code/shared/src/test/scala/maf/test/persistence/Analysis.scala new file mode 100644 index 000000000..f648295b7 --- /dev/null +++ b/code/shared/src/test/scala/maf/test/persistence/Analysis.scala @@ -0,0 +1,445 @@ +package maf.test.persistence + +import io.bullet.borer +import io.bullet.borer.Decoder +import io.bullet.borer.Encoder +import io.bullet.borer.Json +import io.bullet.borer.Writer +import maf.core.Address +import maf.core.Expression +import maf.core.Identifier +import maf.core.NoCodeIdentityDebug +import maf.core.Position +import maf.language.CScheme.CSchemeParser +import maf.language.scheme.SchemeBegin +import maf.language.scheme.SchemeExp +import maf.language.scheme.SchemeLambdaExp +import maf.language.scheme.SchemeVar +import maf.modular.AnalysisEntry +import maf.modular.ModAnalysis +import maf.modular.scheme.SchemeConstantPropagationDomain +import maf.modular.scheme.modf.SchemeModFNoSensitivity +import maf.modular.scheme.modf.SimpleSchemeModFAnalysis +import maf.modular.worklist.FIFOWorklistAlgorithm +import maf.save.AbstractDecoder +import maf.save.AbstractEncoder +import maf.save.ArrayDecoder +import maf.save.ArrayEncoder +import maf.save.ArrayKeyDecoder +import maf.save.ArrayKeyEncoder +import maf.save.Load +import maf.save.LoadActualComponents +import maf.save.LoadActualExpressions +import maf.save.LoadComponents +import maf.save.LoadExpressions +import maf.save.LoadSchemeExpressions +import maf.save.LoadStandardSchemeComponents +import maf.save.MapDecoder +import maf.save.MapEncoder +import maf.save.Save +import maf.save.SaveActualComponents +import maf.save.SaveComponents +import maf.save.SaveModularDomain +import maf.save.SaveStandardSchemeComponents +import maf.save.save.SaveActualExpressions +import maf.save.save.SaveExpressions +import maf.save.save.SaveSchemeExpressions +import maf.util.Reader +import maf.util.benchmarks.Timeout.T +import org.scalacheck.Gen +import org.scalacheck.Prop._ +import org.scalatest.GivenWhenThen +import org.scalatest.matchers.should.Matchers +import org.scalatest.prop.TableDrivenPropertyChecks +import org.scalatest.propspec.AnyPropSpec +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks + +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths +import scala.jdk.CollectionConverters.* +import maf.save.SaveValue +import maf.save.LoadValue +import maf.core.Lattice +import maf.core.Identity +import maf.core.SimpleIdentity +import maf.save.SaveContext +import maf.save.LoadContext +import maf.save.SaveAddr +import maf.save.LoadAddr +import maf.modular.scheme.PrmAddr +import maf.core.Environment +import maf.language.scheme.SchemeLambdaExp +import maf.core.Address +import maf.core.Position +import maf.modular.scheme.PtrAddr +import maf.core.Identifier +import maf.modular.scheme.VarAddr +import maf.modular.scheme.modf.BaseEvalM +import maf.modular.scheme.modf.SchemeModFComponent +import maf.modular.scheme.modf.BaseSchemeModFSemanticsM +import maf.core.BasicEnvironment +import maf.language.scheme.SchemeLambda +import maf.save.SaveModF +import maf.save.LoadModF +import maf.save.SaveEnvironment +import maf.save.LoadEnvironment + +trait Generator: + protected case class StringValue(str: String) + protected val optionStr = Gen.option(Gen.alphaLowerStr) + protected val str = Gen.alphaLowerStr + val stringAddr: Gen[Address] = str.map(str => new PrmAddr(str)) + val stringValues = functionGen(stringValue) + val stringContexts = functionGen(stringValue) + val stringComponents = functionGen(stringValue) + val stringEnvironments = stringAddr.map(addr => new BasicEnvironment[Address](Map((str.sample.get, addr)))) + + def stringValue(): StringValue = return new StringValue(str.sample.get) + + def functionGen[T](f: () => T): Gen[T] = Gen.const("").map(_ => f()) + protected val maxDepth = 5 + protected var depth = 0 + def maxDepthFunctionGen[T](generate: () => T, retry: () => T): Gen[T] = + Gen.const("") + .map(_ => + if depth < maxDepth then + depth += 1 + val res = generate() + depth -= 1 + res + else retry() + ) + +object PersistenceSpec: + def simpleIdentity(tag: String): Identity = new SimpleIdentity(Position.Position(-2, 0, new Position.SimplePTag(tag))) + def simpleExpression(tag: String): Expression = Identifier("", new SimpleIdentity(Position.Position(-2, 0, new Position.SimplePTag(tag)))) + def simpleSchemeExpression(tag: String): SchemeExp = SchemeVar( + Identifier("", simpleIdentity(tag)) + ) + def simpleSchemeLambdaExpression(tag: String): SchemeLambdaExp = + new SchemeLambda(None, List(), List(simpleSchemeExpression(tag)), None, simpleIdentity(tag)) + +trait PersistenceSpec extends AnyPropSpec with ScalaCheckPropertyChecks with TableDrivenPropertyChecks with GivenWhenThen with Matchers with Generator: + + /** + * Class used for getting access to the encoding/decoding givens. + * + * @note + * This analysis shouldn't be used to run actual analyses since the run method is a noop. + */ + class TestAnalysis extends ModAnalysis[Expression](PersistenceSpec.simpleExpression(str.sample.get)) with Save[Expression] with Load[Expression]: + override def finished: Boolean = return true + override def intraAnalysis(component: Component): IntraAnalysis = ??? + override def addToWorkList(cmp: Component): Unit = ??? + override def initialComponent: Component = ??? + override def expr(cmp: Component): Expression = ??? + override protected def run(timeout: T): Unit = return + + /** + * Class used for getting access to the encoding/decoding givens using scheme expressions. + * + * @note + * This analysis shouldn't be used to run actual analyses since the run method is a noop. + */ + class TestSchemeAnalysis + extends ModAnalysis[SchemeExp](PersistenceSpec.simpleSchemeExpression(str.sample.get)) + with Save[SchemeExp] + with Load[SchemeExp]: + override def finished: Boolean = return true + override def intraAnalysis(component: Component): IntraAnalysis = ??? + override def addToWorkList(cmp: Component): Unit = ??? + override def initialComponent: Component = ??? + override def expr(cmp: Component): SchemeExp = ??? + override protected def run(timeout: T): Unit = return + + abstract class TestBaseSchemeModFSemanticsAnalysis extends TestSchemeAnalysis with BaseSchemeModFSemanticsM: + override def expr(cmp: Component): SchemeExp = ??? + override def intraAnalysis(cmp: Component): SchemeModFSemanticsIntra = ??? + override def allocCtx( + clo: (SchemeLambdaExp, Environment[Address]), + args: List[Value], + call: Position.Position, + caller: Component + ): ComponentContext = + ??? + override def allocPtr(exp: SchemeExp, cmp: Component): PtrAddr[AllocationContext] = ??? + override def allocVar(id: Identifier, cmp: Component): VarAddr[AllocationContext] = ??? + override def baseEnv: Env = ??? + implicit override lazy val baseEvalM: BaseEvalM[M] = ??? + override def newComponent(call: SchemeModFComponent.Call[ComponentContext]): Component = ??? + override def view(cmp: Component): SchemeModFComponent = ??? + // Members declared in maf.modular.GlobalStore + override def store: Map[Addr, Value] = ??? + override def store_=(store: Map[Addr, Value]): Unit = ??? + + trait SaveStringContext[Expr <: Expression] extends SaveContext[Expr]: + override type EncodeContext = StringValue + override given contextEncoder: Encoder[EncodeContext] with + override def write(writer: Writer, context: EncodeContext): Writer = + writer.writeMapOpen(1) + writer.write("==TESTING== NO CONTEXT") + writer.write(context.str) + writer.writeMapClose() + + trait LoadStringContext[Expr <: Expression] extends LoadContext[Expr]: + override type DecodeContext = StringValue + override given contextDecoder: Decoder[DecodeContext] with + override def read(reader: borer.Reader): DecodeContext = + reader.readMapOpen(1) + if !reader.tryReadString("==TESTING== NO CONTEXT") then return reader.unexpectedDataItem("==TESTING== NO CONTEXT") + val str = reader.readString() + reader.readBreak() + return StringValue(str) + + trait SaveStringValue[Expr <: Expression] extends SaveValue[Expr]: + override type Value = StringValue + implicit override lazy val lattice: Lattice[Value] = ??? + override given valueEncoder: Encoder[Value] with + override def write(writer: Writer, value: Value): Writer = + writer.writeMapOpen(1) + writer.write("==TESTING== NO VALUE") + writer.write(value.str) + writer.writeMapClose() + + trait LoadStringValue[Expr <: Expression] extends LoadValue[Expr]: + override type Value = StringValue + implicit override lazy val lattice: Lattice[Value] = ??? + override given valueDecoder: Decoder[Value] with + override def read(reader: borer.Reader): Value = + reader.readMapOpen(1) + if !reader.tryReadString("==TESTING== NO VALUE") then return reader.unexpectedDataItem("==TESTING== NO VALUE") + val str = reader.readString() + reader.readBreak() + return StringValue(str) + + trait SaveStringComponent[Expr <: Expression] extends SaveComponents[Expr]: + override type Component = StringValue + override given componentEncoder: Encoder[Component] with + override def write(writer: Writer, component: Component): Writer = + writer.writeMapOpen(1) + writer.write("==TESTING== NO COMPONENT") + writer.write(component.str) + writer.writeMapClose() + + trait LoadStringComponent[Expr <: Expression] extends LoadComponents[Expr]: + override type Component = StringValue + override given componentDecoder: Decoder[Component] with + override def read(reader: borer.Reader): Component = + reader.readMapOpen(1) + if !reader.tryReadString("==TESTING== NO COMPONENT") then return reader.unexpectedDataItem("==TESTING== NO COMPONENT") + val str = reader.readString() + reader.readBreak() + return StringValue(str) + + trait SaveStringExpression[Expr <: Expression] extends SaveExpressions[Expr]: + override given expressionEncoder: Encoder[Expr] with + override def write(writer: Writer, expression: Expr): Writer = + writer.writeMapOpen(1) + if expression.isInstanceOf[SchemeLambdaExp] then writer.write("==TESTING== NO LAMBDA EXPRESSION") + else writer.write("==TESTING== NO EXPRESSION") + writer.write(expression.idn.pos.tag.show) + writer.writeMapClose() + + trait LoadStringExpression[Expr <: Expression] extends LoadExpressions[Expr]: + def simpleExpression(name: String): Expr + def simpleLambdaExpression(name: String): Expr + override given expressionDecoder: Decoder[Expr] with + override def read(reader: borer.Reader): Expr = + reader.readMapOpen(1) + val lambdaExp = reader.tryReadString("==TESTING== NO LAMBDA EXPRESSION") + if !lambdaExp && !reader.tryReadString("==TESTING== NO EXPRESSION") then return reader.unexpectedDataItem("==TESTING== NO EXPRESSION") + val str = reader.readString() + reader.readBreak() + return if lambdaExp then simpleLambdaExpression(str) else simpleExpression(str) + + trait SaveStringAddr[Expr <: Expression] extends SaveAddr[Expr]: + override given addressEncoder: Encoder[Address] with + override def write(writer: Writer, addr: Address): Writer = + writer.writeMapOpen(1) + writer.write("==TESTING== NO ADDRESS") + writer.write(addr.asInstanceOf[PrmAddr].nam) + writer.writeBreak() + + trait LoadStringAddr[Expr <: Expression] extends LoadAddr[Expr]: + override def addressDecoders: List[(String, Decoder[? <: Address])] = + List(("==TESTING== NO ADDRESS", summon[Decoder[PrmAddr]])) + given Decoder[PrmAddr] with + override def read(reader: borer.Reader): PrmAddr = + val str = reader.readString() + return new PrmAddr(str) + + trait LoadStringSchemeExpression extends LoadStringExpression[SchemeExp]: + override def simpleExpression(tag: String): SchemeExp = PersistenceSpec.simpleSchemeExpression(tag) + override def simpleLambdaExpression(tag: String): SchemeExp = PersistenceSpec.simpleSchemeLambdaExpression(tag) + + trait SaveStringEnvironment[Expr <: Expression] extends SaveEnvironment[Expr] with SaveStringAddr[Expr] + trait LoadStringEnvironment[Expr <: Expression] extends LoadEnvironment[Expr] with LoadStringAddr[Expr] + + /** + * Test whether an object can be encoded/decoded and whether or not the decoded value is equal to the original object. + * + * @param name + * The name of the object + * @param object + * The object to test + * @param codec + * The encoder and decoder for these objects + */ + def testEncodingDecoding[T, ASSERTION]( + name: String, + obj: T, + codec: () => (Encoder[T], Decoder[T]), + eq: (original: T, decoded: T) => ASSERTION + ): Unit = + property(s"A ${name} should be encoded") { + val encoded = Json.encode(obj)(using codec()._1).toByteArray + encoded.length should be > (0) + } + + property(s"A ${name} should be decoded") { + val encoded = Json.encode(obj)(using codec()._1).toByteArray + val decoded = Json.decode(encoded).to[T](using codec()._2) + decoded.valueTry.isSuccess should be(true) + } + + property(s"A ${name} should remain the same when encoding and decoding") { + val encoded = Json.encode(obj)(using codec()._1).toByteArray + val decoded = Json.decode(encoded).to[T](using codec()._2) + decoded.valueTry.isSuccess should be(true) + eq(obj, decoded.value) + } + + /** + * Test whether a type can be encoded/decoded and whether or not the decoded value is equal to the original object. + * + * @param name + * The name of the object + * @param objects + * A generator to create new objects + * @param codec + * The encoder and decoder for these objects + */ + def testEncodingDecoding[T, ASSERTION]( + name: String, + objects: Gen[T], + codec: () => (Encoder[T], Decoder[T]), + eq: (original: T, decoded: T) => ASSERTION, + print: Boolean = true + ): Unit = + property(s"A ${name} should be encoded") { + forAll(objects) { (obj: T) => + if print then Given(name + ": " + obj.toString()) + val encoded = Json.encode(obj)(using codec()._1).toByteArray + encoded.length should be > (0) + } + } + + property(s"A ${name} should be decoded") { + forAll(objects) { (obj: T) => + if print then Given(name + ": " + obj.toString()) + val encoded = Json.encode(obj)(using codec()._1).toByteArray + val decoded = Json.decode(encoded).to[T](using codec()._2) + decoded.valueTry.isSuccess should be(true) + } + } + + property(s"A ${name} should remain the same when encoding and decoding") { + forAll(objects) { (obj: T) => + if print then Given(name + ": " + obj.toString()) + val encoded = Json.encode(obj)(using codec()._1).toByteArray + val decoded = Json.decode(encoded).to[T](using codec()._2) + eq(obj, decoded.value) + } + } + +class PersistAnalysisSpec extends PersistenceSpec: + val programsStream = Files.list(Paths.get("test/R5RS/ad")) + val programsList = if programsStream == null then List() else programsStream.iterator().nn.asScala.toList + val programs = Gen.oneOf[Path](programsList) + + class ContextInsensitiveSchemeAnalysis(program: SchemeExp) + extends SaveAnalysis(program) + with SchemeModFNoSensitivity + with SchemeConstantPropagationDomain + with FIFOWorklistAlgorithm[SchemeExp] + with SaveModF + with LoadModF + + abstract class SaveAnalysis(program: SchemeExp) + extends SimpleSchemeModFAnalysis(program) + with AnalysisEntry[SchemeExp] + with Save[SchemeExp] + with Load[SchemeExp] + + private def save[Analysis <: AnalysisEntry[SchemeExp]](program: SchemeExp, analysis: Analysis): Path = + import analysis.given + analysis.analyze() + val saveFile = Files.createTempFile("maf", ".json") + require(saveFile != null) + analysis.save(saveFile.toString()) + return saveFile.asInstanceOf[Path] + + private def load[Analysis <: AnalysisEntry[_]](analysis: Analysis, saveFile: Path): Analysis = + import analysis.given + analysis.load(saveFile.toString()) + return analysis + + private def testSchemePrograms[ASSERTION, Analysis <: AnalysisEntry[SchemeExp]]( + anl: (program: SchemeExp) => Analysis, + testCorrectness: (result: Analysis, loadedResult: Analysis) => ASSERTION + ): Unit = + forAll(programs) { (path: Path) => + Given(path.toString()) + val program = CSchemeParser.parseProgram(Reader.loadFile(path.toString())) + val analysis = anl(program) + val saveFile = save(program, analysis) + + val loadAnalysis = load(anl(program), saveFile) + + Files.deleteIfExists(saveFile) + testCorrectness(analysis, loadAnalysis) + } + + property("A programs result should remain the same when encoded and decoded for a context insensitive scheme analysis") { + testSchemePrograms( + (program: SchemeExp) => new ContextInsensitiveSchemeAnalysis(program), + (result: ContextInsensitiveSchemeAnalysis, loadedResult: ContextInsensitiveSchemeAnalysis) => + loadedResult.result shouldBe defined + loadedResult.result.get should equal(result.result.get) + ) + } + + property("A programs components should remain the same when encoded and decoded for a context insensitive scheme analysis") { + testSchemePrograms( + (program: SchemeExp) => new ContextInsensitiveSchemeAnalysis(program), + (result: ContextInsensitiveSchemeAnalysis, loadedResult: ContextInsensitiveSchemeAnalysis) => + loadedResult.visited.size should equal(result.visited.size) + // This is done inside of a loop to improve the errors given when a test fails + for component <- result.visited do loadedResult.visited should contain(component) + ) + } + + property("A programs dependencies should remain the same when encoded and decoded for a context insensitive scheme analysis") { + testSchemePrograms( + (program: SchemeExp) => new ContextInsensitiveSchemeAnalysis(program), + (result: ContextInsensitiveSchemeAnalysis, loadedResult: ContextInsensitiveSchemeAnalysis) => + loadedResult.deps.size should equal(result.deps.size) + // This is done inside of a loop to improve the errors given when a test fails + for dependency <- result.deps.keysIterator do + loadedResult.deps.keySet should contain(dependency) + loadedResult.deps.get(dependency) should equal(result.deps.get(dependency)) + ) + } + + property("A programs store should remain the same when encoded and decoded for a context insensitive scheme analysis") { + testSchemePrograms( + (program: SchemeExp) => new ContextInsensitiveSchemeAnalysis(program), + (result: ContextInsensitiveSchemeAnalysis, loadedResult: ContextInsensitiveSchemeAnalysis) => + loadedResult.store.size should equal(result.store.size) + // This is done inside of a loop to improve the errors given when a test fails + for addr <- result.store.keySet do + loadedResult.store.keySet should contain(addr) + loadedResult.store.get(addr) should equal(result.store.get(addr)) + ) + } diff --git a/code/shared/src/test/scala/maf/test/persistence/Component.scala b/code/shared/src/test/scala/maf/test/persistence/Component.scala new file mode 100644 index 000000000..2569bc1e1 --- /dev/null +++ b/code/shared/src/test/scala/maf/test/persistence/Component.scala @@ -0,0 +1,199 @@ +package maf.test.persistence + +import org.scalatest.flatspec.AnyFlatSpec +import maf.core.Position.Position +import io.bullet.borer.Json +import maf.save.SavePosition +import maf.language.scheme.SchemeExp +import maf.modular.ModAnalysis +import maf.save.AbstractEncoder +import maf.save.MapEncoder +import maf.core.Expression +import org.scalatest.BeforeAndAfterEach +import maf.core.Identifier +import maf.core.NoCodeIdentityDebug +import maf.save.ArrayEncoder +import maf.save.ArrayKeyEncoder +import maf.save.LoadPosition +import maf.save.AbstractDecoder +import maf.save.MapDecoder +import org.scalatest.Assertions._ +import org.scalatest.propspec.AnyPropSpec +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks +import org.scalacheck.Gen +import maf.core.Position.PTag +import maf.core.Position.NoPTag +import org.scalatest.GivenWhenThen +import maf.core.Position.SimplePTag +import maf.core.Position.PTagWithSource +import maf.core.Position.SourcePathTag +import org.scalatest.matchers.should.Matchers +import org.scalatest.prop.TableDrivenPropertyChecks +import io.bullet.borer.Encoder +import maf.save.ArrayKeyDecoder +import maf.save.ArrayDecoder +import io.bullet.borer.Decoder +import maf.core.BasicEnvironment +import maf.core.NestedEnv +import maf.save.LoadContext +import maf.save.SaveContext +import maf.save.SaveNoContext +import maf.save.LoadNoContext +import maf.modular.scheme.modf.NoContext +import maf.core.NoCodeIdentity +import maf.modular.scheme.modf.SchemeModFComponent +import maf.core.Address +import maf.save.SaveEnvironment +import maf.save.LoadEnvironment +import maf.core.Environment +import maf.save.SaveStandardSchemeComponents +import maf.save.LoadStandardSchemeComponents +import maf.save.SaveActualComponents +import maf.save.LoadActualComponents +import maf.core.worklist.FIFOWorkList +import scala.collection.immutable.Queue +import maf.save.SaveWorklist +import maf.save.LoadFIFOWorklist +import maf.core.worklist.WorkList +import maf.util.benchmarks.Timeout.T +import maf.save.SaveSequentialWorklist + +trait ComponentGenerator extends Generator: + val pTags = Gen.alphaLowerStr.map((str) => + Gen.oneOf[PTag](NoPTag, new SimplePTag(str), new PTagWithSource(str, Gen.alphaLowerStr.sample.get), new SourcePathTag(str)).sample.get + ) + + val positions = (for + x <- Gen.posNum[Int] + y <- Gen.posNum[Int] + ptag <- pTags + yield Position(x, y, ptag)) + + val identities = Gen.oneOf(NoCodeIdentity, NoCodeIdentityDebug) + val identifiers = Gen.alphaLowerStr.map((str) => new Identifier(str, identities.sample.get)) + + var schemeModFComponents = Gen.oneOf[SchemeModFComponent]( + SchemeModFComponent.Main, + str.map(str => + new SchemeModFComponent.Call[StringValue]((PersistenceSpec.simpleSchemeLambdaExpression(str), stringEnvironments.sample.get), + stringContexts.sample.get + ) + ) + ) + +trait EnvironmentGenerator extends Generator: + val environments = Gen.oneOf( + str.map(str => new BasicEnvironment[Address](Map((str, stringAddr.sample.get)))), + str.map(str => new NestedEnv[Address, Address](Map((str, stringAddr.sample.get)), Gen.option(stringAddr.sample.get).sample.get)) + ) + +class PersistEnvironmentSpec extends PersistenceSpec with EnvironmentGenerator: + class EnvironmentAnalysis + extends TestAnalysis + with SaveEnvironment[Expression] + with LoadEnvironment[Expression] + with SaveStringAddr[Expression] + with LoadStringAddr[Expression] + + testEncodingDecoding( + "environments", + environments, + () => + val analysis = new EnvironmentAnalysis + import analysis.given + (summon[Encoder[Environment[Address]]], summon[Decoder[Environment[Address]]]), + (original: Environment[Address], decoded: Environment[Address]) => decoded should be(original) + ) + +trait WorklistGenerator extends Generator: + val FIFOWorklists = Gen.listOfN(10, stringComponents).map(comps => new FIFOWorkList[StringValue](Queue(), Set()).addAll(comps)) + +class PersistWorklistSpec extends PersistenceSpec with WorklistGenerator: + class FIFOWorklistAnalysis + extends TestAnalysis + with SaveSequentialWorklist[Expression] + with LoadFIFOWorklist[Expression] + with SaveStringComponent[Expression] + with LoadStringComponent[Expression]: + override def addToWorkList(cmp: Component) = workList = workList.add(cmp) + override def finished: Boolean = return true + override def run(timeout: T): Unit = return + override def initialComponent: Component = StringValue("INITIAL") + + testEncodingDecoding( + "FIFO worklist", + FIFOWorklists, + () => + val analysis = new FIFOWorklistAnalysis + import analysis.given + (analysis.worklistEncoder, analysis.worklistDecoder), + (original: WorkList[_], decoded: WorkList[_]) => + decoded.toList.size should equal(original.toList.size) + var orig = original + var dec = decoded + while !orig.isEmpty do + dec.isEmpty should be(false) + dec.head should equal(orig.head) + orig = orig.tail + dec = dec.tail + dec.isEmpty should be(true) + ) + +class PersistComponentSpec extends PersistenceSpec with ComponentGenerator: + class PositionAnalysis extends TestAnalysis with SavePosition[Expression] with LoadPosition[Expression] + testEncodingDecoding( + "position", + positions, + () => + val analysis = new PositionAnalysis + import analysis.given + (summon[Encoder[Position]], summon[Decoder[Position]]), + (original: Position, decoded: Position) => + decoded.line should be(original.line) + decoded.col should be(original.col) + decoded.tag should be(original.tag) + ) + + class SchemeModFComponentAnalysis + extends TestBaseSchemeModFSemanticsAnalysis + with SaveStandardSchemeComponents + with LoadStandardSchemeComponents + with SaveActualComponents[SchemeExp] + with LoadActualComponents[SchemeExp] + with SaveStringEnvironment[SchemeExp] + with LoadStringEnvironment[SchemeExp] + with SaveStringContext[SchemeExp] + with LoadStringContext[SchemeExp] + with SaveStringExpression[SchemeExp] + with LoadStringSchemeExpression: + override lazy val initialComponent: Component = SchemeModFComponent.Main + override def newComponent(call: SchemeModFComponent.Call[ComponentContext]): Component = ??? + override def view(cmp: Component): SchemeModFComponent = ??? + // Members declared in maf.modular.scheme.SchemeDomain + implicit override lazy val lattice: maf.language.scheme.lattices.SchemeLattice[Value, maf.core.Address] = ??? + override lazy val primitives: maf.language.scheme.primitives.SchemePrimitives[Value, maf.core.Address] = ??? + + testEncodingDecoding( + "scheme modF components", + schemeModFComponents, + () => + val analysis = new SchemeModFComponentAnalysis + import analysis.given + (analysis.componentEncoder, analysis.componentDecoder), + (original: SchemeModFComponent, decoded: SchemeModFComponent) => decoded should be(original) + ) + +trait ContextGenerator: + val contexts = Gen.const(NoContext) + +class PersistContextSpec extends PersistenceSpec with ContextGenerator: + class NoContextAnalysis extends TestAnalysis with SaveNoContext[Expression] with LoadNoContext[Expression] + testEncodingDecoding( + "no context", + NoContext, + () => + val analysis = new NoContextAnalysis + import analysis.given + (summon[Encoder[NoContext.type]], summon[Decoder[NoContext.type]]), + (original: NoContext.type, decoded: NoContext.type) => original should equal(decoded) + ) diff --git a/code/shared/src/test/scala/maf/test/persistence/Dependency.scala b/code/shared/src/test/scala/maf/test/persistence/Dependency.scala new file mode 100644 index 000000000..71ce4645d --- /dev/null +++ b/code/shared/src/test/scala/maf/test/persistence/Dependency.scala @@ -0,0 +1,108 @@ +package maf.test.persistence + +import maf.core.Address +import maf.core.Expression +import maf.language.scheme.SchemeExp +import maf.modular.ModAnalysis +import maf.modular.scheme.modf.SchemeModFComponent +import maf.save.LoadAddr +import maf.save.LoadNoContext +import maf.save.LoadSchemeAddr +import maf.save.LoadSchemeExpressions +import maf.save.LoadStandardSchemeComponentPosition +import maf.save.SaveAddr +import maf.save.SaveNoContext +import maf.save.SaveSchemeAddr +import maf.save.SaveStandardSchemeComponentPosition +import maf.save.save.SaveSchemeExpressions +import org.scalacheck.Gen +import maf.modular.scheme.VarAddr +import maf.modular.scheme.modf.NoContext +import maf.modular.ReturnAddr +import maf.modular.scheme.PrmAddr +import maf.modular.scheme.PtrAddr +import io.bullet.borer.Encoder +import io.bullet.borer.Decoder +import maf.save.SaveContext +import maf.save.LoadContext +import maf.modular.AddrDependency +import maf.save.SaveAddrDep +import maf.save.LoadAddrDependency +import maf.modular.Dependency + +trait AddressGenerator extends Generator with ComponentGenerator: + val addresses = + Gen.oneOf(functionGen(generateVarAddr), functionGen(generatePrmAddr), functionGen(generateReturnAddr), functionGen(generatePtrAddr)) + + def generateVarAddr(): VarAddr[Option[StringValue]] = + return new VarAddr(identifiers.sample.get, Gen.option(stringValues).sample.get) + + def generateReturnAddr(): ReturnAddr[StringValue] = + return new ReturnAddr(StringValue(str.sample.get), identities.sample.get) + + def generatePrmAddr(): PrmAddr = + return new PrmAddr(str.sample.get) + + def generatePtrAddr(): PtrAddr[Option[StringValue]] = + return new PtrAddr(PersistenceSpec.simpleSchemeExpression(str.sample.get), Gen.option(stringValues).sample.get) + +trait DependencyGenerator extends Generator: + val dependencies = stringAddr.map(addr => new AddrDependency(addr)) + +class PersistDependencySpec extends PersistenceSpec with DependencyGenerator: + class DepenencyAnalysis + extends TestAnalysis + with SaveAddrDep[Expression] + with LoadAddrDependency[Expression] + with SaveStringAddr[Expression] + with LoadStringAddr[Expression] + with SaveStringComponent[Expression] + with LoadStringComponent[Expression] + + testEncodingDecoding( + "dependency", + dependencies, + () => + val anl = new DepenencyAnalysis + import anl.given + (summon[Encoder[Dependency]], summon[Decoder[Dependency]]), + (original: Dependency, decoded: Dependency) => decoded should equal(original) + ) + +class PersistAddressSpec extends PersistenceSpec with AddressGenerator: + trait AddressAnalysis[Expr <: Expression] extends ModAnalysis[Expr] with SaveAddr[Expr] with LoadAddr[Expr] + class SchemeAddressAnalysis + extends TestSchemeAnalysis + with AddressAnalysis[SchemeExp] + with SaveSchemeAddr + with LoadSchemeAddr + with SaveStringContext[SchemeExp] + with LoadStringContext[SchemeExp] + with SaveStringComponent[SchemeExp] + with LoadStringComponent[SchemeExp] + with SaveStringExpression[SchemeExp] + with LoadStringSchemeExpression + + class NoContextAnalysis extends TestAnalysis with SaveNoContext[Expression] with LoadNoContext[Expression] + + testEncodingDecoding( + "address", + addresses, + () => + val anl = new SchemeAddressAnalysis + import anl.given + (summon[Encoder[Address]], summon[Decoder[Address]]), + (original: Address, decoded: Address) => + decoded.idn should equal(original.idn) + decoded should equal(original) + ) + + testEncodingDecoding( + "no context", + NoContext, + () => + val anl = new NoContextAnalysis + import anl.given + (summon[Encoder[NoContext.type]], summon[Decoder[NoContext.type]]), + (_: NoContext.type, decoded: NoContext.type) => decoded should equal(NoContext) + ) diff --git a/code/shared/src/test/scala/maf/test/persistence/Expression.scala b/code/shared/src/test/scala/maf/test/persistence/Expression.scala new file mode 100644 index 000000000..180ba2d9a --- /dev/null +++ b/code/shared/src/test/scala/maf/test/persistence/Expression.scala @@ -0,0 +1,119 @@ +package maf.test.persistence + +import maf.save.save.SaveSchemeExpressions +import maf.save.LoadSchemeExpressions +import maf.save.save.SaveActualExpressions +import maf.save.LoadActualExpressions +import maf.language.scheme.SchemeExp +import org.scalacheck.Gen +import maf.language.scheme.SchemeVar +import maf.core.Identifier +import maf.core.NoCodeIdentityDebug +import io.bullet.borer.Encoder +import io.bullet.borer.Decoder +import maf.language.scheme.SchemeLambda +import maf.language.scheme.SchemeFuncall +import maf.language.scheme.SchemeVarArgLambda +import maf.language.scheme.SchemeLetrec +import maf.language.scheme.SchemeAssert +import maf.language.scheme.SchemeLet +import maf.language.scheme.SchemeIf +import maf.language.scheme.SchemeSet +import maf.language.scheme.SchemeBegin +import maf.language.scheme.SchemeLetStar +import maf.modular.ModAnalysis +import maf.core.Expression +import maf.language.scheme.SchemeValue +import maf.language.sexp.Value + +trait ExpressionGenerator extends ComponentGenerator with Generator: + val schemeExpressions: Gen[SchemeExp] = functionGen(generateSchemeExp) + private val schemeExpressionList = Gen.listOfN(5, schemeExpressions) + private val identifiersList = Gen.listOfN(10, identifiers) + private val optionStrTuple = Gen.alphaLowerStr.map(str => Gen.option(Gen.const((str, Gen.alphaLowerStr.sample.get))).sample.get) + private val bindings = Gen + .listOfN(10, str) + .map(names => + // This list is converted into a set to ensure that there are no duplicate identifiers + Set(names: _*).toList.map(name => (new Identifier(name, identities.sample.get), generateSchemeExp())) + ) + + def maxDepthFunctionSchemeGen(generate: () => SchemeExp): Gen[SchemeExp] = maxDepthFunctionGen[SchemeExp](generate, generateSchemeExp) + def generateSchemeExp(): SchemeExp = + val res = Gen + .frequency( + (1, maxDepthFunctionSchemeGen(generateSchemeFuncall)), + (10, identifiers.map(id => new SchemeVar(id))), + (1, maxDepthFunctionSchemeGen(generateSchemeLambda)), + (1, maxDepthFunctionSchemeGen(generateSchemeVarArgLambda)), + (10, functionGen(generateSchemeValue)), + (1, maxDepthFunctionSchemeGen(generateSchemeLetRec)), + (1, maxDepthFunctionSchemeGen(generateSchemeAssert)), + (1, maxDepthFunctionSchemeGen(generateSchemeLet)), + (1, maxDepthFunctionSchemeGen(generateSchemeIf)), + (1, maxDepthFunctionSchemeGen(generateSchemeSet)), + (1, maxDepthFunctionSchemeGen(generateSchemeBegin)), + ) + .sample + .get + return res + + def generateSchemeValue(): SchemeValue = + return new SchemeValue(Value.String(str.sample.get), identities.sample.get) + + def generateSchemeLetStart(): SchemeLetStar = + return new SchemeLetStar(bindings.sample.get, schemeExpressionList.sample.get, identities.sample.get) + + def generateSchemeBegin(): SchemeBegin = + return new SchemeBegin(schemeExpressionList.sample.get, identities.sample.get) + + def generateSchemeSet(): SchemeSet = + return new SchemeSet(identifiers.sample.get, schemeExpressions.sample.get, identities.sample.get) + + def generateSchemeIf(): SchemeIf = + return new SchemeIf(schemeExpressions.sample.get, schemeExpressions.sample.get, schemeExpressions.sample.get, identities.sample.get) + + def generateSchemeLet(): SchemeLet = + return new SchemeLet(bindings.sample.get, schemeExpressionList.sample.get, identities.sample.get) + + def generateSchemeAssert(): SchemeAssert = + return new SchemeAssert(schemeExpressions.sample.get, identities.sample.get) + + def generateSchemeLetRec(): SchemeLetrec = + return new SchemeLetrec(bindings.sample.get, schemeExpressionList.sample.get, identities.sample.get) + + def generateSchemeVarArgLambda(): SchemeVarArgLambda = + return new SchemeVarArgLambda(optionStr.sample.get, + identifiersList.sample.get, + identifiers.sample.get, + schemeExpressionList.sample.get, + optionStrTuple.sample.get, + identities.sample.get + ) + + def generateSchemeFuncall(): SchemeFuncall = + return new SchemeFuncall(generateSchemeExp(), schemeExpressionList.sample.get, identities.sample.get) + + def generateSchemeLambda(): SchemeLambda = + val str = Gen.alphaLowerStr.sample.get + val id = identities.sample.get + val lambda = new SchemeLambda(optionStr.sample.get, identifiersList.sample.get, schemeExpressionList.sample.get, optionStrTuple.sample.get, id) + return lambda + +class PersistExpressionSpec extends PersistenceSpec with ExpressionGenerator: + trait ExpressionAnalysis[Expr <: Expression] extends ModAnalysis[Expr] with SaveActualExpressions[Expr] with LoadActualExpressions[Expr] + class SchemeExpressionAnalysis extends TestSchemeAnalysis with ExpressionAnalysis[SchemeExp] with SaveSchemeExpressions with LoadSchemeExpressions + + testEncodingDecoding( + "scheme expressions", + schemeExpressions, + () => + val anl = new SchemeExpressionAnalysis + import anl.given + (summon[Encoder[SchemeExp]], summon[Decoder[SchemeExp]]), + (original: SchemeExp, decoded: SchemeExp) => + decoded.idn should equal(original.idn) + decoded.height should equal(original.height) + decoded should equal(original), + false + ) diff --git a/code/shared/src/test/scala/maf/test/persistence/Store.scala b/code/shared/src/test/scala/maf/test/persistence/Store.scala new file mode 100644 index 000000000..a65aa6479 --- /dev/null +++ b/code/shared/src/test/scala/maf/test/persistence/Store.scala @@ -0,0 +1,135 @@ +package maf.test.persistence + +import maf.save.SaveValue +import maf.modular.ModAnalysis +import maf.save.LoadValue +import maf.save.SaveModularDomain +import maf.save.LoadModularDomain +import maf.core.Expression +import maf.language.scheme.SchemeExp +import maf.save.SaveLattice +import maf.save.LoadLattice +import maf.lattice.HMapKey +import maf.language.scheme.lattices.SchemeLattice +import maf.modular.scheme.SchemeConstantPropagationDomain +import maf.language.scheme.lattices.ModularSchemeLattice +import maf.modular.scheme.ModularSchemeLatticeWrapper +import maf.lattice.ConstantPropagation +import org.scalacheck.Gen +import io.bullet.borer.Encoder +import io.bullet.borer.Decoder +import maf.save.LoadModularSchemeLattices +import maf.lattice.HMap +import maf.lattice.interfaces.IntLattice +import scala.collection.mutable.HashMap +import maf.core.BasicEnvironment +import maf.core.Address + +trait ValueGenerator extends Generator: + type SchemeLattice = ModularSchemeLattice[?, ?, ?, ?, ?, ?, ?] + val modularLattice = SchemeConstantPropagationDomain.modularLattice + + val schemeLattices: Gen[(HMapKey, SchemeLattice#Value)] = functionGen(generateSchemeLattice) + val schemeLatticeMaps = Gen.mapOfN(3, schemeLattices) + + def generateSchemeLattice(): (HMapKey, SchemeLattice#Value) = + val lattice = Gen + .oneOf( + functionGen(generateSchemeIntLattice), + functionGen(generateSchemeBoolLattice), + functionGen(generateSchemeStringLattice), + functionGen(generateSchemePrimLattice), + functionGen(generateSchemeSymbolLattice), + maxDepthFunctionGen(generateSchemeConsLattice, generateSchemeLattice), + maxDepthFunctionGen(generateSchemeVectorLattice, generateSchemeLattice), + functionGen(generateSchemeClosureLattice), + functionGen(generateSchemeNilLattice), + functionGen(generateSchemeVoidLattice), + functionGen(generateSchemePointerLattice) + ) + .sample + .get + return lattice + + val hMaps = schemeLatticeMaps.map((map) => new HMap(map)) + + def generateConstantSchemeLattice[T](gen: Gen[T]): Gen[ConstantPropagation.L[T]] = + return Gen.oneOf(ConstantPropagation.Top, ConstantPropagation.Bottom, ConstantPropagation.Constant(gen.sample.get)) + + def generateSchemeIntLattice(): (HMapKey, SchemeLattice#Int) = + return (modularLattice.IntT, new modularLattice.Int(generateConstantSchemeLattice(Gen.Choose.chooseBigInt.choose(0, 1000000)).sample.get)) + + def generateSchemeBoolLattice(): (HMapKey, SchemeLattice#Bool) = + return (modularLattice.BoolT, new modularLattice.Bool(generateConstantSchemeLattice(Gen.prob(0.5)).sample.get)) + + def generateSchemeStringLattice(): (HMapKey, SchemeLattice#Str) = + return (modularLattice.StrT, new modularLattice.Str(generateConstantSchemeLattice(str.sample.get).sample.get)) + + def generateSchemePrimLattice(): (HMapKey, SchemeLattice#Prim) = + return (modularLattice.PrimT, new modularLattice.Prim(Gen.listOfN(5, str).sample.get.toSet)) + + def generateSchemeClosureLattice(): (HMapKey, SchemeLattice#Clo) = + return (modularLattice.CloT, + new modularLattice.Clo( + Set( + (PersistenceSpec.simpleSchemeLambdaExpression(str.sample.get), + new BasicEnvironment[Address](Map((str.sample.get, stringAddr.sample.get))) + ) + ) + ) + ) + + def generateSchemePointerLattice(): (HMapKey, SchemeLattice#Pointer) = + return (modularLattice.PointerT, modularLattice.Pointer(Gen.listOfN(5, stringAddr).sample.get.toSet)) + + def generateSchemeSymbolLattice(): (HMapKey, SchemeLattice#Symbol) = + return (modularLattice.SymbolT, new modularLattice.Symbol(generateConstantSchemeLattice(str).sample.get)) + + def generateSchemeConsLattice(): (HMapKey, SchemeLattice#Cons) = + val car = hMaps.sample.get + val cdr = hMaps.sample.get + return (modularLattice.ConsT, new modularLattice.Cons(car, cdr)) + + def generateSchemeVectorLattice(): (HMapKey, SchemeLattice#Vec) = + val length = Gen.Choose.chooseBigInt.choose(1, 5).sample.get + val lengthLattice = ConstantPropagation.Constant(length) + + val list = (for i <- Range(0, length.toInt) yield ((ConstantPropagation.Constant(BigInt(i)), hMaps.sample.get))) + return (modularLattice.VecT, modularLattice.Vec(lengthLattice, list.toMap)) + + def generateSchemeNilLattice(): (HMapKey, modularLattice.Nil.type) = + return (modularLattice.NilT, modularLattice.Nil) + + def generateSchemeVoidLattice(): (HMapKey, modularLattice.Void.type) = + return (modularLattice.VoidT, modularLattice.Void) + +class PersistValueSpec extends PersistenceSpec with ValueGenerator: + trait ValueAnalysis[Expr <: Expression] extends ModAnalysis[Expr] with SaveValue[Expr] with LoadValue[Expr] + class ModularDomainAnalysis + extends TestBaseSchemeModFSemanticsAnalysis + with ValueAnalysis[SchemeExp] + with SaveModularDomain + with LoadModularDomain + with LoadModularSchemeLattices + with SaveStringContext[SchemeExp] + with LoadStringContext[SchemeExp] + with SaveStringComponent[SchemeExp] + with LoadStringComponent[SchemeExp] + with SaveStringExpression[SchemeExp] + with LoadStringSchemeExpression + with SaveStringAddr[SchemeExp] + with LoadStringAddr[SchemeExp]: + override val modularLatticeWrapper: ModularSchemeLatticeWrapper = SchemeConstantPropagationDomain + + testEncodingDecoding( + "lattice", + schemeLattices, + () => + val anl = ModularDomainAnalysis() + import anl.given + (summon[Encoder[(HMapKey, Any)]], summon[Decoder[(HMapKey, Any)]]), + (original: (HMapKey, Any), decoded: (HMapKey, Any)) => + decoded._1 should equal(original._1) + decoded._2 should equal(original._2), + false + ) From 5650b5fe1dff5d622fc846a3e01c57b0b126dcf6 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 3 Jun 2024 07:29:06 +0200 Subject: [PATCH 78/97] feat(load): Add CBOR support --- .../scala/maf/persistence/load/Analysis.scala | 39 +++++++++++++++---- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/code/shared/src/main/scala/maf/persistence/load/Analysis.scala b/code/shared/src/main/scala/maf/persistence/load/Analysis.scala index 1a3af57e8..d7cacfa50 100644 --- a/code/shared/src/main/scala/maf/persistence/load/Analysis.scala +++ b/code/shared/src/main/scala/maf/persistence/load/Analysis.scala @@ -11,6 +11,7 @@ import scala.collection.mutable.HashMap import maf.modular.AnalysisEntry import maf.modular.ModAnalysis import scala.collection.mutable.ListBuffer +import io.bullet.borer.Cbor /** * Contains info about the top-level objects that need to be loaded. @@ -38,18 +39,18 @@ trait Load[Expr <: Expression] extends AnalysisEntry[Expr]: given analysisDecoder: Decoder[Load[Expr]] with override def read(reader: Reader): Load[Expr] = val loadInfo = Load.this.loadInfo - load = loadInfo.map(_._1).toSet + loadSet = loadInfo.map(_._1).toSet reader.read[Load[Expr]]() - load = Set[String]() + loadSet = Set[String]() return Load.this - private var load = Set[String]() - private given excludedAnalysisDecoder: MapDecoder[Load[Expr]] with + protected var loadSet = Set[String]() + protected given excludedAnalysisDecoder: MapDecoder[Load[Expr]] with override def read(reader: Reader): Load[Expr] = reader.start() var loadResults = ListBuffer[() => Unit]() for (key, value) <- loadInfo do - if load.contains(key) then + if loadSet.contains(key) then val result = reader.readMember(key)(using value.decoder) if result.hasValue then value.load(result.value) else loadResults = loadResults.addOne(() => value.load(result.value)) @@ -57,11 +58,15 @@ trait Load[Expr <: Expression] extends AnalysisEntry[Expr]: reader.close() return Load.this + def startLoad(): Unit = return + override def load(filename: String): Unit = + startLoad() val bytes = Files.readAllBytes(Paths.get(filename)) if bytes != null then Json.decode(bytes).to[Load[Expr]](using analysisDecoder).value override def load(filename: String, load: Set[String]): Unit = + startLoad() val bytes = Files.readAllBytes(Paths.get(filename)) if bytes != null then Json.decode(bytes).to[Load[Expr]](using excludedAnalysisDecoder).value @@ -78,13 +83,31 @@ trait Load[Expr <: Expression] extends AnalysisEntry[Expr]: */ def loadInfo: List[(String, Loadable[_])] = List(("name", Loadable((name: String) => ()))) +trait LoadCbor[Expr <: Expression] extends Load[Expr]: + override def load(filename: String): Unit = + startLoad() + val bytes = Files.readAllBytes(Paths.get(filename)) + if bytes != null then Cbor.decode(bytes).to[Load[Expr]](using analysisDecoder).value + + override def load(filename: String, load: Set[String]): Unit = + startLoad() + val bytes = Files.readAllBytes(Paths.get(filename)) + if bytes != null then Json.decode(bytes).to[Load[Expr]](using excludedAnalysisDecoder).value + trait LoadInitialized[Expr <: Expression] extends ModAnalysis[Expr] with Load[Expr]: override def loadInfo: List[(String, Loadable[?])] = - super.loadInfo ++ List(("initialized", Loadable((initialized: Boolean) => analysisInitialized = initialized))) + super.loadInfo ++ List( + ("initialized", + Loadable((initialized: Boolean) => + if !visited.contains(initialComponent) then visited = visited + initialComponent + analysisInitialized = initialized + ) + ) + ) /** The trait used to load the modF analysis. */ trait LoadModF - extends Load[SchemeExp] + extends LoadCbor[SchemeExp] with LoadInitialized[SchemeExp] with LoadExpressionIntID[SchemeExp] with LoadSchemeExpressions @@ -96,4 +119,4 @@ trait LoadModF with LoadDependency[SchemeExp] with LoadAddrDependency[SchemeExp] with LoadGlobalStore[SchemeExp] - with LoadModularSchemeLattices + with LoadModularSchemeDomain From e39505840d05ccd62d40b7a1bfbe71b3da3596bc Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 3 Jun 2024 07:31:42 +0200 Subject: [PATCH 79/97] feat(load): Add extra functions to decoder --- .../src/main/scala/maf/persistence/load/Decoder.scala | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/code/shared/src/main/scala/maf/persistence/load/Decoder.scala b/code/shared/src/main/scala/maf/persistence/load/Decoder.scala index 33114ca80..2e434d25e 100644 --- a/code/shared/src/main/scala/maf/persistence/load/Decoder.scala +++ b/code/shared/src/main/scala/maf/persistence/load/Decoder.scala @@ -395,6 +395,16 @@ trait AbstractDecoder[T] extends Decoder[T]: def start(): Unit = openEncapsulation(reader) + def start(amount: Int): Unit = openEncapsulation(reader, amount) + + def open(): Unit = + readKey(reader); + openEncapsulation(reader); + + def open(amount: Int): Unit = + readKey(reader); + openEncapsulation(reader, amount); + /** [TODO: description] */ def close(): Unit = readUntilBeforeBreak(None, From fb43e418916a9e19edc7af7ce7910a8d6084888a Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 3 Jun 2024 07:36:32 +0200 Subject: [PATCH 80/97] feat(load): Load main Scheme body --- .../main/scala/maf/persistence/load/Expression.scala | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/code/shared/src/main/scala/maf/persistence/load/Expression.scala b/code/shared/src/main/scala/maf/persistence/load/Expression.scala index dc8749cda..1a7efb623 100644 --- a/code/shared/src/main/scala/maf/persistence/load/Expression.scala +++ b/code/shared/src/main/scala/maf/persistence/load/Expression.scala @@ -19,6 +19,7 @@ import maf.language.scheme.SchemeAssert import io.bullet.borer.Reader import scala.collection.mutable.HashMap import io.bullet.borer.derivation.CompactMapBasedCodecs +import maf.modular.scheme.modf.BaseSchemeModFSemanticsM /** * The base trait for decoding expressions. @@ -68,8 +69,17 @@ trait LoadExpressionID[Expr <: Expression] extends Load[Expr] with LoadActualExp /** Decodes an expression using an ID */ protected given expressionIDDecoder: Decoder[Expr] +trait LoadMainSchemeBody extends BaseSchemeModFSemanticsM with LoadExpressions[SchemeExp]: + override def loadInfo: List[(String, Loadable[?])] = + super.loadInfo ++ List(("mainBody", Loadable((exp: SchemeExp) => mainBody = exp))) + trait LoadExpressionIntID[Expr <: Expression] extends LoadExpressionID[Expr]: - private val expressions: HashMap[Int, Expr] = HashMap[Int, Expr]() + private var expressions: HashMap[Int, Expr] = HashMap[Int, Expr]() + + override def startLoad(): Unit = + super.startLoad() + expressions = HashMap[Int, Expr]() + override protected given expressionSetDecoder: MapDecoder[Set[Expr]] with override def read(reader: Reader): Set[Expr] = reader.start() From 8f48aefa46b5f6e3813ac08aa260658a7b480fe3 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 3 Jun 2024 07:39:49 +0200 Subject: [PATCH 81/97] feat(load): Add more decodable lattices and seperate them into a trait --- .../scala/maf/persistence/load/Store.scala | 140 ++++++++++++------ 1 file changed, 92 insertions(+), 48 deletions(-) diff --git a/code/shared/src/main/scala/maf/persistence/load/Store.scala b/code/shared/src/main/scala/maf/persistence/load/Store.scala index e0ab14c05..257dc6a6a 100644 --- a/code/shared/src/main/scala/maf/persistence/load/Store.scala +++ b/code/shared/src/main/scala/maf/persistence/load/Store.scala @@ -16,6 +16,8 @@ import maf.language.scheme.lattices.SchemeLattice import maf.language.scheme.SchemeLambdaExp import maf.core.Address import maf.modular.scheme.modf.BaseSchemeModFSemanticsM +import maf.modular.scheme.SchemeConstantPropagationDomain +import maf.lattice.ConstantPropagation.L /** * The base trait for decoding [[AbstractDomain.Value values]]. @@ -59,29 +61,51 @@ trait LoadLattice[Expr <: Expression] extends Load[Expr]: else if reader.tryReadString("bottom") then ConstantPropagation.Bottom else return new ConstantPropagation.Constant[T](reader.read[T]()) +trait LoadModularSchemeDomainLattices extends Load[SchemeExp] with ModularSchemeDomain: + given stringLatticeDecoder: Decoder[S] + given booleanLatticeDecoder: Decoder[B] + given integerLatticeDecoder: Decoder[I] + given realLatticeDecoder: Decoder[R] + given charLatticeDecoder: Decoder[C] + given symbolLatticeDecoder: Decoder[Sym] + +trait LoadSchemeConstantPropagationDomain extends SchemeConstantPropagationDomain with LoadModularSchemeDomainLattices with LoadLattice[SchemeExp]: + override given stringLatticeDecoder: Decoder[S] = latticeDecoder[L, String].asInstanceOf[Decoder[S]] + override given booleanLatticeDecoder: Decoder[B] = latticeDecoder[L, Boolean].asInstanceOf[Decoder[B]] + override given integerLatticeDecoder: Decoder[I] = latticeDecoder[L, BigInt].asInstanceOf[Decoder[I]] + override given realLatticeDecoder: Decoder[R] = latticeDecoder[L, Double].asInstanceOf[Decoder[R]] + override given charLatticeDecoder: Decoder[C] = latticeDecoder[L, Char].asInstanceOf[Decoder[C]] + override given symbolLatticeDecoder: Decoder[Sym] = latticeDecoder[L, String].asInstanceOf[Decoder[Sym]] + /** * Trait to decode [[ModularSchemeLattice modular scheme lattices]]. * * Implementation of [[LoadModularDomain]]. */ -trait LoadModularSchemeLattices +trait LoadModularSchemeDomain extends LoadModularDomain with LoadAddr[SchemeExp] with LoadExpressions[SchemeExp] with BaseSchemeModFSemanticsM with LoadEnvironment[SchemeExp] - with LoadLattice[SchemeExp]: - type SchemeLattice = ModularSchemeLattice[?, ?, ?, ?, ?, ?, ?] + with LoadLattice[SchemeExp] + with LoadComponents[SchemeExp] + with LoadSchemeConstantPropagationDomain: + type LoadSchemeLattice = ModularSchemeLattice[?, S, B, I, R, C, Sym] override def hMapDecoders = super.hMapDecoders ++ Set( - ("int", summon[Decoder[(HMapKey, SchemeLattice#Int)]]), - ("boolean", summon[Decoder[(HMapKey, SchemeLattice#Bool)]]), - ("string", summon[Decoder[(HMapKey, SchemeLattice#Str)]]), - ("primitive", summon[Decoder[(HMapKey, SchemeLattice#Prim)]]), - ("closure", summon[Decoder[(HMapKey, SchemeLattice#Clo)]]), - ("pointer", summon[Decoder[(HMapKey, SchemeLattice#Pointer)]]), - ("symbol", summon[Decoder[(HMapKey, SchemeLattice#Symbol)]]), - ("cons", summon[Decoder[(HMapKey, SchemeLattice#Cons)]]), - ("vector", summon[Decoder[(HMapKey, SchemeLattice#Vec)]]), + ("int", summon[Decoder[(HMapKey, LoadSchemeLattice#Int)]]), + ("boolean", summon[Decoder[(HMapKey, LoadSchemeLattice#Bool)]]), + ("string", summon[Decoder[(HMapKey, LoadSchemeLattice#Str)]]), + ("real", summon[Decoder[(HMapKey, LoadSchemeLattice#Real)]]), + ("char", summon[Decoder[(HMapKey, LoadSchemeLattice#Char)]]), + ("inputPort", summon[Decoder[(HMapKey, LoadSchemeLattice#InputPort)]]), + ("kont", KDecoder), + ("primitive", summon[Decoder[(HMapKey, LoadSchemeLattice#Prim)]]), + ("closure", summon[Decoder[(HMapKey, LoadSchemeLattice#Clo)]]), + ("pointer", summon[Decoder[(HMapKey, LoadSchemeLattice#Pointer)]]), + ("symbol", summon[Decoder[(HMapKey, LoadSchemeLattice#Symbol)]]), + ("cons", summon[Decoder[(HMapKey, LoadSchemeLattice#Cons)]]), + ("vector", summon[Decoder[(HMapKey, LoadSchemeLattice#Vec)]]), ("nil", nilLatticeDecoder), ("void", summon[Decoder[(HMapKey, modularLattice.Void.type)]]), ("ERROR", errorDecoder) @@ -93,28 +117,48 @@ trait LoadModularSchemeLattices System.err.nn.println("The lattice was not correctly encoded and had error: `" + error + "`, using `nil` instead.") return (modularLattice.NilT, modularLattice.Nil) - given Decoder[(HMapKey, SchemeLattice#Int)] with - override def read(reader: Reader): (HMapKey, SchemeLattice#Int) = - val lattice = reader.read[Lattice[BigInt]]() - return (modularLattice.IntT, new modularLattice.Int(lattice.asInstanceOf[LoadModularSchemeLattices.this.modularLatticeWrapper.I])) - - given Decoder[(HMapKey, SchemeLattice#Bool)] with - override def read(reader: Reader): (HMapKey, SchemeLattice#Bool) = - val lattice = reader.read[Lattice[Boolean]]() - return (modularLattice.BoolT, new modularLattice.Bool(lattice.asInstanceOf[LoadModularSchemeLattices.this.modularLatticeWrapper.B])) - - given Decoder[(HMapKey, SchemeLattice#Str)] with - override def read(reader: Reader): (HMapKey, SchemeLattice#Str) = - val lattice = reader.read[Lattice[String]]() - return (modularLattice.StrT, new modularLattice.Str(lattice.asInstanceOf[LoadModularSchemeLattices.this.modularLatticeWrapper.S])) - - given Decoder[(HMapKey, SchemeLattice#Symbol)] with - override def read(reader: Reader): (HMapKey, SchemeLattice#Symbol) = - val lattice = reader.read[Lattice[String]]() - return (modularLattice.SymbolT, new modularLattice.Symbol(lattice.asInstanceOf[LoadModularSchemeLattices.this.modularLatticeWrapper.Sym])) - - given Decoder[(HMapKey, SchemeLattice#Prim)] with - override def read(reader: Reader): (HMapKey, SchemeLattice#Prim) = + given Decoder[(HMapKey, LoadSchemeLattice#Int)] with + override def read(reader: Reader): (HMapKey, LoadSchemeLattice#Int) = + val lattice = reader.read[I]() + return (modularLattice.IntT, new modularLattice.Int(lattice)) + + given Decoder[(HMapKey, LoadSchemeLattice#Real)] with + override def read(reader: Reader): (HMapKey, LoadSchemeLattice#Real) = + val lattice = reader.read[R]() + return (modularLattice.RealT, new modularLattice.Real(lattice)) + + given Decoder[(HMapKey, LoadSchemeLattice#Bool)] with + override def read(reader: Reader): (HMapKey, LoadSchemeLattice#Bool) = + val lattice = reader.read[B]() + return (modularLattice.BoolT, new modularLattice.Bool(lattice)) + + private given KDecoder: Decoder[(HMapKey, LoadSchemeLattice#K)] with + override def read(reader: Reader): (HMapKey, LoadSchemeLattice#K) = + val lattice = reader.read[Set[Component]]().asInstanceOf[Set[LoadSchemeLattice#K]] + return (modularLattice.KontT, new modularLattice.Kont(lattice)) + + given Decoder[(HMapKey, LoadSchemeLattice#Char)] with + override def read(reader: Reader): (HMapKey, LoadSchemeLattice#Char) = + val lattice = reader.read[C]() + return (modularLattice.CharT, new modularLattice.Char(lattice)) + + given Decoder[(HMapKey, LoadSchemeLattice#InputPort)] with + override def read(reader: Reader): (HMapKey, LoadSchemeLattice#InputPort) = + val lattice = reader.read[LoadSchemeLattice#L]() + return (modularLattice.InputPortT, new modularLattice.InputPort(lattice)) + + given Decoder[(HMapKey, LoadSchemeLattice#Str)] with + override def read(reader: Reader): (HMapKey, LoadSchemeLattice#Str) = + val lattice = reader.read[S]() + return (modularLattice.StrT, new modularLattice.Str(lattice)) + + given Decoder[(HMapKey, LoadSchemeLattice#Symbol)] with + override def read(reader: Reader): (HMapKey, LoadSchemeLattice#Symbol) = + val lattice = reader.read[Sym]() + return (modularLattice.SymbolT, new modularLattice.Symbol(lattice)) + + given Decoder[(HMapKey, LoadSchemeLattice#Prim)] with + override def read(reader: Reader): (HMapKey, LoadSchemeLattice#Prim) = return (modularLattice.PrimT, new modularLattice.Prim(reader.read[Set[String]]())) given MapDecoder[(SchemeLambdaExp, Env)] with @@ -125,8 +169,8 @@ trait LoadModularSchemeLattices reader.close() return (expression.value, address.value) - given ArrayDecoder[(HMapKey, SchemeLattice#Clo)]() with - override def read(reader: Reader): (HMapKey, SchemeLattice#Clo) = + given ArrayDecoder[(HMapKey, LoadSchemeLattice#Clo)]() with + override def read(reader: Reader): (HMapKey, LoadSchemeLattice#Clo) = reader.start() val closures = reader.readUntilBeforeBreak[Set[(SchemeLambdaExp, Env)]](Set[(SchemeLambdaExp, Env)](), (closures) => closures + (reader.read[(SchemeLambdaExp, Env)]()) @@ -134,30 +178,30 @@ trait LoadModularSchemeLattices reader.close() return (modularLattice.CloT, new modularLattice.Clo(closures)) - given ArrayDecoder[(HMapKey, SchemeLattice#Pointer)]() with - override def read(reader: Reader): (HMapKey, SchemeLattice#Pointer) = + given ArrayDecoder[(HMapKey, LoadSchemeLattice#Pointer)]() with + override def read(reader: Reader): (HMapKey, LoadSchemeLattice#Pointer) = reader.start() val pointers = reader.readUntilBeforeBreak[Set[Address]](Set(), (pointers) => pointers + (reader.read[Address]())) reader.close() return (modularLattice.PointerT, new modularLattice.Pointer(pointers)) - given MapDecoder[(HMapKey, SchemeLattice#Cons)]() with - override def read(reader: Reader): (HMapKey, SchemeLattice#Cons) = + given MapDecoder[(HMapKey, LoadSchemeLattice#Cons)]() with + override def read(reader: Reader): (HMapKey, LoadSchemeLattice#Cons) = reader.start() - val car = reader.readMember[SchemeLattice#L]("car") - val cdr = reader.readMember[SchemeLattice#L]("cdr") + val car = reader.readMember[LoadSchemeLattice#L]("car") + val cdr = reader.readMember[LoadSchemeLattice#L]("cdr") reader.close() return (modularLattice.ConsT, new modularLattice.Cons(car.value, cdr.value)) - given MapDecoder[(HMapKey, SchemeLattice#Vec)] with LoadMapToArray with - override def read(reader: Reader): (HMapKey, SchemeLattice#Vec) = + given MapDecoder[(HMapKey, LoadSchemeLattice#Vec)] with LoadMapToArray with + override def read(reader: Reader): (HMapKey, LoadSchemeLattice#Vec) = reader.start() - val size = reader.readMember[Lattice[BigInt]]("size").value.asInstanceOf[LoadModularSchemeLattices.this.modularLatticeWrapper.I] + val size = reader.readMember[I]("size").value val elements = reader - .readMember[Map[Lattice[BigInt], SchemeLattice#L]]("elements") + .readMember[Map[I, LoadSchemeLattice#L]]("elements") .value - .asInstanceOf[Map[LoadModularSchemeLattices.this.modularLatticeWrapper.I, SchemeLattice#L]] + .asInstanceOf[Map[LoadModularSchemeDomain.this.modularLatticeWrapper.I, LoadSchemeLattice#L]] reader.close() return (modularLattice.VecT, new modularLattice.Vec(size, elements)) @@ -175,7 +219,7 @@ trait LoadModularSchemeLattices * Base trait for decoding values as [[ModularSchemeLattice modular scheme lattices]], as defined in [[ModularSchemeDomain]]. * * @note - * This trait gives the methods needed to decode values, but does not implement them yet, other traits like [[LoadModularSchemeLattices]] should be + * This trait gives the methods needed to decode values, but does not implement them yet, other traits like [[LoadModularSchemeDomain]] should be * mixed in for the implementation. The trait that should be mixed in depends on the kind of values that is used in your analysis. * * @tparam Expr From 2acbeefc407ea8ce35759008c43d739d150825de Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 3 Jun 2024 07:40:52 +0200 Subject: [PATCH 82/97] fix(load): Components not loaded correctly --- .../maf/persistence/load/Component.scala | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/code/shared/src/main/scala/maf/persistence/load/Component.scala b/code/shared/src/main/scala/maf/persistence/load/Component.scala index f1c0b4aa9..91434f837 100644 --- a/code/shared/src/main/scala/maf/persistence/load/Component.scala +++ b/code/shared/src/main/scala/maf/persistence/load/Component.scala @@ -86,7 +86,11 @@ protected trait LoadActualComps[Expr <: Expression] extends LoadComponents[Expr] * Implementation of [[LoadComponents]] */ trait LoadActualComponents[Expr <: Expression] extends LoadActualComps[Expr]: - override given componentDecoder: Decoder[Component] = actualComponentDecoder + override given componentDecoder: Decoder[Component] with + override def read(reader: Reader): Component = + val component = reader.read[Component]()(using actualComponentDecoder) + if !visited.contains(component) then visited = visited + component + return component /** * Load standard scheme components @@ -99,7 +103,7 @@ trait LoadStandardSchemeComponents with LoadContext[SchemeExp] with LoadEnvironment[SchemeExp] with LoadExpressions[SchemeExp]: - override given actualComponentDecoder: Decoder[Component] with + override given actualComponentDecoder: MapDecoder[Component] with override def read(reader: Reader): Component = if reader.tryReadString("main") then return initialComponent else reader.read[SchemeModFComponent.Call[DecodeContext]]() @@ -141,7 +145,7 @@ trait LoadContext[Expr <: Expression] extends Load[Expr]: /** * Trait to decode the context for an analysis with no context. * - * This will expect 'ε' when reading context. + * This will expect 'None' when reading context. * * @tparam Expr * The type of expression used in the analysis @@ -150,7 +154,7 @@ trait LoadNoContext[Expr <: Expression] extends LoadContext[Expr]: override type DecodeContext = NoContext.type override given contextDecoder: Decoder[DecodeContext] with override def read(reader: Reader): DecodeContext = - if !reader.tryReadString("ε") then return reader.unexpectedDataItem("ε") + if !reader.tryReadString("None") then return reader.unexpectedDataItem("None") return NoContext /** @@ -228,6 +232,10 @@ trait LoadComponentID[Expr <: Expression] extends LoadActualComps[Expr] with Loa /** Map that connects component IDs to the component. */ var components = HashMap[ID, Component]() + override def startLoad(): Unit = + super.startLoad() + components = HashMap[ID, Component]() + /** * Register a loaded component, this allows you to also reference components using their ID using the [[components]] map. * @@ -260,11 +268,14 @@ trait LoadStandardSchemeComponentPosition extends LoadComponentID[SchemeExp] wit if component != initialComponent then components.addOne(component.asInstanceOf[SchemeModFComponent.Call[DecodeContext]].clo._1.idn.pos, component) - override protected given componentSetDecoder: MapDecoder[Set[Component]] with + override protected given componentSetDecoder: ArrayDecoder[Set[Component]] with override def read(reader: Reader): Set[Component] = reader.start() val components = - reader.readUntilBeforeBreak(Set[Component](), (components: Set[Component]) => components + reader.readMember[Component]().value) + reader.readUntilBeforeBreak( + Set[Component](), + (components: Set[Component]) => components + reader.readMember[Component]()(using actualComponentDecoder).value + ) reader.close() return components From fe480f45ad6fcc50c9038aaba1138cd8b2253d65 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 8 Jul 2024 08:02:04 +0200 Subject: [PATCH 83/97] perf(persistence): Add performance tests --- .../performance/PersistencePerformance.scala | 370 ++++++++++++++++++ 1 file changed, 370 insertions(+) create mode 100644 code/jvm/src/main/scala/maf/cli/experiments/performance/PersistencePerformance.scala diff --git a/code/jvm/src/main/scala/maf/cli/experiments/performance/PersistencePerformance.scala b/code/jvm/src/main/scala/maf/cli/experiments/performance/PersistencePerformance.scala new file mode 100644 index 000000000..7d3780a16 --- /dev/null +++ b/code/jvm/src/main/scala/maf/cli/experiments/performance/PersistencePerformance.scala @@ -0,0 +1,370 @@ +package maf.cli.experiments.performance + +import maf.cli.experiments.SchemeAnalyses +import maf.cli.experiments.performance.PerformanceEvaluationJMHPersistence.AnalyzedProgram +import maf.cli.experiments.performance.PerformanceEvaluationJMHPersistence.Program +import maf.cli.experiments.performance.PerformanceEvaluationJMHPersistence.ProgramPath +import maf.cli.experiments.performance.PerformanceEvaluationJMHPersistence.SavedProgram +import maf.cli.experiments.performance.PerformanceEvaluationJMHPersistence.testSizeFile +import maf.core.Expression +import maf.language.CScheme.CSchemeParser +import maf.language.scheme.SchemeExp +import maf.modular.AnalysisEntry +import maf.modular.ModAnalysis +import maf.modular.scheme.SchemeConstantPropagationDomain +import maf.modular.scheme.modf.SchemeModFNoSensitivity +import maf.modular.scheme.modf.SimpleSchemeModFAnalysis +import maf.modular.worklist.FIFOWorklistAlgorithm +import maf.save.Load +import maf.save.LoadActualComponents +import maf.save.LoadActualExpressions +import maf.save.LoadAddrDependency +import maf.save.LoadComponentIntID +import maf.save.LoadComponents +import maf.save.LoadDependency +import maf.save.LoadExpressionIntID +import maf.save.LoadFIFOWorklist +import maf.save.LoadGlobalStore +import maf.save.LoadInitialized +import maf.save.LoadModularSchemeDomain +import maf.save.LoadNoContext +import maf.save.LoadSchemeAddr +import maf.save.LoadSchemeConstantPropagationDomain +import maf.save.LoadSchemeExpressions +import maf.save.LoadStandardSchemeComponentPosition +import maf.save.LoadStandardSchemeComponents +import maf.save.LoadWorklist +import maf.save.Save +import maf.save.SaveActualComponents +import maf.save.SaveAddrDep +import maf.save.SaveComponentIntID +import maf.save.SaveComponents +import maf.save.SaveDependency +import maf.save.SaveGlobalStore +import maf.save.SaveInitialized +import maf.save.SaveModularSchemeDomain +import maf.save.SaveNoContext +import maf.save.SaveSchemeAddr +import maf.save.SaveSchemeConstantPropagationDomain +import maf.save.SaveSequentialWorklist +import maf.save.SaveStandardSchemeComponentPosition +import maf.save.SaveStandardSchemeComponents +import maf.save.SaveWorklist +import maf.save.save.SaveActualExpressions +import maf.save.save.SaveExpressionID +import maf.save.save.SaveExpressions +import maf.save.save.SaveRecursiveSchemeExpressionsIntID +import maf.save.save.SaveSchemeExpressions +import maf.save.save.SaveWorklistExpressionsID +import maf.util.Reader +import org.openjdk.jmh.annotations.* +import org.openjdk.jmh.infra.BenchmarkParams +import org.openjdk.jmh.infra.BenchmarkParamsL2 +import org.openjdk.jmh.infra.IterationParams +import org.openjdk.jmh.profile.ExternalProfiler +import org.openjdk.jmh.profile.InternalProfiler +import org.openjdk.jmh.profile.Profiler +import org.openjdk.jmh.results.AggregationPolicy +import org.openjdk.jmh.results.BenchmarkResult +import org.openjdk.jmh.results.BenchmarkResultMetaData +import org.openjdk.jmh.results.IterationResult +import org.openjdk.jmh.results.Result +import org.openjdk.jmh.results.ScalarResult +import org.openjdk.jmh.util.Multimap + +import java.io.File +import java.io.IOException +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths +import java.nio.file.StandardCopyOption +import java.util.ArrayList +import java.util.Collection +import java.util.concurrent.TimeUnit +import maf.save.save.SaveExpressionIntID +import maf.save.SaveCbor +import maf.save.LoadCbor +import maf.save.save.SaveMainSchemeBody +import maf.save.LoadMainSchemeBody + +object PersistenceEvaluation: + trait SaveEvaluation[Expr <: Expression] + extends Save[Expr] + with SaveInitialized[Expr] + with SaveComponents[Expr] + with SaveWorklist[Expr] + with SaveGlobalStore[Expr] + with SaveDependency[Expr] + with SaveAddrDep[Expr] + + trait SaveModF + extends SaveEvaluation[SchemeExp] + with SaveStandardSchemeComponents + with SaveModularSchemeDomain + with SaveSchemeConstantPropagationDomain + with SaveSchemeAddr + with SaveSchemeExpressions + with SaveNoContext[SchemeExp] + with SaveSequentialWorklist[SchemeExp] + with SaveMainSchemeBody + + trait LoadEvaluation[Expr <: Expression] + extends Load[Expr] + with LoadInitialized[Expr] + with LoadComponents[Expr] + with LoadWorklist[Expr] + with LoadGlobalStore[Expr] + with LoadDependency[Expr] + with LoadAddrDependency[Expr] + + trait LoadModF + extends LoadEvaluation[SchemeExp] + with LoadStandardSchemeComponents + with LoadModularSchemeDomain + with LoadSchemeConstantPropagationDomain + with LoadSchemeAddr + with LoadFIFOWorklist[SchemeExp] + with LoadSchemeExpressions + with LoadNoContext[SchemeExp] + with LoadMainSchemeBody + + trait SimpleModF + extends SimpleSchemeModFAnalysis + with SchemeModFNoSensitivity + with SchemeConstantPropagationDomain + with FIFOWorklistAlgorithm[SchemeExp] + with SaveModF + with LoadModF + + class SimpleModFActual(program: SchemeExp) + extends SimpleSchemeModFAnalysis(program) + with SaveActualExpressions[SchemeExp] + with LoadActualExpressions[SchemeExp] + with SaveActualComponents[SchemeExp] + with LoadActualComponents[SchemeExp] + with SimpleModF + + class SimpleModFPositionComponents(program: SchemeExp) + extends SimpleSchemeModFAnalysis(program) + with SaveActualExpressions[SchemeExp] + with LoadActualExpressions[SchemeExp] + with SaveStandardSchemeComponentPosition + with LoadStandardSchemeComponentPosition + with SimpleModF + + class SimpleModFIDComponents(program: SchemeExp) + extends SimpleSchemeModFAnalysis(program) + with SaveActualExpressions[SchemeExp] + with LoadActualExpressions[SchemeExp] + with SaveComponentIntID[SchemeExp] + with LoadComponentIntID[SchemeExp] + with SimpleModF + + class SimpleModFIDExpressions(program: SchemeExp) + extends SimpleSchemeModFAnalysis(program) + with SaveExpressionIntID[SchemeExp] + with LoadExpressionIntID[SchemeExp] + with SaveActualComponents[SchemeExp] + with LoadActualComponents[SchemeExp] + with SimpleModF + + class SimpleModFIDSchemeExpressions(program: SchemeExp, override val maxASTHeight: Int) + extends SimpleSchemeModFAnalysis(program) + with SaveRecursiveSchemeExpressionsIntID + with LoadExpressionIntID[SchemeExp] + with SaveActualComponents[SchemeExp] + with LoadActualComponents[SchemeExp] + with SimpleModF + + class SimpleModFIDs(program: SchemeExp, override val maxASTHeight: Int) + extends SimpleSchemeModFAnalysis(program) + with SaveRecursiveSchemeExpressionsIntID + with LoadExpressionIntID[SchemeExp] + with SaveComponentIntID[SchemeExp] + with LoadComponentIntID[SchemeExp] + with SimpleModF + + class SimpleModFActualCbor(program: SchemeExp) extends SimpleModFActual(program) with SaveCbor[SchemeExp] with LoadCbor[SchemeExp] + class SimpleModFPositionComponentsCbor(program: SchemeExp) + extends SimpleModFPositionComponents(program) + with SaveCbor[SchemeExp] + with LoadCbor[SchemeExp] + class SimpleModFIDComponentsCbor(program: SchemeExp) extends SimpleModFIDComponents(program) with SaveCbor[SchemeExp] with LoadCbor[SchemeExp] + class SimpleModFIDExpressionsCbor(program: SchemeExp) extends SimpleModFIDExpressions(program) with SaveCbor[SchemeExp] with LoadCbor[SchemeExp] + class SimpleModFIDSchemeExpressionsCbor(program: SchemeExp, maxASTHeight: Int) + extends SimpleModFIDSchemeExpressions(program, maxASTHeight) + with SaveCbor[SchemeExp] + with LoadCbor[SchemeExp] + class SimpleModFIDsCbor(program: SchemeExp, maxASTHeight: Int) + extends SimpleModFIDs(program, maxASTHeight) + with SaveCbor[SchemeExp] + with LoadCbor[SchemeExp] + + final val simpleModFActual = "simpleModFActual" + final val simpleModFPositionComponents = "simpleModFPositionComponents" + final val simpleModFIDComponents = "simpleModFIDComponents" + final val simpleModFIDExpressions = "simpleModFIDExpressions" + final val simpleModFIDSchemeExpressions = "simpleModFIDSchemeExpressions" + final val simpleModFIDs = "simpleModFIDs" + +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@BenchmarkMode(Array(Mode.AverageTime)) +class PerformanceEvaluationJMHPersistence: + @Benchmark + def analyze(program: Program): ModAnalysis[SchemeExp] = + program.analysis.analyze() + return program.analysis + + @Benchmark + def save(program: AnalyzedProgram): Unit = program.analyzedProgram.save(program.saveFile.toString()) + + @Benchmark + def load(program: SavedProgram): ModAnalysis[SchemeExp] = + program.loadAnalysis.load(program.saveFile.toString()) + return program.loadAnalysis + +object PerformanceEvaluationJMHPersistence: + // Use a static path to ensure that this file can be accessed throughout different JVM forks, this is necessary to allow it to be read by the profiler + final val testSizeFile = + Paths.get(System.getProperty("java.io.tmpdir").asInstanceOf[String] + "/testMAFPersistenceFileSize.json").asInstanceOf[Path] + @State(Scope.Benchmark) + class ProgramPath: + @Param( + Array( + "test/R5RS/ad/bst.scm", + "test/R5RS/ad/queue.scm", + "test/R5RS/ad/linear.scm", + "test/R5RS/various/infinite-2.scm", + "test/R5RS/various/mceval.scm", + "test/R5RS/icp/icp_3_leval.scm", + "test/R5RS/gambit/sboyer.scm", + "test/R5RS/gambit/scheme.scm", + ) + ) + var program: String = _ + @Param( + Array( + PersistenceEvaluation.simpleModFActual, + PersistenceEvaluation.simpleModFPositionComponents, + PersistenceEvaluation.simpleModFIDComponents, + PersistenceEvaluation.simpleModFIDExpressions, + PersistenceEvaluation.simpleModFIDs, + PersistenceEvaluation.simpleModFIDSchemeExpressions + ) + ) + var runAnalysis: String = _ + @Param(Array("0", "1", "2", "3", "5")) + var maxASTHeight: Int = _ + @Param(Array("true", "false")) + var cbor: Boolean = _ + + @Setup(Level.Trial) + def skipMaxASTHeight: Unit = + if maxASTHeight != 0 && !(runAnalysis == PersistenceEvaluation.simpleModFIDSchemeExpressions || runAnalysis == PersistenceEvaluation.simpleModFIDs) then + System.exit(0) + + def getAnalysis( + program: SchemeExp, + analysis: String, + maxADTHeight: Int = maxASTHeight + ): ModAnalysis[SchemeExp] = + runAnalysis match { + case PersistenceEvaluation.simpleModFActual => + if cbor then return new PersistenceEvaluation.SimpleModFActualCbor(program) + else return new PersistenceEvaluation.SimpleModFActual(program) + case PersistenceEvaluation.simpleModFPositionComponents => + return if cbor then new PersistenceEvaluation.SimpleModFPositionComponentsCbor(program) + else new PersistenceEvaluation.SimpleModFPositionComponents(program) + case PersistenceEvaluation.simpleModFIDComponents => + return if cbor then new PersistenceEvaluation.SimpleModFIDComponentsCbor(program) + else new PersistenceEvaluation.SimpleModFIDComponents(program) + case PersistenceEvaluation.simpleModFIDExpressions => + return if cbor then new PersistenceEvaluation.SimpleModFIDExpressionsCbor(program) + else new PersistenceEvaluation.SimpleModFIDExpressions(program) + case PersistenceEvaluation.simpleModFIDSchemeExpressions => + return if cbor then new PersistenceEvaluation.SimpleModFIDSchemeExpressionsCbor(program, maxADTHeight) + else new PersistenceEvaluation.SimpleModFIDSchemeExpressions(program, maxADTHeight) + case PersistenceEvaluation.simpleModFIDs => + return if cbor then new PersistenceEvaluation.SimpleModFIDsCbor(program, maxADTHeight) + else new PersistenceEvaluation.SimpleModFIDs(program, maxADTHeight) + } + + protected def getExpression(file: String): SchemeExp = return CSchemeParser.parseProgram(Reader.loadFile(s"../../$file")) + protected def newSaveFile: Path = + val saveFile = Files.createTempFile("maf", ".json") + if saveFile == null then return throw IOException("Could not create new temporary file.") + else return saveFile + + @State(Scope.Benchmark) + class Program extends ProgramPath: + var analysis: ModAnalysis[SchemeExp] = _ + + @Setup(Level.Invocation) + def loadProgram: Unit = + // Only run the analysis once for each program, since this does not change based on how it would be saved + if runAnalysis != PersistenceEvaluation.simpleModFActual || cbor then System.exit(0) + analysis = getAnalysis(getExpression(program), runAnalysis) + + @State(Scope.Benchmark) + class AnalyzedProgram extends ProgramPath: + var analyzedProgram: ModAnalysis[SchemeExp] = _ + var saveFile: Path = _ + + @Setup(Level.Invocation) + def createSaveFile: Unit = saveFile = newSaveFile + + @TearDown(Level.Invocation) + def removeSaveFile: Unit = Files.move(saveFile, testSizeFile, StandardCopyOption.REPLACE_EXISTING) + + @Setup(Level.Trial) + def analyzeProgram: Unit = + analyzedProgram = getAnalysis(getExpression(program), runAnalysis) + analyzedProgram.analyze() + + @State(Scope.Benchmark) + class SavedProgram extends ProgramPath: + var expression: SchemeExp = _ + var analyzedProgram: ModAnalysis[SchemeExp] = _ + var saveFile: Path = _ + var loadAnalysis: ModAnalysis[SchemeExp] = _ + + @TearDown(Level.Trial) + def removeSaveFile: Unit = Files.deleteIfExists(saveFile) + + @Setup(Level.Trial) + def loadProgram: Unit = + expression = getExpression(program) + loadAnalysis = getAnalysis(expression, runAnalysis) + analyzedProgram = getAnalysis(expression, runAnalysis) + analyzedProgram.analyze() + saveFile = newSaveFile + analyzedProgram.save(saveFile.toString()) + +/** + * This is a profile to be used by JMH, and can be used if you add `-prof maf.cli.experiments.performance.MaxMemoryProfiler` to the JMH command. This + * will add a profiler that looks at the file at `PerformanceEvaluationJMHPersistence.testSizeFile` and will report the generated file size in the JMH + * report. This is used in order to test how large a file is after a benchmark is run for the persistence benchmarks. + */ +class MaxMemoryProfiler extends ExternalProfiler: + override def addJVMInvokeOptions(params: BenchmarkParams): Collection[String] = return new ArrayList() + override def addJVMOptions(params: BenchmarkParams): Collection[String] = return new ArrayList() + override def allowPrintErr(): Boolean = true + override def allowPrintOut(): Boolean = true + override def beforeTrial(params: BenchmarkParams): Unit = + params.getForks() + return + override def getDescription(): String = return "Get the generated file size" + override def afterTrial(br: BenchmarkResult, pid: Long, stdOut: File, stdErr: File): Collection[_ <: Result[_]] = + val results: Collection[ScalarResult] = new ArrayList(); + if Files.exists(PerformanceEvaluationJMHPersistence.testSizeFile) then + results.add( + new ScalarResult("Saved file size", + Files.size(PerformanceEvaluationJMHPersistence.testSizeFile).toDouble / 1000, + "kB", + AggregationPolicy.MAX + ) + ); + Files.deleteIfExists(PerformanceEvaluationJMHPersistence.testSizeFile) + return results + Files.deleteIfExists(PerformanceEvaluationJMHPersistence.testSizeFile) + return results From c9697db5b355056413c0b84566bb90b8bb7c6ae6 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 8 Jul 2024 08:07:41 +0200 Subject: [PATCH 84/97] fix: Scheme assert has incorrect height --- code/shared/src/main/scala/maf/language/scheme/SchemeExp.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/shared/src/main/scala/maf/language/scheme/SchemeExp.scala b/code/shared/src/main/scala/maf/language/scheme/SchemeExp.scala index a43f886ee..34af2f2a9 100644 --- a/code/shared/src/main/scala/maf/language/scheme/SchemeExp.scala +++ b/code/shared/src/main/scala/maf/language/scheme/SchemeExp.scala @@ -379,7 +379,7 @@ sealed trait SchemeLettishExp extends SchemeExp: val body: List[SchemeExp] val idn: Identity override type T <: SchemeLettishExp - override val height: Int = 1 + bindings.foldLeft(0)((mx, b) => mx.max(b._2.height).max(body.foldLeft(0)((mx, e) => mx.max(e.height)))) + override val height: Int = 1 + bindings.foldLeft(0)((mx, b) => mx.max(b._2.height)).max(body.foldLeft(0)((mx, e) => mx.max(e.height))) def subexpressions: List[Expression] = bindings.foldLeft(List[Expression]())((a, b) => b._2 :: b._1 :: a) ::: body override def isomorphic(other: Expression): Boolean = super.isomorphic(other) && body.length == other.asInstanceOf[SchemeLettishExp].body.length override def eql(other: Expression): Boolean = super.eql(other) && body.length == other.asInstanceOf[SchemeLettishExp].body.length @@ -1048,6 +1048,7 @@ case class SchemeAssert(exp: SchemeExp, idn: Identity) extends SchemeExp: override def toString: String = s"(assert $exp)" def fv: Set[String] = exp.fv val label: Label = ASS + override val height: Int = 1 + exp.height override def deepDropIdentifier(id: Identifier): Option[SchemeExp] = exp.deepDropIdentifier(id) match From 4a0c3b01fcbe1cc5ae8bd6186bcf2905fa98aa24 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 8 Jul 2024 08:10:15 +0200 Subject: [PATCH 85/97] refactor(load): Remove unnecessary abstraction --- .../scala/maf/persistence/load/Analysis.scala | 14 +- .../maf/persistence/load/Component.scala | 20 +- .../scala/maf/persistence/load/Decoder.scala | 301 ++---------------- .../maf/persistence/load/Dependency.scala | 22 +- .../maf/persistence/load/Expression.scala | 41 +-- .../scala/maf/persistence/load/Store.scala | 19 +- .../scala/maf/persistence/load/Util.scala | 2 +- 7 files changed, 90 insertions(+), 329 deletions(-) diff --git a/code/shared/src/main/scala/maf/persistence/load/Analysis.scala b/code/shared/src/main/scala/maf/persistence/load/Analysis.scala index d7cacfa50..d30b40d77 100644 --- a/code/shared/src/main/scala/maf/persistence/load/Analysis.scala +++ b/code/shared/src/main/scala/maf/persistence/load/Analysis.scala @@ -48,13 +48,10 @@ trait Load[Expr <: Expression] extends AnalysisEntry[Expr]: protected given excludedAnalysisDecoder: MapDecoder[Load[Expr]] with override def read(reader: Reader): Load[Expr] = reader.start() - var loadResults = ListBuffer[() => Unit]() for (key, value) <- loadInfo do if loadSet.contains(key) then val result = reader.readMember(key)(using value.decoder) - if result.hasValue then value.load(result.value) - else loadResults = loadResults.addOne(() => value.load(result.value)) - for loadRes <- loadResults do loadRes() + value.load(result) reader.close() return Load.this @@ -107,12 +104,15 @@ trait LoadInitialized[Expr <: Expression] extends ModAnalysis[Expr] with Load[Ex /** The trait used to load the modF analysis. */ trait LoadModF - extends LoadCbor[SchemeExp] + extends Load[SchemeExp] with LoadInitialized[SchemeExp] - with LoadExpressionIntID[SchemeExp] with LoadSchemeExpressions + // with LoadExpressionIntID[SchemeExp] + with LoadActualExpressions[SchemeExp] + with LoadMainSchemeBody with LoadComponents[SchemeExp] - with LoadComponentIntID[SchemeExp] + // with LoadComponentIntID[SchemeExp] + with LoadActualComponents[SchemeExp] with LoadStandardSchemeComponents with LoadNoContext[SchemeExp] with LoadSchemeAddr diff --git a/code/shared/src/main/scala/maf/persistence/load/Component.scala b/code/shared/src/main/scala/maf/persistence/load/Component.scala index 91434f837..768bf30ae 100644 --- a/code/shared/src/main/scala/maf/persistence/load/Component.scala +++ b/code/shared/src/main/scala/maf/persistence/load/Component.scala @@ -45,6 +45,7 @@ import scala.collection.immutable.Queue import maf.core.worklist.FIFOWorkList import maf.modular.ModAnalysis import io.bullet.borer.derivation.CompactMapBasedCodecs +import java.{util => ju} /** * The base trait for decoding components. @@ -111,11 +112,11 @@ trait LoadStandardSchemeComponents given MapDecoder[SchemeModFComponent.Call[DecodeContext]] with override def read(reader: Reader): SchemeModFComponent.Call[DecodeContext] = reader.start() - val lambda = reader.readMember[SchemeExp]("lambda").asInstanceOf[ReadValue[String, SchemeLambdaExp]] + val lambda = reader.readMember[SchemeExp]("lambda").asInstanceOf[SchemeLambdaExp] val environment = reader.readMember[Environment[Address]]("environment") val context = reader.readMember[DecodeContext]("context") reader.close() - return new SchemeModFComponent.Call[DecodeContext]((lambda.value, environment.value), context.value) + return new SchemeModFComponent.Call[DecodeContext]((lambda, environment), context) /** * The base trait for decoding context. @@ -192,11 +193,10 @@ trait LoadEnvironment[Expr <: Expression] extends Load[Expr] with LoadAddr[Expr] reader.start() val value = reader .readMembers[Environment[T]]( - Array(("basicEnvironment", summon[Decoder[BasicEnvironment[T]]]), ("nestedEnvironment", summon[Decoder[NestedEnv[T, T]]])) + Map("basicEnvironment" -> summon[Decoder[BasicEnvironment[T]]], "nestedEnvironment" -> summon[Decoder[NestedEnv[T, T]]]) ) - .value reader.close() - return value + return value._2 given [T <: Address]: Decoder[BasicEnvironment[T]] with override def read(reader: Reader): BasicEnvironment[T] = return new BasicEnvironment( @@ -209,7 +209,7 @@ trait LoadEnvironment[Expr <: Expression] extends Load[Expr] with LoadAddr[Expr] val content = reader.readMember[Map[String, Address]]("content") val rst = reader.readOptionMember[Address]("rst") reader.close() - return new NestedEnv(content.value.asInstanceOf[Map[String, T]], rst.value.asInstanceOf[Option[K]]) + return new NestedEnv(content.asInstanceOf[Map[String, T]], rst.asInstanceOf[Option[K]]) /** * The base trait for decoding components only by their ID. @@ -274,7 +274,7 @@ trait LoadStandardSchemeComponentPosition extends LoadComponentID[SchemeExp] wit val components = reader.readUntilBeforeBreak( Set[Component](), - (components: Set[Component]) => components + reader.readMember[Component]()(using actualComponentDecoder).value + (components: Set[Component]) => components + reader.readMember[Component]()(using actualComponentDecoder)._2 ) reader.close() return components @@ -316,9 +316,9 @@ trait LoadComponentIntID[Expr <: Expression] extends LoadComponentID[Expr]: Set[Component](), (components: Set[Component]) => val component = reader.readMember[Component]()(using componentDecoder) - val key = component.key.toInt - LoadComponentIntID.this.components.addOne((key, component.value)) - components + (component.value) + val key = component._1.toInt + LoadComponentIntID.this.components.addOne((key, component._2)) + components + (component._2) ) reader.close() return components diff --git a/code/shared/src/main/scala/maf/persistence/load/Decoder.scala b/code/shared/src/main/scala/maf/persistence/load/Decoder.scala index 2e434d25e..ba28c7e6a 100644 --- a/code/shared/src/main/scala/maf/persistence/load/Decoder.scala +++ b/code/shared/src/main/scala/maf/persistence/load/Decoder.scala @@ -12,135 +12,6 @@ import scala.util.{Failure, Success} import scala.concurrent.ExecutionContext.Implicits.global import scala.reflect.ClassTag -/** - * A class representing a decoded key-value pair. - * - * This is a class that holds a key-value pair after decoding it, but because it it possible that the key that is should be decoded hasn't been - * encountered yet, it is possible that either the key or the value don't have a value yet. To check if they have a value you can use [[hasKey]] and - * [[hasValue]], if you try to retrieve the key or the value without one being present, an error will be thrown. - * - * @constructor - * Create a new key-value pair that doesn't have a key nor a value yet. - * - * @tparam K - * The type of the key - * @tparam V - * The type of the value - */ -class ReadValue[K, V](private val forceRead: (key: Option[K]) => Unit): - protected var _key: Option[K] = None - protected var _value: Option[V] = None - protected var _updateValue: Option[ReadValue[K, V]] = None - protected var _forceRead: Option[(key: Option[K]) => Unit] = Some(forceRead) - - /** - * Create a new key-value pair with a key and a value. - * - * @param _key - * The key of the key-value pair - * @param _value - * The value of the key-value pair - */ - def this(_key: K, _value: V) = - this((key: Option[K]) => ()) - this._key = Some(_key) - this._value = Some(_value) - - /** - * Create a new key-value pair with a key. - * - * @param _key - * The key of the key-value pair - */ - def this(_key: K, forceRead: (key: Option[K]) => Unit) = - this(forceRead) - this._key = Some(_key) - - /** Check if this has a value. */ - def hasValue: Boolean = _value.isDefined - - /** - * Returns the value, or tries to find if if there is none yet. - * - * @note - * If there is no value yet, it will first try to find it anyway, discarding any other values that haven't been read yet. If a value can still - * not be found, an error will be thrown. - * - * @throws NoSuchElementException - * If there is no value - */ - def value: V = - if _value.isEmpty && _forceRead.isDefined then - if _key.isDefined then _forceRead.get(Some(_key.get)) - else _forceRead.get(None) - if _value.isEmpty then - if _key.isDefined then throw new NoSuchElementException(s"The key '${key}' does not have a value.") - else throw new NoSuchElementException("This element does not have a value.") - _value.get - - /** Set the value. */ - def value_=(newValue: V): Unit = - _value = Some(newValue) - if _updateValue.isDefined then _updateValue.get.value = newValue - - /** Check if this has a key. */ - def hasKey: Boolean = _key.isDefined - - /** - * Returns the key, or tries to find it if there is no key yet. - * - * @note - * If there is no key yet, it will first try to find it anyway, discarding any other key-value pairs that haven't been read yet. If a key can - * still not be found, an error will be thrown. - * - * @throws NoSuchElementException - * If there is no key - */ - def key: K = - if _key.isEmpty && _forceRead.isDefined then _forceRead.get(None) - if _key.isEmpty then throw new NoSuchElementException("This element does not have a key.") - _key.get - - /** Set the key. */ - def key_=(newKey: K): Unit = - _key = Some(newKey) - if _updateValue.isDefined then _updateValue.get.key = newKey - - /** Set the [[ReadValue]] that should have be updated if this [[ReadValue]] gets a key or a value */ - def updateValue_=(newUpdateValue: ReadValue[K, V]) = _updateValue = Some(newUpdateValue) - - /** The [[ReadValue]] that will be updated when this [[ReadValue]] gets a key or a value. */ - def updateValue = _updateValue - -class ReadOptionValue[K, V](private val forceRead: (key: Option[K]) => Unit) extends ReadValue[K, Option[V]](forceRead): - /** - * Create a new key-value pair with a key and a value. - * - * @param _key - * The key of the key-value pair - * @param _value - * The value of the key-value pair - */ - def this(_key: K, _value: Option[V]) = - this((key: Option[K]) => ()) - this._key = Some(_key) - this._value = Some(_value) - - /** - * Create a new key-value pair with a key. - * - * @param _key - * The key of the key-value pair - */ - def this(_key: K, forceRead: (key: Option[K]) => Unit) = - this(forceRead) - this._key = Some(_key) - - override def value: Option[V] = - if _value.isEmpty then return None - return _value.get - override def hasValue: Boolean = true - /** * Base trait for an decoder. * @@ -221,7 +92,7 @@ trait AbstractDecoder[T] extends Decoder[T]: * @throws IllegalStateException * When you call this before calling [[readKey]] */ - def readValue[T: Decoder](reader: Reader): ReadValue[String, T] + def readValue[T: Decoder](reader: Reader): T = reader.read[T]() /** * Opens either a new map or a new array, based on the decoder that is used. @@ -265,18 +136,6 @@ trait AbstractDecoder[T] extends Decoder[T]: */ def closeEncapsulation[T](reader: Reader): Unit - /** - * Read a key-value pair. - * - * This will read until it finds the specified key, discarding any other elements that it cannot decode. - * - * @param reader - * The reader used read - * @param key - * The key to find - */ - def forceReadValue(reader: Reader, key: String): Unit - /** * Read the first element that you can, discarding any other elements. * @@ -286,15 +145,8 @@ trait AbstractDecoder[T] extends Decoder[T]: def forceReadValue(reader: Reader): Unit /** [TODO: description] */ - def decodeOption[T: Decoder](reader: Reader): Option[T] + def decodeOption[T: Decoder](reader: Reader, key: String): Option[T] - given [T: Decoder]: Decoder[Option[T]] with - override def read(reader: Reader): Option[T] = - return decodeOption[T](reader) - - def forceRead(reader: Reader, key: Option[String]) = - if key.isDefined then forceReadValue(reader, key.get) - else forceReadValue(reader) extension (reader: Reader) /** * Read a key-value pair. @@ -312,7 +164,7 @@ trait AbstractDecoder[T] extends Decoder[T]: * The key-value pair that is read, if the key cannot be read yet because it appears later in the file, the value will be empty and will be * filled in later. */ - def readMember[T: Decoder](key: String): ReadValue[String, T] = + def readMember[T: Decoder](key: String): T = readKey(reader, key) readValue[T](reader) @@ -331,18 +183,12 @@ trait AbstractDecoder[T] extends Decoder[T]: * @returns * The key-value pair that is read. */ - def readMember[T: Decoder](): ReadValue[String, T] = - readKey(reader) - readValue[T](reader) + def readMember[T: Decoder](): (String, T) = + val key = readKey(reader) + (key, readValue[T](reader)) - def readOptionMember[T](key: String)(using Decoder[T]): ReadValue[String, Option[T]] = - readKey(reader, key) - val value = readValue[Option[T]](reader) - val optionValue = - if value.hasValue then new ReadOptionValue[String, T](value.key, value.value) - else new ReadOptionValue[String, T](value.key, forceRead(reader, _)) - value.updateValue = optionValue; - return optionValue + def readOptionMember[T](key: String)(using Decoder[T]): Option[T] = + return decodeOption[T](reader, key) /** * Read one of the given key-value pairs. @@ -363,14 +209,10 @@ trait AbstractDecoder[T] extends Decoder[T]: * The key-value pair that is read, if none of the keys can be read because it appears later in the file, the key and the value will be * empty and will be filled in later. */ - def readMembers[T](keys: Array[(String, Decoder[_ <: T])]): ReadValue[String, T] = - val res = new ReadValue[String, T](forceRead(reader, _)) - for (key, valueDecoder) <- keys do - readKey(reader, key) - val value = readValue[T](reader)(using valueDecoder.asInstanceOf[Decoder[T]]) - if value.hasValue then return value - value.updateValue = res - return res + def readMembers[T](keys: Map[String, Decoder[_ <: T]]): (String, T) = + var key = readKey(reader) + if !keys.contains(key) then reader.unexpectedDataItem("", key) + return (key, reader.read[T]()(using keys.get(key).get.asInstanceOf[Decoder[T]])) /** * Read until a map/array end is found and accumulate the result without reading the actual map/array end. @@ -432,91 +274,20 @@ trait AbstractDecoder[T] extends Decoder[T]: * The type to encode */ trait MapDecoder[T] extends AbstractDecoder[T]: - /** - * Stores a key-value pair and a decoder that can be used to decode this value. - * - * This is used to store an decoder for later if the key cannot be read yet and keep the same type for the decoder and the value. - * - * @param value - * The key-value pair - * @param decoder - * The decoder - */ - protected case class ValueDecoder[T](value: ReadValue[String, T], decoder: Decoder[T]) - - /** [TODO: description] */ - protected class DecoderInfo: - val keys: HashMap[String, ValueDecoder[_]] = new HashMap[String, ValueDecoder[_]]() - var currentKey: Option[String] = None - var decodeKey: Option[String] = None - var previous: DecoderInfo = this - - protected var decoderInfo = new DecoderInfo() - - /** Read the next key for which the value should be decoded, if there isn't currently a value being decoded */ - protected def readDecodeKey(reader: Reader): Unit = - if decoderInfo.decodeKey.isEmpty && reader.hasString then decoderInfo.decodeKey = Some(reader.readString()) - override def readKey(reader: Reader): String = - readDecodeKey(reader) - decoderInfo.currentKey = decoderInfo.decodeKey - return if decoderInfo.decodeKey.isDefined then decoderInfo.decodeKey.get else "" + override def readKey(reader: Reader): String = return reader.readString() override def readKey(reader: Reader, key: String): String = - readDecodeKey(reader) - decoderInfo.currentKey = Some(key) - return key - override def readValue[T: Decoder](reader: Reader): ReadValue[String, T] = - if decoderInfo.currentKey.isEmpty then throw new IllegalStateException("Trying to read a value before reading a key.") - - if decoderInfo.decodeKey == decoderInfo.currentKey then - val key = decoderInfo.decodeKey.get - decoderInfo.decodeKey = None - decoderInfo.currentKey = None - val res = reader.read[T]() - if reader.hasString then decoderInfo.decodeKey = Some(reader.readString()) - if decoderInfo.decodeKey.isDefined && decoderInfo.keys.contains(decoderInfo.decodeKey.get) then - decoderInfo.currentKey = decoderInfo.decodeKey - val valueDecoder = decoderInfo.keys.get(decoderInfo.currentKey.get).get - readValue(reader)(using valueDecoder.decoder) - decoderInfo.currentKey = None - if decoderInfo.keys.contains(key) then - val valueDecoder = decoderInfo.keys.remove(key).get.asInstanceOf[ValueDecoder[T]] - valueDecoder.value.value = res - return valueDecoder.value - else return new ReadValue[String, T](key, res) - else - val readValue = new ReadValue[String, T](decoderInfo.currentKey.get, forceRead(reader, _)) - decoderInfo.keys.addOne((decoderInfo.currentKey.get, ValueDecoder(readValue, summon[Decoder[T]]))) - decoderInfo.currentKey = None - return readValue - override def openEncapsulation(reader: Reader): Unit = - val info = new DecoderInfo() - info.previous = decoderInfo - decoderInfo = info - reader.readMapStart() - override def openEncapsulation(reader: Reader, amount: Int): Unit = - val info = new DecoderInfo() - info.previous = decoderInfo - decoderInfo = info - reader.readMapOpen(amount) - override def closeEncapsulation[T](reader: Reader): Unit = - decoderInfo = decoderInfo.previous - reader.readBreak() - override def forceReadValue(reader: Reader, key: String): Unit = - while decoderInfo.decodeKey.isDefined do forceReadValue(reader) + val read = reader.readString() + if !read.equals(key) then reader.unexpectedDataItem(key, read) + return read + override def openEncapsulation(reader: Reader): Unit = reader.readMapStart() + override def openEncapsulation(reader: Reader, amount: Int): Unit = reader.readMapOpen(amount) + override def closeEncapsulation[T](reader: Reader): Unit = reader.readBreak() override def forceReadValue(reader: Reader): Unit = - val tmpCurrentKey = decoderInfo.currentKey - readDecodeKey(reader) - while decoderInfo.decodeKey.isDefined && !decoderInfo.keys.contains(decoderInfo.decodeKey.get) do - reader.skipElement() - decoderInfo.decodeKey = None - readDecodeKey(reader) - if !decoderInfo.decodeKey.isDefined then return - val valueDecoder = decoderInfo.keys.get(decoderInfo.decodeKey.get).get - decoderInfo.currentKey = decoderInfo.decodeKey - readValue(reader)(using valueDecoder.decoder) - decoderInfo.currentKey = tmpCurrentKey - override def decodeOption[T: Decoder](reader: Reader): Option[T] = - return Some(reader.read[T]()) + reader.skipElement() + reader.skipElement() + override def decodeOption[T: Decoder](reader: Reader, key: String): Option[T] = + if reader.tryReadString(key) then return Some(reader.read[T]()) + else return None /** * Decoder that uses arrays to decode values. @@ -539,29 +310,19 @@ trait MapDecoder[T] extends AbstractDecoder[T]: trait ArrayDecoder[T] extends AbstractDecoder[T]: /** Used to generate IDs if no key is provided, this is used to store the values. */ protected var id = -1 - protected var currentKey: Option[String] = None override def readKey(reader: Reader): String = return "" - override def readKey(reader: Reader, key: String): String = - currentKey = Some(key) - return key - override def readValue[T: Decoder](reader: Reader): ReadValue[String, T] = - val key = if currentKey.isDefined then currentKey.get else id.toString() - currentKey = None - if reader.hasBreak then throw reader.unexpectedDataItem(key) - id += 1 - val res = reader.read[T]() - return ReadValue[String, T](key, res) + override def readKey(reader: Reader, key: String): String = return key override def openEncapsulation(reader: Reader): Unit = reader.readArrayStart() override def openEncapsulation(reader: Reader, amount: Int): Unit = reader.readArrayOpen(amount) override def closeEncapsulation[T](reader: Reader): Unit = reader.readBreak() - override def forceReadValue(reader: Reader, key: String): Unit = reader.skipElement() - override def forceReadValue(reader: Reader): Unit = reader.skipElement() - override def decodeOption[T: Decoder](reader: Reader): Option[T] = + override def decodeOption[T: Decoder](reader: Reader, key: String): Option[T] = var res: Option[T] = None reader.readArrayOpen(1) if !reader.hasBreak then res = Some(reader.read[T]()) reader.readBreak() return res + override def forceReadValue(reader: Reader): Unit = + reader.skipElement() /** * Decoder that uses arrays to decode values, but preserves keys. @@ -635,12 +396,12 @@ trait ArrayKeyDecoder[T] extends MapDecoder[T]: * @throws IllegalStateException * When you call this before calling [[readKey]] */ - def readKeyValue[V, T: Decoder](reader: Reader): ReadValue[V, T] = + def readKeyValue[V, T: Decoder](reader: Reader): (V, T) = if key.isEmpty then throw new IllegalStateException(s"Trying to read a value before reading a key.") val res = reader.read[T]() val tmpKey = key key = None - return ReadValue(tmpKey.get.asInstanceOf[V], res) + return (tmpKey.get.asInstanceOf[V], res) override def openEncapsulation(reader: Reader): Unit = reader.readArrayStart() override def openEncapsulation(reader: Reader, amount: Int): Unit = reader.readArrayOpen(amount) override def closeEncapsulation[T](reader: Reader): Unit = reader.readBreak() @@ -658,6 +419,6 @@ trait ArrayKeyDecoder[T] extends MapDecoder[T]: * @tparam U * The type of the value that should be read, this type should have an decoder */ - def readMember[K: Decoder, V: Decoder](): ReadValue[K, V] = + def readMember[K: Decoder, V: Decoder](): (K, V) = readKey[K](reader) readKeyValue[K, V](reader) diff --git a/code/shared/src/main/scala/maf/persistence/load/Dependency.scala b/code/shared/src/main/scala/maf/persistence/load/Dependency.scala index 6dc35873e..09e6e77d7 100644 --- a/code/shared/src/main/scala/maf/persistence/load/Dependency.scala +++ b/code/shared/src/main/scala/maf/persistence/load/Dependency.scala @@ -51,9 +51,9 @@ trait LoadDependency[Expr <: Expression] extends LoadMapToArray with LoadCompone given MapDecoder[Dependency] with override def read(reader: Reader): Dependency = reader.start() - val dependency = reader.readMembers(dependencyDecoders.toArray).value + val dependency = reader.readMembers(dependencyDecoders.toMap) reader.close() - return dependency + return dependency._2 /** * The base trait for decoding addresses. @@ -67,12 +67,12 @@ trait LoadDependency[Expr <: Expression] extends LoadMapToArray with LoadCompone */ trait LoadAddr[Expr <: Expression] extends Load[Expr] with LoadPosition[Expr]: /** Returns a map that links a key to a specific decoder. */ - def addressDecoders = List[(String, Decoder[_ <: Address])]() + def addressDecoders = Set[(String, Decoder[_ <: Address])]() given MapDecoder[Address] with override def read(reader: Reader): Address = reader.start() - val address = reader.readMembers(addressDecoders.toArray).value + val address = reader.readMembers(addressDecoders.toMap)._2 reader.close() return address @@ -83,7 +83,7 @@ trait LoadAddr[Expr <: Expression] extends Load[Expr] with LoadPosition[Expr]: */ trait LoadSchemeAddr extends LoadAddr[SchemeExp] with LoadContext[SchemeExp] with LoadComponents[SchemeExp] with LoadExpressions[SchemeExp]: override def addressDecoders = - super.addressDecoders ++ List( + super.addressDecoders ++ Set( ("varAddr", summon[Decoder[VarAddr[DecodeContext]]]), ("prmAddr", summon[Decoder[PrmAddr]]), ("returnAddr", summon[Decoder[ReturnAddr[Component]]]), @@ -93,10 +93,10 @@ trait LoadSchemeAddr extends LoadAddr[SchemeExp] with LoadContext[SchemeExp] wit given MapDecoder[ReturnAddr[Component]] with override def read(reader: Reader): ReturnAddr[Component] = reader.start() - val component = reader.readMember[Component]("component") val identity = reader.readMember[Identity]("identity") + val component = reader.readMember[Component]("component") reader.close() - return new ReturnAddr[Component](component.value, identity.value) + return new ReturnAddr[Component](component, identity) given MapDecoder[VarAddr[DecodeContext]] with override def read(reader: Reader): VarAddr[DecodeContext] = @@ -105,8 +105,8 @@ trait LoadSchemeAddr extends LoadAddr[SchemeExp] with LoadContext[SchemeExp] wit val context = reader.readOptionMember[DecodeContext]("context") reader.close() return new VarAddr[DecodeContext]( - name.value, - context.value.asInstanceOf[DecodeContext] + name, + context.asInstanceOf[DecodeContext] ) given Decoder[PrmAddr] with @@ -119,6 +119,6 @@ trait LoadSchemeAddr extends LoadAddr[SchemeExp] with LoadContext[SchemeExp] wit val context = reader.readOptionMember[DecodeContext]("context") reader.close() return new PtrAddr[DecodeContext]( - expression.value, - context.value.asInstanceOf[DecodeContext] + expression, + context.asInstanceOf[DecodeContext] ) diff --git a/code/shared/src/main/scala/maf/persistence/load/Expression.scala b/code/shared/src/main/scala/maf/persistence/load/Expression.scala index 1a7efb623..3609abcbc 100644 --- a/code/shared/src/main/scala/maf/persistence/load/Expression.scala +++ b/code/shared/src/main/scala/maf/persistence/load/Expression.scala @@ -20,6 +20,7 @@ import io.bullet.borer.Reader import scala.collection.mutable.HashMap import io.bullet.borer.derivation.CompactMapBasedCodecs import maf.modular.scheme.modf.BaseSchemeModFSemanticsM +import maf.util.Writer.write /** * The base trait for decoding expressions. @@ -87,9 +88,9 @@ trait LoadExpressionIntID[Expr <: Expression] extends LoadExpressionID[Expr]: Set[Expr](), (expressions: Set[Expr]) => val expression = reader.readMember[Expr]()(using actualExpressionDecoder) - val key = expression.key.toInt - LoadExpressionIntID.this.expressions.addOne((key, expression.value)) - expressions + (expression.value) + val key = expression._1.toInt + LoadExpressionIntID.this.expressions.addOne((key, expression._2)) + expressions + (expression._2) ) reader.close() return expressions @@ -118,21 +119,21 @@ trait LoadSchemeExpressions extends LoadActualExprs[SchemeExp] with LoadSchemeSu override protected given actualExpressionDecoder: MapDecoder[SchemeExp] with override def read(reader: Reader): SchemeExp = reader.start() - val expression = reader.readMembers[SchemeExp]( - Array( - ("funcall", summon[Decoder[SchemeFuncall]]), - ("var", summon[Decoder[SchemeVar]]), - ("lambda", summon[Decoder[SchemeLambda]]), - ("argLambda", summon[Decoder[SchemeVarArgLambda]]), - ("value", summon[Decoder[SchemeValue]]), - ("letrec", summon[Decoder[SchemeLetrec]]), - ("assert", summon[Decoder[SchemeAssert]]), - ("let", summon[Decoder[SchemeLet]]), - ("schemeIf", summon[Decoder[SchemeIf]]), - ("set", summon[Decoder[SchemeSet]]), - ("begin", summon[Decoder[SchemeBegin]]), - ("letStar", summon[Decoder[SchemeLetStar]]), - ) - ) + val map: Map[String, Decoder[_ <: SchemeExp]] = + Map( + "funcall" -> summon[Decoder[SchemeFuncall]], + "var" -> summon[Decoder[SchemeVar]], + "lambda" -> summon[Decoder[SchemeLambda]], + "argLambda" -> summon[Decoder[SchemeVarArgLambda]], + "value" -> summon[Decoder[SchemeValue]], + "letrec" -> summon[Decoder[SchemeLetrec]], + "assert" -> summon[Decoder[SchemeAssert]], + "let" -> summon[Decoder[SchemeLet]], + "schemeIf" -> summon[Decoder[SchemeIf]], + "set" -> summon[Decoder[SchemeSet]], + "begin" -> summon[Decoder[SchemeBegin]], + "letStar" -> summon[Decoder[SchemeLetStar]], + ) + val expression = reader.readMembers[SchemeExp](map) reader.close() - return expression.value + return expression._2 diff --git a/code/shared/src/main/scala/maf/persistence/load/Store.scala b/code/shared/src/main/scala/maf/persistence/load/Store.scala index 257dc6a6a..69fcd87a8 100644 --- a/code/shared/src/main/scala/maf/persistence/load/Store.scala +++ b/code/shared/src/main/scala/maf/persistence/load/Store.scala @@ -51,9 +51,9 @@ trait LoadLattice[Expr <: Expression] extends Load[Expr]: given latticeDecoder[P[T] <: Lattice[T], T: Decoder]: MapDecoder[P[T]] with override def read(reader: Reader): P[T] = reader.start() - val lattice = reader.readMembers[P[T]](latticeDecoders.toArray.asInstanceOf[Array[(String, io.bullet.borer.Decoder[? <: P[T]])]]) + val lattice = reader.readMembers[P[T]](latticeDecoders.toMap.asInstanceOf[Map[String, io.bullet.borer.Decoder[? <: P[T]]]]) reader.close() - return lattice.value + return lattice._2 given constantLatticeDecoder[T: Decoder]: Decoder[ConstantPropagation.L[T]] with override def read(reader: Reader): ConstantPropagation.L[T] = @@ -164,10 +164,10 @@ trait LoadModularSchemeDomain given MapDecoder[(SchemeLambdaExp, Env)] with override def read(reader: Reader): (SchemeLambdaExp, Env) = reader.start() - val expression = reader.readMember[SchemeExp]("expression").asInstanceOf[ReadValue[String, SchemeLambdaExp]] + val expression = reader.readMember[SchemeExp]("expression").asInstanceOf[SchemeLambdaExp] val address = reader.readMember[Env]("address") reader.close() - return (expression.value, address.value) + return (expression, address) given ArrayDecoder[(HMapKey, LoadSchemeLattice#Clo)]() with override def read(reader: Reader): (HMapKey, LoadSchemeLattice#Clo) = @@ -191,16 +191,15 @@ trait LoadModularSchemeDomain val car = reader.readMember[LoadSchemeLattice#L]("car") val cdr = reader.readMember[LoadSchemeLattice#L]("cdr") reader.close() - return (modularLattice.ConsT, new modularLattice.Cons(car.value, cdr.value)) + return (modularLattice.ConsT, new modularLattice.Cons(car, cdr)) given MapDecoder[(HMapKey, LoadSchemeLattice#Vec)] with LoadMapToArray with override def read(reader: Reader): (HMapKey, LoadSchemeLattice#Vec) = reader.start() - val size = reader.readMember[I]("size").value + val size = reader.readMember[I]("size") val elements = reader .readMember[Map[I, LoadSchemeLattice#L]]("elements") - .value .asInstanceOf[Map[LoadModularSchemeDomain.this.modularLatticeWrapper.I, LoadSchemeLattice#L]] reader.close() return (modularLattice.VecT, new modularLattice.Vec(size, elements)) @@ -232,14 +231,14 @@ trait LoadModularDomain extends LoadValue[SchemeExp] with ModularSchemeDomain: given MapDecoder[(HMapKey, Any)] with override def read(reader: Reader): (HMapKey, Any) = reader.start() - val hmap = reader.readMembers(hMapDecoders.toArray) + val hmap = reader.readMembers(hMapDecoders.toMap) reader.close() - return hmap.value + return hmap._2 override given valueDecoder: ArrayDecoder[HMap] with override def read(reader: Reader): HMap = reader.start() - val hmap = reader.readUntilBeforeBreak[Map[HMapKey, Any]](Map(), (hMap) => hMap + reader.readMember[(HMapKey, Any)]().value) + val hmap = reader.readUntilBeforeBreak[Map[HMapKey, Any]](Map(), (hMap) => hMap + reader.readMember[(HMapKey, Any)]()._2) reader.close() return new HMap(hmap) diff --git a/code/shared/src/main/scala/maf/persistence/load/Util.scala b/code/shared/src/main/scala/maf/persistence/load/Util.scala index f9a8b5601..6f02e80ee 100644 --- a/code/shared/src/main/scala/maf/persistence/load/Util.scala +++ b/code/shared/src/main/scala/maf/persistence/load/Util.scala @@ -41,6 +41,6 @@ trait LoadMapToArray: val elements = mutable.Set[(K, V)]() while !reader.hasBreak do val res = reader.readMember[K, V]()(using keyDecoder, valueDecoder) - elements.add(res.key, res.value) + elements.add(res._1, res._2) reader.close() return elements.toMap From a5e6cfa3f2b0d0a9eb8e77e2a35da8201946e552 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 8 Jul 2024 08:33:46 +0200 Subject: [PATCH 86/97] feat(save): Add ability to save as CBOR --- .../scala/maf/persistence/save/Analysis.scala | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/code/shared/src/main/scala/maf/persistence/save/Analysis.scala b/code/shared/src/main/scala/maf/persistence/save/Analysis.scala index 801ea9d6f..cd3b956e6 100644 --- a/code/shared/src/main/scala/maf/persistence/save/Analysis.scala +++ b/code/shared/src/main/scala/maf/persistence/save/Analysis.scala @@ -11,6 +11,11 @@ import maf.save.save.SaveSchemeExpressions import maf.save.save.SaveRecursiveSchemeExpressionsIntID import maf.modular.AnalysisEntry import maf.modular.ModAnalysis +import maf.save.save.SaveWorklistExpressionsID +import io.bullet.borer.Cbor +import maf.save.save.SaveActualExpressions +import maf.save.save.SaveExpressionIntID +import maf.save.save.SaveMainSchemeBody /** * Contains info about the top-level objects that need to be saved. @@ -41,11 +46,11 @@ trait Save[Expr <: Expression] extends AnalysisEntry[Expr]: for (key, value) <- saveInfo do writer.writeMember(key, value.value)(using value.encoder) writer.close() - private var save = Set[String]() - private given excludedAnalysisEncoder: MapEncoder[Save[Expr]] with + protected var saveSet = Set[String]() + protected given excludedAnalysisEncoder: MapEncoder[Save[Expr]] with override def write(writer: Writer, value: Save[Expr]): Writer = writer.start() - for (key, value) <- saveInfo do if save.contains(key) then writer.writeMember(key, value.value)(using value.encoder) + for (key, value) <- saveInfo do if saveSet.contains(key) then writer.writeMember(key, value.value)(using value.encoder) writer.close() override def save(filename: String): Unit = @@ -54,8 +59,9 @@ trait Save[Expr <: Expression] extends AnalysisEntry[Expr]: override def save(filename: String, save: Set[String]): Unit = this.save = save + this.saveSet = save val res = Json.encode(this)(using excludedAnalysisEncoder).toByteArray - this.save = Set[String]() + this.saveSet = Set[String]() Files.write(Paths.get(filename), res) /** @@ -71,6 +77,28 @@ trait Save[Expr <: Expression] extends AnalysisEntry[Expr]: */ def saveInfo: List[(String, Savable[_])] = List(("name", Savable(analysisName))) +/** + * Trait to allow you to save analyses using CBOR instead of JSON. + * + * Implementing this allows you to save your analysis, by default it will only save the name of your analysis and you should mixin other traits like + * [[SaveComponents]] to also save components. + * + * @tparam Expr + * The type of expression used in the analysis + */ +trait SaveCbor[Expr <: Expression] extends Save[Expr]: + override def save(filename: String): Unit = + startSave() + val res = Cbor.encode(this)(using analysisEncoder).toByteArray + Files.write(Paths.get(filename), res) + + override def save(filename: String, save: Set[String]): Unit = + startSave() + this.saveSet = save + val res = Cbor.encode(this)(using excludedAnalysisEncoder).toByteArray + this.saveSet = Set[String]() + Files.write(Paths.get(filename), res) + trait SaveInitialized[Expr <: Expression] extends ModAnalysis[Expr] with Save[Expr]: override def saveInfo: List[(String, Savable[_])] = super.saveInfo ++ List(("initialized", Savable(analysisInitialized))) From a308643d90622f65f3c9b1adad4952fcd8f783f6 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Mon, 8 Jul 2024 08:35:57 +0200 Subject: [PATCH 87/97] feat(save): Add function that runs before saving --- .../src/main/scala/maf/persistence/save/Analysis.scala | 4 +++- .../main/scala/maf/persistence/save/Component.scala | 5 +++++ .../main/scala/maf/persistence/save/Expression.scala | 10 ++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/code/shared/src/main/scala/maf/persistence/save/Analysis.scala b/code/shared/src/main/scala/maf/persistence/save/Analysis.scala index cd3b956e6..4d2034353 100644 --- a/code/shared/src/main/scala/maf/persistence/save/Analysis.scala +++ b/code/shared/src/main/scala/maf/persistence/save/Analysis.scala @@ -53,12 +53,14 @@ trait Save[Expr <: Expression] extends AnalysisEntry[Expr]: for (key, value) <- saveInfo do if saveSet.contains(key) then writer.writeMember(key, value.value)(using value.encoder) writer.close() + def startSave(): Unit = return override def save(filename: String): Unit = + startSave() val res = Json.encode(this)(using analysisEncoder).toByteArray Files.write(Paths.get(filename), res) override def save(filename: String, save: Set[String]): Unit = - this.save = save + startSave() this.saveSet = save val res = Json.encode(this)(using excludedAnalysisEncoder).toByteArray this.saveSet = Set[String]() diff --git a/code/shared/src/main/scala/maf/persistence/save/Component.scala b/code/shared/src/main/scala/maf/persistence/save/Component.scala index 88bfc60a2..7a65953c5 100644 --- a/code/shared/src/main/scala/maf/persistence/save/Component.scala +++ b/code/shared/src/main/scala/maf/persistence/save/Component.scala @@ -130,6 +130,11 @@ trait SaveComponentIntID[Expr <: Expression] extends SaveActualComps[Expr] with override given componentIDEncoder: Encoder[Component] with override def write(writer: Writer, component: Component): Writer = writer.write(components(component)) + override def startSave(): Unit = + id = 0 + components = HashMap[Component, Int]() + super.startSave() + /** * Trait that encodes components using their position. * diff --git a/code/shared/src/main/scala/maf/persistence/save/Expression.scala b/code/shared/src/main/scala/maf/persistence/save/Expression.scala index ab89f396e..eddaaea80 100644 --- a/code/shared/src/main/scala/maf/persistence/save/Expression.scala +++ b/code/shared/src/main/scala/maf/persistence/save/Expression.scala @@ -113,6 +113,11 @@ trait SaveExpressionIntID[Expr <: Expression] extends SaveExpressionID[Expr] wit if expressions.contains(expr) then writer.write(expressions(expr)) else writer.write(expr)(using actualExpressionEncoder) + override def startSave(): Unit = + id = 0 + expressions = HashMap[Expr, Int]() + super.startSave() + /** * Trait to encode scheme expressions recursively using integer IDs. * @@ -179,6 +184,11 @@ trait SaveRecursiveSchemeExpressionsIntID extends SaveExpressionID[SchemeExp] wi if expressions.contains(expr) then writer.write(expressions(expr)) else writer.write(expr)(using actualExpressionEncoder) + override def startSave(): Unit = + id = 0 + expressions = HashMap[SchemeExp, Int]() + super.startSave() + /** * Save the expressions normally. * From ad57a4a7be181fea2732fb4690780eec594acef5 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Tue, 9 Jul 2024 08:01:16 +0200 Subject: [PATCH 88/97] feat(save): Allow for saving of main body --- .../scala/maf/modular/scheme/modf/SchemeModFSemantics.scala | 2 +- .../src/main/scala/maf/persistence/save/Expression.scala | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/code/shared/src/main/scala/maf/modular/scheme/modf/SchemeModFSemantics.scala b/code/shared/src/main/scala/maf/modular/scheme/modf/SchemeModFSemantics.scala index 0e022b62a..3a9351f65 100644 --- a/code/shared/src/main/scala/maf/modular/scheme/modf/SchemeModFSemantics.scala +++ b/code/shared/src/main/scala/maf/modular/scheme/modf/SchemeModFSemantics.scala @@ -74,7 +74,7 @@ trait BaseSchemeModFSemanticsM caller: Component ): M[ComponentContext] = Monad[M].unit(allocCtx(clo, args, call, caller)) - lazy val mainBody = program + var mainBody = program def expr(cmp: Component): SchemeExp = body(cmp) def body(cmp: Component): SchemeExp = body(view(cmp)) def body(cmp: SchemeModFComponent): SchemeExp = cmp match diff --git a/code/shared/src/main/scala/maf/persistence/save/Expression.scala b/code/shared/src/main/scala/maf/persistence/save/Expression.scala index eddaaea80..53a7eb32e 100644 --- a/code/shared/src/main/scala/maf/persistence/save/Expression.scala +++ b/code/shared/src/main/scala/maf/persistence/save/Expression.scala @@ -87,6 +87,9 @@ trait SaveExpressionID[Expr <: Expression] extends ModAnalysis[Expr] with Save[E /** Encodes an expression using an ID */ protected given expressionIDEncoder: Encoder[Expr] +trait SaveMainSchemeBody extends BaseSchemeModFSemanticsM with SaveExpressions[SchemeExp]: + override def saveInfo: List[(String, Savable[?])] = super.saveInfo ++ List(("mainBody" -> Savable(mainBody))) + /** * Trait to encode expressions using integer IDs. * From f992aa7da8c4fd6c1ae59f6c374a9b4ed44536fd Mon Sep 17 00:00:00 2001 From: Merlijn Date: Tue, 9 Jul 2024 08:02:27 +0200 Subject: [PATCH 89/97] refactor(save): Create separate trait for lattices --- .../scala/maf/persistence/save/Store.scala | 77 ++++++++++++------- 1 file changed, 51 insertions(+), 26 deletions(-) diff --git a/code/shared/src/main/scala/maf/persistence/save/Store.scala b/code/shared/src/main/scala/maf/persistence/save/Store.scala index de8e2ae51..95d3589e9 100644 --- a/code/shared/src/main/scala/maf/persistence/save/Store.scala +++ b/code/shared/src/main/scala/maf/persistence/save/Store.scala @@ -20,6 +20,9 @@ import maf.core.Environment import maf.lattice.{ConcreteLattice, ConstantPropagation} import maf.lattice.Concrete import maf.save.save.SaveExpressions +import maf.modular.scheme.ModularSchemeLatticeWrapper +import maf.modular.scheme.SchemeConstantPropagationDomain +import maf.lattice.ConstantPropagation.L /** * Base trait for encoding [[AbstractDomain.Value values]]. @@ -66,55 +69,73 @@ trait SaveLattice[Expr <: Expression] extends Save[Expr]: case ConstantPropagation.Bottom => writer.write("bottom") writer +trait SaveModularSchemeDomainLattices extends Save[SchemeExp] with ModularSchemeDomain: + given stringLatticeEncoder: Encoder[S] + given booleanLatticeEncoder: Encoder[B] + given integerLatticeEncoder: Encoder[I] + given realLatticeEncoder: Encoder[R] + given charLatticeEncoder: Encoder[C] + given symbolLatticeEncoder: Encoder[Sym] + +trait SaveSchemeConstantPropagationDomain extends SchemeConstantPropagationDomain with SaveModularSchemeDomainLattices with SaveLattice[SchemeExp]: + override given stringLatticeEncoder: Encoder[S] = latticeEncoder[L, String].asInstanceOf[Encoder[S]] + override given booleanLatticeEncoder: Encoder[B] = latticeEncoder[L, Boolean].asInstanceOf[Encoder[B]] + override given integerLatticeEncoder: Encoder[I] = latticeEncoder[L, BigInt].asInstanceOf[Encoder[I]] + override given realLatticeEncoder: Encoder[R] = latticeEncoder[L, Double].asInstanceOf[Encoder[R]] + override given charLatticeEncoder: Encoder[C] = latticeEncoder[L, Char].asInstanceOf[Encoder[C]] + override given symbolLatticeEncoder: Encoder[Sym] = latticeEncoder[L, String].asInstanceOf[Encoder[Sym]] + /** * Trait for encoding values as [[ModularSchemeLattice modular scheme lattices]], as defined in [[ModularSchemeDomain]]. * * Implementation of [[SaveValue]]. */ -trait SaveModularDomain +trait SaveModularSchemeDomain extends SaveValue[SchemeExp] with ModularSchemeDomain with SaveAddr[SchemeExp] with SaveExpressions[SchemeExp] with SaveEnvironment[SchemeExp] - with SaveLattice[SchemeExp]: + with SaveComponents[SchemeExp] + with SaveModularSchemeDomainLattices: /** Generic modular scheme lattice that is used for typechecking of nested class inside of this. */ - type SchemeLattice = ModularSchemeLattice[?, ?, ?, ?, ?, ?, ?] + type SaveSchemeLattice = ModularSchemeLattice[?, S, B, I, R, C, Sym] + override given valueEncoder: ArrayEncoder[HMap] with override def write(writer: Writer, hmap: HMap): Writer = writer.start() hmap.contents.foreach((key, value) => writer.writeMember((key, value))) writer.close() - given MapEncoder[SchemeLattice#Clo]() with - override def write(writer: Writer, closure: SchemeLattice#Clo): Writer = - writer.writeArrayOpen(closure.closures.size) + given MapEncoder[SaveSchemeLattice#Clo]() with + override def write(writer: Writer, closure: SaveSchemeLattice#Clo): Writer = + writer.writeArrayStart() closure.closures.foreach((clo) => writer.start() writer.writeMember("expression", clo._1.asInstanceOf[SchemeExp]) writer.writeMember("address", clo._2.asInstanceOf[Environment[Address]]) writer.close() ) - writer.writeArrayClose() + writer.writeBreak() - given ArrayEncoder[SchemeLattice#Pointer]() with - override def write(writer: Writer, pointer: SchemeLattice#Pointer): Writer = + given ArrayEncoder[SaveSchemeLattice#Pointer]() with + override def write(writer: Writer, pointer: SaveSchemeLattice#Pointer): Writer = writer.start() pointer.ptrs.foreach(writer.write(_)) writer.close() - given MapEncoder[SchemeLattice#Cons]() with - override def write(writer: Writer, cons: SchemeLattice#Cons): Writer = + given MapEncoder[SaveSchemeLattice#Cons]() with + override def write(writer: Writer, cons: SaveSchemeLattice#Cons): Writer = writer.start() writer.writeMember("car", cons.car) writer.writeMember("cdr", cons.cdr) writer.close() - given MapEncoder[SchemeLattice#Vec]() with SaveMapToArray with - override def write(writer: Writer, vec: SchemeLattice#Vec): Writer = + given MapEncoder[SaveSchemeLattice#Vec]() with SaveMapToArray with + override def write(writer: Writer, vec: SaveSchemeLattice#Vec): Writer = writer.start() - writer.writeMember("size", vec.size.asInstanceOf[Lattice[BigInt]]) - writer.writeMember("elements", vec.elements.asInstanceOf[Map[Lattice[BigInt], SchemeLattice#L]]) + writer.writeMember("size", vec.size) + writer.writeMember("elements", vec.elements) writer.close() given MapEncoder[(HMapKey, Any)] with @@ -123,17 +144,21 @@ trait SaveModularDomain val (key, value) = hMapPair value match { - case int: SchemeLattice#Int => writer.writeMember("int", int.i.asInstanceOf[Lattice[BigInt]]) - case bool: SchemeLattice#Bool => writer.writeMember("boolean", bool.b.asInstanceOf[Lattice[Boolean]]) - case str: SchemeLattice#Str => writer.writeMember("string", str.s.asInstanceOf[Lattice[String]]) - case symbol: SchemeLattice#Symbol => writer.writeMember("symbol", symbol.s.asInstanceOf[Lattice[String]]) - case prim: SchemeLattice#Prim => writer.writeMember("primitive", prim.prims) - case clo: SchemeLattice#Clo => writer.writeMember("closure", clo) - case pointer: SchemeLattice#Pointer => writer.writeMember("pointer", pointer) - case cons: SchemeLattice#Cons => writer.writeMember("cons", cons) - case vec: SchemeLattice#Vec => writer.writeMember("vector", vec) - case modularLattice.Nil => writer.writeMember("nil", "") - case modularLattice.Void => writer.writeMember("void", "") + case int: SaveSchemeLattice#Int => writer.writeMember("int", int.i) + case bool: SaveSchemeLattice#Bool => writer.writeMember("boolean", bool.b) + case str: SaveSchemeLattice#Str => writer.writeMember("string", str.s) + case char: SaveSchemeLattice#Char => writer.writeMember("char", char.c) + case inputPort: SaveSchemeLattice#InputPort => writer.writeMember("inputPort", inputPort.id) + case real: SaveSchemeLattice#Real => writer.writeMember("real", real.r) + case symbol: SaveSchemeLattice#Symbol => writer.writeMember("symbol", symbol.s) + case prim: SaveSchemeLattice#Prim => writer.writeMember("primitive", prim.prims) + case clo: SaveSchemeLattice#Clo => writer.writeMember("closure", clo) + case pointer: SaveSchemeLattice#Pointer => writer.writeMember("pointer", pointer) + case cons: SaveSchemeLattice#Cons => writer.writeMember("cons", cons) + case vec: SaveSchemeLattice#Vec => writer.writeMember("vector", vec) + case kont: SaveSchemeLattice#Kont => writer.writeMember("kont", kont.k.asInstanceOf[Set[Component]]) + case modularLattice.Nil => writer.writeMember("nil", "") + case modularLattice.Void => writer.writeMember("void", "") case _ => System.err.nn.println("The lattice with type `" + key.getClass + "` could not be encoded") writer.writeMember("ERROR", "Unknown type: " + key.getClass.toString()) From b25e94cdc07da3738142e5c087b31d1560ba3bab Mon Sep 17 00:00:00 2001 From: Merlijn Date: Tue, 9 Jul 2024 08:03:54 +0200 Subject: [PATCH 90/97] feat(save): Change the way options are saved --- code/shared/src/main/scala/maf/persistence/save/Encoder.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/shared/src/main/scala/maf/persistence/save/Encoder.scala b/code/shared/src/main/scala/maf/persistence/save/Encoder.scala index 9ded0cbc7..fba92cfc4 100644 --- a/code/shared/src/main/scala/maf/persistence/save/Encoder.scala +++ b/code/shared/src/main/scala/maf/persistence/save/Encoder.scala @@ -296,7 +296,7 @@ trait ArrayEncoder[T] extends AbstractEncoder[T]: override def openEncapsulation(writer: Writer): Writer = writer.writeArrayStart() override def openEncapsulation(writer: Writer, amount: Int): Writer = writer.writeArrayOpen(amount) override def encodeOption[T: Encoder](writer: Writer, key: String, option: Option[T]): Writer = - writer.writeArrayOpen(if option.isDefined then 1 else 0) + writer.writeArrayStart() if option.isDefined then writer.write(option.get) writer.writeBreak() From 363f1fa19c44b3c42854cafa13f31638c16ed11d Mon Sep 17 00:00:00 2001 From: Merlijn Date: Tue, 9 Jul 2024 08:16:19 +0200 Subject: [PATCH 91/97] fix(save): Use correct encoder when encoding expressions --- .../maf/persistence/save/Expression.scala | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/code/shared/src/main/scala/maf/persistence/save/Expression.scala b/code/shared/src/main/scala/maf/persistence/save/Expression.scala index 53a7eb32e..f9742c0cc 100644 --- a/code/shared/src/main/scala/maf/persistence/save/Expression.scala +++ b/code/shared/src/main/scala/maf/persistence/save/Expression.scala @@ -26,6 +26,7 @@ import maf.language.scheme.SchemeLambdaExp import maf.modular.ModAnalysis import maf.save.MapEncoder import io.bullet.borer.derivation.CompactMapBasedCodecs +import maf.modular.scheme.modf.BaseSchemeModFSemanticsM /** * The base trait for encoding expressions. @@ -99,7 +100,7 @@ trait SaveMainSchemeBody extends BaseSchemeModFSemanticsM with SaveExpressions[S * The type of expression used in the analysis */ trait SaveExpressionIntID[Expr <: Expression] extends SaveExpressionID[Expr] with SaveExpressions[Expr]: - private val expressions = HashMap[Expr, Int]() + private var expressions = HashMap[Expr, Int]() private var id = 0 override protected given expressionSetEncoder: MapEncoder[Set[Expr]] with @@ -130,7 +131,7 @@ trait SaveExpressionIntID[Expr <: Expression] extends SaveExpressionID[Expr] wit * Implementation of [[SaveExpressionID]] */ trait SaveRecursiveSchemeExpressionsIntID extends SaveExpressionID[SchemeExp] with SaveExpressions[SchemeExp]: - private val expressions = HashMap[SchemeExp, Int]() + private var expressions = HashMap[SchemeExp, Int]() private var id = 0 /** The max height of the AST before you encode it normally. */ @@ -153,24 +154,26 @@ trait SaveRecursiveSchemeExpressionsIntID extends SaveExpressionID[SchemeExp] wi case lambda: SchemeLambda => writer.write(lambda.body) case argLambda: SchemeVarArgLambda => writer.write(argLambda.body) case letrec: SchemeLetrec => - for (binding <- letrec.bindings) writer.write(binding._2) + for (binding <- letrec.bindings) writer.write(binding._2)(using recursiveExpressionEncoder) writer.write(letrec.body) - case assert: SchemeAssert => writer.write(assert.exp) + case assert: SchemeAssert => writer.write(assert.exp)(using recursiveExpressionEncoder) case let: SchemeLet => - for (binding <- let.bindings) writer.write(binding._2) + for (binding <- let.bindings) writer.write(binding._2)(using recursiveExpressionEncoder) writer.write(let.body) case schemeIf: SchemeIf => - writer.write(schemeIf.cond) - writer.write(schemeIf.cons) - writer.write(schemeIf.alt) + writer.write(schemeIf.cond)(using recursiveExpressionEncoder) + writer.write(schemeIf.cons)(using recursiveExpressionEncoder) + writer.write(schemeIf.alt)(using recursiveExpressionEncoder) case set: SchemeSet => - writer.write(set.value) + writer.write(set.value)(using recursiveExpressionEncoder) case begin: SchemeBegin => writer.write(begin.exps) case letStar: SchemeLetStar => - for (binding <- letStar.bindings) writer.write(binding._2) + for (binding <- letStar.bindings) writer.write(binding._2)(using recursiveExpressionEncoder) writer.write(letStar.body) - case _ => () + case _: SchemeVar => () + case _: SchemeValue => () + case _ => System.err.nn.println("The expression with type `" + expr.getClass + "` could not be encoded") writer.writeMember(id.toString(), expr)(using actualExpressionEncoder) expressions.addOne(expr, id) From a6ba6fe5e483cc3f643df65d2a4523908e962470 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Tue, 9 Jul 2024 08:17:02 +0200 Subject: [PATCH 92/97] feat(save): Add ability to save expressions from worklist --- .../src/main/scala/maf/persistence/save/Expression.scala | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/code/shared/src/main/scala/maf/persistence/save/Expression.scala b/code/shared/src/main/scala/maf/persistence/save/Expression.scala index f9742c0cc..00f0d7d91 100644 --- a/code/shared/src/main/scala/maf/persistence/save/Expression.scala +++ b/code/shared/src/main/scala/maf/persistence/save/Expression.scala @@ -26,6 +26,7 @@ import maf.language.scheme.SchemeLambdaExp import maf.modular.ModAnalysis import maf.save.MapEncoder import io.bullet.borer.derivation.CompactMapBasedCodecs +import maf.save.SaveWorklist import maf.modular.scheme.modf.BaseSchemeModFSemanticsM /** @@ -80,7 +81,8 @@ trait SaveActualExprs[Expr <: Expression] extends SaveExpressions[Expr]: */ trait SaveExpressionID[Expr <: Expression] extends ModAnalysis[Expr] with Save[Expr] with SaveActualExprs[Expr]: override given expressionEncoder: Encoder[Expr] = expressionIDEncoder - override def saveInfo: List[(String, Savable[_])] = super.saveInfo ++ List(("expressions" -> Savable(visited.map(expr(_))))) + override def saveInfo: List[(String, Savable[_])] = super.saveInfo ++ List(("expressions" -> Savable(getExpressions()))) + def getExpressions(): Set[Expr] = return visited.map(expr(_)) /** Encodes a set of expressions, this is used to e.g. add ID info to the expressions. */ protected given expressionSetEncoder: Encoder[Set[Expr]] @@ -88,6 +90,9 @@ trait SaveExpressionID[Expr <: Expression] extends ModAnalysis[Expr] with Save[E /** Encodes an expression using an ID */ protected given expressionIDEncoder: Encoder[Expr] +trait SaveWorklistExpressionsID[Expr <: Expression] extends SaveExpressionID[Expr] with SaveWorklist[Expr]: + override def getExpressions(): Set[Expr] = return super.getExpressions() ++ getWorklist.toSet.map(expr(_)) + trait SaveMainSchemeBody extends BaseSchemeModFSemanticsM with SaveExpressions[SchemeExp]: override def saveInfo: List[(String, Savable[?])] = super.saveInfo ++ List(("mainBody" -> Savable(mainBody))) From a6b15634afcbfa2bbd8cf383ce250ffc9060fc99 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Tue, 9 Jul 2024 13:21:37 +0200 Subject: [PATCH 93/97] refactor(save): Make saving worklist more abstract --- .../main/scala/maf/persistence/save/Component.scala | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/code/shared/src/main/scala/maf/persistence/save/Component.scala b/code/shared/src/main/scala/maf/persistence/save/Component.scala index 7a65953c5..f2db6885f 100644 --- a/code/shared/src/main/scala/maf/persistence/save/Component.scala +++ b/code/shared/src/main/scala/maf/persistence/save/Component.scala @@ -255,14 +255,12 @@ trait SaveStandardSchemeComponents writer.writeMember("context", context.asInstanceOf[EncodeContext]) writer.close() -trait SaveWorklist[Expr <: Expression] extends Save[Expr]: - type WorklistComponent - given worklistEncoder: Encoder[WorkList[WorklistComponent]] - def getWorklist: WorkList[WorklistComponent] +trait SaveWorklist[Expr <: Expression] extends Save[Expr] with SaveComponents[Expr]: + given worklistEncoder: Encoder[WorkList[Component]] + def getWorklist: WorkList[Component] override def saveInfo: List[(String, Savable[_])] = super.saveInfo ++ List(("worklist", Savable(getWorklist))) -trait SaveSequentialWorklist[Expr <: Expression] extends SaveWorklist[Expr] with SequentialWorklistAlgorithm[Expr] with SaveComponents[Expr]: - type WorklistComponent = Component +trait SaveSequentialWorklist[Expr <: Expression] extends SaveWorklist[Expr] with SequentialWorklistAlgorithm[Expr]: override def getWorklist: WorkList[Component] = workList override given worklistEncoder: ArrayEncoder[WorkList[Component]] with override def write(writer: Writer, worklist: WorkList[Component]): Writer = From a6a3e6d34a4f3c2f5f7a1ff42782896a3276bf82 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Tue, 9 Jul 2024 13:24:13 +0200 Subject: [PATCH 94/97] chore(save): Change symbol that represents no context --- .../src/main/scala/maf/persistence/save/Component.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/shared/src/main/scala/maf/persistence/save/Component.scala b/code/shared/src/main/scala/maf/persistence/save/Component.scala index f2db6885f..297db28ec 100644 --- a/code/shared/src/main/scala/maf/persistence/save/Component.scala +++ b/code/shared/src/main/scala/maf/persistence/save/Component.scala @@ -217,7 +217,7 @@ trait SaveContext[Expr <: Expression] extends Save[Expr]: /** * Trait to encode the context for an analysis with no context. * - * This will just write 'ε' when asked to write the context. + * This will just write 'None' when asked to write the context. * * @tparam T * The type of the value the needs to be saved @@ -225,7 +225,7 @@ trait SaveContext[Expr <: Expression] extends Save[Expr]: trait SaveNoContext[Expr <: Expression] extends SaveContext[Expr]: override type EncodeContext = NoContext.type override given contextEncoder: Encoder[EncodeContext] with - override def write(writer: Writer, context: EncodeContext): Writer = writer.write("ε") + override def write(writer: Writer, context: EncodeContext): Writer = writer.write("None") /** * Trait to encode standard scheme components. From 800c89436e121a60793c184594e3f37a05fc4d02 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Tue, 9 Jul 2024 13:24:52 +0200 Subject: [PATCH 95/97] fix(save): Saving components --- .../scala/maf/persistence/save/Component.scala | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/code/shared/src/main/scala/maf/persistence/save/Component.scala b/code/shared/src/main/scala/maf/persistence/save/Component.scala index 297db28ec..65332891a 100644 --- a/code/shared/src/main/scala/maf/persistence/save/Component.scala +++ b/code/shared/src/main/scala/maf/persistence/save/Component.scala @@ -97,7 +97,7 @@ trait SaveActualComponents[Expr <: Expression] extends SaveActualComps[Expr]: * @tparam T * The type of the value the needs to be saved */ -trait SaveComponentID[Expr <: Expression] extends SaveComponents[Expr] with SavePosition[Expr]: +trait SaveComponentID[Expr <: Expression] extends SaveActualComps[Expr] with SavePosition[Expr]: /** Encodes a component by their ID */ given componentIDEncoder: Encoder[Component] override given componentEncoder: Encoder[Component] = componentIDEncoder @@ -115,7 +115,7 @@ trait SaveComponentID[Expr <: Expression] extends SaveComponents[Expr] with Save * The type of expression used in the analysis */ trait SaveComponentIntID[Expr <: Expression] extends SaveActualComps[Expr] with SaveComponentID[Expr]: - private val components = HashMap[Component, Int]() + private var components = HashMap[Component, Int]() private var id = 0 override protected given componentSetEncoder: MapEncoder[Set[Component]] with @@ -144,26 +144,24 @@ trait SaveComponentIntID[Expr <: Expression] extends SaveActualComps[Expr] with * Because this trait only encodes the component position, the entire component should be encoded somewhere else if you want to decode this again. */ trait SaveStandardSchemeComponentPosition extends SaveComponentID[SchemeExp] with StandardSchemeModFComponents: + override type Component = SchemeModFComponent + /** Encodes a component by their position */ override given componentIDEncoder: Encoder[Component] with def write(writer: Writer, component: Component): Writer = - writer.start() if component.equals(initialComponent) then writer.write("main") else writer.write(component.asInstanceOf[SchemeModFComponent.Call[ComponentContext]])(schemeComponentIDEncoder) - writer.close() /** Encodes a scheme component using their position */ given schemeComponentIDEncoder[T]: Encoder[SchemeModFComponent.Call[T]] with def write(writer: Writer, component: SchemeModFComponent.Call[T]): Writer = - writer.start() val (lambda, _) = component.clo writer.write(lambda.idn.pos) - writer.close() override protected given componentSetEncoder: ArrayEncoder[Set[Component]] with override def write(writer: Writer, components: Set[Component]): Writer = writer.start() - for (component <- components) do writer.writeMember(component) + for (component <- components) do writer.writeMember(component)(using actualComponentEncoder) writer.close() /** @@ -240,7 +238,7 @@ trait SaveStandardSchemeComponents with SaveExpressions[SchemeExp]: override type Component = SchemeModFComponent - override given actualComponentEncoder: Encoder[Component] with + override given actualComponentEncoder: MapEncoder[Component] with def write(writer: Writer, component: Component): Writer = if component.equals(initialComponent) then writer.write("main") else writer.write(component.asInstanceOf[SchemeModFComponent.Call[ComponentContext]]) From cb99eb719bc925895ce8358ad2ba12b36dfbe7c6 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Tue, 9 Jul 2024 13:27:17 +0200 Subject: [PATCH 96/97] chore(save): Change used traits for saving --- .../src/main/scala/maf/persistence/save/Analysis.scala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/code/shared/src/main/scala/maf/persistence/save/Analysis.scala b/code/shared/src/main/scala/maf/persistence/save/Analysis.scala index 4d2034353..bfd6b58d3 100644 --- a/code/shared/src/main/scala/maf/persistence/save/Analysis.scala +++ b/code/shared/src/main/scala/maf/persistence/save/Analysis.scala @@ -111,11 +111,14 @@ trait SaveModF with SaveSchemeExpressions with SaveRecursiveSchemeExpressionsIntID with SaveComponentIntID[SchemeExp] + with SaveWorklistExpressionsID[SchemeExp] + with SaveMainSchemeBody with SaveStandardSchemeComponents - with SaveModularDomain + with SaveModularSchemeDomain + with SaveSchemeConstantPropagationDomain with SaveAddrDep[SchemeExp] with SaveSchemeAddr with SaveGlobalStore[SchemeExp] with SaveSequentialWorklist[SchemeExp] with SaveNoContext[SchemeExp]: - override val maxASTHeight = 3 + override val maxASTHeight: Int = 0 From ab3bdb331d55ea7523190d9f240f2e335d650b87 Mon Sep 17 00:00:00 2001 From: Merlijn Date: Tue, 9 Jul 2024 13:28:04 +0200 Subject: [PATCH 97/97] test(persistance): Update test suite --- .../scala/maf/test/persistence/Analysis.scala | 386 +++++++++++++++--- .../maf/test/persistence/Component.scala | 1 - .../scala/maf/test/persistence/Store.scala | 26 +- 3 files changed, 350 insertions(+), 63 deletions(-) diff --git a/code/shared/src/test/scala/maf/test/persistence/Analysis.scala b/code/shared/src/test/scala/maf/test/persistence/Analysis.scala index f648295b7..ae95de290 100644 --- a/code/shared/src/test/scala/maf/test/persistence/Analysis.scala +++ b/code/shared/src/test/scala/maf/test/persistence/Analysis.scala @@ -25,7 +25,6 @@ import maf.save.AbstractDecoder import maf.save.AbstractEncoder import maf.save.ArrayDecoder import maf.save.ArrayEncoder -import maf.save.ArrayKeyDecoder import maf.save.ArrayKeyEncoder import maf.save.Load import maf.save.LoadActualComponents @@ -39,7 +38,6 @@ import maf.save.MapEncoder import maf.save.Save import maf.save.SaveActualComponents import maf.save.SaveComponents -import maf.save.SaveModularDomain import maf.save.SaveStandardSchemeComponents import maf.save.save.SaveActualExpressions import maf.save.save.SaveExpressions @@ -84,6 +82,51 @@ import maf.save.SaveModF import maf.save.LoadModF import maf.save.SaveEnvironment import maf.save.LoadEnvironment +import maf.modular.GlobalStore +import maf.save.SaveInitialized +import maf.save.SaveWorklist +import maf.save.SaveGlobalStore +import maf.save.SaveDependency +import maf.save.SaveAddrDep +import maf.save.SaveModularSchemeDomain +import maf.save.SaveSchemeConstantPropagationDomain +import maf.save.SaveSchemeAddr +import maf.save.SaveNoContext +import maf.save.SaveSequentialWorklist +import maf.save.LoadInitialized +import maf.save.LoadWorklist +import maf.save.LoadGlobalStore +import maf.save.LoadDependency +import maf.save.LoadAddrDependency +import maf.save.LoadModularSchemeDomain +import maf.save.LoadSchemeConstantPropagationDomain +import maf.save.LoadSchemeAddr +import maf.save.LoadFIFOWorklist +import maf.save.LoadNoContext +import maf.save.SaveStandardSchemeComponentPosition +import maf.save.LoadStandardSchemeComponentPosition +import maf.save.SaveComponentIntID +import maf.save.LoadComponentIntID +import maf.save.save.SaveRecursiveSchemeExpressionsIntID +import maf.save.LoadExpressionIntID +import maf.test.persistence.PersistenceSpecAnalysises.SimpleModFActual +import maf.test.persistence.PersistenceSpecAnalysises.SimpleModFPositionComponents +import maf.test.persistence.PersistenceSpecAnalysises.SimpleModFIDComponents +import maf.test.persistence.PersistenceSpecAnalysises.SimpleModFIDExpressions +import maf.test.persistence.PersistenceSpecAnalysises.SimpleModFIDs +import org.scalatest.compatible.Assertion +import maf.save.save.SaveExpressionIntID +import maf.test.persistence.PersistenceSpecAnalysises.SimpleModFIDSchemeExpressions +import maf.test.persistence.PersistenceSpecAnalysises.SimpleModFActualCbor +import maf.test.persistence.PersistenceSpecAnalysises.SimpleModFPositionComponentsCbor +import maf.test.persistence.PersistenceSpecAnalysises.SimpleModFIDComponentsCbor +import maf.test.persistence.PersistenceSpecAnalysises.SimpleModFIDExpressionsCbor +import maf.test.persistence.PersistenceSpecAnalysises.SimpleModFIDSchemeExpressionsCbor +import maf.test.persistence.PersistenceSpecAnalysises.SimpleModFIDsCbor +import maf.save.SaveCbor +import maf.save.LoadCbor +import maf.save.save.SaveMainSchemeBody +import maf.save.LoadMainSchemeBody trait Generator: protected case class StringValue(str: String) @@ -262,8 +305,8 @@ trait PersistenceSpec extends AnyPropSpec with ScalaCheckPropertyChecks with Tab writer.writeBreak() trait LoadStringAddr[Expr <: Expression] extends LoadAddr[Expr]: - override def addressDecoders: List[(String, Decoder[? <: Address])] = - List(("==TESTING== NO ADDRESS", summon[Decoder[PrmAddr]])) + override def addressDecoders: Set[(String, Decoder[? <: Address])] = + Set(("==TESTING== NO ADDRESS", summon[Decoder[PrmAddr]])) given Decoder[PrmAddr] with override def read(reader: borer.Reader): PrmAddr = val str = reader.readString() @@ -354,9 +397,11 @@ trait PersistenceSpec extends AnyPropSpec with ScalaCheckPropertyChecks with Tab } class PersistAnalysisSpec extends PersistenceSpec: - val programsStream = Files.list(Paths.get("test/R5RS/ad")) - val programsList = if programsStream == null then List() else programsStream.iterator().nn.asScala.toList - val programs = Gen.oneOf[Path](programsList) + val programs = Gen.oneOf[Path](getFilesList("test/R5RS/ad").appendedAll(getFilesList("test/R5RS/various"))) + + def getFilesList(folder: String): List[Path] = + val programsStream = Files.list(Paths.get(folder)) + return if programsStream == null then List() else programsStream.iterator().nn.asScala.toList class ContextInsensitiveSchemeAnalysis(program: SchemeExp) extends SaveAnalysis(program) @@ -385,61 +430,286 @@ class PersistAnalysisSpec extends PersistenceSpec: analysis.load(saveFile.toString()) return analysis - private def testSchemePrograms[ASSERTION, Analysis <: AnalysisEntry[SchemeExp]]( + private def testSchemePrograms[ASSERTION, Analysis <: ModAnalysis[SchemeExp]]( anl: (program: SchemeExp) => Analysis, - testCorrectness: (result: Analysis, loadedResult: Analysis) => ASSERTION + testCorrectness: (result: Analysis, loadedResult: Analysis) => ASSERTION, + runTest: ((Path) => Unit) => Unit, + limitHeight: Int ): Unit = - forAll(programs) { (path: Path) => - Given(path.toString()) + runTest((path: Path) => val program = CSchemeParser.parseProgram(Reader.loadFile(path.toString())) - val analysis = anl(program) - val saveFile = save(program, analysis) + whenever(program.height < limitHeight) { + Given(path.toString()) + val analysis = anl(program) + val saveFile = save(program, analysis) + + val loadAnalysis = load(anl(program), saveFile) - val loadAnalysis = load(anl(program), saveFile) + Files.deleteIfExists(saveFile) + testCorrectness(analysis, loadAnalysis) + } + ) - Files.deleteIfExists(saveFile) - testCorrectness(analysis, loadAnalysis) + private def testAnalysis[ASSERTION, Analysis <: GlobalStore[SchemeExp]]( + name: String, + anl: (program: SchemeExp) => GlobalStore[SchemeExp] & BaseSchemeModFSemanticsM, + runTest: ((Path) => Unit) => Unit = (test: (Path) => Unit) => forAll(programs) { (path: Path) => test(path) }, + limitHeight: Int = Int.MaxValue + ) = + property(s"A programs result should remain the same when encoded and decoded for a $name") { + testSchemePrograms( + anl, + (result: GlobalStore[SchemeExp], loadedResult: GlobalStore[SchemeExp]) => + loadedResult.result shouldBe defined + loadedResult.result.get should equal(result.result.get), + runTest, + limitHeight + ) } - property("A programs result should remain the same when encoded and decoded for a context insensitive scheme analysis") { - testSchemePrograms( - (program: SchemeExp) => new ContextInsensitiveSchemeAnalysis(program), - (result: ContextInsensitiveSchemeAnalysis, loadedResult: ContextInsensitiveSchemeAnalysis) => - loadedResult.result shouldBe defined - loadedResult.result.get should equal(result.result.get) - ) - } - - property("A programs components should remain the same when encoded and decoded for a context insensitive scheme analysis") { - testSchemePrograms( - (program: SchemeExp) => new ContextInsensitiveSchemeAnalysis(program), - (result: ContextInsensitiveSchemeAnalysis, loadedResult: ContextInsensitiveSchemeAnalysis) => - loadedResult.visited.size should equal(result.visited.size) - // This is done inside of a loop to improve the errors given when a test fails - for component <- result.visited do loadedResult.visited should contain(component) - ) - } - - property("A programs dependencies should remain the same when encoded and decoded for a context insensitive scheme analysis") { - testSchemePrograms( - (program: SchemeExp) => new ContextInsensitiveSchemeAnalysis(program), - (result: ContextInsensitiveSchemeAnalysis, loadedResult: ContextInsensitiveSchemeAnalysis) => - loadedResult.deps.size should equal(result.deps.size) - // This is done inside of a loop to improve the errors given when a test fails - for dependency <- result.deps.keysIterator do - loadedResult.deps.keySet should contain(dependency) - loadedResult.deps.get(dependency) should equal(result.deps.get(dependency)) - ) - } - - property("A programs store should remain the same when encoded and decoded for a context insensitive scheme analysis") { - testSchemePrograms( - (program: SchemeExp) => new ContextInsensitiveSchemeAnalysis(program), - (result: ContextInsensitiveSchemeAnalysis, loadedResult: ContextInsensitiveSchemeAnalysis) => - loadedResult.store.size should equal(result.store.size) - // This is done inside of a loop to improve the errors given when a test fails - for addr <- result.store.keySet do - loadedResult.store.keySet should contain(addr) - loadedResult.store.get(addr) should equal(result.store.get(addr)) - ) - } + property(s"A programs components should remain the same when encoded and decoded for a $name") { + testSchemePrograms( + anl, + (result: GlobalStore[SchemeExp], loadedResult: GlobalStore[SchemeExp]) => + loadedResult.visited.size should equal(result.visited.size) + // This is done inside of a loop to improve the errors given when a test fails + for component <- result.visited do loadedResult.visited should contain(component), + runTest, + limitHeight + ) + } + + property(s"A programs dependencies should remain the same when encoded and decoded for a $name") { + testSchemePrograms( + anl, + (result: GlobalStore[SchemeExp], loadedResult: GlobalStore[SchemeExp]) => + loadedResult.deps.size should equal(result.deps.size) + // This is done inside of a loop to improve the errors given when a test fails + for dependency <- result.deps.keysIterator do + loadedResult.deps.keySet should contain(dependency) + loadedResult.deps.get(dependency) should equal(result.deps.get(dependency)) + , + runTest, + limitHeight + ) + } + + property(s"A programs store should remain the same when encoded and decoded for a $name") { + testSchemePrograms( + anl, + (result: GlobalStore[SchemeExp], loadedResult: GlobalStore[SchemeExp]) => + loadedResult.store.size should equal(result.store.size) + // This is done inside of a loop to improve the errors given when a test fails + for addr <- result.store.keySet do + loadedResult.store.keySet should contain(addr) + loadedResult.store.get(addr) should equal(result.store.get(addr)) + , + runTest, + limitHeight + ) + } + + testAnalysis("context insensitive modf analysis saving the actual expressions and components", + (program: SchemeExp) => new SimpleModFActual(program), + limitHeight = 20 + ) + + testAnalysis( + "context insensitive modf analysis saving the components as their positions and actual expressions", + (program: SchemeExp) => new SimpleModFPositionComponents(program), + limitHeight = 20 + ) + + testAnalysis( + "context insensitive modf analysis saving the components as integer IDs and actual expressions", + (program: SchemeExp) => new SimpleModFIDComponents(program), + limitHeight = 20 + ) + + testAnalysis( + "context insensitive modf analysis saving the actual components and expressions as integer IDs", + (program: SchemeExp) => new SimpleModFIDExpressions(program), + limitHeight = 20 + ) + + var maxADTHeight = 0 + testAnalysis( + s"context insensitive modf analysis saving the expressions as integer IDs recursively and saving the actual components", + (program: SchemeExp) => new SimpleModFIDSchemeExpressions(program, maxADTHeight), + (test: (Path) => Unit) => + forAll(programs, Gen.chooseNum(0, 15))({ (path: Path, maxADTHeight: Int) => + this.maxADTHeight = maxADTHeight + Given(s"max ADT height: $maxADTHeight") + test(path) + }) + ) + + testAnalysis( + s"context insensitive modf analysis saving the expressions recursively as integer IDs and components as integer IDs", + (program: SchemeExp) => new SimpleModFIDs(program, maxADTHeight), + (test: (Path) => Unit) => + forAll(programs, Gen.chooseNum(0, 15))({ (path: Path, maxADTHeight: Int) => + this.maxADTHeight = maxADTHeight + Given(s"max ADT height: $maxADTHeight") + test(path) + }) + ) + + testAnalysis( + "context insensitive modf analysis saving the actual expressions and components in CBOR", + (program: SchemeExp) => new SimpleModFActualCbor(program), + limitHeight = 500 + ) + + testAnalysis( + "context insensitive modf analysis saving the components as their positions and actual expressions in CBOR", + (program: SchemeExp) => new SimpleModFPositionComponentsCbor(program), + limitHeight = 500 + ) + + testAnalysis( + "context insensitive modf analysis saving the components as integer IDs and actual expressions in CBOR", + (program: SchemeExp) => new SimpleModFIDComponentsCbor(program), + limitHeight = 500 + ) + + testAnalysis( + "context insensitive modf analysis saving the actual components and expressions as integer IDs in CBOR", + (program: SchemeExp) => new SimpleModFIDExpressionsCbor(program), + limitHeight = 500 + ) + + testAnalysis( + s"context insensitive modf analysis saving the expressions as integer IDs recursively and saving the actual components in CBOR", + (program: SchemeExp) => new SimpleModFIDSchemeExpressionsCbor(program, maxADTHeight), + (test: (Path) => Unit) => + forAll(programs, Gen.chooseNum(0, 100))({ (path: Path, maxADTHeight: Int) => + this.maxADTHeight = maxADTHeight + Given(s"max ADT height: $maxADTHeight") + test(path) + }) + ) + + testAnalysis( + s"context insensitive modf analysis saving the expressions recursively as integer IDs and components as integer IDs in CBOR", + (program: SchemeExp) => new SimpleModFIDsCbor(program, maxADTHeight), + (test: (Path) => Unit) => + forAll(programs, Gen.chooseNum(0, 100))({ (path: Path, maxADTHeight: Int) => + this.maxADTHeight = maxADTHeight + Given(s"max ADT height: $maxADTHeight") + test(path) + }) + ) + +object PersistenceSpecAnalysises: + trait SaveSpec[Expr <: Expression] + extends Save[Expr] + with SaveInitialized[Expr] + with SaveComponents[Expr] + with SaveWorklist[Expr] + with SaveGlobalStore[Expr] + with SaveDependency[Expr] + with SaveAddrDep[Expr] + + trait SaveModF + extends SaveSpec[SchemeExp] + with SaveStandardSchemeComponents + with SaveModularSchemeDomain + with SaveSchemeConstantPropagationDomain + with SaveSchemeAddr + with SaveSchemeExpressions + with SaveNoContext[SchemeExp] + with SaveSequentialWorklist[SchemeExp] + with SaveMainSchemeBody + + trait LoadSpec[Expr <: Expression] + extends Load[Expr] + with LoadInitialized[Expr] + with LoadComponents[Expr] + with LoadWorklist[Expr] + with LoadGlobalStore[Expr] + with LoadDependency[Expr] + with LoadAddrDependency[Expr] + + trait LoadModF + extends LoadSpec[SchemeExp] + with LoadStandardSchemeComponents + with LoadModularSchemeDomain + with LoadSchemeConstantPropagationDomain + with LoadSchemeAddr + with LoadFIFOWorklist[SchemeExp] + with LoadSchemeExpressions + with LoadNoContext[SchemeExp] + with LoadMainSchemeBody + + trait SimpleModF + extends SimpleSchemeModFAnalysis + with SchemeModFNoSensitivity + with SchemeConstantPropagationDomain + with FIFOWorklistAlgorithm[SchemeExp] + with SaveModF + with LoadModF + + class SimpleModFActual(program: SchemeExp) + extends SimpleSchemeModFAnalysis(program) + with SaveActualExpressions[SchemeExp] + with LoadActualExpressions[SchemeExp] + with SaveActualComponents[SchemeExp] + with LoadActualComponents[SchemeExp] + with SimpleModF + + class SimpleModFPositionComponents(program: SchemeExp) + extends SimpleSchemeModFAnalysis(program) + with SaveActualExpressions[SchemeExp] + with LoadActualExpressions[SchemeExp] + with SaveStandardSchemeComponentPosition + with LoadStandardSchemeComponentPosition + with SimpleModF + + class SimpleModFIDComponents(program: SchemeExp) + extends SimpleSchemeModFAnalysis(program) + with SaveActualExpressions[SchemeExp] + with LoadActualExpressions[SchemeExp] + with SaveComponentIntID[SchemeExp] + with LoadComponentIntID[SchemeExp] + with SimpleModF + + class SimpleModFIDExpressions(program: SchemeExp) + extends SimpleSchemeModFAnalysis(program) + with SaveExpressionIntID[SchemeExp] + with LoadExpressionIntID[SchemeExp] + with SaveActualComponents[SchemeExp] + with LoadActualComponents[SchemeExp] + with SimpleModF + + class SimpleModFIDSchemeExpressions(program: SchemeExp, override val maxASTHeight: Int) + extends SimpleSchemeModFAnalysis(program) + with SaveRecursiveSchemeExpressionsIntID + with LoadExpressionIntID[SchemeExp] + with SaveActualComponents[SchemeExp] + with LoadActualComponents[SchemeExp] + with SimpleModF + + class SimpleModFIDs(program: SchemeExp, override val maxASTHeight: Int) + extends SimpleSchemeModFAnalysis(program) + with SaveRecursiveSchemeExpressionsIntID + with LoadExpressionIntID[SchemeExp] + with SaveComponentIntID[SchemeExp] + with LoadComponentIntID[SchemeExp] + with SimpleModF + + class SimpleModFActualCbor(program: SchemeExp) extends SimpleModFActual(program) with SaveCbor[SchemeExp] with LoadCbor[SchemeExp] + class SimpleModFPositionComponentsCbor(program: SchemeExp) + extends SimpleModFPositionComponents(program) + with SaveCbor[SchemeExp] + with LoadCbor[SchemeExp] + class SimpleModFIDComponentsCbor(program: SchemeExp) extends SimpleModFIDComponents(program) with SaveCbor[SchemeExp] with LoadCbor[SchemeExp] + class SimpleModFIDExpressionsCbor(program: SchemeExp) extends SimpleModFIDExpressions(program) with SaveCbor[SchemeExp] with LoadCbor[SchemeExp] + class SimpleModFIDSchemeExpressionsCbor(program: SchemeExp, maxASTHeight: Int) + extends SimpleModFIDSchemeExpressions(program, maxASTHeight) + with SaveCbor[SchemeExp] + with LoadCbor[SchemeExp] + class SimpleModFIDsCbor(program: SchemeExp, maxASTHeight: Int) + extends SimpleModFIDs(program, maxASTHeight) + with SaveCbor[SchemeExp] + with LoadCbor[SchemeExp] diff --git a/code/shared/src/test/scala/maf/test/persistence/Component.scala b/code/shared/src/test/scala/maf/test/persistence/Component.scala index 2569bc1e1..0f9f11ab2 100644 --- a/code/shared/src/test/scala/maf/test/persistence/Component.scala +++ b/code/shared/src/test/scala/maf/test/persistence/Component.scala @@ -30,7 +30,6 @@ import maf.core.Position.SourcePathTag import org.scalatest.matchers.should.Matchers import org.scalatest.prop.TableDrivenPropertyChecks import io.bullet.borer.Encoder -import maf.save.ArrayKeyDecoder import maf.save.ArrayDecoder import io.bullet.borer.Decoder import maf.core.BasicEnvironment diff --git a/code/shared/src/test/scala/maf/test/persistence/Store.scala b/code/shared/src/test/scala/maf/test/persistence/Store.scala index a65aa6479..ad2f7a25b 100644 --- a/code/shared/src/test/scala/maf/test/persistence/Store.scala +++ b/code/shared/src/test/scala/maf/test/persistence/Store.scala @@ -3,7 +3,6 @@ package maf.test.persistence import maf.save.SaveValue import maf.modular.ModAnalysis import maf.save.LoadValue -import maf.save.SaveModularDomain import maf.save.LoadModularDomain import maf.core.Expression import maf.language.scheme.SchemeExp @@ -18,12 +17,14 @@ import maf.lattice.ConstantPropagation import org.scalacheck.Gen import io.bullet.borer.Encoder import io.bullet.borer.Decoder -import maf.save.LoadModularSchemeLattices import maf.lattice.HMap import maf.lattice.interfaces.IntLattice import scala.collection.mutable.HashMap import maf.core.BasicEnvironment import maf.core.Address +import maf.save.SaveModularSchemeDomain +import maf.save.SaveSchemeConstantPropagationDomain +import maf.save.LoadModularSchemeDomain trait ValueGenerator extends Generator: type SchemeLattice = ModularSchemeLattice[?, ?, ?, ?, ?, ?, ?] @@ -38,10 +39,13 @@ trait ValueGenerator extends Generator: functionGen(generateSchemeIntLattice), functionGen(generateSchemeBoolLattice), functionGen(generateSchemeStringLattice), + functionGen(generateSchemeCharLattice), functionGen(generateSchemePrimLattice), + functionGen(generateSchemeKontLattice), functionGen(generateSchemeSymbolLattice), maxDepthFunctionGen(generateSchemeConsLattice, generateSchemeLattice), maxDepthFunctionGen(generateSchemeVectorLattice, generateSchemeLattice), + maxDepthFunctionGen(generateSchemeInputPortLattice, generateSchemeLattice), functionGen(generateSchemeClosureLattice), functionGen(generateSchemeNilLattice), functionGen(generateSchemeVoidLattice), @@ -65,6 +69,19 @@ trait ValueGenerator extends Generator: def generateSchemeStringLattice(): (HMapKey, SchemeLattice#Str) = return (modularLattice.StrT, new modularLattice.Str(generateConstantSchemeLattice(str.sample.get).sample.get)) + def generateSchemeCharLattice(): (HMapKey, SchemeLattice#Char) = + return (modularLattice.CharT, new modularLattice.Char(generateConstantSchemeLattice(Gen.alphaChar).sample.get)) + + def generateSchemeInputPortLattice(): (HMapKey, SchemeLattice#InputPort) = + return (modularLattice.InputPortT, new modularLattice.InputPort(hMaps.sample.get)) + + def generateSchemeKontLattice(): (HMapKey, SchemeLattice#Kont) = + return (modularLattice.KontT, + new modularLattice.Kont( + Gen.listOfN(10, stringComponents).sample.get.asInstanceOf[List[SchemeLattice#Kont]].toSet + ) + ) + def generateSchemePrimLattice(): (HMapKey, SchemeLattice#Prim) = return (modularLattice.PrimT, new modularLattice.Prim(Gen.listOfN(5, str).sample.get.toSet)) @@ -108,9 +125,10 @@ class PersistValueSpec extends PersistenceSpec with ValueGenerator: class ModularDomainAnalysis extends TestBaseSchemeModFSemanticsAnalysis with ValueAnalysis[SchemeExp] - with SaveModularDomain + with SaveModularSchemeDomain + with SaveSchemeConstantPropagationDomain with LoadModularDomain - with LoadModularSchemeLattices + with LoadModularSchemeDomain with SaveStringContext[SchemeExp] with LoadStringContext[SchemeExp] with SaveStringComponent[SchemeExp]