final-pretty-printer-0.1.0.0: Text/PrettyPrint/Final/Demos/STLCDemo.hs
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
-- | A demonstration of the precedence and environment extensions
module Text.PrettyPrint.Final.Demos.STLCDemo () where
import Control.Monad
import Control.Applicative
import Control.Monad.Identity
import Control.Monad.Reader
import Control.Monad.Writer
import Control.Monad.State
import Control.Monad.RWS
import Data.List
import Data.String (IsString(..))
import Data.Text (Text)
import qualified Data.Text as T
import Data.Map (Map)
import qualified Data.Map as Map
import Text.PrettyPrint.Final hiding (collection)
import Text.PrettyPrint.Final.Extensions.Environment
import Text.PrettyPrint.Final.Extensions.Precedence
import Text.PrettyPrint.Final.Rendering.HTML
data Ann = Class Text | Tooltip Text
deriving (Eq, Ord, Show)
-- The Language
data Ty = Int | Arr Ty Ty
data Op = Plus | Minus | Times | Div
data Exp =
Lit Int
| Bin Op Exp Exp
| Ifz Exp Exp Exp
| Var Text
| Lam Text Ty Exp
| App Exp Exp
| Raw Doc
infixl 5 /+/
infixl 5 /-/
infixl 6 /*/
infixl 6 ///
infixl 9 /@/
(/+/) :: Exp -> Exp -> Exp
(/+/) = Bin Plus
(/-/) :: Exp -> Exp -> Exp
(/-/) = Bin Minus
(/*/) :: Exp -> Exp -> Exp
(/*/) = Bin Times
(///) :: Exp -> Exp -> Exp
(///) = Bin Div
(/@/) :: Exp -> Exp -> Exp
(/@/) = App
-- The Pretty Printer
-- class shortcuts
lit :: Ann
lit = Class "lit"
var :: Ann
var = Class "var"
pun :: Ann
pun = Class "pun"
bdr :: Ann
bdr = Class "bdr"
kwd :: Ann
kwd = Class "kwd"
opr :: Ann
opr = Class "opr"
type TEnv = Map Text Text
tEnv0 :: TEnv
tEnv0 = Map.empty
newtype DocM a = DocM { unDocM :: EnvT TEnv (PrecT Ann (RWST (PEnv Int Ann ()) (POut Int Ann) (PState Int ()) Maybe)) a }
deriving
( Functor, Applicative, Monad, Alternative
, MonadReader (PEnv Int Ann ()), MonadWriter (POut Int Ann), MonadState (PState Int ())
, MonadReaderPrec Ann, MonadReaderEnv TEnv
)
instance MonadPretty Int Ann () DocM
instance MonadPrettyPrec Int Ann () DocM
instance MonadPrettyEnv TEnv Int Ann () DocM
instance Measure Int () DocM where
measure = return . runIdentity . measure
runDocM :: PEnv Int Ann () -> PrecEnv Ann -> TEnv -> PState Int () -> DocM a -> Maybe (PState Int (), POut Int Ann, a)
runDocM e pe te s d = (\(a,s',o) -> (s',o,a)) <$> runRWST (runPrecT pe (runEnvT te $ unDocM d)) e s
askTEnv :: DocM TEnv
askTEnv = askEnv
localTEnv :: (TEnv -> TEnv) -> DocM a -> DocM a
localTEnv f = localEnv f
-- Doc
env0 :: (Num w) => PEnv w ann ()
env0 = PEnv
{ maxWidth = 80
, maxRibbon = 60
, layout = Break
, failure = CantFail
, nesting = 0
, formatting = mempty
, formatAnn = const mempty
}
state0 :: PState Int ()
state0 = PState
{ curLine = []
}
type Doc = DocM ()
execDoc :: Doc -> POut Int Ann
execDoc d =
let rM = runDocM env0 precEnv0 tEnv0 state0 d
in case rM of
Nothing -> PAtom $ AChunk $ CText "<internal pretty printing error>"
Just (_, o, ()) -> o
instance IsString Doc where
fromString = text . fromString
instance Monoid Doc where
mempty = return ()
mappend = (>>)
renderAnnotation :: Ann -> Text -> Text
renderAnnotation (Class c) t = mconcat [ "<span class='" , c , "'>" , t , "</span>" ]
renderAnnotation (Tooltip p) t = mconcat [ "<span title='" , p , "'>" , t , "</span>" ]
instance Show Doc where
show = T.unpack . (render renderAnnotation) . execDoc
-- Pretty Class for this Doc
class Pretty a where
pretty :: a -> Doc
instance Pretty Doc where
pretty = id
instance Pretty Text where
pretty = text . T.pack . show
-- printing expressions
ftTy :: Ty -> Text
ftTy Int = "Int"
ftTy (Arr t1 t2) = ftTy t1 `T.append` " -> " `T.append` ftTy t2
ppOp :: Op -> Doc -> Doc -> Doc
ppOp Plus x1 x2 = infl 20 (annotate opr "+") (grouped x1) (grouped x2)
ppOp Minus x1 x2 = infl 20 (annotate opr "-") (grouped x1) (grouped x2)
ppOp Times x1 x2 = infl 30 (annotate opr "*") (grouped x1) (grouped x2)
ppOp Div x1 x2 = infl 30 (annotate opr "/") (grouped x1) (grouped x2)
ppExp :: Exp -> Doc
ppExp (Lit i) = annotate lit $ text $ T.pack $ show i
ppExp (Bin o e1 e2) = ppOp o (ppExp e1) (ppExp e2)
ppExp (Ifz e1 e2 e3) = grouped $ atLevel 10 $ hvsep
[ grouped $ nest 2 $ hvsep [ annotate kwd "ifz" , botLevel $ ppExp e1 ]
, grouped $ nest 2 $ hvsep [ annotate kwd "then" , botLevel $ ppExp e2 ]
, grouped $ nest 2 $ hvsep [ annotate kwd "else" , ppExp e3 ]
]
ppExp (Var x) = do
tEnv <- askTEnv
let tt = tEnv Map.! x
annotate (Tooltip tt) $ annotate var $ text x
ppExp (Lam x ty e) = localTEnv (Map.insert x $ ftTy ty) $ grouped $ atLevel 10 $ nest 2 $ hvsep
[ hsep [ annotate kwd "lam" , annotate (Tooltip $ ftTy ty) $ annotate bdr $ text x , annotate pun "." ]
, ppExp e
]
ppExp (App e1 e2) = app (ppExp e1) [ppExp e2]
ppExp (Raw d) = d
precDebug :: Doc
precDebug = do
lvl <- askLevel
bmp <- askBumped
text $ "p:" `T.append` T.pack (show lvl) `T.append` (if bmp then "B" else "")
instance Pretty Exp where
pretty = ppExp
e1 :: Exp
e1 = Lam "x" Int $ Var "x"
-- ifz ((1 - 2) + (3 - 4)) * (5 / 7)
-- then lam x . x
-- else (lam y . y) (ifz 1 then 2 else 3)
e2 :: Exp
e2 = Ifz ((Lit 1 /-/ Lit 2 /+/ (Lit 3 /-/ Lit 4)) /*/ (Lit 5 /// Lit 7) /+/ Lit 8)
(Lam "x" Int $ Var "x")
((Lam "y" Int $ Var "y") /@/ (Ifz (Lit 1) (Lit 2) (Lit 3)))
-- run this file to output an html file "stlc_demo.html", which links against
-- "stlc_demo.css"
main :: IO ()
main = do
let output = show $ localMaxWidth (const 15) $ pretty e2
putStrLn output
writeFile "stlc_demo.html" $ concat
[ "<html><head><link rel='stylesheet' type='text/css' href='stlc_demo.css'></head><body><p>"
, output
, "</p></body></html>"
]