Scala: как совместить комбинаторы парсеров с разными объектами

Учитывая семейство объектов, реализующих компараторы парсеров, как я могу объединить парсеры? Поскольку Parsers.Parser является внутренним классом, а в Scala внутренними классами привязаны к внешнему объекту, история становится немного сложной.

Вот пример, который пытается объединить два парсера из разных объектов.

import scala.util.parsing.combinator._

class BinaryParser extends JavaTokenParsers {
  def anyrep: Parser[Any] = rep(any)
  def any: Parser[Any] = zero | one
  def zero: Parser[Any] = "0"
  def one: Parser[Any] = "1"
}

object LongChainParser extends BinaryParser {
  def parser1: Parser[Any] = zero~zero~one~one
}

object ShortChainParser extends BinaryParser {
  def parser2: Parser[Any] = zero~zero
}

object ExampleParser extends BinaryParser {
  def parser: Parser[Any] = (LongChainParser.parser1
    ||| ShortChainParser.parser2) ~ anyrep

  def main(args: Array[String]) {
    println(parseAll(parser, args(0) ))
  }
}

Это приводит к следующей ошибке:

<console>:11: error: type mismatch;
 found   : ShortChainParser.Parser[Any]
 required: LongChainParser.Parser[?]
         def parser: Parser[Any] = (LongChainParser.parser1 
           ||| ShortChainParser.parser2) ~ anyrep

Я нашел решение этой проблемы уже, но так как она была поднята недавно на scala -user ML (Проблема с вводом одного парсера в другой), возможно, стоит поставить его и здесь.

Ответы

Ответ 1

Быстрый ответ заключается в использовании trait вместо размещения парсеров в object s:

import scala.util.parsing.combinator._

trait BinaryParser extends JavaTokenParsers {
  def anyrep: Parser[Any] = rep(any)
  def any: Parser[Any] = zero | one
  def zero: Parser[Any] = "0"
  def one: Parser[Any] = "1"
}

trait LongChainParser extends BinaryParser {
  def parser1: Parser[Any] = zero~zero~one~one
}

trait ShortChainParser extends BinaryParser {
  def parser2: Parser[Any] = zero~zero
}

object ExampleParser extends LongChainParser with ShortChainParser  {
  def parser: Parser[Any] = (parser1 ||| parser2) ~ anyrep

  def main(args: Array[String]) {
    println(parseAll(parser, args(0) ))
  }
}

Поскольку операторы-комбайнаторы, такие как ~ и |, пишутся против внутреннего класса, эскалация ссылок парсера на уровень класса, говоря BinaryParser#Parser[_], не делает вам ничего хорошего. Использование признаков решает все проблемы внутри класса, так как Parser[Any] от LongChainParser и ShortChainParser теперь относится к внутреннему классу объекта ExampleParser.