前回までに出てきた基本的なパーサをくっつけることで複雑なパーサが書けるわけですが、 今回はそのような連接とかを手助けしてくれるパーサコンビネータを紹介?します。
Kern のパーサコンビネータはシーケンス化、反復、選択、それと括弧の対応とかの面倒を見てくれるみたいです。
例のごとく kern をロード。
(use 'blancas.kern.core)
skip-ws は入力のはじめにあるすべての空白をスキップ。
doc.core> (run (skip-ws letter) "\t \n x") \x nil
<|> はあたえられたパーサを成功するまで試す。すべてのパーサが失敗するか、入力を消費するパーサが失敗すると全体として失敗する。
doc.core> (run (<|> digit letter) "x") \x nil doc.core> (run (<|> digit letter) "9") \9 nil doc.core> (run (<|> digit letter tab) "?") line 1 column 1 unexpected \? expecting digit, letter or tab nil
で、 >> は1つ以上のパーサを適用する。最後のパーサの結果だけを返す。
doc.core> (run (>> digit digit letter) "91x") \x nil
<< は1つ以上のパーサを適用する。最初のパーサの結果だけを返す。
doc.core> (run (<< upper (sym* \-) digit digit) "F-16") \F nil
<$> は与えられたパーサを入力に適用して、そのあとパースした結果に対して与えられた関数を適用、その結果を返す。
doc.core> (run (<$> #(* % %) dec-num) "12") 144 nil
<*> は2つ以上のパーサを適用して、それらの結果をベクタにして返す。
doc.core> (run (<*> dec-num tab oct-num tab hex-num) "737\t747\tA340") [737 \tab 487 \tab 41792] nil
<:> はバックトラックするパーサらしい。もし失敗しても消費した入力は回復。
doc.core> (:input (parse (<:> float-num) "5005.a")) (\5 \0 \0 \5 \. \a)
many は入力に対しパーサを0回以上、失敗するまで適用。many1 は1回以上で、最低1回生功しないと失敗。
doc.core> (run (many letter) "123") [] nil doc.core> (run (many letter) "xyz") [\x \y \z] nil doc.core> (run (many1 letter) "xyz") [\x \y \z] nil doc.core> (run (many1 letter) "123") line 1 column 1 unexpected \1 expecting letter nil
optional はパーサが成功するか消費を行わかなった場合に全体として成功する。
doc.core> (run (<+> (optional (sym* \-)) dec-num) "-45") "-45" nil doc.core> (run (<+> (optional (sym* \-)) dec-num) "45") "45" nil doc.core> (run (<+> (optional float-num) letter) "45.A") line 1 column 4 unexpected \A expecting digit nil
option は与えられたパーサを適用する。もし入力を消費せずに失敗したら、与えられた値を結果として返すっぽい。
doc.core> (run (option 3.1416 float-num) "foo") 3.1416 nil
skip は1つ以上のパーサを取ってそれらの結果をすべて飛ばす。結果として nil を返す。
doc.core> (run (skip letter digit letter digit) "A5E8") nil nil
skip-many はパーサを0回以上適用し結果を飛ばす。nil を返す。skip-many は1回以上。
doc.core> (run (skip-many letter) "xyz") nil nil doc.core> (run (skip-many1 letter) "xyz") nil nil doc.core> (run (skip-many1 letter) "123") line 1 column 1 unexpected \1 expecting letter nil
sep-by は与えられたパーサによって分けたものに対して別のパーサを0回以上適用する。sep-by1 は1回以上。
doc.core> (run (sep-by (sym* \&) (many1 letter)) "a&bb&ccc&ddd") [[\a] [\b \b] [\c \c \c] [\d \d \d]] nil
end-by は与えられたパーサによって別れる/終わるものに対して別のパーサを0回以上適用する。end-by1 は1回以上。
doc.core> (run (end-by (sym* \&) (many1 letter)) "a&bb&ccc&") [[\a] [\b \b] [\c \c \c]] nil
between は1,2つ目のパーサの間のちに対して3つ目のパーサを適用する。
doc.core> (run (between (sym \() (sym \)) dec-num) "(1984)") 1984 nil
<+> はひとつ以上のパーサをとる。で、結果を潰してくっつけて、文字列にする。
doc.core> (run (<+> letter (many alpha-num)) "a177") "a177" nil