简体   繁体   中英

Scala: How to combine parser combinators from different objects

Given a family of objects that implement parser combinators, how do I combine the parsers? Since Parsers.Parser is an inner class, and in Scala inner classes are bound to the outer object , the story becomes slightly complicated.

Here's an example that attempts to combine two parsers from different objects.

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) ))
  }
}

This results to the following error:

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

I've found the solution to this problem already, but since it was brought up recently on scala-user ML ( Problem injecting one parser into another ), it's probably worth putting it here too.

The quick answer is to use the trait s instead of hosting the parsers in 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) ))
  }
}

Because the combinator operators like ~ and | are written against the inner class, escalating the parser references to class-level by saying BinaryParser#Parser[_] doesn't do you any good. Using traits solves all that inner-class issues since both Parser[Any] from LongChainParser and ShortChainParser now refer to the inner class of ExampleParser object.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM