ampersand-3.1.0: src/Database/Design/Ampersand/ADL1/PrettyPrinters.hs
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Database.Design.Ampersand.ADL1.PrettyPrinters(prettyPrint)
where
import Text.PrettyPrint.Leijen
import Database.Design.Ampersand.Basics (fatalMsg)
import Database.Design.Ampersand.Core.ParseTree
import Database.Design.Ampersand.Input.ADL1.Lexer(keywords)
import Data.List (intercalate,intersperse)
import Data.List.Utils (replace)
import Data.Char (toUpper)
fatal :: Int -> String -> a
fatal = fatalMsg "PrettyPrinters"
prettyPrint :: Pretty a => a -> String
prettyPrint x = displayS (renderPretty rfrac col_width doc) ""
where col_width = 120
rfrac = 0.4
doc = pretty x
(<~>) :: Pretty b => Doc -> b -> Doc
(<~>) a b = a <+> pretty b
(<+\>) :: Doc -> Doc -> Doc
(<+\>) a b = a <$$> b
(<~\>) :: Pretty b => Doc -> b -> Doc
(<~\>) a b = a <+\> pretty b
perline :: Pretty a => [a] -> Doc
perline = vsep . map pretty
perlinePrefix :: Pretty a => String -> [a] -> Doc
perlinePrefix pref xs = vsep $ map addPrefix xs
where addPrefix x = text pref <+> pretty x
--quoteWith :: String -> String -> String -> Doc
--quoteWith l r x = enclose (text l) (text r) (text x)
quote :: String -> Doc
quote = text.show
--singlequote :: String -> Doc
--singlequote = squotes.text.escapeAll
--escapeAll :: [Char] -> [Char]
--escapeAll = escapeQuote.escapeBreaklines.escapeSlash
-- where escapeQuote = escape "\""
-- escapeBreaklines = replace "\n" "\\n"
-- escapeSlash = escape "\\"
-- escape x = replace x ("\\" ++ x)
quoteAll :: [String] -> [Doc]
quoteAll = map quote
quotePurpose :: String -> Doc
quotePurpose p = text "{+" </> escapeExpl p </> text "-}"
where escapeExpl = text.escapeCommentStart.escapeLineComment.escapeExplEnd
escapeCommentStart = escape "{-"
escapeLineComment = escape "--"
escapeExplEnd = escape "-}"
escape x = replace x (intersperse ' ' x)
isId :: String -> Bool
isId a = not (null a) && all isIdChar a && isFirstIdChar(head a) && a `notElem` keywords
where isFirstIdChar x = elem x $ "_"++['a'..'z']++['A'..'Z']
isIdChar x = isFirstIdChar x || elem x ['0'..'9']
isUpperId :: String -> Bool
isUpperId xs = isId xs && head xs `elem` ['A'..'Z']
maybeQuote :: String -> Doc
maybeQuote a = if isId a then text a else quote a
-- adds quotes unless it's an upper identifier
quoteConcept :: String -> Doc
quoteConcept a = if isUpperId a then text a else quote a
prettyhsep :: Pretty a => [a] -> Doc
prettyhsep = hsep . map pretty
commas :: [Doc] -> Doc
commas = encloseSep empty empty comma
listOf :: Pretty a => [a] -> Doc
listOf = commas . map pretty
listOfLists :: [[String]] -> Doc
listOfLists xs = commas $ map (hsep.quoteAll) xs
separate :: Pretty a => String -> [a] -> Doc
separate d xs = encloseSep empty empty (text d) $ map pretty xs
--TODO: This replace shouldn't be necessary, I don't know why quotes are getting into the Prel
-- Example to test: AmpersandData\FormalAmpersand\AST.adl
takeQuote :: String -> String
takeQuote = replace "\"" ""
labelArgs :: [[String]] -> Doc
labelArgs args = if null args || all null args
then empty
else braces $ listOfLists args
prettyLabel :: String -> [[String]] -> Doc
prettyLabel nm strs = maybeQuote nm <+> labelArgs strs
instance Pretty P_Context where
pretty (PCtx nm _ lang markup thms pats rs ds cs ks rrules rrels reprs vs gs ifcs ps pops sql php metas) =
text "CONTEXT"
<+> quoteConcept nm
<~> lang
<~> markup
<+\> perline metas
<+\> themes
<+\> perline ps
<+\> perline pats
<+\> perline rs
<+\> perline ds
<+\> perline cs
<+\> perline ks
<+\> perline rrules
<+\> perline rrels
<+\> perline reprs
<+\> perline vs
<+\> perline gs
<+\> perline ifcs
<+\> perline pops
<+\> perlinePrefix "SQLPLUG" sql
<+\> perlinePrefix "PHPPLUG" php
<+\> text "ENDCONTEXT"
where themes | null thms = empty
| otherwise = text "THEMES" <+> commas (map quoteConcept thms)
instance Pretty Meta where
pretty (Meta _ obj name val) =
text "META" <~> obj <+> quote name <+> quote val
instance Pretty MetaObj where
pretty ContextMeta = empty -- for the context meta we don't need a keyword
instance Pretty P_RoleRelation where
pretty (P_RR _ roles rels) =
text "ROLE" <+> listOf roles <+> text "EDITS" <+> listOf rels
instance Pretty P_RoleRule where
pretty (Maintain _ roles rules) =
text "ROLE" <+> listOf roles <+> text "MAINTAINS" <+> commas (map maybeQuote rules)
instance Pretty Role where
pretty (Role name) = maybeQuote name
pretty (Service name) = maybeQuote name
instance Pretty P_Pattern where
pretty (P_Pat _ nm rls gns dcs rruls rrels reprs cds ids vds xps pop _) =
text keyword
<+> quoteConcept nm
<+\> perline rls
<+\> perline gns
<+\> perline dcs
<+\> perline rruls
<+\> perline rrels
<+\> perline reprs
<+\> perline cds
<+\> perline ids
<+\> perline vds
<+\> perline xps
<+\> perline pop
<+> text ("END"++keyword)
where keyword = if null rruls && null rrels then "PATTERN" else "PROCESS"
instance Pretty P_Declaration where
pretty (P_Sgn nm sign prps pragma mean popu _ plug) =
text "RELATION" <+> text nm <~> sign <+> props <+> byplug <+\> pragmas <+\> prettyhsep mean <+\> content
where props = if prps == [Sym, Asy] then text "[PROP]"
else text "[" <> listOf prps <> text "]"
byplug | plug = text "BYPLUG"
| otherwise = empty
pragmas | null pragma = empty
| otherwise = text "PRAGMA" <+> hsep (map quote pragma)
content | null popu = empty
| otherwise = text "=\n[" <+> commas (map pretty popu) <+> text "]"
instance Pretty a => Pretty (Term a) where
pretty p = case p of
Prim a -> pretty a
-- level 0 (rule)
PEqu _ t1 t2 -> two t1 t2 "="
PInc _ t1 t2 -> two t1 t2 " |- "
-- level 1
PIsc _ t1 t2 -> two t1 t2 "/\\"
PUni _ t1 t2 -> two t1 t2 "\\/"
-- level 2
PDif _ t1 t2 -> two t1 t2 "-"
-- level 3
PLrs _ t1 t2 -> two t1 t2 "/"
PRrs _ t1 t2 -> two t1 t2 "\\"
PDia _ t1 t2 -> two t1 t2 "<>"
-- level 4
PCps _ t1 t2 -> two t1 t2 ";"
PRad _ t1 t2 -> two t1 t2 "!"
PPrd _ t1 t2 -> two t1 t2 "#"
-- level 5
PKl0 _ t -> pos t "*"
PKl1 _ t -> pos t "+"
PFlp _ t -> pos t "~"
PCpl _ t -> pre t " -" -- a double dash can happen when combined with PDif, therefore the extra space
-- level 6
PBrk _ t -> parens $ pretty t
where pos t op = pretty t <> text op
pre t op = text op <> pretty t
two t1 t2 op = pretty t1 <> text op <> pretty t2
instance Pretty TermPrim where
pretty p = case p of
PI _ -> text "I"
Pid _ concept -> text "I[" <> pretty concept <> text "]"
Patm _ val (Just concept) -> text (show val) <> text "[" <> pretty concept <> text "]"
Patm _ val Nothing -> text (show val)
PVee _ -> text "V"
Pfull _ s1 s2 -> text "V" <~> P_Sign s1 s2
PNamedR rel -> pretty rel
--instance Pretty PSingleton where
-- pretty = text . show
instance Pretty P_NamedRel where
pretty (PNamedRel _ str mpSign) = text (takeQuote str) <~> mpSign
instance Pretty a => Pretty (PairView a) where
pretty (PairView ss) = text "VIOLATION" <+> parens (listOf ss)
instance Pretty a => Pretty (PairViewSegment a) where
pretty (PairViewText _ str) = text "TXT" <+> quote str
pretty (PairViewExp _ srcTgt term) = pretty srcTgt <~> term
instance Pretty SrcOrTgt where
pretty Src = text "SRC"
pretty Tgt = text "TGT"
instance Pretty a => Pretty (P_Rule a) where
pretty (P_Ru _ nm expr mean msg viol) =
text "RULE" <+> name <~>
expr <+\>
perline mean <+\>
perline msg <~\>
viol
where name = if null nm then empty
else maybeQuote nm <> text ":"
instance Pretty ConceptDef where
pretty (Cd _ cpt plug def ref _) -- from, the last argument, is not used in the parser
= text "CONCEPT" <+> quoteConcept cpt <+> (if plug then text "BYPLUG" else empty)
<+> quote def <+> maybeText ref
where maybeText txt = if null txt then empty
else quote txt
instance Pretty P_Population where
pretty p = case p of
P_RelPopu _ _ _ nrel cs -> text "POPULATION" <+> pretty nrel <+> text "CONTAINS" <+> contents cs
P_CptPopu _ nm ps -> text "POPULATION" <+> quoteConcept nm <+> text "CONTAINS" <+> pretty ps
where contents = list . map pretty
instance Pretty Representation where
pretty (Repr _ cs tt) = text "REPRESENT" <+> listOf cs <~> text "TYPE" <+> pretty tt
instance Pretty TType where
pretty = text . show
instance Pretty P_Interface where
pretty (P_Ifc name klass prms args roles obj _ _) =
text "INTERFACE" <+> maybeQuote name <+> class_
<+> params <+> labelArgs args <+> iroles
<+> text ":" <~\> obj_ctx obj <~> obj_msub obj
where class_ = case klass of
Nothing -> empty
Just str -> text "CLASS" <+> quoteConcept str
params = if null prms then empty
else parens $ listOf prms
iroles = if null roles then empty
else text "FOR" <+> listOf roles
instance Pretty a => Pretty (P_ObjDef a) where
pretty (P_Obj nm _ ctx mView msub strs) =
prettyLabel nm strs <+> text ":"
<~> ctx <+> view mView <~> msub
where view Nothing = empty
view (Just v) = text ("<" ++ v ++ ">")
instance Pretty a => Pretty (P_SubIfc a) where
pretty p = case p of
P_Box _ c bs -> box_type c <+> text "[" <> listOf bs <> text "]"
P_InterfaceRef _ isLink str -> text ((if isLink then "LINKTO "else "")++"INTERFACE") <+> maybeQuote str
where box_type Nothing = text "BOX"
box_type (Just x) = text x -- ROWS, COLS, TABS
instance Pretty a => Pretty (P_IdentDf a) where
pretty (P_Id _ lbl cpt ats) =
text "IDENT" <+> maybeQuote lbl <+> text ":" <~> cpt <+> parens (listOf ats)
instance Pretty a => Pretty (P_IdentSegmnt a) where
pretty (P_IdentExp (P_Obj nm _ ctx mView _ strs)) =
if null nm
then pretty ctx -- no label
else prettyLabel nm strs <> text ":" <~> ctx <+> view mView
where view Nothing = empty
view (Just v) = pretty v
instance Pretty a => Pretty (P_ViewD a) where
pretty (P_Vd _ lbl cpt True Nothing ats) = -- legacy syntax
text "VIEW" <+> maybeQuote lbl <+> text ":"
<~> cpt <+> parens (listOf ats)
pretty (P_Vd _ lbl cpt isDefault html ats) = -- new syntax
text "VIEW" <+> maybeQuote lbl <+> text ":"
<~> cpt <+> (if isDefault then text "DEFAULT" else empty)
<+> braces (listOf ats) <~> html <+> text "ENDVIEW"
instance Pretty ViewHtmlTemplate where
pretty (ViewHtmlTemplateFile str) = text "HTML" <+> text "TEMPLATE" <+> quote str
instance Pretty a => Pretty (P_ViewSegmt a) where
pretty (P_ViewExp _ (P_Obj nm _ ctx _ _ _))
= maybeQuote nm <+> text ":" <~> ctx
pretty (P_ViewText _ txt) = text "TXT" <+> quote txt
pretty (P_ViewHtml _ htm) = text "PRIMHTML" <+> quote htm
instance Pretty PPurpose where
pretty (PRef2 _ obj markup refIds) =
text "PURPOSE" <~> obj <~> lang <+> refs refIds
<+\> quotePurpose (mString markup)
where lang = mFormat markup
refs rs = if null rs then empty
else text "REF" <+> quote (intercalate "; " rs)
instance Pretty PRef2Obj where
pretty p = case p of
PRef2ConceptDef str -> text "CONCEPT" <+> quoteConcept str
PRef2Declaration namedRel -> text "RELATION" <~> namedRel
PRef2Rule str -> text "RULE" <+> maybeQuote str
PRef2IdentityDef str -> text "IDENT" <+> maybeQuote str
PRef2ViewDef str -> text "VIEW" <+> maybeQuote str
PRef2Pattern str -> text "PATTERN" <+> maybeQuote str
PRef2Interface str -> text "INTERFACE" <+> maybeQuote str
PRef2Context str -> text "CONTEXT" <+> maybeQuote str
instance Pretty PMeaning where
pretty (PMeaning markup) = text "MEANING" <~> markup
instance Pretty PMessage where
pretty (PMessage markup) = text "MESSAGE" <~> markup
instance Pretty P_Concept where
pretty (PCpt name) = quoteConcept name
pretty P_Singleton = text "ONE"
instance Pretty P_Sign where
pretty (P_Sign src tgt) = brackets (pretty src <> maybeTgt)
where maybeTgt = if src `equal` tgt then empty
else text "*" <> pretty tgt
equal (PCpt x) (PCpt y) = x == y
equal P_Singleton P_Singleton = True
equal _ _ = False
instance Pretty P_Gen where
pretty p = case p of
PGen _ spc gen -> text "CLASSIFY" <~> spc <+> text "ISA" <~> gen
P_Cy _ spc rhs -> text "CLASSIFY" <~> spc <+> text "IS" <+> separate "/\\" rhs
instance Pretty Lang where
pretty Dutch = text "IN DUTCH"
pretty English = text "IN ENGLISH"
instance Pretty P_Markup where
pretty (P_Markup lang format str) =
pretty lang <~> format <+\> quotePurpose str
instance Pretty PandocFormat where
pretty p = case p of
ReST -> text "REST"
HTML -> text "HTML"
LaTeX -> text "LATEX"
Markdown -> text "MARKDOWN"
instance Pretty Prop where
pretty p = text $ case p of
Uni -> "UNI"
Inj -> "INJ"
Sur -> "SUR"
Tot -> "TOT"
Sym -> "SYM"
Asy -> "ASY"
Trn -> "TRN"
Rfx -> "RFX"
Irf -> "IRF"
Aut -> "AUT"
Prop -> "PROP"
instance Pretty PAtomPair where
pretty (PPair _ l r) = text "(" <+> pretty l
<~> text "," <+> pretty r
<~> text ")"
instance Pretty PAtomValue where
pretty pav =
case pav of
PSingleton _ _ mav -> case mav of
Nothing -> fatal 405 $ "The singleton "++show pav++" has no type, so it cannot be accuratly prettyprinted in a population statement."
Just val -> pretty val
ScriptString _ s -> text . show $ s
XlsxString _ s -> text . show $ s
ScriptInt _ i -> text . show $ i
ScriptFloat _ d -> text . show $ d
XlsxDouble _ _ -> fatal 267 $ "We got a value from an .xlsx file, which has to be shown in an expression, however the technicaltype is not known"
ComnBool _ b -> text . map toUpper . show $ b
ScriptDate _ x -> text . show $ x
ScriptDateTime _ x -> text . show $ x