mirror of
https://github.com/fluencelabs/aqua.git
synced 2024-12-04 22:50:18 +00:00
Model refactoring to isolate imports, exports (#156)
* Model refactoring to isolate imports, exports * Filter duplicate errors * Do not export imports * Add types to ServiceModel, AquaContext * Print compiler version in AquaCli
This commit is contained in:
parent
d300a7dea3
commit
4696e95129
9
aqua-src/app.aqua
Normal file
9
aqua-src/app.aqua
Normal file
@ -0,0 +1,9 @@
|
||||
import "chatApp.aqua"
|
||||
import "service.aqua"
|
||||
import "builtin.aqua"
|
||||
import "demo.aqua"
|
||||
|
||||
func createUser(nickname: string):
|
||||
on nickname:
|
||||
AppConfig.getApp()
|
||||
AppConfig.getApp()
|
3
aqua-src/chatApp.aqua
Normal file
3
aqua-src/chatApp.aqua
Normal file
@ -0,0 +1,3 @@
|
||||
service AppConfig("affinidi/get-config"):
|
||||
getApp: -> string
|
||||
getLocalService: -> LocalService
|
@ -1,3 +1,5 @@
|
||||
import "builtin.aqua"
|
||||
|
||||
service OpH("op"):
|
||||
puk(s: string) -> string
|
||||
|
||||
@ -12,6 +14,7 @@ func a(b: string) -> string:
|
||||
|
||||
x = "!!!"
|
||||
OpH.puk(x)
|
||||
Op.identity()
|
||||
<- c
|
||||
|
||||
func d(e: string) -> string:
|
||||
|
5
aqua-src/service.aqua
Normal file
5
aqua-src/service.aqua
Normal file
@ -0,0 +1,5 @@
|
||||
import "chatApp.aqua"
|
||||
|
||||
service Service("affinidi/chat"):
|
||||
create_user(peer_id: string, relay_id: string, nickname: string) -> string
|
||||
chats_by_peer_id(peer_id: string) -> string
|
@ -1,11 +1,13 @@
|
||||
package aqua.backend.ts
|
||||
|
||||
import aqua.model.ScriptModel
|
||||
import aqua.model.AquaContext
|
||||
import aqua.model.transform.BodyConfig
|
||||
import cats.data.Chain
|
||||
|
||||
case class TypescriptFile(script: ScriptModel) {
|
||||
def funcs: Chain[TypescriptFunc] = script.resolveFunctions.map(TypescriptFunc(_))
|
||||
case class TypescriptFile(context: AquaContext) {
|
||||
|
||||
def funcs: Chain[TypescriptFunc] =
|
||||
Chain.fromSeq(context.funcs.values.toSeq).map(TypescriptFunc(_))
|
||||
|
||||
def generateTS(conf: BodyConfig = BodyConfig()): String =
|
||||
TypescriptFile.Header + "\n\n" + funcs.map(_.generateTypescript(conf)).toList.mkString("\n\n")
|
||||
|
@ -54,6 +54,7 @@ object AquaCli extends IOApp with LogSupport {
|
||||
val bc = BodyConfig(wrapWithXor = !noXor)
|
||||
bc.copy(relayVarName = bc.relayVarName.filterNot(_ => noRelay))
|
||||
}
|
||||
info(s"Aqua Compiler ${versionStr}")
|
||||
AquaCompiler
|
||||
.compileFilesTo[F](
|
||||
input,
|
||||
|
@ -4,18 +4,16 @@ import aqua.backend.air.FuncAirGen
|
||||
import aqua.backend.ts.TypescriptFile
|
||||
import aqua.io.{AquaFileError, AquaFiles, FileModuleId, Unresolvable}
|
||||
import aqua.linker.Linker
|
||||
import aqua.model.ScriptModel
|
||||
import aqua.model.AquaContext
|
||||
import aqua.model.transform.BodyConfig
|
||||
import aqua.parser.lexer.Token
|
||||
import aqua.parser.lift.FileSpan
|
||||
import aqua.semantics.{CompilerState, Semantics}
|
||||
import aqua.semantics.{RulesViolated, SemanticError, Semantics}
|
||||
import cats.Applicative
|
||||
import cats.data.Validated.{Invalid, Valid}
|
||||
import cats.data._
|
||||
import cats.effect.kernel.Concurrent
|
||||
import cats.syntax.flatMap._
|
||||
import cats.syntax.functor._
|
||||
import cats.syntax.monoid._
|
||||
import cats.syntax.show._
|
||||
import fs2.io.file.Files
|
||||
import fs2.text
|
||||
@ -28,10 +26,10 @@ object AquaCompiler extends LogSupport {
|
||||
case object TypescriptTarget extends CompileTarget
|
||||
case object AirTarget extends CompileTarget
|
||||
|
||||
case class Prepared(modFile: Path, srcPath: Path, targetPath: Path, model: ScriptModel) {
|
||||
case class Prepared(modFile: Path, srcPath: Path, targetPath: Path, context: AquaContext) {
|
||||
|
||||
def hasOutput(target: CompileTarget): Boolean = target match {
|
||||
case _ => model.funcs.nonEmpty
|
||||
case _ => context.funcs.nonEmpty
|
||||
}
|
||||
|
||||
def targetPath(ext: String): Validated[Throwable, Path] =
|
||||
@ -64,15 +62,10 @@ object AquaCompiler extends LogSupport {
|
||||
targetPath: Path
|
||||
): F[ValidatedNec[String, Chain[Prepared]]] =
|
||||
AquaFiles
|
||||
.readAndResolve[F, CompilerState.S[FileSpan.F]](
|
||||
.readAndResolve[F, ValidatedNec[SemanticError[FileSpan.F], AquaContext]](
|
||||
srcPath,
|
||||
imports,
|
||||
ast =>
|
||||
_.flatMap(m => {
|
||||
for {
|
||||
y <- Semantics.astToState(ast)
|
||||
} yield m |+| y
|
||||
})
|
||||
ast => _.andThen(ctx => Semantics.process(ast, ctx))
|
||||
)
|
||||
.value
|
||||
.map {
|
||||
@ -80,27 +73,22 @@ object AquaCompiler extends LogSupport {
|
||||
Validated.invalid(fileErrors.map(_.showForConsole))
|
||||
|
||||
case Right(modules) =>
|
||||
Linker[FileModuleId, AquaFileError, CompilerState.S[FileSpan.F]](
|
||||
Linker[FileModuleId, AquaFileError, ValidatedNec[SemanticError[FileSpan.F], AquaContext]](
|
||||
modules,
|
||||
ids => Unresolvable(ids.map(_.id.file.toString).mkString(" -> "))
|
||||
) match {
|
||||
case Validated.Valid(files) ⇒
|
||||
val (errs, preps) =
|
||||
files.toSeq.foldLeft[(Chain[String], Chain[Prepared])]((Chain.empty, Chain.empty)) {
|
||||
case ((errs, preps), (modId, proc)) =>
|
||||
proc.run(CompilerState()).value match {
|
||||
case (proc, _) if proc.errors.nonEmpty =>
|
||||
(errs ++ showProcErrors(proc.errors), preps)
|
||||
|
||||
case (_, model: ScriptModel) =>
|
||||
(errs, preps :+ Prepared(modId.file, srcPath, targetPath, model))
|
||||
|
||||
case (_, model) =>
|
||||
(
|
||||
errs.append(Console.RED + "Unknown model: " + model + Console.RESET),
|
||||
preps
|
||||
)
|
||||
}
|
||||
val (errs, _, preps) =
|
||||
files.toSeq.foldLeft[(Chain[String], Set[String], Chain[Prepared])](
|
||||
(Chain.empty, Set.empty, Chain.empty)
|
||||
) { case ((errs, errsSet, preps), (modId, proc)) =>
|
||||
proc.fold(
|
||||
es => {
|
||||
val newErrs = showProcErrors(es.toChain).filterNot(errsSet.contains)
|
||||
(errs ++ newErrs, errsSet ++ newErrs.iterator, preps)
|
||||
},
|
||||
c => (errs, errsSet, preps :+ Prepared(modId.file, srcPath, targetPath, c))
|
||||
)
|
||||
}
|
||||
NonEmptyChain
|
||||
.fromChain(errs)
|
||||
@ -115,14 +103,17 @@ object AquaCompiler extends LogSupport {
|
||||
}
|
||||
|
||||
def showProcErrors(
|
||||
errors: Chain[(Token[FileSpan.F], String)]
|
||||
errors: Chain[SemanticError[FileSpan.F]]
|
||||
): Chain[String] =
|
||||
errors.map(err =>
|
||||
err._1.unit._1
|
||||
.focus(2)
|
||||
.map(_.toConsoleStr(err._2, Console.CYAN))
|
||||
.getOrElse("(Dup error, but offset is beyond the script)") + "\n"
|
||||
)
|
||||
errors.map {
|
||||
case RulesViolated(token, hint) =>
|
||||
token.unit._1
|
||||
.focus(2)
|
||||
.map(_.toConsoleStr(hint, Console.CYAN))
|
||||
.getOrElse("(Dup error, but offset is beyond the script)") + "\n"
|
||||
case _ =>
|
||||
"Semantic error"
|
||||
}
|
||||
|
||||
def compileFilesTo[F[_]: Files: Concurrent](
|
||||
srcPath: Path,
|
||||
@ -148,11 +139,11 @@ object AquaCompiler extends LogSupport {
|
||||
case Invalid(t) =>
|
||||
EitherT.pure(t.getMessage)
|
||||
case Valid(tp) =>
|
||||
writeFile(tp, TypescriptFile(p.model).generateTS(bodyConfig)).flatTap { _ =>
|
||||
writeFile(tp, TypescriptFile(p.context).generateTS(bodyConfig)).flatTap { _ =>
|
||||
EitherT.pure(
|
||||
Validated.catchNonFatal(
|
||||
info(
|
||||
s"Result ${tp.toAbsolutePath}: compilation OK (${p.model.funcs.length} functions)"
|
||||
s"Result ${tp.toAbsolutePath}: compilation OK (${p.context.funcs.size} functions)"
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -165,8 +156,9 @@ object AquaCompiler extends LogSupport {
|
||||
case AirTarget =>
|
||||
preps
|
||||
.flatMap(p =>
|
||||
p.model.resolveFunctions
|
||||
.map(fc => (fc.funcName -> FuncAirGen(fc).generateAir(bodyConfig).show))
|
||||
Chain
|
||||
.fromSeq(p.context.funcs.values.toSeq)
|
||||
.map(fc => fc.funcName -> FuncAirGen(fc).generateAir(bodyConfig).show)
|
||||
.map { case (fnName, generated) =>
|
||||
val tpV = p.targetPath(fnName + ".air")
|
||||
tpV match {
|
||||
@ -180,7 +172,7 @@ object AquaCompiler extends LogSupport {
|
||||
EitherT.pure(
|
||||
Validated.catchNonFatal(
|
||||
info(
|
||||
s"Result ${tp.toAbsolutePath}: compilation OK (${p.model.funcs.length} functions)"
|
||||
s"Result ${tp.toAbsolutePath}: compilation OK (${p.context.funcs.size} functions)"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -36,7 +36,7 @@ object AquaFiles {
|
||||
_.collect {
|
||||
case f if f.isFile && f.getName.endsWith(".aqua") =>
|
||||
AquaFile
|
||||
.read(f.toPath.toAbsolutePath)
|
||||
.read(f.toPath.toAbsolutePath.normalize())
|
||||
.map(Chain(_))
|
||||
.leftMap(NonEmptyChain.one)
|
||||
case f if f.isDirectory =>
|
||||
|
140
model/src/main/scala/aqua/model/AquaContext.scala
Normal file
140
model/src/main/scala/aqua/model/AquaContext.scala
Normal file
@ -0,0 +1,140 @@
|
||||
package aqua.model
|
||||
|
||||
import aqua.model.func.body.{CallServiceTag, FuncOp}
|
||||
import aqua.model.func.{ArgsCall, FuncCallable, FuncModel}
|
||||
import aqua.types.{ProductType, Type}
|
||||
import cats.Monoid
|
||||
import cats.data.NonEmptyMap
|
||||
import cats.syntax.apply._
|
||||
import cats.syntax.functor._
|
||||
import cats.syntax.monoid._
|
||||
|
||||
import scala.collection.immutable.SortedMap
|
||||
|
||||
case class AquaContext(
|
||||
funcs: Map[String, FuncCallable],
|
||||
types: Map[String, Type],
|
||||
values: Map[String, ValueModel],
|
||||
abilities: Map[String, AquaContext],
|
||||
// TODO: merge this with abilities, when have ability resolution variance
|
||||
services: Map[String, ServiceModel]
|
||||
) {
|
||||
|
||||
def allTypes(prefix: String = ""): Map[String, Type] =
|
||||
abilities
|
||||
.foldLeft(types) { case (ts, (k, v)) =>
|
||||
ts ++ v.allTypes(k + ".")
|
||||
}
|
||||
.map(_.swap.map(prefix + _).swap)
|
||||
|
||||
def allFuncs(prefix: String = ""): Map[String, FuncCallable] =
|
||||
abilities
|
||||
.foldLeft(funcs) { case (ts, (k, v)) =>
|
||||
ts ++ v.allFuncs(k + ".")
|
||||
}
|
||||
.map(_.swap.map(prefix + _).swap)
|
||||
|
||||
def allValues(prefix: String = ""): Map[String, ValueModel] =
|
||||
abilities
|
||||
.foldLeft(values) { case (ts, (k, v)) =>
|
||||
ts ++ v.allValues(k + ".")
|
||||
}
|
||||
.map(_.swap.map(prefix + _).swap)
|
||||
|
||||
def allServices(prefix: String = ""): Map[String, ServiceModel] =
|
||||
abilities
|
||||
.foldLeft(services) { case (ts, (k, v)) =>
|
||||
ts ++ v.allServices(k + ".")
|
||||
}
|
||||
.map(_.swap.map(prefix + _).swap)
|
||||
|
||||
def `type`(name: String): Option[ProductType] =
|
||||
NonEmptyMap
|
||||
.fromMap(
|
||||
SortedMap.from(
|
||||
funcs.view.mapValues(_.arrowType) ++
|
||||
types ++
|
||||
values.view.mapValues(_.lastType) ++
|
||||
services.view.mapValues(_.`type`) ++
|
||||
abilities.flatMap { case (n, c) =>
|
||||
c.`type`(n).map(n -> _)
|
||||
}
|
||||
)
|
||||
)
|
||||
.map(ProductType(name, _))
|
||||
}
|
||||
|
||||
object AquaContext {
|
||||
|
||||
implicit object AquaContextMonoid extends Monoid[AquaContext] {
|
||||
|
||||
override def empty: AquaContext =
|
||||
AquaContext(Map.empty, Map.empty, Map.empty, Map.empty, Map.empty)
|
||||
|
||||
override def combine(x: AquaContext, y: AquaContext): AquaContext =
|
||||
AquaContext(
|
||||
x.funcs ++ y.funcs,
|
||||
x.types ++ y.types,
|
||||
x.values ++ y.values,
|
||||
x.abilities ++ y.abilities,
|
||||
x.services ++ y.services
|
||||
)
|
||||
}
|
||||
|
||||
def fromServiceModel(sm: ServiceModel, serviceId: ValueModel): AquaContext =
|
||||
AquaContext(
|
||||
funcs = sm.arrows.toSortedMap.map { case (fnName, arrowType) =>
|
||||
val (args, call, ret) = ArgsCall.arrowToArgsCallRet(arrowType)
|
||||
fnName ->
|
||||
FuncCallable(
|
||||
fnName,
|
||||
// TODO: capture ability resolution, get ID from the call context
|
||||
FuncOp.leaf(CallServiceTag(serviceId, fnName, call, None)),
|
||||
args,
|
||||
(ret.map(_.model), arrowType.res).mapN(_ -> _),
|
||||
Map.empty,
|
||||
Map.empty
|
||||
)
|
||||
},
|
||||
types = Map.empty,
|
||||
values = Map.empty,
|
||||
abilities = Map.empty,
|
||||
services = Map.empty
|
||||
)
|
||||
|
||||
def fromScriptModel(sm: ScriptModel, init: AquaContext = Monoid.empty[AquaContext]): AquaContext =
|
||||
sm.models
|
||||
.foldLeft((init, Monoid.empty[AquaContext])) {
|
||||
case ((ctx, export), c: ConstantModel) =>
|
||||
val add =
|
||||
Monoid
|
||||
.empty[AquaContext]
|
||||
.copy(values =
|
||||
if (c.allowOverrides && ctx.values.contains(c.name)) ctx.values
|
||||
else ctx.values.updated(c.name, c.value.resolveWith(ctx.values))
|
||||
)
|
||||
(ctx |+| add, export |+| add)
|
||||
case ((ctx, export), func: FuncModel) =>
|
||||
val fr = func.capture(ctx.funcs, ctx.values)
|
||||
val add =
|
||||
Monoid.empty[AquaContext].copy(funcs = ctx.funcs.updated(func.name, fr))
|
||||
(ctx |+| add, export |+| add)
|
||||
case ((ctx, export), t: TypeModel) =>
|
||||
val add =
|
||||
Monoid.empty[AquaContext].copy(types = ctx.types.updated(t.name, t.`type`))
|
||||
(ctx |+| add, export |+| add)
|
||||
case ((ctx, export), m: ServiceModel) =>
|
||||
val add =
|
||||
Monoid
|
||||
.empty[AquaContext]
|
||||
.copy(
|
||||
abilities = m.defaultId.fold(ctx.abilities)(id =>
|
||||
ctx.abilities.updated(m.name, fromServiceModel(m, id))
|
||||
),
|
||||
services = ctx.services.updated(m.name, m)
|
||||
)
|
||||
(ctx |+| add, export |+| add)
|
||||
case (ce, _) => ce
|
||||
}
|
||||
._2
|
||||
}
|
@ -1,3 +1,3 @@
|
||||
package aqua.model
|
||||
|
||||
case class ConstantModel(name: String, value: ValueModel) extends Model
|
||||
case class ConstantModel(name: String, value: ValueModel, allowOverrides: Boolean) extends Model
|
||||
|
@ -1,32 +1,12 @@
|
||||
package aqua.model
|
||||
|
||||
import aqua.model.func.{FuncCallable, FuncModel}
|
||||
import aqua.model.func.FuncModel
|
||||
import cats.Monoid
|
||||
import cats.data.Chain
|
||||
|
||||
case class ScriptModel(
|
||||
models: Chain[Model] = Chain.empty
|
||||
) extends Model {
|
||||
|
||||
case class Acc(
|
||||
arrows: Map[String, FuncCallable] = Map.empty,
|
||||
values: Map[String, ValueModel] = Map.empty,
|
||||
output: Chain[FuncCallable] = Chain.empty
|
||||
)
|
||||
|
||||
lazy val funcs: Chain[FuncModel] = models.collect { case c: FuncModel => c }
|
||||
lazy val constants: Chain[ConstantModel] = models.collect { case c: ConstantModel => c }
|
||||
|
||||
lazy val resolveFunctions: Chain[FuncCallable] = models
|
||||
.foldLeft(Acc()) {
|
||||
case (a, c: ConstantModel) => a.copy(values = a.values.updated(c.name, c.value))
|
||||
case (a, func: FuncModel) =>
|
||||
val fr = func.capture(a.arrows, a.values)
|
||||
a.copy(output = a.output :+ fr, arrows = a.arrows.updated(func.name, fr))
|
||||
case (a, _) => a
|
||||
}
|
||||
.output
|
||||
}
|
||||
) extends Model
|
||||
|
||||
object ScriptModel {
|
||||
|
||||
|
@ -1,10 +1,12 @@
|
||||
package aqua.model
|
||||
|
||||
import aqua.types.ArrowType
|
||||
import aqua.types.{ArrowType, ProductType}
|
||||
import cats.data.NonEmptyMap
|
||||
|
||||
case class ServiceModel(
|
||||
name: String,
|
||||
arrows: NonEmptyMap[String, ArrowType],
|
||||
defaultId: Option[ValueModel]
|
||||
) extends Model
|
||||
) extends Model {
|
||||
def `type`: ProductType = ProductType(name, arrows)
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package aqua.model.func
|
||||
|
||||
import aqua.model.func.body.{AssignmentTag, CallArrowTag, FuncOp, OpTag}
|
||||
import aqua.model.{ValueModel, VarModel}
|
||||
import aqua.model.{Model, ValueModel, VarModel}
|
||||
import aqua.types.{ArrowType, Type}
|
||||
import cats.Eval
|
||||
import cats.data.Chain
|
||||
@ -15,7 +15,7 @@ case class FuncCallable(
|
||||
ret: Option[(ValueModel, Type)],
|
||||
capturedArrows: Map[String, FuncCallable],
|
||||
capturedValues: Map[String, ValueModel]
|
||||
) {
|
||||
) extends Model {
|
||||
|
||||
private val logger = Logger.of[FuncCallable]
|
||||
import logger._
|
||||
|
@ -1,7 +1,6 @@
|
||||
package aqua.semantics
|
||||
|
||||
import aqua.model.{EmptyModel, Model}
|
||||
import aqua.parser.lexer.Token
|
||||
import aqua.model.{AquaContext, EmptyModel, Model}
|
||||
import aqua.semantics.rules.abilities.AbilitiesState
|
||||
import aqua.semantics.rules.names.NamesState
|
||||
import aqua.semantics.rules.types.TypesState
|
||||
@ -10,7 +9,7 @@ import cats.kernel.Monoid
|
||||
import cats.syntax.monoid._
|
||||
|
||||
case class CompilerState[F[_]](
|
||||
errors: Chain[(Token[F], String)] = Chain.empty[(Token[F], String)],
|
||||
errors: Chain[SemanticError[F]] = Chain.empty[SemanticError[F]],
|
||||
names: NamesState[F] = NamesState[F](),
|
||||
abilities: AbilitiesState[F] = AbilitiesState[F](),
|
||||
types: TypesState[F] = TypesState[F]()
|
||||
@ -19,6 +18,12 @@ case class CompilerState[F[_]](
|
||||
object CompilerState {
|
||||
type S[F[_]] = State[CompilerState[F], Model]
|
||||
|
||||
def init[F[_]](ctx: AquaContext): CompilerState[F] = CompilerState(
|
||||
names = NamesState.init[F](ctx),
|
||||
abilities = AbilitiesState.init[F](ctx),
|
||||
types = TypesState.init[F](ctx)
|
||||
)
|
||||
|
||||
implicit def compilerStateMonoid[F[_]]: Monoid[S[F]] = new Monoid[S[F]] {
|
||||
override def empty: S[F] = State.pure(EmptyModel("compiler state monoid empty"))
|
||||
|
||||
|
@ -0,0 +1,8 @@
|
||||
package aqua.semantics
|
||||
|
||||
import aqua.parser.Ast
|
||||
import aqua.parser.lexer.Token
|
||||
|
||||
sealed trait SemanticError[F[_]]
|
||||
case class RulesViolated[F[_]](token: Token[F], message: String) extends SemanticError[F]
|
||||
case class WrongAST[F[_]](ast: Ast[F]) extends SemanticError[F]
|
@ -1,6 +1,6 @@
|
||||
package aqua.semantics
|
||||
|
||||
import aqua.model.Model
|
||||
import aqua.model.{AquaContext, Model, ScriptModel}
|
||||
import aqua.model.func.body.FuncOp
|
||||
import aqua.parser.lexer.Token
|
||||
import aqua.parser.{Ast, Expr}
|
||||
@ -16,7 +16,7 @@ import aqua.semantics.rules.types.{TypeOp, TypesAlgebra, TypesInterpreter, Types
|
||||
import cats.Eval
|
||||
import cats.arrow.FunctionK
|
||||
import cats.data.Validated.{Invalid, Valid}
|
||||
import cats.data.{Chain, EitherK, NonEmptyChain, State, ValidatedNec}
|
||||
import cats.data.{Chain, EitherK, NonEmptyChain, State, Validated, ValidatedNec}
|
||||
import cats.free.Free
|
||||
import monocle.Lens
|
||||
import monocle.macros.GenLens
|
||||
@ -59,7 +59,7 @@ object Semantics {
|
||||
|
||||
implicit val re: ReportError[F, CompilerState[F]] =
|
||||
(st: CompilerState[F], token: Token[F], hint: String) =>
|
||||
st.focus(_.errors).modify(_.append(token -> hint))
|
||||
st.focus(_.errors).modify(_.append(RulesViolated(token, hint)))
|
||||
|
||||
implicit val ns: Lens[CompilerState[F], NamesState[F]] = GenLens[CompilerState[F]](_.names)
|
||||
|
||||
@ -83,13 +83,17 @@ object Semantics {
|
||||
def astToState[F[_]](ast: Ast[F]): State[CompilerState[F], Model] =
|
||||
(transpile[F] _ andThen interpret[F])(ast)
|
||||
|
||||
def generateModel[F[_]](ast: Ast[F]): ValidatedNec[(Token[F], String), Model] =
|
||||
def process[F[_]](ast: Ast[F], init: AquaContext): ValidatedNec[SemanticError[F], AquaContext] =
|
||||
astToState[F](ast)
|
||||
.run(CompilerState[F]())
|
||||
.map { case (state, gen) =>
|
||||
NonEmptyChain
|
||||
.fromChain(state.errors)
|
||||
.fold[ValidatedNec[(Token[F], String), Model]](Valid(gen))(Invalid(_))
|
||||
.run(CompilerState.init[F](init))
|
||||
.map {
|
||||
case (state, gen: ScriptModel) =>
|
||||
val ctx = AquaContext.fromScriptModel(gen)
|
||||
NonEmptyChain
|
||||
.fromChain(state.errors)
|
||||
.fold[ValidatedNec[SemanticError[F], AquaContext]](Valid(ctx))(Invalid(_))
|
||||
case (_, _) =>
|
||||
Validated.invalidNec[SemanticError[F], AquaContext](WrongAST(ast))
|
||||
}
|
||||
.value
|
||||
}
|
||||
|
@ -6,7 +6,9 @@ import aqua.parser.expr.AbilityIdExpr
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
||||
import cats.free.Free
|
||||
import cats.syntax.flatMap._
|
||||
import cats.syntax.functor._
|
||||
|
||||
class AbilityIdSem[F[_]](val expr: AbilityIdExpr[F]) extends AnyVal {
|
||||
|
||||
@ -14,10 +16,13 @@ class AbilityIdSem[F[_]](val expr: AbilityIdExpr[F]) extends AnyVal {
|
||||
A: AbilitiesAlgebra[F, Alg],
|
||||
V: ValuesAlgebra[F, Alg]
|
||||
): Prog[Alg, Model] =
|
||||
V.ensureIsString(expr.id) >> A.setServiceId(expr.ability, expr.id) >> V.valueToModel(
|
||||
V.ensureIsString(expr.id) >> V.valueToModel(
|
||||
expr.id
|
||||
) map {
|
||||
case Some(id) => FuncOp.leaf(AbilityIdTag(id, expr.ability.value)): Model
|
||||
case _ => Model.error("Cannot resolve ability ID")
|
||||
) >>= {
|
||||
case Some(id) =>
|
||||
A.setServiceId(expr.ability, expr.id, id) as (FuncOp.leaf(
|
||||
AbilityIdTag(id, expr.ability.value)
|
||||
): Model)
|
||||
case _ => Free.pure[Alg, Model](Model.error("Cannot resolve ability ID"))
|
||||
}
|
||||
}
|
||||
|
@ -58,12 +58,12 @@ class CallArrowSem[F[_]](val expr: CallArrowExpr[F]) extends AnyVal {
|
||||
Option(at -> sid) // Here we assume that Ability is a Service that must be resolved
|
||||
case _ => None
|
||||
}.flatMap(_.fold(Free.pure[Alg, Option[FuncOp]](None)) { case (arrowType, serviceId) =>
|
||||
(checkArgsRes(arrowType), V.valueToModel(serviceId)).mapN {
|
||||
case ((argsResolved, t), Some(serviceIdM)) =>
|
||||
checkArgsRes(arrowType).map {
|
||||
case (argsResolved, t) =>
|
||||
Option(
|
||||
FuncOp.leaf(
|
||||
CallServiceTag(
|
||||
serviceId = serviceIdM,
|
||||
serviceId = serviceId,
|
||||
funcName = funcName.value,
|
||||
Call(argsResolved, (variable.map(_.value), t).mapN(Call.Export))
|
||||
)
|
||||
|
@ -34,7 +34,8 @@ class ConstantSem[F[_]](val expr: ConstantExpr[F]) extends AnyVal {
|
||||
case (_, Some(t), _) =>
|
||||
N.defineConstant(expr.name, t._2) as (ConstantModel(
|
||||
expr.name.value,
|
||||
t._1
|
||||
t._1,
|
||||
expr.skipIfAlreadyDefined
|
||||
): Model)
|
||||
}
|
||||
} yield model
|
||||
|
@ -34,9 +34,11 @@ class ServiceSem[F[_]](val expr: ServiceExpr[F]) extends AnyVal {
|
||||
arrows,
|
||||
defaultId
|
||||
)
|
||||
_ <- expr.id
|
||||
_ <- (expr.id zip defaultId)
|
||||
.fold(Free.pure[Alg, Unit](()))(idV =>
|
||||
(V.ensureIsString(idV) >> A.setServiceId(expr.name, idV)).map(_ => ())
|
||||
(V.ensureIsString(idV._1) >> A.setServiceId(expr.name, idV._1, idV._2)).map(_ =>
|
||||
()
|
||||
)
|
||||
)
|
||||
} yield
|
||||
if (defineResult) {
|
||||
|
@ -25,10 +25,10 @@ class AbilitiesAlgebra[F[_], Alg[_]](implicit A: InjectK[AbilityOp[F, *], Alg])
|
||||
def getArrow(name: Ability[F], arrow: Name[F]): Free[Alg, Option[ArrowType]] =
|
||||
Free.liftInject[Alg](GetArrow[F](name, arrow))
|
||||
|
||||
def setServiceId(name: Ability[F], id: Value[F]): Free[Alg, Boolean] =
|
||||
Free.liftInject[Alg](SetServiceId[F](name, id))
|
||||
def setServiceId(name: Ability[F], id: Value[F], vm: ValueModel): Free[Alg, Boolean] =
|
||||
Free.liftInject[Alg](SetServiceId[F](name, id, vm))
|
||||
|
||||
def getServiceId(name: Ability[F]): Free[Alg, Option[Value[F]]] =
|
||||
def getServiceId(name: Ability[F]): Free[Alg, Option[ValueModel]] =
|
||||
Free.liftInject[Alg](GetServiceId[F](name))
|
||||
|
||||
def beginScope(token: Token[F]): Free[Alg, Unit] =
|
||||
|
@ -1,7 +1,7 @@
|
||||
package aqua.semantics.rules.abilities
|
||||
|
||||
import aqua.model.ServiceModel
|
||||
import aqua.parser.lexer.{Name, Value}
|
||||
import aqua.model.{ServiceModel, ValueModel}
|
||||
import aqua.parser.lexer.Name
|
||||
import aqua.semantics.rules.{ReportError, StackInterpreter}
|
||||
import aqua.types.ArrowType
|
||||
import cats.data.{NonEmptyList, State}
|
||||
@ -17,6 +17,7 @@ class AbilitiesInterpreter[F[_], X](implicit
|
||||
GenLens[AbilitiesState[F]](_.stack)
|
||||
) with (AbilityOp[F, *] ~> State[X, *]) {
|
||||
|
||||
// TODO: resolve abilities as well
|
||||
private def getService(name: String): S[Option[ServiceModel]] =
|
||||
getState.map(_.services.get(name))
|
||||
|
||||
@ -56,9 +57,11 @@ class AbilitiesInterpreter[F[_], X](implicit
|
||||
getService(s.name.value).flatMap {
|
||||
case Some(_) =>
|
||||
mapStackHead(
|
||||
modify(st => st.copy(rootServiceIds = st.rootServiceIds.updated(s.name.value, s.id)))
|
||||
modify(st =>
|
||||
st.copy(rootServiceIds = st.rootServiceIds.updated(s.name.value, s.id -> s.vm))
|
||||
)
|
||||
.as(true)
|
||||
)(h => h.copy(serviceIds = h.serviceIds.updated(s.name.value, s.id)) -> true)
|
||||
)(h => h.copy(serviceIds = h.serviceIds.updated(s.name.value, s.id -> s.vm)) -> true)
|
||||
|
||||
case None =>
|
||||
report(s.name, "Service with this name is not registered, can't set its ID").as(false)
|
||||
@ -66,15 +69,19 @@ class AbilitiesInterpreter[F[_], X](implicit
|
||||
|
||||
case s: GetServiceId[F] =>
|
||||
getState.flatMap(st =>
|
||||
st.stack.flatMap(_.serviceIds.get(s.name.value)).headOption orElse st.rootServiceIds.get(
|
||||
s.name.value
|
||||
) match {
|
||||
st.stack
|
||||
.flatMap(_.serviceIds.get(s.name.value).map(_._2))
|
||||
.headOption orElse st.rootServiceIds
|
||||
.get(
|
||||
s.name.value
|
||||
)
|
||||
.map(_._2) orElse st.services.get(s.name.value).flatMap(_.defaultId) match {
|
||||
case None =>
|
||||
report(
|
||||
s.name,
|
||||
s"Service ID unresolved, use `${s.name.value} id` expression to set it"
|
||||
)
|
||||
.as(Option.empty[Value[F]])
|
||||
.as(Option.empty[ValueModel])
|
||||
|
||||
case v => State.pure(v)
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package aqua.semantics.rules.abilities
|
||||
|
||||
import aqua.model.ServiceModel
|
||||
import aqua.model.{AquaContext, ServiceModel, ValueModel}
|
||||
import aqua.parser.lexer.{Ability, Name, Token, Value}
|
||||
import aqua.types.ArrowType
|
||||
import cats.Monoid
|
||||
@ -9,7 +9,8 @@ import cats.data.NonEmptyList
|
||||
case class AbilitiesState[F[_]](
|
||||
stack: List[AbilitiesState.Frame[F]] = Nil,
|
||||
services: Map[String, ServiceModel] = Map.empty,
|
||||
rootServiceIds: Map[String, Value[F]] = Map.empty[String, Value[F]],
|
||||
abilities: Map[String, AquaContext] = Map.empty,
|
||||
rootServiceIds: Map[String, (Value[F], ValueModel)] = Map.empty[String, (Value[F], ValueModel)],
|
||||
definitions: Map[String, Ability[F]] = Map.empty[String, Ability[F]]
|
||||
) {
|
||||
|
||||
@ -28,7 +29,7 @@ object AbilitiesState {
|
||||
case class Frame[F[_]](
|
||||
token: Token[F],
|
||||
arrows: Map[String, (Name[F], ArrowType)] = Map.empty[String, (Name[F], ArrowType)],
|
||||
serviceIds: Map[String, Value[F]] = Map.empty[String, Value[F]]
|
||||
serviceIds: Map[String, (Value[F], ValueModel)] = Map.empty[String, (Value[F], ValueModel)]
|
||||
)
|
||||
|
||||
implicit def abilitiesStateMonoid[F[_]]: Monoid[AbilitiesState[F]] =
|
||||
@ -39,8 +40,12 @@ object AbilitiesState {
|
||||
AbilitiesState(
|
||||
Nil,
|
||||
x.services ++ y.services,
|
||||
x.abilities ++ y.abilities,
|
||||
x.rootServiceIds ++ y.rootServiceIds,
|
||||
x.definitions ++ y.definitions
|
||||
)
|
||||
}
|
||||
|
||||
def init[F[_]](context: AquaContext): AbilitiesState[F] =
|
||||
AbilitiesState(services = context.allServices(), abilities = context.abilities)
|
||||
}
|
||||
|
@ -20,9 +20,10 @@ case class DefineService[F[_]](
|
||||
|
||||
case class GetArrow[F[_]](name: Ability[F], arrow: Name[F]) extends AbilityOp[F, Option[ArrowType]]
|
||||
|
||||
case class SetServiceId[F[_]](name: Ability[F], id: Value[F]) extends AbilityOp[F, Boolean]
|
||||
case class SetServiceId[F[_]](name: Ability[F], id: Value[F], vm: ValueModel)
|
||||
extends AbilityOp[F, Boolean]
|
||||
|
||||
case class GetServiceId[F[_]](name: Ability[F]) extends AbilityOp[F, Option[Value[F]]]
|
||||
case class GetServiceId[F[_]](name: Ability[F]) extends AbilityOp[F, Option[ValueModel]]
|
||||
|
||||
case class BeginScope[F[_]](token: Token[F]) extends AbilityOp[F, Unit]
|
||||
|
||||
|
@ -1,8 +1,10 @@
|
||||
package aqua.semantics.rules.names
|
||||
|
||||
import aqua.parser.lexer.{Name, Token, Value}
|
||||
import aqua.model.AquaContext
|
||||
import aqua.parser.lexer.{Name, Token}
|
||||
import aqua.types.{ArrowType, Type}
|
||||
import cats.kernel.Monoid
|
||||
import cats.syntax.functor._
|
||||
|
||||
case class NamesState[F[_]](
|
||||
stack: List[NamesState.Frame[F]] = Nil,
|
||||
@ -46,4 +48,10 @@ object NamesState {
|
||||
constants = x.constants ++ y.constants
|
||||
)
|
||||
}
|
||||
|
||||
def init[F[_]](context: AquaContext): NamesState[F] =
|
||||
NamesState(
|
||||
rootArrows = context.allFuncs().map(_.map(_.arrowType)),
|
||||
constants = context.allValues().map(_.map(_.lastType))
|
||||
)
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package aqua.semantics.rules.types
|
||||
|
||||
import aqua.model.{IntoArrayModel, IntoFieldModel, IntoIndexModel, LambdaModel}
|
||||
import aqua.model.{AquaContext, IntoArrayModel, IntoFieldModel, IntoIndexModel, LambdaModel}
|
||||
import aqua.parser.lexer.{
|
||||
ArrayTypeToken,
|
||||
ArrowTypeToken,
|
||||
@ -128,4 +128,6 @@ object TypesState {
|
||||
definitions = x.definitions ++ y.definitions
|
||||
)
|
||||
}
|
||||
|
||||
def init[F[_]](context: AquaContext): TypesState[F] = TypesState(strict = context.allTypes())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user