{-# OPTIONS -Wall #-}---------------------------------------------------------------------------------- |-- Module : Wumpus.Basic.Utils.FormatCombinators-- Copyright : (c) Stephen Tetley 2010-- License : BSD3---- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>-- Stability : highly unstable-- Portability : GHC---- Formatting combinators - pretty printers without the fitting.---- Note - indentation support is very limited. Generally one -- should use a proper pretty printing library.-- --------------------------------------------------------------------------------moduleWumpus.Basic.Utils.FormatCombinators(Doc,DocS,Format(..),empty,showsDoc,(<>),(<+>),vconcat,separate,hcat,hsep,vcat,text,char,int,integer,integral,float,double,hex4,space,comma,semicolon,line,fill,fillStringR,fillStringL,punctuate,enclose,squotes,dquotes,parens,brackets,braces,angles,lparen,rparen,lbracket,rbracket,lbrace,rbrace,langle,rangle,list,tupled,semiBraces,indent,writeDoc)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=runDocinstanceMonoidDocwheremempty=emptymappend=(<>)classFormatawhereformat::a->Doc--------------------------------------------------------------------------------infixr6<>,<+>-- | Create an empty, zero length document.--empty::Docempty=Doc1id-- | Create a document from a ShowS function.--showsDoc::ShowS->DocshowsDoc=Doc1-- | Horizontally concatenate two documents with no space -- between them.-- (<>)::Doc->Doc->Doca<>b=Joinab-- | 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-- | Show the Int as hexadecimal, padding up to 4 digits if -- necessary.---- No trucation occurs if the value has more than 4 digits.--hex4::Int->Dochex4n|n<0x0010=text"000"<>showsDoc(showHexn)|n<0x0100=text"00"<>showsDoc(showHexn)|n<0x1000=text"0"<>showsDoc(showHexn)|otherwise=showsDoc(showHexn)-- | 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)-- | String version of 'fill'.---- This is more efficient than 'fill' as the input is a string-- so its length is more accesible.---- Padding is the space character appended to the right.-- fillStringR::Int->String->DocfillStringRis=step(lengths)wherestepn|n>=i=textsstepn=texts<>text(replicate(i-n)' ')-- | Left-padding version of 'fillStringR'.--fillStringL::Int->String->DocfillStringLis=step(lengths)wherestepn|n>=i=textsstepn=text(replicate(i-n)' ')<>texts---------------------------------------------------------------------------------- | 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---------------------------------------------------------------------------------- | Write a Doc to file.--writeDoc::FilePath->Doc->IO()writeDocfilepathd=writeFilefilepath$showd