【问题标题】:scala parser combinator infinite loopscala解析器组合器无限循环
【发布时间】:2015-06-23 05:35:58
【问题描述】:

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

我在下面有 2 种解析方法。一个使用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")
    }
  }
}

【问题讨论】:

    标签: scala parsing parser-combinators


    【解决方案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:嗯...你想在你的键名中允许=-signs吗?那么我的版本在这里不起作用,因为它不会在键名之后强制使用空格。

    【讨论】:

    • 呃,我不应该迟到。这是一个可怕的问题....我会更新,但是目标是编写一个解析器(用于学习),强制 K = V 强制空白。它看起来像是一个简单的入门解析器。
    • 我不确定在这里做什么。似乎问题已得到解答,但尚未得到解答。这在很大程度上是我的错,因为我问的问题很糟糕。是否有可能给 Andrey Tyukin 一些业力或其他东西以进行一次伟大的尝试,但这仍然表明我被困在这里?
    • 你好。对不起,我没有意识到你关心空白。我认为这完全与您的string-Parser 中的*-vs-+ 错误有关。我添加了另一个明确处理空白的版本。
    【解决方案2】:

    这不是重复的,它是带有 RegexParsers 的不同版本,它明确处理空白

    如果您出于某种原因真的很关心空白,那么您可以坚持使用 RegexParsers,并执行以下操作(注意 skipWhitespace = false、空白的显式解析器 ws、两个带有波浪线的 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)
    

    删除了string* 而非+ 的错误,与之前的版本一样。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-01-17
      • 1970-01-01
      • 2010-10-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-24
      • 2015-07-12
      相关资源
      最近更新 更多