-- | Analysis of a context-free grammar
{-# LANGUAGE ScopedTypeVariables #-}
module Data.Cfg.Analysis (
Analysis(..),
mkAnalysis,
Prediction,
Predictions
) where
import Data.Cfg.Augment
import qualified Data.Cfg.Cfg as Cfg
import Data.Cfg.FreeCfg
import qualified Data.Cfg.Internal.FirstSet as I
import qualified Data.Cfg.Internal.FollowSet as I
import qualified Data.Cfg.Internal.Nullable as I
import qualified Data.Cfg.Internal.PredictSet as I
import Data.Cfg.Internal.PredictSet(Prediction, Predictions)
import Data.Cfg.LookaheadSet
import qualified Data.Map.Strict as M
import qualified Data.Set as S
-- | Analysis of a context-free grammar
data Analysis t nt = Analysis {
baseCfg :: FreeCfg t nt,
-- ^ (a 'FreeCfg' equivalent to) the source grammar
augmentedCfg :: FreeCfg (AugT t) (AugNT nt),
-- ^ the augmented grammar
nullables :: S.Set (AugNT nt),
-- ^ the nonterminals in the grammar that can produce the
-- empty string
firstSet :: AugNT nt -> LookaheadSet t,
-- ^ the first set of the nonterminal for the grammar
firstsOfVs :: AugVs t nt -> LookaheadSet t,
-- ^ the first set of a list of symbols
followSet :: AugNT nt -> LookaheadSet t,
-- ^ the follow set of the nonterminal for the grammar
predictSet :: AugProduction t nt -> LookaheadSet t,
-- ^ the predict set of the production
isLL1 :: Bool,
-- ^ 'True' iff the grammar is LL(1)
ll1Info :: AugNT nt -> Predictions t nt
-- ^ the productions for this nonterminal and the lookaheads
-- that predict them
}
-- | Analyzes a context-free grammar
mkAnalysis :: forall cfg t nt
. (Cfg.Cfg cfg t nt, Ord nt, Ord t)
=> cfg t nt -> Analysis t nt
mkAnalysis cfg = Analysis {
baseCfg = bcfg,
augmentedCfg = cfg',
nullables = ns,
firstSet = fs,
firstsOfVs = I.firstsOfVs fs,
followSet = fols,
predictSet = predict,
isLL1 = isLL1',
ll1Info = (ll1InfoMap M.!)
}
where
bcfg = toFreeCfg cfg
cfg' = augmentCfg bcfg
ns = I.nullables cfg'
fsm = I.firstSetMap cfg'
fs nt = fsm M.! nt
folm = I.followSetMap cfg' fs
fols nt = folm M.! nt
predict = I.predictSet fs fols
ll1InfoMap = I.ll1InfoMap cfg' predict
isLL1' = I.isLL1 ll1InfoMap