2012-03-07 24 views
11

Napisałem parser z biblioteki kombinatorycznej. Chcę ogólnej funkcji, która przekształca dowolny rozmiar gniazda ~ w listę. Jak to zrobić ?Scala: Sparuj parseresult (~) z parsera kombinatorów na List?

Oto mój przykład parsera, którego używam (mój prawdziwy parser ma bardzo długi łańcuch ~ więc chcę uniknąć mojego obecnego rozwiązania, które jest w komentarzu poniżej).

object CombinatorParser extends RegexParsers { 

    lazy val a = "a" 
    lazy val b = "b" 
    lazy val c = "c" 
    lazy val content = a ~ b ~ c // ^^ {case a~b => a::b::c::Nil work but I want something more general that work for any ~ length. 
} 

object CombinatorTesting { 

    def main(args:Array[String]) { 
    val testChar = "abc" 
    val output = CombinatorParser.parseAll(CombinatorParser.content, testChar) 
    println(output) // ((a~b)~c) but I want List(a,b,c) 
    } 
} 
+0

Nie sądzę, że to możliwe. Nie możesz podzielić swoich łańcuchów na mniejsze kawałki? Co dokładnie próbujesz zrobić? Może jeśli podasz trochę więcej kontekstu, ktoś ma lepsze rozwiązanie. – drexin

Odpowiedz

19

Jest to dobry (i dość prosty) wniosek o rodzaju ogólnych technik programowania wskazanym w shapeless.

Biorąc swoją definicję,

object CombinatorParser extends RegexParsers { 
    lazy val a = "a" 
    lazy val b = "b" 
    lazy val c = "c" 
    lazy val content = a ~ b ~ c 
} 

Możemy rekurencyjnie określić klasę typu, który będzie go wyprostować za wyniki w następujący sposób,

import CombinatorParser._ 

najpierw zdefiniować cechy, które (abstrakcyjnie) spłaszcza dowolny mecz M do List[String],

trait Flatten[M] extends (M => List[String]) { 
    def apply(m : M) : List[String] 
} 

T kura zapewniamy instancje klasy typu dla wszystkich kształtów M które Cię interesują: w tym przypadku, String, A ~ B i ParseResult[T] (gdzie A, B i T są wszystkie typy, dla których istnieją Flatten przypadki),

// Flatten instance for String 
implicit def flattenString = new Flatten[String] { 
    def apply(m : String) = List(m) 
} 

// Flatten instance for `A ~ B`. Requires Flatten instances for `A` and `B`. 
implicit def flattenPattern[A, B] 
    (implicit flattenA : Flatten[A], flattenB : Flatten[B]) = 
    new Flatten[A ~ B] { 
     def apply(m : A ~ B) = m match { 
     case a ~ b => flattenA(a) ::: flattenB(b) 
     } 
} 

// Flatten instance for ParseResult[T]. Requires a Flatten instance for T. 
implicit def flattenParseResult[T] 
    (implicit flattenT : Flatten[T]) = new Flatten[ParseResult[T]] { 
    def apply(p : ParseResult[T]) = (p map flattenT) getOrElse Nil 
} 

Wreszcie możemy zdefiniować funkcję wygoda w celu uproszczenia stosowania Flatten instancji do analizowania wyników,

def flatten[P](p : P)(implicit flatten : Flatten[P]) = flatten(p) 

a teraz jesteśmy gotowi iść,

val testChar = "abc" 
val output = parseAll(content, testChar) 
println(output)   // ((a~b)~c) but I want List(a, b, c) 

val flattenedOutput = flatten(output) 
println(flattenedOutput) // List(a, b, c) 
6

Jeśli wolisz rozwiązanie bez programowania generycznego ...

def flatten(res: Any): List[String] = res match { 
    case x ~ y => flatten(x) ::: flatten(y) 
    case None => Nil 
    case Some(x) => flatten(x) 
    case x:String => List(x) 
    } 

    val testChar = "abc" 
    val output = CombinatorParser.parseAll(CombinatorParser.content, testChar).getOrElse(None) 
    println(flatten(output)) 
+0

Dzięki za prostotę. Po prostu działa. – JulienD