Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scala 3 perf improvements, consolidate rep implementations #273

Merged
merged 14 commits into from
Mar 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import de.tobiasroeser.mill.vcs.version.VcsVersion
import $ivy.`com.github.lolgab::mill-mima::0.0.13`
import com.github.lolgab.mill.mima._

val scala31 = "3.1.3"
val scala31 = "3.2.2"
val scala213 = "2.13.10"
val scala212 = "2.12.17"
val scala211 = "2.11.12"
Expand Down Expand Up @@ -237,16 +237,17 @@ object perftests extends Module{
object bench2 extends PerfTestModule {
def scalaVersion0 = scala213
def moduleDeps = Seq(
scalaparse.jvm(scala212).test,
pythonparse.jvm(scala212).test,
cssparse.jvm(scala212).test,
fastparse.jvm(scala212).test,
scalaparse.jvm(scala213).test,
pythonparse.jvm(scala213).test,
cssparse.jvm(scala213).test,
fastparse.jvm(scala213).test,
)

}

object benchScala3 extends PerfTestModule {
def scalaVersion0 = scala31
def sources = T.sources{ bench2.sources() }
def moduleDeps = Seq(
scalaparse.jvm(scala31).test,
pythonparse.jvm(scala31).test,
Expand Down
2 changes: 1 addition & 1 deletion fastparse/src-2/fastparse/internal/MacroImpls.scala
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ object MacroImpls {
if (ctx0.verboseFailures) {
ctx0.aggregateMsg(
startIndex,
Msgs(List(new Lazy(() => name.splice.value))),
Msgs(new Lazy(() => name.splice.value) :: Nil),
ctx0.failureGroupAggregate,
startIndex < ctx0.traceIndex
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package fastparse.internal


import fastparse.{Implicits, NoWhitespace, ParsingRun}

import Util.{aggregateMsgInRep, aggregateMsgPostSep}
import scala.annotation.tailrec


class RepImpls[T](val parse0: () => ParsingRun[T]) extends AnyVal{
def repX[V](min: Int = 0,
sep: => ParsingRun[_] = null,
Expand Down Expand Up @@ -148,7 +149,9 @@ class RepImpls[T](val parse0: () => ParsingRun[T]) extends AnyVal{
outerCut: Boolean,
sepMsg: Msgs,
lastAgg: Msgs): ParsingRun[V] = {

ctx.cut = precut | (count < min && outerCut)

if (count == 0 && actualMax == 0) ctx.freshSuccess(repeater.result(acc), startIndex)
else {
parse0()
Expand All @@ -171,36 +174,34 @@ class RepImpls[T](val parse0: () => ParsingRun[T]) extends AnyVal{
if (verboseFailures) ctx.setMsg(startIndex, () => parsedMsg.render + ".rep" + (if(min == 0) "" else s"($min)"))
res
}
else if (!consumeWhitespace(whitespace, ctx, false)) ctx.asInstanceOf[ParsingRun[Nothing]]
else {
if (whitespace ne NoWhitespace.noWhitespaceImplicit) Util.consumeWhitespace(whitespace, ctx)

if (!ctx.isSuccess && ctx.cut) ctx.asInstanceOf[ParsingRun[Nothing]]
else {
ctx.cut = false
val sep1 = sep
val sepCut = ctx.cut
val endCut = outerCut | postCut | sepCut
if (sep1 == null) rec(beforeSepIndex, nextCount, false, endCut, null, parsedAgg)
else if (ctx.isSuccess) {
if (whitespace ne NoWhitespace.noWhitespaceImplicit) Util.consumeWhitespace(whitespace, ctx)
if (!ctx.isSuccess && sepCut) ctx.asInstanceOf[ParsingRun[Nothing]]
else rec(beforeSepIndex, nextCount, sepCut, endCut, ctx.shortParserMsg, parsedAgg)
}
ctx.cut = false
val sep1 = sep
val sepCut = ctx.cut
val endCut = outerCut | postCut | sepCut
if (sep1 == null) rec(beforeSepIndex, nextCount, false, endCut, null, parsedAgg)
else if (ctx.isSuccess) {
if (!consumeWhitespace(whitespace, ctx, sepCut)) ctx.asInstanceOf[ParsingRun[Nothing]]
else {
val res =
if (sepCut) ctx.augmentFailure(beforeSepIndex, endCut)
else end(beforeSepIndex, beforeSepIndex, nextCount, endCut)

if (verboseFailures) aggregateMsgPostSep(startIndex, min, ctx, parsedMsg, parsedAgg)
res
rec(beforeSepIndex, nextCount, sepCut, endCut, ctx.shortParserMsg, parsedAgg)
}
}
else {
val res =
if (sepCut) ctx.augmentFailure(beforeSepIndex, endCut)
else end(beforeSepIndex, beforeSepIndex, nextCount, endCut)

if (verboseFailures) aggregateMsgPostSep(startIndex, min, ctx, parsedMsg, parsedAgg)
res
}
}
}
}
}
rec(ctx.index, 0, false, ctx.cut, null, null)
}

def rep[V](min: Int,
sep: => ParsingRun[_])
(implicit repeater: Implicits.Repeater[T, V],
Expand Down Expand Up @@ -236,19 +237,18 @@ class RepImpls[T](val parse0: () => ParsingRun[T]) extends AnyVal{
val beforeSepIndex = ctx.index
repeater.accumulate(ctx.successValue.asInstanceOf[T], acc)
val nextCount = count + 1
if (whitespace ne NoWhitespace.noWhitespaceImplicit) Util.consumeWhitespace(whitespace, ctx)

if (!ctx.isSuccess && ctx.cut) ctx.asInstanceOf[ParsingRun[Nothing]]
if (!consumeWhitespace(whitespace, ctx, false)) ctx.asInstanceOf[ParsingRun[Nothing]]
else {
ctx.cut = false
val sep1 = sep
val sepCut = ctx.cut
val endCut = outerCut | postCut | sepCut
if (sep1 == null) rec(beforeSepIndex, nextCount, false, endCut, null, parsedAgg)
else if (ctx.isSuccess) {
if (whitespace ne NoWhitespace.noWhitespaceImplicit) Util.consumeWhitespace(whitespace, ctx)

rec(beforeSepIndex, nextCount, sepCut, endCut, ctx.shortParserMsg, parsedAgg)
if (!consumeWhitespace(whitespace, ctx, sepCut)) ctx.asInstanceOf[ParsingRun[Nothing]]
else {
rec(beforeSepIndex, nextCount, sepCut, endCut, ctx.shortParserMsg, parsedAgg)
}
}
else {
val res =
Expand All @@ -264,49 +264,12 @@ class RepImpls[T](val parse0: () => ParsingRun[T]) extends AnyVal{
rec(ctx.index, 0, false, ctx.cut, null, null)
}

private def aggregateMsgPostSep[V](startIndex: Int,
min: Int,
ctx: ParsingRun[Any],
parsedMsg: Msgs,
lastAgg: Msgs) = {
ctx.aggregateMsg(
startIndex,
() => parsedMsg.render + s".rep($min)",
// When we fail on a sep, we collect the failure aggregate of the last
// non-sep rep body together with the failure aggregate of the sep, since
// the last non-sep rep body continuing is one of the valid ways of
// continuing the parse
ctx.failureGroupAggregate ::: lastAgg

)
}

private def aggregateMsgInRep[V](startIndex: Int,
min: Int,
ctx: ParsingRun[Any],
sepMsg: Msgs,
parsedMsg: Msgs,
lastAgg: Msgs,
precut: Boolean) = {
if (sepMsg == null || precut) {
ctx.aggregateMsg(
startIndex,
() => parsedMsg.render + s".rep($min)",
if (lastAgg == null) ctx.failureGroupAggregate
else ctx.failureGroupAggregate ::: lastAgg
)
} else {
ctx.aggregateMsg(
startIndex,
() => parsedMsg.render + s".rep($min)",
// When we fail on a rep body, we collect both the concatenated
// sep and failure aggregate of the rep body that we tried (because
// we backtrack past the sep on failure) as well as the failure
// aggregate of the previous rep, which we could have continued
if (lastAgg == null) Util.joinBinOp(sepMsg, parsedMsg)
else Util.joinBinOp(sepMsg, parsedMsg) ::: lastAgg
)
private def consumeWhitespace(whitespace: fastparse.Whitespace, ctx: ParsingRun[_], extraCut: Boolean) = {
if (whitespace eq NoWhitespace.noWhitespaceImplicit) true
else {
Util.consumeWhitespace(whitespace, ctx)
if (!ctx.isSuccess && (extraCut || ctx.cut)) false
else true
}
}

}
20 changes: 8 additions & 12 deletions fastparse/src-3/fastparse/internal/MacroInlineImpls.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,18 @@ object MacroInlineImpls {
}
} else {
val xLength = Expr[Int](x.length)
val checker =
'{ (string: _root_.fastparse.ParserInput, offset: _root_.scala.Int) =>
${
x.zipWithIndex
.map { case (char, i) => '{ string.apply(offset + ${ Expr(i) }) == ${ Expr(char) } } }
.reduce[Expr[Boolean]] { case (l, r) => '{ $l && $r } }
}
}
'{

$ctx match {
case ctx1 =>
val index = ctx1.index
val end = index + $xLength
val input = ctx1.input
val res =
if (input.isReachable(end - 1) && ${ checker }(input, index)) {
if (input.isReachable(end - 1) && ${
x.zipWithIndex
.map { case (char, i) => '{ input.apply(index + ${ Expr(i) }) == ${ Expr(char) } } }
.reduce[Expr[Boolean]] { case (l, r) => '{ $l && $r } }
}) {
ctx1.freshSuccessUnit(end)
} else {
ctx1.freshFailure().asInstanceOf[ParsingRun[Unit]]
Expand Down Expand Up @@ -99,17 +94,18 @@ object MacroInlineImpls {

val startIndex = ctx1.index
val instrument = ctx1.instrument != null
val ctx0 = t
if (instrument) {
ctx1.instrument.beforeParse(name.value, startIndex)
}
val ctx0 = t

if (instrument) {
ctx1.instrument.afterParse(name.value, ctx0.index, ctx0.isSuccess)
}
if (ctx0.verboseFailures) {
ctx0.aggregateMsg(
startIndex,
Msgs(List(new Lazy(() => name.value))),
Msgs(new Lazy(() => name.value) :: Nil),
ctx0.failureGroupAggregate,
startIndex < ctx0.traceIndex
)
Expand Down
Loading