module Data.Cfg.Productive (
productives,
unproductives,
removeUnproductives
) where
import Control.Monad(guard, unless)
import Data.Cfg.Cfg(Cfg(..), Production, V(..), Vs, productions)
import Data.Cfg.FixedPoint(fixedPoint)
import Data.Cfg.FreeCfg(FreeCfg(..))
import qualified Data.Set as S
productives :: forall cfg t nt
. (Cfg cfg t nt, Ord nt, Ord t)
=> cfg t nt -> S.Set (Production t nt)
productives cfg = S.fromList
$ filter (isProductiveProduction productiveNTs)
$ productions cfg
where
productiveNTs :: S.Set nt
productiveNTs = productiveNonterminals cfg
unproductives :: forall cfg t nt
. (Cfg cfg t nt, Ord nt, Ord t)
=> cfg t nt -> S.Set (Production t nt)
unproductives cfg = S.fromList (productions cfg) S.\\ productives cfg
removeUnproductives :: forall cfg t nt
. (Cfg cfg t nt, Ord nt, Ord t)
=> cfg t nt -> FreeCfg t nt
removeUnproductives cfg = FreeCfg {
terminals' = terminals cfg,
startSymbol' = startSymbol cfg,
nonterminals' = nts,
productionRules' = rules
}
where
nts :: S.Set nt
nts = productiveNonterminals cfg
rules :: nt -> S.Set (Vs t nt)
rules nt = if nt `S.member` nts
then S.filter (isProductiveVs nts) $ productionRules cfg nt
else S.empty
productiveNonterminals :: forall cfg t nt
. (Cfg cfg t nt, Ord nt, Ord t)
=> cfg t nt -> S.Set nt
productiveNonterminals cfg = fixedPoint f S.empty
where
f :: S.Set nt -> S.Set nt
f productiveNTs = S.fromList $ do
nt <- S.toList $ nonterminals cfg
unless (nt `S.member` productiveNTs) $ do
let rhss = productionRules cfg nt
guard (any (isProductiveVs productiveNTs) $ S.toList rhss)
return nt
isProductiveProduction :: forall t nt
. (Ord nt)
=> S.Set nt -> Production t nt -> Bool
isProductiveProduction productiveNTs (hd, rhs)
= hd `S.member` productiveNTs
&& isProductiveVs productiveNTs rhs
isProductiveVs :: forall t nt
. (Ord nt)
=> S.Set nt -> Vs t nt -> Bool
isProductiveVs productiveNTs = all isProductiveV
where
isProductiveV :: V t nt -> Bool
isProductiveV v = case v of
NT nt -> nt `S.member` productiveNTs
_ -> True