Scala Parser Combinators用于递归bnf?

时间:2010-07-27 12:42:08

标签: scala ebnf parser-combinators

我试图匹配这种语法:

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

或者如果我将|更改为|||,我会收到堆栈溢出:

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解析器。

IE中。将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);
    }
}

2 个答案:

答案 0 :(得分:10)

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

  

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

     
      
  • 任何扩展Parsers的类/特征(直接或通过   子类)可以在PackratParsers中混合使用。   示例:对象MyGrammar扩展   StandardTokenParsers用   PackratParsers
  •   
  • 以前声称为def的每个语法生成没有   形式参数变得懒惰,   它的类型改变了   Parser [Elem]到PackratParser [Elem]。   因此,例如,def生产:   解析器[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))
}

答案 1 :(得分:1)

制作

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

是递归的。它扩展到

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

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

你应该重写你的语法,避免左递归制作。

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