tba
tba

Reputation: 6571

Discarding several literals in a scala parser combinator with ~> <~

I'm parsing a language with a parser-combinator in Scala, and I'd like to express the CFG rule

stmt -> if (stmt) {stmt}

Naturally, I wish to discard the "if(){}" string literals when constructing my result. The two ways I've considered are ugly.

Method (A) requires us to duplicate the position of the string literals inside the "case" statement:

lazy val stmt: PackratParser[Stmt] =
    (
      // other rules...
      | "if"~"("~stmt~")"~"{"~stmt~"}" ^^ { case _~_~s1~_~_~s2~_ ⇒ If(s1, s2) }
    )

Method (B) requires confusing parenthesis due to the precedence of ~ and ~>.

lazy val stmt: PackratParser[Stmt] =
    (
      // other rules...
      | ("if"~>"("~>stmt)~(")"~>"{"~>stmt<~"}") ^^ { case s1~s2 ⇒ If(s1, s2) }
    )

Ideally I'd like to discard the "(",")" literals without needing parenthesis in my Scala code. Is there a clean way to do this?

Upvotes: 3

Views: 137

Answers (1)

Travis Brown
Travis Brown

Reputation: 139038

The usual way to avoid precedence issues without lots of parentheses is to define some helpers, and that works here pretty nicely, to my eye:

val cond       = "(" ~> stmt <~ ")"
val thenClause = "{" ~> stmt <~ "}"
val ifStmt = "if" ~> cond ~ thenClause ^^ { case s1 ~ s2 => If(s1, s2) }

If this wasn't an option for some reason, I'd definitely avoid your first approach, and I'd adjust the second to make the precedence-managing parentheses match up more cleanly with the syntax—i.e., something like this:

"if" ~> ("(" ~> stmt <~ ")") ~ ("{" ~> stmt <~ "}") ^^ {
   case s1 ~ s2 => If(s1, s2)
}

This isn't great, but it's not absolutely unreadable, either.

Upvotes: 4

Related Questions