scala解析器组合器无限循环

时间:2015-04-16 16:41:15

标签: scala parsing parser-combinators

我正在尝试在scala中编写一个简单的解析器,但是当我添加一个重复的令牌时,Scala似乎陷入了无限循环。

我有两种解析方法。一个使用rep()。非重复版本按预期工作(不是我想要的),使用rep()版本会导致无限循环。

编辑: 这是一个学习的例子,我厌倦了执行'='被空白包围。

如果有用,这是我的实际测试文件:

a = 1
b = 2
c = 1 2 3

我能够解析:(使用parse1方法) K = V

但是在尝试将练习扩展到以下时遇到了这个问题: K = V1 V2 V3

import scala.util.parsing.combinator._
import scala.io.Source.fromFile

class MyParser extends RegexParsers {
  override def skipWhitespace(): Boolean = { false }

  def key: Parser[String] = """[a-zA-Z]+""".r ^^ { _.toString }
  def eq: Parser[String]   = """\s+=\s+""".r ^^ { _.toString.trim }
  def string: Parser[String] = """[^ \t\n]*""".r ^^ { _.toString.trim }
  def value: Parser[List[String]] = rep(string)

  def foo(key: String, value: String): Boolean = {
    println(key + " = " + value)
    true
  }

  def parse1: Parser[Boolean] = key ~ eq ~ string ^^ { case k ~ eq ~ string => foo(k, string) }
  def parse2: Parser[Boolean] = key ~ eq ~ value ^^ { case k ~ eq ~ value => foo(k, value.toString) }

  def parseLine(line: String): Boolean = {
      parse(parse2, line) match {
      case Success(matched, _) => true
      case Failure(msg, _) => false
      case Error(msg, _) => false
    }
  }
}

object TestParser {
  def usage() = {
    System.out.println("<file>")
  }

  def main(args: Array[String]) : Unit = {
    if (args.length != 1) {
      usage()
    } else {
      val mp = new MyParser()

      fromFile(args(0)).getLines().foreach { mp.parseLine }
      println("done")
    }
  }
}

2 个答案:

答案 0 :(得分:1)

下一次,请提供一些具体的例子,你的输入应该是什么样子并不明显。

与此同时,您可以试试这个,也许您觉得它很有用:

import scala.util.parsing.combinator._
import scala.io.Source.fromFile

class MyParser extends JavaTokenParsers {
  // override def skipWhitespace(): Boolean = { false }

  def key: Parser[String] = """[a-zA-Z]+""".r ^^ { _.toString }
  def eq: Parser[String]   = "="
  def string: Parser[String] = """[^ \t\n]+""".r
  def value: Parser[List[String]] = rep(string)

  def foo(key: String, value: String): Boolean = {
    println(key + " = " + value)
    true
  }

  def parse1: Parser[Boolean] = key ~ eq ~ string ^^ { case k ~ eq ~ string => foo(k, string) }
  def parse2: Parser[Boolean] = key ~ eq ~ value ^^ { case k ~ eq ~ value => foo(k, value.toString) }

  def parseLine(line: String): Boolean = {
      parseAll(parse2, line) match {
      case Success(matched, _) => true
      case Failure(msg, _) => false
      case Error(msg, _) => false
    }
  }
}

val mp = new MyParser()
for (line <- List("hey = hou", "hello = world ppl", "foo = bar baz blup")) {
  println(mp.parseLine(line))
}

说明:

JavaTokenParsers和RegexParsers以不同方式处理空格。 JavaTokenParsers为您处理空白区域,它不是特定于Java的,它适用于大多数非深奥语言。只要你不打算解析Whitespace,JavaTokenParsers就是一个很好的起点。

您的字符串定义包含*,这会导致无限递归。 你的eq定义包括与空白处理相混淆的东西(除非真的有必要,否则不要这样做)。 此外,如果要解析整行,则必须调用parseAll, 否则它只会以非贪婪的方式解析字符串的开头。

最后评论:逐行解析键值对,一些String.split和 String.trim就足够了。 Scala Parser Combinators对此有点矫枉过正。

PS:嗯......你想允许= - 你的钥匙名中的标志吗?然后我的版本在这里不起作用,因为它不会在键名后强制使用空格。

答案 1 :(得分:1)

这不是重复的,它是RegexParsers的另一个版本,可以明确地处理空白

如果您出于某些原因真的关心空白区域,那么您可以坚持使用RegexParsers,并执行以下操作(注意skipWhitespace = false显式解析器的空格ws,两个{{ 1}}在等号周围有凹线,ws明确指定repsep):

ws

现在解析器拒绝等号周围没有空格的行:

import scala.util.parsing.combinator._
import scala.io.Source.fromFile

class MyParser extends RegexParsers {
  override def skipWhitespace(): Boolean = false

  def ws: Parser[String] = "[ \t]+".r
  def key: Parser[String] = """[a-zA-Z]+""".r ^^ { _.toString }
  def eq: Parser[String]   = ws ~> """=""" <~ ws
  def string: Parser[String] = """[^ \t\n]+""".r
  def value: Parser[List[String]] = repsep(string, ws)

  def foo(key: String, value: String): Boolean = {
    print(key + " = " + value)
    true
  }

  def parse1: Parser[Boolean] = (key ~ eq ~ string) ^^ { case k ~ e ~ v => foo(k, v) }
  def parse2: Parser[Boolean] = (key ~ eq ~ value) ^^ { case k ~ e ~ v => foo(k, v.toString) }

  def parseLine(line: String): Boolean = {
      parseAll(parse2, line) match {
      case Success(matched, _) => true
      case Failure(msg, _) => false
      case Error(msg, _) => false
    }
  }
}

val mp = new MyParser()
for (line <- List("hey = hou", "hello = world ppl", "foo = bar baz blup", "foo= bar baz", "foo =bar baz")) {
  println(" (Matches: " + mp.parseLine(line) + ")")
}

hey = List(hou) (Matches: true) hello = List(world, ppl) (Matches: true) foo = List(bar, baz, blup) (Matches: true) (Matches: false) (Matches: false) *代替+的错误已被删除,就像之前的版本一样。