packages feed

language-smtlib-0.2.0.0: src/Language/SMTLIB/Printer/Class.hs

{-# LANGUAGE FlexibleInstances #-}

-- | The 'Pretty' class that renders the SMT-LIB AST back to concrete syntax,
-- together with the symbol\/keyword\/string quoting rules.  Annotations are
-- ignored, so @pretty x == pretty (noAnn x)@ for every node.
--
-- Quoting is centralised here ('prettySymbol', 'prettyKeyword',
-- 'prettyStringLit') and always derives the surface form from the /logical/
-- value, which guarantees @parse . render == id@ for well-formed trees.
module Language.SMTLIB.Printer.Class
  ( Pretty(..)
  , prettySymbol
  , prettyKeyword
  , prettyStringLit
  , prettyBool
  ) where

import Data.Text (Text)
import qualified Data.Text as T
import Prettyprinter (Doc, (<+>), hsep, parens)
import qualified Prettyprinter as PP

import Language.SMTLIB.Internal.Lexical
  (escapeStringLit, symbolNeedsQuoting)
import Language.SMTLIB.Syntax.Attribute
import Language.SMTLIB.Syntax.Command
import Language.SMTLIB.Syntax.Constant
import Language.SMTLIB.Syntax.Datatype
import Language.SMTLIB.Syntax.Identifier
import Language.SMTLIB.Syntax.Response
import Language.SMTLIB.Syntax.Term

-- | Render an AST node to a 'Doc'.  The renderers in
-- "Language.SMTLIB.Printer" turn the result into 'Text'.
class Pretty a where
  pretty :: a -> Doc ann

text :: Text -> Doc ann
text = PP.pretty

int :: Integer -> Doc ann
int = PP.pretty

-- | Render a symbol, quoting with @|...|@ when it is not a simple symbol.
prettySymbol :: Symbol -> Doc ann
prettySymbol s
  | symbolNeedsQuoting s = text (T.concat ["|", s, "|"])
  | otherwise            = text s

-- | Render a symbol that appears in a @match@ pattern binding position.  The
-- @_@ wildcard (SMT-LIB 2.7) is printed bare; every other symbol follows the
-- usual quoting rules (note @_@ is otherwise a reserved word).
prettyPatternSymbol :: Symbol -> Doc ann
prettyPatternSymbol s
  | s == "_"  = text "_"
  | otherwise = prettySymbol s

-- | Render a keyword (stored without its colon) as @:keyword@.
prettyKeyword :: Keyword -> Doc ann
prettyKeyword k = text (T.cons ':' k)

-- | Render the logical value of a string as a quoted, escaped string literal.
prettyStringLit :: Text -> Doc ann
prettyStringLit s = text (T.concat ["\"", escapeStringLit s, "\""])

-- | Render a boolean as @true@ \/ @false@.
prettyBool :: Bool -> Doc ann
prettyBool True  = text "true"
prettyBool False = text "false"

-- | @(head args...)@, dropping to just @head@ when there are no args.
app :: Doc ann -> [Doc ann] -> Doc ann
app hd [] = hd
app hd xs = parens (hsep (hd : xs))

instance Pretty (SpecConstant a) where
  pretty = \case
    SCNumeral n _     -> int n
    SCDecimal t _     -> text t
    SCHexadecimal t _ -> text (T.append "#x" t)
    SCBinary t _      -> text (T.append "#b" t)
    SCString t _      -> prettyStringLit t

instance Pretty (Index a) where
  pretty = \case
    IxNumeral n _ -> int n
    IxSymbol s _  -> prettySymbol s

instance Pretty (Identifier a) where
  pretty (Identifier s [] _)  = prettySymbol s
  pretty (Identifier s ixs _) =
    parens (hsep (text "_" : prettySymbol s : map pretty ixs))

instance Pretty (Sort a) where
  pretty (Sort i args _) = app (pretty i) (map pretty args)

instance Pretty (QualIdentifier a) where
  pretty = \case
    QIdentifier i _     -> pretty i
    QIdentifierAs i s _ -> parens (text "as" <+> pretty i <+> pretty s)

instance Pretty (SExpr a) where
  pretty = \case
    SEConstant c _ -> pretty c
    SESymbol s _   -> prettySymbol s
    SEKeyword k _  -> prettyKeyword k
    SEReserved s _ -> text s
    SEList xs _    -> parens (hsep (map pretty xs))

instance Pretty (AttributeValue a) where
  pretty = \case
    AVConstant c _ -> pretty c
    AVSymbol s _   -> prettySymbol s
    AVSExpr xs _   -> parens (hsep (map pretty xs))

instance Pretty (Attribute a) where
  pretty = \case
    Attribute k _       -> prettyKeyword k
    AttributeWith k v _ -> prettyKeyword k <+> pretty v

instance Pretty (VarBinding a) where
  pretty (VarBinding s t _) = parens (prettySymbol s <+> pretty t)

instance Pretty (SortedVar a) where
  pretty (SortedVar s srt _) = parens (prettySymbol s <+> pretty srt)

instance Pretty (Pattern a) where
  pretty = \case
    PVar s _     -> prettyPatternSymbol s
    PCtor c xs _ -> parens (hsep (prettySymbol c : map prettyPatternSymbol xs))

instance Pretty (MatchCase a) where
  pretty (MatchCase p t _) = parens (pretty p <+> pretty t)

instance Pretty (Term a) where
  pretty = \case
    TConstant c _  -> pretty c
    TQualIdent q _ -> pretty q
    TApp q args _  -> parens (hsep (pretty q : map pretty args))
    TLet bs t _    ->
      parens (text "let" <+> parens (hsep (map pretty bs)) <+> pretty t)
    TLambda vs t _ ->
      parens (text "lambda" <+> parens (hsep (map pretty vs)) <+> pretty t)
    TForall vs t _ ->
      parens (text "forall" <+> parens (hsep (map pretty vs)) <+> pretty t)
    TExists vs t _ ->
      parens (text "exists" <+> parens (hsep (map pretty vs)) <+> pretty t)
    TMatch t cs _  ->
      parens (text "match" <+> pretty t <+> parens (hsep (map pretty cs)))
    TAnnot t as _  ->
      parens (hsep (text "!" : pretty t : map pretty as))

instance Pretty (SortDec a) where
  pretty (SortDec s n _) = parens (prettySymbol s <+> int n)

instance Pretty (SelectorDec a) where
  pretty (SelectorDec s srt _) = parens (prettySymbol s <+> pretty srt)

instance Pretty (ConstructorDec a) where
  pretty (ConstructorDec s sels _) =
    parens (hsep (prettySymbol s : map pretty sels))

instance Pretty (DatatypeDec a) where
  pretty (DatatypeDec [] ctors _) = parens (hsep (map pretty ctors))
  pretty (DatatypeDec ps ctors _) =
    parens (text "par"
            <+> parens (hsep (map prettySymbol ps))
            <+> parens (hsep (map pretty ctors)))

instance Pretty (FunctionDec a) where
  pretty (FunctionDec s vs srt _) =
    parens (prettySymbol s <+> parens (hsep (map pretty vs)) <+> pretty srt)

-- | A t'FunctionDef' renders /without/ surrounding parens, since the enclosing
-- command supplies them.
instance Pretty (FunctionDef a) where
  pretty (FunctionDef s vs srt t _) =
    prettySymbol s <+> parens (hsep (map pretty vs)) <+> pretty srt <+> pretty t

instance Pretty (Option a) where
  pretty = \case
    DiagnosticOutputChannel s _   -> text ":diagnostic-output-channel" <+> prettyStringLit s
    GlobalDeclarations b _        -> text ":global-declarations" <+> prettyBool b
    InteractiveMode b _           -> text ":interactive-mode" <+> prettyBool b
    PrintSuccess b _              -> text ":print-success" <+> prettyBool b
    ProduceAssertions b _         -> text ":produce-assertions" <+> prettyBool b
    ProduceAssignments b _        -> text ":produce-assignments" <+> prettyBool b
    ProduceModels b _             -> text ":produce-models" <+> prettyBool b
    ProduceProofs b _             -> text ":produce-proofs" <+> prettyBool b
    ProduceUnsatAssumptions b _   -> text ":produce-unsat-assumptions" <+> prettyBool b
    ProduceUnsatCores b _         -> text ":produce-unsat-cores" <+> prettyBool b
    RandomSeed n _                -> text ":random-seed" <+> int n
    RegularOutputChannel s _      -> text ":regular-output-channel" <+> prettyStringLit s
    ReproducibleResourceLimit n _ -> text ":reproducible-resource-limit" <+> int n
    Verbosity n _                 -> text ":verbosity" <+> int n
    OptionAttribute attr _        -> pretty attr

instance Pretty (InfoFlag a) where
  pretty = \case
    AllStatistics _        -> text ":all-statistics"
    AssertionStackLevels _ -> text ":assertion-stack-levels"
    Authors _              -> text ":authors"
    ErrorBehaviorFlag _    -> text ":error-behavior"
    InfoName _             -> text ":name"
    ReasonUnknownFlag _    -> text ":reason-unknown"
    InfoVersion _          -> text ":version"
    InfoFlagKeyword k _    -> prettyKeyword k

instance Pretty (Command a) where
  pretty = \case
    SetLogic s _            -> parens (text "set-logic" <+> prettySymbol s)
    SetOption o _           -> parens (text "set-option" <+> pretty o)
    SetInfo attr _          -> parens (text "set-info" <+> pretty attr)
    DeclareSort s n _       -> parens (text "declare-sort" <+> prettySymbol s <+> int n)
    DeclareSortParameter s _ -> parens (text "declare-sort-parameter" <+> prettySymbol s)
    DefineSort s args srt _ ->
      parens (text "define-sort" <+> prettySymbol s
              <+> parens (hsep (map prettySymbol args)) <+> pretty srt)
    DeclareConst s srt _    -> parens (text "declare-const" <+> prettySymbol s <+> pretty srt)
    DefineConst s srt t _   ->
      parens (text "define-const" <+> prettySymbol s <+> pretty srt <+> pretty t)
    DeclareFun s args ret _ ->
      parens (text "declare-fun" <+> prettySymbol s
              <+> parens (hsep (map pretty args)) <+> pretty ret)
    DefineFun fd _          -> parens (text "define-fun" <+> pretty fd)
    DefineFunRec fd _       -> parens (text "define-fun-rec" <+> pretty fd)
    DefineFunsRec decs ts _ ->
      parens (text "define-funs-rec"
              <+> parens (hsep (map pretty decs))
              <+> parens (hsep (map pretty ts)))
    DeclareDatatype s dd _  -> parens (text "declare-datatype" <+> prettySymbol s <+> pretty dd)
    DeclareDatatypes sds dds _ ->
      parens (text "declare-datatypes"
              <+> parens (hsep (map pretty sds))
              <+> parens (hsep (map pretty dds)))
    Push n _                -> parens (text "push" <+> int n)
    Pop n _                 -> parens (text "pop" <+> int n)
    Reset _                 -> parens (text "reset")
    ResetAssertions _       -> parens (text "reset-assertions")
    Assert t _              -> parens (text "assert" <+> pretty t)
    CheckSat _              -> parens (text "check-sat")
    CheckSatAssuming ts _   -> parens (text "check-sat-assuming" <+> parens (hsep (map pretty ts)))
    GetAssertions _         -> parens (text "get-assertions")
    GetModel _              -> parens (text "get-model")
    GetProof _              -> parens (text "get-proof")
    GetUnsatCore _          -> parens (text "get-unsat-core")
    GetUnsatAssumptions _   -> parens (text "get-unsat-assumptions")
    GetValue ts _           -> parens (text "get-value" <+> parens (hsep (map pretty ts)))
    GetAssignment _         -> parens (text "get-assignment")
    GetOption k _           -> parens (text "get-option" <+> prettyKeyword k)
    GetInfo flag _          -> parens (text "get-info" <+> pretty flag)
    Echo s _                -> parens (text "echo" <+> prettyStringLit s)
    Exit _                  -> parens (text "exit")
    UnknownCommand kw args _ -> parens (hsep (text kw : map pretty args))

instance Pretty CheckSatResponse where
  pretty Sat     = text "sat"
  pretty Unsat   = text "unsat"
  pretty Unknown = text "unknown"

instance Pretty ErrorBehavior where
  pretty ImmediateExit       = text "immediate-exit"
  pretty ContinuedExecution  = text "continued-execution"

instance Pretty (ReasonUnknown a) where
  pretty = \case
    RUMemout     -> text "memout"
    RUIncomplete -> text "incomplete"
    RUOther e    -> pretty e

instance Pretty (InfoResponse a) where
  pretty = \case
    IRAssertionStackLevels n -> text ":assertion-stack-levels" <+> int n
    IRAuthors s              -> text ":authors" <+> prettyStringLit s
    IRErrorBehavior b        -> text ":error-behavior" <+> pretty b
    IRName s                 -> text ":name" <+> prettyStringLit s
    IRReasonUnknown r        -> text ":reason-unknown" <+> pretty r
    IRVersion s              -> text ":version" <+> prettyStringLit s
    IRAttribute attr         -> pretty attr

instance Pretty (ValuationPair a) where
  pretty (ValuationPair t v) = parens (pretty t <+> pretty v)

instance Pretty (ModelResponse a) where
  pretty = \case
    MRDefineFun fd        -> parens (text "define-fun" <+> pretty fd)
    MRDefineFunRec fd     -> parens (text "define-fun-rec" <+> pretty fd)
    MRDefineFunsRec ds ts ->
      parens (text "define-funs-rec"
              <+> parens (hsep (map pretty ds))
              <+> parens (hsep (map pretty ts)))

instance Pretty (CommandResponse a) where
  pretty = \case
    RSuccess               -> text "success"
    RUnsupported           -> text "unsupported"
    RError s               -> parens (text "error" <+> prettyStringLit s)
    RCheckSat r            -> pretty r
    REcho s                -> prettyStringLit s
    RGetAssertions ts      -> parens (hsep (map pretty ts))
    RGetAssignment ps      -> parens (hsep (map assignPair ps))
    RGetInfo irs           -> parens (hsep (map pretty irs))
    RGetModel ms           -> parens (hsep (map pretty ms))
    RGetOption v           -> pretty v
    RGetProof e            -> pretty e
    RGetUnsatAssumptions ts -> parens (hsep (map pretty ts))
    RGetUnsatCore ss       -> parens (hsep (map prettySymbol ss))
    RGetValue vps          -> parens (hsep (map pretty vps))
    ROther e               -> pretty e
    where assignPair (s, b) = parens (prettySymbol s <+> prettyBool b)