Type checker works

This commit is contained in:
dmitry 2021-03-15 15:49:48 +03:00
parent e33b499c42
commit 6b1f7dd0af
8 changed files with 61 additions and 97 deletions

View File

@ -1,43 +1,15 @@
package aqua
import aqua.context.{Abilities, AbilitiesResolve, ArgsAndVars, Arrows, Types, VarTypes}
import aqua.context.scope.ScopeWalker
import aqua.context.walker.Walker
import aqua.parser.Block
import cats.data.{NonEmptyList, Validated, ValidatedNel}
import cats.parse.{Parser => P, Parser0 => P0}
import aqua.ast.{Ast, Gen}
import cats.data.ValidatedNel
import aqua.parser.lift.Span
import cats.data.Validated.Valid
import shapeless.HNil
object Aqua {
private val parser: P0[List[Block[Span.F, HNil]]] = Block.blocks[Span.F]
val step1 =
Walker
.hnil[Span.F]
.andThen(new ScopeWalker(_))
.andThen(new Arrows.ExpDef(_))
.andThen(new ArgsAndVars.ExpDef(_))
.andThen(new Types.ExpDef(_))
.andThen(new Abilities.ExpDef(_))
.andThen(new AbilitiesResolve.ExpDef(_))
def parse(input: String): ValidatedNel[AquaError, Ast[Span.F]] =
Ast.fromString[Span.F](input)
val step2 =
new VarTypes.Checker[Span.F, step1.Out, step1.Out](Walker.noopFrom(step1))
def compile(input: String): ValidatedNel[AquaError, Gen] =
parse(input).andThen(ast => Compiler.compile(ast).leftMap(_.map(ts => CompilerError(ts._1.unit._1, ts._2))))
def parse(input: String): ValidatedNel[AquaError, List[Block[Span.F, step2.Out]]] =
Validated
.fromEither(
parser
.parseAll(input)
.left
.map(pe => NonEmptyList.one[AquaError](SyntaxError(pe.failedAtOffset, pe.expected)))
)
.andThen { blocks =>
step1.walkValidate(blocks).leftMap(_.map(_.toStringF).map(sv => CompilerError(sv._1, sv._2)))
}
.andThen { blocks =>
step2.walkValidate(blocks).leftMap(_.map(_.toStringF).map(sv => CompilerError(sv._1, sv._2)))
}
}

View File

