【问题标题】:Scala Parser Combinators tricks for recursive bnf?递归 bnf 的 Scala Parser Combinators 技巧?
【发布时间】:2010-07-27 12:42:08
【问题描述】:

我正在尝试匹配这个语法:

pgm ::= exprs
exprs ::= expr [; exprs]
expr ::= ID | expr . [0-9]+

我的 scala packrat 解析器组合器如下所示:

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

object Dotter extends StandardTokenParsers with PackratParsers {
    lexical.delimiters ++= List(".",";")
    def pgm = repsep(expr,";")
    def expr :Parser[Any]= ident | expr~"."~num
    def num = numericLit

       def parse(input: String) =
    phrase(pgm)(new PackratReader(new lexical.Scanner(input))) match {
      case Success(result, _) => println("Success!"); Some(result)
      case n @ _ => println(n);println("bla"); None
    }  

    def main(args: Array[String]) {
      val prg = "x.1.2.3;" +
            "y.4.1.1;" +
            "z;" +
            "n.1.10.30"


            parse(prg);
    }
}

但这不起作用。要么它“匹配贪婪”并告诉我:

[1.2] failure: end of input expected 
x.1.2.3;y.4.1.1;z;n.1.10.30

或者如果我将| 更改为|||,我会得到一个stackoverflow:

Exception in thread "main" java.lang.StackOverflowError
at java.lang.Character.isLetter(Unknown Source)
at java.lang.Character.isLetter(Unknown Source)
at scala.util.parsing.combinator.lexical.Lexical$$anonfun$letter$1.apply(Lexical.scala:32)
at scala.util.parsing.combinator.lexical.Lexical$$anonfun$letter$1.apply(Lexical.scala:32)
...

我有点理解为什么会出现错误;我能做些什么来解析像上面这样的语法?对我来说似乎并不深奥

编辑: 基于http://scala-programming-language.1934581.n4.nabble.com/Packrat-parser-guidance-td1956908.html 中引用的论文 我发现我的程序实际上并没有使用新的 packrat 解析器。

即。将Parser[Any] 更改为PackratParser[Any] 并使用lazy val 而不是def

我将上面的内容重写为:

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

object Dotter extends StandardTokenParsers with PackratParsers {
    lexical.delimiters ++= List(".",";")
    lazy val pgm : PackratParser[Any] = repsep(expr,";")
    lazy val expr :PackratParser[Any]= expr~"."~num | ident
    lazy val num = numericLit

    def parse(input: String) =
    phrase(pgm)(new PackratReader(new lexical.Scanner(input))) match {
      case Success(result, _) => println("Success!"); Some(result)
      case n @ _ => println(n);println("bla"); None
    }  

    def main(args: Array[String]) {
      val prg = "x.1.2.3 ;" +
            "y.4.1.1;" +
            "z;" +
            "n.1.10.30"


            parse(prg);
    }
}

【问题讨论】:

    标签: scala ebnf parser-combinators


    【解决方案1】:

    问题是(至少部分地)您实际上并没有使用 Packrat 解析器。请参阅 Scala 的 PackratParsers 特征的文档,其中说

    使用 PackratParsers 非常相似 使用解析器:

    • 任何扩展解析器的类/特征(直接或通过 子类)可以混入 PackratParsers。 示例:对象 MyGrammar 扩展 StandardTokenParsers 与 PackratParsers
    • 以前声明为 def 的每个语法产生式都没有 形参变成惰性值, 它的类型从 Parser[Elem] 到 PackratParser[Elem]。 因此,例如,def 生产: Parser[Int] = {...} 变成惰性值 生产:PackratParser[Int] = {...}
    • 重要提示:使用 PackratParsers 不是一个全有或全无的决定。 它们可以免费与常规混合 单一语法中的解析器。

    我对 Scala 2.8 的解析器组合器了解得不够多,无法完全解决这个问题,但通过以下修改,我能够让它解析到分号,这比你之前的改进完成。

    object Dotter extends StandardTokenParsers with PackratParsers {
        lexical.delimiters ++= List(".",";")
        lazy val pgm:PackratParser[Any] = repsep(expr,";")
        lazy val expr:PackratParser[Any]= ident ||| (expr~"."~numericLit)
    
        def parse(input: String) = phrase(expr)(lex(input)) match {
          case Success(result, _) => println("Success!"); Some(result)
          case n @ _ => println(n);println("bla"); None
        }  
    
        def lex(input:String) = new PackratReader(new lexical.Scanner(input))
    }
    

    【讨论】:

    • 没错!我正在重新阅读文档并弄清楚了这一点。最后需要的是 parse 方法中的拼写错误:phrase(expr) 应该是 phrase(pgm)。干杯!
    【解决方案2】:

    生产

    expr ::= ID | expr . [0-9]+
    

    是左递归的。它扩展为

    expr ::= ID
    expr ::= expr . [0-9]+
    

    左递归发生在第二行。这就是导致解析器溢出堆栈的原因。

    你应该重写你的语法,避免左递归产生式。

    expr ::= ID {. [0-9]+}
    

    【讨论】:

    • Packrat 解析器不应该允许我进行左递归吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-06
    • 2022-11-18
    相关资源
    最近更新 更多