import Text.Parsec import Text.Parsec.String import Data.Functor (($>)) import Data.List (nub) import Debug.Trace import GHC.Stack (HasCallStack) type Set = [String] sets :: [(String, Set)] sets = [ ("Dy", ["a", "b", "c"]) , ("ExD", ["a", "b", "d"]) , ("DVi", ["a", "b", "c", "d", "e"]) , ("dz", ["a", "b"]) ] parens :: Parser Set parens = do _ <- char '(' p <- set _ <- char ')' return p identifier :: Parser Set identifier = choice $ (\(name, els) -> string name $> els) <$> sets union :: Parser Set union = do a <- trace "| left" $ setLeft _ <- oneOf "|∪" b <- trace "| right" $ set return $ nub $ a ++ b intersection :: Parser Set intersection = do a <- trace "& left" $ setLeft _ <- oneOf "&∩" b <- trace "& right" set return $ filter (`elem` b) a without :: Parser Set without = do a <- trace "~ left" $ setLeft _ <- oneOf "-~" b <- trace "~ right" $ set return $ filter (not . (`elem` b)) a set :: Parser Set set = between spaces spaces $ try union <|> try intersection <|> try without <|> identifier <|> parens setLeft :: Parser Set setLeft = between spaces spaces $ identifier <|> parens p :: HasCallStack => String -> Set p str = case parse (set <* eof) "" str of Right set -> set Left err -> error $ show err main :: IO () main = do print $ p "Dy" print $ p "ExD" print $ p "Dy | ExD" print $ p "Dy & ExD" print $ p "Dy ~ ExD" print $ p "Dy | (ExD ~ dz)"