Skip to content

Commit

Permalink
ZeroOrMore: avoid instantiating builder when no match
Browse files Browse the repository at this point in the history
Fix #319
  • Loading branch information
yanns committed Sep 21, 2022
1 parent a54ad33 commit 96dda11
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -358,18 +358,24 @@ trait OpTreeContext[OpTreeCtx <: ParserMacros.ParserContext] {
else q"val m = __saveState; if (${separator(wrapped)}) rec(m) else m"

q"""
${collector.valBuilder}

@_root_.scala.annotation.tailrec def rec(mark: org.parboiled2.Parser.Mark): org.parboiled2.Parser.Mark = {
val matched = ${op.render(wrapped)}
if (matched) {
${collector.popToBuilder}
$recurse
} else mark
}
val firstMatch = ${op.render(wrapped)}
if (!firstMatch) {
${collector.pushEmptyCollector}
} else {
${collector.valBuilder}
${collector.popToBuilder}

@_root_.scala.annotation.tailrec def rec(mark: org.parboiled2.Parser.Mark): org.parboiled2.Parser.Mark = {
val matched = ${op.render(wrapped)}
if (matched) {
${collector.popToBuilder}
$recurse
} else mark
}

__restoreState(rec(__saveState))
${collector.pushBuilderResult}"""
__restoreState($recurse)
${collector.pushBuilderResult}
}"""
}
}

Expand Down Expand Up @@ -743,6 +749,7 @@ trait OpTreeContext[OpTreeCtx <: ParserMacros.ParserContext] {
/////////////////////////////////// helpers ////////////////////////////////////

class Collector(
val pushEmptyCollector: Tree,
val valBuilder: Tree,
val popToBuilder: Tree,
val pushBuilderResult: Tree,
Expand All @@ -752,10 +759,11 @@ trait OpTreeContext[OpTreeCtx <: ParserMacros.ParserContext] {

lazy val rule0Collector = {
val unit = q"()"
new Collector(unit, unit, q"true", unit, unit)
new Collector(q"true", unit, unit, q"true", unit, unit)
}

lazy val rule1Collector = new Collector(
pushEmptyCollector = q"valueStack.push(scala.collection.immutable.Vector.empty[Any]); true",
valBuilder = q"val builder = new scala.collection.immutable.VectorBuilder[Any]",
popToBuilder = q"builder += valueStack.pop()",
pushBuilderResult = q"valueStack.push(builder.result()); true",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,10 @@ class OpTreeContext(parser: Expr[Parser])(using Quotes) {
def withSeparator(sep: Separator) = copy(separator = sep)
def ruleTraceNonTerminalKey = '{ RuleTrace.ZeroOrMore }

def renderInner(start: Expr[Int], wrapped: Boolean): Expr[Boolean] =
collector.withCollector { coll =>
def renderInner(start: Expr[Int], wrapped: Boolean): Expr[Boolean] = {
def nonEmptyResult: Expr[Boolean] = collector.withCollector { coll =>
'{
${ coll.popToBuilder }
@ _root_.scala.annotation.tailrec
def rec(mark: org.parboiled2.Parser.Mark): org.parboiled2.Parser.Mark = {
val matched = ${ op.render(wrapped) }
Expand All @@ -160,11 +161,31 @@ class OpTreeContext(parser: Expr[Parser])(using Quotes) {
} else mark
}

$parser.__restoreState(rec($parser.__saveState))
val state = ${
if (separator eq null) '{ rec($parser.__saveState) }
else
'{
val m = $parser.__saveState
if (${ separator(wrapped) }) rec(m) else m
}
}

$parser.__restoreState(state)
${ coll.pushBuilderResult }
true
}
}

'{
val firstMatch = ${ op.render(wrapped) }
if (!firstMatch) {
${ collector.pushEmptyCollector }
true
} else {
${ nonEmptyResult }
}
}
}
}
case class OneOrMore(op: OpTree, collector: Collector, separator: Separator = null) extends WithSeparator {
def withSeparator(sep: Separator) = copy(separator = sep)
Expand Down Expand Up @@ -953,6 +974,7 @@ class OpTreeContext(parser: Expr[Parser])(using Quotes) {
/////////////////////////////////// helpers ////////////////////////////////////

trait Collector {
def pushEmptyCollector: Expr[Unit]
def withCollector(f: CollectorInstance => Expr[Boolean]): Expr[Boolean]
}
trait CollectorInstance {
Expand All @@ -964,15 +986,22 @@ class OpTreeContext(parser: Expr[Parser])(using Quotes) {

// no-op collector
object rule0Collector extends Collector with CollectorInstance {
private val unit: Expr[Unit] = '{}
override def pushEmptyCollector: Expr[Unit] = unit

override def withCollector(f: CollectorInstance => Expr[Boolean]): Expr[Boolean] = f(this)
private val unit: Expr[Unit] = '{}
def popToBuilder: Expr[Unit] = unit
def pushBuilderResult: Expr[Unit] = unit
def pushSomePop: Expr[Unit] = unit
def pushNone: Expr[Unit] = unit
}

object rule1Collector extends Collector {

override def pushEmptyCollector: Expr[Unit] = '{
$parser.valueStack.push(scala.collection.immutable.Vector.empty[Any])
}

override def withCollector(f: CollectorInstance => Expr[Boolean]): Expr[Boolean] = '{
val builder = new scala.collection.immutable.VectorBuilder[Any]
${
Expand Down

0 comments on commit 96dda11

Please sign in to comment.