簡體   English   中英

Scala解析器組合器無限循環

[英]scala parser combinator infinite loop

我試圖在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")
    }
  }
}

下次,請提供一些具體示例,您輸入的內容看起來並不明顯。

同時,您可以嘗試一下,也許您會發現它有所幫助:

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專用的,它適用於大多數非深奧的語言。 只要您不嘗試解析空白,JavaTokenParsers都是一個很好的起點。

您的字符串定義包含* ,這引起了無限遞歸。 您的eq定義包含一些與空空間處理有關的內容(除非確實必要,否則不要這樣做)。 此外,如果要解析整行,則必須調用parseAll ,否則它將以非貪婪方式僅解析字符串的開頭。

最后一點:對於逐行解析鍵值對,一些String.split和String.trim就足夠了。 Scala Parser組合器對此有點矯kill過正。

PS:嗯...您想允許= -在您的鍵名中簽名嗎? 然后,我的版本在這里將無法使用,因為它不會在鍵名后面強制使用空格。

這不是重復的,而是RegexParsers的不同版本,該版本明確處理空白

如果您出於某種原因真正關心空白,則可以堅持使用RegexParsers,然后執行以下操作(注意skipWhitespace = false ,空白ws顯式解析器,兩個等號周圍帶有波浪形的wsrepsep使用明確指定的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中用*代替+的錯誤已被刪除。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM