如何创建一个解析器组合器,其中行结尾很重要?

时间:2010-03-04 20:54:23

标签: scala parsing parser-combinators

我正在创建一个DSL,并使用Scala的解析器组合库来解析DSL。 DSL遵循简单的类似Ruby的语法。源文件可以包含一系列看起来像这样的块:

create_model do
  at 0,0,0
end

线路结尾在DSL中很重要,因为它们被有效地用作语句终结符。

我编写了一个Scala解析器,如下所示:

class ML3D extends JavaTokenParsers {
  override val whiteSpace = """[ \t]+""".r

  def model: Parser[Any] = commandList
  def commandList: Parser[Any] = rep(commandBlock)
  def commandBlock: Parser[Any] = command~"do"~eol~statementList~"end"
  def eol: Parser[Any] = """(\r?\n)+""".r
  def command: Parser[Any] = commandName~opt(commandLabel)
  def commandName: Parser[Any] = ident
  def commandLabel: Parser[Any] = stringLiteral
  def statementList: Parser[Any] = rep(statement)
  def statement: Parser[Any] = functionName~argumentList~eol
  def functionName: Parser[Any] = ident
  def argumentList: Parser[Any] = repsep(argument, ",")
  def argument: Parser[Any] = stringLiteral | constant
  def constant: Parser[Any] = wholeNumber | floatingPointNumber
}

由于行结尾很重要,我覆盖whiteSpace以便它只将空格和制表符视为空格(而不是将新行视为空格,从而忽略它们)。

除了commandBlock的“结束”声明外,这是有效的。由于我的源文件包含一个尾随的新行,解析器会抱怨它只是end,但在end关键字后面有一个新行。

所以我将commandBlock的定义更改为:

def commandBlock: Parser[Any] = command~"do"~eol~statementList~"end"~opt(eol)

(也就是说,我在“结束”之后添加了一个可选的新行。)

但是现在,在解析源文件时,我收到以下错误:

[4.1] failure: `end' expected but `' found

认为这是因为,在它吸收尾随的新行之后,解析器遇到一个它认为无效的空字符串,但我不确定它为什么这样做。< / p>

有关如何解决此问题的任何提示?我可能会从Scala的解析器组合库扩展错误的解析器,因此欢迎任何关于如何使用重要的新行字符创建语言定义的建议。

2 个答案:

答案 0 :(得分:9)

我在两个方面都得到同样的错误,但我认为你误解了它。它的含义是期望end,但它已经到了输入的末尾。

正在发生的原因是end被读作一个陈述。现在,我确信有一个很好的解决方法,但我对Scala解析器的经验不足。似乎要走的路是将令牌解析器与扫描部分一起使用,但我无法想办法让标准令牌解析器不将换行视为空格。

所以,这是另一种选择:

import scala.util.parsing.combinator.JavaTokenParsers

class ML3D extends JavaTokenParsers {
  override val whiteSpace = """[ \t]+""".r
  def keywords: Parser[Any] = "do" | "end"
  def identifier: Parser[Any] = not(keywords)~ident

  def model: Parser[Any] = commandList
  def commandList: Parser[Any] = rep(commandBlock)
  def commandBlock: Parser[Any] = command~"do"~eol~statementList~"end"~opt(eol)
  def eol: Parser[Any] = """(\r?\n)+""".r
  def command: Parser[Any] = commandName~opt(commandLabel)
  def commandName: Parser[Any] = identifier
  def commandLabel: Parser[Any] = stringLiteral
  def statementList: Parser[Any] = rep(statement)
  def statement: Parser[Any] = functionName~argumentList~eol
  def functionName: Parser[Any] = identifier
  def argumentList: Parser[Any] = repsep(argument, ",")
  def argument: Parser[Any] = stringLiteral | constant
  def constant: Parser[Any] = wholeNumber | floatingPointNumber
}

答案 1 :(得分:0)

如果您需要更多控制权,那么您可以override protected val whiteSpace(正则表达式),其默认值为"""\s+""".roverride protected def handleWhiteSpace(...)方法以正则表达式实现。这两个成员都在RegexParsers中进行创建,RegexParsers是JavaTokenParsers的基类。