-- | -- -- TODO grammar-level indices should be colored red! also, make grammar -- globally available (reader monad) module FormalLanguage.CFG.PrettyPrint.ANSI -- ( grammarDoc -- , rulesDoc -- , printDoc -- , symbolDoc -- ) where where import Control.Lens hiding (outside,Index) import Control.Monad.Reader import Data.List (intersperse) import Prelude hiding ((<$>)) import qualified Data.Map as M import qualified Data.Set as S import System.IO (stdout) import Text.PrettyPrint.ANSI.Leijen import Data.Char (toUpper) import FormalLanguage.CFG.Grammar --import FormalLanguage.CFG.Parser genGrammarDoc :: Grammar -> Doc genGrammarDoc g = runReader (grammarDoc g) g grammarDoc :: Grammar -> Reader Grammar Doc grammarDoc g = do let numR = length $ g^..rules.folded ga <- indexDoc $ g^..params.folded ss <- fmap (ind "syntactic symbols:" 2 . vcat) . mapM steDoc $ g^..synvars.folded os <- fmap (ind "syntactic terminals:" 2 . vcat) . mapM steDoc $ g^..synterms.folded ts <- fmap (ind "terminals:" 2 . vcat) . mapM steDoc $ g^..termvars.folded s <- fmap (ind "start symbol:" 2) $ symbolDoc (g^.start) rs <- fmap (ind ("rules (" ++ show numR ++ "):") 2 . vcat) . rulesDoc $ g^..rules.folded ind <- undefined return $ text "Grammar: " <+> (text $ g^.grammarName) <+> ga <$> indent 2 (vsep $ [ss] ++ [os | Outside _ <- [g^.outside]] ++ [ts, s, rs]) <$> line where ind s k d = text s <$> indent k d rulesDoc :: [Rule] -> Reader Grammar [Doc] rulesDoc rs = mapM ruleDoc rs ruleDoc :: Rule -> Reader Grammar Doc ruleDoc (Rule lhs fun rhs) = do l <- symbolDoc lhs rs <- fmap (intersperse (text " ")) . mapM (fmap (fill 5) . symbolDoc) $ rhs return $ fill 10 l <+> text "->" <+> f <+> text "<<<" <+> hcat rs where f = fill 10 . text . concat . (over (_tail.traverse._head) toUpper) $ fun^..folded.getAttr steDoc :: SynTermEps -> Reader Grammar Doc steDoc (SynVar n i s k) = indexDoc i >>= return . blue . (text (n^.getSteName) <>) steDoc (SynTerm n i ) = indexDoc i >>= return . magenta . (text (n^.getSteName) <>) steDoc (Term n i ) = return . green . text $ n^.getSteName steDoc (Epsilon ) = return . red . text $ "ε" steDoc (Deletion ) = return . red . text $ "-" indexDoc :: [Index] -> Reader Grammar Doc indexDoc [] = return empty indexDoc xs = fmap (encloseSep lbrace rbrace comma) . mapM iDoc $ xs where iDoc (Index n i _ is s) = do ps <- asks _params return $ (if n `M.member` ps then red else id) $ if (not $ null is) then text $ _getIndexName n ++ "∈" ++ show is else text $ _getIndexName n ++ "=" ++ show i sDoc s | s==0 = empty | s>=0 = text $ "+" ++ show s | s< 0 = text $ show s symbolDoc :: Symbol -> Reader Grammar Doc symbolDoc (Symbol [x]) | SynVar _ _ n k <- x , n > 1 = fmap (<> text "_" <> integer k) $ steDoc x | otherwise = steDoc x symbolDoc s@(Symbol xs ) | isAllSplit s = fmap (encloseSep langle rangle comma) . mapM steDoc $ xs | otherwise = fmap list . mapM steDoc $ xs printDoc :: Doc -> IO () printDoc d = displayIO stdout (renderPretty 0.8 160 $ d <> linebreak) -- testPrint = test >>= \z -> case z of {Just g -> mapM_ (printDoc . genGrammarDoc) g} {- -- | Prettyprint a grammar ANSI-style. -- -- TODO Later on, it would be really nice to better align the LHS, fun, and RHS -- of the rules grammarDoc :: Grammar -> Doc grammarDoc g = text "Grammar: " <+> (text $ g^.name) <$> indent 2 (ns <$> is <$> ts <$> es <$> ss <$> rs) <$> line where ns = ind "syntactic symbols:" 2 . vcat $ map (\z -> (symbolDoc z <+> (text . show $ z))) (g^..nsyms.folded) is = if S.null (g^.nIsms) then text "" else ind "inside syntactic symbols (acting as terminals .. in a way):" 2 . vcat $ map (\z -> (symbolDoc z <+> (text . show $ z))) (g^..nIsms.folded) ts = ind "terminals:" 2 . vcat . map (\z -> symbolDoc z <+> (text . show $ z)) $ g^..tsyms.folded es = ind "epsilons:" 2 . vcat . map (\z -> tnDoc z <+> (text . show $ z)) $ g^..epsis.folded ss = ind "start symbol:" 2 . startDoc $ g^.start rs = ind "rules:" 2 . vcat $ zipWith (\k r -> (fill 5 $ int k) <+> (ruleDoc r)) [1..] (g^..rules.folded) ind s k d = text s <$> indent k d -- | Print just a set of rules (for the GrammarProducts Proofs). rulesDoc :: S.Set Rule -> Doc rulesDoc rs = text "rules:" <$> (indent 2 . vcat . map ruleDoc $ rs^..folded) <$> line -- | Prettify the start symbol, or give warning. startDoc :: Maybe Symb -> Doc startDoc Nothing = red $ text "no start symbol is set!" startDoc (Just s) = symbolDoc s -- | Render a rule. ruleDoc :: Rule -> Doc ruleDoc r = fill 10 l <+> text "->" <+> fill 10 f <+> rs where l = symbolDoc $ r^.lhs f = case r^.fun of [] -> text "MISSING!" [z] -> text z xs -> list . map text $ xs rs = hcat $ punctuate space $ map symbolDoc $ r^.rhs -- | A symbol is rendered either as a ``symbol'' or a list of symbols for -- multi-tape grammars. symbolDoc :: Symb -> Doc symbolDoc s | [z] <- s^.symb = outside $ tnDoc z | otherwise = outside . list $ map tnDoc $ s^.symb where outside = case s^.symbInOut of {Inside -> id; Outside -> underline . bold . (<> red (text "*"))} -- | Prettyprint a (non-)terminal symbol. tnDoc :: TN -> Doc tnDoc (E ) = blue $ text "ε" tnDoc (T s ) = green $ text s tnDoc (N s e) | Singular <- e = red $ text s | IntBased k z <- e = (red $ text s) <+> (magenta $ text $ show k) -- | -}