我正在创建一个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的解析器组合库扩展错误的解析器,因此欢迎任何关于如何使用重要的新行字符创建语言定义的建议。
答案 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+""".r
或override
protected def handleWhiteSpace(...)
方法以正则表达式实现。这两个成员都在RegexParsers中进行创建,RegexParsers是JavaTokenParsers的基类。