Вы на правильном пути. В вашем парсере есть несколько проблем. Я опубликую исправленный код, затем объясню изменения.
import scala.util.parsing.combinator._
import scala.util.parsing.combinator.syntactical._
case class Book (name: String, isbn: String) {
def niceName = name + " : " + isbn
}
object BookParser extends StandardTokenParsers {
lexical.reserved += ("book","has","isbn")
def bookSpec: Parser[Book] = "book" ~ ident ~ "has" ~ "isbn" ~ ident ^^ {
case "book" ~ name ~ "has" ~ "isbn" ~ isbn => new Book(name, isbn) }
def parse (s: String) = {
val tokens = new lexical.Scanner(s)
phrase(bookSpec)(tokens)
}
def test (exprString : String) = {
parse (exprString) match {
case Success(book, _) => println("Book: " + book.niceName)
case Failure(msg, _) => println("Failure: " + msg)
case Error(msg, _) => println("Error: " + msg)
}
}
def main (args: Array[String]) = {
test ("book ABC has isbn DEF")
}
}
1. Возвращаемое значение парсера
Чтобы вернуть книгу из анализатора, вам нужно помочь выводителю типа. Я изменил определение функции bookSpec, чтобы оно было явным: оно возвращает Parser [Book]. То есть он возвращает объект, который является парсером для книг.
2. stringLit
Используемая вами функция stringLit взята из черты StdTokenParsers. stringLit - это функция, которая возвращает Parser [String], но соответствующий ей шаблон включает в себя двойные кавычки, которые большинство языков используют для разделения строкового литерала. Если вас устраивают двойные кавычки в вашем DSL, то вам нужен stringLit. В целях простоты я заменил stringLit на идент. id ищет идентификатор языка Java. Это не совсем правильный формат для ISBN, но он прошел ваш тестовый пример. : -)
Чтобы правильно сопоставить номера ISBN, я думаю, вам нужно использовать выражение регулярных выражений вместо идентификаторов.
3. Игнорировать левая последовательность
Ваш сопоставитель использовал строку ~> сумматоров. Это функция, которая принимает два объекта Parser [_] и возвращает Parser, который распознает оба по порядку, а затем возвращает результат с правой стороны. Используя целую цепочку из них, чтобы привести к вашему последнему stringLit, ваш синтаксический анализатор будет игнорировать все, кроме последнего слова в предложении. Это означает, что это также выбросило бы название книги.
Кроме того, когда вы используете ~> или <~, игнорируемые токены не должны появляться в вашем сопоставлении с образцом. </p>
Для простоты я изменил их все на простые функции последовательности и оставил дополнительные токены в сопоставлении с образцом.
4. Подходящие результаты
Метод тестирования должен соответствовать всем возможным результатам функции parse (). Итак, я добавил случаи Failure () и Error (). Кроме того, даже успех включает в себя и возвращаемое значение и объект Reader. Нас не волнует читатель, поэтому я просто использовал «_», чтобы игнорировать его в сопоставлении с образцом.
Надеюсь, это поможет!