language-rust-0.1.0.0: src/Language/Rust/Pretty.hs
{-|
Module : Language.Rust.Pretty
Description : Pretty printing
Copyright : (c) Alec Theriault, 2017-2018
License : BSD-style
Maintainer : alec.theriault@gmail.com
Stability : experimental
Portability : portable
This module provides functions for turning ASTs into values of type 'Doc'. These values can then be
rendered into concrete string types using functions from the @prettyprinter@ package. This has some
advantages over printing plain old strings:
* /Backend independent/: you can use a variety of existing backends to efficiently render to all
sorts of formats like 'Data.Text.Text', 'String', HTML, and terminal.
* /Dynamic layouts/: the AST will render differently depending on the desired page width
>>> :set -XTypeApplications -XOverloadedStrings
>>> import Language.Rust.Parser
>>> import Data.Text.Prettyprint.Doc.Util ( putDocW )
>>> let src = parse' @(SourceFile Span) "fn foo(x: i32, y: i32, z: i32) -> i32 { x - y + z }"
>>> let doc = pretty' src <> "\n"
>>> putDocW 80 doc
fn foo(x: i32, y: i32, z: i32) -> i32 {
x - y + z
}
>>> putDocW 10 doc
fn foo(
x: i32,
y: i32,
z: i32,
) -> i32 {
x - y + z
}
* /Annotations/: Depending on the backend you are using to render the 'Doc', annotations can
determine colours, styling, links, etc.
The examples below assume the following GHCi flag and import:
>>> :set -XOverloadedStrings
>>> import Language.Rust.Syntax.AST
-}
{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}
module Language.Rust.Pretty (
-- * Printing
pretty,
pretty',
prettyAnnotated,
prettyAnnotated',
writeSourceFile,
writeTokens,
Pretty(..),
PrettyAnnotated(..),
Doc,
-- * Resolving
Resolve(..),
-- * Error reporting
ResolveFail(..),
Issue(..),
Severity(..),
) where
import Language.Rust.Data.Ident
import Language.Rust.Data.Position
import Language.Rust.Syntax.AST
import Language.Rust.Syntax.Token
import Language.Rust.Pretty.Internal
import Language.Rust.Pretty.Resolve
import System.IO ( Handle )
import Data.Typeable ( Typeable )
import Data.Text.Prettyprint.Doc.Render.Text ( renderIO )
import Data.Text.Prettyprint.Doc ( Doc )
import qualified Data.Text.Prettyprint.Doc as PP
import Control.Exception ( throw )
-- | Resolve (see the 'Resolve' typeclass) and pretty print something.
--
-- >>> let one = Lit [] (Int Dec 1 Unsuffixed ()) ()
-- >>> let two = Lit [] (Int Dec 2 Unsuffixed ()) ()
-- >>> let three = Lit [] (Int Dec 3 Unsuffixed ()) ()
-- >>> let bogusVar = PathExpr [] Nothing (Path False [PathSegment "let" Nothing ()] ()) ()
-- >>> pretty (Binary [] MulOp (Binary [] AddOp one two ()) three ())
-- Right (1 + 2) * 3
-- >>> pretty (Binary [] AddOp one bogusVar ())
-- Left (invalid AST (identifier `let' is a keyword))
--
pretty :: (Resolve a, Pretty a) => a -> Either ResolveFail (Doc b)
pretty = fmap prettyUnresolved . resolve
-- | Same as 'pretty', but throws a 'ResolveFail' exception on invalid ASTs. This function is
-- intended for situations in which you are already stuck catching exceptions - otherwise you should
-- prefer 'pretty'.
--
-- >>> let one = Lit [] (Int Dec 1 Unsuffixed ()) ()
-- >>> let two = Lit [] (Int Dec 2 Unsuffixed ()) ()
-- >>> let three = Lit [] (Int Dec 3 Unsuffixed ()) ()
-- >>> let bogusVar = PathExpr [] Nothing (Path False [PathSegment "let" Nothing ()] ()) ()
-- >>> pretty' (Binary [] MulOp (Binary [] AddOp one two ()) three ())
-- (1 + 2) * 3
-- >>> pretty' (Binary [] AddOp one bogusVar ())
-- *** Exception: invalid AST (identifier `let' is a keyword))
--
pretty' :: (Resolve a, Pretty a) => a -> Doc b
pretty' = either throw id . pretty
-- | Resolve (see the 'Resolve' typeclass) and pretty print something with annotations. Read more
-- about annotations in "Data.Text.Prettyprint.Doc".
--
-- prop> fmap Data.Text.Prettyprint.Doc.noAnnotate . prettyAnnotated = pretty
--
prettyAnnotated :: (Resolve (f a), PrettyAnnotated f) => f a -> Either ResolveFail (Doc a)
prettyAnnotated = fmap prettyAnnUnresolved . resolve
-- | Same as 'prettyAnnotated', but throws a 'ResolveFail' exception on invalid ASTs. This function
-- is intended for situations in which you are already stuck catching exceptions - otherwise you
-- should prefer 'prettyAnnotated'.
--
-- prop> Data.Text.Prettyprint.Doc.noAnnotate . prettyAnnotated' = pretty'
--
prettyAnnotated' :: (Resolve (f a), PrettyAnnotated f) => f a -> Doc a
prettyAnnotated' = either throw id . prettyAnnotated
-- | Given a handle to a file, write a 'SourceFile' in with a desired width of 100 characters.
writeSourceFile :: (Monoid a, Typeable a) => Handle -> SourceFile a -> IO ()
writeSourceFile hdl = renderIO hdl . PP.layoutPretty layout . prettyAnnotated'
where layout = PP.LayoutOptions (PP.AvailablePerLine 100 1.0)
-- | Given a handle to a file, write a 'SourceFile' in with a desired width of 100 characters.
--
-- The 'Span' associated with the tokens (if present) will be used as a hint to for spacing
-- out the tokens.
writeTokens :: Handle -> [Spanned Token] -> IO ()
writeTokens hdl = renderIO hdl . PP.layoutPretty layout . pretty' . Stream . map mkTT
where layout = PP.LayoutOptions (PP.AvailablePerLine 100 1.0)
mkTT (Spanned s t) = Tree (Token t s)
-- | Describes things that can be pretty printed
class Pretty a where
-- | Pretty print the given value without resolving it.
prettyUnresolved :: a -> Doc b
instance Pretty Abi where prettyUnresolved = printAbi
instance Pretty BindingMode where prettyUnresolved = printBindingMode
instance Pretty BinOp where prettyUnresolved = printBinOp
instance Pretty Ident where prettyUnresolved = printIdent
instance Pretty ImplPolarity where prettyUnresolved = printPolarity
instance Pretty Suffix where prettyUnresolved = printLitSuffix
instance Pretty LitTok where prettyUnresolved = printLitTok
instance Pretty Mutability where prettyUnresolved = printMutability
instance Pretty RangeLimits where prettyUnresolved = printRangeLimits
instance Pretty Token where prettyUnresolved = printToken
instance Pretty TokenTree where prettyUnresolved = printTt
instance Pretty TokenStream where prettyUnresolved = printTokenStream
instance Pretty UnOp where prettyUnresolved = printUnOp
instance Pretty Unsafety where prettyUnresolved = printUnsafety
instance Pretty (Attribute a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (Block a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (SourceFile a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (Expr a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (Field a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (FieldPat a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (FnDecl a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (ForeignItem a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (Generics a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (ImplItem a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (Item a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (Lifetime a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (LifetimeDef a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (Lit a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (Mac a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (Nonterminal a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (Pat a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (Path a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (PolyTraitRef a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (Stmt a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (StructField a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (TraitItem a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (TraitRef a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (Ty a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (TyParam a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (TyParamBound a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (Variant a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (UseTree a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (Visibility a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (WhereClause a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty (WherePredicate a) where prettyUnresolved = PP.unAnnotate . prettyAnnUnresolved
instance Pretty Position where prettyUnresolved = PP.pretty . prettyPosition
instance Pretty Span where prettyUnresolved = PP.pretty . prettySpan
-- | Similar to 'Pretty', but for types which are parametrized over an annotation type.
class PrettyAnnotated p where
-- | Pretty print the given value without resolving it, adding annotations in the 'Doc' whenever
-- possible.
prettyAnnUnresolved :: p a -> Doc a
-- | This instance prints attributes inline
instance PrettyAnnotated Attribute where prettyAnnUnresolved = flip printAttr True
instance PrettyAnnotated Block where prettyAnnUnresolved = printBlock
instance PrettyAnnotated SourceFile where prettyAnnUnresolved = printSourceFile
instance PrettyAnnotated Expr where prettyAnnUnresolved = printExpr
instance PrettyAnnotated Field where prettyAnnUnresolved = printField
instance PrettyAnnotated FieldPat where prettyAnnUnresolved = printFieldPat
instance PrettyAnnotated FnDecl where prettyAnnUnresolved = printFnArgsAndRet
instance PrettyAnnotated ForeignItem where prettyAnnUnresolved = printForeignItem
instance PrettyAnnotated Generics where prettyAnnUnresolved = printGenerics
instance PrettyAnnotated ImplItem where prettyAnnUnresolved = printImplItem
instance PrettyAnnotated Item where prettyAnnUnresolved = printItem
instance PrettyAnnotated Lifetime where prettyAnnUnresolved = printLifetime
instance PrettyAnnotated LifetimeDef where prettyAnnUnresolved = printLifetimeDef
instance PrettyAnnotated Lit where prettyAnnUnresolved = printLit
instance PrettyAnnotated Mac where prettyAnnUnresolved = printMac Paren
instance PrettyAnnotated Nonterminal where prettyAnnUnresolved = printNonterminal
instance PrettyAnnotated Pat where prettyAnnUnresolved = printPat
instance PrettyAnnotated Path where prettyAnnUnresolved = flip printPath False
instance PrettyAnnotated PolyTraitRef where prettyAnnUnresolved = printPolyTraitRef
instance PrettyAnnotated Stmt where prettyAnnUnresolved = printStmt
instance PrettyAnnotated StructField where prettyAnnUnresolved = printStructField
instance PrettyAnnotated TraitItem where prettyAnnUnresolved = printTraitItem
instance PrettyAnnotated TraitRef where prettyAnnUnresolved = printTraitRef
instance PrettyAnnotated Ty where prettyAnnUnresolved = printType
instance PrettyAnnotated TyParam where prettyAnnUnresolved = printTyParam
instance PrettyAnnotated TyParamBound where prettyAnnUnresolved = printBound
instance PrettyAnnotated Variant where prettyAnnUnresolved = printVariant
instance PrettyAnnotated UseTree where prettyAnnUnresolved = printUseTree
instance PrettyAnnotated Visibility where prettyAnnUnresolved = printVis
instance PrettyAnnotated WhereClause where prettyAnnUnresolved = printWhereClause True
instance PrettyAnnotated WherePredicate where prettyAnnUnresolved = printWherePredicate