-- | Reachability of nonterminals in the grammar.
{-# LANGUAGE ScopedTypeVariables #-}
module Data.Cfg.Reachable
( reachables
, unreachables
, removeUnreachables
) where
import Data.Cfg.Cfg (Cfg(..), V(..), Vs)
import Data.Cfg.FreeCfg (FreeCfg(..))
import qualified Data.Set as S
-- | Returns the nonterminals of this grammar reachable from the start
-- symbol.
reachables ::
forall cfg t nt. (Cfg cfg t nt, Ord nt)
=> cfg t nt
-> S.Set nt
reachables cfg = go [startSymbol cfg] S.empty
where
go :: [nt] -> S.Set nt -> S.Set nt
go [] seen = seen
go (nt:nts) seen =
if nt `S.member` seen
then go nts seen
else do
let seen' = S.insert nt seen
let vs = concat $ S.toList $ productionRules cfg nt
go (nts ++ [nt' | NT nt' <- vs]) seen'
-- | Returns the nonterminals of this grammar unreachable from the
-- start symbol.
unreachables ::
forall cfg t nt. (Cfg cfg t nt, Ord nt)
=> cfg t nt
-> S.Set nt
unreachables cfg = nonterminals cfg S.\\ reachables cfg
-- | Returns an equivalent grammar not including unreachable
-- nonterminals.
removeUnreachables ::
forall cfg t nt. (Cfg cfg t nt, Ord nt)
=> cfg t nt
-> FreeCfg t nt
removeUnreachables cfg =
FreeCfg
{ nonterminals' = res
, terminals' = terminals cfg
, productionRules' = pr
, startSymbol' = startSymbol cfg
}
where
res :: S.Set nt
res = reachables cfg
pr :: nt -> S.Set (Vs t nt)
pr nt =
if nt `S.member` res
then productionRules cfg nt
else S.empty