mip*_*adi 16 parsing scala parser-combinators
我正在创建一个DSL,并使用Scala的解析器组合库来解析DSL.DSL遵循简单的类似Ruby的语法.源文件可以包含一系列看起来像这样的块:
create_model do
at 0,0,0
end
Run Code Online (Sandbox Code Playgroud)
线路结尾在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
}
Run Code Online (Sandbox Code Playgroud)
由于行结尾很重要,我覆盖whiteSpace
它以便它只将空格和制表符视为空格(而不是将新行视为空格,从而忽略它们).
这是有效的,除了"结束"声明commandBlock
.由于我的源文件包含一个尾随的新行,因此解析器会抱怨它只是一个end
但在end
关键字后面有一个新行.
所以我把commandBlock
定义改为:
def commandBlock: Parser[Any] = command~"do"~eol~statementList~"end"~opt(eol)
Run Code Online (Sandbox Code Playgroud)
(也就是说,我在"结束"之后添加了一个可选的新行).
但是现在,在解析源文件时,我收到以下错误:
[4.1] failure: `end' expected but `' found
Run Code Online (Sandbox Code Playgroud)
我认为这是因为,在它吸收尾随的新行之后,解析器遇到一个它认为无效的空字符串,但我不确定它为什么这样做.
有关如何解决此问题的任何提示?我可能会从Scala的解析器组合库中扩展错误的解析器,因此任何关于如何使用重要的新行字符创建语言定义的建议也是受欢迎的.
我在两个方面都得到了同样的错误,但我认为你误解了它.它的意思是它期待一个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
}
Run Code Online (Sandbox Code Playgroud)