基本的なパーサー
プログラミングHaskellより。
ここで、他のパーサーを構築するのに利用する基本的なパーサー三つを定義しよう。
ちょっと本の表記から変えます。
return
が予約語なので、メソッド名をreturn
->succeed
にします。String => List[Char]への変換は明示的に行う(Stringにunapplyメソッドがないからパターンマッチが行われてなさそう)*1
// def succeed[T]: T => Parser[T] // def failure[T]: Parser[T] // def item: Parser[Char] def succeed[T](t: T) = (inp: String) => Seq((t, inp)) def failure[T] = (inp: String) => Seq() def item = (inp: String) => inp.toList match { case List() => Seq() case x :: xs => Seq((x, xs.toString)) }
こんな感じだろうか.....
パーサーは関数なので、通常の関数適用を使って、パーサーを文字列へ適用できる。 しかし、パーサーの実現方法を抽象化して、独自の適用関数を定義するほうが望ましい
わからない!どうしてそっちのが望ましいんだろう。。
// def parse[T]: Parser[T] => String => Seq[(T, String)] def parse[T](p: Parser[T])(inp: String): Seq[(T, String)] = p(inp)
どう振る舞うのかの例を示す。
以下のように書いたScalaプログラムを動かしてみます。
object Main extends App { import Parsers._ println("sample1: " + parse(succeed(1))("abc")) println("sample2: " + parse(failure)("abc")) println("sample3: " + parse(item)("")) println("sample4: " + parse(item)("abc")) } object Parsers { type Parser[T] = (String) => Seq[(T, String)] def succeed[T](t: T) = (inp: String) => Seq((t, inp)) def failure[T] = (inp: String) => Seq() def item = (inp: String) => inp.toList match { case List() => Seq() case x :: xs => Seq((x, xs.mkString)) } def parse[T](p: Parser[T])(inp: String): Seq[(T, String)] = p(inp) }
結果。
sample1: List((1,abc)) sample2: List() sample3: List() sample4: List((a,bc))
早速心が折れてしまいそうだけどw、少しずつ続けよう。