This commit is contained in:
Dima 2021-08-18 13:06:14 +03:00 committed by GitHub
parent f59a93ac27
commit 296c64836d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 192 additions and 243 deletions

View File

@ -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
) )

View File

@ -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!")
}
}

View File

@ -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()
} }
} }

View File

@ -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)

View File

@ -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
}
} }
} }
} }

View File

@ -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

View File

@ -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)]]] =

View File

@ -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) =>

View File

@ -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]