Skip to content

Commit

Permalink
Performance improvement: single parser instance (#254)
Browse files Browse the repository at this point in the history
  • Loading branch information
DieMyst committed Aug 27, 2021
1 parent 7e0fe47 commit dae234d
Show file tree
Hide file tree
Showing 49 changed files with 362 additions and 83 deletions.
4 changes: 2 additions & 2 deletions build.sbt
Expand Up @@ -85,8 +85,8 @@ lazy val parser = crossProject(JVMPlatform, JSPlatform)
.settings(commons: _*)
.settings(
libraryDependencies ++= Seq(
"org.typelevel" %%% "cats-parse" % catsParseV,
"org.typelevel" %%% "cats-free" % catsV
"org.typelevel" %%% "cats-parse" % catsParseV,
"org.typelevel" %%% "cats-free" % catsV
)
)
.dependsOn(types)
Expand Down
24 changes: 22 additions & 2 deletions cli/src/main/scala/aqua/AquaPathCompiler.scala
Expand Up @@ -5,13 +5,20 @@ import aqua.compiler.{AquaCompiler, AquaError}
import aqua.files.{AquaFileSources, FileModuleId}
import aqua.io.*
import aqua.model.transform.TransformConfig
import aqua.parser.{Ast, LexerError}
import aqua.parser.lift.FileSpan
import cats.data.*
import cats.syntax.functor.*
import cats.syntax.show.*
import cats.{Monad, Show}
import cats.{~>, Eval, Monad, Show}
import scribe.Logging
import fs2.io.file.{Files, Path}
import aqua.parser.lift.{LiftParser, Span}
import cats.parse.LocationMap
import cats.~>
import aqua.parser.lift.LiftParser.LiftErrorOps
import Span.spanLiftParser
import aqua.parser.Parser

object AquaPathCompiler extends Logging {

Expand All @@ -27,7 +34,20 @@ object AquaPathCompiler extends Logging {
AquaCompiler
.compileTo[F, AquaFileError, FileModuleId, FileSpan.F, String](
sources,
(fmid, src) => FileSpan.fileSpanLiftParser(fmid.file.toString, src),
id => {
source => {
val nat = new (Span.F ~> FileSpan.F) {
override def apply[A](span: Span.F[A]): FileSpan.F[A] = {
(
FileSpan(id.file.fileName.toString, Eval.later(LocationMap(source)), span._1),
span._2
)
}
}
import Span.spanLiftParser
Parser.natParser(Parser.spanParser, nat)(source)
}
},
backend,
bodyConfig,
sources.write(targetPath)
Expand Down
14 changes: 8 additions & 6 deletions compiler/src/main/scala/aqua/compiler/AquaCompiler.scala
Expand Up @@ -5,25 +5,27 @@ import aqua.linker.Linker
import aqua.model.AquaContext
import aqua.model.transform.TransformConfig
import aqua.model.transform.res.AquaRes
import aqua.parser.lift.LiftParser
import aqua.parser.Ast
import aqua.parser.lift.{LiftParser, Span}
import aqua.parser.{Ast, ParserError}
import aqua.semantics.Semantics
import aqua.semantics.header.HeaderSem
import cats.data.Validated.{validNec, Invalid, Valid}
import cats.data.{Chain, NonEmptyChain, NonEmptyMap, Validated, ValidatedNec}
import cats.parse.Parser0
import cats.syntax.applicative.*
import cats.syntax.flatMap.*
import cats.syntax.functor.*
import cats.syntax.traverse.*
import cats.syntax.monoid.*
import cats.{Comonad, Monad, Monoid, Order}
import scribe.Logging
import cats.~>

object AquaCompiler extends Logging {

def compile[F[_]: Monad, E, I: Order, S[_]: Comonad](
sources: AquaSources[F, E, I],
liftI: (I, String) => LiftParser[S],
parser: I => String => ValidatedNec[ParserError[S], Ast[S]],
backend: Backend,
config: TransformConfig
): F[ValidatedNec[AquaError[I, E, S], Chain[AquaCompiled[I]]]] = {
Expand All @@ -32,7 +34,7 @@ object AquaCompiler extends Logging {
type Ctx = NonEmptyMap[I, AquaContext]
type ValidatedCtx = ValidatedNec[Err, Ctx]

new AquaParser[F, E, I, S](sources, liftI)
new AquaParser[F, E, I, S](sources, parser)
.resolve[ValidatedCtx](mod =>
context =>
// Context with prepared imports
Expand Down Expand Up @@ -95,12 +97,12 @@ object AquaCompiler extends Logging {

def compileTo[F[_]: Monad, E, I: Order, S[_]: Comonad, T](
sources: AquaSources[F, E, I],
liftI: (I, String) => LiftParser[S],
parser: I => String => ValidatedNec[ParserError[S], Ast[S]],
backend: Backend,
config: TransformConfig,
write: AquaCompiled[I] => F[Seq[Validated[E, T]]]
): F[ValidatedNec[AquaError[I, E, S], Chain[T]]] =
compile[F, E, I, S](sources, liftI, backend, config).flatMap {
compile[F, E, I, S](sources, parser, backend, config).flatMap {
case Valid(compiled) =>
compiled.map { ac =>
write(ac).map(
Expand Down
15 changes: 8 additions & 7 deletions compiler/src/main/scala/aqua/compiler/AquaParser.scala
@@ -1,21 +1,23 @@
package aqua.compiler

import aqua.linker.{AquaModule, Modules}
import aqua.parser.Ast
import aqua.parser.{Ast, ParserError}
import aqua.parser.head.{FilenameExpr, ImportExpr}
import aqua.parser.lift.LiftParser
import aqua.parser.lift.{LiftParser, Span}
import cats.data.{Chain, NonEmptyChain, Validated, ValidatedNec}
import cats.parse.Parser0
import cats.syntax.applicative.*
import cats.syntax.flatMap.*
import cats.syntax.functor.*
import cats.syntax.traverse.*
import cats.{Comonad, Monad}
import scribe.Logging
import cats.~>

// TODO: add tests
class AquaParser[F[_]: Monad, E, I, S[_]: Comonad](
sources: AquaSources[F, E, I],
liftI: (I, String) => LiftParser[S]
parser: I => String => ValidatedNec[ParserError[S], Ast[S]]
) extends Logging {

type Body = Ast[S]
Expand All @@ -26,8 +28,8 @@ class AquaParser[F[_]: Monad, E, I, S[_]: Comonad](
sources.sources
.map(
_.leftMap(_.map[Err](SourcesErr(_))).andThen(_.map { case (i, s) =>
implicit val lift: LiftParser[S] = liftI(i, s)
Ast.fromString[S](s).bimap(_.map[Err](ParserErr(_)), ast => Chain.one(i -> ast))
parser(i)(s)
.bimap(_.map[Err](ParserErr(_)), ast => Chain.one(i -> ast))
}.foldLeft(Validated.validNec[Err, Chain[(I, Body)]](Chain.nil))(_ combine _))
)

Expand Down Expand Up @@ -84,8 +86,7 @@ class AquaParser[F[_]: Monad, E, I, S[_]: Comonad](
sources
.load(imp)
.map(_.leftMap(_.map[Err](SourcesErr(_))).andThen { src =>
implicit val lift: LiftParser[S] = liftI(imp, src)
Ast.fromString[S](src).leftMap(_.map[Err](ParserErr(_)))
parser(imp)(src).leftMap(_.map[Err](ParserErr(_)))
})
.flatMap {
case Validated.Valid(ast) =>
Expand Down
21 changes: 4 additions & 17 deletions parser/src/main/scala/aqua/parser/Ast.scala
@@ -1,13 +1,13 @@
package aqua.parser

import aqua.parser.expr._
import aqua.parser.expr.*
import aqua.parser.head.{HeadExpr, HeaderExpr}
import aqua.parser.lift.LiftParser
import aqua.parser.lift.LiftParser._
import aqua.parser.lift.{LiftParser, Span}
import aqua.parser.lift.LiftParser.*
import cats.data.{Chain, Validated, ValidatedNec}
import cats.free.Cofree
import cats.parse.Parser0 as P0
import cats.{Comonad, Eval}
import cats.~>

case class Ast[S[_]](head: Ast.Head[S], tree: Ast.Tree[S]) {

Expand All @@ -21,17 +21,4 @@ case class Ast[S[_]](head: Ast.Head[S], tree: Ast.Tree[S]) {
object Ast {
type Tree[S[_]] = Cofree[Chain, Expr[S]]
type Head[S[_]] = Cofree[Chain, HeaderExpr[S]]

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[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[S](e.wrapErr))
}

}
3 changes: 3 additions & 0 deletions parser/src/main/scala/aqua/parser/Expr.scala
Expand Up @@ -11,13 +11,16 @@ import cats.free.Cofree
import cats.parse.Parser as P
import cats.syntax.comonad.*
import cats.{Comonad, Eval}
import cats.~>

abstract class Expr[F[_]](val companion: Expr.Companion, val token: Token[F]) {

lazy val isBlock: Boolean = companion match {
case _: Expr.Block => true
case _ => false
}

def mapK[K[_]: Comonad](fk: F ~> K): Expr[K]
}

object Expr {
Expand Down
44 changes: 44 additions & 0 deletions parser/src/main/scala/aqua/parser/Parser.scala
@@ -0,0 +1,44 @@
package aqua.parser

import cats.data.{Validated, ValidatedNec}
import aqua.parser.Ast
import aqua.parser.ParserError
import aqua.parser.LexerError
import aqua.parser.expr.RootExpr
import aqua.parser.head.HeadExpr
import aqua.parser.lift.{FileSpan, LiftParser, Span}
import cats.{Comonad, Eval, ~>}
import cats.parse.LocationMap
import cats.parse.Parser0 as P0
import cats.Id
import aqua.parser.lift.LiftParser.LiftErrorOps


object Parser {

import Span.spanLiftParser
lazy val spanParser = parserSchema[Span.F]()
import LiftParser.Implicits.idLiftParser
lazy val idParser = parserSchema[Id]()

def parserSchema[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 parser[S[_] : LiftParser : Comonad](p: P0[ValidatedNec[ParserError[S], Ast[S]]])(source: String): ValidatedNec[ParserError[S], Ast[S]] = {
p.parseAll(source) match {
case Right(value) => value
case Left(e) => Validated.invalidNec(LexerError(e.wrapErr))
}
}

def natParser[S[_] : LiftParser : Comonad, K[_] : Comonad](
p: P0[ValidatedNec[ParserError[S], Ast[S]]],
nat: S ~> K
)(source: String): ValidatedNec[ParserError[K], Ast[K]] =
parser[S](p)(source).bimap(
e => e.map(_.mapK(nat)),
ast => Ast[K](ast.head.map(_.mapK(nat)), ast.tree.map(_.mapK(nat)))
)
}
19 changes: 15 additions & 4 deletions parser/src/main/scala/aqua/parser/ParserError.scala
@@ -1,9 +1,20 @@
package aqua.parser

import cats.parse.Parser
import cats.~>

trait ParserError[F[_]]
trait ParserError[F[_]] {
def mapK[K[_]](fk: F ~> K): ParserError[K]
}

case class LexerError[F[_]](err: F[Parser.Error]) extends ParserError[F]
case class BlockIndentError[F[_]](indent: F[String], message: String) extends ParserError[F]
case class FuncReturnError[F[_]](point: F[Unit], message: String) extends ParserError[F]
case class LexerError[F[_]](err: F[Parser.Error]) extends ParserError[F] {
def mapK[K[_]](fk: F ~> K): LexerError[K] = copy(fk(err))
}
case class BlockIndentError[F[_]](indent: F[String], message: String) extends ParserError[F] {
def mapK[K[_]](fk: F ~> K): BlockIndentError[K] =
copy(fk(indent))
}
case class FuncReturnError[F[_]](point: F[Unit], message: String) extends ParserError[F] {
def mapK[K[_]](fk: F ~> K): FuncReturnError[K] =
copy(fk(point))
}
8 changes: 7 additions & 1 deletion parser/src/main/scala/aqua/parser/expr/AbilityIdExpr.scala
Expand Up @@ -6,9 +6,15 @@ import aqua.parser.lexer.{Ability, Value}
import aqua.parser.lift.LiftParser
import cats.Comonad
import cats.parse.{Parser => P}
import cats.~>

case class AbilityIdExpr[F[_]](ability: Ability[F], id: Value[F])
extends Expr[F](AbilityIdExpr, ability)
extends Expr[F](AbilityIdExpr, ability) {

def mapK[K[_]: Comonad](fk: F ~> K): AbilityIdExpr[K] =
copy(ability.copy(fk(ability.name)), id.mapK(fk))

}

object AbilityIdExpr extends Expr.Leaf {

Expand Down
5 changes: 4 additions & 1 deletion parser/src/main/scala/aqua/parser/expr/AliasExpr.scala
Expand Up @@ -6,9 +6,12 @@ import aqua.parser.lexer.{CustomTypeToken, TypeToken}
import aqua.parser.lift.LiftParser
import cats.Comonad
import cats.parse.Parser
import cats.~>

case class AliasExpr[F[_]](name: CustomTypeToken[F], target: TypeToken[F])
extends Expr[F](AliasExpr, name)
extends Expr[F](AliasExpr, name) {
def mapK[K[_]: Comonad](fk: F ~> K): AliasExpr[K] = copy(name.mapK(fk), target.mapK(fk))
}

object AliasExpr extends Expr.Leaf {

Expand Down
5 changes: 4 additions & 1 deletion parser/src/main/scala/aqua/parser/expr/ArrowTypeExpr.scala
Expand Up @@ -6,9 +6,12 @@ import aqua.parser.lexer.{ArrowTypeToken, DataTypeToken, Name}
import aqua.parser.lift.LiftParser
import cats.Comonad
import cats.parse.Parser
import cats.~>

case class ArrowTypeExpr[F[_]](name: Name[F], `type`: ArrowTypeToken[F])
extends Expr[F](ArrowTypeExpr, name)
extends Expr[F](ArrowTypeExpr, name) {
def mapK[K[_]: Comonad](fk: F ~> K): ArrowTypeExpr[K] = copy(name.mapK(fk), `type`.mapK(fk))
}

object ArrowTypeExpr extends Expr.Leaf {

Expand Down
5 changes: 4 additions & 1 deletion parser/src/main/scala/aqua/parser/expr/AssignmentExpr.scala
Expand Up @@ -6,11 +6,14 @@ import aqua.parser.lexer.{Name, Value}
import aqua.parser.lift.LiftParser
import cats.Comonad
import cats.parse.{Parser => P}
import cats.~>

case class AssignmentExpr[F[_]](
variable: Name[F],
value: Value[F]
) extends Expr[F](AssignmentExpr, variable)
) extends Expr[F](AssignmentExpr, variable) {
def mapK[K[_]: Comonad](fk: F ~> K): AssignmentExpr[K] = copy(variable.mapK(fk), value.mapK(fk))
}

object AssignmentExpr extends Expr.Leaf {

Expand Down
12 changes: 11 additions & 1 deletion parser/src/main/scala/aqua/parser/expr/CallArrowExpr.scala
Expand Up @@ -6,13 +6,23 @@ import aqua.parser.lexer.{Ability, Name, Value}
import aqua.parser.lift.LiftParser
import cats.Comonad
import cats.parse.{Parser => P}
import cats.~>

case class CallArrowExpr[F[_]](
variables: List[Name[F]],
ability: Option[Ability[F]],
funcName: Name[F],
args: List[Value[F]]
) extends Expr[F](CallArrowExpr, funcName)
) extends Expr[F](CallArrowExpr, funcName) {

def mapK[K[_]: Comonad](fk: F ~> K): CallArrowExpr[K] =
copy(
variables.map(_.mapK(fk)),
ability.map(_.mapK(fk)),
funcName.mapK(fk),
args.map(_.mapK(fk))
)
}

object CallArrowExpr extends Expr.Leaf {

Expand Down
5 changes: 4 additions & 1 deletion parser/src/main/scala/aqua/parser/expr/CatchExpr.scala
Expand Up @@ -6,8 +6,11 @@ import aqua.parser.lexer.Token._
import aqua.parser.lift.LiftParser
import cats.Comonad
import cats.parse.Parser
import cats.~>

case class CatchExpr[F[_]](name: Name[F]) extends Expr[F](CatchExpr, name)
case class CatchExpr[F[_]](name: Name[F]) extends Expr[F](CatchExpr, name) {
def mapK[K[_]: Comonad](fk: F ~> K): CatchExpr[K] = copy(name.mapK(fk))
}

object CatchExpr extends Expr.AndIndented {

Expand Down
5 changes: 4 additions & 1 deletion parser/src/main/scala/aqua/parser/expr/CoExpr.scala
Expand Up @@ -7,8 +7,11 @@ import aqua.parser.lift.LiftParser._
import cats.Comonad
import cats.parse.Parser
import Token.`co`
import cats.~>

case class CoExpr[F[_]](point: Token[F]) extends Expr[F](CoExpr, point)
case class CoExpr[F[_]](point: Token[F]) extends Expr[F](CoExpr, point) {
def mapK[K[_]: Comonad](fk: F ~> K): CoExpr[K] = copy(point.mapK(fk))
}

object CoExpr extends Expr.Prefix {
override def continueWith: List[Expr.Lexem] = ParExpr.continueWith
Expand Down

0 comments on commit dae234d

Please sign in to comment.