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) = do newF <- doExec env ctxt f
102 laziness <- getLaziness newF
103 newArgs <- mapM (doExec env ctxt) args
104 trace (show newF ++ show (zip args laziness)) $ return ()
105 execApp' env ctxt newF newArgs
106 where getLaziness (P _ (UN "lazy") _) = return [True]
107 getLaziness (P _ n _) = do ist <- getIState
108 let argInfo = idris_implicits ist
109 case lookupCtxtName Nothing n argInfo of
110 [] -> return (repeat False)
111 [ps] -> return $ map lazyarg (snd ps)
112 many -> fail $ "Ambiguous " ++ show n ++ ", found " ++ show many
113 getLaziness x = return (repeat False) -- ok due to zip above
116execApp' :: Env -> Context -> Term -> [Term] -> Idris Term
117execApp' env ctxt v [] = return v -- no args is just a constant! can result from function calls
118execApp' env ctxt (P _ (UN "unsafePerformIO") _) [ty, action] | (prim__IO, [ty', v]) <- unApply action = return v
119execApp' env ctxt (P _ (UN "mkForeign") _) args =
120 do res <- stepForeign args
122 Nothing -> fail "Could not call foreign function"
124execApp' env ctxt (P _ (UN "prim__readString") _) [P _ (UN "prim__stdin") _] =
125 do line <- lift getLine
126 return (Constant (Str line))
128execApp' env ctxt (P _ (UN "prim__concat") _) [(Constant (Str s1)), (Constant (Str s2))] =
129 return $ Constant (Str (s1 ++ s2))
131execApp' env ctxt (P _ (UN "prim__eqInt") _) [(Constant (I i1)), (Constant (I i2))] =
132 return $ if i1 == i2 then Constant (I 1) else Constant (I 0)
134execApp' env ctxt (P _ (UN "prim__readString") _) [ptr] | Just p <- unPtr ptr =
135 do fn <- findForeign "freadStr"
137 Just (Fun _ freadStr) -> do
138 res <- lift $ callFFI freadStr retCString [argPtr p]
139 str <- lift $ peekCString res
140 trace ("Found " ++ str) $ return ()
141 return $ Constant (Str str)
142 Nothing -> fail "Could not load freadStr"
144execApp' env ctxt f@(P _ n _) args =
145 do let val = lookupDef Nothing n ctxt
147 [Function _ tm] -> fail "should already have been eval'd"
148 [TyDecl nt ty] -> return $ mkApp f args
149 [Operator tp arity op] -> fail $ "Can't apply operator " ++ show n ++ " which needs special-casing"
150 [CaseOp _ _ _ _ _ [] (STerm tm) _ _] -> -- nullary fun
151 do rhs <- doExec env ctxt tm
152 doExec env ctxt (mkApp tm args)
153 [CaseOp _ _ _ _ _ ns sc _ _] ->
154 do res <- execCase env ctxt ns sc args
155 return $ fromMaybe (mkApp f args) res
156 thing -> return $ mkApp f args
158execApp' env ctxt bnd@(Bind n b body) (arg:args) = let ctxt' = addToCtxt n arg Erased ctxt in
159 doExec ((n, b):env) ctxt' body
161execApp' env ctxt f args = return (mkApp f args)
163execCase :: Env -> Context -> [Name] -> SC -> [Term] -> Idris (Maybe Term)
164execCase env ctxt ns sc args =
165 let arity = length ns in
166 if arity <= length args
167 then do let amap = (zip ns (take arity args))
168 caseRes <- execCase' env ctxt amap sc
170 Just res-> Just <$> doExec env ctxt (mkApp res (drop arity args))
171 Nothing -> return Nothing
174execCase' :: Env -> Context -> [(Name, Term)] -> SC -> Idris (Maybe Term)
175execCase' env ctxt amap (STerm tm) =
176 Just <$> doExec env (foldl (\c (n, t) -> addToCtxt n t Erased c) ctxt amap) tm
177execCase' env ctxt amap (Case n alts) | Just tm <- lookup n amap
178 , Just (newCase, newBindings) <- chooseAlt tm alts =
179 execCase' env ctxt (amap ++ newBindings) newCase
180 | otherwise = return Nothing
182chooseAlt :: Term -> [CaseAlt] -> Maybe (SC, [(Name, Term)])
183chooseAlt _ (DefaultCase sc : alts) = Just (sc, [])
184chooseAlt (Constant c) (ConstCase c' sc : alts) | c == c' = Just (sc, [])
185chooseAlt tm (ConCase n i ns sc : alts) | ((P _ cn _), args) <- unApply tm, cn == n = Just (sc, zip ns args)
186 | otherwise = chooseAlt tm alts
187chooseAlt tm (_:alts) = chooseAlt tm alts
188chooseAlt _ [] = Nothing
190data FTy = FInt | FFloat | FChar | FString | FPtr | FUnit deriving (Show, Read)
192idrisType :: FTy -> Type
193idrisType FUnit = P Ref unitTy (TType (UVal 0))
194idrisType ft = Constant (idr ft)
195 where idr FInt = IType
198 idr FString = StrType
201data Foreign = FFun String [FTy] FTy deriving Show
203-- | A representation of Ptr values, which otherwise don't work in TT
207-- | Convert a Haskell pointer to a Ptr term in TT
209ptr p = App (P (DCon 1 0) ptrCon Erased) (Constant (I (addr p)))
210 where addr p = p `minusPtr` nullPtr
212-- | Convert a Ptr term in TT to a Haskell pointer
213unPtr :: Term -> Maybe (Ptr a)
214unPtr (App (P _ con _) (Constant (I addr))) | con == ptrCon = Just (unAddr addr)
215 where unAddr a = nullPtr `plusPtr` a
219call :: Foreign -> [Term] -> Idris (Maybe Term)
220call (FFun name argTypes retType) args =
221 do fn <- findForeign name
223 Nothing -> return Nothing
224 Just f -> do res <- call' f args retType
225 return . Just $ mkApp (P Ref (UN "prim__IO") Erased) [idrisType retType, res]
226 where call' :: ForeignFun -> [Term] -> FTy -> Idris Term
227 call' (Fun _ h) args FInt = do res <- lift $ callFFI h retCInt (prepArgs args)
228 return (Constant (I (fromIntegral res)))
229 call' (Fun _ h) args FFloat = do res <- lift $ callFFI h retCDouble (prepArgs args)
230 return (Constant (Fl (realToFrac res)))
231 call' (Fun _ h) args FChar = do res <- lift $ callFFI h retCChar (prepArgs args)
232 return (Constant (Ch (castCCharToChar res)))
233 call' (Fun _ h) args FString = do res <- lift $ callFFI h retCString (prepArgs args)
234 hStr <- lift $ peekCString res
236 return (Constant (Str hStr))
238 call' (Fun _ h) args FPtr = do res <- lift $ callFFI h (retPtr retVoid) (prepArgs args)
240 call' (Fun _ h) args FUnit = do res <- lift $ callFFI h retVoid (prepArgs args)
241 return (P Ref unitCon (P Ref unitTy (TType (UVal 0)))) -- FIXME check universe level
242-- call' (Fun _ h) args other = fail ("Unsupported foreign return type " ++ show other)
245 prepArgs = map prepArg
246 prepArg (Constant (I i)) = argCInt (fromIntegral i)
247 prepArg (Constant (Fl f)) = argCDouble (realToFrac f)
248 prepArg (Constant (Ch c)) = argCChar (castCharToCChar c) -- FIXME - castCharToCChar only safe for first 256 chars
249 prepArg (Constant (Str s)) = argString s
250 prepArg ptr | Just p <- unPtr ptr = argPtr p
251 prepArg other = trace ("Could not use " ++ show other ++ " as FFI arg.") undefined
255foreignFromTT :: Term -> Maybe Foreign
256foreignFromTT t = case (unApply t) of
257 (_, [(Constant (Str name)), args, ret]) ->
258 do argTy <- unList args
259 argFTy <- sequence $ map getFTy argTy
261 return $ FFun name argFTy retFTy
262 _ -> trace "failed to construct ffun" Nothing
264getFTy :: Term -> Maybe FTy
265getFTy (P _ (UN t) _) = case t of
267 "FFloat" -> Just FFloat
268 "FChar" -> Just FChar
269 "FString" -> Just FString
271 "FUnit" -> Just FUnit
275unList :: Term -> Maybe [Term]
276unList tm = case unApply tm of
277 (nil, [_]) -> Just []
278 (cons, ([_, x, xs])) ->
283toConst :: Term -> Maybe Const
284toConst (Constant c) = Just c
287stepForeign :: [Term] -> Idris (Maybe Term)
288stepForeign (ty:fn:args) = do let ffun = foreignFromTT fn
289 f' <- case (call <$> ffun) of
291 Nothing -> return Nothing
293stepForeign _ = fail "Tried to call foreign function that wasn't mkForeign"
295mapMaybeM :: Monad m => (a -> m (Maybe b)) -> [a] -> m [b]
296mapMaybeM f [] = return []
297mapMaybeM f (x:xs) = do rest <- mapMaybeM f xs
300 Just x'' -> return (x'':rest)
301 Nothing -> return rest
303findForeign :: String -> Idris (Maybe ForeignFun)
304findForeign fn = do i <- getIState
305 let libs = idris_dynamic_libs i
306 fns <- mapMaybeM getFn libs
308 [f] -> return (Just f)
309 [] -> do iputStrLn $ "Symbol \"" ++ fn ++ "\" not found"
311 fs -> do iputStrLn $ "Symbol \"" ++ fn ++ "\" is ambiguous. Found " ++
312 show (length fs) ++ " occurrences."
314 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