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 对待白色 space 的方式不同。
JavaTokenParsers 为您处理白色 space,它不特定于 Java,它适用于大多数非深奥的语言。只要您不尝试解析 Whitespace,JavaTokenParsers 就是一个很好的起点。
您的字符串定义包含一个 *
,这导致了无限递归。
您的 eq 定义包含一些与空 space 处理混淆的东西(除非确实有必要,否则不要这样做)。
此外,如果要解析整行,则必须调用 parseAll
,
否则它只以非贪婪的方式解析字符串的开头。
结语:为了逐行解析键值对,一些String.split和
String.trim 就足够了。 Scala Parser Combinators 有点矫枉过正。
PS:嗯...你想在你的键名中允许 =
符号吗?那么我的版本在这里不起作用,因为它不会在键名后强制执行空 space。
这不是重复的,它是 RegexParsers
的不同版本,它明确处理了白色space
如果您出于某种原因真的很关心白色 space,那么您可以坚持使用 RegexParsers,并执行以下操作(注意 skipWhitespace = false
,白色 [=31= 的显式解析器] ws
,等号周围有波浪线的两个 ws
,以及明确指定 ws
的 repsep
:
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) + ")")
}
现在解析器拒绝等号周围没有白色space的行:
hey = List(hou) (Matches: true)
hello = List(world, ppl) (Matches: true)
foo = List(bar, baz, blup) (Matches: true)
(Matches: false)
(Matches: false)
在 string
中使用 *
而不是 +
的错误已被删除,就像在以前的版本中一样。
我正在尝试在 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 对待白色 space 的方式不同。 JavaTokenParsers 为您处理白色 space,它不特定于 Java,它适用于大多数非深奥的语言。只要您不尝试解析 Whitespace,JavaTokenParsers 就是一个很好的起点。
您的字符串定义包含一个 *
,这导致了无限递归。
您的 eq 定义包含一些与空 space 处理混淆的东西(除非确实有必要,否则不要这样做)。
此外,如果要解析整行,则必须调用 parseAll
,
否则它只以非贪婪的方式解析字符串的开头。
结语:为了逐行解析键值对,一些String.split和 String.trim 就足够了。 Scala Parser Combinators 有点矫枉过正。
PS:嗯...你想在你的键名中允许 =
符号吗?那么我的版本在这里不起作用,因为它不会在键名后强制执行空 space。
这不是重复的,它是 RegexParsers
的不同版本,它明确处理了白色space
如果您出于某种原因真的很关心白色 space,那么您可以坚持使用 RegexParsers,并执行以下操作(注意 skipWhitespace = false
,白色 [=31= 的显式解析器] ws
,等号周围有波浪线的两个 ws
,以及明确指定 ws
的 repsep
:
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) + ")")
}
现在解析器拒绝等号周围没有白色space的行:
hey = List(hou) (Matches: true)
hello = List(world, ppl) (Matches: true)
foo = List(bar, baz, blup) (Matches: true)
(Matches: false)
(Matches: false)
在 string
中使用 *
而不是 +
的错误已被删除,就像在以前的版本中一样。