Parsec 入門


Parsec でユーザ用状態を使う

ユーザ用状態

Parsec モナドには、ユーザが状態を使える機能がある。ユーザ用の状態を操作する関数は putState, getState, modifyState の3つだ。それぞれ State モナドの put, get, modify に対応している。
Prelude Text.Parsec> :t putState
putState :: Monad m => u -> ParsecT s u m ()
Prelude Text.Parsec> :t getState
getState :: Monad m => ParsecT s u m u
Prelude Text.Parsec> :t modifyState
modifyState :: Monad m => (u -> u) -> ParsecT s u m ()

runP 関数

これらの関数をテストするのには parseTest 関数は使えない。parseTest ではモナドのユーザ状態は使わない設定になっているからだ。そのかわり、runP 関数を使うようになっている。runP 関数の第2引数はユーザ状態の初期値だ。

Prelude Text.Parsec> :t parseTest
parseTest
:: (Show a, Stream s Data.Functor.Identity.Identity t) =>
Parsec s () a -> s -> IO ()
Prelude Text.Parsec> :t runP
runP
:: Stream s Data.Functor.Identity.Identity t =>
Parsec s u a -> u -> SourceName -> s -> Either ParseError a

getState の動作を確認してみた。

Prelude Text.Parsec> runP (getState) "hello" "SourceName" "abc" Right "hello"

State モナドのときと同じように状態をスタックとして使ってみる。

Prelude Text.Parsec> pop = getState >>= \xs-> putState (tail xs) >>
 return (head xs)
Prelude Text.Parsec> push x = getState >>= \xs-> putState (x:xs)
Prelude Text.Parsec> runP (push "hello" >> pop) [] "SourceName" "abc"
Right "hello"

マッチした文字数のカウントもできる。

Prelude Text.Parsec> runP (many (char 'a' >> modifyState (+1)) >> getState)
 0 "SourceName" "aaaaa"
Right 5

ユーザ状態を実際には何に使うのかは謎だが、Applicative を調べたときにも「何に使うの?」状態だったので、きっと面白い使いみちがあるのだろう。