Parsecにちょっと触ってみる。

Parsecちょっと触ってみます。
パーサーコンビネータは、小さなパーサを組み合わせて大きなパーサを作っていきますが、まずは小さなパーサがどういうものか。

run :: Show a => Parser a -> String -> IO ()
run p input
        = case (parse p "" input) of
            Left err -> do{ putStr "parse error at "
                          ; print err
                          }
            Right x  -> print x

parse 関数は Text.ParserCombinators.Parsec で定義されている失敗したときは Left 正常終了したときにはパース結果を返す関数です。

-- Defined in Text.ParserCombinators.Parsec.Prim
parse :: GenParser tok () a -> SourceName -> [tok] -> Either ParseError a
>  parse (char 'a') "" "abcdef"
Right 'a'

>  parse (digit) "" "123abcdef"
Right '1'

>  parse (string "abc") "" "abcdef"
Right "abc"

>  parse (many1 digit) "" "123"
Right "123"

>  parse (many1 digit) "" "123abn"
Right "123"

>  parse (letter) "" "abcdef"
Right 'a'

>  parse (many letter) "" "abcdef"
Right "abcdef"

>  parse (letter) "" "123abcdef"
Left (line 1, column 1):
unexpected "1"
expecting letter

そして、小さなパーサを "<|>" により組み合わせていきます。

> parse (many (anyChar >>=(\c->return [c]))) "" "1bc"
Right ["1","b","c"]

> parse (many ((many1 digit) <|> (anyChar >>=(\c->return [c])))) "" "1bc"
Right ["1","b","c"]
> parse (many ((many1 digit) <|> (anyChar >>=(\c->return [])))) "" "abc1b23c"
Right ["","","","1","","23",""]

> parse (many ((many1 digit) <|> (many1 letter))) "" "abc1b23c"
Right ["abc","1","b","23","c"]

予測的なパーサ
(<|>) コンビネータは予測的なパーサ。

> :m + Text.ParserCombinators.Parsec
> parse p "" input

>let test = string "(a)"  <|> string "(b)"
parse test "" "(a)"               --=> Right "(a)"
parse test "" "(b)"
Left (line 1, column 1):
unexpected "b"
expecting "(a)"

(try p) というパーサは p が失敗したときには入力を全く消費しません。

>let test = try(string "(a)")  <|> string "(b)"
parse test "" "(a)"               --=> Right "(a)"
parse test "" "(b)"               --=> Right "(b)"

>let test = do{ try (string "(a"); char ')'; return "(a)" }  <|> string "(b)"
parse test "" "(a)"               --=> Right "(a)"
parse test "" "(b)"               --=> Right "(b)"

対応するカッコのネストする深さを、対応するカッコがなければ0を返すパーサ。
パースしたことによる生まれた意味を返している。

> let nesting = do{ char '(' ; n <- nesting ; char ')' ; m <- nesting ; return (max (n+1) m) } <|> return 0
> parse nesting "" ""
Right 0
> parse nesting "" "()"
Right 1
> parse nesting "" "((()))"
Right 3
> parse nesting "" "()((()))(())"
Right 3
  • many と many1 の違い。many は 0 以上にマッチし、 many1 は1以上にマッチする。
> parse (many letter) "err:Not letter" "123" -- > Right ""

> parse (many1 letter) "err:Not letter" "123"
-- > 
Left "err:Not letter" (line 1, column 1):
unexpected "1"
expecting letter

skipMany は一致した文字を 0 以上に読みとばし、skipMany1 は1一致した文字を 0 以上に読みとばす。

> let sp0 = skipMany space
> let sp1 = skipMany1 space
> parse sp0 "" "    "              --=> Right ()
> parse sp1 "" "    "              --=> Right ()
> parse sp0 "" ""                  --=> Right ()
> parse sp1 "" ""
Left (line 1, column 1):
unexpected end of input
expecting space