module Transfer.Interpreter where import Transfer.Core.Abs import Transfer.Core.Print import Control.Monad import Data.List import Data.Maybe import Debug.Trace data Value = VStr String | VInt Integer | VDbl Double | VType | VRec [(CIdent,Value)] | VClos Env Exp | VCons CIdent [Value] | VPrim (Value -> Value) | VMeta Integer deriving (Show) instance Show (a -> b) where show _ = "<>" -- -- * Environment -- newtype Env = Env [(CIdent,Value)] deriving Show mkEnv :: [(CIdent,Value)] -> Env mkEnv = Env addToEnv :: [(CIdent,Value)] -> Env -> Env addToEnv bs (Env e) = Env (bs ++ e) lookupEnv :: Env -> CIdent -> Value lookupEnv (Env e) id = case lookup id e of Just x -> x Nothing -> error $ "Variable " ++ printTree id ++ " not in environment." ++ " Environment contains: " ++ show (map (printTree . fst) e) prEnv :: Env -> String prEnv (Env e) = unlines [ printTree id ++ ": " ++ printValue v | (id,v) <- e ] seqEnv :: Env -> Env seqEnv (Env e) = Env $! deepSeqList [ fst p `seq` p | p <- e ] -- | The built-in types and functions. builtin :: Env builtin = mkEnv [(CIdent "Integer",VType), (CIdent "Double",VType), (CIdent "String",VType), mkIntUn "neg" negate toInt, mkIntBin "add" (+) toInt, mkIntBin "sub" (-) toInt, mkIntBin "mul" (*) toInt, mkIntBin "div" div toInt, mkIntBin "mod" mod toInt, mkIntBin "eq" (==) toBool, mkIntBin "cmp" compare toOrd, mkIntUn "show" show toStr, mkDblUn "neg" negate toDbl, mkDblBin "add" (+) toDbl, mkDblBin "sub" (-) toDbl, mkDblBin "mul" (*) toDbl, mkDblBin "div" (/) toDbl, mkDblBin "mod" (\_ _ -> 0.0) toDbl, mkDblBin "eq" (==) toBool, mkDblBin "cmp" compare toOrd, mkDblUn "show" show toStr, mkStrBin "add" (++) toStr, mkStrBin "eq" (==) toBool, mkStrBin "cmp" compare toOrd, mkStrUn "show" show toStr ] where toInt i = VInt i toDbl i = VDbl i toBool b = VCons (CIdent (show b)) [] toOrd o = VCons (CIdent (show o)) [] toStr s = VStr s mkUn t a x f g = let c = CIdent ("prim_" ++ x ++ "_" ++ t) in (c, VPrim (\n -> a f g n)) mkBin t a x f g = let c = CIdent ("prim_" ++ x ++ "_" ++ t) in (c, VPrim (\n -> VPrim (\m -> a f g n m ))) mkIntUn = mkUn "Integer" $ \ f g x -> case x of VInt n -> g (f n) _ -> error $ printValue x ++ " is not an integer" mkIntBin = mkBin "Integer" $ \ f g x y -> case (x,y) of (VInt n,VInt m) -> g (f n m) _ -> error $ printValue x ++ " and " ++ printValue y ++ " are not both integers" mkDblUn = mkUn "Double" $ \ f g x -> case x of VDbl n -> g (f n) _ -> error $ printValue x ++ " is not a double" mkDblBin = mkBin "Double" $ \ f g x y -> case (x,y) of (VDbl n,VDbl m) -> g (f n m) _ -> error $ printValue x ++ " and " ++ printValue y ++ " are not both doubles" mkStrUn = mkUn "String" $ \ f g x -> case x of VStr n -> g (f n) _ -> error $ printValue x ++ " is not a string" mkStrBin = mkBin "String" $ \ f g x y -> case (x,y) of (VStr n,VStr m) -> g (f n m) _ -> error $ printValue x ++ " and " ++ printValue y ++ " are not both strings" addModuleEnv :: Env -> Module -> Env addModuleEnv env (Module ds) = let bs = [ (c,VCons c []) | DataDecl _ _ cs <- ds, ConsDecl c _ <- cs ] ++ [ (t,VCons t []) | DataDecl t _ _ <- ds ] ++ [ (x,eval env' e) | ValueDecl x e <- ds] env' = addToEnv bs env in env' -- -- * Evaluation. -- eval :: Env -> Exp -> Value eval env x = case x of ELet defs exp2 -> let env' = [ (id, v) | LetDef id e <- defs, let v = eval env' e] `addToEnv` env in eval (seqEnv env') exp2 ECase exp cases -> let v = eval env exp r = case firstMatch env v cases of Nothing -> error $ "No pattern matched " ++ printValue v Just (e,env') -> eval env' e in v `seq` r EAbs _ _ -> VClos env x EPi _ _ _ -> VClos env x EApp exp1 exp2 -> let v1 = eval env exp1 v2 = eval env exp2 in case v1 of VClos env' (EAbs id e) -> eval (bind id v2 `addToEnv` env') e VPrim f -> f $! v2 VCons c vs -> (VCons $! c) $! ((++) $! vs) $! [v2] _ -> error $ "Bad application (" ++ printValue v1 ++ ") (" ++ printValue v2 ++ ")" EProj exp id -> let v = eval env exp in case v of VRec fs -> recLookup id fs _ -> error $ printValue v ++ " is not a record, " ++ "cannot get field " ++ printTree id ERecType fts -> VRec $! deepSeqList $! [v `seq` (f,v) | FieldType f e <- fts, let v = eval env e] ERec fvs -> VRec $! deepSeqList $! [v `seq` (f,v) | FieldValue f e <- fvs, let v = eval env e] EVar id -> lookupEnv env id EType -> VType EStr str -> VStr str EInteger n -> VInt n EDouble n -> VDbl n EMeta (TMeta t) -> VMeta (read $ drop 1 t) firstMatch :: Env -> Value -> [Case] -> Maybe (Exp,Env) firstMatch _ _ [] = Nothing firstMatch env v (Case p g e:cs) = case match p v of Nothing -> firstMatch env v cs Just bs -> let env' = bs `addToEnv` env in case eval env' g of VCons (CIdent "True") [] -> Just (e,env') VCons (CIdent "False") [] -> firstMatch env v cs x -> error $ "Error in guard: " ++ printValue x ++ " is not a Bool" bind :: PatternVariable -> Value -> [(CIdent,Value)] bind (PVVar x) v = [(x,v)] bind PVWild _ = [] match :: Pattern -> Value -> Maybe [(CIdent,Value)] match (PCons c' ps) (VCons c vs) | c == c' = if length vs == length ps then concatM $ zipWith match ps vs else error $ "Wrong number of arguments to " ++ printTree c match (PVar x) v = Just (bind x v) match (PRec fps) (VRec fs) = concatM [ match p (recLookup f fs) | FieldPattern f p <- fps ] match (PInt i) (VInt i') | i == i' = Just [] match (PStr s) (VStr s') | s == s' = Just [] match (PInt i) (VInt i') | i == i' = Just [] match _ _ = Nothing recLookup :: CIdent -> [(CIdent,Value)] -> Value recLookup l fs = case lookup l fs of Just x -> x Nothing -> error $ printValue (VRec fs) ++ " has no field " ++ printTree l -- -- * Utilities -- concatM :: Monad m => [m [a]] -> m [a] concatM = liftM concat . sequence -- | Force a list and its values. deepSeqList :: [a] -> [a] deepSeqList = foldr (\x xs -> x `seq` xs `seq` (x:xs)) [] -- -- * Convert values to expressions -- valueToExp :: Value -> Exp valueToExp v = case v of VStr s -> EStr s VInt i -> EInteger i VDbl i -> EDouble i VType -> EType VRec fs -> ERec [ FieldValue f (valueToExp v) | (f,v) <- fs] VClos env e -> e VCons c vs -> foldl EApp (EVar c) (map valueToExp vs) VPrim _ -> EVar (CIdent "<>") -- FIXME: what to return here? VMeta n -> EMeta $ TMeta $ "?" ++ show n -- -- * Pretty printing of values -- printValue :: Value -> String printValue v = printTree (valueToExp v)