Question

I've been playing around with Scala recently, and when working with parser combinators, I kind of had some problems extending them. Basically, what I'm trying to do is something like this.

import scala.util.parsing.combinator.RegexParsers

abstract class Statement

case object Foo extends Statement
case object Bar extends Statement
case object Baz extends Statement

class Program(statements: List[Statement])

class FooBarLanguageParser extends RegexParsers {
  def program: Parser[Program] = rep(statement) ^^ (v => new Program(List() ++ v))
  def statement: Parser[Statement] = foo|bar
  def foo: Parser[Statement] = "foo" ^^ (_ => Foo)
  def bar: Parser[Statement] = "bar" ^^ (_ => Bar)
}

trait BazStatement { this: FooBarLanguageParser =>
  override def statement: Parser[Statement] = baz|this.statement  // inifinite recursion
  def baz: Parser[Statement] = "baz" ^^ (_ => Baz)
}

object Main {
  def main(args: Array[String]) {
    val normalFooBar = new FooBarLanguageParser()
    val fooBarProgram = normalFooBar.parseAll(normalFooBar.program, "foo bar")
    val extendedFooBar = new FooBarLanguageParser() with BazStatement
    val extendedFooBarProgram = extendedFooBar.parseAll(extendedFooBar.program,
       "foo bar baz")
    println(fooBarProgram.successful)
    println(extendedFooBarProgram.successful)
  }
}

Here, I would like to call FooBarLanguageParser's statement method in BazStatement, but with the above code I'm recursively calling BazStatement's method, so no way it would work properly.

I already took a look at the answers around, and I know that I could extend the class and use abstract override, or somethink like this, but I don't think it would really make any sense here, as BazStatement is really an additional feature I want to mixin with my basic parser. I definitely don't want to have some random things like

object StrangeInheritance extends BazStatement

authorized in the code.

Would there be any idiomatic way to do this properly in Scala or do I need to think again about the general design of the app?

Was it helpful?

Solution

Use super to call supertype's method, also use traits to define top-level abstractions. This one runs ok:

import scala.util.parsing.combinator.RegexParsers

abstract class Statement

case object Foo extends Statement
case object Bar extends Statement
case object Baz extends Statement

class Program(statements: List[Statement])

trait FooBarLanguageParser extends RegexParsers {
  def program: Parser[Program] = rep(statement) ^^ (v => new Program(List() ++ v))
  def statement: Parser[Statement] = foo|bar
  def foo: Parser[Statement] = "foo" ^^ (_ => Foo)
  def bar: Parser[Statement] = "bar" ^^ (_ => Bar)
}

trait BazStatement extends FooBarLanguageParser {
  override def statement: Parser[Statement] = baz| super.statement  // inifinite recursion
  def baz: Parser[Statement] = "baz" ^^ (_ => Baz)
}

object Main {
  def main(args: Array[String]) {
    val normalFooBar = new FooBarLanguageParser() {}
    val fooBarProgram = normalFooBar.parseAll(normalFooBar.program, "foo bar")
    val extendedFooBar = new FooBarLanguageParser() with BazStatement
    val extendedFooBarProgram = extendedFooBar.parseAll(extendedFooBar.program,
       "foo bar baz")
    println(fooBarProgram.successful)
    println(extendedFooBarProgram.successful)
  }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top