我有一个导致堆栈溢出的代码。
// Lexer.Token defined elsewhere
object BadParser extends Parsers {
import Lexer.Token
type Elem = Token
private def PUNCT = Token.PUNCT
private def parens[T](p: Parser[T]) = PUNCT("(") ~ p ~ PUNCT(")")
def expr: Parser[Any] = parens(expr) | PUNCT(".")
}
class ParensTest extends AnyFlatSpec {
def testBad(input: String) =
val tokens = Lexer.scan(input)
BadParser.expr(tokens)
"bad parser" should "not recur forever but it does" in {
testBad("((.))")
}
}
这已经够奇怪的了,但还有更奇怪的当我试着像这样检查括号(expr)时:
def expr: Parser[Any] = log(parens(expr))("parens expr") | PUNCT(".")
不再有堆栈溢出。
我决定尝试像这样内联parans函数和参数:
inline parens(inline p... and that also fixed it.
我的困惑是,内联https://docs.scala-lang.org/scala3/guides/macros/inline.html的文档说内联参数不应该改变函数的语义,但它显然改变了。我错过了什么?
(my对内联的理解是,它迫使编译器实际内联,而不仅仅是给予最大的努力,当你说参数内联时,它将内联参数,而不是通过值或引用传递)
编辑:我使用Scala Parser Combinator库
2条答案
按热度按时间icomxhvb1#
By-name(也称为lazy evaluation)可以防止无限递归。即
操作符
~
的参数被定义为by-name,因此内联会对您有所帮助。然而,如果内联了太多的调用站点,则不会给予好的结果。sirbozc52#
你在写的时候有一个无限递归:
expr
在能够调用parens
之前调用了自己。为什么内联时它会消失?
内联代码就像你写了下面的代码:
现在,* 这是一个假设,因为我们没有完整的代码,*
expr
首先调用~
方法,我猜在你的情况下不需要计算expr
。