Как мне выполнять не жадные повторяющиеся совпадения с комбинаторами scala-парсера?

#scala #parsing #parser-combinators

Вопрос:

С помощью комбинаторов scala-синтаксического анализатора, как я могу проанализировать 42 содержимое следующего документа?

 1 Turning and turning in the widening gyre
2 The falcon cannot hear the falconer;
3 Things fall apart; the centre cannot hold;
4 Mere anarchy is loosed upon the world,
5 The blood-dimmed tide is loosed, and everywhere
6 The ceremony of innocence is drowned;
7 The best lack all conviction, while the worst
8 Are full of passionate intensity.

42
 
 class PoemParser extends RegexParsers
{
    def textLine:  Parser[Unit] = ".*[^ ] .*n".r ^^^ {}
    def blankLine: Parser[Unit] = "\h*n".r      ^^^ {}
    def intLine:   Parser[Int]  = "\d n".r      ^^ { _.trim.toInt }
    def meaning:   Parser[Int]  = rep(textLine | blankLine) ~> intLine
}

object RunPoemParser extends PoemParser with App {
    val poem =
        """
          |1 Turning and turning in the widening gyre
          |2 The falcon cannot hear the falconer;
          |3 Things fall apart; the centre cannot hold;
          |4 Mere anarchy is loosed upon the world,
          |5 The blood-dimmed tide is loosed, and everywhere
          |6 The ceremony of innocence is drowned;
          |7 The best lack all conviction, while the worst
          |8 Are full of passionate intensity.
          |
          |42
          |""".stripMargin

    parse(meaning, poem) match {
        case Success(matched, _) => println(matched.toString)
        case Failure(msg, _)     => println("FAILURE: "   msg)
        case Error(msg, _)       => println("ERROR: "   msg)
    }
}
 

Проблема , конечно, в том, что textLine синтаксический анализатор также совпадает "42n" , поэтому он жадно поглощает каждую строку, прежде чем intLine у синтаксического анализатора появится шанс сопоставить ее.

В этом простом примере я могу создать специальный сопоставитель только для первых восьми строк (что-то вроде \d .*n".r ). Но как мне решить эту проблему в целом? Какую комбинацию guard not , ~! , или других операторов/методов я должен использовать , чтобы не жадно потреблять произвольное количество токенов, прежде чем сопоставлять тот, который я хочу?