Crossprojects for JVM and JS platforms (#237)

This commit is contained in:
Dima 2021-08-11 19:53:36 +03:00 committed by GitHub
parent 50aa4db4e8
commit e2da2e90d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 454 additions and 327 deletions

6
.jvmopts Normal file
View File

@ -0,0 +1,6 @@
-Dfile.encoding=UTF8
-Xms1G
-Xmx5G
-XX:ReservedCodeCacheSize=500M
-XX:+TieredCompilation
-XX:+UseParallelGC

View File

@ -0,0 +1,7 @@
package aqua.backend
object Version {
// TODO: get version for JS compiler
lazy val version = "Unknown (JS)"
}

View File

@ -0,0 +1,8 @@
package aqua.backend
object Version {
lazy val version = Option(getClass.getPackage.getImplementationVersion)
.filter(_.nonEmpty)
.getOrElse("Unknown")
}

View File

@ -7,14 +7,14 @@ import aqua.types.StreamType
import cats.Eval
import cats.data.Chain
import cats.free.Cofree
import wvlet.log.LogSupport
import scribe.Logging
sealed trait AirGen {
def generate: Air
}
object AirGen extends LogSupport {
object AirGen extends Logging {
def lambdaToString(ls: List[LambdaModel]): String = ls match {
case Nil => ""
@ -54,7 +54,7 @@ object AirGen extends LogSupport {
case o :: Nil => ParGen(o, NullGen)
case _ =>
ops.toList.reduceLeftOption(ParGen(_, _)).getOrElse {
warn("ParRes with no children converted to Null")
logger.warn("ParRes with no children converted to Null")
NullGen
}
})
@ -63,7 +63,7 @@ object AirGen extends LogSupport {
case o :: Nil => XorGen(o, NullGen)
case _ =>
ops.toList.reduceLeftOption(XorGen(_, _)).getOrElse {
warn("XorRes with no children converted to Null")
logger.warn("XorRes with no children converted to Null")
NullGen
}
})

View File

@ -1,5 +1,6 @@
package aqua.backend.js
import aqua.backend.Version
import aqua.model.AquaContext
import aqua.model.transform.GenerationConfig
import cats.data.Chain
@ -21,9 +22,7 @@ object JavaScriptFile {
| * This file is auto-generated. Do not edit manually: changes may be erased.
| * Generated by Aqua compiler: https://github.com/fluencelabs/aqua/.
| * If you find any bugs, please write an issue on GitHub: https://github.com/fluencelabs/aqua/issues
| * Aqua version: ${Option(getClass.getPackage.getImplementationVersion)
.filter(_.nonEmpty)
.getOrElse("Unknown")}
| * Aqua version: ${Version.version}
| *
| */
|import { RequestFlowBuilder } from '@fluencelabs/fluence/dist/api.unstable';

View File

@ -1,5 +1,6 @@
package aqua.backend.ts
import aqua.backend.Version
import aqua.model.AquaContext
import aqua.model.transform.GenerationConfig
import cats.data.Chain
@ -21,9 +22,7 @@ object TypeScriptFile {
| * This file is auto-generated. Do not edit manually: changes may be erased.
| * Generated by Aqua compiler: https://github.com/fluencelabs/aqua/.
| * If you find any bugs, please write an issue on GitHub: https://github.com/fluencelabs/aqua/issues
| * Aqua version: ${Option(getClass.getPackage.getImplementationVersion)
.filter(_.nonEmpty)
.getOrElse("Unknown")}
| * Aqua version: ${Version.version}
| *
| */
|import { FluenceClient, PeerIdB58 } from '@fluencelabs/fluence';

103
build.sbt
View File

@ -8,19 +8,12 @@ val catsV = "2.6.1"
val catsParseV = "0.3.4"
val monocleV = "3.0.0-M6"
val scalaTestV = "3.2.9"
val fs2V = "3.0.6"
val fs2V = "3.1.0"
val catsEffectV = "3.2.1"
val airframeLogV = "21.5.4"
val log4catsV = "2.1.1"
val slf4jV = "1.7.30"
val declineV = "2.1.0"
val airframeLog = "org.wvlet.airframe" %% "airframe-log" % airframeLogV
val catsEffect = "org.typelevel" %% "cats-effect" % catsEffectV
val fs2Io = "co.fs2" %% "fs2-io" % fs2V
val catsFree = "org.typelevel" %% "cats-free" % catsV
val cats = "org.typelevel" %% "cats-core" % catsV
name := "aqua-hll"
val commons = Seq(
@ -28,9 +21,8 @@ val commons = Seq(
version := baseAquaVersion.value + "-" + sys.env.getOrElse("BUILD_NUMBER", "SNAPSHOT"),
scalaVersion := dottyVersion,
libraryDependencies ++= Seq(
"org.typelevel" %% "log4cats-core" % log4catsV,
airframeLog,
"org.scalatest" %% "scalatest" % scalaTestV % Test
"com.outr" %%% "scribe" % "3.5.5",
"org.scalatest" %%% "scalatest" % scalaTestV % Test
),
scalacOptions ++= {
Seq(
@ -47,95 +39,124 @@ val commons = Seq(
commons
lazy val cli = project
lazy val cli = crossProject(JSPlatform, JVMPlatform)
.withoutSuffixFor(JVMPlatform)
.crossType(CrossType.Pure)
.in(file("cli"))
.settings(commons: _*)
.settings(
libraryDependencies ++= Seq(
"org.typelevel" %%% "cats-effect" % catsEffectV,
"com.monovore" %%% "decline" % declineV,
"com.monovore" %%% "decline-effect" % declineV,
"co.fs2" %%% "fs2-io" % fs2V
)
)
.dependsOn(compiler, `backend-air`, `backend-ts`, `backend-js`)
lazy val cliJS = cli.js
.settings(
scalaJSUseMainModuleInitializer := true
)
lazy val cliJVM = cli.jvm
.settings(
Compile / run / mainClass := Some("aqua.AquaCli"),
assembly / mainClass := Some("aqua.AquaCli"),
assembly / assemblyJarName := "aqua-cli-" + version.value + ".jar",
libraryDependencies ++= Seq(
"com.monovore" %% "decline" % declineV,
"com.monovore" %% "decline-effect" % declineV,
catsEffect,
fs2Io,
"org.typelevel" %% "log4cats-slf4j" % log4catsV,
"org.slf4j" % "slf4j-jdk14" % slf4jV
)
)
.dependsOn(compiler, `backend-air`, `backend-ts`, `backend-js`)
lazy val types = project
lazy val types = crossProject(JVMPlatform, JSPlatform)
.withoutSuffixFor(JVMPlatform)
.crossType(CrossType.Pure)
.settings(commons)
.settings(
libraryDependencies ++= Seq(
cats
"org.typelevel" %%% "cats-core" % catsV
)
)
lazy val parser = project
lazy val parser = crossProject(JVMPlatform, JSPlatform)
.withoutSuffixFor(JVMPlatform)
.crossType(CrossType.Pure)
.settings(commons: _*)
.settings(
libraryDependencies ++= Seq(
"org.typelevel" %% "cats-parse" % catsParseV,
catsFree
"org.typelevel" %%% "cats-parse" % catsParseV,
"org.typelevel" %%% "cats-free" % catsV
)
)
.dependsOn(types)
lazy val linker = project
lazy val linker = crossProject(JVMPlatform, JSPlatform)
.withoutSuffixFor(JVMPlatform)
.crossType(CrossType.Pure)
.settings(commons: _*)
.settings(
libraryDependencies ++= Seq(
airframeLog
)
)
.dependsOn(parser)
lazy val model = project
lazy val model = crossProject(JVMPlatform, JSPlatform)
.withoutSuffixFor(JVMPlatform)
.crossType(CrossType.Pure)
.settings(commons: _*)
.settings(
libraryDependencies ++= Seq(
catsFree
"org.typelevel" %%% "cats-free" % catsV
)
)
.dependsOn(types)
lazy val `test-kit` = project
lazy val `test-kit` = crossProject(JVMPlatform, JSPlatform)
.withoutSuffixFor(JVMPlatform)
.crossType(CrossType.Pure)
.in(file("model/test-kit"))
.settings(commons: _*)
.dependsOn(model)
lazy val semantics = project
lazy val semantics = crossProject(JVMPlatform, JSPlatform)
.withoutSuffixFor(JVMPlatform)
.crossType(CrossType.Pure)
.settings(commons: _*)
.settings(
libraryDependencies ++= Seq(
"com.github.julien-truffaut" %% "monocle-core" % monocleV,
"com.github.julien-truffaut" %% "monocle-macro" % monocleV
"com.github.julien-truffaut" %%% "monocle-core" % monocleV,
"com.github.julien-truffaut" %%% "monocle-macro" % monocleV
)
)
.dependsOn(model, `test-kit` % Test, parser)
lazy val compiler = project
lazy val compiler = crossProject(JVMPlatform, JSPlatform)
.withoutSuffixFor(JVMPlatform)
.crossType(CrossType.Pure)
.in(file("compiler"))
.settings(commons: _*)
.dependsOn(semantics, linker, backend)
lazy val backend = project
lazy val backend = crossProject(JVMPlatform, JSPlatform)
.withoutSuffixFor(JVMPlatform)
.crossType(CrossType.Pure)
.in(file("backend"))
.settings(commons: _*)
.dependsOn(model)
lazy val `backend-air` = project
lazy val `backend-air` = crossProject(JVMPlatform, JSPlatform)
.withoutSuffixFor(JVMPlatform)
.crossType(CrossType.Pure)
.in(file("backend/air"))
.settings(commons: _*)
.dependsOn(backend)
lazy val `backend-ts` = project
lazy val `backend-ts` = crossProject(JVMPlatform, JSPlatform)
.withoutSuffixFor(JVMPlatform)
.crossType(CrossType.Pure)
.in(file("backend/ts"))
.settings(commons: _*)
.dependsOn(`backend-air`)
lazy val `backend-js` = project
lazy val `backend-js` = crossProject(JVMPlatform, JSPlatform)
.withoutSuffixFor(JVMPlatform)
.crossType(CrossType.Pure)
.in(file("backend/js"))
.settings(commons: _*)
.dependsOn(`backend-air`)

View File

@ -0,0 +1,48 @@
package aqua
import aqua.backend.Backend
import aqua.backend.ts.TypeScriptBackend
import aqua.compiler.{AquaCompiler, AquaError, AquaSources}
import aqua.model.transform.GenerationConfig
import cats.effect.IO
import cats.effect.unsafe.implicits.global
import cats.data.*
import cats.syntax.functor.*
import cats.syntax.applicative.*
import cats.{Applicative, Monad, Show}
import aqua.parser.lift.FileSpan
object JsApp {
trait AquaJsError {}
case class FileId(name: String)
class Sources[F[_]: Applicative] extends AquaSources[F, AquaJsError, FileId] {
def sources: F[ValidatedNec[AquaJsError, Chain[(FileId, String)]]] =
Validated.Valid(Chain.empty).pure[F]
// Resolve id of the imported imp string from I file
def resolveImport(from: FileId, imp: String): F[ValidatedNec[AquaJsError, FileId]] =
Validated.Valid(FileId("")).pure[F]
// Load file by its resolved I
def load(file: FileId): F[ValidatedNec[AquaJsError, String]] = Validated.Valid("").pure[F]
}
def main(args: Array[String]): Unit = {
// test code
// val sources = new Sources[IO]()
// val bodyConfig = GenerationConfig()
// val b = AquaCompiler
// .compileTo[IO, AquaJsError, FileId, FileSpan.F, String](
// sources,
// (fmid, src) => FileSpan.fileSpanLiftParser(fmid.name, src),
// TypeScriptBackend,
// bodyConfig,
// (a) => ???
// )
println("Hello world!")
}
}

View File

@ -6,16 +6,15 @@ import aqua.parser.expr.ConstantExpr
import aqua.parser.lift.LiftParser
import cats.data.Validated.{Invalid, Valid}
import cats.data.{NonEmptyList, Validated, ValidatedNel}
import cats.effect.ExitCode
import cats.effect.{unsafe, ExitCode, IO}
import cats.effect.std.Console
import cats.syntax.functor.*
import cats.syntax.traverse.*
import cats.{Comonad, Functor}
import com.monovore.decline.Opts.help
import com.monovore.decline.{Opts, Visibility}
import wvlet.log.{LogLevel => WLogLevel}
import java.nio.file.Path
import scribe.Level
import fs2.io.file.{Files, Path}
object AppOps {
@ -25,13 +24,13 @@ object AppOps {
val versionOpt: Opts[Unit] =
Opts.flag("version", help = "Show version", "v", Visibility.Partial)
val logLevelOpt: Opts[WLogLevel] =
val logLevelOpt: Opts[Level] =
Opts.option[String]("log-level", help = "Set log level").withDefault("info").mapValidated {
str =>
Validated.fromEither(toLogLevel(str))
}
def toLogLevel(logLevel: String): Either[NonEmptyList[String], WLogLevel] = {
def toLogLevel(logLevel: String): Either[NonEmptyList[String], Level] = {
LogLevel.stringToLogLevel
.get(logLevel.toLowerCase)
.toRight(
@ -42,56 +41,68 @@ object AppOps {
)
}
def checkPath: Path => ValidatedNel[String, Path] = { p =>
Validated
.fromEither(Validated.catchNonFatal {
val f = p.toFile
if (f.exists()) {
if (f.isFile) {
val filename = f.getName
val ext = Option(filename)
.filter(_.contains("."))
.map(f => f.substring(f.lastIndexOf(".") + 1))
.getOrElse("")
if (ext != "aqua") Left("File must be with 'aqua' extension")
else Right(p)
} else Right(p)
} else {
Left(s"There is no path '${p.toString}'")
}
}.toEither.left.map(t => s"An error occurred on imports reading: ${t.getMessage}").flatten)
.toValidatedNel
def checkPath(implicit runtime: unsafe.IORuntime): String => ValidatedNel[String, Path] = {
pathStr =>
Validated
.fromEither(Validated.catchNonFatal {
val p = Path(pathStr)
Files[IO]
.exists(p)
.flatMap { exists =>
if (exists)
Files[IO].isRegularFile(p).map { isFile =>
if (isFile) {
val filename = p.fileName.toString
val ext = Option(filename)
.filter(_.contains("."))
.map(f => f.substring(f.lastIndexOf(".") + 1))
.getOrElse("")
if (ext != "aqua") Left("File must be with 'aqua' extension")
else Right(p)
} else
Right(p)
}
else
IO(Left(s"There is no path '${p.toString}'"))
}
// TODO: make it with correct effects
.unsafeRunSync()
}.toEither.left.map(t => s"An error occurred on imports reading: ${t.getMessage}").flatten)
.toValidatedNel
}
val inputOpts: Opts[Path] =
def inputOpts(implicit runtime: unsafe.IORuntime): Opts[Path] =
Opts
.option[Path](
.option[String](
"input",
"Path to an aqua file or an input directory that contains your .aqua files",
"i"
)
.mapValidated(checkPath)
val outputOpts: Opts[Path] =
Opts.option[Path]("output", "Path to the output directory", "o").mapValidated(checkPath)
def outputOpts(implicit runtime: unsafe.IORuntime): Opts[Path] =
Opts.option[String]("output", "Path to the output directory", "o").mapValidated(checkPath)
val importOpts: Opts[List[Path]] =
def importOpts(implicit runtime: unsafe.IORuntime): Opts[List[Path]] =
Opts
.options[Path]("import", "Path to the directory to import from", "m")
.options[String]("import", "Path to the directory to import from", "m")
.mapValidated { ps =>
val checked = ps
.map(p => {
.map(pStr => {
Validated.catchNonFatal {
val f = p.toFile
if (f.exists() && f.isDirectory) {
Right(p)
} else {
Left(s"There is no path ${p.toString} or it is not a directory")
}
val p = Path(pStr)
(for {
exists <- Files[IO].exists(p)
isDir <- Files[IO].isDirectory(p)
} yield {
if (exists && isDir) Right(p)
else Left(s"There is no path ${p.toString} or it is not a directory")
})
// TODO: make it with correct effects
.unsafeRunSync()
}
})
.toList
checked.map {
case Validated.Valid(pE) =>
pE match {

View File

@ -9,18 +9,16 @@ import aqua.model.transform.GenerationConfig
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
import cats.Id
import cats.data.Validated
import cats.effect._
import cats.effect.std.{Console => ConsoleEff}
import cats.syntax.apply._
import cats.syntax.functor._
import cats.effect.*
import cats.effect.std.Console as ConsoleEff
import cats.syntax.apply.*
import cats.syntax.functor.*
import com.monovore.decline.Opts
import com.monovore.decline.effect.CommandIOApp
import fs2.io.file.Files
import org.typelevel.log4cats.slf4j.Slf4jLogger
import org.typelevel.log4cats.{Logger, SelfAwareStructuredLogger}
import wvlet.log.{LogSupport, Logger => WLogger}
import scribe.Logging
object AquaCli extends IOApp with LogSupport {
object AquaCli extends IOApp with Logging {
import AppOps._
sealed trait CompileTarget
@ -39,7 +37,8 @@ object AquaCli extends IOApp with LogSupport {
}
}
def main[F[_]: Concurrent: Files: ConsoleEff: Logger]: Opts[F[ExitCode]] = {
def main[F[_]: Concurrent: Files: ConsoleEff](runtime: unsafe.IORuntime): Opts[F[ExitCode]] = {
implicit val r = runtime
versionOpt
.as(
versionAndExit
@ -59,8 +58,11 @@ object AquaCli extends IOApp with LogSupport {
constantOpts[Id]
).mapN {
case (input, imports, output, toAir, toJs, noRelay, noXor, h, v, logLevel, constants) =>
WLogger.setDefaultLogLevel(logLevel)
WLogger.setDefaultFormatter(CustomLogFormatter)
scribe.Logger.root
.clearHandlers()
.clearModifiers()
.withHandler(formatter = LogFormatter.formatter, minimumLevel = Some(logLevel))
.replace()
implicit val aio: AquaIO[F] = new AquaFilesIO[F]
@ -75,7 +77,7 @@ object AquaCli extends IOApp with LogSupport {
val bc = GenerationConfig(wrapWithXor = !noXor, constants = constants)
bc.copy(relayVarName = bc.relayVarName.filterNot(_ => noRelay))
}
info(s"Aqua Compiler ${versionStr}")
logger.info(s"Aqua Compiler ${versionStr}")
AquaPathCompiler
.compileFilesTo[F](
input,
@ -89,7 +91,7 @@ object AquaCli extends IOApp with LogSupport {
errs.map(System.out.println)
ExitCode.Error
case Validated.Valid(results) =>
results.map(info(_))
results.map(logger.info(_))
ExitCode.Success
}
}
@ -97,17 +99,13 @@ object AquaCli extends IOApp with LogSupport {
}
override def run(args: List[String]): IO[ExitCode] = {
implicit def logger[F[_]: Sync]: SelfAwareStructuredLogger[F] =
Slf4jLogger.getLogger[F]
CommandIOApp.run[IO](
"aqua-c",
"Aquamarine compiler",
helpFlag = false,
None
)(
main[IO],
main[IO](runtime),
// Weird ugly hack: in case version flag or help flag is present, ignore other options,
// be it correct or not
args match {

View File

@ -3,20 +3,19 @@ package aqua
import aqua.backend.Backend
import aqua.compiler.{AquaCompiler, AquaError}
import aqua.files.{AquaFileSources, FileModuleId}
import aqua.io._
import aqua.io.*
import aqua.model.transform.GenerationConfig
import aqua.parser.lift.FileSpan
import cats.data._
import cats.syntax.functor._
import cats.syntax.show._
import cats.data.*
import cats.syntax.functor.*
import cats.syntax.show.*
import cats.{Monad, Show}
import wvlet.log.LogSupport
import scribe.Logging
import fs2.io.file.{Files, Path}
import java.nio.file.Path
object AquaPathCompiler extends Logging {
object AquaPathCompiler extends LogSupport {
def compileFilesTo[F[_]: AquaIO: Monad](
def compileFilesTo[F[_]: AquaIO: Monad: Files](
srcPath: Path,
imports: List[Path],
targetPath: Path,

View File

@ -53,7 +53,7 @@ object ErrorRendering {
val span = token.unit._1
showForConsole(span, s"Cannot resolve import")
case CycleError(modules) =>
s"Cycle loops detected in imports: ${modules.map(_.file.getFileName)}"
s"Cycle loops detected in imports: ${modules.map(_.file.fileName)}"
case CompileError(err) =>
err match {
case RulesViolated(token, message) =>

View File

@ -5,24 +5,19 @@ import aqua.files.AquaFilesIO
import aqua.model.transform.GenerationConfig
import cats.data.Validated
import cats.effect.{IO, IOApp, Sync}
import org.typelevel.log4cats.SelfAwareStructuredLogger
import org.typelevel.log4cats.slf4j.Slf4jLogger
import java.nio.file.Paths
import fs2.io.file.Path
object Test extends IOApp.Simple {
implicit def logger[F[_]: Sync]: SelfAwareStructuredLogger[F] =
Slf4jLogger.getLogger[F]
implicit val aio: AquaIO[IO] = new AquaFilesIO[IO]
override def run: IO[Unit] =
AquaPathCompiler
.compileFilesTo[IO](
Paths.get("./aqua-src"),
List(Paths.get("./aqua")),
Paths.get("./target"),
Path("./aqua-src"),
List(Path("./aqua")),
Path("./target"),
TypeScriptBackend,
GenerationConfig()
)

View File

@ -3,19 +3,22 @@ package aqua.files
import aqua.AquaIO
import aqua.compiler.{AquaCompiled, AquaSources}
import aqua.io.{AquaFileError, FileSystemError, ListAquaErrors}
import cats.Monad
import cats.{Functor, Monad}
import cats.data.{Chain, NonEmptyChain, Validated, ValidatedNec}
import cats.implicits.catsSyntaxApplicativeId
import cats.syntax.either._
import cats.syntax.flatMap._
import cats.syntax.functor._
import cats.syntax.traverse._
import cats.syntax.either.*
import cats.syntax.flatMap.*
import cats.syntax.functor.*
import cats.syntax.monad.*
import cats.syntax.traverse.*
import fs2.io.file.{Files, Path}
import java.nio.file.{Path, Paths}
import scala.util.Try
class AquaFileSources[F[_]: AquaIO: Monad](sourcesPath: Path, importFrom: List[Path])
extends AquaSources[F, AquaFileError, FileModuleId] {
class AquaFileSources[F[_]: AquaIO: Monad: Files: Functor](
sourcesPath: Path,
importFrom: List[Path]
) extends AquaSources[F, AquaFileError, FileModuleId] {
private val filesIO = implicitly[AquaIO[F]]
override def sources: F[ValidatedNec[AquaFileError, Chain[(FileModuleId, String)]]] =
@ -49,10 +52,11 @@ class AquaFileSources[F[_]: AquaIO: Monad](sourcesPath: Path, importFrom: List[P
from: FileModuleId,
imp: String
): F[ValidatedNec[AquaFileError, FileModuleId]] = {
Validated.fromEither(Try(Paths.get(imp)).toEither.leftMap(FileSystemError.apply)) match {
val validatedPath = Validated.fromEither(Try(Path(imp)).toEither.leftMap(FileSystemError.apply))
validatedPath match {
case Validated.Valid(importP) =>
filesIO
.resolve(importP, from.file.getParent +: importFrom)
.resolve(importP, importFrom.prependedAll(from.file.parent))
.bimap(NonEmptyChain.one, FileModuleId(_))
.value
.map(Validated.fromEither)
@ -64,6 +68,15 @@ class AquaFileSources[F[_]: AquaIO: Monad](sourcesPath: Path, importFrom: List[P
override def load(file: FileModuleId): F[ValidatedNec[AquaFileError, String]] =
filesIO.readFile(file.file).leftMap(NonEmptyChain.one).value.map(Validated.fromEither)
// Get a directory of a file, or this file if it is a directory itself
private def getDir(path: Path): F[Path] = {
Files[F]
.isDirectory(path)
.map { res =>
if (res) path else path.parent.getOrElse(path)
}
}
/**
* @param srcFile aqua source
* @param targetPath a main path where all output files will be written
@ -74,23 +87,36 @@ class AquaFileSources[F[_]: AquaIO: Monad](sourcesPath: Path, importFrom: List[P
srcFile: Path,
targetPath: Path,
suffix: String
): Validated[Throwable, Path] =
Validated.catchNonFatal {
val srcDir = if (sourcesPath.toFile.isDirectory) sourcesPath else sourcesPath.getParent
val srcFilePath = srcDir.toAbsolutePath
.normalize()
.relativize(srcFile.toAbsolutePath.normalize())
): F[Validated[Throwable, Path]] =
getDir(sourcesPath).map { srcDir =>
Validated.catchNonFatal {
val srcFilePath = srcDir.absolute.normalize
.relativize(srcFile.absolute.normalize)
val targetDir =
targetPath.toAbsolutePath
.normalize()
.resolve(
srcFilePath
)
val targetDir =
targetPath.absolute.normalize
.resolve(
srcFilePath
)
targetDir.getParent.resolve(srcFile.getFileName.toString.stripSuffix(".aqua") + suffix)
targetDir.parent
.getOrElse(targetDir)
.resolve(srcFile.fileName.toString.stripSuffix(".aqua") + suffix)
}
}
// Write content to a file and return a success message
private def writeWithResult(target: Path, content: String, size: Int) = {
filesIO
.writeFile(
target,
content
)
.as(s"Result $target: compilation OK ($size functions)")
.value
.map(Validated.fromEither)
}
def write(
targetPath: Path
)(ac: AquaCompiled[FileModuleId]): F[Seq[Validated[AquaFileError, String]]] =
@ -106,19 +132,14 @@ class AquaFileSources[F[_]: AquaIO: Monad](sourcesPath: Path, importFrom: List[P
ac.sourceId.file,
targetPath,
compiled.suffix
).leftMap(FileSystemError.apply)
.map { target =>
filesIO
.writeFile(
target,
compiled.content
)
.as(s"Result $target: compilation OK (${ac.compiled.size} functions)")
.value
.map(Validated.fromEither)
}
// TODO: we use both EitherT and F[Validated] to handle errors, that's why so weird
.traverse(identity)
).flatMap { result =>
result
.leftMap(FileSystemError.apply)
.map { target =>
writeWithResult(target, compiled.content, ac.compiled.size)
}
.traverse(identity)
}
}.traverse(identity)
.map(_.map(_.andThen(identity)))
}

View File

@ -1,11 +1,11 @@
package aqua.files
import java.nio.file.Path
import fs2.io.file.Path
case class FileModuleId private (file: Path)
object FileModuleId {
def apply(file: Path): FileModuleId =
new FileModuleId(file.toAbsolutePath.normalize())
new FileModuleId(file.absolute.normalize)
}

View File

@ -5,18 +5,16 @@ import aqua.files.{AquaFileSources, AquaFilesIO, FileModuleId}
import cats.data.Chain
import cats.effect.IO
import cats.effect.unsafe.implicits.global
import fs2.io.file.Files
import fs2.text
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import java.nio.file.Paths
import fs2.io.file.{Files, Path}
class SourcesSpec extends AnyFlatSpec with Matchers {
implicit val aquaIO: AquaIO[IO] = AquaFilesIO.summon[IO]
"AquaFileSources" should "generate correct fileId with imports" in {
val path = Paths.get("cli/src/test/test-dir/path-test")
val path = Path("cli/.jvm/src/test/test-dir/path-test")
val importPath = path.resolve("imports")
val sourceGen = new AquaFileSources[IO](path, importPath :: Nil)
@ -42,7 +40,7 @@ class SourcesSpec extends AnyFlatSpec with Matchers {
}
"AquaFileSources" should "throw an error if a source file doesn't exist" in {
val path = Paths.get("some/random/path")
val path = Path("some/random/path")
val sourceGen = new AquaFileSources[IO](path, Nil)
@ -51,7 +49,7 @@ class SourcesSpec extends AnyFlatSpec with Matchers {
}
"AquaFileSources" should "throw an error if there is no import that is indicated in a source" in {
val path = Paths.get("cli/src/test/test-dir")
val path = Path("cli/.jvm/src/test/test-dir")
val importPath = path.resolve("random/import/path")
val sourceGen = new AquaFileSources[IO](path, importPath :: Nil)
@ -61,7 +59,7 @@ class SourcesSpec extends AnyFlatSpec with Matchers {
}
"AquaFileSources" should "find correct imports" in {
val srcPath = Paths.get("cli/src/test/test-dir/index.aqua")
val srcPath = Path("cli/.jvm/src/test/test-dir/index.aqua")
val importPath = srcPath.resolve("imports")
val sourceGen = new AquaFileSources[IO](srcPath, importPath :: Nil)
@ -73,7 +71,8 @@ class SourcesSpec extends AnyFlatSpec with Matchers {
.unsafeRunSync()
result.isValid shouldBe true
result.getOrElse(FileModuleId(Paths.get("/some/random"))).file.toFile.exists() shouldBe true
val file = result.getOrElse(FileModuleId(Path("/some/random"))).file
Files[IO].exists(file).unsafeRunSync() shouldBe true
// should be found near src file
val result2 =
@ -82,7 +81,8 @@ class SourcesSpec extends AnyFlatSpec with Matchers {
.unsafeRunSync()
result2.isValid shouldBe true
result2.getOrElse(FileModuleId(Paths.get("/some/random"))).file.toFile.exists() shouldBe true
val file2 = result2.getOrElse(FileModuleId(Path("/some/random"))).file
Files[IO].exists(file2).unsafeRunSync() shouldBe true
// near src file but in another directory
val sourceGen2 = new AquaFileSources[IO](srcPath, Nil)
@ -92,20 +92,21 @@ class SourcesSpec extends AnyFlatSpec with Matchers {
.unsafeRunSync()
result3.isValid shouldBe true
result3.getOrElse(FileModuleId(Paths.get("/some/random"))).file.toFile.exists() shouldBe true
val file3 = result3.getOrElse(FileModuleId(Path("/some/random"))).file
Files[IO].exists(file3).unsafeRunSync() shouldBe true
}
"AquaFileSources" should "resolve correct path for target" in {
val path = Paths.get("cli/src/test/test-dir")
val path = Path("cli/.jvm/src/test/test-dir")
val filePath = path.resolve("some-dir/file.aqua")
val targetPath = Paths.get("/target/dir/")
val targetPath = Path("/target/dir/")
val sourceGen = new AquaFileSources[IO](path, Nil)
val suffix = "_custom.super"
val resolved = sourceGen.resolveTargetPath(filePath, targetPath, suffix)
val resolved = sourceGen.resolveTargetPath(filePath, targetPath, suffix).unsafeRunSync()
resolved.isValid shouldBe true
val targetFilePath = resolved.toOption.get
@ -113,13 +114,13 @@ class SourcesSpec extends AnyFlatSpec with Matchers {
}
"AquaFileSources" should "write correct file with correct path" in {
val path = Paths.get("cli/src/test/test-dir")
val path = Path("cli/.jvm/src/test/test-dir")
val filePath = path.resolve("imports/import.aqua")
val targetPath = path.resolve("target/")
// clean up
val resultPath = Paths.get("cli/src/test/test-dir/target/imports/import_hey.custom")
val resultPath = Path("cli/.jvm/src/test/test-dir/target/imports/import_hey.custom")
Files[IO].deleteIfExists(resultPath).unsafeRunSync()
val sourceGen = new AquaFileSources[IO](path, Nil)
@ -136,13 +137,13 @@ class SourcesSpec extends AnyFlatSpec with Matchers {
Files[IO].exists(resultPath).unsafeRunSync() shouldBe true
val resultText = Files[IO]
.readAll(resultPath, 1000)
.readAll(resultPath)
.fold(
Vector
.empty[Byte]
)((acc, b) => acc :+ b)
.flatMap(fs2.Stream.emits)
.through(text.utf8Decode)
.through(text.utf8.decode)
.attempt
.compile
.last

View File

@ -8,14 +8,14 @@ import cats.effect.unsafe.implicits.global
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import java.nio.file.{Files, Paths}
import fs2.io.file.{Files, Path}
class WriteFileSpec extends AnyFlatSpec with Matchers {
"cli" should "compile aqua code in js" in {
val src = Paths.get("./cli/src/test/aqua")
val targetTs = Files.createTempDirectory("ts")
val targetJs = Files.createTempDirectory("js")
val targetAir = Files.createTempDirectory("air")
val src = Path("./cli/.jvm/src/test/aqua")
val targetTs = Files[IO].createTempDirectory.unsafeRunSync()
val targetJs = Files[IO].createTempDirectory.unsafeRunSync()
val targetAir = Files[IO].createTempDirectory.unsafeRunSync()
import aqua.files.AquaFilesIO.summon
@ -29,8 +29,8 @@ class WriteFileSpec extends AnyFlatSpec with Matchers {
}
.isValid should be(true)
val targetTsFile = targetTs.resolve("test.ts")
targetTsFile.toFile.exists() should be(true)
Files.deleteIfExists(targetTsFile)
Files[IO].exists(targetTsFile).unsafeRunSync() should be(true)
Files[IO].deleteIfExists(targetTsFile).unsafeRunSync()
AquaPathCompiler
.compileFilesTo[IO](src, List.empty, targetJs, JavaScriptBackend, bc)
@ -41,8 +41,8 @@ class WriteFileSpec extends AnyFlatSpec with Matchers {
}
.isValid should be(true)
val targetJsFile = targetJs.resolve("test.js")
targetJsFile.toFile.exists() should be(true)
Files.deleteIfExists(targetJsFile)
Files[IO].exists(targetJsFile).unsafeRunSync() should be(true)
Files[IO].deleteIfExists(targetJsFile).unsafeRunSync()
AquaPathCompiler
.compileFilesTo[IO](src, List.empty, targetAir, AirBackend, bc)
@ -55,11 +55,13 @@ class WriteFileSpec extends AnyFlatSpec with Matchers {
val targetAirFileFirst = targetAir.resolve("test.first.air")
val targetAirFileSecond = targetAir.resolve("test.second.air")
val targetAirFileThird = targetAir.resolve("test.third.air")
targetAirFileFirst.toFile.exists() should be(true)
targetAirFileSecond.toFile.exists() should be(true)
targetAirFileThird.toFile.exists() should be(true)
Files[IO].exists(targetAirFileFirst).unsafeRunSync() should be(true)
Files[IO].exists(targetAirFileSecond).unsafeRunSync() should be(true)
Files[IO].exists(targetAirFileThird).unsafeRunSync() should be(true)
Seq(targetAirFileFirst, targetAirFileSecond, targetAirFileThird).map(Files.deleteIfExists)
Seq(targetAirFileFirst, targetAirFileSecond, targetAirFileThird).map(f =>
Files[IO].deleteIfExists(f).unsafeRunSync()
)
}
}

View File

@ -3,7 +3,7 @@ package aqua
import aqua.io.AquaFileError
import cats.data.{Chain, EitherT, ValidatedNec}
import java.nio.file.Path
import fs2.io.file.Path
trait AquaIO[F[_]] {
def readFile(file: Path): EitherT[F, AquaFileError, String]

View File

@ -1,13 +0,0 @@
package aqua
import wvlet.log.LogFormatter.{appendStackTrace, highlightLog}
import wvlet.log.{LogFormatter, LogRecord}
object CustomLogFormatter extends LogFormatter {
override def formatLog(r: LogRecord): String = {
val log =
s"[${highlightLog(r.level, r.level.name)}] ${highlightLog(r.level, r.getMessage)}"
appendStackTrace(log, r)
}
}

View File

@ -0,0 +1,7 @@
package aqua
import scribe.format._
object LogFormatter {
val formatter: Formatter = formatter"$date ${string("[")}$levelColored${string("]")} $message$mdc"
}

View File

@ -1,16 +1,16 @@
package aqua
import wvlet.log.{LogLevel => WLogLevel}
import scribe.Level
object LogLevel {
val stringToLogLevel: Map[String, WLogLevel] = Map(
("debug" -> WLogLevel.DEBUG),
("trace" -> WLogLevel.TRACE),
("info" -> WLogLevel.INFO),
("off" -> WLogLevel.OFF),
("warn" -> WLogLevel.WARN),
("error" -> WLogLevel.ERROR),
("all" -> WLogLevel.ALL)
val stringToLogLevel: Map[String, Level] = Map(
("debug" -> Level.Debug),
("trace" -> Level.Trace),
("info" -> Level.Info),
("off" -> Level.Fatal),
("warn" -> Level.Warn),
("error" -> Level.Error),
("all" -> Level.Trace)
)
}

View File

@ -1,20 +1,21 @@
package aqua.files
import aqua.AquaIO
import aqua.io._
import aqua.io.*
import cats.data.*
import cats.data.Validated.{Invalid, Valid}
import cats.data._
import cats.effect.kernel.Concurrent
import cats.syntax.applicative._
import cats.syntax.apply._
import cats.syntax.either._
import cats.syntax.flatMap._
import cats.syntax.functor._
import cats.syntax.applicativeError._
import fs2.io.file.Files
import cats.syntax.applicative.*
import cats.syntax.applicativeError.*
import cats.syntax.apply.*
import cats.syntax.either.*
import cats.syntax.flatMap.*
import cats.syntax.functor.*
import cats.syntax.traverse.*
import cats.syntax.foldable.*
import fs2.io.file.{Files, Path}
import fs2.text
import java.nio.file.Path
import scala.util.Try
class AquaFilesIO[F[_]: Files: Concurrent] extends AquaIO[F] {
@ -22,13 +23,13 @@ class AquaFilesIO[F[_]: Files: Concurrent] extends AquaIO[F] {
override def readFile(file: Path): EitherT[F, AquaFileError, String] =
EitherT(
Files[F]
.readAll(file, 4096)
.readAll(file)
.fold(Vector.empty[Byte])((acc, b) => acc :+ b)
// TODO fix for comment on last line in air
// TODO should be fixed by parser
.map(_.appendedAll("\n\r".getBytes))
.flatMap(fs2.Stream.emits)
.through(text.utf8Decode)
.through(text.utf8.decode)
.attempt
.compile
.last
@ -49,14 +50,14 @@ class AquaFilesIO[F[_]: Files: Concurrent] extends AquaIO[F] {
): EitherT[F, AquaFileError, Path] =
in.headOption.fold(notFound)(p =>
EitherT(
Concurrent[F].attempt(p.toFile.isFile.pure[F])
Concurrent[F].attempt(Files[F].isRegularFile(p))
)
.leftMap[AquaFileError](FileSystemError.apply)
.recover({ case _ => false })
.flatMap {
case true =>
EitherT(
Concurrent[F].attempt(p.toAbsolutePath.normalize().pure[F])
Concurrent[F].attempt(p.absolute.normalize.pure[F])
).leftMap[AquaFileError](FileSystemError.apply)
case false =>
findFirstF(in.tail, notFound)
@ -76,56 +77,72 @@ class AquaFilesIO[F[_]: Files: Concurrent] extends AquaIO[F] {
EitherT.leftT(FileNotFound(src, imports))
)
override def listAqua(folder: Path): F[ValidatedNec[AquaFileError, Chain[Path]]] =
Validated
.fromEither(
Try {
val f = folder.toFile
if (!f.exists()) {
Left(FileNotFound(folder, Nil))
} else if (f.isDirectory) {
Right(f.listFiles().toList)
} else {
Right(f :: Nil)
// Get all files for every path if the path in the list is a directory or this path otherwise
private def gatherFiles(files: List[Path], listFunction: (f: Path) => F[ValidatedNec[AquaFileError, Chain[Path]]]): List[F[ValidatedNec[AquaFileError, Chain[Path]]]] = {
files.map(f => gatherFile(f, listFunction))
}
// Get all files if the path is a directory or this path otherwise
private def gatherFile(f: Path, listFunction: (f: Path) => F[ValidatedNec[AquaFileError, Chain[Path]]]): F[ValidatedNec[AquaFileError, Chain[Path]]] = {
Files[F].isDirectory(f).flatMap { isDir =>
if (isDir)
listFunction(f)
else
Files[F].isRegularFile(f).map { isFile =>
if (isFile)
Validated.validNec(Chain.one(f.absolute.normalize))
else
Validated.invalidNec(FileNotFound(f, Nil))
}
}
}
// Get all files if the path is a directory or this path otherwise
override def listAqua(folder: Path): F[ValidatedNec[AquaFileError, Chain[Path]]] = {
Files[F]
.exists(folder)
.flatMap { exists =>
if (!exists) {
Left(FileNotFound(folder, Nil): AquaFileError).pure[F]
} else {
Files[F].isDirectory(folder).flatMap { isDir =>
if (isDir) {
Files[F].list(folder).compile.toList.map(Right(_))
} else {
Right(folder :: Nil).pure[F]
}
}
}.toEither.leftMap[AquaFileError](FileSystemError.apply).flatMap(identity)
)
.leftMap(NonEmptyChain.one)
.pure[F]
}
}
.map(Validated.fromEither)
.map(_.leftMap(NonEmptyChain.one))
.flatMap {
case Valid(files) =>
files.collect {
case f if f.isFile && f.getName.endsWith(".aqua") =>
Validated
.fromTry(
Try(Chain.one(f.toPath.toAbsolutePath.normalize()))
)
.leftMap(FileSystemError.apply)
.leftMap(NonEmptyChain.one)
.pure[F]
case f if f.isDirectory =>
listAqua(f.toPath)
}.foldLeft(Validated.validNec[AquaFileError, Chain[Path]](Chain.nil).pure[F]) {
case (acc, v) =>
(acc, v).mapN(_ combine _)
gatherFiles(files, listAqua).foldLeft(
Validated.validNec[AquaFileError, Chain[Path]](Chain.nil).pure[F]
) { case (acc, v) =>
(acc, v).mapN(_ combine _)
}
case Invalid(errs) =>
Validated.invalid[NonEmptyChain[AquaFileError], Chain[Path]](errs).pure[F]
}
}
private def deleteIfExists(file: Path): EitherT[F, AquaFileError, Boolean] =
Files[F].deleteIfExists(file).attemptT.leftMap(FileSystemError.apply)
private def createDirectories(path: Path): EitherT[F, AquaFileError, Path] =
Files[F].createDirectories(path).attemptT.leftMap(FileSystemError.apply)
private def createDirectories(path: Path): EitherT[F, AquaFileError, Unit] =
Files[F].createDirectories(path).attemptT.leftMap(FileSystemError.apply(_): AquaFileError)
// Writes to a file, creates directories if they do not exist
override def writeFile(file: Path, content: String): EitherT[F, AquaFileError, Unit] =
deleteIfExists(file) >> createDirectories(file.getParent) >>
deleteIfExists(file) >> file.parent
.map(createDirectories)
.getOrElse(EitherT.liftF(().pure[F])) >>
EitherT(
fs2.Stream
.emit(content)
.through(text.utf8Encode)
.through(text.utf8.encode)
.through(Files[F].writeAll(file))
.attempt
.compile

View File

@ -2,7 +2,7 @@ package aqua.io
import cats.data.NonEmptyChain
import java.nio.file.Path
import fs2.io.file.Path
sealed trait AquaFileError {
def showForConsole: String

View File

@ -3,11 +3,11 @@ package aqua.linker
import cats.data.{NonEmptyChain, Validated, ValidatedNec}
import cats.kernel.{Monoid, Semigroup}
import cats.syntax.monoid._
import wvlet.log.LogSupport
import scribe.Logging
import scala.annotation.tailrec
object Linker extends LogSupport {
object Linker extends Logging {
@tailrec
def iter[I, E, T: Semigroup](
@ -20,22 +20,22 @@ object Linker extends LogSupport {
Right(proc)
case _ =>
val (canHandle, postpone) = mods.partition(_.dependsOn.keySet.forall(proc.contains))
debug("ITERATE, can handle: " + canHandle.map(_.id))
debug(s"dependsOn = ${mods.map(_.dependsOn.keySet)}")
debug(s"postpone = ${postpone.map(_.id)}")
debug(s"proc = ${proc.keySet}")
logger.debug("ITERATE, can handle: " + canHandle.map(_.id))
logger.debug(s"dependsOn = ${mods.map(_.dependsOn.keySet)}")
logger.debug(s"postpone = ${postpone.map(_.id)}")
logger.debug(s"proc = ${proc.keySet}")
if (canHandle.isEmpty && postpone.nonEmpty)
Left(cycleError(postpone))
else {
val folded = canHandle.foldLeft(proc) { case (acc, m) =>
val importKeys = m.dependsOn.keySet
debug(s"${m.id} dependsOn $importKeys")
logger.debug(s"${m.id} dependsOn $importKeys")
val deps: T => T =
importKeys.map(acc).foldLeft[T => T](identity) { case (fAcc, f) =>
debug("COMBINING ONE TIME ")
logger.debug("COMBINING ONE TIME ")
t => {
debug(s"call combine $t")
logger.debug(s"call combine $t")
fAcc(t) |+| f(t)
}
}

View File

@ -7,7 +7,7 @@ import cats.Monoid
import cats.data.NonEmptyMap
import cats.syntax.functor.*
import cats.syntax.monoid.*
import wvlet.log.LogSupport
import scribe.Logging
import scala.collection.immutable.SortedMap
@ -19,7 +19,7 @@ case class AquaContext(
// TODO: merge this with abilities, when have ability resolution variance
services: Map[String, ServiceModel]
) {
private def prefixFirst[T](prefix: String, pair: (String, T)): (String, T) =
(prefix + pair._1, pair._2)
@ -28,7 +28,7 @@ case class AquaContext(
.foldLeft(types) { case (ts, (k, v)) =>
ts ++ v.allTypes(k + ".")
}
.map(prefixFirst(prefix, _))
.map(prefixFirst(prefix, _))
def allFuncs(prefix: String = ""): Map[String, FuncCallable] =
abilities
@ -67,7 +67,7 @@ case class AquaContext(
.map(StructType(name, _))
}
object AquaContext extends LogSupport {
object AquaContext extends Logging {
trait Implicits {
implicit val aquaContextMonoid: Monoid[AquaContext]

View File

@ -3,7 +3,7 @@ package aqua.model
import aqua.types._
import cats.Eq
import cats.data.{Chain, NonEmptyMap}
import wvlet.log.LogSupport
import scribe.Logging
sealed trait ValueModel {
def `type`: Type
@ -46,7 +46,8 @@ case class IntoFieldModel(field: String, `type`: Type) extends LambdaModel
case class IntoIndexModel(idx: Int, `type`: Type) extends LambdaModel
case class VarModel(name: String, `type`: Type, lambda: Chain[LambdaModel] = Chain.empty)
extends ValueModel with LogSupport {
extends ValueModel with Logging {
def deriveFrom(vm: VarModel): VarModel = vm.copy(lambda = vm.lambda ++ lambda)
override val lastType: Type = lambda.lastOption.map(_.`type`).getOrElse(`type`)
@ -81,7 +82,7 @@ case class VarModel(name: String, `type`: Type, lambda: Chain[LambdaModel] = Cha
deriveFrom(nvm)
case valueModel =>
if (lambda.nonEmpty)
error(
logger.error(
s"Var $name derived from scalar $valueModel, but lambda is lost: $lambda"
)
valueModel

View File

@ -7,7 +7,7 @@ import aqua.types.{ArrowType, ProductType, StreamType, Type}
import cats.Eval
import cats.data.Chain
import cats.free.Cofree
import wvlet.log.Logger
import scribe.Logging
case class FuncCallable(
funcName: String,
@ -16,10 +16,7 @@ case class FuncCallable(
ret: List[ValueModel],
capturedArrows: Map[String, FuncCallable],
capturedValues: Map[String, ValueModel]
) extends Model {
private val logger = Logger.of[FuncCallable]
import logger._
) extends Model with Logging {
lazy val args: List[(String, Type)] = arrowType.domain.toLabelledList()
lazy val argNames: List[String] = args.map(_._1)
@ -48,7 +45,7 @@ case class FuncCallable(
forbiddenNames: Set[String]
): Eval[(FuncOp, List[ValueModel])] = {
debug("Call: " + call)
logger.debug("Call: " + call)
// Collect all arguments: what names are used inside the function, what values are received
val argsFull = ArgsCall(arrowType.domain, call.args)
@ -140,7 +137,7 @@ case class FuncCallable(
case (acc @ (_, resolvedExports), tag) =>
tag match {
case CallArrowTag(fn, _) if !allArrows.contains(fn) =>
error(s"UNRESOLVED $fn in $funcName, skipping, will become (null) in AIR!")
logger.error(s"UNRESOLVED $fn in $funcName, skipping, will become (null) in AIR!")
case _ =>
}

View File

@ -3,11 +3,11 @@ package aqua.model.func.raw
import aqua.model.ValueModel
import cats.data.Chain
import cats.data.Chain.{:==, ==:, nil}
import wvlet.log.LogSupport
import scribe.Logging
import scala.annotation.tailrec
object PathFinder extends LogSupport {
object PathFinder extends Logging {
def find(from: RawCursor, to: RawCursor, isExit: Boolean = false): Chain[ValueModel] = {
@ -21,14 +21,14 @@ object PathFinder extends LogSupport {
!to.parentTag.exists(_.isInstanceOf[ParGroupTag])
if (wasHandled) {
debug("Was handled")
debug(" :: " + from)
debug(" -> " + to)
logger.debug("Was handled")
logger.debug(" :: " + from)
logger.debug(" -> " + to)
Chain.empty
} else {
debug("Find path")
debug(" :: " + from)
debug(" -> " + to)
logger.debug("Find path")
logger.debug(" :: " + from)
logger.debug(" -> " + to)
findPath(
fromOn,
toOn,
@ -49,10 +49,10 @@ object PathFinder extends LogSupport {
case (acc, p) if acc.contains(p) => acc.takeWhile(_ != p) :+ p
case (acc, p) => acc :+ p
}
trace(s"PEER IDS: $optimized")
trace(s"PREFIX: $prefix")
trace(s"SUFFIX: $suffix")
trace(s"OPTIMIZED WITH PREFIX AND SUFFIX: $optimized")
logger.trace(s"PEER IDS: $optimized")
logger.trace(s"PREFIX: $prefix")
logger.trace(s"SUFFIX: $suffix")
logger.trace(s"OPTIMIZED WITH PREFIX AND SUFFIX: $optimized")
val noPrefix = skipPrefix(optimized, prefix, optimized)
skipSuffix(noPrefix, suffix, noPrefix)
}
@ -63,28 +63,28 @@ object PathFinder extends LogSupport {
fromPeer: Option[ValueModel],
toPeer: Option[ValueModel]
): Chain[ValueModel] = {
trace(s"FROM ON: $fromOn")
trace(s"TO ON: $toOn")
logger.trace(s"FROM ON: $fromOn")
logger.trace(s"TO ON: $toOn")
val (from, to) = skipCommonPrefix(fromOn, toOn)
val fromFix =
if (from.isEmpty && fromPeer != toPeer) Chain.fromOption(fromOn.lastOption) else from
val toFix = if (to.isEmpty && fromPeer != toPeer) Chain.fromOption(toOn.lastOption) else to
trace("FIND PATH FROM | " + fromFix)
trace(" TO | " + toFix)
logger.trace("FIND PATH FROM | " + fromFix)
logger.trace(" TO | " + toFix)
val fromTo = fromFix.reverse.flatMap(_.via.reverse) ++ toFix.flatMap(_.via)
trace(s"FROM TO: $fromTo")
logger.trace(s"FROM TO: $fromTo")
val fromPeerCh = Chain.fromOption(fromPeer)
val toPeerCh = Chain.fromOption(toPeer)
val optimized = optimizePath(fromPeerCh ++ fromTo ++ toPeerCh, fromPeerCh, toPeerCh)
trace(
logger.trace(
s"FROM PEER '${fromPeer.map(_.toString).getOrElse("None")}' TO PEER '${toPeer.map(_.toString).getOrElse("None")}'"
)
trace(" Optimized: " + optimized)
logger.trace(" Optimized: " + optimized)
optimized
}

View File

@ -6,11 +6,12 @@ import cats.Eval
import cats.data.{Chain, NonEmptyList, OptionT}
import cats.free.Cofree
import cats.syntax.traverse._
import wvlet.log.LogSupport
import scribe.Logging
// Can be heavily optimized by caching parent cursors, not just list of zippers
case class RawCursor(tree: NonEmptyList[ChainZipper[FuncOp.Tree]])
extends ChainCursor[RawCursor, FuncOp.Tree](RawCursor.apply) with LogSupport {
extends ChainCursor[RawCursor, FuncOp.Tree](RawCursor.apply) with Logging {
def tag: RawTag = current.head
def parentTag: Option[RawTag] = parent.map(_.head)

View File

@ -10,9 +10,9 @@ import cats.data.Chain.nil
import cats.data.{Chain, NonEmptyChain, NonEmptyList, OptionT}
import cats.free.Cofree
import cats.syntax.traverse._
import wvlet.log.LogSupport
import scribe.Logging
object Topology extends LogSupport {
object Topology extends Logging {
type Tree = Cofree[Chain, RawTag]
type Res = Cofree[Chain, ResolvedOp]
@ -50,7 +50,7 @@ object Topology extends LogSupport {
val cursor = RawCursor(NonEmptyList.one(ChainZipper.one(op)))
val resolvedCofree = cursor
.cata(wrap) { rc =>
debug(s"<:> $rc")
logger.debug(s"<:> $rc")
val resolved = MakeRes
.resolve(rc.currentPeerId)
.lift
@ -64,12 +64,12 @@ object Topology extends LogSupport {
through(rc.pathToNext)
)
if (cz.next.nonEmpty || cz.prev.nonEmpty) {
debug(s"Resolved $rc -> $cofree")
logger.debug(s"Resolved $rc -> $cofree")
if (cz.prev.nonEmpty)
trace("From prev: " + cz.prev.map(_.head).toList.mkString(" -> "))
logger.trace("From prev: " + cz.prev.map(_.head).toList.mkString(" -> "))
if (cz.next.nonEmpty)
trace("To next: " + cz.next.map(_.head).toList.mkString(" -> "))
} else debug(s"EMPTY $rc -> $cofree")
logger.trace("To next: " + cz.next.map(_.head).toList.mkString(" -> "))
} else logger.debug(s"EMPTY $rc -> $cofree")
cz
}
)
@ -78,11 +78,11 @@ object Topology extends LogSupport {
resolvedCofree.map(NonEmptyChain.fromChain(_).map(_.uncons)).map {
case None =>
error("Topology emitted nothing")
logger.error("Topology emitted nothing")
Cofree(SeqRes, MakeRes.nilTail)
case Some((el, `nil`)) => el
case Some((el, tail)) =>
warn("Topology emitted many nodes, that's unusual")
logger.warn("Topology emitted many nodes, that's unusual")
Cofree(SeqRes, Eval.now(el +: tail))
}
}

View File

@ -7,9 +7,9 @@ import aqua.model.topology.Topology
import aqua.types.ScalarType
import cats.data.Chain
import cats.free.Cofree
import wvlet.log.LogSupport
import scribe.Logging
object Transform extends LogSupport {
object Transform extends Logging {
def defaultFilter(t: ResolvedOp): Boolean = t match {
case _: NoAir => false

View File

@ -113,7 +113,7 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
| <- v
|""".stripMargin
parser[Id]().parseAll(script).value.toEither shouldBe Symbol("left")
parser[Id]().parseAll(script).value.toEither.isLeft shouldBe true
}
"function with multiline definitions" should "parse without error" in {
@ -121,7 +121,7 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
"""func tryGen(a: string,
| b: string)""".stripMargin
FuncExpr.p[Id].parseAll(script) shouldBe Symbol("right")
FuncExpr.p[Id].parseAll(script).isRight shouldBe true
}
"function with root expression without children" should "parse with error" in {
@ -131,7 +131,7 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
| <- v
|""".stripMargin
parser[Id]().parseAll(script).value.toEither shouldBe Symbol("left")
parser[Id]().parseAll(script).value.toEither.isLeft shouldBe true
}
"multi function expression" should "parse" in {

View File

@ -1 +1,3 @@
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.15.0")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.15.0")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.7.0")
addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.1.0")

View File

@ -23,9 +23,9 @@ import cats.syntax.apply._
import cats.syntax.semigroup._
import monocle.Lens
import monocle.macros.GenLens
import wvlet.log.LogSupport
import scribe.Logging
object Semantics extends LogSupport {
object Semantics extends Logging {
def folder[F[_], G[_]](implicit
A: AbilitiesAlgebra[F, G],