[Int]をparsecでパースする
>>> parse (many even') "numbers" [0,0,2,4,9999]
Right [0,0,2,4]
>>> parse (many (zero <|> odd') >> eof) "numbers" [0,1,1,3,5,8]
Left "numbers" (line 1, column 6):
unexpected 8
expecting zero, odd or end of input
[Int]
だとおふざけだが、複雑な何かだと便利かもしれない1
Monad m => Stream [tok] m tok
とあるので、list
に包めば何でもparse可能に見える
しかしStream s m Char =>
制約の代わりに(Stream s m tok, Eq tok) =>
なものが見当たらない
標準のsatisfy
さえ:: Stream s m Char => (Char -> Bool) -> ParsecT s u m Char
と、Char
しか受け入れてくれない2
仕方がないので仕方なくなかった(追記)satisfy
を再定義する
updatePos :: SourcePos -> Column -> SourcePos
updatePos p n = setSourceColumn p (sourceColumn p + n)
satisfy' :: (Stream s m t, Show t) => (t -> Bool) -> ParsecT s u m t
satisfy' f = tokenPrim show
(\ pos _ _ -> updatePos pos 1)
(\ c -> if f c then Just c else Nothing)
Text.Parsec.Char
のほぼ全ての関数3はsatisfy
経由で定義されているので、
Text.Parsec.Int
を作ることが可能になる
zero :: (Num a, Eq a, Stream s m a, Show a) => ParsecT s u m a
zero = satisfy' (== 0) <?> "zero"
even', odd' :: (Integral a, Stream s m a, Show a) => ParsecT s u m a
even' = satisfy' even <?> "even"
odd' = satisfy' odd <?> "odd"
参考
追記
定義されてた
Text.Parsec.Combinator.anyToken
anyToken :: (Stream s m t, Show t) => ParsecT s u m t
なので
satisfy' f = try $ lookAhead (anyToken >>= (\ x -> if f x then return x else unexpected $ show x))
としても良い