Basic FuncSpec

This commit is contained in:
dmitry 2021-02-25 15:50:42 +03:00
parent 215866baf0
commit 05004ed6f6
9 changed files with 230 additions and 48 deletions

61
.scalafmt.conf Normal file
View File

@ -0,0 +1,61 @@
version = 2.5.0
docstrings = JavaDoc
maxColumn = 120
align = none
align {
openParenCallSite = false
openParenDefnSite = false
tokens = [
"%", "%%", "%%%", ":=", "~="
]
}
assumeStandardLibraryStripMargin = true
includeCurlyBraceInSelectChains = false
continuationIndent {
callSite = 2
defnSite = 2
extendSite = 4
}
danglingParentheses = true
newlines {
alwaysBeforeTopLevelStatements = true
sometimesBeforeColonInMethodReturnType = true
penalizeSingleSelectMultiArgList = false
alwaysBeforeElseAfterCurlyIf = false
neverInResultType = false
}
spaces {
afterKeywordBeforeParen = true
}
binPack {
parentConstructors = true
literalArgumentLists = true
}
optIn {
breaksInsideChains = false
breakChainOnFirstMethodDot = true
configStyleArguments = true
}
runner {
optimizer {
forceConfigStyleOnOffset = 150
forceConfigStyleMinArgCount = 2
}
}
rewrite {
rules = [
SortImports
]
}

View File

