16

我正在创建一个 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)

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

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

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

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

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

4

2 回答 2

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
}
于 2010-03-05T02:28:20.217 回答
0

如果您需要比使用正则表达式轻松实现的更多控制,您可以使用默认为override的(a Regex)或方法。这两个成员都起源于 RegexParsers,它是 JavaTokenParsers 的基类。protected val whiteSpace"""\s+""".roverrideprotected def handleWhiteSpace(...)

于 2010-03-04T21:00:50.380 回答