1{-# LANGUAGE PatternGuards, ExistentialQuantification, CPP #-}
2module Core.Execute (execute) where
5import Idris.AbsSyntaxTree
6import IRTS.Lang(FType(..))
8import Idris.Primitives(Prim(..), primitives)
16import Util.DynamicLinker
19import Control.Applicative hiding (Const)
20import Control.Monad.Trans
21import Control.Monad.Trans.State.Strict
22import Control.Monad.Trans.Error
26import qualified Data.Map as M
30import Foreign.C.String
31import Foreign.Marshal.Alloc (free)
38execute :: Term -> Idris Term
39execute tm = fail "libffi not supported, rebuild Idris with -f FFI"
41-- else is rest of file
42readMay :: (Read a) => String -> Maybe a
43readMay s = case reads s of
47data Lazy = Delayed ExecEnv Context Term | Forced ExecVal deriving Show
49data ExecState = ExecState { exec_thunks :: M.Map Int Lazy -- ^ Thunks - the result of evaluating "lazy" or calling lazy funcs
50 , exec_next_thunk :: Int -- ^ Ensure thunk key uniqueness
51 , exec_implicits :: Ctxt [PArg] -- ^ Necessary info on laziness from idris monad
52 , exec_dynamic_libs :: [DynamicLib] -- ^ Dynamic libs from idris monad
55data ExecVal = EP NameType Name ExecVal
57 | EBind Name (Binder ExecVal) (ExecVal -> Exec ExecVal)
58 | EApp ExecVal ExecVal
62 | forall a. EPtr (Ptr a)
66instance Show ExecVal where
67 show (EP _ n _) = show n
68 show (EV i) = "!!V" ++ show i ++ "!!"
69 show (EBind n b body) = "EBind " ++ show b ++ " <<fn>>"
70 show (EApp e1 e2) = show e1 ++ " (" ++ show e2 ++ ")"
71 show (EType _) = "Type"
73 show (EConstant c) = show c
74 show (EPtr p) = "<<ptr " ++ show p ++ ">>"
75 show (EThunk i) = "<<thunk " ++ show i ++ ">>"
76 show (EHandle h) = "<<handle " ++ show h ++ ">>"
78toTT :: ExecVal -> Exec Term
79toTT (EP nt n ty) = (P nt n) <$> (toTT ty)
80toTT (EV i) = return $ V i
81toTT (EBind n b body) = do body' <- body $ EP Bound n EErased
83 Bind n b' <$> toTT body'
84 where fixBinder (Lam t) = Lam <$> toTT t
85 fixBinder (Pi t) = Pi <$> toTT t
86 fixBinder (Let t1 t2) = Let <$> toTT t1 <*> toTT t2
87 fixBinder (NLet t1 t2) = NLet <$> toTT t1 <*> toTT t2
88 fixBinder (Hole t) = Hole <$> toTT t
89 fixBinder (GHole i t) = GHole i <$> toTT t
90 fixBinder (Guess t1 t2) = Guess <$> toTT t1 <*> toTT t2
91 fixBinder (PVar t) = PVar <$> toTT t
92 fixBinder (PVTy t) = PVTy <$> toTT t
93toTT (EApp e1 e2) = do e1' <- toTT e1
96toTT (EType u) = return $ TType u
97toTT EErased = return Erased
98toTT (EConstant c) = return (Constant c)
99toTT (EThunk i) = return (P (DCon 0 0) (MN i "THUNK") Erased) --(force i) >>= toTT
100toTT (EHandle _) = return Erased
102unApplyV :: ExecVal -> (ExecVal, [ExecVal])
103unApplyV tm = ua [] tm
104 where ua args (EApp f a) = ua (a:args) f
105 ua args t = (t, args)
107mkEApp :: ExecVal -> [ExecVal] -> ExecVal
109mkEApp f (a:args) = mkEApp (EApp f a) args
111initState :: Idris ExecState
112initState = do ist <- getIState
113 return $ ExecState M.empty 0 (idris_implicits ist) (idris_dynamic_libs ist)
115type Exec = ErrorT String (StateT ExecState IO)
117runExec :: Exec a -> ExecState -> IO (Either String a)
118runExec ex st = fst <$> runStateT (runErrorT ex) st
120getExecState :: Exec ExecState
121getExecState = lift get
123putExecState :: ExecState -> Exec ()
124putExecState = lift . put
126execFail :: String -> Exec a
129execIO :: IO a -> Exec a
132delay :: ExecEnv -> Context -> Term -> Exec ExecVal
134 do st <- getExecState
135 let i = exec_next_thunk st
136 putExecState $ st { exec_thunks = M.insert i (Delayed env ctxt tm) (exec_thunks st)
137 , exec_next_thunk = exec_next_thunk st + 1
141force :: Int -> Exec ExecVal
142force i = do st <- getExecState
143 case M.lookup i (exec_thunks st) of
144 Just (Delayed env ctxt tm) -> do tm' <- doExec env ctxt tm
152 Just (Forced tm) -> return tm
153 Nothing -> execFail "Tried to exec non-existing thunk. This is a bug!"
154 where update :: ExecVal -> Int -> Exec ()
155 update tm i = do est <- getExecState
156 putExecState $ est { exec_thunks = M.insert i (Forced tm) (exec_thunks est) }
158tryForce :: ExecVal -> Exec ExecVal
159tryForce (EThunk i) = force i
160tryForce tm = return tm
162debugThunks :: Exec ()
163debugThunks = do st <- getExecState
164 execIO $ putStrLn (take 4000 (show (exec_thunks st)))
166execute :: Term -> Idris Term
167execute tm = do est <- initState
169 res <- lift . lift $ flip runExec est $ do res <- doExec [] ctxt tm
173 Right tm' -> return tm'
175ioWrap :: ExecVal -> ExecVal
176ioWrap tm = mkEApp (EP (DCon 0 2) (UN "prim__IO") EErased) [EErased, tm]
179ioUnit = ioWrap (EP Ref unitCon EErased)
181type ExecEnv = [(Name, ExecVal)]
183doExec :: ExecEnv -> Context -> Term -> Exec ExecVal
184doExec env ctxt p@(P Ref n ty) | Just v <- lookup n env = return v
185doExec env ctxt p@(P Ref n ty) =
186 do let val = lookupDef n ctxt
188 [Function _ tm] -> doExec env ctxt tm
189 [TyDecl _ _] -> return (EP Ref n EErased) -- abstract def
190 [Operator tp arity op] -> return (EP Ref n EErased) -- will be special-cased later
191 [CaseOp _ _ _ _ (CaseDefs _ ([], STerm tm) _ _)] -> -- nullary fun
193 [CaseOp _ _ _ _ (CaseDefs _ (ns, sc) _ _)] -> return (EP Ref n EErased)
194 [] -> execFail $ "Could not find " ++ show n ++ " in definitions."
195 thing -> trace (take 200 $ "got to " ++ show thing ++ " lookup up " ++ show n) $ undefined
196doExec env ctxt p@(P Bound n ty) =
198 Nothing -> execFail "not found"
200doExec env ctxt (P (DCon a b) n _) = return (EP (DCon a b) n EErased)
201doExec env ctxt (P (TCon a b) n _) = return (EP (TCon a b) n EErased)
202doExec env ctxt v@(V i) | i < length env = return (snd (env !! i))
203 | otherwise = execFail "env too small"
204doExec env ctxt (Bind n (Let t v) body) = do v' <- doExec env ctxt v
205 doExec ((n, v'):env) ctxt body
206doExec env ctxt (Bind n (NLet t v) body) = trace "NLet" $ undefined
207doExec env ctxt tm@(Bind n b body) = return $
208 EBind n (fmap (\_->EErased) b)
209 (\arg -> doExec ((n, arg):env) ctxt body)
210doExec env ctxt a@(App _ _) = execApp env ctxt (unApply a)
211doExec env ctxt (Constant c) = return (EConstant c)
212doExec env ctxt (Proj tm i) = let (x, xs) = unApply tm in
213 doExec env ctxt ((x:xs) !! i)
214doExec env ctxt Erased = return EErased
215doExec env ctxt Impossible = fail "Tried to execute an impossible case"
216doExec env ctxt (TType u) = return (EType u)
218execApp :: ExecEnv -> Context -> (Term, [Term]) -> Exec ExecVal
219execApp env ctxt (f, args) = do newF <- doExec env ctxt f
220 laziness <- (++ repeat False) <$> (getLaziness newF)
221 newArgs <- mapM argExec (zip args laziness)
222 execApp' env ctxt newF newArgs
223 where getLaziness (EP _ (UN "lazy") _) = return [False, True]
224 getLaziness (EP _ n _) = do est <- getExecState
225 let argInfo = exec_implicits est
226 case lookupCtxtName n argInfo of
227 [] -> return (repeat False)
228 [ps] -> return $ map lazyarg (snd ps)
229 many -> execFail $ "Ambiguous " ++ show n ++ ", found " ++ (take 200 $ show many)
230 getLaziness _ = return (repeat False) -- ok due to zip above
231 argExec :: (Term, Bool) -> Exec ExecVal
232 argExec (tm, False) = doExec env ctxt tm
233 argExec (tm, True) = delay env ctxt tm
236execApp' :: ExecEnv -> Context -> ExecVal -> [ExecVal] -> Exec ExecVal
237execApp' env ctxt v [] = return v -- no args is just a constant! can result from function calls
238execApp' env ctxt (EP _ (UN "unsafePerformPrimIO") _) (ty:action:rest) | (prim__IO, [_, v]) <- unApplyV action =
239 execApp' env ctxt v rest
241execApp' env ctxt (EP _ (UN "prim_io_bind") _) args@(_:_:v:k:rest) | (prim__IO, [_, v']) <- unApplyV v =
242 do v'' <- tryForce v'
243 res <- execApp' env ctxt k [v''] >>= tryForce
244 execApp' env ctxt res rest
245execApp' env ctxt con@(EP _ (UN "prim_io_return") _) args@(tp:v:rest) =
247 execApp' env ctxt (mkEApp con [tp, v']) rest
249-- Special cases arising from not having access to the C RTS in the interpreter
250execApp' env ctxt (EP _ (UN "mkForeignPrim") _) (_:fn:EConstant (Str arg):_:rest)
251 | Just (FFun "putStr" _ _) <- foreignFromTT fn
252 = do execIO (putStr arg)
253 execApp' env ctxt ioUnit rest
254execApp' env ctxt (EP _ (UN "mkForeignPrim") _) (_:fn:_:EHandle h:_:rest)
255 | Just (FFun "idris_readStr" _ _) <- foreignFromTT fn
256 = do contents <- execIO $ hGetLine h
257 execApp' env ctxt (EConstant (Str (contents ++ "\n"))) rest
258execApp' env ctxt (EP _ (UN "mkForeignPrim") _) (_:fn:EConstant (Str f):EConstant (Str mode):rest)
259 | Just (FFun "fileOpen" _ _) <- foreignFromTT fn
260 = do m <- case mode of
261 "r" -> return ReadMode
262 "w" -> return WriteMode
263 "a" -> return AppendMode
264 "rw" -> return ReadWriteMode
265 "wr" -> return ReadWriteMode
266 "r+" -> return ReadWriteMode
267 _ -> execFail ("Invalid mode for " ++ f ++ ": " ++ mode)
268 h <- execIO $ openFile f m
269 execApp' env ctxt (ioWrap (EHandle h)) (tail rest)
271execApp' env ctxt (EP _ (UN "mkForeignPrim") _) (_:fn:(EHandle h):rest)
272 | Just (FFun "fileEOF" _ _) <- foreignFromTT fn
273 = do eofp <- execIO $ hIsEOF h
274 let res = ioWrap (EConstant (I $ if eofp then 1 else 0))
275 execApp' env ctxt res (tail rest)
277execApp' env ctxt (EP _ (UN "mkForeignPrim") _) (_:fn:(EHandle h):rest)
278 | Just (FFun "fileClose" _ _) <- foreignFromTT fn
279 = do execIO $ hClose h
280 execApp' env ctxt ioUnit (tail rest)
282execApp' env ctxt (EP _ (UN "mkForeignPrim") _) (_:fn:(EPtr p):rest)
283 | Just (FFun "isNull" _ _) <- foreignFromTT fn
284 = let res = ioWrap . EConstant . I $
285 if p == nullPtr then 1 else 0
286 in execApp' env ctxt res (tail rest)
288-- Throw away the 'World' argument to the foreign function
290execApp' env ctxt f@(EP _ (UN "mkForeignPrim") _) args@(ty:fn:xs)
291 | Just (FFun f argTs retT) <- foreignFromTT fn
292 , length xs >= length argTs =
293 do let (args', xs') = (take (length argTs) xs, -- foreign args
294 drop (length argTs + 1) xs) -- rest
295 res <- stepForeign (ty:fn:args')
297 Nothing -> fail $ "Could not call foreign function \"" ++ f ++
298 "\" with args " ++ show args
299 Just r -> return (mkEApp r xs')
300 | otherwise = return (mkEApp f args)
302execApp' env ctxt c@(EP (DCon _ arity) n _) args =
303 do args' <- mapM tryForce (take arity args)
304 let restArgs = drop arity args
305 execApp' env ctxt (mkEApp c args') restArgs
307execApp' env ctxt c@(EP (TCon _ arity) n _) args =
308 do args' <- mapM tryForce (take arity args)
309 let restArgs = drop arity args
310 execApp' env ctxt (mkEApp c args') restArgs
312execApp' env ctxt f@(EP _ n _) args =
313 do let val = lookupDef n ctxt
315 [Function _ tm] -> fail "should already have been eval'd"
316 [TyDecl nt ty] -> return $ mkEApp f args
317 [Operator tp arity op] ->
318 if length args >= arity
319 then do args' <- mapM tryForce $ take arity args
320 case getOp n args' of
321 Just res -> do r <- res
322 execApp' env ctxt r (drop arity args)
323 Nothing -> return (mkEApp f args)
324 else return (mkEApp f args)
325 [CaseOp _ _ _ _ (CaseDefs _ ([], STerm tm) _ _)] -> -- nullary fun
326 do rhs <- doExec env ctxt tm
327 execApp' env ctxt rhs args
328 [CaseOp _ _ _ _ (CaseDefs _ (ns, sc) _ _)] ->
329 do res <- execCase env ctxt ns sc args
330 return $ fromMaybe (mkEApp f args) res
331 thing -> return $ mkEApp f args
332execApp' env ctxt bnd@(EBind n b body) (arg:args) = do ret <- body arg
333 let (f', as) = unApplyV ret
334 execApp' env ctxt f' (as ++ args)
335execApp' env ctxt (EThunk i) args = do f <- force i
336 execApp' env ctxt f args
337execApp' env ctxt app@(EApp _ _) args2 | (f, args1) <- unApplyV app = execApp' env ctxt f (args1 ++ args2)
338execApp' env ctxt f args = return (mkEApp f args)
340-- | Look up primitive operations in the global table and transform them into ExecVal functions
341getOp :: Name -> [ExecVal] -> Maybe (Exec ExecVal)
342getOp (UN "prim__believe_me") [_, _, x] = Just (return x)
343getOp (UN "prim__readString") [EP _ (UN "prim__stdin") _] =
344 Just $ do line <- execIO getLine
345 return (EConstant (Str line))
346getOp (UN "prim__readString") [EHandle h] =
347 Just $ do contents <- execIO $ hGetLine h
348 return (EConstant (Str (contents ++ "\n")))
349getOp n args = getPrim n primitives >>= flip applyPrim args
350 where getPrim :: Name -> [Prim] -> Maybe ([ExecVal] -> Maybe ExecVal)
351 getPrim n [] = Nothing
352 getPrim n ((Prim pn _ arity def _ _) : prims) | n == pn = Just $ execPrim def
353 | otherwise = getPrim n prims
355 execPrim :: ([Const] -> Maybe Const) -> [ExecVal] -> Maybe ExecVal
356 execPrim f args = EConstant <$> (mapM getConst args >>= f)
358 getConst (EConstant c) = Just c
361 applyPrim :: ([ExecVal] -> Maybe ExecVal) -> [ExecVal] -> Maybe (Exec ExecVal)
362 applyPrim fn args = return <$> fn args
367-- | Overall wrapper for case tree execution. If there are enough arguments, it takes them,
368-- evaluates them, then begins the checks for matching cases.
369execCase :: ExecEnv -> Context -> [Name] -> SC -> [ExecVal] -> Exec (Maybe ExecVal)
370execCase env ctxt ns sc args =
371 let arity = length ns in
372 if arity <= length args
373 then do let amap = zip ns args
374-- trace ("Case " ++ show sc ++ "\n in " ++ show amap ++"\n in env " ++ show env ++ "\n\n" ) $ return ()
375 caseRes <- execCase' env ctxt amap sc
377 Just res -> Just <$> execApp' (map (\(n, tm) -> (n, tm)) amap ++ env) ctxt res (drop arity args)
378 Nothing -> return Nothing
381-- | Take bindings and a case tree and examines them, executing the matching case if possible.
382execCase' :: ExecEnv -> Context -> [(Name, ExecVal)] -> SC -> Exec (Maybe ExecVal)
383execCase' env ctxt amap (UnmatchedCase _) = return Nothing
384execCase' env ctxt amap (STerm tm) =
385 Just <$> doExec (map (\(n, v) -> (n, v)) amap ++ env) ctxt tm
386execCase' env ctxt amap (Case n alts) | Just tm <- lookup n amap =
387 do tm' <- tryForce tm
388 case chooseAlt tm' alts of
389 Just (newCase, newBindings) ->
390 let amap' = newBindings ++ (filter (\(x,_) -> not (elem x (map fst newBindings))) amap) in
391 execCase' env ctxt amap' newCase
392 Nothing -> return Nothing
394chooseAlt :: ExecVal -> [CaseAlt] -> Maybe (SC, [(Name, ExecVal)])
395chooseAlt _ (DefaultCase sc : alts) = Just (sc, [])
396chooseAlt (EConstant c) (ConstCase c' sc : alts) | c == c' = Just (sc, [])
397chooseAlt tm (ConCase n i ns sc : alts) | ((EP _ cn _), args) <- unApplyV tm
398 , cn == n = Just (sc, zip ns args)
399 | otherwise = chooseAlt tm alts
400chooseAlt tm (_:alts) = chooseAlt tm alts
401chooseAlt _ [] = Nothing
406idrisType :: FType -> ExecVal
407idrisType FUnit = EP Ref unitTy EErased
408idrisType ft = EConstant (idr ft)
409 where idr (FArith ty) = AType ty
410 idr FString = StrType
413data Foreign = FFun String [FType] FType deriving Show
416call :: Foreign -> [ExecVal] -> Exec (Maybe ExecVal)
417call (FFun name argTypes retType) args =
418 do fn <- findForeign name
419 maybe (return Nothing) (\f -> Just . ioWrap <$> call' f args retType) fn
420 where call' :: ForeignFun -> [ExecVal] -> FType -> Exec ExecVal
421 call' (Fun _ h) args (FArith (ATInt ITNative)) = do
422 res <- execIO $ callFFI h retCInt (prepArgs args)
423 return (EConstant (I (fromIntegral res)))
424 call' (Fun _ h) args (FArith (ATInt (ITFixed IT8))) = do
425 res <- execIO $ callFFI h retCChar (prepArgs args)
426 return (EConstant (B8 (fromIntegral res)))
427 call' (Fun _ h) args (FArith (ATInt (ITFixed IT16))) = do
428 res <- execIO $ callFFI h retCWchar (prepArgs args)
429 return (EConstant (B16 (fromIntegral res)))
430 call' (Fun _ h) args (FArith (ATInt (ITFixed IT32))) = do
431 res <- execIO $ callFFI h retCInt (prepArgs args)
432 return (EConstant (B32 (fromIntegral res)))
433 call' (Fun _ h) args (FArith (ATInt (ITFixed IT64))) = do
434 res <- execIO $ callFFI h retCLong (prepArgs args)
435 return (EConstant (B64 (fromIntegral res)))
436 call' (Fun _ h) args (FArith ATFloat) = do
437 res <- execIO $ callFFI h retCDouble (prepArgs args)
438 return (EConstant (Fl (realToFrac res)))
439 call' (Fun _ h) args (FArith (ATInt ITChar)) = do
440 res <- execIO $ callFFI h retCChar (prepArgs args)
441 return (EConstant (Ch (castCCharToChar res)))
442 call' (Fun _ h) args FString = do res <- execIO $ callFFI h retCString (prepArgs args)
443 hStr <- execIO $ peekCString res
445 return (EConstant (Str hStr))
447 call' (Fun _ h) args FPtr = EPtr <$> (execIO $ callFFI h (retPtr retVoid) (prepArgs args))
448 call' (Fun _ h) args FUnit = do _ <- execIO $ callFFI h retVoid (prepArgs args)
449 return $ EP Ref unitCon EErased
450-- call' (Fun _ h) args other = fail ("Unsupported foreign return type " ++ show other)
453 prepArgs = map prepArg
454 prepArg (EConstant (I i)) = argCInt (fromIntegral i)
455 prepArg (EConstant (B8 i)) = argCChar (fromIntegral i)
456 prepArg (EConstant (B16 i)) = argCWchar (fromIntegral i)
457 prepArg (EConstant (B32 i)) = argCInt (fromIntegral i)
458 prepArg (EConstant (B64 i)) = argCLong (fromIntegral i)
459 prepArg (EConstant (Fl f)) = argCDouble (realToFrac f)
460 prepArg (EConstant (Ch c)) = argCChar (castCharToCChar c) -- FIXME - castCharToCChar only safe for first 256 chars
461 prepArg (EConstant (Str s)) = argString s
462 prepArg (EPtr p) = argPtr p
463 prepArg other = trace ("Could not use " ++ take 100 (show other) ++ " as FFI arg.") undefined
467foreignFromTT :: ExecVal -> Maybe Foreign
468foreignFromTT t = case (unApplyV t) of
469 (_, [(EConstant (Str name)), args, ret]) ->
470 do argTy <- unEList args
471 argFTy <- sequence $ map getFTy argTy
473 return $ FFun name argFTy retFTy
474 _ -> trace "failed to construct ffun" Nothing
476getFTy :: ExecVal -> Maybe FType
477getFTy (EApp (EP _ (UN "FIntT") _) (EP _ (UN intTy) _)) =
479 "ITNative" -> Just (FArith (ATInt ITNative))
480 "ITChar" -> Just (FArith (ATInt ITChar))
481 "IT8" -> Just (FArith (ATInt (ITFixed IT8)))
482 "IT16" -> Just (FArith (ATInt (ITFixed IT16)))
483 "IT32" -> Just (FArith (ATInt (ITFixed IT32)))
484 "IT64" -> Just (FArith (ATInt (ITFixed IT64)))
486getFTy (EP _ (UN t) _) =
488 "FFloat" -> Just (FArith ATFloat)
489 "FString" -> Just FString
491 "FUnit" -> Just FUnit
495unList :: Term -> Maybe [Term]
496unList tm = case unApply tm of
497 (nil, [_]) -> Just []
498 (cons, ([_, x, xs])) ->
503unEList :: ExecVal -> Maybe [ExecVal]
504unEList tm = case unApplyV tm of
505 (nil, [_]) -> Just []
506 (cons, ([_, x, xs])) ->
507 do rest <- unEList xs
512toConst :: Term -> Maybe Const
513toConst (Constant c) = Just c
516stepForeign :: [ExecVal] -> Exec (Maybe ExecVal)
517stepForeign (ty:fn:args) = let ffun = foreignFromTT fn
518 in case (call <$> ffun) of
520 Nothing -> return Nothing
521stepForeign _ = fail "Tried to call foreign function that wasn't mkForeignPrim"
523mapMaybeM :: (Functor m, Monad m) => (a -> m (Maybe b)) -> [a] -> m [b]
524mapMaybeM f [] = return []
525mapMaybeM f (x:xs) = do rest <- mapMaybeM f xs
526 maybe rest (:rest) <$> f x
527findForeign :: String -> Exec (Maybe ForeignFun)
528findForeign fn = do est <- getExecState
529 let libs = exec_dynamic_libs est
530 fns <- mapMaybeM getFn libs
532 [f] -> return (Just f)
533 [] -> do execIO . putStrLn $ "Symbol \"" ++ fn ++ "\" not found"
535 fs -> do execIO . putStrLn $ "Symbol \"" ++ fn ++ "\" is ambiguous. Found " ++
536 show (length fs) ++ " occurrences."
538 where getFn lib = execIO $ 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