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:
Dmitry Kurinskiy 2021-06-08 15:55:06 +03:00 committed by GitHub
parent d300a7dea3
commit 4696e95129
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 298 additions and 113 deletions

9
aqua-src/app.aqua Normal file
View 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
View File

@ -0,0 +1,3 @@
service AppConfig("affinidi/get-config"):
getApp: -> string
getLocalService: -> LocalService

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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