Basic Names impl

This commit is contained in:
dmitry 2021-02-26 17:14:14 +03:00
parent d2676c8b14
commit 9cf2db90d8
9 changed files with 173 additions and 12 deletions

View File

@ -15,5 +15,5 @@ case class Aqua(
object Aqua {
import aqua.parser.lexer.Token._
val `parser`: P0[List[Block[Span]]] = P.repSep0(Block.`block`[Span], ` \n*`)
val `parser`: P0[List[Block[Span.F]]] = P.repSep0(Block.`block`[Span.F], ` \n*`)
}

View File

@ -0,0 +1,22 @@
package aqua.model
import aqua.parser.DataType
import aqua.parser.lexer.Value
trait Instr
// Fully resolved Scope must have no expected abilities (all resolved)
case class Scope(
// None means "inherit"
peerId: Option[Value],
// Take vars, set vars
importData: Map[String, DataType],
exportData: Map[String, DataType],
// Abilities can be imported or set
expectedAbilities: Set[String],
resolvedAbilities: Set[String],
// We don't know the types yet
expectArrows: Set[String],
// resolved subtrees, or unresolved shit
body: List[Either[Scope, Instr]]
)

View File

@ -44,6 +44,7 @@ object DefFunc {
case (n, (a, r)) FuncHead(n, a, r)
}
// TODO: if funchead has return type, for the last op, add extract, add Return.reply(extracted)
def `deffunc`[F[_]: LiftParser: Functor]: P[DefFunc[F]] =
((`funchead` <* ` : ` <* ` \n*`) ~ FuncOp.body).map {
case (h, b) DefFunc(h, b)

View File

@ -43,7 +43,7 @@ object Token {
P.repSep0(p, `,` <* ` \n*`.rep0)
def indented[T](p: P[T]): P[NonEmptyList[T]] =
` `.flatMap(indent (p.map(NonEmptyList.one) <* ` \n*`) ~ (P.string(indent) *> p).repSep0(` \n*`)).map {
case (nel, l) nel ++ l
` `.flatMap(indent p.map(NonEmptyList.one) ~ (` \n*` *> (P.string(indent) *> p).repSep0(` \n*`)).?).map {
case (nel, l) nel ++ l.getOrElse(Nil)
}
}

View File

@ -0,0 +1,89 @@
package aqua.parser.lift
import aqua.parser.{AbilityFuncCall, AbilityId, DataType, ExecOp, Extract, FuncCall, FuncOp, On, Par}
import aqua.parser.lexer.{Value, VarLambda}
import cats.Comonad
import cats.data.NonEmptyList
import cats.syntax.comonad._
import cats.syntax.functor._
// TODO: add comonad to show where import/export is declared
// Fully resolved Scope must have no expected abilities (all resolved)
case class Names(
// None means "inherit"
peerId: Option[Value] = None,
// Take vars, set vars
// Data type is not yet known
importData: Set[String] = Set.empty,
exportData: Set[String] = Set.empty,
// Abilities can be imported or set
expectedAbilities: Set[String] = Set.empty,
resolvedAbilities: Set[String] = Set.empty,
// We don't know the types yet
expectArrows: Set[String] = Set.empty,
// Set when know
mode: Names.Mode.Value = Names.Mode.Seq
)
object Names {
object Mode extends Enumeration {
val Seq, Par, Xor = Value;
}
def funcOps[G[_]: Comonad](ops: NonEmptyList[G[FuncOp[G]]]): Names =
ops.toList.map(_.extract).map(funcOp[G]).foldLeft(Names()) {
case (acc, n) if n.mode == Mode.Seq =>
acc.copy(
exportData = acc.exportData ++ n.exportData,
importData = acc.importData ++ (n.importData -- acc.exportData),
resolvedAbilities =
if (n.peerId == acc.peerId) acc.resolvedAbilities ++ n.resolvedAbilities else acc.resolvedAbilities,
expectedAbilities = acc.expectedAbilities ++ (n.expectedAbilities -- acc.resolvedAbilities),
expectArrows = acc.expectArrows ++ n.expectArrows
)
case (acc, n) if n.mode == Mode.Par =>
acc.copy(
exportData = acc.exportData ++ n.exportData,
importData = acc.importData ++ n.importData,
expectedAbilities = acc.expectedAbilities ++ n.expectedAbilities,
expectArrows = acc.expectArrows ++ n.expectArrows
)
case (acc, n) if n.mode == Mode.Xor =>
acc.copy(
importData = acc.importData ++ n.importData,
expectedAbilities = acc.expectedAbilities ++ n.expectedAbilities,
expectArrows = acc.expectArrows ++ n.expectArrows
)
}
def funcOp[G[_]: Comonad](op: FuncOp[G]): Names =
op match {
case FuncCall(fname, fargs) =>
Names(
expectArrows = Set(fname.extract),
importData = fargs
.collect(_.extract match {
case VarLambda(name, _) => name
})
.toSet
)
case AbilityFuncCall(ab, fc) =>
val funcNames = funcOp(fc.extract)
funcNames.copy(expectedAbilities = Set(ab.extract))
case Extract(n, fn) =>
val funcNames = funcOp(fn.extract)
funcNames.copy(exportData = Set(n.extract))
case AbilityId(ab, _) =>
Names(resolvedAbilities = Set(ab.extract))
case On(p, ops) =>
val ns = funcOps(ops.map(_.widen[FuncOp[G]])).copy(peerId = Some(p.extract))
p.extract match {
case VarLambda(name, _) =>
ns.copy(importData = ns.importData + name)
case _ => ns
}
case Par(op) =>
funcOp(op.extract).copy(mode = Mode.Par)
}
}

View File

@ -2,27 +2,27 @@ package aqua.parser.lift
import cats.Comonad
import cats.parse.{Parser P}
import cats.free.Free
import scala.language.implicitConversions
case class Span[T](startIndex: Int, endIndex: Int, value: T)
case class Span(startIndex: Int, endIndex: Int)
object Span {
type F[T] = (Span, T)
implicit object spanComonad extends Comonad[Span] {
override def extract[A](x: Span[A]): A = x.value
implicit object spanComonad extends Comonad[F] {
override def extract[A](x: F[A]): A = x._2
override def coflatMap[A, B](fa: Span[A])(f: Span[A] B): Span[B] = fa.copy(value = f(fa))
override def coflatMap[A, B](fa: F[A])(f: F[A] B): F[B] = fa.copy(_2 = f(fa))
override def map[A, B](fa: Span[A])(f: A B): Span[B] = fa.copy(value = f(fa.value))
override def map[A, B](fa: F[A])(f: A B): F[B] = fa.copy(_2 = f(fa._2))
}
implicit object spanLiftParser extends LiftParser[Span] {
implicit object spanLiftParser extends LiftParser[F] {
override def lift[T](p: P[T]): P[Span[T]] =
override def lift[T](p: P[T]): P[F[T]] =
(P.index.with1 ~ p ~ P.index).map {
case ((s, v), e) Span(s, e, v)
case ((s, v), e) (Span(s, e), v)
}
}

View File

@ -41,6 +41,21 @@ class FuncSpec extends AnyFlatSpec with Matchers with EitherValues {
)
}
"function" should "parse single line fn" in {
val func =
"""func getTime(peer: PeerId, ret: i32 -> ()) -> string:
| ret(43)""".stripMargin
DefFunc.`deffunc`.parseAll(func).right.value should be(
DefFunc[Id](
getTimeHead,
NonEmptyList.of(
FuncCall[Id]("ret", Literal("43", BasicType.number) :: Nil)
)
)
)
}
"function" should "parse getTime as a whole" in {
val func =
"""func getTime(peer: PeerId, ret: i32 -> ()) -> string:

View File

@ -0,0 +1,28 @@
package aqua.parser
import aqua.parser.lift.Names
import cats.Id
import cats.data.NonEmptyList
import org.scalatest.EitherValues
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
class NamesSpec extends AnyFlatSpec with Matchers with EitherValues {
private val namesId = Names.funcOps[Id](_)
private val funcOpsP = (v: String) => FuncOp.body[Id].parseAll(v).right.value
private val namesP = (v: String) => namesId(funcOpsP(v))
"names" should "extract from funcops" in {
namesP(" func()") should be(Names(expectArrows = Set("func")))
namesP(" fn(32)") should be(Names(expectArrows = Set("fn")))
namesP(" fn(s)") should be(Names(expectArrows = Set("fn"), importData = Set("s")))
namesP(" x <- fn(s)") should be(Names(expectArrows = Set("fn"), importData = Set("s"), exportData = Set("x")))
namesP(" x <- fn(s)\n y <- fn(z)") should be(
Names(expectArrows = Set("fn"), importData = Set("s", "z"), exportData = Set("x", "y"))
)
namesP(" x <- fn(s)\n y <- fn(x)") should be(
Names(expectArrows = Set("fn"), importData = Set("s"), exportData = Set("x", "y"))
)
}
}

View File

@ -1,6 +1,7 @@
package aqua.parser.lexer
import aqua.parser.lexer.Token._
import cats.data.NonEmptyList
import org.scalatest.EitherValues
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
@ -44,4 +45,9 @@ class TokenSpec extends AnyFlatSpec with Matchers with EitherValues {
|""".stripMargin).right.value should be(())
}
"indented" should "parse 1 or more lines" in {
indented(`.`).parseAll(" .\n .").right.value should be(NonEmptyList.of((), ()))
indented(`.`).parseAll(" .").right.value should be(NonEmptyList.of(()))
}
}