簡體   English   中英

如何避免 Fastparse 中的左遞歸無限循環?

[英]How to avoid left-recursion infinite loops in Fastparse?

我有一個在 Scala Packrat 解析器組合器中運行良好的解析器。 我想用 Fastparse 庫更快地嘗試一些東西。 但是,它不能處理左遞歸無限循環。 有什么標准的方法來應對嗎?

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)

我對 Fastparse 了解不多,但我還是會嘗試回答您的問題。 現在,你的語法看起來像這樣:

expr ::= div | num
div  ::= expr "/" expr
num  ::= 0 | 1 | ...

因此,如果您想將1/2解析為表達式,它將首先嘗試匹配div 為此,它將嘗試再次匹配expr ,並且基本上是無限地匹配 go。 我們可以通過將num放在div之前來解決這個問題,正如上面評論中所建議的:

expr ::= num | div
Or
def expr[_: P]: P[Expr] = P(num | div)

成功? 或者是它,在更仔細地查看結果后,您會發現它不是Div(Num(1), Num(2)) ,而只是Num(1) 要解決此問題,請使用End

def expr[_: P]: P[Expr] = P((num | div) ~ End)

現在它失敗了,說它找到了“/ 2”。 它首先成功匹配num ,因此沒有理由認為第一個數字是除法運算的一部分。 所以我們畢竟必須在num之前使用div ,以確保使用更大的模式,但需要做一些事情來避免遞歸。 我們可以這樣重構它:

expr ::= div
div  ::= num ("/" num)*

div不僅匹配除法,它還可以匹配單個數字,但它會在可能的情況下嘗試匹配除法。 在 Scala 中,這將是:

def div[_: P] = P(num ~ ("/" ~/ num).rep).map {
  case (a, ops) => ops.foldLeft(a: Expr){ case (a, b) => Div(a, b) }
}

def expr[_: P]: P[Expr] = P(div ~ End)

這樣,我們可以匹配“1/2”、“1/2/3”、“1/2/3/4”等。

Output for parse("1/2/3/4", expr(_))Parsed.Success(Div(Div(Div(Num(1),Num(2)),Num(3)),Num(4)), 7)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM