parsec1-1.0.0.8: Text/ParserCombinators/Parsec/Prim.hs
{-# LANGUAGE CPP #-}
{- |
Module : Text.ParserCombinators.Parsec.Prim
Copyright : (c) Daan Leijen 1999-2001
License : BSD-style (see the file LICENSE)
Maintainer : Christian Maeder <chr.maeder@web.de>
Stability : provisional
Portability : portable
The primitive parser combinators.
-}
module Text.ParserCombinators.Parsec.Prim
( -- operators: label a parser, alternative
(*>)
, (<$)
, (<$>)
, (<*)
, (<*>)
, (<?>)
, (<|>)
-- basic types
, GenParser
, Parser
, parse
, parseFromFile
, parseTest
, runParser
-- primitive parsers
, label
, labels
, lookAhead
, pzero
, token
, tokenPrim
, tokenPrimEx
, tokens
, try
, unexpected
-- primitive because of space behaviour
, many
, skipMany
-- user state manipulation
, getState
, setState
, updateState
-- state manipulation
, State (..)
, getInput
, getParserState
, getPosition
, setInput
, setParserState
, setPosition
) where
import Text.ParserCombinators.Parsec.Error
import Text.ParserCombinators.Parsec.Pos
import Control.Applicative
import Control.Monad
#if __GLASGOW_HASKELL__ >= 801
import Control.Monad.Fail
#endif
infix 0 <?>
{- | The parser @p <?> msg@ behaves as parser @p@, but whenever the
parser @p@ fails /without consuming any input/, it replaces expect
error messages with the expect error message @msg@.
This is normally used at the end of a set alternatives where we want
to return an error message in terms of a higher level construct
rather than returning all possible characters. For example, if the
@expr@ parser from the 'try' example would fail, the error
message is: '...: expecting expression'. Without the @(\<?>)@
combinator, the message would be like '...: expecting \"let\" or
letter', which is less friendly. -}
(<?>) :: GenParser tok st a -> String -> GenParser tok st a
p <?> msg = label p msg
{- ---------------------------------------------------------
User state combinators
--------------------------------------------------------- -}
-- | Returns the current user state.
getState :: GenParser tok st st
getState = stateUser <$> getParserState
-- | @setState st@ set the user state to @st@.
setState :: st -> GenParser tok st ()
setState st =
() <$ updateParserState (\ (State input pos _) -> State input pos st)
{- | @updateState f@ applies function @f@ to the user state. Suppose
that we want to count identifiers in a source, we could use the user
state as:
> expr = do
> x <- identifier
> updateState (+1)
> return (Id x) -}
updateState :: (st -> st) -> GenParser tok st ()
updateState f =
() <$ updateParserState (\ (State input pos user) -> State input pos (f user))
{- ---------------------------------------------------------
Parser state combinators
--------------------------------------------------------- -}
-- | Returns the current source position. See also 'SourcePos'.
getPosition :: GenParser tok st SourcePos
getPosition = statePos <$> getParserState
-- | Returns the current input
getInput :: GenParser tok st [tok]
getInput = stateInput <$> getParserState
-- | @setPosition pos@ sets the current source position to @pos@.
setPosition :: SourcePos -> GenParser tok st ()
setPosition pos =
() <$ updateParserState (\ (State input _ user) -> State input pos user)
-- | @setInput input@ continues parsing with @input@.
setInput :: [tok] -> GenParser tok st ()
setInput input =
() <$ updateParserState (\ (State _ pos user) -> State input pos user)
-- | Returns the full parser state as a 'State' record.
getParserState :: GenParser tok st (State tok st)
getParserState = updateParserState id
-- | @setParserState st@ set the full parser state to @st@.
setParserState :: State tok st -> GenParser tok st (State tok st)
setParserState st = updateParserState (const st)
{- ---------------------------------------------------------
Parser definition.
GenParser tok st a:
General parser for tokens of type "tok",
a user state "st" and a result type "a"
--------------------------------------------------------- -}
type Parser a = GenParser Char () a
newtype GenParser tok st a = Parser
{ runP :: State tok st -> Consumed (Reply tok st a) }
data Consumed a
= Consumed a -- input is consumed
| Empty !a -- no input is consumed
data Reply tok st a
= Ok !a !(State tok st) ParseError -- parsing succeeded with "a"
| Error ParseError -- parsing failed
data State tok st = State
{ stateInput :: [tok]
, statePos :: !SourcePos
, stateUser :: !st }
{- ---------------------------------------------------------
run a parser
--------------------------------------------------------- -}
parseFromFile :: Parser a -> SourceName -> IO (Either ParseError a)
parseFromFile p fname = do
input <- readFile fname
return $ parse p fname input
{- | The expression @parseTest p input@ applies a parser @p@ against
input @input@ and prints the result to stdout. Used for testing
parsers. -}
parseTest :: Show a => GenParser tok () a -> [tok] -> IO ()
parseTest p input = case runParser p () "" input of
Left err -> putStr "parse error at " >> print err
Right x -> print x
{- | @parse p filePath input@ runs a parser @p@ without user
state. The @filePath@ is only used in error messages and may be the
empty string. Returns either a 'ParseError' ('Left')
or a value of type @a@ ('Right').
> main = case parse numbers "" "11, 2, 43" of
> Left err -> print err
> Right xs -> print (sum xs)
>
> numbers = commaSep integer -}
parse :: GenParser tok () a -> SourceName -> [tok] -> Either ParseError a
parse p = runParser p ()
{- | The most general way to run a parser. @runParser p state filePath
input@ runs parser @p@ on the input list of tokens @input@,
obtained from source @filePath@ with the initial user state @st@.
The @filePath@ is only used in error messages and may be the empty
string. Returns either a 'ParseError' ('Left') or a
value of type @a@ ('Right').
> parseFromFile p fname = do
> input <- readFile fname
> return (runParser p () fname input) -}
runParser :: GenParser tok st a -> st -> SourceName -> [tok]
-> Either ParseError a
runParser p st name input =
case parserReply . runP p $ State input (initialPos name) st of
Ok x _ _ -> Right x
Error err -> Left err
parserReply :: Consumed p -> p
parserReply result = case result of
Consumed reply -> reply
Empty reply -> reply
{- ---------------------------------------------------------
Functor: fmap
--------------------------------------------------------- -}
instance Functor (GenParser tok st) where
fmap f p = parsecMap f p
parsecMap :: (a -> b) -> GenParser tok st a -> GenParser tok st b
parsecMap f (Parser p) = Parser $ \ state -> case p state of
Consumed reply -> Consumed $ mapReply reply
Empty reply -> Empty $ mapReply reply
where
mapReply reply = case reply of
Ok x state err -> let fx = f x in seq fx $ Ok fx state err
Error err -> Error err
{- ---------------------------------------------------------
Monad: return, sequence (>>=) and fail
--------------------------------------------------------- -}
instance Monad (GenParser tok st) where
return = parsecReturn
(>>=) = parsecBind
(>>) = (*>)
#if !MIN_VERSION_base (4, 13, 0)
fail = parsecFail
#endif
#if __GLASGOW_HASKELL__ >= 801
instance MonadFail (GenParser tok st) where
fail = parsecFail
#endif
instance Applicative (GenParser tok st) where
pure = parsecReturn
(<*>) = ap
p1 *> p2 = p1 >>= const p2
p1 <* p2 = p1 >>= (<$ p2)
parsecReturn :: a -> GenParser tok st a
parsecReturn x = Parser $ \ state -> Empty . Ok x state $ unknownError state
parsecBind :: GenParser tok st a -> (a -> GenParser tok st b)
-> GenParser tok st b
parsecBind (Parser p) f = Parser $ \ state -> case p state of
Consumed reply1 -> Consumed $ case reply1 of
Ok x state1 err1 -> case runP (f x) state1 of
Empty reply2 -> mergeErrorReply err1 reply2
Consumed reply2 -> reply2
Error err1 -> Error err1
Empty reply1 -> case reply1 of
Ok x state1 err1 -> case runP (f x) state1 of
Empty reply2 -> Empty (mergeErrorReply err1 reply2)
other -> other
Error err1 -> Empty (Error err1)
mergeErrorReply :: ParseError -> Reply tok st a -> Reply tok st a
mergeErrorReply err1 reply = case reply of
Ok x state err2 -> Ok x state (mergeError err1 err2)
Error err2 -> Error (mergeError err1 err2)
parsecFail :: String -> GenParser tok st a
parsecFail msg =
Parser $ Empty . Error . newErrorMessage (Message msg) . statePos
{- ---------------------------------------------------------
MonadPlus: alternative (mplus) and mzero
--------------------------------------------------------- -}
instance MonadPlus (GenParser tok st) where
mzero = parsecZero
mplus p1 p2 = parsecPlus p1 p2
instance Alternative (GenParser tok st) where
(<|>) = mplus
empty = mzero
many = manyAux
pzero :: GenParser tok st a
pzero = parsecZero
{- | @parsecZero@ always fails without consuming any input. @parsecZero@ is
defined equal to the 'mzero' member of the 'MonadPlus' class and to the
'Control.Applicative.empty' member of the 'Control.Applicative.Applicative'
class. -}
parsecZero :: GenParser tok st a
parsecZero = Parser $ Empty . Error . unknownError
parsecPlus :: GenParser tok st a -> GenParser tok st a -> GenParser tok st a
parsecPlus (Parser p1) (Parser p2) = Parser $ \ state -> case p1 state of
Empty (Error err) -> case p2 state of
Empty reply -> Empty $ mergeErrorReply err reply
consumed -> consumed
other -> other
{- | The parser @try p@ behaves like parser @p@, except that it
pretends that it hasn't consumed any input when an error occurs.
This combinator is used whenever arbitrary look ahead is needed.
Since it pretends that it hasn't consumed any input when @p@ fails,
the ('<|>') combinator will try its second alternative even when the
first parser failed while consuming input.
The @try@ combinator can for example be used to distinguish
identifiers and reserved words. Both reserved words and identifiers
are a sequence of letters. Whenever we expect a certain reserved
word where we can also expect an identifier we have to use the @try@
combinator. Suppose we write:
> expr = letExpr <|> identifier <?> "expression"
>
> letExpr = string "let" *> ...
> identifier = many1 letter
If the user writes \"lexical\", the parser fails with: @unexpected
\'x\', expecting \'t\' in \"let\"@. Indeed, since the ('<|>') combinator
only tries alternatives when the first alternative hasn't consumed
input, the @identifier@ parser is never tried (because the prefix
\"le\" of the @string \"let\"@ parser is already consumed). The
right behaviour can be obtained by adding the @try@ combinator:
> expr = letExpr <|> identifier <?> "expression"
>
> letExpr = try (string "let") *>
> identifier = many1 letter -}
try :: GenParser tok st a -> GenParser tok st a
try (Parser p) = Parser $ \ state -> case p state of
Consumed (Error err) -> Empty (Error err)
Consumed ok -> Consumed ok -- was: Empty ok
mty -> mty
-- | @lookAhead p@ parses @p@ without consuming any input.
lookAhead :: GenParser tok st a -> GenParser tok st a
lookAhead p = Parser $ \ state -> case runP p state of
Consumed (Ok r _ _) -> Empty $ Ok r state $ unknownError state
reply -> reply
{- | The parser @token showTok posFromTok testTok@ accepts a token @t@
with result @x@ when the function @testTok t@ returns @'Just' x@. The
source position of the @t@ should be returned by @posFromTok t@ and
the token can be shown using @showTok t@.
This combinator is expressed in terms of 'tokenPrim'.
It is used to accept user defined token streams. For example,
suppose that we have a stream of basic tokens tupled with source
positions. We can than define a parser that accepts single tokens as:
> mytoken x
> = token showTok posFromTok testTok
> where
> showTok (pos,t) = show t
> posFromTok (pos,t) = pos
> testTok (pos,t) = if x == t then Just t else Nothing -}
token :: (tok -> String) -> (tok -> SourcePos) -> (tok -> Maybe a)
-> GenParser tok st a
token shw tokpos = tokenPrim shw nextpos where
nextpos _ tok ts = case ts of
t : _ -> tokpos t
_ -> tokpos tok
{- | The parser @token showTok nextPos testTok@ accepts a token @t@
with result @x@ when the function @testTok t@ returns @'Just' x@. The
token can be shown using @showTok t@. The position of the /next/
token should be returned when @nextPos@ is called with the current
source position @pos@, the current token @t@ and the rest of the
tokens @toks@, @nextPos pos t toks@.
This is the most primitive combinator for accepting tokens. For
example, the 'Text.Parsec.Char.char' parser could be implemented as:
> char c
> = tokenPrim showChar nextPos testChar
> where
> showChar x = "'" ++ x ++ "'"
> testChar x = if x == c then Just x else Nothing
> nextPos pos x xs = updatePosChar pos x -}
tokenPrim :: (tok -> String) -> (SourcePos -> tok -> [tok] -> SourcePos)
-> (tok -> Maybe a) -> GenParser tok st a
tokenPrim shw nextpos = tokenPrimEx shw nextpos Nothing
{- | The most primitive token recogniser. The expression
@tokenPrimEx show nextpos mbnextstate test@, recognises tokens when @test@
returns @Just x@ (and returns the value @x@). Tokens are shown in error
messages using @show@. The position is calculated using @nextpos@, and finally,
@mbnextstate@, can hold a function that updates the user state on every token
recognised (nice to count tokens :-).
The function is packed into a 'Maybe' type for performance reasons. -}
tokenPrimEx :: (tok -> String) -> (SourcePos -> tok -> [tok] -> SourcePos)
-> Maybe (SourcePos -> tok -> [tok] -> st -> st) -> (tok -> Maybe a)
-> GenParser tok st a
tokenPrimEx shw nextpos mbNextState test = case mbNextState of
Nothing -> Parser $ \ (State input pos user) -> case input of
c : cs -> case test c of
Just x -> let
newpos = nextpos pos c cs
newstate = State cs newpos user
in seq newpos . seq newstate .
Consumed . Ok x newstate $ newErrorUnknown newpos
Nothing -> Empty (sysUnExpectError (shw c) pos)
[] -> Empty (sysUnExpectError "" pos)
Just nextState -> Parser $ \ (State input pos user) -> case input of
c : cs -> case test c of
Just x -> let
newpos = nextpos pos c cs
newuser = nextState pos c cs user
newstate = State cs newpos newuser
in seq newpos . seq newstate .
Consumed . Ok x newstate $ newErrorUnknown newpos
Nothing -> Empty $ sysUnExpectError (shw c) pos
[] -> Empty $ sysUnExpectError "" pos
label :: GenParser tok st a -> String -> GenParser tok st a
label p msg = labels p [msg]
labels :: GenParser tok st a -> [String] -> GenParser tok st a
labels (Parser p) msgs = Parser $ \ state -> case p state of
Empty reply -> Empty $ case reply of
Error err -> Error $ setExpectErrors err msgs
Ok x state1 err -> if errorIsUnknown err then reply else
Ok x state1 $ setExpectErrors err msgs
other -> other
-- | @updateParserState f@ applies function @f@ to the parser state.
updateParserState :: (State tok st -> State tok st)
-> GenParser tok st (State tok st)
updateParserState f = Parser $ \ state -> let newstate = f state in
Empty . Ok state newstate $ unknownError newstate
{- | The parser @unexpected msg@ always fails with an unexpected error
message @msg@ without consuming any input.
The parsers @fail@, ('<?>') and @unexpected@ are the three parsers
used to generate error messages. Of these, only ('<?>') is commonly
used. For an example of the use of @unexpected@, see the definition
of 'Text.ParserCombinators.Parsec.Combinator.notFollowedBy'. -}
unexpected :: String -> GenParser tok st a
unexpected msg =
Parser $ Empty . Error . newErrorMessage (UnExpect msg) . statePos
setExpectErrors :: ParseError -> [String] -> ParseError
setExpectErrors err ms = case ms of
[] -> setErrorMessage (Expect "") err
[msg] -> setErrorMessage (Expect msg) err
msg : msgs -> foldr (addErrorMessage . Expect)
(setErrorMessage (Expect msg) err) msgs
sysUnExpectError :: String -> SourcePos -> Reply tok st a
sysUnExpectError msg = Error . newErrorMessage (SysUnExpect msg)
unknownError :: State tok st -> ParseError
unknownError = newErrorUnknown . statePos
{- ---------------------------------------------------------
Parsers unfolded for space:
if many and skipMany are not defined as primitives,
they will overflow the stack on large inputs
--------------------------------------------------------- -}
{- | @manyAux p@ applies the parser @p@ /zero/ or more times. Returns a
list of the returned values of @p@.
> identifier = do
> c <- letter
> cs <- many (alphaNum <|> char '_')
> return (c : cs) -}
manyAux :: GenParser tok st a -> GenParser tok st [a]
manyAux p = do
xs <- manyAccum (:) p
return (reverse xs)
{- | @skipMany p@ applies the parser @p@ /zero/ or more times, skipping
its result.
> spaces = skipMany space -}
skipMany :: GenParser tok st a -> GenParser tok st ()
skipMany p = () <$ manyAccum (\ _ _ -> []) p
manyAccum :: (a -> [a] -> [a]) -> GenParser tok st a -> GenParser tok st [a]
manyAccum accum (Parser p) = Parser $ \ state -> let
errMty = error $ "Text.ParserCombinators.Parsec.Prim.many: combinator "
++ "'many' is applied to a parser that accepts an empty string."
walk xs st r = case r of
Empty (Error err) -> Ok xs st err
Empty _ -> errMty
Consumed (Error err) -> Error err
Consumed (Ok x state' _) -> let ys = accum x xs in
seq ys . walk ys state' $ p state'
in case p state of
Empty reply -> case reply of
Ok {} -> errMty
Error err -> Empty (Ok [] state err)
consumed -> Consumed $ walk [] state consumed
{- ---------------------------------------------------------
Parsers unfolded for speed:
tokens
--------------------------------------------------------- -}
{- specification of @tokens@:
tokens showss nextposs s
= scan s
where
scan [] = return s
scan (c:cs) = do{ token show nextpos c <?> shows s; scan cs }
show c = shows [c]
nextpos pos c = nextposs pos [c]
-}
tokens :: Eq tok => ([tok] -> String) -> (SourcePos -> [tok] -> SourcePos)
-> [tok] -> GenParser tok st [tok]
tokens shws nextposs s = Parser $ \ (State input pos user) -> let
ok cs = let
newpos = nextposs pos s
newstate = State cs newpos user
in seq newpos . seq newstate . Ok s newstate $ newErrorUnknown newpos
errMsg m = Error . setErrorMessage (Expect (shws s)) $ newErrorMessage
(SysUnExpect m) pos
errEof = errMsg ""
errExpect = errMsg . shws . reverse
walk r xs cs = case xs of
[] -> ok cs
x : rs -> case cs of
[] -> errExpect r
c : ss -> if x == c then walk (x : r) rs ss else errExpect $ c : r
walk1 xs cs = case xs of
[] -> Empty $ ok cs
x : rs -> case cs of
[] -> Empty errEof
c : ss ->
if x == c then Consumed $ walk [x] rs ss else Empty $ errExpect [c]
in walk1 s input