nirum-0.1.0: src/Nirum/Constructs/DeclarationSet.hs
{-# LANGUAGE OverloadedLists, TypeFamilies #-}
module Nirum.Constructs.DeclarationSet ( DeclarationSet()
, NameDuplication( BehindNameDuplication
, FacialNameDuplication
)
, empty
, fromList
, lookup
, lookup'
, null
, null'
, size
, toList
, union
, (!)
) where
import Data.Maybe (fromJust)
import qualified GHC.Exts as L
import Prelude hiding (lookup, null)
import qualified Data.Map.Strict as M
import qualified Data.Set as S
import Nirum.Constructs.Declaration (Declaration(name))
import Nirum.Constructs.Identifier (Identifier)
import Nirum.Constructs.Name (Name(Name, behindName, facialName))
data Declaration a => DeclarationSet a
-- | The set of 'Declaration' values.
-- Every 'name' has to be unique in the set.
-- The order of declarations is maintained.
= DeclarationSet { declarations :: M.Map Identifier a
, index :: ![Identifier]
}
deriving (Eq, Ord, Show)
data NameDuplication = FacialNameDuplication !Name
| BehindNameDuplication !Name
deriving (Eq, Show)
empty :: Declaration a => DeclarationSet a
empty = DeclarationSet { declarations = M.empty
, index = []
}
fromList :: Declaration a => [a] -> Either NameDuplication (DeclarationSet a)
fromList declarations' =
case findDup names facialName S.empty of
Just dup -> Left $ FacialNameDuplication dup
_ -> case findDup names behindName S.empty of
Just dup -> Left $ BehindNameDuplication dup
_ -> Right DeclarationSet { declarations = M.fromList mapList
, index = index'
}
where
names :: [Name]
names = map name declarations'
index' :: [Identifier]
index' = map facialName names
mapList = [(facialName (name d), d) | d <- declarations']
findDup :: [Name] -> (Name -> Identifier) -> S.Set Identifier -> Maybe Name
findDup names' f dups =
case names' of
x:xs -> let name' = f x
in if name' `S.member` dups
then Just x
else findDup xs f $ S.insert name' dups
_ -> Nothing
toList :: Declaration a => DeclarationSet a -> [a]
toList (DeclarationSet declarations' index') =
map ((M.!) declarations') index'
size :: Declaration a => DeclarationSet a -> Int
size (DeclarationSet declarations' _) = M.size declarations'
null :: Declaration a => DeclarationSet a -> Bool
null = null'
null' :: Declaration a => DeclarationSet a -> Bool
null' (DeclarationSet declarations' _) = M.null declarations'
lookup :: Declaration a => Identifier -> DeclarationSet a -> Maybe a
lookup = lookup'
lookup' :: Declaration a => Identifier -> DeclarationSet a -> Maybe a
lookup' facialName' (DeclarationSet declarations' _) =
M.lookup facialName' declarations'
(!) :: Declaration a => DeclarationSet a -> Identifier -> a
declarationSet ! facialName' = fromJust $ lookup' facialName' declarationSet
union :: Declaration a
=> DeclarationSet a
-> DeclarationSet a
-> Either NameDuplication (DeclarationSet a)
union a b = fromList $ toList a ++ toList b
instance (Declaration a) => L.IsList (DeclarationSet a) where
type Item (DeclarationSet a) = a
fromList declarations' =
case fromList declarations' of
Right set -> set
Left (FacialNameDuplication (Name fname _)) ->
error ("identifiers must be unique; " ++ show fname ++
" is defined more than once")
Left (BehindNameDuplication (Name _ bname)) ->
error ("behind names must be unique; " ++ show bname ++
" is defined more than once")
toList = toList