-- | This module contains the definitions for a generic parser, without-- running state. These are the parts that are shared between the Plain-- and Lazy variations. Do not import this module directly, but only-- via T.P.Poly.Plain or T.P.Poly.Lazy.
module Text.ParserCombinators.Poly.Parser
( -- * The Parser datatype
Parser(P) -- datatype, instance of: Functor, Monad, PolyParse
, Result(..) -- internal to the Parser Monad.-- ** Basic parsers
, next -- :: Parser t t
, eof -- :: Parser t ()
, satisfy -- :: (t->Bool) -> Parser t t
, onFail -- :: Parser t a -> Parser t a -> Parser t a-- ** Re-parsing
, reparse -- :: [t] -> Parser t ()
) where
import Text.ParserCombinators.Poly.Base
import Text.ParserCombinators.Poly.Result
-- | 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 Poly.State-- instead)
newtype Parserta = P ([t] -> Result [t] a)
instance Functor (Parsert) where
fmapf (Pp) = P (fmapf.p)
instance Monad (Parsert) where
returnx = P (\ts-> Successtsx)
faile = P (\ts-> Failuretse)
(Pf) >>=g = P (continue.f)
where
continue (Successtsx) = let (Pg') = gx in g'tscontinue (Committedr) = Committed (continuer)
continue (Failuretse) = Failuretse
instance Commitment (Parsert) where
commit (Pp) = P (Committed.squash.p)
where
squash (Committedr) = squashrsquashr = r
(Pp) `adjustErr`f = P (adjust.p)
where
adjust (Failureze) = Failurez (fe)
adjust (Committedr) = Committed (adjustr)
adjustgood = goodoneOf' = accum []
where accumerrs [] =
fail ("failed to parse any of the possible choices:\n"++indent2 (concatMapshowErr (reverseerrs)))
accumerrs ((e,Pp):ps) =
P (\ts-> case pts of
Failure _ err ->
let (Pp) = accum ((e,err):errs) ps
in ptsr@(Successza) -> rr@(Committed _) -> r )
showErr (name,err) = name++":\n"++indent2errinfixl 6 `onFail`-- not sure about precedence 6?-- | @p `onFail` q@ means parse p, unless p fails, in which case-- parse q instead.-- Can be chained together to give multiple attempts to parse something.-- (Note that q could itself be a failing parser, e.g. to change the error-- message from that defined in p to something different.)-- However, a severe failure in p cannot be ignored.onFail :: Parserta -> Parserta -> Parserta
(Pp) `onFail` (Pq) = P (\ts-> continuets$pts)
where
continuets (Failureze) = qts-- continue _ (Committed r) = r -- no, remain Committedcontinue _ r = r-------------------------------------------------------------------------- | Simply return the next token in the input tokenstream.next :: Parserttnext = P (\ts-> case ts of
[] -> Failure [] "Ran out of input (EOF)"
(t:ts') -> Successts't )
-- | Succeed if the end of file/input has been reached, fail otherwise.eof :: Parsert()eof = P (\ts-> case ts of
[] -> Success [] ()
(t:ts') -> Failurets"Expected end of input (EOF)" )
-- | Return the next token if it satisfies the given predicate.satisfy :: (t->Bool) -> Parserttsatisfypred = do { x <- next
; if predx then returnx 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] -> Parsert()reparsets = P (\inp-> Success (ts++inp) ())
------------------------------------------------------------------------