{-# OPTIONS -Wall #-}---------------------------------------------------------------------------------- |-- Module : Wumpus.Core.Utils.FormatCombinators-- Copyright : (c) Stephen Tetley 2010-2012-- License : BSD3---- Maintainer : stephen.tetley@gmail.com-- Stability : unstable-- Portability : GHC---- Formatting combinators - pretty printers without the fitting.----------------------------------------------------------------------------------moduleWumpus.Core.Utils.FormatCombinators(Doc,DocS,Format(..),empty,showsDoc,(><),(<+>),vconcat,separate,hcat,hsep,vcat,text,char,int,integer,integral,float,double,space,comma,semicolon,line,fill,punctuate,enclose,squotes,dquotes,parens,brackets,braces,angles,lparen,rparen,lbracket,rbracket,lbrace,rbrace,langle,rangle,list,tupled,semiBraces,indent)whereimportData.MonoidimportNumeric-- | Doc is a Join List ...--dataDoc=Doc1ShowS|JoinDocDoc|Line|Indent!IntDoctypeDocS=Doc->Doc-- Join could be improved...--unDoc::Doc->ShowSunDoc=step0idwherestep_acc(Doc1sf)=acc.sfstepnacc(Joinab)=letacc'=stepnaccainstepnacc'bstepnaccLine=acc.showChar'\n'.indentSnstepnacc(Indentid)=step(n+i)(acc.(indentSi))dindentS::Int->ShowSindentSi|i<1=id|otherwise=showString$replicatei' 'runDoc::Doc->StringrunDoc=($"").unDocinstanceShowDocwhereshow=runDoc-- Eq is horrible but it is required for PrimitiveinstanceEqDocwhere(==)(Doc1f)(Doc1g)=(f[])==(g[])-- horribly expensive!(==)(Joinab)(Joinxy)=a==x&&b==y(==)LineLine=True(==)(Indentia)(Indentjx)=i==j&&a==x(==)__=FalseinstanceMonoidDocwheremempty=emptymappend=(><)--------------------------------------------------------------------------------classFormatawhereformat::a->DocinstanceFormatIntwhereformat=intinstanceFormatIntegerwhereformat=integerinstanceFormatDoublewhereformat=double---------------------------------------------------------------------------------- | Create an empty, zero length document.--empty::Docempty=Doc1id-- | Create a document from a ShowS function.--showsDoc::ShowS->DocshowsDoc=Doc1infixr6><-- | Horizontally concatenate two documents with no space -- between them.-- (><)::Doc->Doc->Doca><b=Joinabinfixr6<+>-- | Horizontally concatenate two documents with a single space -- between them.-- (<+>)::Doc->Doc->Doca<+>b=Joina(Joinspaceb)-- | Vertical concatenate two documents with a line break.-- vconcat::Doc->Doc->Docvconcatab=a><Line><bseparate::Doc->[Doc]->Docseparate_[]=emptyseparatesep(a:as)=stepaaswherestepacc[]=accstepacc(x:xs)=step(acc><sep><x)xs-- | Horizontally concatenate a list of documents with @(\<\>)@.--hcat::[Doc]->Dochcat=foldr(><)empty-- | Horizontally concatenate a list of documents with @(\<+\>)@.--hsep::[Doc]->Dochsep=separatespace-- | Vertically concatenate a list of documents, with a line -- break between each doc.--vcat::[Doc]->Docvcat[]=emptyvcat(x:xs)=stepxxswherestepacc(z:zs)=step(acc`vconcat`z)zsstepacc[]=acc-- | Create a document from a literal string.-- -- The string should not contain newlines (though this is not -- enforced). --text::String->Doctext=Doc1.showString-- | Create a document from a literal character.---- The char should not be a tab or newline. --char::Char->Docchar=Doc1.showChar-- | Show the Int as a Doc.---- > int = text . show--int::Int->Docint=Doc1.showInt-- | Show the Integer as a Doc.--integer::Integer->Docinteger=Doc1.showInt-- | Show an \"integral value\" as a Doc via 'fromIntegral'.--integral::Integrala=>a->Docintegral=Doc1.showInt-- | Show the Float as a Doc.--float::Double->Docfloat=Doc1.showFloat-- | Show the Double as a Doc.--double::Double->Docdouble=Doc1.showFloat-- | Create a Doc containing a single space character.--space::Docspace=char' '-- | Create a Doc containing a comma, \",\".--comma::Doccomma=char','-- | Create a Doc containing a semi colon, \";\".--semicolon::Docsemicolon=char';'-- | Create a Doc containing newline, \"\\n\".--line::Docline=char'\n'---------------------------------------------------------------------------------- | Fill a doc to the supplied length, padding the right-hand-- side with spaces.---- Note - this function is expensive - it unrolls the functional-- representation of the String. -- -- Also it should only be used for single line Doc\'s.-- fill::Int->Doc->Docfillid=Doc1(padri' '$unDocd)padr::Int->Char->ShowS->ShowSpadricdf=step(length$df[])wheresteplen|len>=i=df|otherwise=df.showString(replicate(i-len)c)---------------------------------------------------------------------------------- | Punctuate the Doc list with the separator, producing a Doc. --punctuate::Doc->[Doc]->Docpunctuate_[]=emptypunctuate_[x]=xpunctuates(x:xs)=x><s><punctuatesxs-- | Enclose the final Doc within the first two.---- There are no spaces between the documents:---- > enclose l r d = l >< d >< r--enclose::Doc->Doc->Doc->Docencloselrd=l><d><r-- | Enclose the Doc within single quotes.--squotes::Doc->Docsquotes=enclose(char'\'')(char'\'')-- | Enclose the Doc within double quotes.--dquotes::Doc->Docdquotes=enclose(char'"')(char'"')-- | Enclose the Doc within parens @()@.--parens::Doc->Docparens=encloselparenrparen-- | Enclose the Doc within square brackets @[]@.--brackets::Doc->Docbrackets=encloselbracketrbracket-- | Enclose the Doc within curly braces @{}@.--braces::Doc->Docbraces=encloselbracerbrace-- | Enclose the Doc within angle brackets @\<\>@.--angles::Doc->Docangles=encloselanglerangle-- | Create a Doc containing a left paren, \'(\'.--lparen::Doclparen=char'('-- | Create a Doc containing a right paren, \')\'.--rparen::Docrparen=char')'-- | Create a Doc containing a left square bracket, \'[\'.--lbracket::Doclbracket=char'['-- | Create a Doc containing a right square bracket, \']\'.--rbracket::Docrbracket=char']'-- | Create a Doc containing a left curly brace, \'{\'.--lbrace::Doclbrace=char'{'-- | Create a Doc containing a right curly brace, \'}\'.--rbrace::Docrbrace=char'}'-- | Create a Doc containing a left angle bracket, \'\<\'.--langle::Doclangle=char'<'-- | Create a Doc containing a right angle bracket, \'\>\'.--rangle::Docrangle=char'>'-- | Comma separate the list of documents and enclose in square-- brackets.--list::[Doc]->Doclist=brackets.punctuatecomma-- | Comma separate the list of documents and enclose in parens.--tupled::[Doc]->Doctupled=parens.punctuatecomma-- | Separate the list with a semicolon and enclose in curly -- braces.--semiBraces::[Doc]->DocsemiBraces=braces.punctuatesemicolon-- | Horizontally indent a Doc.---- Note - this space-prefixes the Doc on /the current line/. It-- does not indent subsequent lines if the Doc spans multiple -- lines.--indent::Int->Doc->Docindentid|i<1=d|otherwise=Indentid