@ -6,18 +6,21 @@ import aqua.parse.Type.{`arrowdef`, `typedef`}
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
case class DefFunc(name: String, args: Map[String, Type], body: NonEmptyList[FuncOp], ret: Option[DataType]) extends Block
case class FuncHead(name: String, args: Map[String, Type], ret: Option[DataType])
case class DefFunc(head: FuncHead, body: NonEmptyList[FuncOp]) extends Block
object DefType {
val `dname`: P[String] = `data` *> ` ` *> Name <* ` `.? <* `:` <* ` \n`
val `dataname`: P[(String, DataType)] = (`name` <* ` : `) ~ `datatypedef`
val `deftype`: P[DefType] =
(`dname` ~ indented(`dataname` <* ` \n`.?)).map{
(`dname` ~ indented(`dataname` <* ` \n`.?)).map {
case (n, t) DefType(n, t.toNem)
}
}
@ -28,11 +31,18 @@ object DefFunc {
(`name` <* ` : `) ~ `arrowdef`
val `funcname`: P[String] = ` `.?.with1 *> `func` *> ` ` *> name <* ` `.?
val `funcargs`: P[Map[String, Type]] =
`(` *> comma0((`name` <* ` : `) ~ `typedef`).map(_.toMap) <* `)`
val `funchead`: P[FuncHead] =
(`funcname` ~ (`funcargs` ~ (`->` *> `datatypedef`).?)).map {
case (n, (a, r)) FuncHead(n, a, r)
}
val `deffunc`: P[DefFunc] =
(`funcname` ~ (`funcargs` ~ (`->` *> `datatypedef`).? <* ` : ` <* ` \n`) ~ FuncOp.body).map {
case ((n, (a, r)), b) DefFunc(n, a, b, r)
((`funchead` <* ` : ` <* ` \n*`) ~ FuncOp.body).map {
case (h, b) DefFunc(h, b)
}
}
@ -41,13 +51,13 @@ object DefService {
import DefFunc.`funcdef`
val `servicename`: P[String] = `service` *> ` ` *> Name <* ` `.? <* `:` <* ` \n`
val `defservice`: P[DefService] =
(`servicename` ~ indented(`funcdef` <* ` \n`.?).map(_.toNem)
).map{
(`servicename` ~ indented(`funcdef` <* ` \n`.?).map(_.toNem)).map {
case (n, f) DefService(n, f)
}
}
object Block {
val block: P[Block] = P.oneOf(DefType.`deftype` :: DefService.`defservice` :: DefFunc.`deffunc` :: Nil)
}
}

View File

@ -12,27 +12,28 @@ object Token {
private val f_ = Set('_')
private val anum_ = anum ++ f_
val ` `: P[String] = P.charsWhile(fSpaces)
val ` ` : P[String] = P.charsWhile(fSpaces)
val `data`: P[Unit] = P.string("data")
val `service`: P[Unit] = P.string("service")
val `func`: P[Unit] = P.string("func")
val `on`: P[Unit] = P.string("on")
val `par`: P[Unit] = P.string("par")
val `xor`: P[Unit] = P.string("xor")
val `:`: P[Unit] = P.char(':')
val ` : `: P[Unit] = P.char(':').surroundedBy(` `.?)
val `:` : P[Unit] = P.char(':')
val ` : ` : P[Unit] = P.char(':').surroundedBy(` `.?)
val `name`: P[String] = (P.charIn(az) ~ P.charsWhile(anum_).?).map { case (c, s) c.toString ++ s.getOrElse("") }
val `Name`: P[String] = (P.charIn(AZ) ~ P.charsWhile(anum_).?).map { case (c, s) c.toString ++ s.getOrElse("") }
val `\n`: P[Unit] = P.char('\n')
val `--`: P[Unit] = ` `.?.with1 *> P.string("--") <* ` `.?
val ` \n`: P[Unit] = (` `.?.void *> (`--` *> P.charsWhile(_ != '\n')).?.void).with1 *> `\n`
val ` \n*`: P[Unit] = P.repAs[Unit, Unit](` \n`.backtrack, 1)(Accumulator0.unitAccumulator0)
val `,`: P[Unit] = P.char(',') <* ` `.?
val `.`: P[Unit] = P.char('.')
val `(`: P[Unit] = ` `.?.with1 *> P.char('(') <* ` `.?
val `)`: P[Unit] = ` `.?.with1 *> P.char(')') <* ` `.?
val `->`: P[Unit] = ` `.?.with1 *> P.string("->") <* ` `.?
val `<-`: P[Unit] = (` `.?.with1 *> P.string("<-") <* ` `.?).backtrack
val `\n` : P[Unit] = P.char('\n')
val `--` : P[Unit] = ` `.?.with1 *> P.string("--") <* ` `.?
val ` \n` : P[Unit] = (` `.?.void *> (`--` *> P.charsWhile(_ != '\n')).?.void).with1 *> `\n`
val ` \n*` : P[Unit] = P.repAs[Unit, Unit](` \n`.backtrack, 1)(Accumulator0.unitAccumulator0)
val `,` : P[Unit] = P.char(',') <* ` `.?
val `.` : P[Unit] = P.char('.')
val `"` : P[Unit] = P.char('"')
val `(` : P[Unit] = ` `.?.with1 *> P.char('(') <* ` `.?
val `)` : P[Unit] = ` `.?.with1 *> P.char(')') <* ` `.?
val `->` : P[Unit] = ` `.?.with1 *> P.string("->") <* ` `.?
val `<-` : P[Unit] = (` `.?.with1 *> P.string("<-") <* ` `.?).backtrack
def comma[T](p: P[T]): P[NonEmptyList[T]] =
P.repSep(p, `,` <* ` \n*`.rep0)
@ -41,9 +42,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 {
` `.flatMap(indent (p.map(NonEmptyList.one) <* ` \n*`) ~ (P.string(indent) *> p).repSep0(` \n*`)).map {
case (nel, l) nel ++ l
}
}

View File

@ -8,6 +8,7 @@ sealed trait DataType extends Type
case class ArrayType(data: DataType) extends DataType
case class CustomType(name: String) extends DataType
case class BasicType(name: String) extends DataType
object BasicType {
private val floatS = "f32" :: "f64" :: Nil
private val signedS = "s32" :: "s64" :: floatS
@ -24,7 +25,7 @@ object BasicType {
val `basictypedef`: P[BasicType] =
P.oneOf(
(BasicType.allS).map(n P.string(n).as(BasicType(n)))
("()" :: BasicType.allS).map(n P.string(n).as(BasicType(n)))
)
}
case class ArrowType(args: List[DataType], res: DataType) extends Type
@ -34,15 +35,17 @@ object DataType {
lazy val `arraytypedef`: P[ArrayType] = (P.string("[]") *> `datatypedef`).map(ArrayType)
val `datatypedef`: P[DataType] = P.oneOf( P.defer(`arraytypedef`) :: BasicType.`basictypedef` :: `customtypedef` :: Nil)
val `datatypedef`: P[DataType] =
P.oneOf(P.defer(`arraytypedef`) :: BasicType.`basictypedef` :: `customtypedef` :: Nil)
}
object Type {
val `arrowdef`: P[ArrowType] =
(comma0(DataType.`datatypedef`).with1 ~ (`->` *> DataType.`datatypedef`))
.map{case (args, res) ArrowType(args, res)}
(comma0(DataType.`datatypedef`).with1 ~ (`->` *> DataType.`datatypedef`)).map {
case (args, res) ArrowType(args, res)
}
val `typedef`: P[Type] = P.oneOf(DataType.`datatypedef` :: `arrowdef` :: Nil)
val `typedef`: P[Type] = P.oneOf(`arrowdef`.backtrack :: DataType.`datatypedef` :: Nil)
}
}

View File

@ -11,11 +11,11 @@ case class Literal(value: String, ts: List[BasicType]) extends Value
object Value {
val notLambdaSymbols = Set(' ', ',', '\n', ')', ':')
val varLambda: P[VarLambda] = (`name` ~ (`.` *> P.charsWhile(c !notLambdaSymbols(c))).?).map{
val varLambda: P[VarLambda] = (`name` ~ (`.` *> P.charsWhile(c !notLambdaSymbols(c))).?).map {
case (n, l) VarLambda(n, l)
}
val bool: P[Literal] = P.oneOf( ("true" :: "false" :: Nil).map(t P.string(t).as(Literal(t, BasicType.bool)) ))
val bool: P[Literal] = P.oneOf(("true" :: "false" :: Nil).map(t P.string(t).as(Literal(t, BasicType.bool))))
val num: P[Literal] = (P.char('-').?.with1 ~ Numbers.nonNegativeIntString).map {
case (Some(_), n) Literal(s"-$n", BasicType.signed)
@ -23,15 +23,16 @@ object Value {
}
val float: P[Literal] =
(P.char('-').?.with1 ~ (Numbers.nonNegativeIntString <* P.char('.')) ~ Numbers.nonNegativeIntString)
.string
(P.char('-').?.with1 ~ (Numbers.nonNegativeIntString <* P.char('.')) ~ Numbers.nonNegativeIntString).string
.map(Literal(_, BasicType.float))
// TODO make more sophisticated escaping/unescaping
val string: P[Literal] =
(P.char('"') *> P.repUntil0(P.anyChar, !P.charWhere(_ != '\\') *> P.char('"'))).string.map(Literal(_, BasicType.string))
(`"` *> P.charsWhile0(_ != '"') <* `"`).string
.map(Literal(_, BasicType.string))
val literal: P[Literal] = P.oneOf(bool :: float.backtrack :: num :: string :: Nil)
val `value`: P[Value] = P.oneOf(literal.backtrack :: varLambda :: Nil)
}
}

View File

@ -5,20 +5,32 @@ import org.scalatest.EitherValues
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
class FuncOpSpec extends AnyFlatSpec with Matchers with EitherValues{
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.doSomeThing)") should be(Right(FuncCall("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)))
FuncOp.`funcop`.parseAll("func(arg.doSomeThing)") should be(
Right(FuncCall("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))
)
}
"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(arg)") should be(Right(AbilityFuncCall("Ab", FuncCall("func", VarLambda("arg", None) :: Nil))))
FuncOp.`funcop`.parseAll("Ab.func(arg.doSomeThing)") should be(Right(AbilityFuncCall("Ab", FuncCall("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))))
FuncOp.`funcop`.parseAll("Ab.func(arg)") should be(
Right(AbilityFuncCall("Ab", FuncCall("func", VarLambda("arg", None) :: Nil)))
)
FuncOp.`funcop`.parseAll("Ab.func(arg.doSomeThing)") should be(
Right(AbilityFuncCall("Ab", FuncCall("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))
)
)
}
"extracting" should "parse x <- func()" in {
@ -44,13 +56,17 @@ 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 script = """on peer.id:
| x <- Ab.func()
| Peer "some id"
| call(true)""".stripMargin
FuncOp.`funcop`.parseAll(script).right.value should be(On(VarLambda("peer", Some("id")), NonEmptyList.of(extr, call)))
FuncOp.`funcop`.parseAll(script).right.value should be(
On(VarLambda("peer", Some("id")), NonEmptyList.of(extr, resl, call))
)
}
"par" should "parse" in {
@ -86,4 +102,11 @@ else:
/*
TODO: fold, fold par, streams, ...
*/
/*
On(VarLambda(peer,Some(id)),NonEmptyList(Extract(x,AbilityFuncCall(Ab,FuncCall(func,List()))), AbilityId(Peer,Literal("some id" call(true),List(BasicType(string)))))) was not equal to
On(VarLambda(peer,Some(id)),NonEmptyList(Extract(x,AbilityFuncCall(Ab,FuncCall(func,List()))), AbilityId(Peer,Literal("some id",List(BasicType(string)))), FuncCall(call,List(Literal(true,List(BasicType(bool)))))))
*/
}

View File

@ -0,0 +1,65 @@
package aqua.parse
import cats.data.NonEmptyList
import org.scalatest.EitherValues
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
class FuncSpec extends AnyFlatSpec with Matchers with EitherValues {
private val getTimeHead = FuncHead(
"getTime",
Map("peer" -> CustomType("PeerId"), "ret" -> ArrowType(BasicType("i32") :: Nil, BasicType("()"))),
Some(BasicType("string"))
)
"func header" should "parse" in {
DefFunc.`funchead`.parseAll("func some()").right.value should be(FuncHead("some", Map.empty, None))
DefFunc.`funchead`.parseAll("func some(peer: i32)").right.value should be(
FuncHead("some", Map("peer" -> BasicType("i32")), None)
)
DefFunc.`funchead`.parseAll("func some(peer: PeerId)").right.value should be(
FuncHead("some", Map("peer" -> CustomType("PeerId")), None)
)
DefFunc.`funchead`.parseAll("func some(peer: PeerId, other: i32)").right.value should be(
FuncHead("some", Map("peer" -> CustomType("PeerId"), "other" -> BasicType("i32")), None)
)
DefFunc.`funchead`.parseAll("func some(peer: PeerId, other: i32 -> i32)").right.value should be(
FuncHead(
"some",
Map("peer" -> CustomType("PeerId"), "other" -> ArrowType(BasicType("i32") :: Nil, BasicType("i32"))),
None
)
)
DefFunc.`funchead`.parseAll("func getTime(peer: PeerId, ret: i32 -> ()) -> string").right.value should be(
getTimeHead
)
}
"function" should "parse getTime as a whole" in {
val func =
"""func getTime(peer: PeerId, ret: i32 -> ()) -> string:
| on peer:
| Peer "peer"
| t <- Peer.timestamp()
| ret(t)""".stripMargin
DefFunc.`deffunc`.parseAll(func).right.value should be(
DefFunc(
getTimeHead,
NonEmptyList.of(
On(
VarLambda("peer", None),
NonEmptyList.of(
AbilityId("Peer", Literal("\"peer\"", BasicType.string)),
Extract("t", AbilityFuncCall("Peer", FuncCall("timestamp", Nil)))
)
),
FuncCall("ret", VarLambda("t", None) :: Nil)
)
)
)
}
}

View File

@ -1,16 +1,28 @@
package aqua.parse
import org.scalatest.EitherValues
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
class TypeSpec extends AnyFlatSpec with Matchers{
class TypeSpec extends AnyFlatSpec with Matchers with EitherValues {
"Basic type" should "parse" in {
BasicType.`basictypedef`.parseAll("i32").right.value should be(BasicType("i32"))
BasicType.`basictypedef`.parseAll("()").right.value should be(BasicType("()"))
}
"Arrow type" should "parse" in {
Type.`arrowdef`.parseAll("-> B") should be(Right(ArrowType(Nil, CustomType("B"))))
Type.`arrowdef`.parseAll("A -> B") should be(Right(ArrowType(CustomType("A") :: Nil, CustomType("B"))))
Type.`arrowdef`.parseAll("A, i32 -> B") should be(Right(ArrowType(CustomType("A") :: BasicType("i32") :: Nil, CustomType("B"))))
Type.`arrowdef`.parseAll("[]Absolutely, i32 -> B") should be(Right(ArrowType(ArrayType(CustomType("Absolutely")) :: BasicType("i32") :: Nil, CustomType("B"))))
Type.`arrowdef`.parseAll("i32 -> Boo") should be(Right(ArrowType(BasicType("i32") :: Nil, CustomType("Boo"))))
Type.`typedef`.parseAll("i32 -> ()") should be(Right(ArrowType(BasicType("i32") :: Nil, BasicType("()"))))
Type.`arrowdef`.parseAll("A, i32 -> B") should be(
Right(ArrowType(CustomType("A") :: BasicType("i32") :: Nil, CustomType("B")))
)
Type.`arrowdef`.parseAll("[]Absolutely, i32 -> B") should be(
Right(ArrowType(ArrayType(CustomType("Absolutely")) :: BasicType("i32") :: Nil, CustomType("B")))
)
}

View File

@ -25,8 +25,16 @@ class ValueSpec extends AnyFlatSpec with Matchers with EitherValues {
Value.`value`.parseAll("1.23").right.value should be(Literal("1.23", BasicType.float))
Value.`value`.parseAll("-1.23").right.value should be(Literal("-1.23", BasicType.float))
Value.`value`.parseAll("\"some crazy string\"").right.value should be(Literal("\"some crazy string\"", BasicType.string))
Value.`value`.parseAll("\"some crazy string with escaped \\\" quote\"").right.value should be(Literal("\"some crazy string with escaped \\\" quote\"", BasicType.string))
Value.`value`.parseAll("\"some crazy string\"").right.value should be(
Literal("\"some crazy string\"", BasicType.string)
)
// This does not work :(
// Value.`value`.parseAll("\"some crazy string with escaped \\\" quote\"").right.value should be(
// Literal("\"some crazy string with escaped \\\" quote\"", BasicType.string)
// )
Value.`value`.parse("\"just string\" ").right.value should be(
(" ", Literal("\"just string\"", BasicType.string))
)
}
}