moduleText.ParserCombinators.Poly.StateText(-- * The Parser datatypeParser(P),Result(..),runParser-- ** Basic parsers,next,eof,satisfy,onFail-- ** Derived parsers (but implemented more efficiently),manySatisfy,many1Satisfy-- ** State-handling,stUpdate-- :: (s->s) -> Parser s t (),stQuery-- :: (s->a) -> Parser s t a,stGet-- :: Parser s t s-- ** Re-parsing,reparse-- * Re-export all more general combinators,moduleText.ParserCombinators.Poly.Base,moduleControl.Applicative)whereimportText.ParserCombinators.Poly.BaseimportText.ParserCombinators.Poly.ResultimportqualifiedData.Text.LazyasTimportData.Text.Lazy(Text)importControl.Applicative-- | This @Parser@ datatype is a specialised parsing monad with error-- reporting. Whereas the standard version can be used for arbitrary-- token types, this version is specialised to Text input only.newtypeParsersa=P(s->Text->Result(Text,s)a)-- | Apply a parser to an input token sequence.runParser::Parsersa->s->Text->(EitherStringa,s,Text)runParser(Pp)=\s->reTuple.resultToEither.pswherereTuple(either,(z,s))=(either,s,z)instanceFunctor(Parsers)wherefmapf(Pp)=P(\s->fmapf.ps)instanceMonad(Parsers)wherereturnx=P(\sts->Success(ts,s)x)faile=P(\sts->Failure(ts,s)e)(Pf)>>=g=P(\s->continue.fs)wherecontinue(Success(ts,s)x)=let(Pg')=gxing'stscontinue(Committedr)=Committed(continuer)continue(Failuretse)=FailuretseinstanceCommitment(Parsers)wherecommit(Pp)=P(\s->Committed.squash.ps)wheresquash(Committedr)=squashrsquashr=r(Pp)`adjustErr`f=P(\s->adjust.ps)whereadjust(Failureze)=Failurez(fe)adjust(Committedr)=Committed(adjustr)adjustgood=goodoneOf'=accum[]whereaccumerrs[]=fail("failed to parse any of the possible choices:\n"++indent2(concatMapshowErr(reverseerrs)))accumerrs((e,Pp):ps)=P(\sts->casepstsofFailure_err->let(Pp')=accum((e,err):errs)psinp'stsr@(Success__)->rr@(Committed_)->r)showErr(name,err)=name++":\n"++indent2errinstanceApplicative(Parsers)wherepuref=returnfpf<*>px=do{f<-pf;x<-px;return(fx)}#if defined(GLASGOW_HASKELL) && GLASGOW_HASKELL > 610p<*q=p`discard`q#endifinstanceAlternative(Parsers)whereempty=fail"no parse"p<|>q=p`onFail`qinstancePolyParse(Parsers)-------------------------------------------------------------------------- | Simply return the next token in the input tokenstream.next::ParsersCharnext=P(\sbs->caseT.unconsbsofNothing->Failure(bs,s)"Ran out of input (EOF)"Just(c,bs')->Success(bs',s)c)-- | Succeed if the end of file/input has been reached, fail otherwise.eof::Parsers()eof=P(\sbs->ifT.nullbsthenSuccess(bs,s)()elseFailure(bs,s)"Expected end of input (EOF)")-- | Return the next token if it satisfies the given predicate.satisfy::(Char->Bool)->ParsersCharsatisfyf=do{x<-next;iffxthenreturnxelsefail"Parse.satisfy: failed"}-- | @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::Parsersa->Parsersa->Parsersa(Pp)`onFail`(Pq)=P(\sts->continuests$psts)wherecontinuests(Failure__)=qsts-- continue _ _ (Committed r) = r -- no, remain Committedcontinue__r=r-------------------------------------------------------------------------- | @manySatisfy p@ is a more efficient fused version of @many (satisfy p)@manySatisfy::(Char->Bool)->ParsersTextmanySatisfyf=P(\sbs->let(pre,suf)=T.spanfbsinSuccess(suf,s)pre)-- | @many1Satisfy p@ is a more efficient fused version of @many1 (satisfy p)@many1Satisfy::(Char->Bool)->ParsersTextmany1Satisfyf=dox<-manySatisfyfifT.nullxthenfail"Parse.many1Satisfy: failed"elsereturnx-------------------------------------------------------------------------- State handling-- | Update the internal state.stUpdate::(s->s)->Parsers()stUpdatef=P(\sbs->Success(bs,fs)())-- | Query the internal state.stQuery::(s->a)->ParsersastQueryf=P(\sbs->Success(bs,s)(fs))-- | Deliver the entire internal state.stGet::ParserssstGet=P(\sbs->Success(bs,s)s)-------------------------------------------------------------------------- | 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::Text->Parsers()reparsets=P(\sinp->Success(ts`T.append`inp,s)())------------------------------------------------------------------------