Как я могу создать комбинатор парсера, в котором конечные строки значительны?
Я создаю 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
так, чтобы он обрабатывал пробелы и вкладки как пробельные символы (вместо того, чтобы обрабатывать новые строки как пробелы и, таким образом, игнорировать их).
Это работает, за исключением оператора "end" для commandBlock
. Поскольку мой исходный файл содержит завершающую новую строку, синтаксический анализатор жалуется, что он ожидал только end
, но получил новую строку после ключевого слова end
.
Итак, я изменил определение commandBlock
на следующее:
def commandBlock: Parser[Any] = command~"do"~eol~statementList~"end"~opt(eol)
(То есть, я добавил дополнительную строку после "end" ).
Но теперь, когда синтаксический анализ исходного файла, я получаю следующую ошибку:
[4.1] failure: `end' expected but `' found
Я думаю, что это связано с тем, что после того, как он сосет его в завершающей новой строке, синтаксический анализатор встречает пустую строку, которая, по ее мнению, является недопустимой, но я не уверен, почему она это делает.
Любые советы о том, как это исправить? Я мог бы пропустить неправильный синтаксический анализатор из Scala библиотеки комбинаторов парсеров, поэтому любые предложения о том, как создать определение языка со значительными новыми символами строки, также приветствуются.
Ответы
Ответ 1
Я получаю ту же ошибку в обоих направлениях, но я думаю, что вы неправильно интерпретируете ее. Говорят, что он ожидает 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
}
Ответ 2
Вы можете либо override
protected val whiteSpace
(Regex), по умолчанию это """\s+""".r
или override
метод protected def handleWhiteSpace(...)
, если вам нужно больше контроля, чем это легко достигается с помощью регулярного выражения. Оба эти элемента orginate в RegexParsers, который является базовым классом для JavaTokenParsers.