Skip to content

Instantly share code, notes, and snippets.

@tiqwab
Last active December 20, 2016 01:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tiqwab/0c9955dac758f98632b2efcc62906414 to your computer and use it in GitHub Desktop.
Save tiqwab/0c9955dac758f98632b2efcc62906414 to your computer and use it in GitHub Desktop.
{-# LANGUAGE FlexibleContexts #-}
import Control.Monad
import Text.Parsec
import Text.ParserCombinators.Parsec hiding (try)
{-
Parsecパッケージの`manyTill`, `notFollowedBy`に関して
-}
spaceChar :: Stream s m Char => ParsecT s st m Char
spaceChar = char ' ' <|> char '\t'
skipSpaces :: Stream s m Char => ParsecT s st m ()
skipSpaces = skipMany spaceChar
blankline :: Stream s m Char => ParsecT s st m Char
blankline = skipSpaces >> newline
blanklines :: Stream s m Char => ParsecT s st m String
blanklines = many1 blankline
---
-- `eof`はinputの終わりとして使うことができる。
-- `manyTill p end`のパースが成功するのは0以上のpのパースが成功した直後にendのパースが成功したとき。
{-
manyTill :: (Stream s m t) => ParsecT s u m a -> ParsecT s u m end -> ParsecT s u m [a]
manyTill p end = scan
where
scan = do{ end; return [] }
<|>
do{ x <- p; xs <- scan; return (x:xs) }
-}
-- parseTest readCsv "col,coll,colll"
-- -> ["col","coll","colll"]
readCsv :: Stream s m Char => ParsecT s st m [String]
readCsv = manyTill (try ( many1 letter <* char ',')
<|> many1 letter)
eof
---
-- `notFollowedBy p`は単体で使われるとその名前と挙動が一致せず少し理解しづらい。
-- pのパースが失敗したとき、`notFollowedBy`としてのパースは成功する。また、その際にinputは消費されない。
{-
notFollowedBy :: (Stream s m t, Show a) => ParsecT s u m a -> ParsecT s u m ()
notFollowedBy p = try (do{ c <- try p; unexpected (show c) }
<|> return ()
)
-}
-- parseTest parseSample "abc123d456efg\n"
-- -> ["abc","123","d","456","efg"]
parseSample :: Stream s m Char => ParsecT s st m [String]
parseSample = many (notFollowedBy closingSample >> contentSample)
where closingSample = blanklines
contentSample = many1 letter <|> many1 digit
main = return ()
import Text.Parsec
import Text.ParserCombinators.Parsec hiding (try)
{-
Basics of `Parsec` package.
-}
{-
data ParsecT s u m a
s: stream type
u: user state type
m: underlying monad
a: return type
type Parsec s u = ParsecT s u Identity
-}
{-
type GenParser tok st = Parsec [tok] st
type CharParser st = GenParser Char st
-}
-- This CharParser parses stream of `Char` and return `String`.
-- parse paren1 "paren1" "(abc)"
-- -> Right "abc"
-- parse paren1 "paren1" "abc"
-- -> Left "paren1" (line 1, column 1):
-- -> unexpected "a"
-- -> expecting "("
paren1 :: CharParser () String
paren1 = do char '('
x <- many letter
char ')'
return x
-- Applicative style of paren1.
paren2 :: CharParser () String
paren2 = char '(' *> many letter <* char ')'
-- Use `between`.
-- parse paren3 "paren3" "( 123 )"
-- -> Right 123
paren3 :: CharParser () Int
paren3 = read <$> between (char '(') (char ')') (spaces *> many digit <* spaces)
-- Choose parser by `<|>`. Note that "aa" cannot be parsed.
-- parse parseWithChoice "" "ab"
-- -> Right "ab"
-- parse parseWithChoice "" "cc"
-- -> Left (line 1, column 1):
-- -> unexpected "c"
-- parse parseWithChoice "" "aa"
-- -> Left (line 1, column 1):
-- -> unexpected "a"
parseWithChoice :: CharParser () String
parseWithChoice = string "ab"
<|> string "aa"
<?> "parseWithChoice"
-- Use `try` to backtrack when a chosen parser fails.
-- parse parseWithTry "" "ab"
-- -> Right "ab"
-- parse parseWithTry "" "aa"
-- -> Right "aa"
parseWithTry :: CharParser () String
parseWithTry = try (string "ab")
<|> string "aa"
<?> "parseWithTry"
-- Keep State during parse.
-- Text.Parsec.runParser parseWithState 3 "" "2,1"
-- -> Right 3
-- Text.Parsec.runParser parseWithState 4 "" "2,1"
-- -> Right 0
parseWithState :: CharParser Int Int
parseWithState = do x <- getState
cols <- many alphaNum `sepBy` char ','
setState (length cols)
modifyState (2 *)
y <- getState
if x < y
then return $ sum (map read cols)
else return 0
main = return ()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment