-- | Augmented grammars.
{-# LANGUAGE ScopedTypeVariables #-}
module Data.Cfg.Augment
( -- * Augmenting grammars
augmentCfg
-- * Augmenting symbols
, AugNT(..)
, AugT(..)
-- * Type synonyms
, AugV
, AugVs
, AugProduction
, AugFreeCfg
) where
import Data.Cfg.Cfg (Cfg(..), Production, V(..), Vs)
import Data.Cfg.FreeCfg (FreeCfg(..))
import qualified Data.Set as S
-- | Nonterminal symbols augmented with a special 'StartSymbol'
data AugNT nt
= StartSymbol
| AugNT nt
deriving (Eq, Ord, Show)
-- | Terminal symbols augmented with a special end-of-file symbol
data AugT t
= EOF
| AugT t
deriving (Eq, Ord, Show)
-- | A convenience synonym for an augmented vocabulary symbol
type AugV t nt = V (AugT t) (AugNT nt)
-- | A convenience synonym for augmented vocabulary symbols
type AugVs t nt = Vs (AugT t) (AugNT nt)
-- | A convenience synonym for augmented productions
type AugProduction t nt = Production (AugT t) (AugNT nt)
-- | A convenience symbol for an augmented grammar
type AugFreeCfg t nt = FreeCfg (AugT t) (AugNT nt)
-- | Returns the /augmented/ grammar: a grammar for the same language
-- but using explicit start and end-of-file symbols.
augmentCfg ::
forall cfg t nt. (Cfg cfg t nt, Ord nt, Ord t)
=> cfg t nt
-> FreeCfg (AugT t) (AugNT nt)
augmentCfg cfg =
FreeCfg
{ nonterminals' = S.insert StartSymbol $ S.map AugNT $ nonterminals cfg
, terminals' = S.insert EOF $ S.map AugT $ terminals cfg
, productionRules' = pr
, startSymbol' = StartSymbol
}
where
pr :: AugNT nt -> S.Set (Vs (AugT t) (AugNT nt))
pr StartSymbol = S.singleton [NT $ AugNT $ startSymbol cfg, T EOF]
pr (AugNT nt) = S.map augmentVs oldRhss
where
oldRhss :: S.Set (Vs t nt)
oldRhss = productionRules cfg nt
augmentVs :: Vs t nt -> Vs (AugT t) (AugNT nt)
augmentVs = map augmentV
augmentV :: V t nt -> V (AugT t) (AugNT nt)
augmentV (NT nt') = NT $ AugNT nt'
augmentV (T t') = T $ AugT t'