module Language.Dove.Optimize
( optimize
) where
import Data.List
import Text.Printf
import Language.Dove.Syntax
optimize :: Expr -> Expr
optimize = optRemoveNullEffect . optConstantProp . optInline . optRemoveNullEffect . optConstantProp
optInline :: Expr -> Expr
optInline = optInline' []
optInline' :: [(String, Expr)] -> Expr -> Expr
optInline' env a = case a of
Var a -> case lookup a env of
Nothing -> Var a
Just a -> a
Let a (Var b) c -> opt' ((a, Var b) : env) c
Let a b c -> Let a (opt b) (if inline then opt' ((a, opt b) : env) c else opt' env c)
where
inline = length (elemIndices a $ vars c) <= 1
ForAll a b -> ForAll a $ opt' [ (a', b) | (a', b) <- env, a' /= a ] b
If a b c -> If (opt a) (opt b) (opt c)
Unit -> Unit
Bool a -> Bool a
Integer a -> Integer a
Record a -> Record [ (a, opt b) | (a, b) <- a ]
RecordOverlay a b -> RecordOverlay (opt a) (opt b)
RecordProject a b -> RecordProject (opt a) b
Array a -> Array $ map opt a
ArrayUpdate a b c -> ArrayUpdate (opt a) (opt b) (opt c)
ArrayAppend a b -> ArrayAppend (opt a) (opt b)
ArrayProject a b -> ArrayProject (opt a) (opt b)
UniOp a b -> UniOp a (opt b)
BinOp a b c -> BinOp a (opt b) (opt c)
Comment _ b -> opt b
where
opt = optInline' env
opt' = optInline'
optRemoveNullEffect :: Expr -> Expr
optRemoveNullEffect a = case a of
Let a b c
| elem a $ vars $ opt c -> Let a (opt b) (opt c)
| otherwise -> opt c
ForAll a b
| elem a $ vars $ opt b -> ForAll a $ opt b
| otherwise -> opt b
Var a -> Var a
If a b c -> If (opt a) (opt b) (opt c)
UniOp a b -> UniOp a (opt b)
BinOp a b c -> BinOp a (opt b) (opt c)
Unit -> Unit
Bool a -> Bool a
Integer a -> Integer a
Record a -> Record [ (a, opt b) | (a, b) <- a ]
RecordOverlay a b -> RecordOverlay (opt a) (opt b)
RecordProject a b -> RecordProject (opt a) b
Array a -> Array (map opt a)
ArrayAppend a b -> ArrayAppend (opt a) (opt b)
ArrayUpdate a b c -> ArrayUpdate (opt a) (opt b) (opt c)
ArrayProject a b -> ArrayProject (opt a) (opt b)
Comment _ b -> opt b
where
opt = optRemoveNullEffect
vars :: Expr -> [String]
vars a = case a of
Var a -> [a]
Let a b c -> vars b ++ [ v | v <- vars c, v /= a ]
ForAll a b -> [ v | v <- vars b, v /= a ]
If a b c -> vars a ++ vars b ++ vars c
UniOp _ a -> vars a
BinOp _ a b -> vars a ++ vars b
Unit -> []
Bool _ -> []
Integer _ -> []
Record a -> concatMap vars $ snd $ unzip a
RecordOverlay a b -> vars a ++ vars b
RecordProject a _ -> vars a
Array a -> concatMap vars a
ArrayAppend a b -> vars a ++ vars b
ArrayProject a b -> vars a ++ vars b
ArrayUpdate a b c -> vars a ++ vars b ++ vars c
Comment _ b -> vars b
optConstantProp :: Expr -> Expr
optConstantProp = optConstantProp' []
optConstantProp' :: [(String, Expr)] -> Expr -> Expr
optConstantProp' env a = case a of
Unit -> Unit
Bool a -> Bool a
Integer a -> Integer a
UniOp Not a -> case opt a of
UniOp Not a -> a
Bool a -> Bool $ not a
a -> UniOp Not a
UniOp Negate a -> case opt a of
Integer a -> Integer $ negate a
a -> UniOp Negate a
UniOp Abs a -> case opt a of
Integer a -> Integer $ abs a
a -> UniOp Abs a
UniOp Signum a -> case opt a of
Integer a -> Integer $ signum a
a -> UniOp Signum a
UniOp Length a -> case opt a of
Array a -> Integer $ fromIntegral $ length a
a -> UniOp Length a
UniOp IsArray a -> case opt a of
Array _ -> Bool True
ArrayAppend _ _ -> Bool True
ArrayUpdate _ _ _ -> Bool True
a -> UniOp IsArray a
UniOp IsInt a -> case opt a of
Integer _ -> Bool True
Unit -> Bool False
Bool _ -> Bool False
Record _ -> Bool False
RecordOverlay _ _ -> Bool False
Array _ -> Bool False
ArrayAppend _ _ -> Bool False
ArrayUpdate _ _ _ -> Bool False
a -> UniOp IsInt a
BinOp And a b -> case (opt a, opt b) of
(Bool a, Bool b) -> Bool $ a && b
(Bool False, _) -> false
(_, Bool False) -> false
(Bool True, b) -> b
(a, Bool True) -> a
(a, UniOp Not b) | a == b -> false
(UniOp Not a, b) | a == b -> false
(a, b)
| a == b -> a
| otherwise -> BinOp And a b
BinOp Or a b -> case (opt a, opt b) of
(Bool a, Bool b) -> Bool $ a || b
(Bool True, _) -> true
(_, Bool True) -> true
(Bool False, b) -> b
(a, Bool False) -> a
(a, UniOp Not b) | a == b -> true
(UniOp Not a, b) | a == b -> true
(a, b)
| a == b -> a
| otherwise -> BinOp Or a b
BinOp Implies a b -> case (opt a, opt b) of
(Bool a, Bool b) -> Bool $ not a || b
(Bool False, _) -> true
(_, Bool True) -> true
(Bool True, b) -> b
(a, b)
| a == b -> true
| otherwise -> BinOp Implies a b
BinOp Eq a b -> case (opt a, opt b) of
(Bool a, Bool b) -> Bool $ a == b
(Integer a, Integer b) -> Bool $ a == b
(a, b)
| a == b -> true
| otherwise -> BinOp Eq a b
BinOp Lt a b -> case (opt a, opt b) of
(Integer a, Integer b) -> Bool $ a < b
(a, b)
| a == b -> false
| otherwise -> BinOp Lt a b
BinOp Le a b -> case (opt a, opt b) of
(Integer a, Integer b) -> Bool $ a <= b
(a, b)
| a == b -> false
| otherwise -> BinOp Le a b
BinOp Gt a b -> case (opt a, opt b) of
(Integer a, Integer b) -> Bool $ a > b
(a, b)
| a == b -> false
| otherwise -> BinOp Gt a b
BinOp Ge a b -> case (opt a, opt b) of
(Integer a, Integer b) -> Bool $ a >= b
(a, b)
| a == b -> false
| otherwise -> BinOp Ge a b
BinOp Add a b -> case (opt a, opt b) of
(Integer a, Integer b) -> Integer $ a + b
(a, b) -> BinOp Add a b
BinOp Sub a b -> case (opt a, opt b) of
(Integer a, Integer b) -> Integer $ a b
(a, b) -> BinOp Sub a b
BinOp Mul a b -> case (opt a, opt b) of
(Integer a, Integer b) -> Integer $ a * b
(a, b) -> BinOp Mul a b
BinOp Mod a b -> case (opt a, opt b) of
(Integer a, Integer b) -> Integer $ mod a b
(a, b) -> BinOp Mod a b
Var a -> case lookup a env of
Nothing -> Var a
Just a -> a
Let a b c -> case opt b of
Unit -> opt' ((a, Unit ) : env) c
Bool b -> opt' ((a, Bool b) : env) c
Integer b -> opt' ((a, Integer b) : env) c
Record b -> opt' ((a, Record b) : env) c
Array b -> opt' ((a, Array b) : env) c
b -> Let a b (opt c)
ForAll a b -> ForAll a $ opt' [ (a', b) | (a', b) <- env, a' /= a ] b
If a b c -> case (opt a, opt b, opt c) of
(Bool a, b, c) -> if a then b else c
(a, b, c)
| b == c -> b
| otherwise -> If a b c
Record a -> Record [ (a, opt b) | (a, b) <- a ]
RecordOverlay a b -> case (opt a, opt b) of
(Record a, Record b) -> Record $ a ++ [ b | b <- b, notElem (fst b) $ fst $ unzip a ]
(a, b) -> RecordOverlay a b
RecordProject a b -> case opt a of
Record a -> case lookup b a of
Nothing -> error $ printf "Record %s doesn't have field '%s'." (show (Record a)) b
Just b -> b
a -> RecordProject a b
Array a -> Array $ map opt a
ArrayAppend a b -> case (opt a, opt b) of
(Array a, Array b) -> Array $ a ++ b
(a, b) -> ArrayAppend a b
ArrayProject a b -> case (opt a, opt b) of
(Array a, Integer b')
| b < length a -> a !! b
| otherwise -> error "Index exceeds bounds of array."
where
b = fromInteger b'
(a, b) -> ArrayProject a b
ArrayUpdate a b c -> case (opt a, opt b, opt c) of
(Integer a', b, Array c)
| a < length c -> Array $ take a c ++ [b] ++ drop (a + 1) c
| otherwise -> error "Index exceeds bounds of array."
where
a = fromInteger a'
(a, b, c) -> ArrayUpdate a b c
Comment _ b -> opt b
where
opt = optConstantProp' env
opt' = optConstantProp'