@ -1,10 +1,5 @@
package aqua
import aqua.context.Types
import aqua.ast.algebra.names.NameOp
import aqua.ast.{Ast, Expr}
import aqua.parser.lift.Span
import cats.Eval
import cats.effect.{IO, IOApp}
import cats.data.Validated
@ -14,8 +9,8 @@ object Main extends IOApp.Simple {
override def run: IO[Unit] =
IO {
def tryParse(str: String) =
Aqua.parse(str) match {
def process(str: String) =
Aqua.compile(str) match {
case Validated.Valid(v)
println(v)
println(Console.GREEN + "Aqua script processed successfully" + Console.RESET)
@ -25,18 +20,7 @@ object Main extends IOApp.Simple {
}
val script = Source.fromResource("typecheck.aqua").mkString
//tryParse(experimental)
val ast = Ast
.fromString[Span.F](script)
.andThen(
Compiler
.compile[Span.F](_)
.leftMap(_.map(ts => CompilerError(ts._1.unit._1, ts._2)))
)
.leftMap(_.map(_.showForConsole(script)).map(println))
ast.map(println(_))
process(script)
}

View File

@ -9,7 +9,7 @@ case class ReadName[F[_]](name: Name[F]) extends NameOp[F, Option[Type]]
case class ReadArrow[F[_]](name: Name[F]) extends NameOp[F, Option[ArrowType]]
case class DefineName[F[_]](name: Name[F], `type`: Type) extends NameOp[F, Boolean]
case class DefineName[F[_]](name: Name[F], `type`: Type, isRoot: Boolean) extends NameOp[F, Boolean]
case class BeginScope[F[_]](token: Token[F]) extends NameOp[F, Unit]

View File

@ -13,8 +13,8 @@ class NamesAlgebra[F[_], Alg[_]](implicit V: InjectK[NameOp[F, *], Alg]) {
def readArrow(name: Name[F]): Free[Alg, Option[ArrowType]] =
Free.liftInject[Alg](ReadArrow(name))
def define(name: Name[F], `type`: Type): Free[Alg, Boolean] =
Free.liftInject[Alg](DefineName(name, `type`))
def define(name: Name[F], `type`: Type, isRoot: Boolean = false): Free[Alg, Boolean] =
Free.liftInject[Alg](DefineName(name, `type`, isRoot))
def beginScope(token: Token[F]): Free[Alg, Unit] =
Free.liftInject[Alg](BeginScope(token))

View File

@ -16,9 +16,11 @@ class NamesInterpreter[F[_], X](implicit lens: Lens[X, NamesState[F]], error: Re
with (NameOp[F, *] ~> State[X, *]) {
def readName(name: String): S[Option[Type]] =
getState.map(_.stack.collectFirst {
case frame if frame.names.contains(name) => frame.names(name)
})
getState.map { st =>
st.stack.collectFirst {
case frame if frame.names.contains(name) => frame.names(name)
} orElse st.rootNames.get(name)
}
override def apply[A](fa: NameOp[F, A]): State[X, A] =
(fa match {
@ -40,9 +42,10 @@ class NamesInterpreter[F[_], X](implicit lens: Lens[X, NamesState[F]], error: Re
readName(dn.name.value).flatMap {
case Some(_) => report(dn.name, "This name was already defined in the scope").as(false)
case None =>
mapStackHead(report(dn.name, "Cannot define a variable in the root scope").as(false))(
_.focus(_.names).index(dn.name.value).replace(dn.`type`) -> true
)
mapStackHead(
if (dn.isRoot) modify(_.focus(_.rootNames).index(dn.name.value).replace(dn.`type`)).as(true)
else report(dn.name, "Cannot define a variable in the root scope").as(false)
)(fr => fr.addName(dn.name.value, dn.`type`) -> true)
}
case bs: BeginScope[F] =>
beginScope(NamesFrame(bs.token))
@ -51,6 +54,8 @@ class NamesInterpreter[F[_], X](implicit lens: Lens[X, NamesState[F]], error: Re
}).asInstanceOf[State[X, A]]
}
case class NamesState[F[_]](stack: List[NamesFrame[F]] = Nil)
case class NamesState[F[_]](stack: List[NamesFrame[F]] = Nil, rootNames: Map[String, Type] = Map.empty)
case class NamesFrame[F[_]](token: Token[F], names: Map[String, Type] = Map.empty)
case class NamesFrame[F[_]](token: Token[F], names: Map[String, Type] = Map.empty) {
def addName(n: String, t: Type): NamesFrame[F] = copy[F](names = names.updated(n, t))
}

View File

@ -15,7 +15,9 @@ sealed trait Type {
}
sealed trait DataType extends Type
case class ScalarType private (name: String) extends DataType
case class ScalarType private (name: String) extends DataType {
override def toString: String = name
}
object ScalarType {
// TODO https://github.com/fluencelabs/interface-types/blob/master/crates/it-types/src/values.rs#L45-L49
@ -46,18 +48,27 @@ object ScalarType {
}
}
case class LiteralType private (oneOf: Set[ScalarType]) extends Type
object LiteralType {
val float = LiteralType(ScalarType.float)
val signed = LiteralType(ScalarType.signed)
val number = LiteralType(ScalarType.number)
val bool = LiteralType(Set(ScalarType.bool))
val string = LiteralType(Set(ScalarType.string))
case class LiteralType private (oneOf: Set[ScalarType], name: String) extends Type {
override def toString: String = name
}
case class ArrayType(element: Type) extends DataType
case class ProductType(name: String, fields: NonEmptyMap[String, Type]) extends DataType
object LiteralType {
val float = LiteralType(ScalarType.float, "float")
val signed = LiteralType(ScalarType.signed, "signed")
val number = LiteralType(ScalarType.number, "number")
val bool = LiteralType(Set(ScalarType.bool), "bool")
val string = LiteralType(Set(ScalarType.string), "string")
}
case class ArrayType(element: Type) extends DataType {
override def toString: String = "[]" + element
}
case class ProductType(name: String, fields: NonEmptyMap[String, Type]) extends DataType {
override def toString: String =
s"$name{${fields.map(_.toString).toNel.toList.map(kv => kv._1 + ": " + kv._2).mkString(", ")}}"
}
sealed trait CallableType extends Type {
def acceptsAsArguments(valueTypes: List[Type]): Boolean
@ -70,16 +81,7 @@ case class ArrowType(args: List[Type], res: Option[Type]) extends CallableType {
override def acceptsAsArguments(valueTypes: List[Type]): Boolean =
(args.length == valueTypes.length) && args.zip(valueTypes).forall(av => av._1.acceptsValueOf(av._2))
}
case class FuncArrowType(funcArgs: List[(String, Type)], res: Option[Type]) extends CallableType {
lazy val toArrowType: ArrowType = ArrowType(funcArgs.map(_._2), res)
override def acceptsAsArguments(valueTypes: List[Type]): Boolean =
toArrowType.acceptsAsArguments(valueTypes)
override def args: List[Type] = toArrowType.args
override def toString: String = args.map(_.toString).mkString(", ") + " -> " + res.map(_.toString).getOrElse("()")
}
object Type {
@ -119,10 +121,10 @@ object Type {
else
(l, r) match {
case (x: ScalarType, y: ScalarType) => ScalarType.scalarOrder.partialCompare(x, y)
case (LiteralType(xs), y: ScalarType) if xs == Set(y) => 0.0
case (LiteralType(xs), y: ScalarType) if xs(y) => -1.0
case (x: ScalarType, LiteralType(ys)) if ys == Set(x) => 0.0
case (x: ScalarType, LiteralType(ys)) if ys(x) => 1.0
case (LiteralType(xs, _), y: ScalarType) if xs == Set(y) => 0.0
case (LiteralType(xs, _), y: ScalarType) if xs(y) => -1.0
case (x: ScalarType, LiteralType(ys, _)) if ys == Set(x) => 0.0
case (x: ScalarType, LiteralType(ys, _)) if ys(x) => 1.0
case (x: ArrayType, y: ArrayType) => cmp(x.element, y.element)
case (ProductType(_, xFields), ProductType(_, yFields)) =>
cmpProd(xFields, yFields)

View File

@ -1,10 +1,8 @@
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,
@ -16,9 +14,8 @@ import aqua.parser.lexer.{
TypeToken
}
import cats.data.Validated.{Invalid, Valid}
import cats.data.{NonEmptyList, NonEmptyMap, State, Validated, ValidatedNel}
import cats.free.Free
import cats.{~>, Comonad}
import cats.data.{NonEmptyList, NonEmptyMap, State, ValidatedNel}
import cats.~>
import monocle.Lens
import cats.syntax.functor._
import cats.syntax.flatMap._

View File

@ -37,8 +37,10 @@ case class FuncExpr[F[_]](name: Name[F], args: List[Arg[F]], ret: Option[DataTyp
// Resolve arg type, remember it
f.flatMap(acc =>
T.resolveType(argType).flatMap {
case Some(t) => N.define(argName, t).as(acc.enqueue(t))
case None => Free.pure(acc)
case Some(t) =>
N.define(argName, t).as(acc.enqueue(t))
case None =>
Free.pure(acc)
}
)
}
@ -50,7 +52,9 @@ case class FuncExpr[F[_]](name: Name[F], args: List[Arg[F]], ret: Option[DataTyp
.map(argsAndRes => ArrowType(argsAndRes._1, argsAndRes._2)),
(funcArrow: ArrowType, bodyGen: Gen) =>
// Erase arguments and internal variables
A.endScope() >> N.endScope() >> N.define(name, funcArrow) as Gen("Function defined, wrap + " + bodyGen)
A.endScope() >> N.endScope() >> N.define(name, funcArrow, isRoot = true) as Gen(
"Function defined, wrap + " + bodyGen
)
)
}