module Data.Katydid.Relapse.Ast
( Pattern(..)
, Grammar
, emptyRef
, union
, newRef
, reverseLookupRef
, lookupRef
, hasRecursion
, listRefs
, nullable
)
where
import qualified Data.Map.Strict as M
import qualified Data.Set as S
import Control.Monad.Extra ( (||^)
, (&&^)
)
import Data.Katydid.Relapse.Expr
data Pattern
= Empty
| ZAny
| Node (Expr Bool) Pattern
| Or Pattern Pattern
| And Pattern Pattern
| Not Pattern
| Concat Pattern Pattern
| Interleave Pattern Pattern
| ZeroOrMore Pattern
| Optional Pattern
| Contains Pattern
| Reference String
deriving (Eq, Ord, Show)
nullable :: Grammar -> Pattern -> Either String Bool
nullable _ Empty = Right True
nullable _ ZAny = Right True
nullable _ Node{} = Right False
nullable g (Or l r ) = nullable g l ||^ nullable g r
nullable g (And l r ) = nullable g l &&^ nullable g r
nullable g (Not p ) = not <$> nullable g p
nullable g (Concat l r ) = nullable g l &&^ nullable g r
nullable g (Interleave l r ) = nullable g l &&^ nullable g r
nullable _ (ZeroOrMore _ ) = Right True
nullable _ (Optional _ ) = Right True
nullable g (Contains p ) = nullable g p
nullable g (Reference refName) = lookupRef g refName >>= nullable g
newtype Grammar = Grammar (M.Map String Pattern)
deriving (Show, Eq)
lookupRef :: Grammar -> String -> Either String Pattern
lookupRef (Grammar m) refName = case M.lookup refName m of
Nothing -> Left $ "missing reference: " ++ refName
(Just p) -> Right p
listRefs :: Grammar -> [String]
listRefs (Grammar m) = M.keys m
reverseLookupRef :: Pattern -> Grammar -> Maybe String
reverseLookupRef p (Grammar m) = case M.keys $ M.filter (== p) m of
[] -> Nothing
(k : _) -> Just k
newRef :: String -> Pattern -> Grammar
newRef key value = Grammar $ M.singleton key value
emptyRef :: Grammar
emptyRef = Grammar M.empty
union :: Grammar -> Grammar -> Grammar
union (Grammar m1) (Grammar m2) = Grammar $ M.union m1 m2
hasRecursion :: Grammar -> Either String Bool
hasRecursion g = do
mainPat <- lookupRef g "main"
hasRec g (S.singleton "main") mainPat
hasRec :: Grammar -> S.Set String -> Pattern -> Either String Bool
hasRec _ _ Empty = Right False
hasRec _ _ ZAny = Right False
hasRec _ _ Node{} = Right False
hasRec g set (Or l r) = hasRec g set l ||^ hasRec g set r
hasRec g set (And l r) = hasRec g set l ||^ hasRec g set r
hasRec g set (Not p ) = hasRec g set p
hasRec g set (Concat l r) =
hasRec g set l ||^ (nullable g l &&^ hasRec g set r)
hasRec g set (Interleave l r ) = hasRec g set l ||^ hasRec g set r
hasRec g set (ZeroOrMore p ) = hasRec g set p
hasRec g set (Optional p ) = hasRec g set p
hasRec g set (Contains p ) = hasRec g set p
hasRec g set (Reference refName) = if S.member refName set
then Right True
else do
pat <- lookupRef g refName
hasRec g (S.insert refName set) pat