module MicroCabal.Normalize(normalize) where import Data.Function import Data.List import Data.Maybe import MicroCabal.Cabal -- Do some normalization -- * computre conditionals and flatten 'if/else' -- * inline 'import' -- * combine identical fields -- * set library name normalize :: FlagInfo -> Cabal -> Cabal normalize info = libName . combine . inline . reduce info combine :: Cabal -> Cabal combine (Cabal ss) = Cabal $ map (\ (Section s n fs) -> Section s n (combineFields fs)) ss combineFields :: [Field] -> [Field] combineFields = map (foldl1 comb) . groupBy ((==) `on` fieldName) . sortBy (compare `on` fieldName) where fieldName (Field n _) = n fieldName _ = undefined -- Cannot happen, the Ifs are gone comb (Field n v1) (Field _ v2) = Field n (combineValue n v1 v2) comb _ _ = undefined combineValue :: FieldName -> Value -> Value -> Value combineValue _ (VItem x) (VItem y) | x == y = VItem x combineValue _ (VItems xs) (VItems ys) = VItems (xs ++ ys) combineValue _ (VBool x) (VBool y) = VBool (x && y) combineValue _ (VPkgs xs) (VPkgs ys) = VPkgs (xs ++ ys) combineValue n v1 v2 = error $ "fields " ++ show n ++ " cannot be combined, values=" ++ show (v1, v2) inline :: Cabal -> Cabal inline (Cabal ss) = Cabal (map sect nss) where (css, nss) = partition (\ (Section s _ _) -> s == "common") ss coms = [ (n, fs) | Section _ n fs <- css ] sect (Section s n fs) = Section s n $ concatMap inl fs inl (Field "import" (VItem n)) = fromMaybe (error $ "No common " ++ show n) $ lookup n coms inl f = [f] libName :: Cabal -> Cabal libName (Cabal []) = undefined libName (Cabal (g@(Section _ _ gs):ss)) = Cabal $ g : map set ss where set (Section "library" "" fs) = Section "library" name fs set s = s name = head $ [ n | Field "name" (VItem n) <- gs ] ++ [error "no name field"] reduce :: FlagInfo -> Cabal -> Cabal reduce info c = reduce' (addFlags c) c where addFlags (Cabal ss) = info{ flags = flags info ++ concatMap sect ss } sect (Section "flag" n fs) = [(n, dflt n fs)] sect _ = [] dflt n fs = head $ [ b | Field "default" (VBool b) <- fs ] ++ [error $ "no default for flag " ++ show n] reduce' :: FlagInfo -> Cabal -> Cabal reduce' info = mapField red where red (If c t e) | cond info c = concatMap red t | otherwise = concatMap red e red f = [f] mapField :: (Field -> [Field]) -> Cabal -> Cabal mapField f (Cabal ss) = Cabal (map sect ss) where sect (Section s n fs) = Section s n (concatMap f fs) cond :: FlagInfo -> Cond -> Bool cond info = eval where eval (CBool b) = b eval (Cand a b) = eval a && eval b eval (Cor a b) = eval a || eval b eval (Cnot a) = not (eval a) eval (Cos s) = os info == s eval (Carch s) = arch info == s eval (Cflag n) = fromMaybe (error $ "Undefined flag " ++ show n) $ lookup n (flags info) eval (Cimpl s mv) = n == s && maybe True (inVersionRange v) mv where (n, v) = impl info inVersionRange :: Version -> VersionRange -> Bool inVersionRange _ _ = True -- XXX