-- | Results of application of the production rules of a grammar.
{-# LANGUAGE ScopedTypeVariables #-}
module Data.Cfg.RuleApplication
( language
, yields
, directlyYields
) where
import Control.Monad (msum)
import Control.Monad.Omega
import Data.Cfg.Cfg
import qualified Data.DList as DL
import qualified Data.Map.Strict as M
import qualified Data.Set as S
-- | Given a grammar and a string of symbols, returns the strings
-- yielded by application of a production rule; that is, by expanding
-- one nonterminal in the string.
directlyYields :: (Cfg cfg t nt) => cfg t nt -> Vs t nt -> [Vs t nt]
directlyYields cfg vs = do
i <- [0 .. length vs - 1]
let (pre, NT nt:post) = splitAt i vs
expansion <- S.toList $ productionRules cfg nt
return (pre ++ expansion ++ post)
-- | Given a grammar, returns all strings yielded by application of
-- production rules.
yields ::
forall cfg t nt. (Cfg cfg t nt, Ord nt)
=> cfg t nt
-> [Vs t nt]
yields cfg = map DL.toList $ runOmega $ yieldNT (startSymbol cfg)
where
yieldNT :: nt -> Omega (DL.DList (V t nt))
yieldNT nt = memoMap M.! nt
where
memoMap :: M.Map nt (Omega (DL.DList (V t nt)))
memoMap =
M.fromList [(nt', yieldNT' nt') | nt' <- S.toList $ nonterminals cfg]
yieldNT' :: nt -> Omega (DL.DList (V t nt))
yieldNT' nt' = msum (return (DL.singleton (NT nt')) : map yieldVs rhss)
where
rhss = S.toList $ productionRules cfg nt'
yieldV :: V t nt -> Omega (DL.DList (V t nt))
yieldV v =
case v of
NT nt' -> yieldNT nt'
t -> return $ DL.singleton t
yieldVs :: Vs t nt -> Omega (DL.DList (V t nt))
yieldVs = fmap DL.concat . mapM yieldV
-- NOTE: you shouldn't get symbol strings repeating if the grammar is
-- unambiguous.
-- | Given a grammar, returns all strings of terminals yielded by
-- application of the production rules to the start symbol. This is
-- the /language/ of the grammar.
language :: (Cfg cfg t nt, Ord nt) => cfg t nt -> [Vs t nt]-- TODO There's certainly a more efficient way to do this.
language = filter (all isT) . yields