Комбинаторы парсера 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]+}