alms-0.4.9: src/Syntax/Expr.hs
{-# LANGUAGE
DeriveDataTypeable,
FlexibleInstances,
MultiParamTypeClasses,
TemplateHaskell,
TypeFamilies,
TypeSynonymInstances #-}
module Syntax.Expr (
-- * Expressions
Expr'(..), Expr, ExprNote(..), newExpr,
-- ** Letrec and case
Binding'(..), Binding, newBinding,
CaseAlt'(..), CaseAlt, newCaseAlt,
-- * Two-level expression constructors
-- | These fill in the source location field based on the
-- subexpressions and perform the free variable analysis
exId, exLit, exCase, exLetRec, exLetDecl, exPair,
exAbs, exApp, exTAbs, exTApp, exPack, exCast, exAnti,
caClause, caAnti,
bnBind, bnAnti,
-- ** Synthetic expression constructors
exVar, exCon, exBVar, exBCon,
exStr, exInt, exFloat,
exLet, exSeq,
-- ** Optimizing expression constructors
exLet', exLetVar', exAbs', exAbsVar', exTAbs',
-- * Expression accessors and updaters
syntacticValue
) where
import Syntax.Notable
import Syntax.Anti
import Syntax.Ident
import Syntax.Type
import Syntax.Lit
import Syntax.Patt
import {-# SOURCE #-} Syntax.Decl
import Viewable
import Meta.DeriveNotable
import Data.Generics (Typeable(..), Data(..))
import qualified Data.Map as M
type Expr i = N (ExprNote i) (Expr' i)
type Binding i = N (ExprNote i) (Binding' i)
type CaseAlt i = N (ExprNote i) (CaseAlt' i)
-- | The underlying expression type, which we can pattern match without
-- dealing with the common fields above.
data Expr' i
-- | variables and datacons
= ExId (Ident i)
-- | literals
| ExLit Lit
-- | case expressions (including desugared @if@ and @let@)
| ExCase (Expr i) [CaseAlt i]
-- | recursive let expressions
| ExLetRec [Binding i] (Expr i)
-- | nested declarations
| ExLetDecl (Decl i) (Expr i)
-- | pair construction
| ExPair (Expr i) (Expr i)
-- | lambda
| ExAbs (Patt i) (Type i) (Expr i)
-- | application
| ExApp (Expr i) (Expr i)
-- | type abstraction
| ExTAbs (TyVar i) (Expr i)
-- | type application
| ExTApp (Expr i) (Type i)
-- | existential construction
| ExPack (Maybe (Type i)) (Type i) (Expr i)
-- | dynamic promotion (True) or static type ascription (False)
| ExCast (Expr i) (Type i) Bool
-- | antiquotes
| ExAnti Anti
deriving (Typeable, Data)
-- | Let-rec bindings require us to give types
data Binding' i
= BnBind {
bnvar :: Lid i,
bntype :: Type i,
bnexpr :: Expr i
}
| BnAnti Anti
deriving (Typeable, Data)
data CaseAlt' i
= CaClause {
capatt :: Patt i,
caexpr :: Expr i
}
| CaAnti Anti
deriving (Typeable, Data)
-- | The annotation on every expression
data ExprNote i
= ExprNote {
-- | source location
eloc_ :: !Loc,
-- | free variables
efv_ :: FvMap i
}
deriving (Typeable, Data)
instance Locatable (ExprNote i) where
getLoc = eloc_
instance Relocatable (ExprNote i) where
setLoc note loc = note { eloc_ = loc }
-- | Types with free variable analyses
instance Id i => Fv (N (ExprNote i) a) i where fv = efv_ . noteOf
instance Notable (ExprNote i) where
newNote = ExprNote {
eloc_ = bogus,
efv_ = M.empty
}
newExpr :: Id i => Expr' i -> Expr i
newExpr e0 = flip N e0 $ case e0 of
ExId i ->
newNote {
efv_ = case view i of
Left y -> M.singleton y 1
_ -> M.empty
}
ExLit _ -> newNote
ExCase e1 cas ->
newNote {
efv_ = fv e1 |*| fv (ADDITIVE cas),
eloc_ = getLoc (e1, cas)
}
ExLetRec bns e2 ->
newNote {
efv_ = let vs = map (J [] . bnvar . dataOf) bns
pot = fv e2 |+| fv bns
in foldl (|-|) pot vs,
eloc_ = getLoc (bns, e2)
}
ExLetDecl d1 e2 ->
newNote {
efv_ = fv d1 |*| (fv e2 |--| qdv d1),
eloc_ = getLoc (d1, e2)
}
ExPair e1 e2 ->
newNote {
efv_ = fv e1 |*| fv e2,
eloc_ = getLoc (e1, e2)
}
ExAbs p1 _ e3 ->
newNote {
efv_ = fv e3 |--| qdv p1,
eloc_ = getLoc (p1, e3)
}
ExApp e1 e2 ->
newNote {
efv_ = fv e1 |*| fv e2,
eloc_ = getLoc (e1, e2)
}
ExTAbs _ e2 ->
newNote {
efv_ = fv e2,
eloc_ = getLoc e2
}
ExTApp e1 t2 ->
newNote {
efv_ = fv e1,
eloc_ = getLoc (e1, t2)
}
ExPack mt1 t2 e3 ->
newNote {
efv_ = fv e3,
eloc_ = getLoc (mt1, t2, e3)
}
ExCast e1 t2 _ ->
newNote {
efv_ = fv e1,
eloc_ = getLoc (e1, t2)
}
ExAnti a ->
newNote {
efv_ = antierror "fv" a
}
newBinding :: Id i => Binding' i -> Binding i
newBinding b0 = flip N b0 $ case b0 of
BnBind x t e ->
newNote {
efv_ = fv e |-| J [] x,
eloc_ = getLoc (t, e)
}
BnAnti a ->
newNote {
efv_ = antierror "fv" a
}
newCaseAlt :: Id i => CaseAlt' i -> CaseAlt i
newCaseAlt ca0 = flip N ca0 $ case ca0 of
CaClause x e ->
newNote {
efv_ = fv e |--| qdv x,
eloc_ = getLoc (x, e)
}
CaAnti a ->
newNote {
efv_ = antierror "fv" a
}
deriveNotable 'newExpr (''Id, [0]) ''Expr
deriveNotable 'newCaseAlt (''Id, [0]) ''CaseAlt
deriveNotable 'newBinding (''Id, [0]) ''Binding
exVar :: Id i => QLid i -> Expr i
exVar = exId . fmap Var
exCon :: Id i => QUid i -> Expr i
exCon = exId . fmap Con
exBVar :: Id i => Lid i -> Expr i
exBVar = exId . J [] . Var
exBCon :: Id i => Uid i -> Expr i
exBCon = exId . J [] . Con
exStr :: Id i => String -> Expr i
exStr = exLit . LtStr
exInt :: Id i => Integer -> Expr i
exInt = exLit . LtInt
exFloat :: Id i => Double -> Expr i
exFloat = exLit . LtFloat
exLet :: Id i => Patt i -> Expr i -> Expr i -> Expr i
exLet x e1 e2 = exCase e1 [caClause x e2]
exSeq :: Id i => Expr i -> Expr i -> Expr i
exSeq e1 e2 = exCase e1 [caClause paWild e2]
-- | Constructs a let expression, but with a special case:
--
-- @let x = e in x == e@
-- @let (x, y) = e in (x, y) == e@
--
-- This is always safe to do.
exLet' :: Id i => Patt i -> Expr i -> Expr i -> Expr i
exLet' x e1 e2 = if (x -==+ e2) then e1 else exLet x e1 e2
-- | Constructs a let expression whose pattern is a variable.
exLetVar' :: Id i => Lid i -> Expr i -> Expr i -> Expr i
exLetVar' = exLet' . paVar
-- | Constructs a lambda expression, but with a special case:
--
-- @exAbs' x t (exApp (exVar f) (exVar x)) == exVar f@
--
-- This eta-contraction is always safe, because f has no effect
exAbs' :: Id i => Patt i -> Type i -> Expr i -> Expr i
exAbs' x t e = case view e of
ExApp e1 e2 -> case (dataOf x, view e1, view e2) of
(PaVar y, ExId (J p (Var f)), ExId (J [] (Var y'))) |
y == y' && J [] y /= J p f
-> exVar (J p f)
_ -> exAbs x t e
_ -> exAbs x t e
-- | Construct an abstraction whose pattern is just a variable.
exAbsVar' :: Id i => Lid i -> Type i -> Expr i -> Expr i
exAbsVar' = exAbs' . paVar
-- | Construct a type-lambda expression, but with a special case:
--
-- @exTAbs' tv (exTApp (exVar f) tv) == exVar f@
--
-- This should always be safe, because f has no effect
exTAbs' :: Id i => TyVar i -> Expr i -> Expr i
exTAbs' tv e = case view e of
ExTApp e1 t2 -> case (view e1, dataOf t2) of
(ExId (J p (Var f)), TyVar tv') |
tv == tv' -> exVar (J p f)
_ -> exTAbs tv e
_ -> exTAbs tv e
-- | Does a pattern exactly match an expression? That is, is
-- @let p = e1 in e@ equivalent to @e1@? Note that we cannot
-- safely handle data constructors, because they may fail to match.
(-==+) :: Id i => Patt i -> Expr i -> Bool
p -==+ e = case (dataOf p, dataOf e) of
(PaVar l, ExId (J [] (Var l')))
-> l == l'
(PaCon (J [] (Uid _ "()")) Nothing,
ExId (J [] (Con (Uid _ "()"))))
-> True
(PaPair p1 p2, ExPair e1 e2)
-> p1 -==+ e1 && p2 -==+ e2
_ -> False
infix 4 -==+
-- | Is the expression conservatively side-effect free?
syntacticValue :: Expr i -> Bool
syntacticValue e = case view e of
ExId _ -> True
ExLit _ -> True
ExPair e1 e2 -> syntacticValue e1 && syntacticValue e2
ExAbs _ _ _ -> True
ExApp e1 e2 -> syntacticConstructor e1 && syntacticValue e2
ExTAbs _ _ -> True
ExTApp e1 _ -> syntacticValue e1
ExAnti a -> antierror "syntacticValue" a
_ -> False
syntacticConstructor :: Expr i -> Bool
syntacticConstructor e = case view e of
ExId (J [] (Con _)) -> True
ExTApp e1 _ -> syntacticConstructor e1
ExApp e1 e2 -> syntacticConstructor e1 && syntacticValue e2
ExAnti a -> antierror "syntacticConstructor" a
_ -> False