Joyfulvillage
Joyfulvillage

Reputation: 557

Scala JavaTokenParsers dynamic parsing

I am trying to use JavaTokenParser to parse a number of repeat of a string that reads the size from a previous token, i.e.

list-name:5
ABCDE

second-list-name:2
AB  //<--the length of the string determines by the value at the token before

so the repN would be determined by the token I after the list name

def body = (listname <~ ":") ~ (numOfRepeat <~ LF) ~ repN(?, char)
def char = """[A-Z]""".r

Is any trick I could pass the just-passed-token (numOfRepeat) as Int value to the next parser (repN)?

Thanks!

Upvotes: 2

Views: 984

Answers (1)

Travis Brown
Travis Brown

Reputation: 139048

Yes!—this is exactly what monadic parsing allows you to do: to have subsequent parsing depend on earlier results. For example:

import scala.util.parsing.combinator._

object parser extends JavaTokenParsers {
  override val skipWhitespace = false

  val lengthAndBody: Parser[String] = for {
    _ <- ident
    _ <- literal(":")
    n <- decimalNumber
    _ <- literal("\\n")
    body <- repN(n.toInt, "[A-Z]".r)
  } yield body.mkString

  def apply(in: String) = parseAll(lengthAndBody, in)
}

And then:

scala> parser("""listName:5\nABCDE""")
res5: parser.ParseResult[String] = [1.18] parsed: ABCDE

But:

scala> parser("""listName:5\nABCD""")
res6: parser.ParseResult[String] =
[1.17] failure: string matching regex `[A-Z]' expected but end of source found

listName:5\nABCD
                ^

And:

scala> parser("""listName:5\nABCDEF""")
res7: parser.ParseResult[String] =
[1.18] failure: string matching regex `\z' expected but `F' found

listName:5\nABCDEF
                 ^

You could also use the ~>, ^^, etc. operators together with >>, which is an alias for flatMap, but I personally find the for-comprehension a lot clearer in this case.

Upvotes: 6

Related Questions