packages feed

Grempa-0.1.1: Data/Parser/Grempa/Grammar/Untyped.hs

{-# LANGUAGE GADTs, DoRec #-}
module Data.Parser.Grempa.Grammar.Untyped
    ( Rule, Prod, Symbol(..), RId(..)
    , unType
    , rules, terminals, nonTerminals
    , firstProd, follow
    )where

import qualified Control.Arrow as A
import Control.Applicative
import Control.Monad.State
import qualified Data.Map as M
import Data.Map(Map)
import Data.Set(Set)
import qualified Data.Set as S

import Data.Parser.Grempa.Aux.Aux
import qualified Data.Parser.Grempa.Aux.MultiMap as MM
import Data.Parser.Grempa.Aux.MultiMap(MultiMap)
import Data.Parser.Grempa.Parser.Table
import Data.Parser.Grempa.Grammar.Token
import qualified Data.Parser.Grempa.Grammar.Typed as T

-- | The recursive data types for untyped grammars
type Rule s = [Prod s]
type Prod s = [Symbol s]

data Symbol s
    = STerm s
    | SRule (RId s)
  deriving (Eq, Ord, Show)

data RId s = RId {rId :: RuleI, rIdRule :: Rule s}

instance Show (RId s) where
    show (RId i _) = show i
instance Eq (RId s) where
    RId i _ == RId j _ = i == j
instance Ord (RId s) where
    RId i _ `compare` RId j _ = i `compare` j

type UnTypeState s' = State (Map Int (RId s'), ProdFunTable)
-- | Returns an untyped tree representation of a typed grammar
--   together with a mapping from rule and production number to
--   a dynamic containing the construction function of the typed
--   production
unType :: (s -> s') -> T.RId s a -> (RId s', ProdFunTable)
unType cs = A.second snd . flip runState (M.empty, []) . unTypeR cs
  where
    unTypeR :: (s -> s') -> T.RId s a -> UnTypeState s' (RId s')
    unTypeR c (T.RId i r) = do
        (rids, funs) <- get
        case M.lookup i rids of
            Just x  -> return x
            Nothing -> do
                let newfuns = zip (zip (repeat i) [0..])
                                  (map T.getFun r)
                rec
                  put (M.insert i res rids, funs ++ newfuns)
                  res <- RId i <$> mapM (unTypeP c) r
                return res
    unTypeP :: (s -> s') -> T.Prod s a -> UnTypeState s' (Prod s')
    unTypeP c p = case p of
        T.PSeq  ps s -> liftM2 (++) (unTypeP c ps) ((:[]) <$> unTypeS c s)
        T.PSeqN ps s -> liftM2 (++) (unTypeP c ps) ((:[]) <$> unTypeS c s)
        T.PFun _    -> return []
    unTypeS :: (s -> s') -> T.Symbol s a -> UnTypeState s' (Symbol s')
    unTypeS c s = case s of
        T.STerm t -> return $ STerm (c t)
        T.SRule r -> SRule <$> unTypeR c r


instance Functor RId where
    fmap = flip evalState M.empty `dot` fmapR
      where
        fmapS :: (a -> b) -> Symbol a -> Done (RId a) (RId b) (Symbol b)
        fmapS f (STerm s) = return $ STerm $ f s
        fmapS f (SRule r) = do
            done <- getDone r
            case done of
              Just r' -> return $ SRule r'
              Nothing -> do
                  rec
                    putDone r res
                    res <- fmapR f r
                  return $ SRule res
        fmapR :: (a -> b) -> RId a -> DoneA (RId a) (RId b)
        fmapR f (RId n r) = RId n <$> mapM (mapM (fmapS f)) r

-------------------------------------------------------------------------------
-- | Get all rules from a grammar by following a rule's non-terminals recursively
rules :: Token s => RId s -> [RId s]
rules = S.toList . recTraverseG rules' . S.singleton
  where
    rules' rs     = (res `S.union` rs, res)
      where
        res = S.unions $ map aux (S.toList rs)
    aux (RId _ r) = S.fromList [rid | p <- r, SRule rid <- p]

-- | Get all terminals (input symbols) from a list of rule IDs
terminals :: Token s => [RId s] -> [Symbol s]
terminals = concatMap (\(RId _ rs) -> [STerm s | as <- rs, STerm s <- as])

-- | Get all non-terminals (variables) from a list of rule IDs
nonTerminals :: Token s => [RId s] -> [Symbol s]
nonTerminals = map SRule

-- | Datatype used in computing the first set
data RecETok s
    = RETok {unRETok :: ETok s}
    | IfEpsilon (RId s) (Prod s)
    | RRule (RId s)
  deriving (Eq, Ord)

type First s a = State (MultiMap (RId s) (RecETok s)) a

-- | Get the first tokens that a symbol eats
-- first :: Token s => Symbol s -> Set (ETok s)
-- first s = firstProd [s]

firstProd :: Token s => Prod s -> Set (ETok s)
firstProd = flip evalState MM.empty . firstProd'

firstProd' :: Token s => Prod s -> First s (Set (ETok s))
firstProd' as = do
    go (RId (-1) undefined) as
    fixf
    results <$> gets (MM.lookup (RId (-1) undefined))
  where
    go :: Token s => RId s -> Prod s -> First s ()
    go rid []          = modify $ MM.insert rid (RETok Epsilon)
    go rid (STerm s:_) = modify $ MM.insert rid (RETok (ETok s))
    go rid (SRule rid'@(RId _ ps):xs) = do
        modify $ MM.insert rid (RRule rid')
        ss <- gets $ MM.lookup rid'
        when (S.null ss) $ mapM_ (go rid') ps
        modify $ MM.insert rid (IfEpsilon rid' xs)

    f :: Token s => RId s -> RecETok s -> First s ()
    f rid rt@(IfEpsilon rid' xs) = do
        ss  <- gets (MM.lookup rid')
        when (S.member (RETok Epsilon) ss) $ do
            modify $ MM.delete rid rt
            go rid xs
    f rid rt@(RRule rid') = do
        ss <- gets (MM.lookup rid')
        when (clean ss) $ do
            modify $ MM.delete rid rt
        modify $ MM.inserts rid (S.filter isRETok ss)
    f _ _ = return ()

    clean :: Set (RecETok s) -> Bool
    clean = S.fold ((&&) . isRETok) True

    isRETok :: RecETok s -> Bool
    isRETok (RETok _) = True
    isRETok _         = False

    fs :: Token s => First s ()
    fs = do
        ss <- get
        sequence_ [f rid rt | (rid, rts) <- MM.toList ss, rt <- S.toList rts]

    results :: Token s => Set (RecETok s) -> Set (ETok s)
    results = S.map unRETok . S.filter isRETok

    fixf :: Token s => First s ()
    fixf = do
        state <- get
        fs
        state' <- get
        when (state /= state') fixf

-- | Get all symbols that can follow a rule,
--   also given the start rule and a list of all rules
follow :: Token s => RId s -> RId s -> [RId s] -> Set (Tok s)
follow rid = evalDone `dot` follow' rid

follow' :: Token s => RId s -> RId s -> [RId s] -> Done (RId s) () (Set (Tok s))
follow' rid startrid rids = ifNotDoneG rid (const S.empty) $ do
    putDone rid ()
    (if rid == startrid then S.insert EOF else id)
        <$> S.unions
        <$> sequence [followProd prod a
                         | a@(RId _ prods) <- rids
                         , prod <- prods]
  where
    followProd []       _ = return S.empty
    followProd (b:beta) a
        | b == SRule rid =  S.union rest
                        <$> liftM2 S.union (followProd beta a)
                                           (if Epsilon `S.member` firstbeta
                                               then follow' a startrid rids
                                               else return S.empty)
        | otherwise      = followProd beta a
      where
        firstbeta = firstProd beta
        rest      = S.map (Tok . unETok) $ S.delete Epsilon firstbeta