Question

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?

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top