-- | 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