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)
28-- | Attempt to perform a side effect. Return either Just the next step in
29-- evaluation (after performing the side effect through IO), or Nothing if no
32-- This function is exceedingly generous about what it will accept in order to
33-- enable convenient testing at the REPL.
34-- step :: TT Name -> Idris (Maybe (TT Name))
35-- step tm = step' (unApply tm)
36-- where step' (P _ (UN "unsafePerformIO") _, [_, arg] ) = step arg -- Only step if arg can be stepped
37-- step' (P _ (UN "mkForeign") _, args) = stepForeign args
38-- step' (P _ (UN "prim__IO") _, [_, arg]) = return (Just arg)
39-- step' (P _ (UN "prim__readString") _, [(P _ (UN "prim__stdin") _)]) =
40-- do line <- lift $ getLine
41-- return . Just . Constant . Str $ line
42-- step' _ = return Nothing
44-- | Perform side effects until no more can be performed, then return the
45-- resulting term (possibly the argument).
46-- execute :: TT Name -> Idris (TT Name)
47-- execute tm = case unApply tm of
48-- (P _ (UN "unsafePerformIO") _, [_, arg] ) -> execute' arg
50-- where execute' tm = do stepped <- step tm
52-- Nothing -> return tm
53-- Just tm' -> execute' tm'
56execute :: Term -> Idris Term
57execute tm = do ctxt <- getContext
60doExec :: Env -> Context -> Term -> Idris Term
61doExec env ctxt p@(P Ref n ty) =
62 do let val = lookupDef Nothing n ctxt
65 do val <- doExec env ctxt tm
67 [TyDecl nt ty] -> return p -- abstract def
68 [Operator tp arity op] -> do ty' <- doExec env ctxt ty
70 [CaseOp _ _ _ _ _ [] (STerm tm) _ _] -> -- nullary fun
72 [CaseOp _ _ _ _ _ ns sc _ _] -> return p
73 thing -> trace ("got to " ++ show thing) $ undefined
74doExec env ctxt p@(P Bound n ty) = case (lookupDef Nothing n ctxt) of -- bound vars must be Function
75 [Function ty tm] -> doExec env ctxt tm
77 x -> fail ("Internal error lookup up bound var " ++ show n ++ " - found " ++ show x)
78doExec env ctxt p@(P (DCon a b) n ty) = do { ty' <- doExec env ctxt ty; return (P (DCon a b) n ty') }
79doExec env ctxt p@(P (TCon a b) n ty) = do { ty' <- doExec env ctxt ty; return (P (TCon a b) n ty') }
80doExec env ctxt v@(V i) | i < length env = do let binder = env !! i
82 (_, (Let t v)) -> return v
83 (_, (NLet t v)) -> return v
84 (n, b) -> doExec env ctxt (P Bound n (binderTy b))
85 | otherwise = return $ V i
86doExec env ctxt (Bind n (Let t v) body) = do v' <- doExec env ctxt v
87 let ctxt' = addToCtxt n v' t ctxt
88 doExec ((n, Let t v'):env) ctxt' body
89doExec env ctxt (Bind n (NLet t v) body) = trace "NLet" $ undefined
90doExec env ctxt tm@(Bind n b body) = return tm -- don't eval under lambda!
91doExec env ctxt a@(App _ _) = execApp env ctxt (unApply a)
92doExec env ctxt (Constant c) = return (Constant c)
93doExec env ctxt (Proj tm i) = let (x, xs) = unApply tm in
95doExec env ctxt Erased = return Erased
96doExec env ctxt Impossible = fail "Tried to execute an impossible case"
97doExec env ctxt (TType u) = return (TType u)
99execApp :: Env -> Context -> (Term, [Term]) -> Idris Term
100execApp env ctxt (f, args) = do newF <- doExec env ctxt f
101 newArgs <- mapM (doExec env ctxt) args
102 execApp' env ctxt newF newArgs
104execApp' :: Env -> Context -> Term -> [Term] -> Idris Term
105execApp' env ctxt v [] = return v -- no args is just a constant! can result from function calls
106execApp' env ctxt (P _ (UN "unsafePerformIO") _) [ty, action] | (prim__IO, [ty', v]) <- unApply action = return v
107execApp' env ctxt (P _ (UN "mkForeign") _) args =
108 do res <- stepForeign args
110 Nothing -> fail "Could not call foreign function"
112execApp' env ctxt (P _ (UN "prim__readString") _) [P _ (UN "prim__stdin") _] =
113 do line <- lift getLine
114 return (Constant (Str line))
116execApp' env ctxt (P _ (UN "prim__concat") _) [(Constant (Str s1)), (Constant (Str s2))] =
117 return $ Constant (Str (s1 ++ s2))
119execApp' env ctxt f@(P _ n _) args =
120 do let val = lookupDef Nothing n ctxt
122 [Function _ tm] -> fail "should already have been eval'd"
123 [TyDecl nt ty] -> return $ mkApp f args
124 [Operator tp arity op] -> fail $ "Can't apply operator " ++ show n ++ " which needs special-casing"
125 [CaseOp _ _ _ _ _ [] (STerm tm) _ _] -> -- nullary fun
126 do rhs <- doExec env ctxt tm
127 doExec env ctxt (mkApp tm args)
128 [CaseOp _ _ _ _ _ ns sc _ _] ->
129 do res <- execCase env ctxt ns sc args
130 return $ fromMaybe (mkApp f args) res
131 thing -> return $ mkApp f args
133execApp' env ctxt bnd@(Bind n b body) (arg:args) = let ctxt' = addToCtxt n arg Erased ctxt in
134 doExec ((n, b):env) ctxt' body
136execApp' env ctxt f args = return (mkApp f args)
138execCase :: Env -> Context -> [Name] -> SC -> [Term] -> Idris (Maybe Term)
139execCase env ctxt ns sc args =
140 let arity = length ns in
141 if arity <= length args
142 then do let amap = (zip ns (take arity args))
143 caseRes <- execCase' env ctxt amap sc
145 Just res-> Just <$> doExec env ctxt (mkApp res (drop arity args))
146 Nothing -> return Nothing
149execCase' :: Env -> Context -> [(Name, Term)] -> SC -> Idris (Maybe Term)
150execCase' env ctxt amap (STerm tm) =
151 Just <$> doExec env (foldl (\c (n, t) -> addToCtxt n t Erased c) ctxt amap) tm
152execCase' env ctxt amap (Case n alts) | Just tm <- lookup n amap
153 , Just (newCase, newBindings) <- chooseAlt tm alts =
154 execCase' env ctxt (amap ++ newBindings) newCase
155 | otherwise = return Nothing
157chooseAlt :: Term -> [CaseAlt] -> Maybe (SC, [(Name, Term)])
158chooseAlt _ (DefaultCase sc : alts) = Just (sc, [])
159chooseAlt (Constant c) (ConstCase c' sc : alts) | c == c' = Just (sc, [])
160chooseAlt tm (ConCase n i ns sc : alts) | ((P _ cn _), args) <- unApply tm, cn == n = Just (sc, zip ns args)
161 | otherwise = chooseAlt tm alts
162chooseAlt tm (_:alts) = chooseAlt tm alts
163chooseAlt _ [] = Nothing
165data FTy = FInt | FFloat | FChar | FString | FPtr | FUnit deriving (Show, Read)
167idrisType :: FTy -> Type
168idrisType FUnit = P Ref unitTy (TType (UVal 0))
169idrisType ft = Constant (idr ft)
170 where idr FInt = IType
173 idr FString = StrType
176data Foreign = FFun String [FTy] FTy deriving Show
178call :: Foreign -> [Term] -> Idris (Maybe Term)
179call (FFun name argTypes retType) args =
180 do fn <- findForeign name
182 Nothing -> return Nothing
183 Just f -> do res <- call' f args retType
184 return . Just $ mkApp (P Ref (UN "prim__IO") Erased) [idrisType retType, res]
185 where call' :: ForeignFun -> [Term] -> FTy -> Idris Term
186 call' (Fun _ h) args FInt = do res <- lift $ callFFI h retCInt (prepArgs args)
187 return (Constant (I (fromIntegral res)))
188 call' (Fun _ h) args FFloat = do res <- lift $ callFFI h retCDouble (prepArgs args)
189 return (Constant (Fl (realToFrac res)))
190 call' (Fun _ h) args FChar = do res <- lift $ callFFI h retCChar (prepArgs args)
191 return (Constant (Ch (castCCharToChar res)))
192 call' (Fun _ h) args FString = do res <- lift $ callFFI h retCString (prepArgs args)
193 hStr <- lift $ peekCString res
195 return (Constant (Str hStr))
196-- awaiting Const constructor for pointers
197-- call' (Fun _ h) args FPtr = do res <- lift $ callFFI h retPtr (prepArgs args)
198-- return (Constant (Ch (castCCharToChar res)))
199 call' (Fun _ h) args FUnit = do res <- lift $ callFFI h retVoid (prepArgs args)
200 return (P Ref unitCon (P Ref unitTy (TType (UVal 0)))) -- FIXME check universe level
202 prepArgs = map prepArg
203 prepArg (Constant (I i)) = argCInt (fromIntegral i)
204 prepArg (Constant (Fl f)) = argCDouble (realToFrac f)
205 prepArg (Constant (Ch c)) = argCChar (castCharToCChar c) -- FIXME - castCharToCChar only safe for first 256 chars
206 prepArg (Constant (Str s)) = argString s
207 prepArg other = trace ("Could not use " ++ show other ++ " as FFI arg.") undefined
211foreignFromTT :: Term -> Maybe Foreign
212foreignFromTT t = case (unApply t) of
213 (_, [(Constant (Str name)), args, ret]) ->
214 do argTy <- unList args
215 argFTy <- sequence $ map getFTy argTy
217 return $ FFun name argFTy retFTy
218 _ -> trace "failed to construct ffun" Nothing
220getFTy :: Term -> Maybe FTy
221getFTy (P _ (UN t) _) = case t of
223 "FFloat" -> Just FFloat
224 "FChar" -> Just FChar
225 "FString" -> Just FString
227 "FUnit" -> Just FUnit
231unList :: Term -> Maybe [Term]
232unList tm = case unApply tm of
233 (nil, [_]) -> Just []
234 (cons, ([_, x, xs])) ->
239toConst :: Term -> Maybe Const
240toConst (Constant c) = Just c
243stepForeign :: [Term] -> Idris (Maybe Term)
244stepForeign (ty:fn:args) = do let ffun = foreignFromTT fn
245 f' <- case (call <$> ffun) of
247 Nothing -> return Nothing
249stepForeign _ = fail "Tried to call foreign function that wasn't mkForeign"
251mapMaybeM :: Monad m => (a -> m (Maybe b)) -> [a] -> m [b]
252mapMaybeM f [] = return []
253mapMaybeM f (x:xs) = do rest <- mapMaybeM f xs
256 Just x'' -> return (x'':rest)
257 Nothing -> return rest
259findForeign :: String -> Idris (Maybe ForeignFun)
260findForeign fn = do i <- getIState
261 let libs = idris_dynamic_libs i
262 fns <- mapMaybeM getFn libs
264 [f] -> return (Just f)
265 [] -> do iputStrLn $ "Symbol \"" ++ fn ++ "\" not found"
267 fs -> do iputStrLn $ "Symbol \"" ++ fn ++ "\" is ambiguous. Found " ++
268 show (length fs) ++ " occurrences."
270 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