Scala解析器组合器:使用PackratParser获得堆栈溢出

Scala解析器组合器:使用PackratParser获得堆栈溢出,scala,parsing,stack-overflow,text-parsing,parser-combinators,Scala,Parsing,Stack Overflow,Text Parsing,Parser Combinators,因为这是我的第一篇帖子,我想借此机会说:多么伟大的网站啊 不管怎样,我想问一个问题: 我是一个Scala新手,我试图用Scala中的解析器组合器解决数据提取和解析问题,我得到了java.lang.StackOverflowerError异常 我的实际示例太大,无法包含,因此我将重用来自的代码,以解决同样的问题。不过,代码被稍加修改。我试图用PackratPassers解决这个问题,但没有成功 import scala.util.parsing.combinator.syntactical.Sta

因为这是我的第一篇帖子,我想借此机会说:多么伟大的网站啊

不管怎样,我想问一个问题:

我是一个Scala新手,我试图用Scala中的解析器组合器解决数据提取和解析问题,我得到了java.lang.StackOverflowerError异常

我的实际示例太大,无法包含,因此我将重用来自的代码,以解决同样的问题。不过,代码被稍加修改。我试图用PackratPassers解决这个问题,但没有成功

import scala.util.parsing.combinator.syntactical.StandardTokenParsers
import scala.util.parsing.combinator.PackratParsers

object ArithmeticParser1 extends StandardTokenParsers with PackratParsers {
  lexical.delimiters ++= List("(", ")", "+", "-", "*", "/")

  lazy val reduceList: Int ~ List[String ~ Int] => Int = {
    case i ~ ps => (i /: ps)(reduce)
  }

  def reduce(x: Int, r: String ~ Int) = (r: @unchecked) match {
    case "+" ~ y => x + y
    case "-" ~ y => x - y
    case "*" ~ y => x * y
    case "/" ~ y => x / y
  }

  lazy val expr  : PackratParser[Int] = term ~ rep ("+" ~ term | "-" ~ term) ^^ reduceList
  lazy val term  : PackratParser[Int] = factor ~ rep ("*" ~ factor | "/" ~ factor) ^^ reduceList
  lazy val factor: PackratParser[Int] = "(" ~> expr <~ ")" | numericLit ^^ (_.toInt)

  def main(args: Array[String]) {
    val times = 500
    val s = "(" * times + "1 + 1" + ")" * times
    val tokens = new PackratReader(new lexical.Scanner(s))
    println(phrase(expr)(tokens))
  }
}
导入scala.util.parsing.combinator.syntactic.StandardTokenParser
导入scala.util.parsing.combinator.PackratParsers
对象算术解析器1使用packratParser扩展了StandardTokenParser{
lexical.delimiters++=列表(“(“,”,“+”,“-”,“*”,“/”)
lazy val reduceList:Int~List[String~Int]=>Int={
案例一~ps=>(i/:ps)(减少)
}
def reduce(x:Int,r:String~Int)=(r:@unchecked)匹配{
大小写“+”~y=>x+y
大小写“-”~y=>x-y
大小写“*”~y=>x*y
大小写“/”~y=>x/y
}
lazy-val-expr:PackratParser[Int]=term~rep(“+”~term |“-”~term)^^reduceList
lazy val term:PackratParser[Int]=factor~rep(“*”~factor |“/”~factor)^^^还原列表
惰性val因子:PackratParser[Int]=“(“~>expr问题
问题是,您确实在填充堆栈。您的表达式由500个开始括号、“1+1”和500个结束括号组成。在语法术语中,您有500个“factor”类型的术语相互嵌套,然后在一个“expr”类型的术语内

对于(嵌套)项的每个开头,解析器都必须在堆栈上推送某些内容(在本例中为函数调用)。当嵌套项完成时,解析的项将从堆栈中弹出此内容(在本例中:函数返回)。如果在最后一个标记之后堆栈为空,并且如果堆栈从未变为负(弹出太多),则您的术语格式正确(在您的示例中,括号是平衡的)

简单来说:解析器使用堆栈计算括号是否平衡

您正在使用多个工具加快解析速度。这些工具都无助于堆栈消耗

你的助手: 使用packrat解析器 packrat解析器缓存已经解析的部分,因此它们不需要再次解析。当您的语法中有许多公共部分的替代品时,这可以带来很好的速度。这对您的情况没有帮助,因为您没有替代品。而且这对堆栈消耗也没有帮助

省略部分结果 您可以使用
忽略解析结果的某些部分。但这只在术语内部有帮助。当调用相应的规则时,解析器仍必须在堆栈上推送某些内容

使用代币 在解析输入流之前,将其分解为标记。这通常会加快解析速度,因为标记化(非递归,通常是正则表达式)比解析(递归)便宜得多。但在您的情况下,问题在于嵌套项的深度(递归问题)。因此,您的压力完全在解析部分,因此会耗尽堆栈。标记化无助于解决该问题

解决方案 我认为解决这个问题并不容易。解析器必须使用某种堆栈数据结构。通常内置堆栈用于性能问题。您必须在堆上使用某种堆栈结构。这通常会慢得多,并且与Scala解析器组合器不兼容

您可以尝试将“factor”封装在(CPS)中,然后将在堆上而不是堆栈上跟踪调用


这个问题没有现成的简单解决方案。

这是一个多么伟大、彻底的答案!我误解了PackratPassers能够解决哪些问题。