将解析器组合器应用于案例 class
Apply parser combinator to case class
我正在考虑使用 Scala 的 Parser Combinators 来解析字符串(没有换行符,人为的示例)。
该字符串由许多不同的部分组成,我想分别提取这些部分并填充一个案例 class。
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)
}
}
这非常有效,但是有没有办法将匹配结果的部分直接应用于案例 class 而无需解构?
val expression: Parser[MyRecord] =
foo ~ bar ~ baz ~ bam ~ bat ^^ MyRecord
更多信息:我正在解析的字符串很长很复杂(实际上,它是一个充满长而复杂的字符串的整个文件)所以更改为正则表达式是不合适的问题。
Shapeless2 库是可能的。对于给定:
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
而不是 ~
:
组合解析器
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)
P.S。内置 scala 功能的问题在于它们构建了基于 ~
的类型化二叉树,这很难遍历和展平为元组。 Shapeless 解决了这个问题——它有自己的基于 ::
的二叉树,称为 HList
,它很相似但有有趣的操作,比如转换为元组或大小写 类(可能是基于宏的)。在这个例子中,我使用 foldLeft
构建 Shapeless-hlist 和 for-comprehension(在解析器上扩展为 flatMap
)来组合解析器,因为它们具有单子性质。在 shapeless 中,您必须将 foldLeft
的处理程序定义为一组通用隐式,可以处理通用输入(如 T
或 U
)。
您可以重复使用我的 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)
不是很酷,但不需要外部库。
我正在考虑使用 Scala 的 Parser Combinators 来解析字符串(没有换行符,人为的示例)。
该字符串由许多不同的部分组成,我想分别提取这些部分并填充一个案例 class。
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)
}
}
这非常有效,但是有没有办法将匹配结果的部分直接应用于案例 class 而无需解构?
val expression: Parser[MyRecord] =
foo ~ bar ~ baz ~ bam ~ bat ^^ MyRecord
更多信息:我正在解析的字符串很长很复杂(实际上,它是一个充满长而复杂的字符串的整个文件)所以更改为正则表达式是不合适的问题。
Shapeless2 库是可能的。对于给定:
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
而不是 ~
:
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)
P.S。内置 scala 功能的问题在于它们构建了基于 ~
的类型化二叉树,这很难遍历和展平为元组。 Shapeless 解决了这个问题——它有自己的基于 ::
的二叉树,称为 HList
,它很相似但有有趣的操作,比如转换为元组或大小写 类(可能是基于宏的)。在这个例子中,我使用 foldLeft
构建 Shapeless-hlist 和 for-comprehension(在解析器上扩展为 flatMap
)来组合解析器,因为它们具有单子性质。在 shapeless 中,您必须将 foldLeft
的处理程序定义为一组通用隐式,可以处理通用输入(如 T
或 U
)。
您可以重复使用我的 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)
不是很酷,但不需要外部库。