如何解析直到在一行中找到一个标记
How to parse until a token is found on a line by itself
我正在尝试解析以下文档:
val doc = """BEGIN
A Bunch
Of Text
With linebreaks
##
"""
这里的想法是,当我看到 ##
单独一行时,我应该认为解析结束。
我试过,使用下面的代码解析这个文档:
object MyParser extends RegexParsers {
val begin: Parser[String] = "BEGIN"
val lines: Parser[Seq[String]] = repsep(line, eol)
val line: Parser[String] = """.+""".r
val eol: Parser[Any] = "\n" | "\r\n" | "\r"
val end: Parser[String] = "##"
val document: Parser[Seq[String]] =
begin ~> lines <~ end
}
MyParser.parseAll(MyParser.document, doc)
然而,当我尝试执行此操作时(在 Annonite 脚本中),我得到以下信息:
java.lang.NullPointerException
scala.util.parsing.combinator.Parsers$class.rep1sep(Parsers.scala:771)
ammonite.$file.vtt$minusparser$MyParser$.rep1sep(vtt-parser.sc:3)
scala.util.parsing.combinator.Parsers$class.repsep(Parsers.scala:687)
ammonite.$file.vtt$minusparser$MyParser$.repsep(vtt-parser.sc:3)
ammonite.$file.vtt$minusparser$MyParser$.<init>(vtt-parser.sc:5)
ammonite.$file.vtt$minusparser$MyParser$.<clinit>(vtt-parser.sc)
ammonite.$file.vtt$minusparser$.<init>(vtt-parser.sc:22)
ammonite.$file.vtt$minusparser$.<clinit>(vtt-parser.sc)
谁能看出我错在哪里?
错误的原因是line
和eol
被定义为正常的class字段val
,但是在lines
中使用了它们在他们的定义之前。给class字段赋值的代码在构造函数中是顺序执行的,line
和eol
在赋值lines
时仍然是null
。
要解决此问题,请将 line
和 eol
定义为 lazy val
s 或 def
s,或者将它们放在代码中的 lines
之前。
解析器本身也存在一些问题。默认情况下,Scala 解析器会自动忽略所有空格,包括 EOL。考虑到没有任何标志的正则表达式 .*
不 包含 EOL,line
自然意味着 "the whole line until the line break",因此您根本不必分析 EOL .
其次,定义的lines
解析器是贪婪的。它会愉快地消耗一切,包括最后的 ##
。例如,要使其在 end
之前停止,您可以使用 not
组合器。
经过所有更改后,解析器如下所示:
object MyParser extends RegexParsers {
val begin: Parser[String] = "BEGIN"
val line: Parser[String] = """.+""".r
val lines: Parser[Seq[String]] = rep(not(end) ~> line)
val end: Parser[String] = "##"
val document: Parser[Seq[String]] =
begin ~> lines <~ end
}
您也可以覆盖跳过空格的行为,并手动分析所有空格。这包括 BEGIN
之后和 ##
:
之后的空格
object MyParser extends RegexParsers {
override def skipWhitespace = false
val eol: Parser[Any] = "\n" | "\r\n" | "\r"
val begin: Parser[String] = "BEGIN" <~ eol
val line: Parser[String] = """.*""".r
val lines: Parser[Seq[String]] = rep(not(end) ~> line <~ eol)
val end: Parser[String] = "##"
val document: Parser[Seq[String]] =
begin ~> lines <~ end <~ whiteSpace
}
请注意,line
在这里定义为 .*
而不是 .+
。这样,如果输入中有任何空行,解析器就不会失败。
我正在尝试解析以下文档:
val doc = """BEGIN
A Bunch
Of Text
With linebreaks
##
"""
这里的想法是,当我看到 ##
单独一行时,我应该认为解析结束。
我试过,使用下面的代码解析这个文档:
object MyParser extends RegexParsers {
val begin: Parser[String] = "BEGIN"
val lines: Parser[Seq[String]] = repsep(line, eol)
val line: Parser[String] = """.+""".r
val eol: Parser[Any] = "\n" | "\r\n" | "\r"
val end: Parser[String] = "##"
val document: Parser[Seq[String]] =
begin ~> lines <~ end
}
MyParser.parseAll(MyParser.document, doc)
然而,当我尝试执行此操作时(在 Annonite 脚本中),我得到以下信息:
java.lang.NullPointerException
scala.util.parsing.combinator.Parsers$class.rep1sep(Parsers.scala:771)
ammonite.$file.vtt$minusparser$MyParser$.rep1sep(vtt-parser.sc:3)
scala.util.parsing.combinator.Parsers$class.repsep(Parsers.scala:687)
ammonite.$file.vtt$minusparser$MyParser$.repsep(vtt-parser.sc:3)
ammonite.$file.vtt$minusparser$MyParser$.<init>(vtt-parser.sc:5)
ammonite.$file.vtt$minusparser$MyParser$.<clinit>(vtt-parser.sc)
ammonite.$file.vtt$minusparser$.<init>(vtt-parser.sc:22)
ammonite.$file.vtt$minusparser$.<clinit>(vtt-parser.sc)
谁能看出我错在哪里?
错误的原因是line
和eol
被定义为正常的class字段val
,但是在lines
中使用了它们在他们的定义之前。给class字段赋值的代码在构造函数中是顺序执行的,line
和eol
在赋值lines
时仍然是null
。
要解决此问题,请将 line
和 eol
定义为 lazy val
s 或 def
s,或者将它们放在代码中的 lines
之前。
解析器本身也存在一些问题。默认情况下,Scala 解析器会自动忽略所有空格,包括 EOL。考虑到没有任何标志的正则表达式 .*
不 包含 EOL,line
自然意味着 "the whole line until the line break",因此您根本不必分析 EOL .
其次,定义的lines
解析器是贪婪的。它会愉快地消耗一切,包括最后的 ##
。例如,要使其在 end
之前停止,您可以使用 not
组合器。
经过所有更改后,解析器如下所示:
object MyParser extends RegexParsers {
val begin: Parser[String] = "BEGIN"
val line: Parser[String] = """.+""".r
val lines: Parser[Seq[String]] = rep(not(end) ~> line)
val end: Parser[String] = "##"
val document: Parser[Seq[String]] =
begin ~> lines <~ end
}
您也可以覆盖跳过空格的行为,并手动分析所有空格。这包括 BEGIN
之后和 ##
:
object MyParser extends RegexParsers {
override def skipWhitespace = false
val eol: Parser[Any] = "\n" | "\r\n" | "\r"
val begin: Parser[String] = "BEGIN" <~ eol
val line: Parser[String] = """.*""".r
val lines: Parser[Seq[String]] = rep(not(end) ~> line <~ eol)
val end: Parser[String] = "##"
val document: Parser[Seq[String]] =
begin ~> lines <~ end <~ whiteSpace
}
请注意,line
在这里定义为 .*
而不是 .+
。这样,如果输入中有任何空行,解析器就不会失败。