haste-compiler-0.6.0.0: src/Haste/AST/Syntax.hs
{-# LANGUAGE GADTs, GeneralizedNewtypeDeriving, FlexibleInstances, CPP,
OverloadedStrings #-}
-- | Abstract syntax of Haste's intermediate format.
module Haste.AST.Syntax where
import qualified Data.Set as S
import qualified Data.Map.Strict as M
import Haste.AST.Op
import qualified Data.ByteString.Char8 as BS
type Arity = Int
type Comment = BS.ByteString
type Reorderable = Bool
-- | A Name consists of a variable name and optional (package, module)
-- information.
data Name = Name {
nameIdent :: !BS.ByteString,
nameQualifier :: !(Maybe (BS.ByteString, BS.ByteString))
} deriving (Eq, Ord, Show)
class HasModule a where
moduleOf :: a -> Maybe BS.ByteString
pkgOf :: a -> Maybe BS.ByteString
instance HasModule Name where
moduleOf (Name _ mmod) = fmap snd mmod
pkgOf (Name _ mmod) = fmap fst mmod
instance HasModule Var where
moduleOf (Foreign _) = Nothing
moduleOf (Internal n _ _) = moduleOf n
pkgOf (Foreign _) = Nothing
pkgOf (Internal n _ _) = pkgOf n
type KnownLoc = Bool
-- | Representation of variables.
data Var where
Foreign :: !BS.ByteString -> Var
-- | Being a "known location" means that we can never substitute this
-- variable for another one, as it is used to hold "return values" from
-- case statements, tail loopification and similar.
-- If a variable is *not* a known location, then we may always perform the
-- substitution @a=b ; exp => exp [a/b]@.
Internal :: !Name -> !Comment -> !KnownLoc -> Var
deriving (Show)
isKnownLoc :: Var -> Bool
isKnownLoc (Internal _ _ knownloc) = knownloc
isKnownLoc _ = False
instance Eq Var where
{-# INLINE (==) #-}
(Foreign f1) == (Foreign f2) = f1 == f2
(Internal i1 _ _) == (Internal i2 _ _) = i1 == i2
_ == _ = False
instance Ord Var where
{-# INLINE compare #-}
compare (Foreign f1) (Foreign f2) = compare f1 f2
compare (Internal i1 _ _) (Internal i2 _ _) = compare i1 i2
compare (Foreign _) (Internal _ _ _) = Prelude.LT
compare (Internal _ _ _) (Foreign _) = Prelude.GT
-- | Left hand side of an assignment. Normally we only assign internal vars,
-- but for some primops we need to assign array elements as well.
data LHS where
-- | Introduce a new variable. May be reorderable.
-- Invariant: a NewVar must be the first occurrence of a 'Var' in its
-- scope.
NewVar :: !Reorderable -> !Var -> LHS
-- | Assign a value to an arbitrary LHS expression. May be reorderable.
LhsExp :: !Reorderable -> !Exp -> LHS
deriving (Eq, Show)
-- | Distinguish between normal, optimized and method calls.
-- Normal and optimized calls take a boolean indicating whether the called
-- function should trampoline or not. This defaults to True, and should
-- only be set to False when there is absolutely no possibility whatsoever
-- that the called function will tailcall.
data Call where
Normal :: !Bool -> Call
Fast :: !Bool -> Call
Method :: !BS.ByteString -> Call
deriving (Eq, Show)
-- | Literals; nothing fancy to see here.
data Lit where
LNum :: !Double -> Lit
LStr :: !BS.ByteString -> Lit
LBool :: !Bool -> Lit
LInt :: !Integer -> Lit
LNull :: Lit
deriving (Eq, Show)
-- | Expressions. Completely predictable.
data Exp where
Var :: !Var -> Exp
Lit :: !Lit -> Exp
-- | A literal JS snippet.
-- Invariant: JSLits must not perform side effects or significant
-- computation.
JSLit :: !BS.ByteString -> Exp
Not :: !Exp -> Exp
BinOp :: !BinOp -> Exp -> !Exp -> Exp
Fun :: ![Var] -> !Stm -> Exp
Call :: !Arity -> !Call -> !Exp -> ![Exp] -> Exp
Index :: !Exp -> !Exp -> Exp
Member :: !Exp -> !BS.ByteString -> Exp
Arr :: ![Exp] -> Exp
Obj :: ![(BS.ByteString, Exp)] -> Exp
AssignEx :: !Exp -> !Exp -> Exp
IfEx :: !Exp -> !Exp -> !Exp -> Exp
Eval :: !Exp -> Exp
Thunk :: !Bool -> !Stm -> Exp -- Thunk may be updatable or not
deriving (Eq, Show)
-- | Is the given expression guaranteed to not be a thunk?
-- @definitelyNotThunk e <=> safe to skip evaluation of e@
definitelyNotThunk :: Exp -> Bool
definitelyNotThunk (Lit {}) = True
definitelyNotThunk (JSLit {}) = True
definitelyNotThunk (Not {}) = True
definitelyNotThunk (BinOp {}) = True
definitelyNotThunk (Fun {}) = True
definitelyNotThunk (Arr {}) = True
definitelyNotThunk (Eval {}) = True
definitelyNotThunk _ = False
-- | Statements. The only mildly interesting thing here are the Case and Jump
-- constructors, which allow explicit sharing of continuations.
data Stm where
Case :: !Exp -> !Stm -> ![Alt] -> !Stm -> Stm
Forever :: !Stm -> Stm
Assign :: !LHS -> !Exp -> !Stm -> Stm
Return :: !Exp -> Stm
Cont :: Stm
Stop :: Stm -- Do nothing at all past this point
Tailcall :: !Exp -> Stm
ThunkRet :: !Exp -> Stm -- Return from a Thunk
deriving (Eq, Show)
-- | Case alternatives - an expression to match and a branch.
type Alt = (Exp, Stm)
-- | Represents a module. A module has a name, an owning
-- package, a dependency map of all its definitions, and a bunch of
-- definitions.
data Module = Module {
modPackageId :: !BS.ByteString,
modName :: !BS.ByteString,
modDeps :: M.Map Name (S.Set Name),
modDefs :: M.Map Name Exp,
modSPT :: [Name]
}
-- | Merge two modules. The module and package IDs of the second argument are
-- used, and the second argument will take precedence for symbols which exist
-- in both.
merge :: Module -> Module -> Module
merge m1 m2 = Module {
modPackageId = modPackageId m2,
modName = modName m2,
modDeps = M.union (modDeps m1) (modDeps m2),
modDefs = M.union (modDefs m1) (modDefs m2),
modSPT = modSPT m1 ++ modSPT m2
}
-- | Imaginary module for foreign code that may need one.
foreignModule :: Module
foreignModule = Module {
modPackageId = "",
modName = "",
modDeps = M.empty,
modDefs = M.empty,
modSPT = []
}
-- | An LHS that's guaranteed to not ever be read, enabling the pretty
-- printer to ignore assignments to it.
blackHole :: LHS
blackHole = LhsExp False $ Var blackHoleVar
-- | Name of the data constructor tag field.
dataConTagField :: BS.ByteString
dataConTagField = "_"
-- | Infinite list of data constructor names.
dataConFieldNames :: [BS.ByteString]
dataConFieldNames = names
where
prefix = BS.singleton '_'
names = concat [small, capital, map (BS.append prefix) names]
small = map BS.singleton ['a'..'z']
capital = map BS.singleton ['A'..'Z']
-- | The global "zero object", which represents @{_:0}@.
zeroObject :: Exp
zeroObject = Var $ Foreign "__Z"
-- | The variable of the blackHole LHS.
blackHoleVar :: Var
blackHoleVar = Internal (Name "" (Just ("$blackhole", "$blackhole"))) "" False
-- | Returns the precedence of the top level operator of the given expression.
-- Everything that's not an operator has equal precedence, higher than any
-- binary operator.
expPrec :: Exp -> Int
expPrec (BinOp Sub (Lit (LNum 0)) _) = 500 -- 0-n is always printed as -n
expPrec (BinOp op _ _) = opPrec op
expPrec (AssignEx _ _) = 0
expPrec (Not _) = 500
expPrec _ = 1000