polyparse-1.1: src/Text/ParserCombinators/Poly/Plain.hs
module Text.ParserCombinators.Poly.Plain
( -- * The Parser datatype
Parser(P) -- datatype, instance of: Functor, Monad, PolyParse
, runParser -- :: Parser t a -> [t] -> (Either String a, [t])
-- ** basic parsers
, next -- :: Parser t t
, satisfy -- :: (t->Bool) -> Parser t t
-- ** re-parsing
, reparse -- :: [t] -> Parser t ()
-- * Re-export all more general combinators
, module Text.ParserCombinators.Poly.Base
) where
import Text.ParserCombinators.Poly.Base
-- | This @Parser@ datatype is a fairly generic parsing monad with error
-- reporting. It can be used for arbitrary token types, not just
-- String input. (If you require a running state, use module PolyState
-- instead)
newtype Parser t a = P ([t] -> (EitherE String a, [t]))
-- A return type like Either, that distinguishes not only between
-- right and wrong answers, but also has gradations of wrongness.
type EitherE a b = Either (Bool,a) b
-- | Apply a parser to an input token sequence.
runParser :: Parser t a -> [t] -> (Either String a, [t])
runParser (P p) =
(\ (e,ts)-> (case e of {Left (_,m)->Left m; Right m->Right m}, ts) )
. p
instance Functor (Parser t) where
fmap f (P p) = P (\ts-> case p ts of
(Left msg, ts') -> (Left msg, ts')
(Right x, ts') -> (Right (f x), ts'))
instance Monad (Parser t) where
return x = P (\ts-> (Right x, ts))
(P f) >>= g = P (\ts-> case f ts of
(Left msg, ts') -> (Left msg, ts')
(Right x, ts') -> let (P g') = g x in g' ts')
fail e = P (\ts-> (Left (False,e), ts))
instance PolyParse (Parser t) where
commit (P p) = P (\ts-> case p ts of
(Left (_,e), ts') -> (Left (True,e), ts')
right -> right )
(P p) `adjustErr` f = P (\ts-> case p ts of
(Left (b,msg), ts') -> (Left (b,(f msg)), ts')
right -> right )
(P p) `onFail` (P q) = P (\ts-> case p ts of
r@(Left (True,_), _) -> r
(Left _, _) -> q ts
right -> right )
oneOf' = accum []
where accum errs [] =
case filter isBad errs of
[] -> fail ("failed to parse any of the possible choices:\n"
++indent 2 (concatMap showErr (reverse errs)))
[(_,(_,e))] -> failBad e
es -> failBad ("one of the following failures occurred:\n"
++indent 2 (concatMap showErr (reverse es)))
accum errs ((e,P p):ps) =
P (\ts-> case p ts of
(Left err,_) -> let (P p) = accum ((e,err):errs) ps
in p ts
right -> right )
showErr (name,(_,err)) = name++":\n"++indent 2 err
isBad (_,(b,_)) = b
------------------------------------------------------------------------
next :: Parser t t
next = P (\ts-> case ts of
[] -> (Left (False,"Ran out of input (EOF)"), [])
(t:ts') -> (Right t, ts') )
satisfy :: (t->Bool) -> Parser t t
satisfy pred = do { x <- next
; if pred x then return x else fail "Parse.satisfy: failed"
}
------------------------------------------------------------------------
-- | Push some tokens back onto the front of the input stream and reparse.
-- This is useful e.g. for recursively expanding macros. When the
-- user-parser recognises a macro use, it can lookup the macro
-- expansion from the parse state, lex it, and then stuff the
-- lexed expansion back down into the parser.
reparse :: [t] -> Parser t ()
reparse ts = P (\inp-> (Right (), ts++inp))
------------------------------------------------------------------------