[Haskell-cafe] Parsec monad transformer with IO?
all-lists at stefan-klinger.de
Thu Mar 18 12:37:22 EDT 2010
Nice, Parsec 3 comes with a monad transformer . So I thought I could
use IO as inner monad, and perform IO operations during parsing.
But I failed. Monad transformers still bend my mind. My problem: I
don't see a function to actually lift the IO operation into the
ParsecT. It should be something like
lift :: IO a -> ParsecT s u IO a
The following is a toy example, I just could not make something
smaller: Let's parse command line arguments (tokens are Strings), and
execute them while parsing.
> import Text.Parsec.Prim
> import Text.Parsec.Pos
> import Text.Parsec
> import System.Environment ( getArgs )
Command line interface parser (Clip) type: Inner monad IO, user state
u, tokens are Strings, returns something typed a.
> type Clip u a = ParsecT [String] u IO a
Source code position for command line arguments: The line is always 1,
column n represents the n-th command line argument.
> nextPos p _ _ = incSourceColumn p 1
Two primitive parsers, one for flags (with a dash) and one for other
clipFlag "x" accepts the command line flag "-x", and returns "x".
> clipFlag :: String -> Clip u String
> clipFlag x
> = tokenPrim
> (\y -> if '-':x == y then Just x else Nothing)
clipValue accepts any command line argument that does not tart with a
> clipValue :: Clip u String
> = tokenPrim id nextPos test
> test ('-':_) = Nothing
> test other = Just other
Now the test program:
Load files given on the command line, and sum up their word count,
until -p is given. -p prints the current word count and sets the
counter to zero. Further files may be processed then. At the end, show
the sum of all word counts.
Example: foo has 12 words, bar has 34 words:
main foo -p bar -p foo bar -p
Counted 12 words, reset counter.
Counted 34 words, reset counter.
Counted 46 words, reset counter.
Grand total: 92
> type CurrentCount = Int -- the user state used with Clip/ParsecT.
root implements the command line grammar (<filename>+ "-p")* and
returns the sum of all word counts.
> root :: Clip CurrentCount Int
> = do ns <- many (many1 loadFile >> printSize)
> return $ sum ns
Interprets each non-flag on the command line as filename, loads it,
counts its words, and adds the count to the state.
> loadFile :: Clip CurrentCount ()
> = do -- expect a filename
> filename <- clipValue
> -- load the file: IO
> content <- lift $ readFile filename
> -- add wordcount to state
> modifyState ((+) (length $ words content))
If -p shows up on the command line, print accumulated count, reset
counter to cero and return count for grand-total calculation.
> printSize :: Clip CurrentCount Int
> = do -- expect flag -p
> clipFlag "p"
> -- print current word count: IO
> n <- getState
> lift . putStrLn $ "Counted "++show n++" words, reset counter."
> -- reset user state to zero, return word count for grand total
> putState 0
> return n
main just runs the root parser on the command line arguments and
checks the result.
> = do result <- getArgs >>= runParserT root 0 "command line"
> case result of
> Left err -> error $ show err
> Right n -> putStrLn $ "Grand total: "++show n
So where is the lift function? Does it exist? Here, I need your help.
> lift :: IO a -> ParsecT s u IO a
> lift = undefined
Any comments are appreciated.
Stefan Klinger o/klettern
/\/ bis zum
send plaintext only - max size 32kB - no spam \ Abfallen
More information about the Haskell-Cafe