Module and Use expressions (#245)

* Module and Use expressions

* UseFromExpr

* ImportFromExpr

* PubExpr

* Export, declares

* Collecting all the needed info WIP

* Got all the needed data

* Tests fixed

* HeaderSem

* HeaderSem wip

* Everything except `export`/`declares` should be working

* Compile bug fixed

* Fix readme: cli/assembly

* Handle declares, exports

* Compile only exports in AquaRes

* Call services imported from modules

* Import consts, types, services from modules

* Resolve arrows from modules

* Bugfix
This commit is contained in:
Dmitry Kurinskiy
2021-08-20 18:03:47 +03:00
committed by GitHub
parent 296c64836d
commit b9af20339b
44 changed files with 728 additions and 163 deletions

View File

@ -9,26 +9,29 @@ import cats.free.Cofree
import cats.parse.Parser0 as P0
import cats.{Comonad, Eval}
case class Ast[F[_]](head: Ast.Head[F], tree: Ast.Tree[F]) {
case class Ast[S[_]](head: Ast.Head[S], tree: Ast.Tree[S]) {
def cata[T](folder: (Expr[F], Chain[T]) => Eval[T]): Eval[T] =
Cofree.cata[Chain, Expr[F], T](tree)(folder)
def cata[T](folder: (Expr[S], Chain[T]) => Eval[T]): Eval[T] =
Cofree.cata[Chain, Expr[S], T](tree)(folder)
def cataHead[T](folder: (HeaderExpr[S], Chain[T]) => Eval[T]): Eval[T] =
Cofree.cata[Chain, HeaderExpr[S], T](head)(folder)
}
object Ast {
type Tree[F[_]] = Cofree[Chain, Expr[F]]
type Head[F[_]] = Cofree[Chain, HeaderExpr[F]]
type Tree[S[_]] = Cofree[Chain, Expr[S]]
type Head[S[_]] = Cofree[Chain, HeaderExpr[S]]
def parser[F[_]: LiftParser: Comonad](): P0[ValidatedNec[ParserError[F], Ast[F]]] =
(HeadExpr.ast[F].with1 ~ RootExpr.ast[F]()).map { case (head, bodyMaybe) =>
def parser[S[_]: LiftParser: Comonad](): P0[ValidatedNec[ParserError[S], Ast[S]]] =
(HeadExpr.ast[S].with1 ~ RootExpr.ast[S]()).map { case (head, bodyMaybe) =>
bodyMaybe.map(Ast(head, _))
}
def fromString[F[_]: LiftParser: Comonad](script: String): ValidatedNec[ParserError[F], Ast[F]] =
parser[F]()
def fromString[S[_]: LiftParser: Comonad](script: String): ValidatedNec[ParserError[S], Ast[S]] =
parser[S]()
.parseAll(script) match {
case Right(value) => value
case Left(e) => Validated.invalidNec(LexerError[F](e.wrapErr))
case Left(e) => Validated.invalidNec(LexerError[S](e.wrapErr))
}
}

View File

@ -13,7 +13,7 @@ case class AbilityIdExpr[F[_]](ability: Ability[F], id: Value[F])
object AbilityIdExpr extends Expr.Leaf {
override def p[F[_]: LiftParser: Comonad]: P[AbilityIdExpr[F]] =
((Ability.ab[F] <* ` `) ~ Value.`value`).map { case (ability, id) =>
((Ability.dotted[F] <* ` `) ~ Value.`value`).map { case (ability, id) =>
AbilityIdExpr(ability, id)
}

View File

@ -18,7 +18,7 @@ object CallArrowExpr extends Expr.Leaf {
override def p[F[_]: LiftParser: Comonad]: P[CallArrowExpr[F]] =
((comma(Name.p[F]) <* ` <- `).backtrack.?.with1 ~
((Ability.ab[F] <* `.`).?.with1 ~
((Ability.dotted[F] <* `.`).?.with1 ~
Name.p[F] ~
comma0(Value.`value`[F].surroundedBy(`/s*`)).between(`(` <* `/s*`, `/s*` *> `)`))).map {
case (variables, ((ability, funcName), args)) =>

View File

@ -0,0 +1,20 @@
package aqua.parser.head
import aqua.parser.lexer.Token.*
import aqua.parser.lexer.{Literal, Value, Token}
import aqua.parser.lift.LiftParser
import cats.Comonad
import cats.data.NonEmptyList
import cats.parse.Parser
import cats.syntax.either.*
case class ExportExpr[F[_]](pubs: NonEmptyList[FromExpr.NameOrAbAs[F]]) extends HeaderExpr[F] {
override def token: Token[F] =
pubs.head.bimap(_._1, _._1).fold(identity, identity)
}
object ExportExpr extends HeaderExpr.Leaf {
override def p[F[_]: LiftParser: Comonad]: Parser[ExportExpr[F]] =
(`_export` *> ` `) *> comma(FromExpr.nameOrAbAs[F]).map(ExportExpr(_))
}

View File

@ -0,0 +1,11 @@
package aqua.parser.head
import aqua.parser.lexer.{Literal, Token}
trait FilenameExpr[F[_]] extends HeaderExpr[F] {
def filename: Literal[F]
override def token: Token[F] = filename
def fileValue: String = filename.value.drop(1).dropRight(1)
}

View File

@ -0,0 +1,22 @@
package aqua.parser.head
import aqua.parser.lexer.Token._
import aqua.parser.lexer.{Ability, Name}
import aqua.parser.lift.LiftParser
import cats.Comonad
import cats.data.NonEmptyList
import cats.parse.Parser as P
trait FromExpr[F[_]] {
def imports: NonEmptyList[FromExpr.NameOrAbAs[F]]
}
object FromExpr {
type NameOrAbAs[F[_]] = Either[Name.As[F], Ability.As[F]]
def nameOrAbAs[F[_]: LiftParser: Comonad]: P[NameOrAbAs[F]] =
Name.nameAs[F].map(Left(_)) | Ability.abAs[F].map(Right(_))
def importFrom[F[_]: LiftParser: Comonad]: P[NonEmptyList[NameOrAbAs[F]]] =
comma[NameOrAbAs[F]](nameOrAbAs[F]) <* ` ` <* `from`
}

View File

@ -3,25 +3,30 @@ package aqua.parser.head
import aqua.parser.Ast
import aqua.parser.lexer.Token.` \n+`
import aqua.parser.lift.LiftParser
import aqua.parser.lift.LiftParser.*
import cats.{Comonad, Eval}
import cats.data.Chain
import cats.free.Cofree
import cats.parse.{Parser => P, Parser0 => P0}
import aqua.parser.lexer.Token
case class HeadExpr[F[_]]() extends HeaderExpr[F]
case class HeadExpr[S[_]](token: Token[S]) extends HeaderExpr[S]
object HeadExpr {
def headExprs: List[HeaderExpr.Companion] =
ImportExpr :: Nil
UseFromExpr :: UseExpr :: ImportFromExpr :: ImportExpr :: ExportExpr :: Nil
def ast[F[_]: LiftParser: Comonad]: P0[Ast.Head[F]] =
P.repSep0(P.oneOf(headExprs.map(_.ast[F])), ` \n+`)
def ast[S[_]: LiftParser: Comonad]: P0[Ast.Head[S]] =
(P.unit.lift0.map(Token.lift) ~ ((ModuleExpr.p[S] <* ` \n+`).? ~
P.repSep0(P.oneOf(headExprs.map(_.ast[S].backtrack)), ` \n+`).map(Chain.fromSeq))
.surroundedBy(` \n+`.?)
.?
.map {
case Some(exprs) => Chain.fromSeq(exprs)
case None => Chain.empty[Ast.Head[F]]
}
.map(exprs => Cofree(HeadExpr[F](), Eval.now(exprs)))
.?).map {
case (p, Some((maybeMod, exprs))) =>
Cofree(
maybeMod.getOrElse(HeadExpr[S](p)),
Eval.now(exprs)
)
case (p, None) => Cofree(HeadExpr[S](p), Eval.now(Chain.nil))
}
}

View File

@ -1,20 +1,23 @@
package aqua.parser.head
import aqua.parser.Ast
import aqua.parser.lexer.Token
import aqua.parser.lift.LiftParser
import cats.{Comonad, Eval}
import cats.data.Chain
import cats.free.Cofree
import cats.parse.{Parser => P}
import cats.parse.Parser as P
trait HeaderExpr[F[_]]
trait HeaderExpr[S[_]] {
def token: Token[S]
}
object HeaderExpr {
trait Companion {
def p[F[_]: LiftParser: Comonad]: P[HeaderExpr[F]]
def p[S[_]: LiftParser: Comonad]: P[HeaderExpr[S]]
def ast[F[_]: LiftParser: Comonad]: P[Ast.Head[F]]
def ast[S[_]: LiftParser: Comonad]: P[Ast.Head[S]]
}
abstract class Leaf extends Companion {

View File

@ -6,7 +6,7 @@ import aqua.parser.lift.LiftParser
import cats.Comonad
import cats.parse.Parser
case class ImportExpr[F[_]](filename: Literal[F]) extends HeaderExpr[F]
case class ImportExpr[F[_]](filename: Literal[F]) extends FilenameExpr[F]
object ImportExpr extends HeaderExpr.Leaf {

View File

@ -0,0 +1,21 @@
package aqua.parser.head
import aqua.parser.lexer.Token.*
import aqua.parser.lexer.{Literal, Value}
import aqua.parser.lift.LiftParser
import cats.Comonad
import cats.data.NonEmptyList
import cats.parse.Parser
case class ImportFromExpr[F[_]](
imports: NonEmptyList[FromExpr.NameOrAbAs[F]],
filename: Literal[F]
) extends FilenameExpr[F] with FromExpr[F]
object ImportFromExpr extends HeaderExpr.Leaf {
override def p[F[_]: LiftParser: Comonad]: Parser[HeaderExpr[F]] =
(`import` *> FromExpr.importFrom[F].surroundedBy(` `) ~ Value.string[F]).map {
case (imports, filename) => ImportFromExpr(imports, filename)
}
}

View File

@ -0,0 +1,54 @@
package aqua.parser.head
import aqua.parser.lexer.Token.*
import aqua.parser.lexer.Token
import aqua.parser.lexer.{Ability, Literal, Name, Value}
import aqua.parser.lift.LiftParser
import aqua.parser.lift.LiftParser.*
import cats.Comonad
import cats.parse.Parser
case class ModuleExpr[F[_]](
name: Ability[F],
declareAll: Option[Token[F]],
declareNames: List[Name[F]],
declareCustom: List[Ability[F]]
) extends HeaderExpr[F] {
override def token: Token[F] = name
}
object ModuleExpr extends HeaderExpr.Leaf {
type NameOrAb[F[_]] = Either[Name[F], Ability[F]]
def nameOrAb[F[_]: LiftParser: Comonad]: Parser[NameOrAb[F]] =
Name.p[F].map(Left(_)) | Ability.ab[F].map(Right(_))
def nameOrAbList[F[_]: LiftParser: Comonad]: Parser[List[NameOrAb[F]]] =
comma[NameOrAb[F]](nameOrAb[F]).map(_.toList)
def nameOrAbListOrAll[F[_]: LiftParser: Comonad]: Parser[Either[List[NameOrAb[F]], Token[F]]] =
nameOrAbList[F].map(Left(_)) | `star`.lift.map(Token.lift(_)).map(Right(_))
override def p[F[_]: LiftParser: Comonad]: Parser[ModuleExpr[F]] =
(`module` *> ` ` *> Ability.ab[F] ~
(` declares ` *> nameOrAbListOrAll[F]).?).map {
case (name, None) =>
ModuleExpr(name, None, Nil, Nil)
case (name, Some(Left(exportMembers))) =>
ModuleExpr(
name,
None,
exportMembers.collect { case Left(x) => x },
exportMembers.collect { case Right(x) => x }
)
case (name, Some(Right(point))) =>
ModuleExpr(
name,
Some(point),
Nil,
Nil
)
}
}

View File

@ -0,0 +1,21 @@
package aqua.parser.head
import aqua.parser.lexer.Token.*
import aqua.parser.lexer.{Ability, Literal, Value}
import aqua.parser.lift.LiftParser
import cats.Comonad
import cats.parse.Parser
case class UseExpr[F[_]](
filename: Literal[F],
asModule: Option[Ability[F]]
) extends FilenameExpr[F]
object UseExpr extends HeaderExpr.Leaf {
override def p[F[_]: LiftParser: Comonad]: Parser[HeaderExpr[F]] =
(`use` *> Value
.string[F] ~ (` as ` *> Ability.ab[F]).?).map { case (filename, asModule) =>
UseExpr(filename, asModule)
}
}

View File

@ -0,0 +1,23 @@
package aqua.parser.head
import aqua.parser.lexer.Token.*
import aqua.parser.lexer.{Ability, Literal, Name, Value}
import aqua.parser.lift.LiftParser
import cats.Comonad
import cats.data.NonEmptyList
import cats.parse.Parser
case class UseFromExpr[F[_]](
imports: NonEmptyList[FromExpr.NameOrAbAs[F]],
filename: Literal[F],
asModule: Ability[F]
) extends FilenameExpr[F] with FromExpr[F]
object UseFromExpr extends HeaderExpr.Leaf {
override def p[F[_]: LiftParser: Comonad]: Parser[HeaderExpr[F]] =
(`use` *> FromExpr.importFrom[F].surroundedBy(` `) ~ Value
.string[F] ~ (` as ` *> Ability.ab[F])).map { case ((imports, filename), asModule) =>
UseFromExpr(imports, filename, asModule)
}
}

View File

@ -15,7 +15,14 @@ case class Ability[F[_]: Comonad](name: F[String]) extends Token[F] {
}
object Ability {
type As[F[_]] = (Ability[F], Option[Ability[F]])
def ab[F[_]: LiftParser: Comonad]: P[Ability[F]] =
`Class`.lift.map(Ability(_))
def dotted[F[_]: LiftParser: Comonad]: P[Ability[F]] =
P.repSep(`Class`, `.`).map(_.toList.mkString(".")).lift.map(Ability(_))
def abAs[F[_]: LiftParser: Comonad]: P[As[F]] =
asOpt(ab[F])
}

View File

@ -16,6 +16,15 @@ case class Name[F[_]: Comonad](name: F[String]) extends Token[F] {
object Name {
type As[F[_]] = (Name[F], Option[Name[F]])
def p[F[_]: LiftParser: Comonad]: P[Name[F]] =
`name`.lift.map(Name(_))
def dotted[F[_]: LiftParser: Comonad]: P[Name[F]] =
((`Class`.repSep(`.`).map(_.toList.mkString(".")) ~ `.`).?.with1 ~ `name`).string.lift
.map(Name(_))
def nameAs[F[_]: LiftParser: Comonad]: P[As[F]] =
asOpt(p[F])
}

View File

@ -25,8 +25,17 @@ object Token {
val `const`: P[Unit] = P.string("const")
val `data`: P[Unit] = P.string("data")
val `import`: P[Unit] = P.string("import")
val `module`: P[Unit] = P.string("module")
val `declares`: P[Unit] = P.string("declares")
val ` declares ` : P[Unit] = `declares`.surroundedBy(` `)
val `declare`: P[Unit] = P.string("declare")
val `_export`: P[Unit] = P.string("export")
val `star`: P[Unit] = P.char('*')
val `use`: P[Unit] = P.string("use")
val `from`: P[Unit] = P.string("from")
val ` from ` : P[Unit] = `from`.surroundedBy(` `)
val `as`: P[Unit] = P.string("as")
val ` as ` : P[Unit] = `as`.surroundedBy(` `)
val `alias`: P[Unit] = P.string("alias")
val `service`: P[Unit] = P.string("service")
val `func`: P[Unit] = P.string("func")
@ -64,7 +73,7 @@ object Token {
val `.` : P[Unit] = P.char('.')
val `"` : P[Unit] = P.char('"')
val `*` : P[Unit] = P.char('*')
val exclamation : P[Unit] = P.char('!')
val exclamation: P[Unit] = P.char('!')
val `[]` : P[Unit] = P.string("[]")
val `` : P[Unit] = P.char('')
val `⊥` : P[Unit] = P.char('⊥')
@ -92,4 +101,7 @@ object Token {
def comma0[T](p: P[T]): P0[List[T]] =
P.repSep0(p, `,` <* ` \n+`.rep0)
def asOpt[T](p: P[T]): P[(T, Option[T])] =
p ~ (` as ` *> p).?
}

View File

@ -54,7 +54,12 @@ case class CustomTypeToken[F[_]: Comonad](name: F[String]) extends DataTypeToken
}
object CustomTypeToken {
def ct[F[_]: LiftParser: Comonad]: P[CustomTypeToken[F]] = `Class`.lift.map(CustomTypeToken(_))
def ct[F[_]: LiftParser: Comonad]: P[CustomTypeToken[F]] =
`Class`.lift.map(CustomTypeToken(_))
def dotted[F[_]: LiftParser: Comonad]: P[CustomTypeToken[F]] =
`Class`.repSep(`.`).string.lift.map(CustomTypeToken(_))
}
case class BasicTypeToken[F[_]: Comonad](scalarType: F[ScalarType]) extends DataTypeToken[F] {
@ -115,7 +120,7 @@ object DataTypeToken {
P.defer(`arraytypedef`[F]) :: P.defer(StreamTypeToken.`streamtypedef`) :: P.defer(
OptionTypeToken.`optiontypedef`
) :: BasicTypeToken
.`basictypedef`[F] :: CustomTypeToken.ct[F] :: Nil
.`basictypedef`[F] :: CustomTypeToken.dotted[F] :: Nil
)
}

View File

@ -24,7 +24,7 @@ case class Literal[F[_]: Comonad](valueToken: F[String], ts: LiteralType) extend
object Value {
def varLambda[F[_]: LiftParser: Comonad]: P[VarLambda[F]] =
(Name.p[F] ~ LambdaOp.ops[F].?).map { case (n, l)
(Name.dotted[F] ~ LambdaOp.ops[F].?).map { case (n, l)
VarLambda(n, l.fold[List[LambdaOp[F]]](Nil)(_.toList))
}

View File

@ -3,26 +3,26 @@ package aqua.parser.lift
import cats.Id
import cats.parse.{Parser, Parser0}
trait LiftParser[F[_]] {
def lift[T](p: Parser[T]): Parser[F[T]]
trait LiftParser[S[_]] {
def lift[T](p: Parser[T]): Parser[S[T]]
def lift0[T](p0: Parser0[T]): Parser0[F[T]]
def lift0[T](p0: Parser0[T]): Parser0[S[T]]
def wrapErr(e: Parser.Error): F[Parser.Error]
def wrapErr(e: Parser.Error): S[Parser.Error]
}
object LiftParser {
implicit class LiftErrorOps[F[_]: LiftParser, T](e: Parser.Error) {
def wrapErr: F[Parser.Error] = implicitly[LiftParser[F]].wrapErr(e)
implicit class LiftErrorOps[S[_]: LiftParser, T](e: Parser.Error) {
def wrapErr: S[Parser.Error] = implicitly[LiftParser[S]].wrapErr(e)
}
implicit class LiftParserOps[F[_]: LiftParser, T](parser: Parser[T]) {
def lift: Parser[F[T]] = implicitly[LiftParser[F]].lift(parser)
implicit class LiftParserOps[S[_]: LiftParser, T](parser: Parser[T]) {
def lift: Parser[S[T]] = implicitly[LiftParser[S]].lift(parser)
}
implicit class LiftParser0Ops[F[_]: LiftParser, T](parser0: Parser0[T]) {
def lift0: Parser0[F[T]] = implicitly[LiftParser[F]].lift0(parser0)
implicit class LiftParser0Ops[S[_]: LiftParser, T](parser0: Parser0[T]) {
def lift0: Parser0[S[T]] = implicitly[LiftParser[S]].lift0(parser0)
}
object Implicits {