Added type argument to parsers

This commit is contained in:
dmitry 2021-02-25 16:57:27 +03:00
parent bde8017d31
commit 37f99eb15e
12 changed files with 169 additions and 94 deletions

View File

@ -1,8 +1,10 @@
package aqua
import aqua.parse.{ArrowType, Block, DataType, Type}
import cats.Id
import cats.data.NonEmptyMap
import cats.parse.{Parser P, Parser0 P0}
import aqua.parse.lift.LiftParser.Implicits.idLiftParser
case class Aqua(
inputs: Map[String, DataType],
@ -12,7 +14,7 @@ case class Aqua(
)
object Aqua {
import aqua.parse.Token._
import aqua.parse.lexer.Token._
val `parser`: P0[List[Block]] = P.repSep0(Block.`block`, ` \n*`)
val `parser`: P0[List[Block[Id]]] = P.repSep0(Block.`block`[Id], ` \n*`)
}

View File

@ -1,25 +1,28 @@
package aqua.parse
import aqua.parse.DataType.`datatypedef`
import aqua.parse.Token._
import aqua.parse.lexer.Token._
import aqua.parse.Type.{`arrowdef`, `typedef`}
import aqua.parse.lift.LiftParser
import aqua.parse.lift.LiftParser._
import cats.Functor
import cats.data.{NonEmptyList, NonEmptyMap}
import cats.parse.{Parser P}
sealed trait Block
case class DefType(name: String, fields: NonEmptyMap[String, DataType]) extends Block
case class DefService(name: String, funcs: NonEmptyMap[String, ArrowType]) extends Block
sealed trait Block[F[_]]
case class DefType[F[_]](name: F[String], fields: NonEmptyMap[String, DataType]) extends Block[F]
case class DefService[F[_]](name: F[String], funcs: NonEmptyMap[String, ArrowType]) extends Block[F]
case class FuncHead(name: String, args: Map[String, Type], ret: Option[DataType])
case class FuncHead[F[_]](name: F[String], args: Map[String, Type], ret: Option[DataType])
case class DefFunc(head: FuncHead, body: NonEmptyList[FuncOp]) extends Block
case class DefFunc[F[_]](head: FuncHead[F], body: NonEmptyList[F[FuncOp[F]]]) extends Block[F]
object DefType {
val `dname`: P[String] = `data` *> ` ` *> Name <* ` `.? <* `:` <* ` \n*`
def `dname`[F[_]: LiftParser]: P[F[String]] = `data` *> ` ` *> Name.lift <* ` `.? <* `:` <* ` \n*`
val `dataname`: P[(String, DataType)] = (`name` <* ` : `) ~ `datatypedef`
val `deftype`: P[DefType] =
def `deftype`[F[_]: LiftParser]: P[DefType[F]] =
(`dname` ~ indented(`dataname`)).map {
case (n, t) DefType(n, t.toNem)
}
@ -30,17 +33,17 @@ object DefFunc {
val `funcdef`: P[(String, ArrowType)] =
(`name` <* ` : `) ~ `arrowdef`
val `funcname`: P[String] = ` `.?.with1 *> `func` *> ` ` *> name <* ` `.?
def `funcname`[F[_]: LiftParser]: P[F[String]] = ` `.?.with1 *> `func` *> ` ` *> name.lift <* ` `.?
val `funcargs`: P[Map[String, Type]] =
`(` *> comma0((`name` <* ` : `) ~ `typedef`).map(_.toMap) <* `)`
val `funchead`: P[FuncHead] =
def `funchead`[F[_]: LiftParser]: P[FuncHead[F]] =
(`funcname` ~ (`funcargs` ~ (`->` *> `datatypedef`).?)).map {
case (n, (a, r)) FuncHead(n, a, r)
}
val `deffunc`: P[DefFunc] =
def `deffunc`[F[_]: LiftParser: Functor]: P[DefFunc[F]] =
((`funchead` <* ` : ` <* ` \n*`) ~ FuncOp.body).map {
case (h, b) DefFunc(h, b)
}
@ -50,9 +53,9 @@ object DefFunc {
object DefService {
import DefFunc.`funcdef`
val `servicename`: P[String] = `service` *> ` ` *> Name <* ` `.? <* `:` <* ` \n*`
def `servicename`[F[_]: LiftParser]: P[F[String]] = `service` *> ` ` *> Name.lift <* ` `.? <* `:` <* ` \n*`
val `defservice`: P[DefService] =
def `defservice`[F[_]: LiftParser]: P[DefService[F]] =
(`servicename` ~ indented(`funcdef`).map(_.toNem)).map {
case (n, f) DefService(n, f)
}
@ -60,6 +63,6 @@ object DefService {
object Block {
val block: P[Block] =
def block[F[_]: LiftParser: Functor]: P[Block[F]] =
` \n*`.rep0.with1 *> P.oneOf(DefType.`deftype` :: DefService.`defservice` :: DefFunc.`deffunc` :: Nil)
}

View File

@ -1,67 +1,84 @@
package aqua.parse
import aqua.parse.Token._
import aqua.parse.lexer.Token._
import aqua.parse.lexer.Value
import cats.data.NonEmptyList
import cats.parse.{Parser P}
import Value.`value`
import aqua.parse.lexer.Value.`value`
import aqua.parse.lift.LiftParser
import aqua.parse.lift.LiftParser._
import cats.Functor
import cats.syntax.functor._
sealed trait FuncOp
sealed trait InstrOp extends FuncOp
sealed trait FuncOp[F[_]]
sealed trait InstrOp[F[_]] extends FuncOp[F]
sealed trait ExecOp extends InstrOp
sealed trait CallOp extends ExecOp
sealed trait ExecOp[F[_]] extends InstrOp[F]
sealed trait CallOp[F[_]] extends ExecOp[F]
case class FuncCall(name: String, args: List[Value]) extends CallOp
case class AbilityFuncCall(ability: String, call: FuncCall) extends CallOp
case class Extract(v: String, from: CallOp) extends ExecOp
case class FuncCall[F[_]](name: F[String], args: List[F[Value]]) extends CallOp[F]
case class AbilityFuncCall[F[_]](ability: F[String], call: F[FuncCall[F]]) extends CallOp[F]
case class Extract[F[_]](v: F[String], from: F[CallOp[F]]) extends ExecOp[F]
case class On(peer: Value, ops: NonEmptyList[ExecOp]) extends InstrOp
case class On[F[_]](peer: F[Value], ops: NonEmptyList[F[ExecOp[F]]]) extends InstrOp[F]
case class Par(op: InstrOp) extends FuncOp
case class Par[F[_]](op: F[InstrOp[F]]) extends FuncOp[F]
// TODO: can't be in Par, can be in On
sealed trait AbilityResolve extends ExecOp
case class AbilityId(ability: String, id: Value) extends AbilityResolve
sealed trait AbilityResolve[F[_]] extends ExecOp[F]
case class AbilityId[F[_]](ability: F[String], id: F[Value]) extends AbilityResolve[F]
object FuncOp {
val funcCall: P[FuncCall] =
(`name` ~ P.repSep0(`value`, `,`).between(`(`, `)`)).map{
def funcCall[F[_]: LiftParser]: P[F[FuncCall[F]]] =
(`name`.lift ~ P.repSep0(`value`.lift, `,`).between(`(`, `)`)).map {
case (fnName, args) FuncCall(fnName, args)
}
}.lift
val abilityFuncCall: P[AbilityFuncCall] =
((`Name` <* `.`) ~ funcCall).map{
def abilityFuncCall[F[_]: LiftParser]: P[F[AbilityFuncCall[F]]] =
((`Name`.lift <* `.`) ~ funcCall).map {
case (abName, fc) AbilityFuncCall(abName, fc)
}
}.lift
val callOp: P[CallOp] = P.oneOf(funcCall :: abilityFuncCall :: Nil)
def callOp[F[_]: LiftParser: Functor]: P[F[CallOp[F]]] =
P.oneOf(funcCall[F].map(_.widen[CallOp[F]]) :: abilityFuncCall[F].map(_.widen[CallOp[F]]) :: Nil)
val extract: P[Extract] = ((`name` <* `<-`) ~ callOp).map{
case (v, f) Extract(v, f)
}
def extract[F[_]: LiftParser: Functor]: P[F[Extract[F]]] =
((`name`.lift <* `<-`) ~ callOp[F]).map {
case (v, f) Extract(v, f)
}.lift
val abilityResolve: P[AbilityResolve] = ((`Name` <* ` `) ~ `value`).map{
case (n, v) AbilityId(n, v)
}
def abilityResolve[F[_]: LiftParser: Functor]: P[F[AbilityResolve[F]]] =
((`Name`.lift <* ` `) ~ `value`.lift).map {
case (n, v) AbilityId(n, v)
}.widen[AbilityResolve[F]].lift
// TODO can't be in Par, can be in On
val execOp: P[ExecOp] = P.oneOf( callOp.backtrack :: abilityResolve.backtrack :: extract :: Nil)
def execOp[F[_]: LiftParser: Functor]: P[F[ExecOp[F]]] =
P.oneOf(
callOp.map(_.widen[ExecOp[F]]).backtrack
:: abilityResolve.map(_.widen[ExecOp[F]]).backtrack
:: extract.map(_.widen[ExecOp[F]]) :: Nil
)
val startOn: P[Value] = `on` *> ` ` *> `value` <* ` `.? <* `:` <* ` \n*`
def startOn[F[_]: LiftParser]: P[F[Value]] = `on` *> ` ` *> `value`.lift <* ` `.? <* `:` <* ` \n*`
val execOn: P[On] =
(startOn ~ indented(execOp)).map{
def execOn[F[_]: LiftParser: Functor]: P[F[On[F]]] =
(startOn ~ indented(execOp[F])).map {
case (v, i) On(v, i)
}
}.lift
val instrOp: P[InstrOp] = P.oneOf( execOn.backtrack :: execOp :: Nil)
def instrOp[F[_]: LiftParser: Functor]: P[F[InstrOp[F]]] =
P.oneOf(
execOn.map(_.widen[InstrOp[F]]).backtrack
:: execOp.map(_.widen[InstrOp[F]]) :: Nil
)
val parOp: P[Par] =
`par` *> ` ` *> instrOp.map(Par)
def parOp[F[_]: LiftParser: Functor]: P[F[Par[F]]] =
(`par` *> ` ` *> instrOp[F].map(Par(_))).lift
val `funcop`: P[FuncOp] = P.oneOf( parOp.backtrack :: instrOp :: Nil)
def `funcop`[F[_]: LiftParser: Functor]: P[F[FuncOp[F]]] =
P.oneOf(parOp.map(_.widen[FuncOp[F]]).backtrack :: instrOp.map(_.widen[FuncOp[F]]) :: Nil)
val body: P[NonEmptyList[FuncOp]] = indented(`funcop`)
def body[F[_]: LiftParser: Functor]: P[NonEmptyList[F[FuncOp[F]]]] = indented(`funcop`)
}

View File

@ -1,6 +1,6 @@
package aqua.parse
import aqua.parse.Token._
import aqua.parse.lexer.Token._
import cats.parse.{Parser P}
sealed trait Type

View File

@ -1,4 +1,4 @@
package aqua.parse
package aqua.parse.lexer
import cats.data.NonEmptyList
import cats.parse.{Accumulator0, Parser P, Parser0 P0}

View File

@ -1,8 +1,8 @@
package aqua.parse
package aqua.parse.lexer
import aqua.parse.Token._
import cats.parse.{Parser P}
import cats.parse.Numbers
import aqua.parse.BasicType
import aqua.parse.lexer.Token._
import cats.parse.{Numbers, Parser P}
sealed trait Value
case class VarLambda(name: String, lambda: Option[String]) extends Value

View File

@ -0,0 +1,23 @@
package aqua.parse.lift
import cats.Id
import cats.parse.Parser
trait LiftParser[F[_]] {
def lift[T](p: Parser[T]): Parser[F[T]]
}
object LiftParser {
implicit class LiftParserOps[F[_]: LiftParser, T](parser: Parser[T]) {
def lift: Parser[F[T]] = implicitly[LiftParser[F]].lift(parser)
}
object Implicits {
implicit object idLiftParser extends LiftParser[Id] {
override def lift[T](p: Parser[T]): Parser[Id[T]] = p
}
}
}

View File

@ -0,0 +1,19 @@
package aqua.parse.lift
import cats.parse.{Parser P}
import scala.language.implicitConversions
case class Span[T](startIndex: Int, endIndex: Int, value: T)
object Span {
implicit object spanLiftParser extends LiftParser[Span] {
override def lift[T](p: P[T]): P[Span[T]] =
(P.index.with1 ~ p ~ P.index).map {
case ((s, v), e) Span(s, e, v)
}
}
}

View File

@ -1,51 +1,59 @@
package aqua.parse
import aqua.parse.lexer.{Literal, VarLambda}
import cats.data.NonEmptyList
import org.scalatest.EitherValues
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import aqua.parse.lift.LiftParser.Implicits.idLiftParser
import cats.Id
class FuncOpSpec extends AnyFlatSpec with Matchers with EitherValues {
"func calls" should "parse func()" in {
FuncOp.`funcop`.parseAll("func()") should be(Right(FuncCall("func", Nil)))
FuncOp.`funcop`.parseAll("func(arg)") should be(Right(FuncCall("func", VarLambda("arg", None) :: Nil)))
FuncOp.`funcop`.parseAll("func(arg)") should be(Right(FuncCall[Id]("func", VarLambda("arg", None) :: Nil)))
FuncOp.`funcop`.parseAll("func(arg.doSomeThing)") should be(
Right(FuncCall("func", VarLambda("arg", Some("doSomeThing")) :: Nil))
Right(FuncCall[Id]("func", VarLambda("arg", Some("doSomeThing")) :: Nil))
)
FuncOp.`funcop`.parseAll("func(arg.doSomeThing, arg2)") should be(
Right(FuncCall("func", VarLambda("arg", Some("doSomeThing")) :: VarLambda("arg2", None) :: Nil))
Right(FuncCall[Id]("func", VarLambda("arg", Some("doSomeThing")) :: VarLambda("arg2", None) :: Nil))
)
}
"ability calls" should "parse Ab.func()" in {
FuncOp.`funcop`.parseAll("Ab.func()") should be(Right(AbilityFuncCall("Ab", FuncCall("func", Nil))))
FuncOp.`funcop`.parseAll("Ab.func()") should be(Right(AbilityFuncCall[Id]("Ab", FuncCall[Id]("func", Nil))))
FuncOp.`funcop`.parseAll("Ab.func(arg)") should be(
Right(AbilityFuncCall("Ab", FuncCall("func", VarLambda("arg", None) :: Nil)))
Right(AbilityFuncCall[Id]("Ab", FuncCall[Id]("func", VarLambda("arg", None) :: Nil)))
)
FuncOp.`funcop`.parseAll("Ab.func(arg.doSomeThing)") should be(
Right(AbilityFuncCall("Ab", FuncCall("func", VarLambda("arg", Some("doSomeThing")) :: Nil)))
Right(AbilityFuncCall[Id]("Ab", FuncCall[Id]("func", VarLambda("arg", Some("doSomeThing")) :: Nil)))
)
FuncOp.`funcop`.parseAll("Ab.func(arg.doSomeThing, arg2)") should be(
Right(
AbilityFuncCall("Ab", FuncCall("func", VarLambda("arg", Some("doSomeThing")) :: VarLambda("arg2", None) :: Nil))
AbilityFuncCall[Id](
"Ab",
FuncCall[Id]("func", VarLambda("arg", Some("doSomeThing")) :: VarLambda("arg2", None) :: Nil)
)
)
)
}
"extracting" should "parse x <- func()" in {
FuncOp.`funcop`.parseAll("someThing <- func()") should be(Right(Extract("someThing", FuncCall("func", Nil))))
FuncOp.`funcop`.parseAll("someThing <- func()") should be(
Right(Extract[Id]("someThing", FuncCall[Id]("func", Nil)))
)
}
"extracting" should "parse x <- Ab.func()" in {
val fCall = AbilityFuncCall("Ab", FuncCall("func", Nil))
FuncOp.`funcop`.parseAll("x <- Ab.func()") should be(Right(Extract("x", fCall)))
val fCall = AbilityFuncCall[Id]("Ab", FuncCall[Id]("func", Nil))
FuncOp.`funcop`.parseAll("x <- Ab.func()") should be(Right(Extract[Id]("x", fCall)))
}
// TODO test with literals
"ability resolve" should "parse id getter" in {
FuncOp.`funcop`.parseAll("Ab x") should be(Right(AbilityId("Ab", VarLambda("x", None))))
FuncOp.`funcop`.parseAll("Ab x.id") should be(Right(AbilityId("Ab", VarLambda("x", Some("id")))))
FuncOp.`funcop`.parseAll("Ab x") should be(Right(AbilityId[Id]("Ab", VarLambda("x", None))))
FuncOp.`funcop`.parseAll("Ab x.id") should be(Right(AbilityId[Id]("Ab", VarLambda("x", Some("id")))))
}
"on" should "parse startOn" in {
@ -54,10 +62,10 @@ class FuncOpSpec extends AnyFlatSpec with Matchers with EitherValues {
}
"on" should "parse on x: y" in {
val fCall = AbilityFuncCall("Ab", FuncCall("func", Nil))
val extr = Extract("x", fCall)
val resl = AbilityId("Peer", Literal("\"some id\"", BasicType.string))
val call = FuncCall("call", Literal("true", BasicType.bool) :: Nil)
val fCall = AbilityFuncCall[Id]("Ab", FuncCall[Id]("func", Nil))
val extr = Extract[Id]("x", fCall)
val resl = AbilityId[Id]("Peer", Literal("\"some id\"", BasicType.string))
val call = FuncCall[Id]("call", Literal("true", BasicType.bool) :: Nil)
val script = """on peer.id:
| x <- Ab.func()
@ -65,12 +73,12 @@ class FuncOpSpec extends AnyFlatSpec with Matchers with EitherValues {
| call(true)""".stripMargin
FuncOp.`funcop`.parseAll(script).right.value should be(
On(VarLambda("peer", Some("id")), NonEmptyList.of(extr, resl, call))
On[Id](VarLambda("peer", Some("id")), NonEmptyList.of(extr, resl, call))
)
}
"par" should "parse" in {
FuncOp.`funcop`.parseAll("par func()") should be(Right(Par(FuncCall("func", Nil))))
FuncOp.`funcop`.parseAll("par func()") should be(Right(Par[Id](FuncCall[Id]("func", Nil))))
val script = """par on peer.id:
|

View File

@ -1,13 +1,16 @@
package aqua.parse
import aqua.parse.lexer.{Literal, VarLambda}
import cats.data.NonEmptyList
import org.scalatest.EitherValues
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import aqua.parse.lift.LiftParser.Implicits.idLiftParser
import cats.Id
class FuncSpec extends AnyFlatSpec with Matchers with EitherValues {
private val getTimeHead = FuncHead(
private val getTimeHead = FuncHead[Id](
"getTime",
Map("peer" -> CustomType("PeerId"), "ret" -> ArrowType(BasicType("i32") :: Nil, BasicType("()"))),
Some(BasicType("string"))
@ -47,17 +50,17 @@ class FuncSpec extends AnyFlatSpec with Matchers with EitherValues {
| ret(t)""".stripMargin
DefFunc.`deffunc`.parseAll(func).right.value should be(
DefFunc(
DefFunc[Id](
getTimeHead,
NonEmptyList.of(
On(
On[Id](
VarLambda("peer", None),
NonEmptyList.of(
AbilityId("Peer", Literal("\"peer\"", BasicType.string)),
Extract("t", AbilityFuncCall("Peer", FuncCall("timestamp", Nil)))
AbilityId[Id]("Peer", Literal("\"peer\"", BasicType.string)),
Extract[Id]("t", AbilityFuncCall[Id]("Peer", FuncCall[Id]("timestamp", Nil)))
)
),
FuncCall("ret", VarLambda("t", None) :: Nil)
FuncCall[Id]("ret", VarLambda("t", None) :: Nil)
)
)
)

View File

@ -1,11 +1,11 @@
package aqua.parse
package aqua.parse.lexer
import aqua.parse.lexer.Token._
import org.scalatest.EitherValues
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import Token._
class TokenSpec extends AnyFlatSpec with Matchers with EitherValues{
class TokenSpec extends AnyFlatSpec with Matchers with EitherValues {
"\\n token" should "be parsed" in {
` \n`.parseAll("\n") should be('right)
@ -36,13 +36,12 @@ class TokenSpec extends AnyFlatSpec with Matchers with EitherValues{
}
"\\n* token" should "match multi-line comments" in {
` \n*`.parseAll(
""" -- comment line 1
|-- line 2
|
| -- line 3
| -- line 4
|""".stripMargin).right.value should be(())
` \n*`.parseAll(""" -- comment line 1
|-- line 2
|
| -- line 3
| -- line 4
|""".stripMargin).right.value should be(())
}
}

View File

@ -1,5 +1,6 @@
package aqua.parse
package aqua.parse.lexer
import aqua.parse.BasicType
import org.scalatest.EitherValues
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers