Monadic Parser Combinators
四角い車輪を再発明してしまった。
今後はParsecを使おうと思っていたのだが,Monadic Parser Combinatorsってどんな感じに実装するのかが気になってしまった。そこで,自分なりに考えてParsecのソースを見て確認しよう,そしてそれで終わり,という予定だったのだが,なんか自分でも書きたくなってしまったのだ。
module Parser where import Data.Char import Control.Monad newtype Parser a b = Parser ([a] -> [([a],b)]) instance Monad (Parser a) where Parser p >>= k = Parser (\xs -> let (xs',vs) = unzip $ p xs ms = map k vs in concat (map (\(Parser q,x) -> q x) (zip ms xs'))) return v = Parser (\xs -> [(xs,v)]) fail s = Parser (\_ -> []) instance MonadPlus (Parser a) where mzero = Parser (\_ -> []) Parser p `mplus` Parser q = Parser (\xs -> p xs ++ q xs) instance Functor (Parser a) where fmap f (Parser p) = Parser (\xs -> map (\(x,v) -> (x,f v)) (p xs)) infixr 1 <|> (<|>) :: Parser a b -> Parser a b -> Parser a b (<|>) = mplus runParser :: Parser a b -> [a] -> [b] runParser p xs = testParser p xs >>= return . snd testParser :: Parser a b -> [a] -> [([a],b)] testParser (Parser p) xs = p xs many :: Parser a b -> Parser a [b] many p = do { x <- p ; xs <- many p ; return (x:xs) } <|> return [] many1 :: Parser a b -> Parser a [b] many1 p = do { x <- p ; xs <- (many1 p <|> return []) ; return (x:xs) } bound :: (Int,Int) -> Parser a b -> Parser a [b] bound (0,0) p = return [] bound (0,max) p = do { x <- p ; xs <- bound (0,max-1) p ; return (x:xs) } <|> return [] bound (min,max) p = do { x <- p ; xs <- bound (min-1,max-1) p ; return (x:xs) } times :: Int -> Parser a b -> Parser a [b] times n p = bound (n,n) p empty :: b -> Parser a b empty v = Parser (\xs -> [(xs,v)]) satisfy :: (a -> Bool) -> Parser a a satisfy pred = Parser (\xs -> case xs of [] -> [] (x:xs) -> if pred x then [(xs,x)] else []) oneOf :: (Eq a) => [a] -> Parser a a oneOf s = let s' = map (\e -> satisfy (e ==)) s in foldr1 (<|>) s' char :: Char -> Parser Char Char char c = satisfy (c ==) space :: Parser Char Char space = satisfy isSpace alpha :: Parser Char Char alpha = satisfy isAlpha alphaNum :: Parser Char Char alphaNum = satisfy isAlphaNum digit :: Parser Char Char digit = satisfy isDigit digits :: Parser Char String digits = many1 digit int :: Parser Char Int int = do { ds <- digits ; return ((read ds)::Int) }
newtypeを使うのは初めて。以下はメモ。
dataとは違いコンストラクタはひとつしかもてない。そのためパターンマッチ自体にコストはかからないとのこと。また,コンストラクタのフィールドはひとつであり,それより少なくても多くてもいけない。
newtype T = C a
とした場合,Tはaの別名と考えられる。typeとの違いは,newtypeなら再帰的に定義できるということ。GHCなら"-XTypeSynonymInstances"を使えば,typeでもinstance化できるらしい。
Parsecとは違い"A<|>B"はAが消費しようがしまいがBは実行される。というか,Aが成功でも失敗でもBは実行される。なので,manyの結果は,
runParser (many (char 'a')) "aaaa" -- => ["aaaa","aaa","aa","a",""]
となる。通常は最長一致で使うことが多いので問題はそれほどないと思うのだがどうだろう。後で試してみるべきだろうなあ。
エラー処理に関しては一切考慮していないが,それについて考えるのもなかなかおもしろそうだ。エラー処理いれればペンダゴンくらいにはなるかな?