2010-04-16 12 views
15

Dada una familia de objetos que implementan los combinadores de analizadores sintácticos, ¿cómo combino los analizadores sintácticos? Dado que Parsers.Parser es una clase interna, y en Scala inner classes are bound to the outer object, la historia se vuelve un poco complicada.Scala: Cómo combinar los combinadores de analizadores de diferentes objetos

Aquí hay un ejemplo que intenta combinar dos analizadores de diferentes objetos.

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

Esto da lugar a los siguientes errores:

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

he encontrado la solución a este problema ya, pero ya que se crió recientemente en Scala-usuario ML (Problem injecting one parser into another), es probablemente valga la pena ponerlo aquí también.

Respuesta

17

La respuesta rápida es utilizar los trait s en lugar de la celebración de los analizadores en 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))) 
    } 
} 

Debido a que los operadores de combinadores como ~ y | están escritos en contra de la clase interna, la escalada de las referencias del analizador a la clase -level diciendo BinaryParser#Parser[_] no te hace ningún bien. El uso de rasgos resuelve todos los problemas de la clase interna, ya que ambos Parser[Any] de LongChainParser y ShortChainParser ahora se refieren a la clase interna del objeto ExampleParser.

+1

Gracias por publicar esta pregunta y, por supuesto, ¡por responderla! Esto es exactamente lo que estaba buscando. –

Cuestiones relacionadas