2015-07-10 57 views
4

我正在尋找使用Scala的Parser Combinators來解析字符串(沒有換行符,人爲的例子)。將解析器組合器應用於案例分類

該字符串由許多不同的部分組成,我想單獨提取並填充案例類。

case class MyRecord(foo: String, bar: String, baz: String, bam: String, bat: String) 

object MyParser extends scala.util.parsing.combinator.RegexParsers { 

    val foo: Parser[String] = "foo" 
    val bar: Parser[String] = "bar" 
    val baz: Parser[String] = "baz" 
    val bam: Parser[String] = "bam" 
    val bat: Parser[String] = "bat" 

    val expression: Parser[MyRecord] = 
    foo ~ bar ~ baz ~ bam ~ bat ^^ { 
     case foo ~ bar ~ baz ~ bam ~ bat => MyRecord(foo, bar, baz, bam, bat) 
    } 

} 

這工作得很好,但有沒有辦法將匹配結果的部分直接應用到case類而不解構?

val expression: Parser[MyRecord] = 
    foo ~ bar ~ baz ~ bam ~ bat ^^ MyRecord 

更多信息:我解析字符串是相當漫長和複雜的(實際上,這是一個整個文件完全長複雜的字符串的),所以更改爲正則表達式是出了問題。

回答

5

這可能與Shapeless2庫。對於給定的:

import shapeless._ 
object f extends Poly2 { 
    implicit def parser[T, U <: HList] = 
    at[Parser[T], Parser[U]]{(a, b) => 
    for {aa <- a; bb <- b} yield aa :: bb 
    } 
} 

val p: Parser[Record] = (foo :: bar :: car :: HNil) 
    .foldRight(success(HNil))(f).map(Generic[Record].from) 

結果:

scala> parseAll(p, "foo bar car").get 
res50: Record = Record(foo,bar,car) 

附註:

object MyParser extends scala.util.parsing.combinator.RegexParsers 
import MyParser._  

val foo: Parser[String] = "foo" 
val bar: Parser[String] = "bar" 
val car: Parser[String] = "car" 

case class Record(f: String, b: String, c: String) 
使用通用 foldRight這一翻譯的 ~

您可以將解析器內置scala功能的問題在於,他們構建了基於~的二叉樹,這種二叉樹很難遍歷並壓扁爲元組。 Shapeless解決了這個問題 - 它有它自己的基於::的二叉樹,名爲HList,它是相似的但有趣的操作,如轉換爲元組或大綱類(可能基於宏)。在這個例子中,我使用foldLeft來構建Shapeless-hlist和for-comprehension(在解析器上擴展爲flatMap)以組合解析器,因爲它們具有monadic特性。在沒有形狀的情況下,您必須將foldLeft的處理程序定義爲一組通用含義,它可以處理通用輸入(如TU)。

您可以重用我的f對象以類型安全的方式組合任何解析器(您可以在這裏組合不同的類型 - 這很好)。


二,少通用的,方法是:

implicit class as2[A, B](t: Parser[A ~ B]){ def ^^^^[T] (co: (A, B) => T) = t map {tt => val (a ~ b) = tt; co(a, b)} } 
implicit class as3[A, B, C](t: Parser[A ~ B ~ C]){ def ^^^^[T] (co: (A, B, C) => T) = t map {tt => val (a ~ b ~ c) = tt; co(a, b, c)} } 
... 
implicit class as21 ... 

用法:

scala> val p = foo ~ bar ~ car ^^^^ Record 
p: MyParser.Parser[Record] = Parser() 

scala> parseAll(p, "foo bar car").get 
res53: Record = Record(foo,bar,car) 

這不是很爽,但不需要外部庫。

+1

某處我有一個類型類的實現,它允許'foo〜bar ^^(t => as [MyRecord](t))'語法 - 我會試着去挖掘它。 –

+0

我把問題打開了幾天,看看是否有更多的答案進來。我想我更喜歡你的第二個答案,因爲它不依賴於外部庫。謝謝你的幫助! – adlawson

相關問題