Комбинаторы парсера Scala для рекурсивных bnf?

Im пытается соответствовать этому синтаксису:

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.apply(Lexical.scala:32)
at scala.util.parsing.combinator.lexical.Lexical$$anonfun$letter.apply(Lexical.scala:32)
...

Я kindoff понимаю, почему я получаю ошибки; что я могу сделать, чтобы разобрать синтаксис, как выше? Мне это не кажется эзотерическим!--12-->

изменить: Основывающийся на документ, указанный в http://scala-programming-language.1934581.n4.nabble.com/Packrat-parser-guidance-td1956908.html Я узнал, что моя программа фактически не использовала новый парсер packrat.

Ie. изменить Parser[Any] to 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 ответов


проблема в том (по крайней мере частично), что вы фактически не используете Парсеры Packrat. Обратитесь к документации для PackratParsers признак, который говорит

использование PackratParsers очень похоже к использованию парсеров:

  • любой класс / признак, который расширяет синтаксические анализаторы (напрямую или через подкласс) может смешиваться в PackratParsers. Пример: объект MyGrammar расширяется StandardTokenParsers с PackratParsers
  • каждый производство грамматики, ранее объявленное как def без формальные параметры становятся ленивым val, и свой тип изменен от Парсер[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))
}

производства

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

- это левую рекурсию. Он расширяется до

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

где левая рекурсия происходит на 2-й строке. Это то, что заставляет парсер переполнять стек.

вы должны переписать свою грамматику, избегая левых рекурсивных производств.

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