首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在Fastparse中避免左递归无限循环?

如何在Fastparse中避免左递归无限循环?
EN

Stack Overflow用户
提问于 2020-07-22 14:08:48
回答 1查看 216关注 0票数 3

我有一个解析器,它在Scala Packrat解析器组合子中工作得很好。我想用Fastparse库尝试一些更快的东西。但是,它不能处理左递归无限循环。有没有什么标准的方法来解决这个问题?

代码语言:javascript
复制
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)
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-07-22 23:33:02

我对Fastparse了解不多,但我会尽力回答你的问题。现在,你的语法看起来像这样:

代码语言:javascript
复制
expr ::= div | num
div  ::= expr "/" expr
num  ::= 0 | 1 | ...

因此,如果您希望将1/2解析为表达式,它将首先尝试匹配div。为了做到这一点,它将尝试再次匹配expr,并且基本上无限地进行。我们可以通过将num放在div之前来解决这个问题,正如上面的评论中所建议的那样:

代码语言:javascript
复制
expr ::= num | div
Or
def expr[_: P]: P[Expr] = P(num | div)

成功!还是真的是这样?仔细观察结果,您会发现它不是一个Div(Num(1), Num(2)),而仅仅是一个Num(1)。要解决此问题,请使用End

代码语言:javascript
复制
def expr[_: P]: P[Expr] = P((num | div) ~ End)

现在它失败了,说它找到了"/2“。它首先成功匹配num,因此它没有理由认为第一个数字是除法运算的一部分。因此,我们将不得不在num之前使用div,以确保使用更大的模式,但需要做一些事情来避免递归。我们可以像这样重构它:

代码语言:javascript
复制
expr ::= div
div  ::= num ("/" num)*

div不只是匹配除法,它还可以匹配单个数字,但如果可能,它会尝试匹配除法。在Scala中,这将是:

代码语言:javascript
复制
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“等。

parse("1/2/3/4", expr(_))的输出为Parsed.Success(Div(Div(Div(Num(1),Num(2)),Num(3)),Num(4)), 7)

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63027937

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档