1N/ACopyright : (c) Christian Maeder and Uni Bremen 2006
1N/AMaintainer : maeder@tzi.de
1N/AStability : provisional
1N/APortability : portable
1N/Adocument data type for displaying (heterogenous) CASL specifications
1N/Aat least as plain text and latex (and maybe in html as well)
1N/Ainspired by John Hughes's and Simon Peyton Jones's Pretty Printer Combinators
1N/ADaan Leijen's PPrint: A prettier printer 2001, Olaf Chiti's
1N/APretty printing with lazy Dequeues 2003
1N/A ( -- * The document type
1N/A -- * Primitive Documents
1N/A -- * Converting values into documents
1N/A -- * Wrapping documents in delimiters
1N/A -- * Combining documents
1N/A -- * docifying annotations and ids
1N/A -- * transforming to existing formats
IdKind | IdSymb | Symbol | Comment | Keyword | TopKey | Indexed | StructId
data Format = Small | FlushRight
= Vert -- ($+$) (no support for $$!)
| HorizOrVert -- either Horiz or Vert
= Empty -- creates an empty line if composed vertically
| AnnoDoc Annotation -- we know how to print annotations
| IdDoc Id -- for plain ids outside applications
| IdApplDoc Id [Doc] -- for mixfix applications and literal terms
| Text TextKind String -- non-empty and no white spaces inside
| Attr Format Doc -- for annotations
IdentBy refDoc startDoc hangDoc
is: startDoc <> (length refDoc - length startDoc) <> hangDoc
(
i.e indent hangBlock by refDoc and put it beside startDoc)
is: startDoc <> hangDoc if it fits on a single line!
empty :: Doc -- ^ An empty document
semi :: Doc -- ^ A ';' character
comma :: Doc -- ^ A ',' character
colon :: Doc -- ^ A ':' character
-- the only legal white space within Text
space :: Doc -- ^ A horizontal space (omitted at end of line)
equals :: Doc -- ^ A '=' character
-- use symbol for signs that need to be put in mathmode for latex
-- for text within comments
commentText :: String -> Doc
commentText = Text Comment
-- don't escape this strings since they are interpreted by latex
lparen, rparen, lbrack, rbrack, lbrace, rbrace, quote, doubleQuote :: Doc
lbrace = symbol "{" -- to allow for latex translations
doubleQuote = symbol "\""
parens :: Doc -> Doc -- ^ Wrap document in @(...)@
parens d = fcat [lparen, d, rparen]
brackets :: Doc -> Doc -- ^ Wrap document in @[...]@
brackets d = fcat [lbrack, d, rbrack]
braces :: Doc -> Doc -- ^ Wrap document in @{...}@
braces d = cat [lbrace <> d, rbrace]
quotes :: Doc -> Doc -- ^ Wrap document in @\'...\'@
quotes d = hcat [quote, d, quote]
doubleQuotes :: Doc -> Doc -- ^ Wrap document in @\"...\"@
doubleQuotes d = hcat [doubleQuote, d, doubleQuote]
(<>) :: Doc -> Doc -> Doc -- ^Beside
a <> b = Cat Horiz [a, b]
hcat :: [Doc] -> Doc -- ^List version of '<>'
(<+>) :: Doc -> Doc -> Doc -- ^Beside, separated by space
a <+> b = if isEmpty a then b else
if isEmpty b then a else Cat Horiz [a, space, b]
punctuate :: Doc -> [Doc] -> [Doc]
punctuate d l = case l of
x : r@(_ : _) -> (x <> d) : punctuate d r
hsep :: [Doc] -> Doc -- ^List version of '<+>'
hsep = hcat . punctuate space
($+$) :: Doc -> Doc -> Doc; -- ^Above, without dovetailing.
a $+$ b = Cat Vert [a, b]
vcat :: [Doc] -> Doc -- ^List version of '$+$'
cat :: [Doc] -> Doc -- ^ Either hcat or vcat
sep :: [Doc] -> Doc -- ^ Either hsep or vcat
sep = cat . punctuate space
fcat :: [Doc] -> Doc -- ^ \"Paragraph fill\" version of cat
fsep :: [Doc] -> Doc -- ^ \"Paragraph fill\" version of sep
fsep = fcat . punctuate space
keyword, topKey, indexed, structId :: String -> Doc
-- | docs possibly rendered differently for Text or LaTeX
dot, bullet, defn, less, lambda, mapsto, funArrow, pfun, cfun, pcfun,
exequal, forallDoc, exists, unique, cross, bar, notDoc, inDoc, andDoc,
orDoc, implies, equiv :: Doc
forallDoc = symbol forallS
unique = symbol existsUnique
-- | we know how to print annotations
annoDoc :: Annotation -> Doc
-- | for plain ids outside applications
-- | for mixfix applications and literal terms (may print \"\" for empty)
idApplDoc :: Id -> [Doc] -> Doc
-- | put document as far to the right as fits (for annotations)
flushRight = Attr FlushRight
{- | print second argument and then indent the last one by the width
indentBy :: Doc -> Doc -> Doc -> Doc
data DocRecord a = DocRecord
, foldAnnoDoc :: Doc -> Annotation -> a
, foldIdDoc :: Doc -> Id -> a
, foldIdApplDoc :: Doc -> Id -> [a] -> a
, foldText :: Doc -> TextKind -> String -> a
, foldCat :: Doc -> ComposeKind -> [a] -> a
, foldAttr :: Doc -> Format -> a -> a
, foldIndentBy :: Doc -> a -> a -> a -> a
foldDoc :: DocRecord a -> Doc -> a
AnnoDoc a -> foldAnnoDoc r d a
IdDoc i -> foldIdDoc r d i
IdApplDoc i l -> foldIdApplDoc r d i $ map (foldDoc r) l
Text k s -> foldText r d k s
Cat k l -> foldCat r d k $ map (foldDoc r) l
Attr a e -> foldAttr r d a $ foldDoc r e
foldIndentBy r d (foldDoc r e) (foldDoc r f) $ foldDoc r g
idRecord :: DocRecord Doc
{ foldEmpty = \ _ -> Empty
, foldAnnoDoc = \ _ -> AnnoDoc
, foldIdDoc = \ _ -> IdDoc
, foldIdApplDoc = \ _ -> IdApplDoc
, foldIndentBy = \ _ -> IndentBy
-- | simple conversion to a standard text document
toText = toHPJDoc emptyGlobalAnnos
-- | simple conversion to a standard text document
toHPJDoc ga = foldDoc anyRecord
, foldCat = \ _ c -> case c of
, foldAttr = \ _ k d -> case k of
FlushRight -> let l = length $ show d in
, foldIndentBy = \ _ d1 d2 d3 ->
toLatex ga = let dm =
Map.map (Map.! DF_LATEX) .
, foldText = \ _ k s -> textToLatex False k s
, foldCat = \ _ c l -> case c of
_ -> latex_macro setTab Pretty.<>
latex_macro startTab Pretty.<> (case c of
, foldAttr = \ o k d -> case k of
FlushRight -> flushright d
Attr Small (Text j s) -> textToLatex True j s
, foldIndentBy = \ _ d1 d2 d3 ->
} . makeSmall False . codeOut ga (Just DF_LATEX) dm
textToLatex :: Bool -> TextKind -> String ->
Pretty.DoctextToLatex b k s = let e = escape_comment_latex s in
if elem s $ map (: []) ",;[]() "
then makeSmallLatex b $ casl_normal_latex s
IdKind -> makeSmallLatex b $ hc_sty_id e
IdSymb -> makeSmallLatex b $ hc_sty_axiom e
Symbol -> makeSmallLatex b $ symbolToLatex s
Comment -> (if b then makeSmallLatex b . casl_comment_latex
else casl_normal_latex) e
-- multiple spaces should be replaced by \hspace
Keyword -> (if b then makeSmallLatex b . hc_sty_small_keyword
else hc_sty_plain_keyword) e
TopKey -> hc_sty_casl_keyword e
Indexed -> hc_sty_structid_indexed e
StructId -> hc_sty_structid e
Native -> makeSmallLatex b $ hc_sty_axiom s
if b then
Pretty.hcat [latex_macro startAnno, d, latex_macro endAnno]
$ escape_latex s) s latexSymbols
, (diamondS, hc_sty_axiom "\\Diamond")
, (percentS, hc_sty_small_keyword "\\%")
, (percents, hc_sty_small_keyword "\\%\\%")
, ("{", casl_normal_latex "\\{")
, ("}", casl_normal_latex "\\}")
, ("__", hc_sty_axiom "\\_\\_")
, (lambdaS, hc_sty_axiom "\\lambda")
, (pContFun, pcfun_latex)
, (exEqual, exequal_latex)
, (forallS, forall_latex)
, (existsS, exists_latex)
, (existsUnique, unique_latex)
, (prodS, hc_sty_axiom "\\times")
, (notS, hc_sty_axiom "\\neg")
, (inS, hc_sty_axiom "\\in")
, (lAnd, hc_sty_axiom "\\wedge")
, (lOr, hc_sty_axiom "\\vee")
, (implS, hc_sty_axiom "\\Rightarrow")
, (equivS, hc_sty_axiom "\\Leftrightarrow") ]
makeSmall :: Bool -> Doc -> Doc
makeSmall b = foldDoc idRecord
{ foldAttr = \ _ k d -> makeSmall (case k of
, foldCat = \ (Cat c l) _ _ -> Cat c $ map (makeSmall b) l
, foldIndentBy = \ (IndentBy d1 d2 d3) _ _ _ ->
IndentBy (makeSmall b d1) (makeSmall b d2) $ makeSmall b d3
, foldText = \ d _ _ -> if b then Attr Small d else d
{- | transform document according to a specific display map and other
global annotations like precedences, associativities, and literal
codeOut :: GlobalAnnos -> Maybe Display_format ->
Map.Map Id [Token] -> Doc
codeOut ga d m = foldDoc idRecord
{ foldAnnoDoc = \ _ -> small . codeOutAnno d m
, foldIdDoc = \ _ -> codeOutId m
, foldIdApplDoc = codeOutAppl ga d m
codeToken :: String -> Doc
h : _ -> (if isAlphaNum h || elem h "._'" then text else Text IdSymb) s
codeOrigId ::
Map.Map Id [Token] -> Id -> [Doc]
codeOrigId m (Id ts cs _) = let
(toks, places) = splitMixToken ts
conv = map (codeToken . tokStr) in
else conv toks ++ codeCompIds m cs : conv places
codeCompIds ::
Map.Map Id [Token] -> [Id] -> Doc
hcat $ lbrack : intersperse comma (map (codeOutId m) cs) ++ [rbrack]
codeIdToks :: [Token] -> [Doc]
codeIdToks = map (\ t -> let s = tokStr t in
if isPlace t then symbol s else native s)
codeOutId ::
Map.Map Id [Token] -> Id -> Doc
Nothing -> codeOrigId m i
annoLine :: String -> Doc
annoLine w = percent <> keyword w
annoLparen :: String -> Doc
annoLparen w = percent <> keyword w <> lparen
wrapAnnoLines :: Maybe Display_format -> Doc -> [String] -> Doc -> Doc
wrapAnnoLines d a l b = case map (commentText .
maybe id (const $ dropWhile isSpace) d) l of
Nothing -> vcat $ fcat [a, x] : init r ++ [fcat [last r, b]]
Just _ -> a <+> vcat ds <> b
percent = symbol percentS
annoRparen = rparen <> percent
cCommaT ::
Map.Map Id [Token] -> [Id] -> [Doc]
cCommaT m = punctuate comma . map (codeOutId m)
hCommaT ::
Map.Map Id [Token] -> [Id] -> Doc
hCommaT m = hsep . cCommaT m
fCommaT ::
Map.Map Id [Token] -> [Id] -> Doc
fCommaT m = fsep . cCommaT m
codeOutAnno :: Maybe Display_format ->
Map.Map Id [Token] -> Annotation -> Doc
codeOutAnno d m a = case a of
Unparsed_anno aw at _ -> case at of
Line_anno s -> (case aw of
Annote_word w -> annoLine w
Comment_start -> symbol percents) <> commentText s
Group_anno l -> case aw of
Annote_word w -> wrapAnnoLines d (annoLparen w) l annoRparen
Comment_start -> wrapAnnoLines d (percent <> lbrace) l
Display_anno i ds _ -> annoLparen displayS <> fsep
( hcat (codeOrigId m i) :
map ( \ (df, s) -> percent <> text (lookupDisplayFormat df)
<+> maybe (commentText s) (const $ codeOutId m i)
List_anno i1 i2 i3 _ -> annoLine listS <+> hCommaT m [i1, i2, i3]
Number_anno i _ -> annoLine numberS <+> codeOutId m i
Float_anno i1 i2 _ -> annoLine floatingS <+> hCommaT m [i1, i2]
String_anno i1 i2 _ -> annoLine stringS <+> hCommaT m [i1, i2]
Prec_anno p l1 l2 _ -> annoLparen precS <>
fsep [ braces $ fCommaT m l1
Higher -> symbol greaterS
BothDirections -> symbol lessS <> symbol greaterS
NoDirection -> symbol greaterS <> symbol lessS
Assoc_anno s l _ -> annoLparen (case s of
<> fCommaT m l <> annoRparen
Label l _ -> wrapAnnoLines d (annoLparen "") l annoRparen
Semantic_anno sa _ -> annoLine $ lookupSemanticAnno sa
splitDoc :: Doc -> Maybe (Id, [Doc])
IdApplDoc i l -> Just (i, l)
data Weight = Weight Int Id Id Id -- top, left, right
-- print literal terms and mixfix applications
codeOutAppl :: GlobalAnnos -> Maybe Display_format ->
Map.Map Id [Token]
-> Doc -> Id -> [Doc] -> Doc
codeOutAppl ga md m origDoc _ args = case origDoc of
IdApplDoc i@(Id ts cs _) aas ->
let mk t = codeToken $ tokStr t
p = getSimpleIdPrec precs i
doSplit = maybe (error "doSplit") id . splitDoc
mkList op largs cl = fsep $ codeOutId m op : punctuate comma
(map (codeOut ga md m) largs)
in if isGenNumber splitDoc ga i aas then
mk $ toNumber doSplit i aas
else if isGenFrac splitDoc ga i aas then
else if isGenFloat splitDoc ga i aas then
mk $ toFloat doSplit ga aas
else if isGenString splitDoc ga i aas then
mk $ toString doSplit ga i aas
else if isGenList splitDoc ga i aas then
toMixfixList mkList doSplit ga i aas
else if null args || length args /= placeCount i then
codeOutId m i <> if null args then empty else
parens (fsep $ punctuate comma args)
parArgs = reverse $ foldl ( \ l (arg, d) ->
in case getWeight ga arg of
Just (Weight q ta la ra) ->
(if isKnownArg assocs precs i ta then
if checkArg ARight ga (i, p) (ta, q) ra
else if isRightArg i l then
if checkArg ALeft ga (i, p) (ta, q) la
else pArg) : l) [] $ zip aas args
fts = fst $ splitMixToken ts
(rArgs, fArgs) = mapAccumL ( \ ac t ->
if isPlace t then case ac of
else (ac, codeToken $ tokStr t)) parArgs fts
in fsep $ fArgs ++ (if null cs then [] else [codeCompIds m cs])
getWeight :: GlobalAnnos -> Doc -> Maybe Weight
IdApplDoc i aas@(hd : _) -> let p = getSimpleIdPrec precs i in
if isGenLiteral splitDoc ga i aas then Nothing else
let lw = case getWeight ga hd of
Just (Weight _ _ l _) -> nextWeight ALeft ga i l
rw = case getWeight ga $ last aas of
Just (Weight _ _ _ r) -> nextWeight ARight ga i r
in Just $ Weight p i lw rw
isKnownArg :: AssocMap -> PrecMap -> Id -> Id -> Bool
isKnownArg assocs p op arg = let m = precMap p in