-- | a 'Boomerang' library for working with '[String]'{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, TemplateHaskell, TypeFamilies, TypeSynonymInstances, TypeOperators #-}moduleText.Boomerang.Strings(-- * TypesStringsError-- * Combinators,(</>),alpha,anyChar,anyString,char,digit,eos,int,integer,lit,readshow,satisfy,satisfyStr,space-- * Running the 'Boomerang',isComplete,parseStrings,unparseStrings)whereimportPreludehiding((.),id,(/))importControl.Category(Category((.),id))importData.Char(isAlpha,isDigit,isSpace)importData.Data(Data,Typeable)importData.List(stripPrefix)importData.String(IsString(..))importNumeric(readDec,readSigned)importText.Boomerang.Combinators(opt,rCons,rList1)importText.Boomerang.Error(ParserError(..),ErrorMsg(..),(<?>),condenseErrors,mkParserError)importText.Boomerang.HStack((:-)(..))importText.Boomerang.Pos(InitialPosition(..),MajorMinorPos(..),incMajor,incMinor)importText.Boomerang.Prim(Parser(..),Boomerang(..),parse1,xmaph,unparse1,val)typeStringsError=ParserErrorMajorMinorPosinstanceInitialPositionStringsErrorwhereinitialPos_=MajorMinorPos00instancea~b=>IsString(BoomerangStringsError[String]ab)wherefromString=lit-- | a constant stringlit::String->BoomerangStringsError[String]rrlitl=Boomerangpfsfwherepf=Parser$\tokpos->casetokof[]->mkParserErrorpos[EOI"input",Expect(showl)]("":_)|(not$nulll)->mkParserErrorpos[EOI"segment",Expect(showl)](p:ps)->casestripPrefixlpof(Justp')->do[Right((id,p':ps),incMinor(lengthl)pos)]Nothing->mkParserErrorpos[UnExpect(showp),Expect(showl)]sfb=[(\strings->casestringsof[]->[l];(s:ss)->((l++s):ss),b)]infixr9</>-- | equivalent to @f . eos . g@(</>)::BoomerangStringsError[String]bc->BoomerangStringsError[String]ab->BoomerangStringsError[String]acf</>g=f.eos.g-- | end of stringeos::BoomerangStringsError[String]rreos=Boomerang(Parser$\pathpos->casepathof[]->[Right((id,[]),incMajor1pos)]-- [] -> mkParserError pos [EOI "input"]("":ps)->[Right((id,ps),incMajor1pos)](p:_)->mkParserErrorpos[Message$"path-segment not entirely consumed: "++p])(\a->[(("":),a)])-- | statisfy a 'Char' predicatesatisfy::(Char->Bool)->BoomerangStringsError[String]r(Char:-r)satisfyp=val(Parser$\tokpos->casetokof[]->mkParserErrorpos[EOI"input"]("":ss)->mkParserErrorpos[EOI"segment"]((c:cs):ss)|pc->do[Right((c,cs:ss),incMinor1pos)]|otherwise->domkParserErrorpos[SysUnExpect$showc])(\c->[\paths->casepathsof[]->[[c]];(s:ss)->((c:s):ss)|pc])-- | satisfy a 'String' predicate.---- Note: must match the entire remainder of the 'String' in this segmentsatisfyStr::(String->Bool)->BoomerangStringsError[String]r(String:-r)satisfyStrp=val(Parser$\tokpos->casetokof[]->mkParserErrorpos[EOI"input"]("":ss)->mkParserErrorpos[EOI"segment"](s:ss)|ps->do[Right((s,"":ss),incMajor1pos)]|otherwise->domkParserErrorpos[SysUnExpect$shows])(\str->[\strings->casestringsof[]->[str];(s:ss)->((str++s):ss)|pstr])-- | ascii digits @\'0\'..\'9\'@digit::BoomerangStringsError[String]r(Char:-r)digit=satisfyisDigit<?>"a digit 0-9"-- | matches alphabetic Unicode characters (lower-case, upper-case and title-case letters,-- plus letters of caseless scripts and modifiers letters). (Uses 'isAlpha')alpha::BoomerangStringsError[String]r(Char:-r)alpha=satisfyisAlpha<?>"an alphabetic Unicode character"-- | matches white-space characters in the Latin-1 range. (Uses 'isSpace')space::BoomerangStringsError[String]r(Char:-r)space=satisfyisSpace<?>"a white-space character"-- | any characteranyChar::BoomerangStringsError[String]r(Char:-r)anyChar=satisfy(constTrue)-- | matches the specified characterchar::Char->BoomerangStringsError[String]r(Char:-r)charc=satisfy(==c)<?>show[c]-- | lift 'Read'/'Show' to a 'Boomerang'---- There are a few restrictions here:---- 1. Error messages are a bit fuzzy. `Read` does not tell us where-- or why a parse failed. So all we can do it use the the position-- that we were at when we called read and say that it failed.---- 2. it is (currently) not safe to use 'readshow' on integral values-- because the 'Read' instance for 'Int', 'Integer', etc,readshow::(Reada,Showa)=>BoomerangStringsError[String]r(a:-r)readshow=valreadParserswheresa=[\strings->casestringsof[]->[showa];(s:ss)->(((showa)++s):ss)]readParser::(Reada)=>ParserStringsError[String]areadParser=Parser$\tokpos->casetokof[]->mkParserErrorpos[EOI"input"]("":_)->mkParserErrorpos[EOI"segment"](p:ps)->casereadspof[]->mkParserErrorpos[SysUnExpectp,Message$"decoding using 'read' failed."][(a,r)]->[Right((a,r:ps),incMinor((lengthp)-(lengthr))pos)]readIntegral::(Reada,Eqa,Numa,Reala)=>String->areadIntegrals=case(readSignedreadDec)sof[(x,[])]->x[]->error"readIntegral: no parse"_->error"readIntegral: ambiguous parse"-- | matches an 'Int'---- Note that the combinator @(rPair . int . int)@ is ill-defined because the parse can not tell where it is supposed to split the sequence of digits to produced two ints.int::BoomerangStringsError[String]r(Int:-r)int=xmaphreadIntegral(Just.show)(opt(rCons.char'-').(rList1digit))-- | matches an 'Integer'---- Note that the combinator @(rPair . integer . integer)@ is ill-defined because the parse can not tell where it is supposed to split the sequence of digits to produced two ints.integer::BoomerangStringsError[String]r(Integer:-r)integer=xmaphreadIntegral(Just.show)(opt(rCons.char'-').(rList1digit))-- | matches any 'String'---- the parser returns the remainder of the current String segment, (but does not consume the 'end of segment'.---- Note that the only combinator that should follow 'anyString' is-- 'eos' or '</>'. Other combinators will lead to inconsistent-- inversions.---- For example, if we have:---- > unparseStrings (rPair . anyString . anyString) ("foo","bar")---- That will unparse to @Just ["foobar"]@. But if we call---- > parseStrings (rPair . anyString . anyString) ["foobar"]---- We will get @Right ("foobar","")@ instead of the original @Right ("foo","bar")@anyString::BoomerangStringsError[String]r(String:-r)anyString=valpssswhereps=Parser$\tokpos->casetokof[]->mkParserErrorpos[EOI"input",Expect"any string"]-- ("":_) -> mkParserError pos [EOI "segment", Expect "any string"](s:ss)->[Right((s,"":ss),incMinor(lengths)pos)]ssstr=[\ss->casessof[]->[str](s:ss')->((str++s):ss')]-- | Predicate to test if we have parsed all the strings.-- Typically used as argument to 'parse1'---- see also: 'parseStrings'isComplete::[String]->BoolisComplete[]=TrueisComplete[""]=TrueisComplete_=False-- | run the parser---- Returns the first complete parse or a parse error.---- > parseStrings (rUnit . lit "foo") ["foo"]parseStrings::BoomerangStringsError[String]()(r:-())->[String]->EitherStringsErrorrparseStringsppstrs=either(Left.condenseErrors)Right$parse1isCompleteppstrs-- | run the printer---- > unparseStrings (rUnit . lit "foo") ()unparseStrings::Boomerange[String]()(r:-())->r->Maybe[String]unparseStringsppr=unparse1[]ppr