exactprint/Thrift/ExactPrint/PrettyPrint.hs (326 lines of code) (raw):
-- Copyright (c) Facebook, Inc. and its affiliates.
{-# LANGUAGE CPP #-}
module Thrift.ExactPrint.PrettyPrint
( exactPrint
, exactPrintThrift
, exactPrintRaw
, roundTrip
) where
import Data.Some
import qualified Data.Text as Strict
import Data.Text.Lazy (Text)
import qualified Data.Text.Lazy as Text
import Data.Text.Lazy.Builder
import Thrift.Compiler.Parser
import Thrift.Compiler.Types
import Thrift.ExactPrint.Convert
import Thrift.ExactPrint.Types
#if MIN_VERSION_dependent_sum(0,6,0)
#define This Some
#endif
exactPrint :: Program l Offset -> Text
exactPrint Program{..} = toLazyText $ mconcat
(map ppHeader progHeaders ++
map ppDecl progDecls ++
map ppComment progComments) <> "\n"
exactPrintThrift :: ThriftFile a Offset -> Text
exactPrintThrift ThriftFile{..} = toLazyText $ mconcat
(map ppHeader thriftHeaders ++
map ppDecl thriftDecls ++
map ppComment thriftComments) <> "\n"
exactPrintRaw :: [Decl s l Offset] -> Text
exactPrintRaw = toLazyText . mconcat . map ppDecl
roundTrip :: FilePath -> IO ()
roundTrip path = do
result <- parse "." path
case result of
Left err -> putStrLn err
Right ThriftFile{..} ->
putStrLn $ Text.unpack $ exactPrintRaw $ declOffsets thriftDecls
-- Headers ---------------------------------------------------------------------
ppHeader :: Header Offset -> Builder
ppHeader HInclude{..} = mconcat
[ addHeader incKeywordLoc
, case incType of
Include -> "include"
HsInclude -> "hs_include"
CppInclude -> "cpp_include"
, addHeader incPathLoc
, ppStr (Strict.pack incPath) incQuoteType
]
ppHeader HNamespace{..} = mconcat
[ addHeader nmKeywordLoc, "namespace"
, addHeader nmLangLoc, fromText nmLang
, addHeader nmNameLoc
, maybe fromText (flip ppStr) nmQuoteType nmName
]
-- Decls -----------------------------------------------------------------------
ppDecl :: Decl s l Offset -> Builder
ppDecl (D_Struct Struct{..}) = ppStruct structLoc structSAnns
(case structType of { StructTy -> "struct" ; ExceptionTy -> "exception" })
structName
(map ppField structMembers)
structAnns
ppDecl (D_Union Union{..}) = ppStruct unionLoc unionSAnns "union" unionName
(map ppUnionAlt unionAlts)
unionAnns
ppDecl (D_Enum Enum{..}) = ppStruct enumLoc enumSAnns "enum" enumName
(map ppEnumVal enumConstants)
enumAnns
ppDecl (D_Typedef Typedef{tdLoc=TypedefLoc{..},..}) = mconcat
[ ppSAnns tdSAnns
, addHeader tdlKeyword, "typedef"
, ppType tdType
, addHeader tdlName, fromText tdName
, ppAnns tdAnns
]
ppDecl (D_Const Const{constLoc=ConstLoc{..},..}) = mconcat
[ ppSAnns constSAnns
, addHeader clKeyword, "const"
, ppType constType
, addHeader clName, fromText constName
, addHeader clEqual, "="
, ppConst constVal
, ppSeparator clSeparator
]
ppDecl (D_Service Service{serviceLoc=StructLoc{..},..}) = mconcat $
[ ppSAnns serviceSAnns
, addHeader slKeyword, "service"
, addHeader slName, fromText serviceName
] ++
(case serviceSuper of
Nothing -> []
Just Super{..} ->
[ addHeader supExtends, "extends"
, addHeader supLoc, fromText supName
]) ++
[ addHeader slOpenBrace, "{" ] ++
map ppFunction serviceFunctions ++
[ addHeader slCloseBrace, "}"
, ppAnns serviceAnns
]
ppStruct
:: StructLoc Offset
-> [StructuredAnnotation s l Offset]
-> Builder
-> Strict.Text
-> [Builder]
-> Maybe (Annotations Offset)
-> Builder
ppStruct StructLoc{..} sAnns keyword name fields anns = mconcat $
[ ppSAnns sAnns
, addHeader slKeyword, keyword
, addHeader slName, fromText name
, addHeader slOpenBrace, "{"
] ++
fields ++
[ addHeader slCloseBrace, "}"
, ppAnns anns
]
-- Fields ----------------------------------------------------------------------
ppField :: Field u s l Offset -> Builder
ppField Field{..} = mconcat
[ ppSAnns fieldSAnns
, addHeader flId, fromText flIdRep
, addHeader flColon, ":"
, case fieldRequiredness of
Default -> ""
Optional loc -> addHeader loc <> "optional"
Required loc -> addHeader loc <> "required"
, ppType fieldType
, addHeader flName, fromText fieldName
, maybe mempty (\loc -> addHeader loc <> "=") flEqual
, maybe mempty ppConst fieldVal
, ppAnns fieldAnns
, ppSeparator flSeparator
]
where
FieldLoc{..} = fieldLoc
ppSeparator :: Separator Offset -> Builder
ppSeparator NoSep = ""
ppSeparator (Comma loc) = addHeader loc <> ","
ppSeparator (Semicolon loc) = addHeader loc <> ";"
-- Union Alts ------------------------------------------------------------------
ppUnionAlt :: UnionAlt s l Offset -> Builder
ppUnionAlt UnionAlt{altLoc=FieldLoc{..},..} = mconcat
[ ppSAnns altSAnns
, addHeader flId, fromText flIdRep
, addHeader flColon, ":"
, ppType altType
, addHeader flName, fromText altName
, ppAnns altAnns
, ppSeparator flSeparator
]
-- Enum Values -----------------------------------------------------------------
ppEnumVal :: EnumValue s l Offset -> Builder
ppEnumVal EnumValue{evLoc=EnumValLoc{..},..} = mconcat
[ ppSAnns evSAnns
, addHeader evlName, fromText evName
, addHeader evlEqual, "="
, addHeader evlValue, fromText evlRep
, ppAnns evAnns
, ppSeparator evlSeparator
]
-- Constants -------------------------------------------------------------------
ppConst :: UntypedConst Offset -> Builder
ppConst UntypedConst{..} =
addHeader ucLoc <> case ucConst of
IntConst _ rep -> fromText rep
DoubleConst _ rep -> fromText rep
BoolConst True -> "true"
BoolConst False -> "false"
StringConst s qt -> ppStr s qt
IdConst i -> fromText i
ListConst{..} -> mconcat $
"[" :
map (ppListElem ppConst) lvElems ++
[ addHeader lvCloseBrace, "]" ]
MapConst{..} -> mconcat $
"{" :
map (ppListElem ppMapPair) mvElems ++
[ addHeader mvCloseBrace, "}" ]
StructConst{..} -> mconcat $
fromText svType :
addHeader svOpenBrace : "{" :
map (ppListElem ppStructPair) svElems ++
[ addHeader svCloseBrace, "}" ]
ppListElem :: (f Offset -> Builder) -> ListElem f Offset -> Builder
ppListElem pp ListElem{..} = pp leElem <> ppSeparator leSeparator
ppMapPair :: MapPair Offset -> Builder
ppMapPair MapPair{..} = mconcat
[ ppConst mpKey
, addHeader mpColon, ":"
, ppConst mpVal
]
ppStructPair :: StructPair Offset -> Builder
ppStructPair StructPair{..} = mconcat
[ addHeader spKeyLoc, fromText spKey
, addHeader spEquals, "="
, ppConst spVal
]
-- Functions -------------------------------------------------------------------
ppFunction :: Function s l Offset -> Builder
ppFunction fun@Function{funLoc=FunLoc{..},..} = mconcat $
[ ppSAnns funSAnns
, case fnlOneway of { Nothing -> "" ; Just loc -> addHeader loc <> "oneway" }
, case funType of
FunType (This ty) -> ppType ty
FunTypeVoid loc -> addHeader loc <> "void"
FunTypeResponseAndStreamReturn ResponseAndStreamReturn{..} -> mconcat
[ maybe mempty ppType rsReturn
, maybe mempty (\comma -> addHeader comma <> ",") rsComma
, ppStream rsStream
]
, addHeader fnlName, fromText funName
, addHeader fnlOpenParen, "("
] ++
map ppField funArgs ++
[ addHeader fnlCloseParen, ")"
, maybe mempty ppThrows $ funThrows fun
, ppAnns funAnns
, ppSeparator fnlSeparator
]
ppStream
:: Stream s l Offset
-> Builder
ppStream Stream{streamLoc=Arity1Loc{..}, ..} = mconcat
[ addHeader a1Ty, "stream"
, addHeader a1OpenBrace, "<"
, ppType streamType
, maybe mempty ppThrows streamThrows
, addHeader a1CloseBrace, ">"
]
ppThrows :: Throws s l Offset -> Builder
ppThrows Throws{..} = mconcat $
[ addHeader tlThrows, "throws"
, addHeader tlOpenParen, "("
] ++
map ppField throwsFields ++
[ addHeader tlCloseParen, ")"
]
where ThrowsLoc{..} = throwsLoc
-- Types -----------------------------------------------------------------------
ppType :: AnnotatedType Offset t -> Builder
ppType AnnotatedType{..} =
(case atType of
-- Arity 0 Types
I8 -> ppType0 atLoc "byte"
I16 -> ppType0 atLoc "i16"
I32 -> ppType0 atLoc "i32"
I64 -> ppType0 atLoc "i64"
TFloat -> ppType0 atLoc "float"
TDouble -> ppType0 atLoc "double"
TBool -> ppType0 atLoc "bool"
TText -> ppType0 atLoc "string"
TBytes -> ppType0 atLoc "binary"
TNamed n -> ppType0 atLoc $ fromText n
-- Arity 1 Types
TList u -> ppType1 atLoc "list" $ ppType u
TSet u -> ppType1 atLoc "set" $ ppType u
THashSet u -> ppType1 atLoc "hash_set" $ ppType u
-- Arity 2 Types
TMap k v -> ppType2 atLoc "map" (ppType k) (ppType v)
THashMap k v -> ppType2 atLoc "hash_map" (ppType k) (ppType v)) <>
ppAnns atAnnotations
ppType0 :: TypeLoc 0 Offset -> Builder -> Builder
ppType0 Arity0Loc{..} ty = addHeader a0Ty <> ty
ppType1 :: TypeLoc 1 Offset -> Builder -> Builder -> Builder
ppType1 Arity1Loc{..} ty inner = mconcat
[ addHeader a1Ty, ty
, addHeader a1OpenBrace, "<"
, inner
, addHeader a1CloseBrace, ">"
]
ppType2 :: TypeLoc 2 Offset -> Builder -> Builder -> Builder -> Builder
ppType2 Arity2Loc{..} ty u v = mconcat
[ addHeader a2Ty, ty
, addHeader a2OpenBrace, "<"
, u
, addHeader a2Comma, ","
, v
, addHeader a2CloseBrace, ">"
]
ppAnns :: Maybe (Annotations Offset) -> Builder
ppAnns Nothing = mempty
ppAnns (Just Annotations{..}) = mconcat $
[ addHeader annOpenParen, "(" ] ++
map ppAnn annList ++
[ addHeader annCloseParen, ")" ]
ppAnn :: Annotation Offset -> Builder
ppAnn SimpleAnn{..} = addHeader saLoc <> fromText saTag <> ppSeparator saSep
ppAnn ValueAnn{..} = mconcat
[ addHeader vaTagLoc, fromText vaTag
, addHeader vaEqual, "="
, addHeader vaValLoc
, case vaVal of
TextAnn txt qt -> ppStr txt qt
IntAnn _ rep -> fromText rep
, ppSeparator vaSep
]
ppSAnns :: [StructuredAnnotation s l Offset] -> Builder
ppSAnns sAnns = mconcat $ map ppSAnn sAnns
ppSAnn :: StructuredAnnotation s l Offset -> Builder
ppSAnn StructuredAnn{..} = mconcat
[ addHeader saAt, "@"
, fromText saType
, ppSAnnElems saMaybeElems
]
ppSAnnElems :: Maybe (StructuredAnnotationElems Offset) -> Builder
ppSAnnElems Nothing = mempty
ppSAnnElems (Just StructuredAnnElems{..}) = mconcat $
[ addHeader saOpenBrace , "{" ] ++
map (ppListElem ppStructPair) saElems ++
[ addHeader saCloseBrace, "}" ]
ppStr :: Strict.Text -> QuoteType -> Builder
ppStr txt qt = quote <> fromText txt <> quote
where quote = case qt of { SingleQuote -> "'" ; DoubleQuote -> "\"" }
-- Helpers ---------------------------------------------------------------------
addHeader :: Located Offset -> Builder
addHeader Located{..} =
mconcat (map ppComment lComments) <> ppOffset lLocation
ppComment :: Comment Offset -> Builder
ppComment (Comment offs txt) = ppOffset offs <> fromText txt
ppOffset :: Offset -> Builder
ppOffset Offset{..} = mconcat $
replicate offsRows (singleton '\n') ++
replicate offsCols (singleton ' ')