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
.