FormalGrammars-0.0.0.1: FormalLanguage/CFG/PrettyPrint/ANSI.hs
{-# LANGUAGE PatternGuards #-}
module FormalLanguage.CFG.PrettyPrint.ANSI
( grammarDoc
, rulesDoc
, printDoc
) where
import Control.Lens
import qualified Data.Set as S
import System.IO (stdout)
import Text.PrettyPrint.ANSI.Leijen
import FormalLanguage.CFG.Grammar
import FormalLanguage.CFG.Parser
-- | 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: " <$> indent 2 (ns <$> ts <$> es <$> ss <$> rs) <$> line where
ns = ind "non terminals:" 2 . vcat $ zipWith (\k z -> (fill 5 $ int k) <+> (symbolDoc z <+> (text . show $ z))) [1..] (g^..nsyms.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 = tnDoc z
| otherwise = list $ map tnDoc $ s^.symb
-- | 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)
-- |
printDoc :: Doc -> IO ()
printDoc d = displayIO stdout (renderPretty 0.8 160 $ d <> linebreak)
-- Print the test grammar from the parser.
test = printDoc $ grammarDoc asG