1{-# LANGUAGE PatternGuards #-}
2module Core.Execute (execute) where
5import Idris.AbsSyntaxTree
13import Util.DynamicLinker
16import Control.Applicative hiding (Const)
17import Control.Monad.Trans
20import qualified Data.Map as M
23import Foreign.C.String
24import Foreign.Marshal.Alloc (free)
29-- | Attempt to perform a side effect. Return either Just the next step in
30-- evaluation (after performing the side effect through IO), or Nothing if no
33-- This function is exceedingly generous about what it will accept in order to
34-- enable convenient testing at the REPL.
35-- step :: TT Name -> Idris (Maybe (TT Name))
36-- step tm = step' (unApply tm)
37-- where step' (P _ (UN "unsafePerformIO") _, [_, arg] ) = step arg -- Only step if arg can be stepped
38-- step' (P _ (UN "mkForeign") _, args) = stepForeign args
39-- step' (P _ (UN "prim__IO") _, [_, arg]) = return (Just arg)
40-- step' (P _ (UN "prim__readString") _, [(P _ (UN "prim__stdin") _)]) =
41-- do line <- lift $ getLine
42-- return . Just . Constant . Str $ line
43-- step' _ = return Nothing
45-- | Perform side effects until no more can be performed, then return the
46-- resulting term (possibly the argument).
47-- execute :: TT Name -> Idris (TT Name)
48-- execute tm = case unApply tm of
49-- (P _ (UN "unsafePerformIO") _, [_, arg] ) -> execute' arg
51-- where execute' tm = do stepped <- step tm
53-- Nothing -> return tm
54-- Just tm' -> execute' tm'
57execute :: Term -> Idris Term
58execute tm = do ctxt <- getContext
61doExec :: Env -> Context -> Term -> Idris Term
62doExec env ctxt p@(P Ref n ty) =
63 do let val = lookupDef Nothing n ctxt
66 do val <- doExec env ctxt tm
68 [TyDecl nt ty] -> return p -- abstract def
69 [Operator tp arity op] -> do ty' <- doExec env ctxt ty
71 [CaseOp _ _ _ _ _ [] (STerm tm) _ _] -> -- nullary fun
73 [CaseOp _ _ _ _ _ ns sc _ _] -> return p
74 thing -> trace ("got to " ++ show thing) $ undefined
75doExec env ctxt p@(P Bound n ty) = case (lookupDef Nothing n ctxt) of -- bound vars must be Function
76 [Function ty tm] -> doExec env ctxt tm
78 x -> fail ("Internal error lookup up bound var " ++ show n ++ " - found " ++ show x)
79doExec env ctxt p@(P (DCon a b) n ty) = do { ty' <- doExec env ctxt ty; return (P (DCon a b) n ty') }
80doExec env ctxt p@(P (TCon a b) n ty) = do { ty' <- doExec env ctxt ty; return (P (TCon a b) n ty') }
81doExec env ctxt v@(V i) | i < length env = do let binder = env !! i
83 (_, (Let t v)) -> return v
84 (_, (NLet t v)) -> return v
85 (n, b) -> doExec env ctxt (P Bound n (binderTy b))
86 | otherwise = return $ V i
87doExec env ctxt (Bind n (Let t v) body) = do v' <- doExec env ctxt v
88 let ctxt' = addToCtxt n v' t ctxt
89 doExec ((n, Let t v'):env) ctxt' body
90doExec env ctxt (Bind n (NLet t v) body) = trace "NLet" $ undefined
91doExec env ctxt tm@(Bind n b body) = return tm -- don't eval under lambda!
92doExec env ctxt a@(App _ _) = execApp env ctxt (unApply a)
93doExec env ctxt (Constant c) = return (Constant c)
94doExec env ctxt (Proj tm i) = let (x, xs) = unApply tm in
96doExec env ctxt Erased = return Erased
97doExec env ctxt Impossible = fail "Tried to execute an impossible case"
98doExec env ctxt (TType u) = return (TType u)
100execApp :: Env -> Context -> (Term, [Term]) -> Idris Term
101execApp env ctxt (f, args) = trace ("Reducing " ++ show f) $
102 do newF <- doExec env ctxt f
103 newArgs <- mapM (doExec env ctxt) args
104 execApp' env ctxt newF newArgs
106execApp' :: Env -> Context -> Term -> [Term] -> Idris Term
107execApp' env ctxt v [] = return v -- no args is just a constant! can result from function calls
108execApp' env ctxt (P _ (UN "unsafePerformIO") _) [ty, action] | (prim__IO, [ty', v]) <- unApply action = return v
109execApp' env ctxt (P _ (UN "mkForeign") _) args =
110 do res <- stepForeign args
112 Nothing -> fail "Could not call foreign function"
114execApp' env ctxt (P _ (UN "prim__readString") _) [P _ (UN "prim__stdin") _] =
115 do line <- lift getLine
116 return (Constant (Str line))
118execApp' env ctxt (P _ (UN "prim__concat") _) [(Constant (Str s1)), (Constant (Str s2))] =
119 return $ Constant (Str (s1 ++ s2))
121execApp' env ctxt (P _ (UN "prim__eqInt") _) [(Constant (I i1)), (Constant (I i2))] =
122 return $ if i1 == i2 then Constant (I 1) else Constant (I 0)
124execApp' env ctxt (P _ (UN "prim__readString") _) [ptr] | Just p <- unPtr ptr =
125 do fn <- findForeign "freadStr"
127 Just (Fun _ freadStr) -> do
128 res <- lift $ callFFI freadStr retCString [argPtr p]
129 str <- lift $ peekCString res
130 trace ("Found " ++ str) $ return ()
131 return $ Constant (Str str)
132 Nothing -> fail "Could not load freadStr"
134execApp' env ctxt f@(P _ n _) args =
135 do let val = lookupDef Nothing n ctxt
137 [Function _ tm] -> fail "should already have been eval'd"
138 [TyDecl nt ty] -> return $ mkApp f args
139 [Operator tp arity op] -> fail $ "Can't apply operator " ++ show n ++ " which needs special-casing"
140 [CaseOp _ _ _ _ _ [] (STerm tm) _ _] -> -- nullary fun
141 do rhs <- doExec env ctxt tm
142 doExec env ctxt (mkApp tm args)
143 [CaseOp _ _ _ _ _ ns sc _ _] ->
144 do res <- execCase env ctxt ns sc args
145 return $ fromMaybe (mkApp f args) res
146 thing -> return $ mkApp f args
148execApp' env ctxt bnd@(Bind n b body) (arg:args) = let ctxt' = addToCtxt n arg Erased ctxt in
149 doExec ((n, b):env) ctxt' body
151execApp' env ctxt f args = return (mkApp f args)
153execCase :: Env -> Context -> [Name] -> SC -> [Term] -> Idris (Maybe Term)
154execCase env ctxt ns sc args =
155 let arity = length ns in
156 if arity <= length args
157 then do let amap = (zip ns (take arity args))
158 caseRes <- execCase' env ctxt amap sc
160 Just res-> Just <$> doExec env ctxt (mkApp res (drop arity args))
161 Nothing -> return Nothing
164execCase' :: Env -> Context -> [(Name, Term)] -> SC -> Idris (Maybe Term)
165execCase' env ctxt amap (STerm tm) =
166 Just <$> doExec env (foldl (\c (n, t) -> addToCtxt n t Erased c) ctxt amap) tm
167execCase' env ctxt amap (Case n alts) | Just tm <- lookup n amap
168 , Just (newCase, newBindings) <- chooseAlt tm alts =
169 execCase' env ctxt (amap ++ newBindings) newCase
170 | otherwise = return Nothing
172chooseAlt :: Term -> [CaseAlt] -> Maybe (SC, [(Name, Term)])
173chooseAlt _ (DefaultCase sc : alts) = Just (sc, [])
174chooseAlt (Constant c) (ConstCase c' sc : alts) | c == c' = Just (sc, [])
175chooseAlt tm (ConCase n i ns sc : alts) | ((P _ cn _), args) <- unApply tm, cn == n = Just (sc, zip ns args)
176 | otherwise = chooseAlt tm alts
177chooseAlt tm (_:alts) = chooseAlt tm alts
178chooseAlt _ [] = Nothing
180data FTy = FInt | FFloat | FChar | FString | FPtr | FUnit deriving (Show, Read)
182idrisType :: FTy -> Type
183idrisType FUnit = P Ref unitTy (TType (UVal 0))
184idrisType ft = Constant (idr ft)
185 where idr FInt = IType
188 idr FString = StrType
191data Foreign = FFun String [FTy] FTy deriving Show
193-- | A representation of Ptr values, which otherwise don't work in TT
197-- | Convert a Haskell pointer to a Ptr term in TT
199ptr p = App (P (DCon 1 0) ptrCon Erased) (Constant (I (addr p)))
200 where addr p = p `minusPtr` nullPtr
202-- | Convert a Ptr term in TT to a Haskell pointer
203unPtr :: Term -> Maybe (Ptr a)
204unPtr (App (P _ con _) (Constant (I addr))) | con == ptrCon = Just (unAddr addr)
205 where unAddr a = nullPtr `plusPtr` a
209call :: Foreign -> [Term] -> Idris (Maybe Term)
210call (FFun name argTypes retType) args =
211 do fn <- findForeign name
213 Nothing -> return Nothing
214 Just f -> do res <- call' f args retType
215 return . Just $ mkApp (P Ref (UN "prim__IO") Erased) [idrisType retType, res]
216 where call' :: ForeignFun -> [Term] -> FTy -> Idris Term
217 call' (Fun _ h) args FInt = do res <- lift $ callFFI h retCInt (prepArgs args)
218 return (Constant (I (fromIntegral res)))
219 call' (Fun _ h) args FFloat = do res <- lift $ callFFI h retCDouble (prepArgs args)
220 return (Constant (Fl (realToFrac res)))
221 call' (Fun _ h) args FChar = do res <- lift $ callFFI h retCChar (prepArgs args)
222 return (Constant (Ch (castCCharToChar res)))
223 call' (Fun _ h) args FString = do res <- lift $ callFFI h retCString (prepArgs args)
224 hStr <- lift $ peekCString res
226 return (Constant (Str hStr))
228 call' (Fun _ h) args FPtr = do res <- lift $ callFFI h (retPtr retVoid) (prepArgs args)
230 call' (Fun _ h) args FUnit = do res <- lift $ callFFI h retVoid (prepArgs args)
231 return (P Ref unitCon (P Ref unitTy (TType (UVal 0)))) -- FIXME check universe level
232-- call' (Fun _ h) args other = fail ("Unsupported foreign return type " ++ show other)
235 prepArgs = map prepArg
236 prepArg (Constant (I i)) = argCInt (fromIntegral i)
237 prepArg (Constant (Fl f)) = argCDouble (realToFrac f)
238 prepArg (Constant (Ch c)) = argCChar (castCharToCChar c) -- FIXME - castCharToCChar only safe for first 256 chars
239 prepArg (Constant (Str s)) = argString s
240 prepArg ptr | Just p <- unPtr ptr = argPtr p
241 prepArg other = trace ("Could not use " ++ show other ++ " as FFI arg.") undefined
245foreignFromTT :: Term -> Maybe Foreign
246foreignFromTT t = case (unApply t) of
247 (_, [(Constant (Str name)), args, ret]) ->
248 do argTy <- unList args
249 argFTy <- sequence $ map getFTy argTy
251 return $ FFun name argFTy retFTy
252 _ -> trace "failed to construct ffun" Nothing
254getFTy :: Term -> Maybe FTy
255getFTy (P _ (UN t) _) = case t of
257 "FFloat" -> Just FFloat
258 "FChar" -> Just FChar
259 "FString" -> Just FString
261 "FUnit" -> Just FUnit
265unList :: Term -> Maybe [Term]
266unList tm = case unApply tm of
267 (nil, [_]) -> Just []
268 (cons, ([_, x, xs])) ->
273toConst :: Term -> Maybe Const
274toConst (Constant c) = Just c
277stepForeign :: [Term] -> Idris (Maybe Term)
278stepForeign (ty:fn:args) = do let ffun = foreignFromTT fn
279 f' <- case (call <$> ffun) of
281 Nothing -> return Nothing
283stepForeign _ = fail "Tried to call foreign function that wasn't mkForeign"
285mapMaybeM :: Monad m => (a -> m (Maybe b)) -> [a] -> m [b]
286mapMaybeM f [] = return []
287mapMaybeM f (x:xs) = do rest <- mapMaybeM f xs
290 Just x'' -> return (x'':rest)
291 Nothing -> return rest
293findForeign :: String -> Idris (Maybe ForeignFun)
294findForeign fn = do i <- getIState
295 let libs = idris_dynamic_libs i
296 fns <- mapMaybeM getFn libs
298 [f] -> return (Just f)
299 [] -> do iputStrLn $ "Symbol \"" ++ fn ++ "\" not found"
301 fs -> do iputStrLn $ "Symbol \"" ++ fn ++ "\" is ambiguous. Found " ++
302 show (length fs) ++ " occurrences."
304 where getFn lib = lift $ catchIO (tryLoadFn fn lib) (\_ -> return Nothing)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list