Parsec --- 2.5 Sequences and separators (2)
many/many1はパースしたものを返しますが,パースしたものを返さないskipMany/skipMany1というパーサコンビネータも用意されています.
skipMany :: GenParser tok st a -> GenParser tok st () skipMany1 :: GenParser tok st a -> GenParser tok st () |haskell|< という型です.パーサを引数として何も返さないパーサを返します. >|| *Main> parseTest (skipMany1 letter >> many digit) "Haskell98" "98" *Main> parseTest (skipMany letter >> many digit) "98" "98" *Main> parseTest (skipMany1 letter >> many digit) "98" parse error at (line 1, column 1): unexpected "9" expecting letter
letterをskipMany/skipMany1でとばした後,digitを0回以上パースして,それを返すわけですが,最初はHaskellをスキップして``98''を,つぎは,0回以上のletterをスキップなのでなくてもよくて``98''を返します.最後はskipMany1なので,letterがなくてエラーになります.
さらに,特定のもので区切られたものをパースするためにsepBy/SepBy1,区切られているばかりではなく,それで終わっているものをパースするためにsepEndBy/SepEndBy1というパーサコンビネータも用意されています.
sepBy :: GenParser tok st a -> GenParser tok st sep -> GenParser tok st [a] sepBy1 :: GenParser tok st a -> GenParser tok st sep -> GenParser tok st [a] sepEndBy :: GenParser tok st a -> GenParser tok st sep -> GenParser tok st [a] sepEndBy1 :: GenParser tok st a -> GenParser tok st sep -> GenParser tok st [a]
「もの」をパースするパーサを第一引数に,「区切り」をパースするパーサを第二引数として,パースされた「もの」から構成されるリストを返すパーサを作ります.1がつついてるものは「ものが一個以上」,ついてないものは「ものが0個以上」を表します.
*Main> parseTest (sepBy (many1 letter) (char ',') ) "AB,CD" ["AB","CD"] *Main> parseTest (sepBy1 (many1 letter) (char ',') ) "AB,CD" ["AB","CD"]
この例では「letterが一回以上」という「もの」が`,'で区切られているものを想定しています.返されるのは「letterが一回以上」の部分からなるリストです.
*Main> parseTest (sepBy (many1 letter) (char ',') ) "" [] *Main> parseTest (sepBy1 (many1 letter) (char ',') ) "" parse error at (line 1, column 1): unexpected end of input expecting letter
空文字列に対しては,sepByでは0回以上なので,空リストが返されますが,sepBy1ではパースできないのでエラーになります.
sepEndBy/sepEndBy1はほとんどsepBy/sepBy1と同様ですが,「区切り」で終わる文字列に対する動作が異なります.sepEndBy/sepEndBy1は区切りで終わる文字列でもOKです.
*Main> parseTest (sepBy1 (many1 letter) (char ',') ) "ABCD,AAA," parse error at (line 1, column 10): unexpected end of input expecting letter *Main> parseTest (sepEndBy1 (many1 letter) (char ',') ) "ABCD,AAA," ["ABCD","AAA"]
最初の例は,sepBy1なので,`,'で終わっているにも関わらず,そのあとにmany1 letterを期待していますが,もう文字列の続きがないので,many1 letterがエラーを出しています(もし,many letterであったならばエラーはでず,["ABCD","AAA",""]が返されます).これをsepEndBy1に変えれば,``,''で終わっているのが認識されて,上のような結果になります(もし,many letterであったならば,終端とは認識されず,["ABCD","AAA",""]が返されます).したがって,つぎのようなことも起こります.
*Main> parseTest (sepEndBy (many1 letter) (char ',') ) "ABCD,AAA,98" ["ABCD","AAA"]
この場合,98の前の`,'でこのパーサがパースできる部分は終わっていて,98はそのまま残されていることになります.これから推測されるのは,sepEndBy p sepはsepByで動作していて,pが失敗したときには直前のsepを終端とみなしているということです.実際,
*Main> parseTest (sepEndBy (many1 letter) (char ',') >> many digit) "ABCD,AAA,98" "98"
という動作が観察されます.