module Text.ParserCombinators.Poly.StateLazy ( -- * The Parser datatype. -- $parser Parser(P) -- datatype, instance of: Functor, Monad , runParser -- :: Parser s t a -> s -> [t] -> (a, s, [t]) -- * Combinators: -- ** Primitives , next -- :: Parser s t t , satisfy -- :: (t->Bool) -> Parser s t t -- ** State-handling , stUpdate -- :: (s->s) -> Parser s t () , stQuery -- :: (s->a) -> Parser s t a , stGet -- :: Parser s t s -- ** Re-parsing , reparse -- :: [t] -> Parser s t () -- * Re-export all more general combinators , module Text.ParserCombinators.Poly.Base ) where import Text.ParserCombinators.Poly.Base #if __GLASGOW_HASKELL__ import Control.Exception hiding (bracket) throwE :: String -> a throwE msg = throw (ErrorCall msg) #else throwE :: String -> a throwE msg = error msg #endif -- $parser -- Parsers do not return explicit failure. An exception is raised -- instead. This allows partial results to be returned before a -- full parse is complete. -- | The @Parser@ datatype is a fairly generic parsing monad with error -- reporting and a running state. It can be used for arbitrary token -- types, not just String input. newtype Parser s t a = P (s -> [t] -> (Either String a, s, [t])) -- | A return type like Either, that distinguishes not only between -- right and wrong answers, but also had gradations of wrongness. -- Not used in this library. !!!!!!!!!!!!!!!!!!!!!!!!!! type EitherE a b = Either (Bool,a) b -- | Apply a parser to an initial state and input token sequence. -- The parser cannot return an error value explicitly, so errors -- raise an exception. Thus, results can be partial (lazily constructed, -- but containing undefined). runParser :: Parser s t a -> s -> [t] -> (a, s, [t]) runParser (P p) s = (\ (e,s,ts)-> (case e of {Left m->throwE m; Right x->x}, s, ts)) . p s instance Functor (Parser s t) where fmap f (P p) = P (\s ts-> case p s ts of (Left msg, s', ts') -> (Left msg, s', ts') (Right x, s', ts') -> (Right (f x), s', ts')) instance Monad (Parser s t) where return x = P (\s ts-> (Right x, s, ts)) (P f) >>= g = P (\s ts-> case f s ts of (Left msg, s', ts') -> (Left msg, s', ts') (Right x, s', ts') -> let (P g') = g x in g' s' ts') fail msg = P (\s ts-> (Left msg, s, ts)) instance PolyParse (Parser s t) where commit (P p) = P (\s ts-> case p s ts of (Left e, s', ts') -> (throwE e, s', ts') right -> right ) (P p) `adjustErr` f = P (\s ts-> case p s ts of (Left msg, s', ts') -> (Left (f msg), s, ts') right -> right ) (P p) `onFail` (P q) = P (\s ts-> case p s ts of (Left _, _, _) -> q s ts right -> right ) oneOf' ps = accum [] ps where accum errs [] = case errs of [] -> failBad ("internal failure in parser (oneOf'):\n" ++indent 2 (show (map fst ps))) [(_,e)] -> fail e es -> fail ("one of the following failures occurred:\n" ++indent 2 (concatMap showErr (reverse es))) accum errs ((e,P p):ps) = P (\u ts-> case p u ts of (Left err,_,_) -> let (P p) = accum ((e,err):errs) ps in p u ts right -> right ) showErr (name,err) = name++":\n"++indent 2 err (P pf) `apply` (P px) = P (\s ts-> case pf s ts of (Left msg, s', ts') -> (Left msg, s', ts') (Right f, s', ts') -> let (x',s'',ts'') = px s' ts' x = case x' of { Right x -> x ; Left e -> throwE e } in (Right (f x), s'', ts'') ) -- Combinators -- | Yield one token. next :: Parser s t t next = P (\s ts-> case ts of [] -> (Left "Ran out of input (EOF)", s, []) (t:ts') -> (Right t, s, ts') ) -- | Yield one token if it satisfies a predicate. satisfy :: (t->Bool) -> Parser s t t satisfy p = do{ x <- next ; if p x then return x else fail "Parse.satisfy: failed" } ------------------------------------------------------------------------ -- State handling -- | Update the internal state. stUpdate :: (s->s) -> Parser s t () stUpdate f = P (\s ts-> (Right (), f s, ts)) -- | Query the internal state. stQuery :: (s->a) -> Parser s t a stQuery f = P (\s ts-> (Right (f s), s, ts)) -- | Deliver the entire internal state. stGet :: Parser s t s stGet = P (\s ts-> (Right s, s, ts)) ------------------------------------------------------------------------ -- | 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 s t () reparse ts = P (\s inp-> (Right (), s, ts++inp)) ------------------------------------------------------------------------