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