1{-# LANGUAGE PatternGuards, ExistentialQuantification, CPP #-}
2module Idris.Core.Execute (execute) where
5import Idris.AbsSyntaxTree
6import IRTS.Lang(FType(..))
8import Idris.Primitives(Prim(..), primitives)
11import Idris.Core.Evaluate
12import Idris.Core.CaseTree
18import Util.DynamicLinker
21import Control.Applicative hiding (Const)
22import Control.Exception
23import Control.Monad.Trans
24import Control.Monad.Trans.State.Strict
25import Control.Monad.Trans.Error
29import qualified Data.Map as M
33import Foreign.C.String
34import Foreign.Marshal.Alloc (free)
41execute :: Term -> Idris Term
42execute tm = fail "libffi not supported, rebuild Idris with -f FFI"
44-- else is rest of file
45readMay :: (Read a) => String -> Maybe a
46readMay s = case reads s of
50data Lazy = Delayed ExecEnv Context Term | Forced ExecVal deriving Show
52newtype ExecState = ExecState { 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)
63 | EThunk Context ExecEnv Term
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 _ env tm) = "<<thunk " ++ show env ++ "||||" ++ show tm ++ ">>"
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 ctxt env tm) = do env' <- mapM toBinder env
100 return $ normalise ctxt env' tm
101 where toBinder (n, v) = do v' <- toTT v
102 return (n, Let Erased v')
103toTT (EHandle _) = return Erased
105unApplyV :: ExecVal -> (ExecVal, [ExecVal])
106unApplyV tm = ua [] tm
107 where ua args (EApp f a) = ua (a:args) f
108 ua args t = (t, args)
110mkEApp :: ExecVal -> [ExecVal] -> ExecVal
112mkEApp f (a:args) = mkEApp (EApp f a) args
114initState :: Idris ExecState
115initState = do ist <- getIState
116 return $ ExecState (idris_dynamic_libs ist)
118type Exec = ErrorT Err (StateT ExecState IO)
120runExec :: Exec a -> ExecState -> IO (Either Err a)
121runExec ex st = fst <$> runStateT (runErrorT ex) st
123getExecState :: Exec ExecState
124getExecState = lift get
126putExecState :: ExecState -> Exec ()
127putExecState = lift . put
129execFail :: Err -> Exec a
132execIO :: IO a -> Exec a
136execute :: Term -> Idris Term
137execute tm = do est <- initState
139 res <- lift . lift . flip runExec est $
140 do res <- doExec [] ctxt tm
143 Left err -> ierror err
144 Right tm' -> return tm'
146ioWrap :: ExecVal -> ExecVal
147ioWrap tm = mkEApp (EP (DCon 0 2) (sUN "prim__IO") EErased) [EErased, tm]
150ioUnit = ioWrap (EP Ref unitCon EErased)
152type ExecEnv = [(Name, ExecVal)]
154doExec :: ExecEnv -> Context -> Term -> Exec ExecVal
155doExec env ctxt p@(P Ref n ty) | Just v <- lookup n env = return v
156doExec env ctxt p@(P Ref n ty) =
157 do let val = lookupDef n ctxt
159 [Function _ tm] -> doExec env ctxt tm
160 [TyDecl _ _] -> return (EP Ref n EErased) -- abstract def
161 [Operator tp arity op] -> return (EP Ref n EErased) -- will be special-cased later
162 [CaseOp _ _ _ _ _ (CaseDefs _ ([], STerm tm) _ _)] -> -- nullary fun
164 [CaseOp _ _ _ _ _ (CaseDefs _ (ns, sc) _ _)] -> return (EP Ref n EErased)
165 [] -> execFail . Msg $ "Could not find " ++ show n ++ " in definitions."
166 thing -> trace (take 200 $ "got to " ++ show thing ++ " lookup up " ++ show n) $ undefined
167doExec env ctxt p@(P Bound n ty) =
169 Nothing -> execFail . Msg $ "not found"
171doExec env ctxt (P (DCon a b) n _) = return (EP (DCon a b) n EErased)
172doExec env ctxt (P (TCon a b) n _) = return (EP (TCon a b) n EErased)
173doExec env ctxt v@(V i) | i < length env = return (snd (env !! i))
174 | otherwise = execFail . Msg $ "env too small"
175doExec env ctxt (Bind n (Let t v) body) = do v' <- doExec env ctxt v
176 doExec ((n, v'):env) ctxt body
177doExec env ctxt (Bind n (NLet t v) body) = trace "NLet" $ undefined
178doExec env ctxt tm@(Bind n b body) = return $
179 EBind n (fmap (\_->EErased) b)
180 (\arg -> doExec ((n, arg):env) ctxt body)
181doExec env ctxt a@(App _ _) =
182 do let (f, args) = unApply a
183 f' <- doExec env ctxt f
185 (EP _ d _) | d == delay ->
187 [t,a,tm] -> do t' <- doExec env ctxt t
188 a' <- doExec env ctxt a
189 return [t', a', EThunk ctxt env tm]
190 _ -> mapM (doExec env ctxt) args -- partial applications are fine to evaluate
191 fun' -> do mapM (doExec env ctxt) args
192 execApp env ctxt f' args'
193doExec env ctxt (Constant c) = return (EConstant c)
194doExec env ctxt (Proj tm i) = let (x, xs) = unApply tm in
195 doExec env ctxt ((x:xs) !! i)
196doExec env ctxt Erased = return EErased
197doExec env ctxt Impossible = fail "Tried to execute an impossible case"
198doExec env ctxt (TType u) = return (EType u)
200execApp :: ExecEnv -> Context -> ExecVal -> [ExecVal] -> Exec ExecVal
201execApp env ctxt v [] = return v -- no args is just a constant! can result from function calls
202execApp env ctxt (EP _ f _) (t:a:delayed:rest)
204 , (_, [_, _, EThunk tmCtxt tmEnv tm]) <- unApplyV delayed =
205 do tm' <- doExec tmEnv tmCtxt tm
206 execApp env ctxt tm' rest
207execApp env ctxt (EP _ fp _) (ty:action:rest)
209 (prim__IO, [_, v]) <- unApplyV action
210 = execApp env ctxt v rest
212execApp env ctxt (EP _ fp _) args@(_:_:v:k:rest)
214 (prim__IO, [_, v']) <- unApplyV v =
215 do res <- execApp env ctxt k [v']
216 execApp env ctxt res rest
217execApp env ctxt con@(EP _ fp _) args@(tp:v:rest)
218 | fp == pioret = execApp env ctxt (mkEApp con [tp, v]) rest
220-- Special cases arising from not having access to the C RTS in the interpreter
221execApp env ctxt (EP _ fp _) (_:fn:str:_:rest)
223 Just (FFun "putStr" _ _) <- foreignFromTT fn
225 EConstant (Str arg) -> do execIO (putStr arg)
226 execApp env ctxt ioUnit rest
227 _ -> execFail . Msg $
228 "The argument to putStr should be a constant string, but it was " ++
230 ". Are all cases covered?"
231execApp env ctxt (EP _ fp _) (_:fn:_:handle:_:rest)
233 Just (FFun "idris_readStr" _ _) <- foreignFromTT fn
235 EHandle h -> do contents <- execIO $ hGetLine h
236 execApp env ctxt (EConstant (Str (contents ++ "\n"))) rest
237 _ -> execFail . Msg $
238 "The argument to idris_readStr should be a handle, but it was " ++
240 ". Are all cases covered?"
241execApp env ctxt (EP _ fp _) (_:fn:fileStr:modeStr:rest)
243 Just (FFun "fileOpen" _ _) <- foreignFromTT fn
244 = case (fileStr, modeStr) of
245 (EConstant (Str f), EConstant (Str mode)) ->
247 catch (do let m = case mode of
248 "r" -> Right ReadMode
249 "w" -> Right WriteMode
250 "a" -> Right AppendMode
251 "rw" -> Right ReadWriteMode
252 "wr" -> Right ReadWriteMode
253 "r+" -> Right ReadWriteMode
254 _ -> Left ("Invalid mode for " ++ f ++ ": " ++ mode)
255 case fmap (openFile f) m of
256 Right h -> do h' <- h; return $ Right (ioWrap (EHandle h'), tail rest)
257 Left err -> return $ Left err)
258 (\e -> let _ = ( e::SomeException)
259 in return $ Right (ioWrap (EPtr nullPtr), tail rest))
261 Left err -> execFail . Msg $ err
262 Right (res, rest) -> execApp env ctxt res rest
263 _ -> execFail . Msg $
264 "The arguments to fileOpen should be constant strings, but they were " ++
265 show fileStr ++ " and " ++ show modeStr ++
266 ". Are all cases covered?"
268execApp env ctxt (EP _ fp _) (_:fn:handle:rest)
270 Just (FFun "fileEOF" _ _) <- foreignFromTT fn
272 EHandle h -> do eofp <- execIO $ hIsEOF h
273 let res = ioWrap (EConstant (I $ if eofp then 1 else 0))
274 execApp env ctxt res (tail rest)
275 _ -> execFail . Msg $
276 "The argument to fileEOF should be a file handle, but it was " ++
278 ". Are all cases covered?"
280execApp env ctxt (EP _ fp _) (_:fn:handle:rest)
282 Just (FFun "fileClose" _ _) <- foreignFromTT fn
284 EHandle h -> do execIO $ hClose h
285 execApp env ctxt ioUnit (tail rest)
286 _ -> execFail . Msg $
287 "The argument to fileClose should be a file handle, but it was " ++
289 ". Are all cases covered?"
291execApp env ctxt (EP _ fp _) (_:fn:ptr:rest)
293 Just (FFun "isNull" _ _) <- foreignFromTT fn
295 EPtr p -> let res = ioWrap . EConstant . I $
296 if p == nullPtr then 1 else 0
297 in execApp env ctxt res (tail rest)
298 -- Handles will be checked as null pointers sometimes - but if we got a
299 -- real Handle, then it's valid, so just return 1.
300 EHandle h -> let res = ioWrap . EConstant . I $ 0
301 in execApp env ctxt res (tail rest)
302 -- A foreign-returned char* has to be tested for NULL sometimes
303 EConstant (Str s) -> let res = ioWrap . EConstant . I $ 0
304 in execApp env ctxt res (tail rest)
305 _ -> execFail . Msg $
306 "The argument to isNull should be a pointer or file handle or string, but it was " ++
308 ". Are all cases covered?"
311-- Right now, there's no way to send command-line arguments to the executor,
313execApp env ctxt (EP _ fp _) (_:fn:rest)
315 Just (FFun "idris_numArgs" _ _) <- foreignFromTT fn
316 = let res = ioWrap . EConstant . I $ 0
317 in execApp env ctxt res (tail rest)
319execApp env ctxt f@(EP _ fp _) args@(ty:fn:xs) | fp == mkfprim
320 = case foreignFromTT fn of
321 Just (FFun f argTs retT) | length xs >= length argTs ->
322 do let (args', xs') = (take (length argTs) xs, -- foreign args
323 drop (length argTs + 1) xs) -- rest
324 res <- stepForeign (ty:fn:args')
326 Nothing -> fail $ "Could not call foreign function \"" ++ f ++
327 "\" with args " ++ show args
328 Just r -> return (mkEApp r xs')
329 Nothing -> return (mkEApp f args)
331execApp env ctxt c@(EP (DCon _ arity) n _) args =
332 do let args' = take arity args
333 let restArgs = drop arity args
334 execApp env ctxt (mkEApp c args') restArgs
336execApp env ctxt c@(EP (TCon _ arity) n _) args =
337 do let args' = take arity args
338 let restArgs = drop arity args
339 execApp env ctxt (mkEApp c args') restArgs
341execApp env ctxt f@(EP _ n _) args =
342 do let val = lookupDef n ctxt
344 [Function _ tm] -> fail "should already have been eval'd"
345 [TyDecl nt ty] -> return $ mkEApp f args
346 [Operator tp arity op] ->
347 if length args >= arity
348 then let args' = take arity args in
349 case getOp n args' of
350 Just res -> do r <- res
351 execApp env ctxt r (drop arity args)
352 Nothing -> return (mkEApp f args)
353 else return (mkEApp f args)
354 [CaseOp _ _ _ _ _ (CaseDefs _ ([], STerm tm) _ _)] -> -- nullary fun
355 do rhs <- doExec env ctxt tm
356 execApp env ctxt rhs args
357 [CaseOp _ _ _ _ _ (CaseDefs _ (ns, sc) _ _)] ->
358 do res <- execCase env ctxt ns sc args
359 return $ fromMaybe (mkEApp f args) res
360 thing -> return $ mkEApp f args
361execApp env ctxt bnd@(EBind n b body) (arg:args) = do ret <- body arg
362 let (f', as) = unApplyV ret
363 execApp env ctxt f' (as ++ args)
364execApp env ctxt app@(EApp _ _) args2 | (f, args1) <- unApplyV app = execApp env ctxt f (args1 ++ args2)
365execApp env ctxt f args = return (mkEApp f args)
367prs = sUN "prim__readString"
368pbm = sUN "prim__believe_me"
369pstd = sUN "prim__stdin"
370mkfprim = sUN "mkForeignPrim"
371pioret = sUN "prim_io_return"
372piobind = sUN "prim_io_bind"
373upio = sUN "unsafePerformPrimIO"
377-- | Look up primitive operations in the global table and transform them into ExecVal functions
378getOp :: Name -> [ExecVal] -> Maybe (Exec ExecVal)
379getOp fn [_, _, x] | fn == pbm = Just (return x)
381 | fn == prs && fn' == pstd =
382 Just $ do line <- execIO getLine
383 return (EConstant (Str line))
386 Just $ do contents <- execIO $ hGetLine h
387 return (EConstant (Str (contents ++ "\n")))
388getOp n args = getPrim n primitives >>= flip applyPrim args
389 where getPrim :: Name -> [Prim] -> Maybe ([ExecVal] -> Maybe ExecVal)
390 getPrim n [] = Nothing
391 getPrim n ((Prim pn _ arity def _ _) : prims) | n == pn = Just $ execPrim def
392 | otherwise = getPrim n prims
394 execPrim :: ([Const] -> Maybe Const) -> [ExecVal] -> Maybe ExecVal
395 execPrim f args = EConstant <$> (mapM getConst args >>= f)
397 getConst (EConstant c) = Just c
400 applyPrim :: ([ExecVal] -> Maybe ExecVal) -> [ExecVal] -> Maybe (Exec ExecVal)
401 applyPrim fn args = return <$> fn args
406-- | Overall wrapper for case tree execution. If there are enough arguments, it takes them,
407-- evaluates them, then begins the checks for matching cases.
408execCase :: ExecEnv -> Context -> [Name] -> SC -> [ExecVal] -> Exec (Maybe ExecVal)
409execCase env ctxt ns sc args =
410 let arity = length ns in
411 if arity <= length args
412 then do let amap = zip ns args
413-- trace ("Case " ++ show sc ++ "\n in " ++ show amap ++"\n in env " ++ show env ++ "\n\n" ) $ return ()
414 caseRes <- execCase' env ctxt amap sc
416 Just res -> Just <$> execApp (map (\(n, tm) -> (n, tm)) amap ++ env) ctxt res (drop arity args)
417 Nothing -> return Nothing
420-- | Take bindings and a case tree and examines them, executing the matching case if possible.
421execCase' :: ExecEnv -> Context -> [(Name, ExecVal)] -> SC -> Exec (Maybe ExecVal)
422execCase' env ctxt amap (UnmatchedCase _) = return Nothing
423execCase' env ctxt amap (STerm tm) =
424 Just <$> doExec (map (\(n, v) -> (n, v)) amap ++ env) ctxt tm
425execCase' env ctxt amap (Case n alts) | Just tm <- lookup n amap =
426 case chooseAlt tm alts of
427 Just (newCase, newBindings) ->
428 let amap' = newBindings ++ (filter (\(x,_) -> not (elem x (map fst newBindings))) amap) in
429 execCase' env ctxt amap' newCase
430 Nothing -> return Nothing
432chooseAlt :: ExecVal -> [CaseAlt] -> Maybe (SC, [(Name, ExecVal)])
433chooseAlt _ (DefaultCase sc : alts) = Just (sc, [])
434chooseAlt (EConstant c) (ConstCase c' sc : alts) | c == c' = Just (sc, [])
435chooseAlt tm (ConCase n i ns sc : alts) | ((EP _ cn _), args) <- unApplyV tm
436 , cn == n = Just (sc, zip ns args)
437 | otherwise = chooseAlt tm alts
438chooseAlt tm (_:alts) = chooseAlt tm alts
439chooseAlt _ [] = Nothing
444idrisType :: FType -> ExecVal
445idrisType FUnit = EP Ref unitTy EErased
446idrisType ft = EConstant (idr ft)
447 where idr (FArith ty) = AType ty
448 idr FString = StrType
451data Foreign = FFun String [FType] FType deriving Show
454call :: Foreign -> [ExecVal] -> Exec (Maybe ExecVal)
455call (FFun name argTypes retType) args =
456 do fn <- findForeign name
457 maybe (return Nothing) (\f -> Just . ioWrap <$> call' f args retType) fn
458 where call' :: ForeignFun -> [ExecVal] -> FType -> Exec ExecVal
459 call' (Fun _ h) args (FArith (ATInt ITNative)) = do
460 res <- execIO $ callFFI h retCInt (prepArgs args)
461 return (EConstant (I (fromIntegral res)))
462 call' (Fun _ h) args (FArith (ATInt (ITFixed IT8))) = do
463 res <- execIO $ callFFI h retCChar (prepArgs args)
464 return (EConstant (B8 (fromIntegral res)))
465 call' (Fun _ h) args (FArith (ATInt (ITFixed IT16))) = do
466 res <- execIO $ callFFI h retCWchar (prepArgs args)
467 return (EConstant (B16 (fromIntegral res)))
468 call' (Fun _ h) args (FArith (ATInt (ITFixed IT32))) = do
469 res <- execIO $ callFFI h retCInt (prepArgs args)
470 return (EConstant (B32 (fromIntegral res)))
471 call' (Fun _ h) args (FArith (ATInt (ITFixed IT64))) = do
472 res <- execIO $ callFFI h retCLong (prepArgs args)
473 return (EConstant (B64 (fromIntegral res)))
474 call' (Fun _ h) args (FArith ATFloat) = do
475 res <- execIO $ callFFI h retCDouble (prepArgs args)
476 return (EConstant (Fl (realToFrac res)))
477 call' (Fun _ h) args (FArith (ATInt ITChar)) = do
478 res <- execIO $ callFFI h retCChar (prepArgs args)
479 return (EConstant (Ch (castCCharToChar res)))
480 call' (Fun _ h) args FString = do res <- execIO $ callFFI h retCString (prepArgs args)
482 then return (EPtr res)
483 else do hStr <- execIO $ peekCString res
484 return (EConstant (Str hStr))
486 call' (Fun _ h) args FPtr = EPtr <$> (execIO $ callFFI h (retPtr retVoid) (prepArgs args))
487 call' (Fun _ h) args FUnit = do _ <- execIO $ callFFI h retVoid (prepArgs args)
488 return $ EP Ref unitCon EErased
489-- call' (Fun _ h) args other = fail ("Unsupported foreign return type " ++ show other)
492 prepArgs = map prepArg
493 prepArg (EConstant (I i)) = argCInt (fromIntegral i)
494 prepArg (EConstant (B8 i)) = argCChar (fromIntegral i)
495 prepArg (EConstant (B16 i)) = argCWchar (fromIntegral i)
496 prepArg (EConstant (B32 i)) = argCInt (fromIntegral i)
497 prepArg (EConstant (B64 i)) = argCLong (fromIntegral i)
498 prepArg (EConstant (Fl f)) = argCDouble (realToFrac f)
499 prepArg (EConstant (Ch c)) = argCChar (castCharToCChar c) -- FIXME - castCharToCChar only safe for first 256 chars
500 prepArg (EConstant (Str s)) = argString s
501 prepArg (EPtr p) = argPtr p
502 prepArg other = trace ("Could not use " ++ take 100 (show other) ++ " as FFI arg.") undefined
506foreignFromTT :: ExecVal -> Maybe Foreign
507foreignFromTT t = case (unApplyV t) of
508 (_, [(EConstant (Str name)), args, ret]) ->
509 do argTy <- unEList args
510 argFTy <- sequence $ map getFTy argTy
512 return $ FFun name argFTy retFTy
513 _ -> trace ("failed to construct ffun") Nothing
515getFTy :: ExecVal -> Maybe FType
516getFTy (EApp (EP _ (UN fi) _) (EP _ (UN intTy) _))
517 | fi == txt "FIntT" =
519 "ITNative" -> Just (FArith (ATInt ITNative))
520 "ITChar" -> Just (FArith (ATInt ITChar))
521 "IT8" -> Just (FArith (ATInt (ITFixed IT8)))
522 "IT16" -> Just (FArith (ATInt (ITFixed IT16)))
523 "IT32" -> Just (FArith (ATInt (ITFixed IT32)))
524 "IT64" -> Just (FArith (ATInt (ITFixed IT64)))
526getFTy (EP _ (UN t) _) =
528 "FFloat" -> Just (FArith ATFloat)
529 "FString" -> Just FString
531 "FUnit" -> Just FUnit
535unEList :: ExecVal -> Maybe [ExecVal]
536unEList tm = case unApplyV tm of
537 (nil, [_]) -> Just []
538 (cons, ([_, x, xs])) ->
539 do rest <- unEList xs
544toConst :: Term -> Maybe Const
545toConst (Constant c) = Just c
548stepForeign :: [ExecVal] -> Exec (Maybe ExecVal)
549stepForeign (ty:fn:args) = let ffun = foreignFromTT fn
550 in case (call <$> ffun) of
552 Nothing -> return Nothing
553stepForeign _ = fail "Tried to call foreign function that wasn't mkForeignPrim"
555mapMaybeM :: (Functor m, Monad m) => (a -> m (Maybe b)) -> [a] -> m [b]
556mapMaybeM f [] = return []
557mapMaybeM f (x:xs) = do rest <- mapMaybeM f xs
558 maybe rest (:rest) <$> f x
559findForeign :: String -> Exec (Maybe ForeignFun)
560findForeign fn = do est <- getExecState
561 let libs = exec_dynamic_libs est
562 fns <- mapMaybeM getFn libs
564 [f] -> return (Just f)
565 [] -> do execIO . putStrLn $ "Symbol \"" ++ fn ++ "\" not found"
567 fs -> do execIO . putStrLn $ "Symbol \"" ++ fn ++ "\" is ambiguous. Found " ++
568 show (length fs) ++ " occurrences."
570 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