プログラミングHaskell 8.6 パーサーの部品
述語pを満足する一文字用のパーサー
sat p
を定義する。
def sat(p: Char => Boolean): Parser[Char] = { item.flatMap(x => p(x) match { case true => succeed(x) case false => failure }) }
これを使えば、数字・アルファベットとかのパーサーも。
def digit: Parser[Char] = sat(_.isDigit) def lower: Parser[Char] = sat(_.isLower) def upper: Parser[Char] = sat(_.isUpper) def letter: Parser[Char] = sat(_.isLetter) def alphanum: Parser[Char] = sat(_.isLetterOrDigit) def char(x: Char): Parser[Char] = sat(_ == x)
実行。
println("sample9: " + parse(digit)("123")) println("sample10: " + parse(digit)("abc")) println("sample11: " + parse(char('a'))("abc")) println("sample12: " + parse(char('a'))("123"))
sample9: List((1,23)) sample10: List() sample11: List((a,bc)) sample12: List()
パーサーcharを使って string xs
を定義。
def string(s: String): Parser[String] = s.toList match { case Nil => succeed("") case x :: xs => for { _ <- char(x) _ <- string(xs.mkString) } yield (x :: xs).mkString }
実行。
println("sample13: " + parse(string("abc"))("abcdef")) println("sample14: " + parse(string("abc"))("ab1234"))
sample13: List((abc,def)) sample14: List()
次の二つのパーサー
many p
とmany1 p
は、パーサーpを失敗するまでできるだけ多く適用し、適用が成功した結果をリストにして返す。
相互再帰になってる。自分で発想できなそう・・・・
def many1[A](p: Parser[A]): Parser[List[A]] = for { v <- p vs <- many(p) } yield (v :: vs) def many[A](p: Parser[A]): Parser[List[A]] = many1(p) +++ succeed(Nil)
実行。
println("sample15: " + parse(many(digit))("123abc")) println("sample16: " + parse(many(digit))("abcdef")) println("sample17: " + parse(many1(digit))("abcdef"))
sample15: List((List(1, 2, 3),abc)) sample16: List((List(),abcdef)) sample17: List()
パーサー
many
とmany1
を使うと、「識別子(変数名)」のパーサーを定義できる。識別子は、小文字で始まり、0個以上のアルファベット文字か数字文字が続く。数字文字が一つ以上繰り返される「自然数」のパーサーや、空白文字やタブ文字、あるいは改行文字が一つ以上繰り返される「空白」のパーサーも定義できる。
本文では read
って関数が使われてるけど、これはキャストする関数っぽい。この(本文の)場合だと、型推論からIntってことがわかるからキャスト先は省略されてるのかな。。Scalaではxsは List[Char]
だから、 mkString
でStringにして、 toInt
でキャスト。
def ident: Parser[String] = for { x <- lower xs <- many(alphanum) } yield (x :: xs).mkString def nat: Parser[Int] = for { xs <- many1(digit) } yield xs.mkString.toInt def space: Parser[Unit] = for { _ <- many(sat(_.isSpaceChar)) } yield(())
実行。
println("sample18: " + parse(ident)("abc def")) println("sample19: " + parse(nat)("123 abc")) println("sample20: " + parse(space)(" abc"))
sample18: List((abc, def)) sample19: List((123, abc)) sample20: List(((),abc))
だんだんわかってきた!きがする!!!many, many1すごい。