Separate Transform from Model (#244)

* Separate Result from Model

* Result->Transform, docs in README.md
This commit is contained in:
Dmitry Kurinskiy 2021-08-16 17:58:15 +03:00 committed by GitHub
parent 6c498b029b
commit 96bc76ef2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 107 additions and 106 deletions

View File

@ -32,6 +32,8 @@ Input directory should contain files with `aqua` scripts.
- **[types](./types)** data types, arrows, stream types definitions and variance
- **[parser](./parser)** - parser, takes source text and produces a source AST
- **[model](./model)** - middle-end, internal representation of the code, optimizations and transfromations
- **[model/transform](./model/transform)** - optimizations and transfromations, converting model to the result, ready to be rendered
- **[model/test-kit](./model/test-kit)** - tests and test helpers for the model and transformations
- **[semantics](./semantics)** - rules to convert source AST into the model
- **[linker](./linker)** - checks dependencies between modules, builds and combines an abstract dependencies tree
- **[backend](./backend)** - compilation backend interface

View File

@ -1,7 +1,7 @@
package aqua.backend.air
import aqua.backend.{Backend, Generated}
import aqua.model.res.AquaRes
import aqua.model.transform.res.AquaRes
import cats.syntax.show.*
object AirBackend extends Backend {

View File

@ -1,8 +1,8 @@
package aqua.backend.air
import aqua.model._
import aqua.model.*
import aqua.model.func.Call
import aqua.model.func.resolved._
import aqua.model.transform.res.*
import aqua.types.StreamType
import cats.Eval
import cats.data.Chain

View File

@ -1,6 +1,6 @@
package aqua.backend.air
import aqua.model.res.FuncRes
import aqua.model.transform.res.FuncRes
case class FuncAirGen(func: FuncRes) {

View File

@ -1,7 +1,7 @@
package aqua.backend.js
import aqua.backend.{Backend, Generated}
import aqua.model.res.AquaRes
import aqua.model.transform.res.AquaRes
import cats.data.NonEmptyChain
object JavaScriptBackend extends Backend {

View File

@ -1,7 +1,7 @@
package aqua.backend.js
import aqua.backend.air.FuncAirGen
import aqua.model.res.FuncRes
import aqua.model.transform.res.FuncRes
import aqua.types.*
import cats.syntax.show.*

View File

@ -1,6 +1,6 @@
package aqua.backend
import aqua.model.res.AquaRes
import aqua.model.transform.res.AquaRes
/**
* Compiler backend generates output based on the processed model

View File

@ -1,7 +1,7 @@
package aqua.backend.ts
import aqua.backend.{Backend, Generated}
import aqua.model.res.AquaRes
import aqua.model.transform.res.AquaRes
import cats.data.NonEmptyChain
object TypeScriptBackend extends Backend {

View File

@ -1,7 +1,7 @@
package aqua.backend.ts
import aqua.backend.Version
import aqua.model.res.AquaRes
import aqua.model.transform.res.AquaRes
case class TypeScriptFile(res: AquaRes) {

View File

@ -1,7 +1,7 @@
package aqua.backend.ts
import aqua.backend.air.FuncAirGen
import aqua.model.res.FuncRes
import aqua.model.transform.res.FuncRes
import aqua.types.*
import cats.syntax.show.*

View File

@ -1,6 +1,6 @@
package aqua.backend.ts
import aqua.model.res.ServiceRes
import aqua.model.transform.res.ServiceRes
case class TypeScriptService(srv: ServiceRes) {

View File

@ -107,12 +107,19 @@ lazy val model = crossProject(JVMPlatform, JSPlatform)
)
.dependsOn(types)
lazy val transform = crossProject(JVMPlatform, JSPlatform)
.withoutSuffixFor(JVMPlatform)
.crossType(CrossType.Pure)
.in(file("model/transform"))
.settings(commons: _*)
.dependsOn(model)
lazy val `test-kit` = crossProject(JVMPlatform, JSPlatform)
.withoutSuffixFor(JVMPlatform)
.crossType(CrossType.Pure)
.in(file("model/test-kit"))
.settings(commons: _*)
.dependsOn(model)
.dependsOn(transform)
lazy val semantics = crossProject(JVMPlatform, JSPlatform)
.withoutSuffixFor(JVMPlatform)
@ -138,7 +145,7 @@ lazy val backend = crossProject(JVMPlatform, JSPlatform)
.crossType(CrossType.Pure)
.in(file("backend"))
.settings(commons: _*)
.dependsOn(model)
.dependsOn(transform)
lazy val `backend-air` = crossProject(JVMPlatform, JSPlatform)
.withoutSuffixFor(JVMPlatform)

View File

@ -3,7 +3,7 @@ package aqua
import aqua.backend.Backend
import aqua.backend.ts.TypeScriptBackend
import aqua.compiler.{AquaCompiler, AquaError, AquaSources}
import aqua.model.transform.GenerationConfig
import aqua.model.transform.TransformConfig
import cats.effect.IO
import cats.effect.unsafe.implicits.global
import cats.data.*

View File

@ -1,12 +1,12 @@
package aqua
import aqua.model.LiteralModel
import aqua.model.transform.GenerationConfig
import aqua.model.transform.TransformConfig
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.{unsafe, ExitCode, IO}
import cats.effect.{ExitCode, IO, unsafe}
import cats.effect.std.Console
import cats.syntax.functor.*
import cats.syntax.traverse.*
@ -120,7 +120,7 @@ object AppOps {
}
.withDefault(List.empty)
def constantOpts[F[_]: LiftParser: Comonad]: Opts[List[GenerationConfig.Const]] =
def constantOpts[F[_]: LiftParser: Comonad]: Opts[List[TransformConfig.Const]] =
Opts
.options[String]("const", "Constant that will be used in an aqua code", "c")
.mapValidated { strs =>
@ -133,9 +133,9 @@ object AppOps {
NonEmptyList
.fromList(errors)
.fold(
Validated.validNel[String, List[GenerationConfig.Const]](parsed.collect {
Validated.validNel[String, List[TransformConfig.Const]](parsed.collect {
case Right(v) =>
GenerationConfig.Const(v._1.value, LiteralModel(v._2.value, v._2.ts))
TransformConfig.Const(v._1.value, LiteralModel(v._2.value, v._2.ts))
})
) { errors =>
Validated.invalid(errors.map(_.toString))

View File

@ -5,7 +5,7 @@ import aqua.backend.air.AirBackend
import aqua.backend.js.JavaScriptBackend
import aqua.backend.ts.TypeScriptBackend
import aqua.files.AquaFilesIO
import aqua.model.transform.GenerationConfig
import aqua.model.transform.TransformConfig
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
import cats.Id
import cats.data.Validated
@ -74,7 +74,7 @@ object AquaCli extends IOApp with Logging {
else if (toJs) JavaScriptTarget
else TypescriptTarget
val bc = {
val bc = GenerationConfig(wrapWithXor = !noXor, constants = constants)
val bc = TransformConfig(wrapWithXor = !noXor, constants = constants)
bc.copy(relayVarName = bc.relayVarName.filterNot(_ => noRelay))
}
logger.info(s"Aqua Compiler ${versionStr}")

View File

@ -4,7 +4,7 @@ import aqua.backend.Backend
import aqua.compiler.{AquaCompiler, AquaError}
import aqua.files.{AquaFileSources, FileModuleId}
import aqua.io.*
import aqua.model.transform.GenerationConfig
import aqua.model.transform.TransformConfig
import aqua.parser.lift.FileSpan
import cats.data.*
import cats.syntax.functor.*
@ -20,7 +20,7 @@ object AquaPathCompiler extends Logging {
imports: List[Path],
targetPath: Path,
backend: Backend,
bodyConfig: GenerationConfig
bodyConfig: TransformConfig
): F[ValidatedNec[String, Chain[String]]] = {
import ErrorRendering.showError
val sources = new AquaFileSources[F](srcPath, imports)

View File

@ -2,10 +2,9 @@ package aqua
import aqua.backend.ts.TypeScriptBackend
import aqua.files.AquaFilesIO
import aqua.model.transform.GenerationConfig
import aqua.model.transform.TransformConfig
import cats.data.Validated
import cats.effect.{IO, IOApp, Sync}
import fs2.io.file.Path
object Test extends IOApp.Simple {
@ -19,7 +18,7 @@ object Test extends IOApp.Simple {
List(Path("./aqua")),
Path("./target"),
TypeScriptBackend,
GenerationConfig()
TransformConfig()
)
.map {
case Validated.Invalid(errs) =>

View File

@ -2,12 +2,11 @@ import aqua.AquaPathCompiler
import aqua.backend.air.AirBackend
import aqua.backend.js.JavaScriptBackend
import aqua.backend.ts.TypeScriptBackend
import aqua.model.transform.GenerationConfig
import aqua.model.transform.TransformConfig
import cats.effect.IO
import cats.effect.unsafe.implicits.global
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import fs2.io.file.{Files, Path}
class WriteFileSpec extends AnyFlatSpec with Matchers {
@ -19,7 +18,7 @@ class WriteFileSpec extends AnyFlatSpec with Matchers {
import aqua.files.AquaFilesIO.summon
val bc = GenerationConfig()
val bc = TransformConfig()
AquaPathCompiler
.compileFilesTo[IO](src, List.empty, targetTs, TypeScriptBackend, bc)
.unsafeRunSync()

View File

@ -3,11 +3,11 @@ package aqua.compiler
import aqua.backend.Backend
import aqua.linker.Linker
import aqua.model.AquaContext
import aqua.model.res.AquaRes
import aqua.model.transform.GenerationConfig
import aqua.model.transform.TransformConfig
import aqua.model.transform.res.AquaRes
import aqua.parser.lift.LiftParser
import aqua.semantics.Semantics
import cats.data.Validated.{validNec, Invalid, Valid}
import cats.data.Validated.{Invalid, Valid, validNec}
import cats.data.{Chain, NonEmptyChain, Validated, ValidatedNec}
import cats.syntax.applicative.*
import cats.syntax.flatMap.*
@ -21,7 +21,7 @@ object AquaCompiler {
sources: AquaSources[F, E, I],
liftI: (I, String) => LiftParser[S],
backend: Backend,
config: GenerationConfig
config: TransformConfig
): F[ValidatedNec[AquaError[I, E, S], Chain[AquaCompiled[I]]]] = {
import config.aquaContextMonoid
type Err = AquaError[I, E, S]
@ -59,11 +59,11 @@ object AquaCompiler {
}
def compileTo[F[_]: Monad, E, I, S[_]: Comonad, T](
sources: AquaSources[F, E, I],
liftI: (I, String) => LiftParser[S],
backend: Backend,
config: GenerationConfig,
write: AquaCompiled[I] => F[Seq[Validated[E, T]]]
sources: AquaSources[F, E, I],
liftI: (I, String) => LiftParser[S],
backend: Backend,
config: TransformConfig,
write: AquaCompiled[I] => F[Seq[Validated[E, T]]]
): F[ValidatedNec[AquaError[I, E, S], Chain[T]]] =
compile[F, E, I, S](sources, liftI, backend, config).flatMap {
case Valid(compiled) =>

View File

@ -2,8 +2,9 @@ package aqua
import aqua.model.func.Call
import aqua.model.func.raw.*
import aqua.model.func.resolved.{CallRes, CallServiceRes, MakeRes, MatchMismatchRes, ResolvedOp}
import aqua.model.transform.{ErrorsCatcher, GenerationConfig}
import aqua.model.transform.TransformConfig
import aqua.model.transform.res.{CallRes, CallServiceRes, MakeRes, MatchMismatchRes, ResolvedOp}
import aqua.model.transform.funcop.ErrorsCatcher
import aqua.model.{LiteralModel, ValueModel, VarModel}
import aqua.types.{ArrayType, LiteralType, ScalarType}
import cats.Eval
@ -88,7 +89,7 @@ object Node {
)
)
def errorCall(bc: GenerationConfig, i: Int, on: ValueModel = initPeer): Res = Node[ResolvedOp](
def errorCall(bc: TransformConfig, i: Int, on: ValueModel = initPeer): Res = Node[ResolvedOp](
CallServiceRes(
bc.errorHandlingCallback,
bc.errorFuncName,
@ -103,7 +104,7 @@ object Node {
)
)
def respCall(bc: GenerationConfig, value: ValueModel, on: ValueModel = initPeer): Res =
def respCall(bc: TransformConfig, value: ValueModel, on: ValueModel = initPeer): Res =
Node[ResolvedOp](
CallServiceRes(
bc.callbackSrvId,
@ -113,7 +114,7 @@ object Node {
)
)
def dataCall(bc: GenerationConfig, name: String, on: ValueModel = initPeer): Res =
def dataCall(bc: TransformConfig, name: String, on: ValueModel = initPeer): Res =
Node[ResolvedOp](
CallServiceRes(
bc.dataSrvId,

View File

@ -2,15 +2,16 @@ package aqua.model.transform
import aqua.Node
import aqua.model.func.raw.{CallArrowTag, CallServiceTag, FuncOp, FuncOps}
import aqua.model.func.resolved.{CallRes, CallServiceRes, MakeRes}
import aqua.model.func.{Call, FuncCallable}
import aqua.model.transform.res.{CallRes, CallServiceRes, MakeRes}
import aqua.model.transform.{Transform, TransformConfig}
import aqua.model.{LiteralModel, VarModel}
import aqua.types.{ArrowType, NilType, ProductType, ScalarType}
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
class TransformSpec extends AnyFlatSpec with Matchers {
import Node._
import Node.*
val stringArrow: ArrowType = ArrowType(NilType, ProductType(ScalarType.string :: Nil))
@ -28,7 +29,7 @@ class TransformSpec extends AnyFlatSpec with Matchers {
Map.empty
)
val bc = GenerationConfig()
val bc = TransformConfig()
val fc = Transform.fn(func, bc)
@ -78,7 +79,7 @@ class TransformSpec extends AnyFlatSpec with Matchers {
Map.empty
)
val bc = GenerationConfig(wrapWithXor = false)
val bc = TransformConfig(wrapWithXor = false)
val fc = Transform.fn(func, bc)
@ -139,7 +140,7 @@ class TransformSpec extends AnyFlatSpec with Matchers {
Map.empty
)
val bc = GenerationConfig(wrapWithXor = false)
val bc = TransformConfig(wrapWithXor = false)
val res = Transform.fn(f2, bc).body: Node.Res

View File

@ -1,10 +1,10 @@
package aqua.model.topology
package aqua.model.transform.topology
import aqua.Node
import aqua.model.VarModel
import aqua.model.func.Call
import aqua.model.func.raw.FuncOps
import aqua.model.func.resolved.{MakeRes, ResolvedOp, XorRes}
import aqua.model.transform.res.{MakeRes, ResolvedOp, XorRes}
import aqua.types.ScalarType
import cats.Eval
import cats.data.Chain

View File

@ -1,10 +1,10 @@
package aqua.model.transform
import aqua.model.func.FuncCallable
import aqua.model.VarModel
import aqua.model.func.resolved.{NoAir, ResolvedOp}
import aqua.model.res.FuncRes
import aqua.model.topology.Topology
import aqua.model.func.FuncCallable
import aqua.model.transform.res.{FuncRes, NoAir, ResolvedOp}
import aqua.model.transform.topology.Topology
import aqua.model.transform.funcop.*
import aqua.types.ScalarType
import cats.data.Chain
import cats.free.Cofree
@ -23,7 +23,7 @@ object Transform extends Logging {
): Cofree[Chain, ResolvedOp] =
tree.copy(tail = tree.tail.map(_.filter(t => filter(t.head)).map(clear(_, filter))))
def fn(func: FuncCallable, conf: GenerationConfig): FuncRes = {
def fn(func: FuncCallable, conf: TransformConfig): FuncRes = {
val initCallable: InitPeerCallable = InitViaRelayCallable(
Chain.fromOption(conf.relayVarName).map(VarModel(_, ScalarType.string))
)

View File

@ -4,7 +4,7 @@ import aqua.model.{AquaContext, LiteralModel, ValueModel, VarModel}
import aqua.types.ScalarType
import cats.kernel.Monoid
case class GenerationConfig(
case class TransformConfig(
getDataService: String = "getDataSrv",
callbackService: String = "callbackSrv",
errorHandlingService: String = "errorHandlingSrv",
@ -12,7 +12,7 @@ case class GenerationConfig(
respFuncName: String = "response",
relayVarName: Option[String] = Some("-relay-"),
wrapWithXor: Boolean = true,
constants: List[GenerationConfig.Const] = Nil
constants: List[TransformConfig.Const] = Nil
) {
val errorId: ValueModel = LiteralModel.quote(errorFuncName)
@ -22,8 +22,8 @@ case class GenerationConfig(
// Host peer id holds %init_peer_id% in case Aqua is not compiled to be executed behind a relay,
// or relay's variable otherwise
val hostPeerId: GenerationConfig.Const =
GenerationConfig.Const(
val hostPeerId: TransformConfig.Const =
TransformConfig.Const(
"host_peer_id",
relayVarName.fold[ValueModel](LiteralModel.initPeerId)(r => VarModel(r, ScalarType.string))
)
@ -45,9 +45,9 @@ case class GenerationConfig(
}
object GenerationConfig {
object TransformConfig {
case class Const(name: String, value: ValueModel)
def forHost: GenerationConfig =
GenerationConfig(wrapWithXor = false, relayVarName = None)
def forHost: TransformConfig =
TransformConfig(wrapWithXor = false, relayVarName = None)
}

View File

@ -1,4 +1,4 @@
package aqua.model.cursor
package aqua.model.transform.cursor
import cats.data.{Chain, NonEmptyList}

View File

@ -1,4 +1,4 @@
package aqua.model.cursor
package aqua.model.transform.cursor
import cats.data.Chain
import cats.free.Cofree

View File

@ -1,4 +1,4 @@
package aqua.model.transform
package aqua.model.transform.funcop
import aqua.model.{ValueModel, VarModel}
import aqua.model.func.Call

View File

@ -1,4 +1,4 @@
package aqua.model.transform
package aqua.model.transform.funcop
import aqua.model.{LiteralModel, ValueModel, VarModel}
import aqua.model.func.Call

View File

@ -1,4 +1,4 @@
package aqua.model.transform
package aqua.model.transform.funcop
import aqua.model.{LiteralModel, ValueModel}
import aqua.model.func.Call

View File

@ -1,4 +1,4 @@
package aqua.model.transform
package aqua.model.transform.funcop
import aqua.model.func.*
import aqua.model.func.raw.{FuncOp, FuncOps}

View File

@ -1,7 +1,8 @@
package aqua.model.res
package aqua.model.transform.res
import aqua.model.AquaContext
import aqua.model.transform.{GenerationConfig, Transform}
import aqua.model.transform.{Transform, TransformConfig}
import aqua.model.transform.res._
import cats.data.Chain
case class AquaRes(funcs: Chain[FuncRes], services: Chain[ServiceRes]) {
@ -10,7 +11,7 @@ case class AquaRes(funcs: Chain[FuncRes], services: Chain[ServiceRes]) {
object AquaRes {
def fromContext(ctx: AquaContext, conf: GenerationConfig): AquaRes =
def fromContext(ctx: AquaContext, conf: TransformConfig): AquaRes =
AquaRes(
funcs = Chain.fromSeq(ctx.funcs.values.toSeq).map(Transform.fn(_, conf)),
services = Chain.fromSeq(ctx.services.values.toSeq).map(ServiceRes.fromModel(_))

View File

@ -1,4 +1,4 @@
package aqua.model.func.resolved
package aqua.model.transform.res
import aqua.model.ValueModel
import aqua.model.func.Call

View File

@ -1,18 +1,17 @@
package aqua.model.res
package aqua.model.transform.res
import aqua.model.func.FuncCallable
import aqua.model.func.resolved.ResolvedOp
import aqua.model.transform.GenerationConfig
import aqua.model.transform.TransformConfig
import aqua.types.{ArrowType, Type}
import cats.data.Chain
import cats.free.Cofree
case class FuncRes(
source: FuncCallable,
conf: GenerationConfig,
conf: TransformConfig,
body: Cofree[Chain, ResolvedOp]
) {
import FuncRes._
import FuncRes.*
lazy val funcName = source.funcName

View File

@ -1,18 +1,8 @@
package aqua.model.func.resolved
package aqua.model.transform.res
import aqua.model.func.Call
import aqua.model.func.raw.{
CallServiceTag,
ForTag,
MatchMismatchTag,
NextTag,
OnTag,
ParTag,
RawTag,
SeqTag,
XorTag
}
import aqua.model.topology.Topology.Res
import aqua.model.func.raw.*
import aqua.model.transform.topology.Topology.Res
import aqua.model.{LiteralModel, ValueModel}
import cats.Eval
import cats.data.Chain

View File

@ -1,4 +1,4 @@
package aqua.model.func.resolved
package aqua.model.transform.res
import aqua.model.ValueModel
import aqua.model.func.Call

View File

@ -1,8 +1,7 @@
package aqua.model.res
package aqua.model.transform.res
import aqua.model.ServiceModel
import aqua.model.{LiteralModel, ServiceModel}
import aqua.types.{ArrowType, ScalarType}
import aqua.model.LiteralModel
case class ServiceRes(name: String, members: List[(String, ArrowType)], defaultId: Option[String])

View File

@ -1,6 +1,7 @@
package aqua.model.func.raw
package aqua.model.transform.topology
import aqua.model.ValueModel
import aqua.model.func.raw.{OnTag, ParGroupTag}
import cats.data.Chain
import cats.data.Chain.{:==, ==:, nil}
import scribe.Logging

View File

@ -1,11 +1,12 @@
package aqua.model.func.raw
package aqua.model.transform.topology
import aqua.model.ValueModel
import aqua.model.cursor.{ChainCursor, ChainZipper}
import aqua.model.func.raw.*
import cats.Eval
import cats.data.{Chain, NonEmptyList, OptionT}
import cats.free.Cofree
import aqua.model.transform.cursor._
import cats.syntax.traverse._
import cats.free.Cofree
import scribe.Logging
// Can be heavily optimized by caching parent cursors, not just list of zippers

View File

@ -1,15 +1,15 @@
package aqua.model.topology
package aqua.model.transform.topology
import aqua.model.cursor.ChainZipper
import aqua.model.func.raw._
import aqua.model.func.resolved._
import aqua.model.transform.cursor.ChainZipper
import aqua.model.func.raw.*
import aqua.model.transform.res.*
import aqua.model.{LiteralModel, ValueModel, VarModel}
import aqua.types.{BoxType, ScalarType}
import cats.Eval
import cats.data.Chain.nil
import cats.data.{Chain, NonEmptyChain, NonEmptyList, OptionT}
import cats.free.Cofree
import cats.syntax.traverse._
import cats.syntax.traverse.*
import scribe.Logging
object Topology extends Logging {

View File

@ -1,9 +1,10 @@
package aqua.semantics
import aqua.Node
import aqua.Node._
import aqua.Node.*
import aqua.model.func.raw.{FuncOp, FuncOps, SeqTag}
import aqua.model.transform._
import aqua.model.transform.TransformConfig
import aqua.model.transform.funcop.*
import aqua.model.{AquaContext, LiteralModel}
import aqua.parser.Ast
import aqua.parser.lift.{LiftParser, Span}
@ -29,7 +30,7 @@ class SemanticsSpec extends AnyFlatSpec with Matchers {
val ast = Ast.fromString(script).toList.head
val ctx = AquaContext.blank
val bc = GenerationConfig()
val bc = TransformConfig()
import bc.aquaContextMonoid
val p = Semantics.process(ast, ctx)