mirror of
https://github.com/fluencelabs/aqua.git
synced 2024-12-04 22:50:18 +00:00
Js build (#247)
This commit is contained in:
parent
f59a93ac27
commit
296c64836d
@ -56,6 +56,7 @@ lazy val cli = crossProject(JSPlatform, JVMPlatform)
|
|||||||
|
|
||||||
lazy val cliJS = cli.js
|
lazy val cliJS = cli.js
|
||||||
.settings(
|
.settings(
|
||||||
|
scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.CommonJSModule)),
|
||||||
scalaJSUseMainModuleInitializer := true
|
scalaJSUseMainModuleInitializer := true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
package aqua
|
|
||||||
|
|
||||||
import aqua.backend.Backend
|
|
||||||
import aqua.backend.ts.TypeScriptBackend
|
|
||||||
import aqua.compiler.{AquaCompiler, AquaError, AquaSources}
|
|
||||||
import aqua.model.transform.TransformConfig
|
|
||||||
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!")
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,11 +6,11 @@ import cats.data.Chain
|
|||||||
import cats.effect.IO
|
import cats.effect.IO
|
||||||
import cats.effect.unsafe.implicits.global
|
import cats.effect.unsafe.implicits.global
|
||||||
import fs2.text
|
import fs2.text
|
||||||
import org.scalatest.flatspec.AnyFlatSpec
|
|
||||||
import org.scalatest.matchers.should.Matchers
|
import org.scalatest.matchers.should.Matchers
|
||||||
import fs2.io.file.{Files, Path}
|
import fs2.io.file.{Files, Path}
|
||||||
|
import org.scalatest.flatspec.AsyncFlatSpec
|
||||||
|
|
||||||
class SourcesSpec extends AnyFlatSpec with Matchers {
|
class SourcesSpec extends AsyncFlatSpec with Matchers {
|
||||||
implicit val aquaIO: AquaIO[IO] = AquaFilesIO.summon[IO]
|
implicit val aquaIO: AquaIO[IO] = AquaFilesIO.summon[IO]
|
||||||
|
|
||||||
"AquaFileSources" should "generate correct fileId with imports" in {
|
"AquaFileSources" should "generate correct fileId with imports" in {
|
||||||
@ -18,25 +18,25 @@ class SourcesSpec extends AnyFlatSpec with Matchers {
|
|||||||
val importPath = path.resolve("imports")
|
val importPath = path.resolve("imports")
|
||||||
|
|
||||||
val sourceGen = new AquaFileSources[IO](path, importPath :: Nil)
|
val sourceGen = new AquaFileSources[IO](path, importPath :: Nil)
|
||||||
|
sourceGen.sources.map { result =>
|
||||||
|
result.isValid shouldBe true
|
||||||
|
|
||||||
val result = sourceGen.sources.unsafeRunSync()
|
val listResult = result
|
||||||
result.isValid shouldBe true
|
.getOrElse(Chain.empty)
|
||||||
|
.toList
|
||||||
|
.map { case (fid, s) =>
|
||||||
|
(fid.file.toString.split("/").last, s)
|
||||||
|
}
|
||||||
|
.sortBy(_._1) // sort cause different systems have different order of file reading
|
||||||
|
|
||||||
val listResult = result
|
val (id, importFile) = listResult(1)
|
||||||
.getOrElse(Chain.empty)
|
id shouldBe "index.aqua"
|
||||||
.toList
|
importFile.nonEmpty shouldBe true
|
||||||
.map { case (fid, s) =>
|
|
||||||
(fid.file.toString.split("/").last, s)
|
|
||||||
}
|
|
||||||
.sortBy(_._1) // sort cause different systems have different order of file reading
|
|
||||||
|
|
||||||
val (id, importFile) = listResult(1)
|
val (importNearId, importFileNear) = listResult.head
|
||||||
id shouldBe "index.aqua"
|
importNearId shouldBe "importNear.aqua"
|
||||||
importFile.nonEmpty shouldBe true
|
importFileNear.nonEmpty shouldBe true
|
||||||
|
}.unsafeToFuture()
|
||||||
val (importNearId, importFileNear) = listResult.head
|
|
||||||
importNearId shouldBe "importNear.aqua"
|
|
||||||
importFileNear.nonEmpty shouldBe true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"AquaFileSources" should "throw an error if a source file doesn't exist" in {
|
"AquaFileSources" should "throw an error if a source file doesn't exist" in {
|
||||||
@ -44,8 +44,7 @@ class SourcesSpec extends AnyFlatSpec with Matchers {
|
|||||||
|
|
||||||
val sourceGen = new AquaFileSources[IO](path, Nil)
|
val sourceGen = new AquaFileSources[IO](path, Nil)
|
||||||
|
|
||||||
val result = sourceGen.sources.unsafeRunSync()
|
sourceGen.sources.map(result => result.isInvalid shouldBe true).unsafeToFuture()
|
||||||
result.isInvalid shouldBe true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"AquaFileSources" should "throw an error if there is no import that is indicated in a source" in {
|
"AquaFileSources" should "throw an error if there is no import that is indicated in a source" in {
|
||||||
@ -53,9 +52,10 @@ class SourcesSpec extends AnyFlatSpec with Matchers {
|
|||||||
val importPath = path.resolve("random/import/path")
|
val importPath = path.resolve("random/import/path")
|
||||||
|
|
||||||
val sourceGen = new AquaFileSources[IO](path, importPath :: Nil)
|
val sourceGen = new AquaFileSources[IO](path, importPath :: Nil)
|
||||||
val result =
|
sourceGen
|
||||||
sourceGen.resolveImport(FileModuleId(path.resolve("no-file.aqua")), "no/file").unsafeRunSync()
|
.resolveImport(FileModuleId(path.resolve("no-file.aqua")), "no/file")
|
||||||
result.isInvalid shouldBe true
|
.map(result => result.isInvalid shouldBe true)
|
||||||
|
.unsafeToFuture()
|
||||||
}
|
}
|
||||||
|
|
||||||
"AquaFileSources" should "find correct imports" in {
|
"AquaFileSources" should "find correct imports" in {
|
||||||
@ -64,36 +64,33 @@ class SourcesSpec extends AnyFlatSpec with Matchers {
|
|||||||
|
|
||||||
val sourceGen = new AquaFileSources[IO](srcPath, importPath :: Nil)
|
val sourceGen = new AquaFileSources[IO](srcPath, importPath :: Nil)
|
||||||
|
|
||||||
// should be found in importPath
|
(for {
|
||||||
val result =
|
// should be found in importPath
|
||||||
sourceGen
|
result <- sourceGen.resolveImport(FileModuleId(srcPath), "imports/import.aqua")
|
||||||
.resolveImport(FileModuleId(srcPath), "imports/import.aqua")
|
exists <- {
|
||||||
.unsafeRunSync()
|
result.isValid shouldBe true
|
||||||
|
val file = result.getOrElse(FileModuleId(Path("/some/random"))).file
|
||||||
|
Files[IO].exists(file)
|
||||||
|
}
|
||||||
|
_ = exists shouldBe true
|
||||||
|
|
||||||
result.isValid shouldBe true
|
// should be found near src file
|
||||||
val file = result.getOrElse(FileModuleId(Path("/some/random"))).file
|
result2 <- sourceGen.resolveImport(FileModuleId(srcPath), "importNear.aqua")
|
||||||
Files[IO].exists(file).unsafeRunSync() shouldBe true
|
exists2 <- {
|
||||||
|
result2.isValid shouldBe true
|
||||||
// should be found near src file
|
val file2 = result2.getOrElse(FileModuleId(Path("/some/random"))).file
|
||||||
val result2 =
|
Files[IO].exists(file2)
|
||||||
sourceGen
|
}
|
||||||
.resolveImport(FileModuleId(srcPath), "importNear.aqua")
|
_ = exists2 shouldBe true
|
||||||
.unsafeRunSync()
|
// near src file but in another directory
|
||||||
|
sourceGen2 = new AquaFileSources[IO](srcPath, Nil)
|
||||||
result2.isValid shouldBe true
|
result3 <- sourceGen2.resolveImport(FileModuleId(srcPath), "imports/import.aqua")
|
||||||
val file2 = result2.getOrElse(FileModuleId(Path("/some/random"))).file
|
exists3 <- {
|
||||||
Files[IO].exists(file2).unsafeRunSync() shouldBe true
|
result3.isValid shouldBe true
|
||||||
|
val file3 = result3.getOrElse(FileModuleId(Path("/some/random"))).file
|
||||||
// near src file but in another directory
|
Files[IO].exists(file3)
|
||||||
val sourceGen2 = new AquaFileSources[IO](srcPath, Nil)
|
}
|
||||||
val result3 =
|
} yield { exists3 shouldBe true }).unsafeToFuture()
|
||||||
sourceGen2
|
|
||||||
.resolveImport(FileModuleId(srcPath), "imports/import.aqua")
|
|
||||||
.unsafeRunSync()
|
|
||||||
|
|
||||||
result3.isValid 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 {
|
"AquaFileSources" should "resolve correct path for target" in {
|
||||||
@ -106,11 +103,15 @@ class SourcesSpec extends AnyFlatSpec with Matchers {
|
|||||||
|
|
||||||
val suffix = "_custom.super"
|
val suffix = "_custom.super"
|
||||||
|
|
||||||
val resolved = sourceGen.resolveTargetPath(filePath, targetPath, suffix).unsafeRunSync()
|
sourceGen
|
||||||
resolved.isValid shouldBe true
|
.resolveTargetPath(filePath, targetPath, suffix)
|
||||||
|
.map { resolved =>
|
||||||
|
resolved.isValid shouldBe true
|
||||||
|
|
||||||
val targetFilePath = resolved.toOption.get
|
val targetFilePath = resolved.toOption.get
|
||||||
targetFilePath.toString shouldBe "/target/dir/some-dir/file_custom.super"
|
targetFilePath.toString shouldBe "/target/dir/some-dir/file_custom.super"
|
||||||
|
}
|
||||||
|
.unsafeToFuture()
|
||||||
}
|
}
|
||||||
|
|
||||||
"AquaFileSources" should "write correct file with correct path" in {
|
"AquaFileSources" should "write correct file with correct path" in {
|
||||||
@ -121,38 +122,36 @@ class SourcesSpec extends AnyFlatSpec with Matchers {
|
|||||||
|
|
||||||
// clean up
|
// clean up
|
||||||
val resultPath = Path("cli/.jvm/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()
|
(for {
|
||||||
|
_ <- Files[IO].deleteIfExists(resultPath)
|
||||||
val sourceGen = new AquaFileSources[IO](path, Nil)
|
sourceGen = new AquaFileSources[IO](path, Nil)
|
||||||
val content = "some random content"
|
content = "some random content"
|
||||||
val compiled = AquaCompiled[FileModuleId](
|
compiled = AquaCompiled[FileModuleId](
|
||||||
FileModuleId(filePath),
|
FileModuleId(filePath),
|
||||||
Seq(Generated("_hey.custom", content))
|
Seq(Generated("_hey.custom", content))
|
||||||
)
|
)
|
||||||
|
resolved <- sourceGen.write(targetPath)(compiled)
|
||||||
val resolved = sourceGen.write(targetPath)(compiled).unsafeRunSync()
|
_ = {
|
||||||
resolved.size shouldBe 1
|
resolved.size shouldBe 1
|
||||||
resolved.head.isValid shouldBe true
|
resolved.head.isValid shouldBe true
|
||||||
|
}
|
||||||
Files[IO].exists(resultPath).unsafeRunSync() shouldBe true
|
exists <- Files[IO].exists(resultPath)
|
||||||
|
_ = exists shouldBe true
|
||||||
val resultText = Files[IO]
|
result <- Files[IO]
|
||||||
.readAll(resultPath)
|
.readAll(resultPath)
|
||||||
.fold(
|
.fold(
|
||||||
Vector
|
Vector
|
||||||
.empty[Byte]
|
.empty[Byte]
|
||||||
)((acc, b) => acc :+ b)
|
)((acc, b) => acc :+ b)
|
||||||
.flatMap(fs2.Stream.emits)
|
.flatMap(fs2.Stream.emits)
|
||||||
.through(text.utf8.decode)
|
.through(text.utf8.decode)
|
||||||
.attempt
|
.attempt
|
||||||
.compile
|
.compile
|
||||||
.last
|
.last
|
||||||
.unsafeRunSync()
|
resultText = result.get.right.get
|
||||||
.get
|
_ <- Files[IO].deleteIfExists(resultPath)
|
||||||
.right
|
} yield {
|
||||||
.get
|
resultText shouldBe content
|
||||||
resultText shouldBe content
|
}).unsafeToFuture()
|
||||||
|
|
||||||
Files[IO].deleteIfExists(resultPath).unsafeRunSync()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
package aqua
|
package aqua
|
||||||
|
|
||||||
|
import aqua.backend.Version
|
||||||
import aqua.model.LiteralModel
|
import aqua.model.LiteralModel
|
||||||
import aqua.model.transform.TransformConfig
|
import aqua.model.transform.TransformConfig
|
||||||
import aqua.parser.expr.ConstantExpr
|
import aqua.parser.expr.ConstantExpr
|
||||||
import aqua.parser.lift.LiftParser
|
import aqua.parser.lift.LiftParser
|
||||||
import cats.data.Validated.{Invalid, Valid}
|
import cats.data.Validated.{Invalid, Valid}
|
||||||
import cats.data.{NonEmptyList, Validated, ValidatedNel}
|
import cats.data.{NonEmptyList, Validated, ValidatedNec, ValidatedNel}
|
||||||
import cats.effect.{ExitCode, IO, unsafe}
|
import cats.effect.{ExitCode, IO}
|
||||||
import cats.effect.std.Console
|
import cats.effect.std.Console
|
||||||
import cats.syntax.functor.*
|
import cats.syntax.functor.*
|
||||||
import cats.syntax.traverse.*
|
import cats.syntax.traverse.*
|
||||||
import cats.{Comonad, Functor}
|
import cats.syntax.applicative.*
|
||||||
|
import cats.syntax.flatMap.*
|
||||||
|
import cats.{Comonad, Functor, Monad}
|
||||||
import com.monovore.decline.Opts.help
|
import com.monovore.decline.Opts.help
|
||||||
import com.monovore.decline.{Opts, Visibility}
|
import com.monovore.decline.{Opts, Visibility}
|
||||||
import scribe.Level
|
import scribe.Level
|
||||||
@ -41,84 +44,56 @@ object AppOps {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def checkPath(implicit runtime: unsafe.IORuntime): String => ValidatedNel[String, Path] = {
|
def checkPath[F[_]: Monad: Files](pathStr: String): F[ValidatedNec[String, Path]] = {
|
||||||
pathStr =>
|
val p = Path(pathStr)
|
||||||
Validated
|
Files[F]
|
||||||
.fromEither(Validated.catchNonFatal {
|
.exists(p)
|
||||||
val p = Path(pathStr)
|
.flatMap { exists =>
|
||||||
Files[IO]
|
if (exists)
|
||||||
.exists(p)
|
Files[F].isRegularFile(p).map { isFile =>
|
||||||
.flatMap { exists =>
|
if (isFile) {
|
||||||
if (exists)
|
if (p.extName != ".aqua") Validated.invalidNec("File must be with 'aqua' extension")
|
||||||
Files[IO].isRegularFile(p).map { isFile =>
|
else Validated.validNec(p)
|
||||||
if (isFile) {
|
} else
|
||||||
val filename = p.fileName.toString
|
Validated.validNec(p)
|
||||||
val ext = Option(filename)
|
}
|
||||||
.filter(_.contains("."))
|
else
|
||||||
.map(f => f.substring(f.lastIndexOf(".") + 1))
|
Validated.invalidNec(s"There is no path '${p.toString}'").pure[F]
|
||||||
.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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def inputOpts(implicit runtime: unsafe.IORuntime): Opts[Path] =
|
def inputOpts[F[_]: Monad: Files]: Opts[F[ValidatedNec[String, Path]]] =
|
||||||
Opts
|
Opts
|
||||||
.option[String](
|
.option[String](
|
||||||
"input",
|
"input",
|
||||||
"Path to an aqua file or an input directory that contains your .aqua files",
|
"Path to an aqua file or an input directory that contains your .aqua files",
|
||||||
"i"
|
"i"
|
||||||
)
|
)
|
||||||
.mapValidated(checkPath)
|
.map(s => checkPath[F](s))
|
||||||
|
|
||||||
def outputOpts(implicit runtime: unsafe.IORuntime): Opts[Path] =
|
def outputOpts[F[_]: Monad: Files]: Opts[F[ValidatedNec[String, Path]]] =
|
||||||
Opts.option[String]("output", "Path to the output directory", "o").mapValidated(checkPath)
|
Opts.option[String]("output", "Path to the output directory", "o").map(s => checkPath[F](s))
|
||||||
|
|
||||||
def importOpts(implicit runtime: unsafe.IORuntime): Opts[List[Path]] =
|
def importOpts[F[_]: Monad: Files]: Opts[F[ValidatedNec[String, List[Path]]]] =
|
||||||
Opts
|
Opts
|
||||||
.options[String]("import", "Path to the directory to import from", "m")
|
.options[String]("import", "Path to the directory to import from", "m")
|
||||||
.mapValidated { ps =>
|
.map { ps =>
|
||||||
val checked = ps
|
val checked: List[F[ValidatedNec[String, Path]]] = ps.toList.map { pStr =>
|
||||||
.map(pStr => {
|
val p = Path(pStr)
|
||||||
Validated.catchNonFatal {
|
(for {
|
||||||
val p = Path(pStr)
|
exists <- Files[F].exists(p)
|
||||||
(for {
|
isDir <- Files[F].isDirectory(p)
|
||||||
exists <- Files[IO].exists(p)
|
} yield {
|
||||||
isDir <- Files[IO].isDirectory(p)
|
if (exists && isDir) Validated.validNec[String, Path](p)
|
||||||
} yield {
|
else
|
||||||
if (exists && isDir) Right(p)
|
Validated.invalidNec[String, Path](
|
||||||
else Left(s"There is no path ${p.toString} or it is not a directory")
|
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 {
|
|
||||||
case Right(p) =>
|
|
||||||
Validated.Valid(p)
|
|
||||||
case Left(e) =>
|
|
||||||
Validated.Invalid(e)
|
|
||||||
}
|
|
||||||
case Validated.Invalid(e) =>
|
|
||||||
Validated.Invalid(s"Error occurred on imports reading: ${e.getMessage}")
|
|
||||||
}.traverse {
|
|
||||||
case Valid(a) => Validated.validNel(a)
|
|
||||||
case Invalid(e) => Validated.invalidNel(e)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checked.sequence.map(_.sequence)
|
||||||
}
|
}
|
||||||
.withDefault(List.empty)
|
|
||||||
|
|
||||||
def constantOpts[F[_]: LiftParser: Comonad]: Opts[List[TransformConfig.Const]] =
|
def constantOpts[F[_]: LiftParser: Comonad]: Opts[List[TransformConfig.Const]] =
|
||||||
Opts
|
Opts
|
||||||
@ -168,7 +143,7 @@ object AppOps {
|
|||||||
.withDefault(false)
|
.withDefault(false)
|
||||||
|
|
||||||
lazy val versionStr: String =
|
lazy val versionStr: String =
|
||||||
Option(getClass.getPackage.getImplementationVersion).filter(_.nonEmpty).getOrElse("no version")
|
Version.version
|
||||||
|
|
||||||
def versionAndExit[F[_]: Console: Functor]: F[ExitCode] = Console[F]
|
def versionAndExit[F[_]: Console: Functor]: F[ExitCode] = Console[F]
|
||||||
.println(versionStr)
|
.println(versionStr)
|
@ -7,19 +7,21 @@ import aqua.backend.ts.TypeScriptBackend
|
|||||||
import aqua.files.AquaFilesIO
|
import aqua.files.AquaFilesIO
|
||||||
import aqua.model.transform.TransformConfig
|
import aqua.model.transform.TransformConfig
|
||||||
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
|
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
|
||||||
import cats.Id
|
import cats.{Functor, Id, Monad}
|
||||||
import cats.data.Validated
|
import cats.data.{Chain, NonEmptyList, Validated, ValidatedNec, ValidatedNel}
|
||||||
import cats.effect.*
|
import cats.effect.*
|
||||||
import cats.effect.std.Console as ConsoleEff
|
import cats.effect.std.Console as ConsoleEff
|
||||||
import cats.syntax.apply.*
|
import cats.syntax.apply.*
|
||||||
import cats.syntax.functor.*
|
import cats.syntax.functor.*
|
||||||
|
import cats.syntax.applicative.*
|
||||||
|
import cats.syntax.flatMap.*
|
||||||
import com.monovore.decline.Opts
|
import com.monovore.decline.Opts
|
||||||
import com.monovore.decline.effect.CommandIOApp
|
import com.monovore.decline.effect.CommandIOApp
|
||||||
import fs2.io.file.Files
|
import fs2.io.file.Files
|
||||||
import scribe.Logging
|
import scribe.Logging
|
||||||
|
|
||||||
object AquaCli extends IOApp with Logging {
|
object AquaCli extends IOApp with Logging {
|
||||||
import AppOps._
|
import AppOps.*
|
||||||
|
|
||||||
sealed trait CompileTarget
|
sealed trait CompileTarget
|
||||||
case object TypescriptTarget extends CompileTarget
|
case object TypescriptTarget extends CompileTarget
|
||||||
@ -45,9 +47,9 @@ object AquaCli extends IOApp with Logging {
|
|||||||
) orElse helpOpt.as(
|
) orElse helpOpt.as(
|
||||||
helpAndExit
|
helpAndExit
|
||||||
) orElse (
|
) orElse (
|
||||||
inputOpts,
|
inputOpts[F],
|
||||||
importOpts,
|
importOpts[F],
|
||||||
outputOpts,
|
outputOpts[F],
|
||||||
compileToAir,
|
compileToAir,
|
||||||
compileToJs,
|
compileToJs,
|
||||||
noRelay,
|
noRelay,
|
||||||
@ -57,7 +59,7 @@ object AquaCli extends IOApp with Logging {
|
|||||||
logLevelOpt,
|
logLevelOpt,
|
||||||
constantOpts[Id]
|
constantOpts[Id]
|
||||||
).mapN {
|
).mapN {
|
||||||
case (input, imports, output, toAir, toJs, noRelay, noXor, h, v, logLevel, constants) =>
|
case (inputF, importsF, outputF, toAir, toJs, noRelay, noXor, h, v, logLevel, constants) =>
|
||||||
scribe.Logger.root
|
scribe.Logger.root
|
||||||
.clearHandlers()
|
.clearHandlers()
|
||||||
.clearModifiers()
|
.clearModifiers()
|
||||||
@ -78,21 +80,35 @@ object AquaCli extends IOApp with Logging {
|
|||||||
bc.copy(relayVarName = bc.relayVarName.filterNot(_ => noRelay))
|
bc.copy(relayVarName = bc.relayVarName.filterNot(_ => noRelay))
|
||||||
}
|
}
|
||||||
logger.info(s"Aqua Compiler ${versionStr}")
|
logger.info(s"Aqua Compiler ${versionStr}")
|
||||||
AquaPathCompiler
|
|
||||||
.compileFilesTo[F](
|
(inputF, outputF, importsF).mapN {(i, o, imp) =>
|
||||||
input,
|
i.andThen { input =>
|
||||||
imports,
|
o.andThen { output =>
|
||||||
output,
|
imp.map { imports =>
|
||||||
targetToBackend(target),
|
AquaPathCompiler
|
||||||
bc
|
.compileFilesTo[F](
|
||||||
)
|
input,
|
||||||
.map {
|
imports,
|
||||||
|
output,
|
||||||
|
targetToBackend(target),
|
||||||
|
bc
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.flatMap {
|
||||||
case Validated.Invalid(errs) =>
|
case Validated.Invalid(errs) =>
|
||||||
errs.map(System.out.println)
|
errs.map(logger.error(_))
|
||||||
ExitCode.Error
|
ExitCode.Error.pure[F]
|
||||||
case Validated.Valid(results) =>
|
case Validated.Valid(result) =>
|
||||||
results.map(logger.info(_))
|
result.map {
|
||||||
ExitCode.Success
|
case Validated.Invalid(errs) =>
|
||||||
|
errs.map(logger.error(_))
|
||||||
|
ExitCode.Error
|
||||||
|
case Validated.Valid(results) =>
|
||||||
|
results.map(logger.info(_))
|
||||||
|
ExitCode.Success
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
package aqua
|
package aqua
|
||||||
|
|
||||||
import aqua.compiler._
|
import aqua.compiler.*
|
||||||
import aqua.files.FileModuleId
|
import aqua.files.FileModuleId
|
||||||
import aqua.io.AquaFileError
|
import aqua.io.AquaFileError
|
||||||
import aqua.parser.lift.FileSpan
|
import aqua.parser.lift.FileSpan
|
@ -12,13 +12,14 @@ import cats.syntax.functor.*
|
|||||||
import cats.syntax.monad.*
|
import cats.syntax.monad.*
|
||||||
import cats.syntax.traverse.*
|
import cats.syntax.traverse.*
|
||||||
import fs2.io.file.{Files, Path}
|
import fs2.io.file.{Files, Path}
|
||||||
|
import scribe.Logging
|
||||||
|
|
||||||
import scala.util.Try
|
import scala.util.Try
|
||||||
|
|
||||||
class AquaFileSources[F[_]: AquaIO: Monad: Files: Functor](
|
class AquaFileSources[F[_]: AquaIO: Monad: Files: Functor](
|
||||||
sourcesPath: Path,
|
sourcesPath: Path,
|
||||||
importFrom: List[Path]
|
importFrom: List[Path]
|
||||||
) extends AquaSources[F, AquaFileError, FileModuleId] {
|
) extends AquaSources[F, AquaFileError, FileModuleId] with Logging {
|
||||||
private val filesIO = implicitly[AquaIO[F]]
|
private val filesIO = implicitly[AquaIO[F]]
|
||||||
|
|
||||||
override def sources: F[ValidatedNec[AquaFileError, Chain[(FileModuleId, String)]]] =
|
override def sources: F[ValidatedNec[AquaFileError, Chain[(FileModuleId, String)]]] =
|
@ -7,15 +7,16 @@ import aqua.model.transform.TransformConfig
|
|||||||
import aqua.model.transform.res.AquaRes
|
import aqua.model.transform.res.AquaRes
|
||||||
import aqua.parser.lift.LiftParser
|
import aqua.parser.lift.LiftParser
|
||||||
import aqua.semantics.Semantics
|
import aqua.semantics.Semantics
|
||||||
import cats.data.Validated.{Invalid, Valid, validNec}
|
import cats.data.Validated.{validNec, Invalid, Valid}
|
||||||
import cats.data.{Chain, NonEmptyChain, Validated, ValidatedNec}
|
import cats.data.{Chain, NonEmptyChain, Validated, ValidatedNec}
|
||||||
import cats.syntax.applicative.*
|
import cats.syntax.applicative.*
|
||||||
import cats.syntax.flatMap.*
|
import cats.syntax.flatMap.*
|
||||||
import cats.syntax.functor.*
|
import cats.syntax.functor.*
|
||||||
import cats.syntax.traverse.*
|
import cats.syntax.traverse.*
|
||||||
import cats.{Comonad, Monad}
|
import cats.{Comonad, Monad}
|
||||||
|
import scribe.Logging
|
||||||
|
|
||||||
object AquaCompiler {
|
object AquaCompiler extends Logging {
|
||||||
|
|
||||||
def compile[F[_]: Monad, E, I, S[_]: Comonad](
|
def compile[F[_]: Monad, E, I, S[_]: Comonad](
|
||||||
sources: AquaSources[F, E, I],
|
sources: AquaSources[F, E, I],
|
||||||
@ -26,10 +27,13 @@ object AquaCompiler {
|
|||||||
import config.aquaContextMonoid
|
import config.aquaContextMonoid
|
||||||
type Err = AquaError[I, E, S]
|
type Err = AquaError[I, E, S]
|
||||||
new AquaParser[F, E, I, S](sources, liftI)
|
new AquaParser[F, E, I, S](sources, liftI)
|
||||||
.resolve[ValidatedNec[Err, AquaContext]](ast =>
|
.resolve[ValidatedNec[Err, AquaContext]] { ast => context =>
|
||||||
context =>
|
context.andThen { ctx =>
|
||||||
context.andThen(ctx => Semantics.process(ast, ctx).leftMap(_.map[Err](CompileError(_))))
|
Semantics
|
||||||
)
|
.process(ast, ctx)
|
||||||
|
.leftMap(_.map[Err](CompileError(_)))
|
||||||
|
}
|
||||||
|
}
|
||||||
.map {
|
.map {
|
||||||
case Valid(modules) =>
|
case Valid(modules) =>
|
||||||
Linker.link[I, AquaError[I, E, S], ValidatedNec[Err, AquaContext]](
|
Linker.link[I, AquaError[I, E, S], ValidatedNec[Err, AquaContext]](
|
||||||
@ -59,11 +63,11 @@ object AquaCompiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def compileTo[F[_]: Monad, E, I, S[_]: Comonad, T](
|
def compileTo[F[_]: Monad, E, I, S[_]: Comonad, T](
|
||||||
sources: AquaSources[F, E, I],
|
sources: AquaSources[F, E, I],
|
||||||
liftI: (I, String) => LiftParser[S],
|
liftI: (I, String) => LiftParser[S],
|
||||||
backend: Backend,
|
backend: Backend,
|
||||||
config: TransformConfig,
|
config: TransformConfig,
|
||||||
write: AquaCompiled[I] => F[Seq[Validated[E, T]]]
|
write: AquaCompiled[I] => F[Seq[Validated[E, T]]]
|
||||||
): F[ValidatedNec[AquaError[I, E, S], Chain[T]]] =
|
): F[ValidatedNec[AquaError[I, E, S], Chain[T]]] =
|
||||||
compile[F, E, I, S](sources, liftI, backend, config).flatMap {
|
compile[F, E, I, S](sources, liftI, backend, config).flatMap {
|
||||||
case Valid(compiled) =>
|
case Valid(compiled) =>
|
||||||
|
@ -5,17 +5,18 @@ import aqua.parser.Ast
|
|||||||
import aqua.parser.head.ImportExpr
|
import aqua.parser.head.ImportExpr
|
||||||
import aqua.parser.lift.LiftParser
|
import aqua.parser.lift.LiftParser
|
||||||
import cats.data.{Chain, NonEmptyChain, Validated, ValidatedNec}
|
import cats.data.{Chain, NonEmptyChain, Validated, ValidatedNec}
|
||||||
import cats.syntax.applicative._
|
import cats.syntax.applicative.*
|
||||||
import cats.syntax.flatMap._
|
import cats.syntax.flatMap.*
|
||||||
import cats.syntax.functor._
|
import cats.syntax.functor.*
|
||||||
import cats.syntax.traverse._
|
import cats.syntax.traverse.*
|
||||||
import cats.{Comonad, Monad}
|
import cats.{Comonad, Monad}
|
||||||
|
import scribe.Logging
|
||||||
|
|
||||||
// TODO: add tests
|
// TODO: add tests
|
||||||
class AquaParser[F[_]: Monad, E, I, S[_]: Comonad](
|
class AquaParser[F[_]: Monad, E, I, S[_]: Comonad](
|
||||||
sources: AquaSources[F, E, I],
|
sources: AquaSources[F, E, I],
|
||||||
liftI: (I, String) => LiftParser[S]
|
liftI: (I, String) => LiftParser[S]
|
||||||
) {
|
) extends Logging {
|
||||||
|
|
||||||
type Body = Ast[S]
|
type Body = Ast[S]
|
||||||
type Err = AquaError[I, E, S]
|
type Err = AquaError[I, E, S]
|
||||||
|
Loading…
Reference in New Issue
Block a user