I had a parser that worked well in Scala Packrat parser combinators. I would like to try something faster with the Fastparse library. However, it cannot handle left-recursion infinite loops. Is there any standard way to cope with that?
sealed trait Expr
case class Num(value: java.lang.Number) extends Expr
case class Div(a: Expr, b: Expr) extends Expr
def num[_: P] = P(CharIn("0-9").rep(1).!).map(n => Num(n.toInt))
def div[_: P] = P(expr ~ "/" ~ expr).map(Div.tupled)
def expr[_: P]: P[Expr] = P(div | num)
I don't know much about Fastparse, but I'll try to answer your question nevertheless. Right now, your grammar looks something like this:
So if you wanted to parse
1/2as an expression, it would first try to matchdiv. To do that, it would try to matchexpragain, and basically go on infinitely. We can fix this by puttingnumbeforediv, as suggested in a comment above:Success! Or is it? Upon looking more closely at the result, you'll see that it's not a
Div(Num(1), Num(2))but rather just aNum(1). To fix this, useEndAnd now it fails, saying it found "/2". It successfully matches
numfirst, so it has no reason to think that that first number is part of a division operation. So we will have to usedivbeforenumafter all, to make sure the bigger pattern is used, but something needs to be done to avoid recursion. We can refactor it like this:divdoesn't just match division, it can also match a single number, but it tries to match division when possible. In Scala, that would be:This way, we can match "1/2", "1/2/3", "1/2/3/4", etc.
Output for
parse("1/2/3/4", expr(_))isParsed.Success(Div(Div(Div(Num(1),Num(2)),Num(3)),Num(4)), 7)