Types interpreter

This commit is contained in:
dmitry 2021-03-15 14:42:07 +03:00
parent ab4a26ee96
commit 401b7a31df
9 changed files with 211 additions and 43 deletions

View File

@ -14,6 +14,12 @@ abstract class StackInterpreter[F[_], X, St, Fr](stackLens: Lens[St, List[Fr]])(
protected def getState: S[St] = State.get.map(lens.get)
protected def setState(st: St): S[Unit] = State.modify(s => lens.replace(st)(s))
protected def report(t: Token[F], hint: String): S[Unit] =
State.modify(error(_, t, hint))
protected def modify(f: St => St): S[Unit] =
State.modify(lens.modify(f))
protected def mapStackHead[A](ifStackEmpty: S[A])(f: Fr => (Fr, A)): S[A] =
getState.map(stackLens.get).flatMap {
case h :: tail =>
@ -38,12 +44,6 @@ abstract class StackInterpreter[F[_], X, St, Fr](stackLens: Lens[St, List[Fr]])(
ifStackEmpty
}
protected def report(t: Token[F], hint: String): S[Unit] =
State.modify(error(_, t, hint))
protected def modify(f: St => St): S[Unit] =
State.modify(lens.modify(f))
protected def endScope: S[Unit] =
modify(stackLens.modify(_.tail))

View File

@ -1,17 +1,17 @@
package aqua.ast.algebra.types
import aqua.parser.lexer.{ArrowDef, CustomTypeToken, LambdaOp, Name, Token, TypeToken}
import aqua.parser.lexer.{ArrowDef, ArrowTypeToken, CustomTypeToken, LambdaOp, Name, Token, TypeToken}
import cats.data.{NonEmptyList, NonEmptyMap}
sealed trait TypeOp[F[_], T]
case class ResolveType[F[_]](token: TypeToken[F]) extends TypeOp[F, Type]
case class ResolveArrowDef[F[_]](arrowDef: ArrowDef[F]) extends TypeOp[F, ArrowType]
case class ResolveType[F[_]](token: TypeToken[F]) extends TypeOp[F, Option[Type]]
case class ResolveArrowDef[F[_]](arrowDef: ArrowTypeToken[F]) extends TypeOp[F, Option[ArrowType]]
case class DefineField[F[_]](name: Name[F], `type`: Type) extends TypeOp[F, Unit]
case class PurgeFields[F[_]]() extends TypeOp[F, NonEmptyList[(Name[F], Type)]]
case class DefineDataType[F[_]](name: CustomTypeToken[F], fields: NonEmptyMap[String, Type]) extends TypeOp[F, Unit]
case class DefineAlias[F[_]](name: CustomTypeToken[F], target: Type) extends TypeOp[F, Unit]
case class DefineField[F[_]](name: Name[F], `type`: Type) extends TypeOp[F, Boolean]
case class PurgeFields[F[_]](token: Token[F]) extends TypeOp[F, Option[NonEmptyMap[String, Type]]]
case class DefineDataType[F[_]](name: CustomTypeToken[F], fields: NonEmptyMap[String, Type]) extends TypeOp[F, Boolean]
case class DefineAlias[F[_]](name: CustomTypeToken[F], target: Type) extends TypeOp[F, Boolean]
case class ResolveLambda[F[_]](root: Type, ops: List[LambdaOp[F]]) extends TypeOp[F, Option[Type]]
case class EnsureTypeMatches[F[_]](token: Token[F], expected: Type, given: Type) extends TypeOp[F, Boolean]

View File

@ -1,28 +1,28 @@
package aqua.ast.algebra.types
import aqua.parser.lexer.{ArrowDef, CustomTypeToken, LambdaOp, Name, Token, TypeToken}
import aqua.parser.lexer.{ArrowDef, ArrowTypeToken, CustomTypeToken, LambdaOp, Name, Token, TypeToken}
import cats.InjectK
import cats.data.{NonEmptyList, NonEmptyMap}
import cats.free.Free
class TypesAlgebra[F[_], Alg[_]](implicit T: InjectK[TypeOp[F, *], Alg]) {
def resolveType(token: TypeToken[F]): Free[Alg, Type] =
def resolveType(token: TypeToken[F]): Free[Alg, Option[Type]] =
Free.liftInject[Alg](ResolveType(token))
def resolveArrowDef(arrowDef: ArrowDef[F]): Free[Alg, ArrowType] =
def resolveArrowDef(arrowDef: ArrowTypeToken[F]): Free[Alg, Option[ArrowType]] =
Free.liftInject[Alg](ResolveArrowDef(arrowDef))
def defineField(name: Name[F], `type`: Type): Free[Alg, Unit] =
def defineField(name: Name[F], `type`: Type): Free[Alg, Boolean] =
Free.liftInject[Alg](DefineField[F](name, `type`))
def purgeFields(): Free[Alg, NonEmptyList[(Name[F], Type)]] =
Free.liftInject[Alg](PurgeFields[F]())
def purgeFields(token: Token[F]): Free[Alg, Option[NonEmptyMap[String, Type]]] =
Free.liftInject[Alg](PurgeFields[F](token))
def defineDataType(name: CustomTypeToken[F], fields: NonEmptyMap[String, Type]): Free[Alg, Unit] =
def defineDataType(name: CustomTypeToken[F], fields: NonEmptyMap[String, Type]): Free[Alg, Boolean] =
Free.liftInject[Alg](DefineDataType(name, fields))
def defineAlias(name: CustomTypeToken[F], target: Type): Free[Alg, Unit] =
def defineAlias(name: CustomTypeToken[F], target: Type): Free[Alg, Boolean] =
Free.liftInject[Alg](DefineAlias(name, target))
def resolveLambda(root: Type, ops: List[LambdaOp[F]]): Free[Alg, Option[Type]] =

View File

@ -0,0 +1,159 @@
package aqua.ast.algebra.types
import aqua.ast.algebra.ReportError
import aqua.context.VarTypes.{Err, ExpectedProduct, FieldNotFound, NotAnArray}
import aqua.parser.lexer.{
ArrayTypeToken,
ArrowDef,
ArrowTypeToken,
BasicTypeToken,
CustomTypeToken,
IntoArray,
IntoField,
LambdaOp,
Name,
Token,
TypeToken
}
import cats.data.Validated.{Invalid, Valid}
import cats.data.{NonEmptyList, NonEmptyMap, State, Validated, ValidatedNel}
import cats.free.Free
import cats.{~>, Comonad}
import monocle.Lens
import cats.syntax.functor._
import cats.syntax.flatMap._
import scala.collection.immutable.{Queue, SortedMap}
class TypesInterpreter[F[_], X](implicit lens: Lens[X, TypesState[F]], error: ReportError[F, X])
extends (TypeOp[F, *] ~> State[X, *]) {
type S[A] = State[X, A]
type St = TypesState[F]
protected def getState: S[St] = State.get.map(lens.get)
protected def setState(st: St): S[Unit] = State.modify(s => lens.replace(st)(s))
protected def report(t: Token[F], hint: String): S[Unit] =
State.modify(error(_, t, hint))
protected def modify(f: St => St): S[Unit] =
State.modify(lens.modify(f))
override def apply[A](fa: TypeOp[F, A]): State[X, A] =
fa match {
case rt: ResolveType[F] =>
getState.map(_.resolveTypeToken(rt.token)).flatMap {
case Some(t) => State.pure(Some(t))
case None => report(rt.token, s"Unresolved type").as(None)
}
case ra: ResolveArrowDef[F] =>
getState.map(_.resolveArrowDef(ra.arrowDef)).flatMap {
case Valid(t) => State.pure[X, Option[ArrowType]](Some(t))
case Invalid(errs) =>
errs
.foldLeft[S[Option[ArrowType]]](State.pure(None)) {
case (n, (tkn, hint)) => report(tkn, hint) >> n
}
}
case df: DefineField[F] =>
getState.map(_.fields.get(df.name.value)).flatMap {
case None =>
modify(st => st.copy(fields = st.fields.updated(df.name.value, df.name -> df.`type`))).as(true)
case Some(_) =>
report(df.name, s"Cannot define field `${df.name.value}`, it was already defined above").as(false)
}
case pf: PurgeFields[F] =>
getState.map(_.fields.view.mapValues(_._2)).map(SortedMap.from(_)).map(NonEmptyMap.fromMap(_)).flatMap {
case Some(fs) => modify(_.copy(fields = Map.empty)).as(Some(fs))
case None => report(pf.token, "Cannot define a data type without fields").as(None)
}
case ddt: DefineDataType[F] =>
getState.map(_.isDefined(ddt.name.value)).flatMap {
case true => report(ddt.name, s"Type `${ddt.name.value}` was already defined").as(false)
case false =>
modify(st => st.copy(strict = st.strict.updated(ddt.name.value, ProductType(ddt.name.value, ddt.fields))))
.as(true)
}
case da: DefineAlias[F] =>
getState.map(_.isDefined(da.name.value)).flatMap {
case true => report(da.name, s"Type `${da.name.value}` was already defined").as(false)
case false => modify(st => st.copy(strict = st.strict.updated(da.name.value, da.target))).as(true)
}
case rl: ResolveLambda[F] =>
getState.map(_.resolveOps(rl.root, rl.ops)).flatMap {
case Left((tkn, hint)) => report(tkn, hint).as(None)
case Right(t) => State.pure(Some(t))
}
case etm: EnsureTypeMatches[F] =>
if (etm.expected.acceptsValueOf(etm.`given`)) State.pure(true)
else report(etm.token, s"Types mismatch, expected: ${etm.expected}, given: ${etm.`given`}").as(false)
}
}
case class TypesState[F[_]](
fields: Map[String, (Name[F], Type)],
strict: Map[String, Type]
) {
def isDefined(t: String): Boolean = strict.contains(t)
def resolveTypeToken(tt: TypeToken[F]): Option[Type] =
tt match {
case ArrayTypeToken(_, dtt) =>
resolveTypeToken(dtt).collect {
case it: DataType => ArrayType(it)
}
case ctt: CustomTypeToken[F] => strict.get(ctt.value)
case btt: BasicTypeToken[F] => Some(btt.value)
case ArrowTypeToken(_, args, res) =>
val strictArgs = args.map(resolveTypeToken).collect {
case Some(dt: DataType) => dt
}
val strictRes = res.flatMap(resolveTypeToken).collect {
case dt: DataType => dt
}
Option.when(strictRes.isDefined == res.isDefined && strictArgs.length == args.length)(
ArrowType(strictArgs, strictRes)
)
}
def resolveArrowDef(ad: ArrowTypeToken[F]): ValidatedNel[(Token[F], String), ArrowType] =
ad.resType.flatMap(resolveTypeToken) match {
case resType if resType.isDefined == ad.resType.isDefined =>
val (errs, argTypes) = ad.argTypes
.map(tt => resolveTypeToken(tt).toRight(tt -> s"Type unresolved"))
.foldLeft[(Queue[(Token[F], String)], Queue[Type])]((Queue.empty, Queue.empty)) {
case ((errs, argTypes), Right(at)) => (errs, argTypes.enqueue(at))
case ((errs, argTypes), Left(e)) => (errs.enqueue(e), argTypes)
}
NonEmptyList
.fromList(errs.toList)
.fold[ValidatedNel[(Token[F], String), ArrowType]](Valid(ArrowType(argTypes.toList, resType)))(Invalid(_))
case _ => Invalid(NonEmptyList.of(ad.resType.getOrElse(ad) -> "Cannot resolve the result type"))
}
def resolveOps(rootT: Type, ops: List[LambdaOp[F]]): Either[(Token[F], String), Type] =
ops.headOption.fold[Either[(Token[F], String), Type]](Right(rootT)) {
case i @ IntoArray(f) =>
rootT match {
case ArrayType(intern) => resolveOps(intern, ops.tail).map[Type](ArrayType)
case _ => Left(i -> s"Expected $rootT to be an array")
}
case i @ IntoField(name) =>
rootT match {
case pt @ ProductType(_, fields) =>
fields(i.value)
.toRight(i -> s"Field `${i.value}` not found in type `${pt.name}``")
.flatMap(resolveOps(_, ops.tail))
case _ => Left(i -> s"Expected product to resolve a field, got $rootT")
}
}
}

View File

@ -6,15 +6,17 @@ import aqua.parser.lexer.Token._
import aqua.parser.lexer.{CustomTypeToken, TypeToken}
import aqua.parser.lift.LiftParser
import cats.Comonad
import cats.free.Free
import cats.parse.Parser
import cats.syntax.functor._
case class AliasExpr[F[_]](name: CustomTypeToken[F], target: TypeToken[F]) extends Expr[F] {
def program[Alg[_]](implicit T: TypesAlgebra[F, Alg]): Prog[Alg, Gen] =
for {
t <- T.resolveType(target)
_ <- T.defineAlias(name, t)
} yield Gen.noop
T.resolveType(target).flatMap {
case Some(t) => T.defineAlias(name, t).as(Gen.noop)
case None => Free.pure(Gen.noop)
}
}

View File

@ -7,15 +7,17 @@ import aqua.parser.lexer.Token._
import aqua.parser.lexer.{ArrowTypeToken, Name}
import aqua.parser.lift.LiftParser
import cats.Comonad
import cats.free.Free
import cats.parse.Parser
import cats.syntax.functor._
case class ArrowTypeExpr[F[_]](name: Name[F], `type`: ArrowTypeToken[F]) extends Expr[F] {
def program[Alg[_]](implicit T: TypesAlgebra[F, Alg], A: AbilitiesAlgebra[F, Alg]): Prog[Alg, Gen] =
for {
t <- T.resolveArrowDef(`type`)
_ <- A.defineArrow(name, t)
} yield Gen.noop
T.resolveArrowDef(`type`).flatMap {
case Some(t) => A.defineArrow(name, t).as(Gen.noop)
case None => Free.pure(Gen.noop)
}
}

View File

@ -7,6 +7,7 @@ import aqua.parser.lexer.CustomTypeToken
import aqua.parser.lexer.Token._
import aqua.parser.lift.LiftParser
import cats.Comonad
import cats.free.Free
import cats.parse.Parser
import cats.syntax.functor._
@ -17,10 +18,11 @@ case class DataStructExpr[F[_]](name: CustomTypeToken[F]) extends Expr[F] {
T: TypesAlgebra[F, Alg]
): Prog[Alg, Gen] =
Prog after T
.purgeFields()
.map(_.map(kv => kv._1.value -> kv._2).toNem)
.flatMap(T.defineDataType(name, _))
.as(Gen("Data struct created"))
.purgeFields(name)
.flatMap {
case Some(fields) => T.defineDataType(name, fields).as(Gen("Data struct created"))
case None => Free.pure(Gen.noop)
}
}

View File

@ -6,15 +6,17 @@ import aqua.parser.lexer.Token._
import aqua.parser.lexer.{DataTypeToken, Name}
import aqua.parser.lift.LiftParser
import cats.Comonad
import cats.free.Free
import cats.parse.Parser
import cats.syntax.functor._
case class FieldTypeExpr[F[_]](name: Name[F], `type`: DataTypeToken[F]) extends Expr[F] {
def program[Alg[_]](implicit T: TypesAlgebra[F, Alg]): Prog[Alg, Gen] =
for {
t <- T.resolveType(`type`)
_ <- T.defineField(name, t)
} yield Gen.noop
T.resolveType(`type`).flatMap {
case Some(t) => T.defineField(name, t).as(Gen.noop)
case None => Free.pure(Gen.noop)
}
}

View File

@ -36,16 +36,17 @@ case class FuncExpr[F[_]](name: Name[F], args: List[Arg[F]], ret: Option[DataTyp
) {
case (f, Arg(argName, argType)) =>
// Resolve arg type, remember it
for {
acc <- f
t <- T.resolveType(argType)
_ <- N.define(argName, t)
} yield acc.enqueue(t)
f.flatMap(acc =>
T.resolveType(argType).flatMap {
case Some(t) => N.define(argName, t).as(acc.enqueue(t))
case None => Free.pure(acc)
}
)
}
.map(_.toList),
// Resolve return type
// TODO handle return VALUE!
ret.fold(Free.pure[Alg, Option[Type]](None))(T.resolveType(_).map(Some(_)))
ret.fold(Free.pure[Alg, Option[Type]](None))(T.resolveType(_))
)
.map(argsAndRes => ArrowType(argsAndRes._1, argsAndRes._2)),
(funcArrow: ArrowType, bodyGen: Gen) =>