alms-0.6.3: src/AST/Expr.hs
module AST.Expr (
-- * Expressions
Expr'(..), Expr, ExprNote(..), newExpr,
-- ** Letrec and case
Binding'(..), Binding, newBinding,
CaseAlt'(..), CaseAlt, newCaseAlt,
Field'(..), Field, newField,
-- * Two-level expression constructors
-- | These fill in the source location field based on the
-- subexpressions and perform the free variable analysis
-- | variables
exVar, exLit, exCon, exLet, exCase, exLetRec, exLetDecl,
exPair, exAbs, exApp, exInj, exEmb, exCast, exRec, exSel, exAnti,
caClause, caPrj, caAnti,
bnBind, bnAnti,
fdField, fdAnti,
-- ** Synthetic expression constructors
exBVar, exBCon,
exChar, exStr, exInt, exFloat,
exSeq,
exUnit, exNilRecord,
exNil, exCons,
ToExpr(..),
-- * Expression accessors and updaters
syntacticValue, isAnnotated, getExprAnnot, cafakepatt,
) where
import Util
import AST.Notable
import AST.Anti
import AST.Ident
import AST.Type
import AST.Lit
import AST.Patt
import {-# SOURCE #-} AST.Decl
import Meta.DeriveNotable
import Prelude ()
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)
type Field i = N (ExprNote i) (Field' i)
-- | The underlying expression type, which we can pattern match without
-- dealing with the common fields above.
data Expr' i
-- | variables
= ExVar (QVarId i)
-- | literals
| ExLit Lit
-- | data construction
| ExCon (QConId i) (Maybe (Expr i))
-- | let expressions
| ExLet (Patt i) (Expr i) (Expr i)
-- | case expressions (including desugared @if@)
| 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) (Expr i)
-- | application
| ExApp (Expr i) (Expr i)
-- | open variant construction
| ExInj (Uid i) (Maybe (Expr i))
-- | open variant embedding
| ExEmb (Uid i) (Expr i)
-- | record extension
-- (@True@ means additive rather than multiplicative records)
| ExRec Bool [Field i] (Expr i)
-- | record lookup
| ExSel (Expr i) (Uid 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 :: VarId i,
bnexpr :: Expr i
}
| BnAnti Anti
deriving (Typeable, Data)
data CaseAlt' i
-- | Normal match clauses
= CaClause {
capatt :: Patt i,
caexpr :: Expr i
}
-- | Open variant elimination
| CaPrj {
calab :: Uid i,
campatt :: Maybe (Patt i),
caexpr :: Expr i
}
-- | Antiquote
| CaAnti Anti
deriving (Typeable, Data)
data Field' i
-- | Normal match clauses
= FdField {
fdsel :: Uid i,
fdexpr :: Expr i
}
-- | Antiquote
| FdAnti 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 Tag i => Fv (N (ExprNote i) a) i where fv = efv_ . noteOf
instance Dv (N (ExprNote i) (Binding' i)) i where
dv (N _ (BnBind f _)) = [f]
dv (N _ (BnAnti _)) = []
instance Notable (ExprNote i) where
newNote = ExprNote {
eloc_ = bogus,
efv_ = M.empty
}
newExpr :: Tag i => Expr' i -> Expr i
newExpr e0 = flip N e0 $ case e0 of
ExVar v ->
newNote {
efv_ = M.singleton v 1
}
ExLit _ -> newNote
ExCon _ me2 ->
newNote {
efv_ = fv me2,
eloc_ = getLoc me2
}
ExLet x1 e2 e3 ->
newNote {
efv_ = fv e2 |*| (fv e3 |--| qdv x1),
eloc_ = getLoc (x1, e2, e3)
}
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 e2 ->
newNote {
efv_ = fv e2 |--| qdv p1,
eloc_ = getLoc (p1, e2)
}
ExApp e1 e2 ->
newNote {
efv_ = fv e1 |*| fv e2,
eloc_ = getLoc (e1, e2)
}
ExInj _ me2 ->
newNote {
efv_ = fv me2,
eloc_ = getLoc me2
}
ExEmb _ e2 ->
newNote {
efv_ = fv e2,
eloc_ = getLoc e2
}
ExRec True flds e2 ->
newNote {
efv_ = fv (ADDITIVE flds) |+| fv e2,
eloc_ = getLoc (flds, e2)
}
ExRec False flds e2 ->
newNote {
efv_ = fv flds |*| fv e2,
eloc_ = getLoc (flds, e2)
}
ExSel e1 _ ->
newNote {
efv_ = fv e1,
eloc_ = getLoc e1
}
ExCast e1 t2 _ ->
newNote {
efv_ = fv e1,
eloc_ = getLoc (e1, t2)
}
ExAnti a ->
newNote {
efv_ = antierror "fv" a
}
newBinding :: Tag i => Binding' i -> Binding i
newBinding b0 = flip N b0 $ case b0 of
BnBind x e ->
newNote {
efv_ = fv e |-| J [] x,
eloc_ = getLoc e
}
BnAnti a ->
newNote {
efv_ = antierror "fv" a
}
newCaseAlt :: Tag 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)
}
CaPrj _ mx e ->
newNote {
efv_ = fv e |--| qdv mx,
eloc_ = getLoc (mx, e)
}
CaAnti a ->
newNote {
efv_ = antierror "fv" a
}
newField :: Tag i => Field' i -> Field i
newField f0 = flip N f0 $ case f0 of
FdField _ e ->
newNote {
efv_ = fv e,
eloc_ = getLoc e
}
FdAnti a ->
newNote {
efv_ = antierror "fv" a
}
deriveNotable 'newExpr (''Tag, [0]) ''Expr
deriveNotable 'newCaseAlt (''Tag, [0]) ''CaseAlt
deriveNotable 'newBinding (''Tag, [0]) ''Binding
deriveNotable 'newField (''Tag, [0]) ''Field
exBVar :: Tag i => VarId i -> Expr i
exBVar = exVar . J []
exBCon :: Tag i => ConId i -> Maybe (Expr i) -> Expr i
exBCon = exCon . J []
exChar :: Tag i => Char -> Expr i
exChar = exLit . LtChar
exStr :: Tag i => String -> Expr i
exStr = exLit . LtStr
exInt :: (Tag i, Integral a) => a -> Expr i
exInt = exLit . LtInt . toInteger
exFloat :: Tag i => Double -> Expr i
exFloat = exLit . LtFloat
exSeq :: Tag i => Expr i -> Expr i -> Expr i
exSeq e1 e2 = exLet paWild e1 e2
exUnit, exNilRecord :: Tag i => Expr i
exUnit = exCon idUnitVal Nothing
exNilRecord = exVar idNilRecord
exCons :: Tag i => Expr i -> Expr i -> Expr i
exCons = exCon idConsList . Just <$$> exPair
exNil :: Tag i => Expr i
exNil = exCon idNilList Nothing
class ToExpr a i | a → i where
toExpr ∷ a → Expr i
instance ToExpr (Expr i) i where
toExpr = id
instance Tag i ⇒ ToExpr (QVarId i) i where
toExpr = exVar
instance Tag i ⇒ ToExpr (VarId i) i where
toExpr = exBVar
instance (Tag i, ToExpr a i, ToExpr b i) ⇒ ToExpr (a, b) i where
toExpr (a, b) = exPair (toExpr a) (toExpr b)
instance Tag i ⇒ ToExpr String i where
toExpr = exStr
instance Tag i ⇒ ToExpr Int i where
toExpr = exInt
instance Tag i ⇒ ToExpr Char i where
toExpr = exChar
instance Tag i ⇒ ToExpr Double i where
toExpr = exFloat
-- | Is the expression conservatively side-effect free?
syntacticValue :: Expr i -> Bool
syntacticValue e = case view e of
ExVar _ → True
ExLit _ → True
ExCon _ me → maybe True syntacticValue me
ExLet _ e1 e2 → syntacticValue e1 && syntacticValue e2
ExCase _ _ → False
ExLetRec bs e2 → all eachBinding bs && syntacticValue e2 where
eachBinding bn = case view bn of
BnBind { bnexpr = e' } → syntacticValue e'
BnAnti a → antierror "syntacticValue" a
ExLetDecl _ _ → False
ExPair e1 e2 → syntacticValue e1 && syntacticValue e2
ExAbs _ _ → True
ExApp _ _ → False
ExInj _ me → maybe True syntacticValue me
ExEmb _ e1 → syntacticValue e1
ExRec b flds e2
→ b ||
(and [ syntacticValue ei | FdField _ ei ← view <$> flds ]
&& syntacticValue e2)
ExSel _ _ → False
ExCast e1 _ b → syntacticValue e1 && not b
ExAnti a → antierror "syntacticValue" a
-- | Is the expression annotated with a type ascription or dynamic cast?
isAnnotated ∷ Expr i → Bool
isAnnotated e = case view e of
ExVar _ → False
ExLit _ → False
ExCon _ _ → False
ExLet _ _ e2 → isAnnotated e2
ExCase _ cs → all eachClause cs where
eachClause c = case view c of
CaClause { caexpr = e' } → isAnnotated e'
CaPrj { caexpr = e' } → isAnnotated e'
CaAnti a → antierror "isAnnotated" a
ExLetRec _ e2 → isAnnotated e2
ExLetDecl _ e2 → isAnnotated e2
ExPair _ _ → False
ExAbs _ _ → False
ExApp _ _ → False
ExInj _ _ → False
ExEmb _ _ → False
ExRec _ _ _ → False
ExSel _ _ → False
ExCast _ _ _ → True
ExAnti a → antierror "syntacticValue" a
-- | Get the (static) type annotation on an expression
getExprAnnot ∷ Expr i → Maybe (Type i)
getExprAnnot e0 = case view e0 of
ExCast _ annot False → Just annot
_ → Nothing
-- | Given a case alternative, produce a (potentially fake)
-- representation of its pattern, suitable for printing.
cafakepatt ∷ Tag i ⇒ CaseAlt i → Patt i
cafakepatt ca0 = case view ca0 of
CaClause x _ → x
CaPrj u mx _ → paCon (qident ('#':idName u)) mx
CaAnti a → $antierror