1{-# LANGUAGE PatternGuards #-}
2module Idris.Prover (prover, showProof, showRunElab) where
4-- Hack for GHC 7.10 and earlier compat without CPP or warnings
5-- This exludes (<$>) as fmap, because wl-pprint uses it for newline
6import Prelude (Eq(..), Show(..),
7 Bool(..), Either(..), Maybe(..), String,
9 concatMap, id, elem, error, fst, flip, foldl, foldr, init,
10 length, lines, map, not, null, repeat, reverse, tail, zip)
12import Idris.Core.Elaborate hiding (Tactic(..))
14import Idris.Core.Evaluate
15import Idris.Core.CaseTree
16import Idris.Core.Typecheck
18import Idris.Elab.Utils
19import Idris.Elab.Value
23import Idris.AbsSyntaxTree
24import Idris.Delaborate
25import Idris.Docs (getDocs, pprintDocs, pprintConstDocs)
27import Idris.Parser hiding (params)
30import Idris.Completion
31import qualified Idris.IdeMode as IdeMode
33import Idris.TypeSearch (searchByType)
35import Text.Trifecta.Result(Result(..))
37import System.IO (Handle, stdin, stdout, hPutStrLn)
38import System.Console.Haskeline
39import System.Console.Haskeline.History
40import Control.Monad.State.Strict
46-- | Launch the proof shell
47prover :: Bool -> Bool -> Name -> Idris ()
51 case lookupTy x ctxt of
52 [t] -> if elem x (map fst (idris_metavars i))
53 then prove mode (idris_optimisation i) ctxt lit x t
54 else ifail $ show x ++ " is not a hole"
55 _ -> fail "No such hole"
57showProof :: Bool -> Name -> [String] -> String
59 = bird ++ show n ++ " = proof" ++ break ++
60 showSep break (map (\x -> " " ++ x) ps) ++
62 where bird = if lit then "> " else ""
65showRunElab :: Bool -> Name -> [String] -> String
66showRunElab lit n ps = birdify (displayS (renderPretty 1.0 80 doc) "")
67 where doc = text (show n) <+> text "=" <+> text "%runElab" <+>
69 (text "do" <+> (align $ vsep (map text ps)))
71 then concatMap ("> " ++) . lines
74proverSettings :: ElabState EState -> Settings Idris
75proverSettings e = setComplete (proverCompletion (assumptionNames e)) defaultSettings
77assumptionNames :: ElabState EState -> [String]
79 = case envAtFocus (proof e) of
82 names ((MN _ _, _) : bs) = names bs
83 names ((n, _) : bs) = show n : names bs
85prove :: Bool -> Ctxt OptInfo -> Context -> Bool -> Name -> Type -> Idris ()
86prove mode opt ctxt lit n ty
87 = do ps <- fmap (\ist -> initElaborator n ctxt (idris_datatypes ist) ty) getIState
88 idemodePutSExp "start-proof-mode" n
91 then elabloop n True ("-" ++ show n) [] (ES (ps, initEState) "" Nothing) [] Nothing []
92 else ploop n True ("-" ++ show n) [] (ES (ps, initEState) "" Nothing) Nothing
93 logLvl 1 $ "Adding " ++ show tm
98 case idris_outputmode i of
99 IdeMode _ _ -> idemodePutSExp "end-proof-mode" (n, shower lit n prf)
100 _ -> iputStrLn $ shower lit n prf
101 let proofs = proof_list i
102 putIState (i { proof_list = (n, (mode, prf)) : proofs })
103 let tree = simpleCase False (STerm Erased) False CompileTime (fileFC "proof") [] [] [([], P Ref n ty, tm)]
105 (ptm, pty) <- recheckC (fileFC "proof") id [] tm
106 logLvl 5 ("Proof type: " ++ show pty ++ "\n" ++
107 "Expected type:" ++ show ty)
108 case converts ctxt [] ty pty of
110 Error e -> ierror (CantUnify False (ty, Nothing) (pty, Nothing) e [] 0)
111 ptm' <- applyOpts ptm
112 ei <- getErasureInfo `fmap` getIState
113 ctxt' <- do ctxt <- getContext
114 tclift $ addCasedef n ei (CaseInfo True True False) False (STerm Erased) True False
115 [] [] -- argtys, inaccArgs
116 [Right (P Ref n ty, ptm)]
117 [([], P Ref n ty, ptm)]
118 [([], P Ref n ty, ptm)]
119 [([], P Ref n ty, ptm)]
120 [([], P Ref n ty, ptm')] ty
124 case idris_outputmode i of
126 runIO . hPutStrLn h $ IdeMode.convSExp "return" (IdeMode.SymbolAtom "ok", "") n
129elabStep :: ElabState EState -> ElabD a -> Idris (a, ElabState EState)
130elabStep st e = case runStateT eCheck st of
131 OK (a, st') -> return (a, st')
133 where eCheck = do res <- e
138 [] -> do tm <- get_term
140 lift $ check ctxt [] (forget tm)
142 ((_,_,_,_,e,_,_):_) -> lift $ Error e
144dumpState :: IState -> [(Name, Type, Term)] -> ProofState -> Idris ()
145dumpState ist menv ps | [] <- holes ps =
146 do let nm = thname ps
147 rendered <- iRender $ prettyName True False [] nm <> colon <+> text "No more goals."
149dumpState ist menv ps | (h : hs) <- holes ps = do
150 let OK ty = goalAtFocus ps
151 let OK env = envAtFocus ps
152 let state = prettyOtherGoals hs <> line <>
153 prettyAssumptions env <> line <>
154 prettyMetaValues (reverse menv) <>
155 prettyGoal (zip (assumptionNames env) (repeat False)) ty
156 rendered <- iRender state
160 (h : hs) = holes ps -- apparently the pattern guards don't give us this
162 ppo = ppOptionIst ist
164 tPretty bnd t = annotate (AnnTerm bnd t) .
165 pprintPTerm ppo bnd [] (idris_infixes ist) $
168 assumptionNames :: Env -> [Name]
169 assumptionNames = map fst
171 prettyPs :: [(Name, Bool)] -> Env -> Doc OutputAnnotation
172 prettyPs bnd [] = empty
173 prettyPs bnd ((n@(MN _ r), _) : bs)
174 | r == txt "rewrite_rule" = prettyPs ((n, False):bnd) bs
175 prettyPs bnd ((n@(MN _ _), _) : bs)
176 | not (n `elem` freeEnvNames bs) = prettyPs bnd bs
177 prettyPs bnd ((n, Let t v) : bs) =
178 line <> bindingOf n False <+> text "=" <+> tPretty bnd v <+> colon <+>
179 align (tPretty bnd t) <> prettyPs ((n, False):bnd) bs
180 prettyPs bnd ((n, b) : bs) =
181 line <> bindingOf n False <+> colon <+>
182 align (tPretty bnd (binderTy b)) <> prettyPs ((n, False):bnd) bs
184 prettyMetaValues [] = empty
185 prettyMetaValues mvs =
186 text "---------- Meta-values: ----------" <$$>
187 prettyMetaValues' [] mvs <> line <> line
188 prettyMetaValues' _ [] = empty
189 prettyMetaValues' bnd [mv] = ppMv bnd mv
190 prettyMetaValues' bnd ((mv@(n, ty, tm)) : mvs) =
192 prettyMetaValues' ((n, False):bnd) mvs
194 ppMv bnd (n, ty, tm) = kwd "let" <+> bindingOf n False <+> colon <+>
195 tPretty bnd ty <+> text "=" <+> tPretty bnd tm
197 kwd = annotate AnnKeyword . text
199 prettyG bnd (Guess t v) = tPretty bnd t <+> text "=?=" <+> tPretty bnd v
200 prettyG bnd b = tPretty bnd $ binderTy b
203 text "---------- Goal: ----------" <$$>
204 bindingOf h False <+> colon <+> align (prettyG bnd ty)
206 prettyAssumptions env =
207 if length env == 0 then
210 text "---------- Assumptions: ----------" <>
211 nest nestingSize (prettyPs [] $ reverse env)
213 prettyOtherGoals hs =
214 if length hs == 0 then
217 text "---------- Other goals: ----------" <$$>
218 nest nestingSize (align . cat . punctuate (text ",") . map (flip bindingOf False) $ hs)
220 freeEnvNames :: Env -> [Name]
221 freeEnvNames = foldl (++) [] . map (\(n, b) -> freeNames (Bind n b Erased))
223lifte :: ElabState EState -> ElabD a -> Idris a
224lifte st e = do (v, _) <- elabStep st e
227receiveInput :: Handle -> ElabState EState -> Idris (Maybe String)
230 let inh = if h == stdout then stdin else h
231 len' <- runIO $ IdeMode.getLen inh
233 Left err -> ierror err
235 l <- runIO $ IdeMode.getNChar inh len ""
236 (sexp, id) <- case IdeMode.parseMessage l of
237 Left err -> ierror err
238 Right (sexp, id) -> return (sexp, id)
239 putIState $ i { idris_outputmode = (IdeMode id h) }
240 case IdeMode.sexpToCommand sexp of
241 Just (IdeMode.REPLCompletions prefix) ->
242 do (unused, compls) <- proverCompletion (assumptionNames e) (reverse prefix, "")
243 let good = IdeMode.SexpList [IdeMode.SymbolAtom "ok", IdeMode.toSExp (map replacement compls, reverse unused)]
244 idemodePutSExp "return" good
246 Just (IdeMode.Interpret cmd) -> return (Just cmd)
247 Just (IdeMode.TypeOf str) -> return (Just (":t " ++ str))
248 Just (IdeMode.DocsFor str _) -> return (Just (":doc " ++ str))
251data ElabShellHistory = ElabStep | LetStep | BothStep
253undoStep :: [String] -> [(Name, Type, Term)] -> ElabState EState -> ElabShellHistory -> Idris ([String], [(Name, Type, Term)], ElabState EState)
254undoStep prf env st ElabStep = do (_, st') <- elabStep st loadState
255 return (init prf, env, st')
256undoStep prf env st LetStep = return (init prf, tail env, st)
257undoStep prf env st BothStep = do (_, st') <- elabStep st loadState
258 return (init prf, tail env, st')
260undoElab :: [String] -> [(Name, Type, Term)] -> ElabState EState -> [ElabShellHistory] -> Idris ([String], [(Name, Type, Term)], ElabState EState, [ElabShellHistory])
261undoElab prf env st [] = ifail "Nothing to undo"
262undoElab prf env st (h:hs) = do (prf', env', st') <- undoStep prf env st h
263 return (prf', env', st', hs)
265elabloop :: Name -> Bool -> String -> [String] -> ElabState EState -> [ElabShellHistory] -> Maybe History -> [(Name, Type, Term)] -> Idris (Term, [String])
266elabloop fn d prompt prf e prev h env
267 = do ist <- getIState
268 when d $ dumpState ist env (proof e)
270 case idris_outputmode ist of
272 runInputT (proverSettings e) $
274 Just history -> putHistory history
276 l <- getInputLine (prompt ++ "> ")
281 i <- receiveInput handle e
283 (cmd, step) <- case x of
284 Nothing -> do iPrintError "" ; ifail "Abandoned"
285 Just input -> return (parseElabShellStep ist input, input)
287 -- if we're abandoning, it has to be outside the scope of the catch
289 Success (Left EAbandon) -> do iPrintError ""; ifail "Abandoned"
292 (d, prev', st, done, prf', env', res) <-
296 return (False, prev, e, False, prf, env, Left . Msg . show . fixColour (idris_colourRepl ist) $ err)
297 Success (Left cmd') ->
299 EQED -> do hs <- lifte e get_holes
300 when (not (null hs)) $ ifail "Incomplete proof"
301 iputStrLn "Proof completed!"
302 return (False, prev, e, True, prf, env, Right $ iPrintResult "")
303 EUndo -> do (prf', env', st', prev') <- undoElab prf env e prev
304 return (True, prev', st', False, prf', env', Right $ iPrintResult "")
306 error "the impossible happened - should have been matched on outer scope"
307 EProofState -> return (True, prev, e, False, prf, env, Right $ iPrintResult "")
309 do tm <- lifte e get_term
310 return (False, prev, e, False, prf, env, Right $ iRenderResult (text "TT:" <+> pprintTT [] tm))
311 EEval tm -> do (d', st', done, prf', go) <- evalTerm e prf tm
312 return (d', prev, st', done, prf', env, go)
313 ECheck (PRef _ _ n) ->
314 do (d', st', done, prf', go) <- checkNameType e prf n
315 return (d', prev, st', done, prf', env, go)
316 ECheck tm -> do (d', st', done, prf', go) <- checkType e prf tm
317 return (d', prev, st', done, prf', env, go)
318 ESearch ty -> do (d', st', done, prf', go) <- search e prf ty
319 return (d', prev, st', done, prf', env, go)
320 EDocStr d -> do (d', st', done, prf', go) <- docStr e prf d
321 return (d', prev, st', done, prf', env, go)
322 Success (Right cmd') ->
324 DoLetP _ _ _ -> ifail "Pattern-matching let not supported here"
325 DoBindP _ _ _ _ -> ifail "Pattern-matching <- not supported here"
326 DoLet fc i ifc Placeholder expr ->
327 do (tm, ty) <- elabVal recinfo ERHS (inLets ist env expr)
329 let tm' = normaliseAll ctxt [] tm
330 ty' = normaliseAll ctxt [] ty
331 return (True, LetStep:prev, e, False, prf ++ [step], (i, ty', tm' ) : env, Right (iPrintResult ""))
332 DoLet fc i ifc ty expr ->
333 do (tm, ty) <- elabVal recinfo ERHS
334 (PApp NoFC (PRef NoFC [] (sUN "the"))
335 [ pexp (inLets ist env ty)
336 , pexp (inLets ist env expr)
339 let tm' = normaliseAll ctxt [] tm
340 ty' = normaliseAll ctxt [] ty
341 return (True, LetStep:prev, e, False, prf ++ [step], (i, ty', tm' ) : env, Right (iPrintResult ""))
342 DoBind fc i ifc expr ->
343 do (tm, ty) <- elabVal recinfo ERHS (inLets ist env expr)
344 (_, e') <- elabStep e saveState -- enable :undo
345 (res, e'') <- elabStep e' $
346 runElabAction ist NoFC [] tm ["Shell"]
348 (v, vty) <- tclift $ check ctxt [] (forget res)
349 let v' = normaliseAll ctxt [] v
350 vty' = normaliseAll ctxt [] vty
351 return (True, BothStep:prev, e'', False, prf ++ [step], (i, vty', v') : env, Right (iPrintResult ""))
353 do (tm, ty) <- elabVal recinfo ERHS (inLets ist env expr)
354 -- TODO: call elaborator with Elab () as goal here
355 (_, e') <- elabStep e saveState -- enable :undo
356 (_, e'') <- elabStep e' $
357 runElabAction ist NoFC [] tm ["Shell"]
358 return (True, ElabStep:prev, e'', False, prf ++ [step], env, Right (iPrintResult "")))
359 (\err -> return (False, prev, e, False, prf, env, Left err))
360 idemodePutSExp "write-proof-state" (prf', length prf')
362 Left err -> do ist <- getIState
363 iRenderError $ pprintErr ist err
364 elabloop fn d prompt prf' st prev' h' env'
366 if done then do (tm, _) <- elabStep st get_term
369 elabloop fn d prompt prf' st prev' h' env'
372 -- A bit of a hack: wrap the value up in a let binding, which will
373 -- normalise away. It would be better to figure out how to call
374 -- the elaborator with a custom environment here to avoid the
376 inLets :: IState -> [(Name, Type, Term)] -> PTerm -> PTerm
377 inLets ist lets tm = foldr (\(n, ty, v) b -> PLet NoFC n NoFC (delab ist ty) (delab ist v) b) tm (reverse lets)
381ploop :: Name -> Bool -> String -> [String] -> ElabState EState -> Maybe History -> Idris (Term, [String])
382ploop fn d prompt prf e h
384 let autoSolve = opt_autoSolve (idris_options i)
385 when d $ dumpState i [] (proof e)
387 case idris_outputmode i of
389 runInputT (proverSettings e) $
390 -- Manually track the history so that we can use the proof state
392 Just history -> putHistory history
394 l <- getInputLine (prompt ++ "> ")
399 i <- receiveInput handle e
401 (cmd, step) <- case x of
402 Nothing -> do iPrintError ""; ifail "Abandoned"
403 Just input -> do return (parseTactic i input, input)
405 Success Abandon -> do iPrintError ""; ifail "Abandoned"
407 (d, st, done, prf', res) <- idrisCatch
409 Failure err -> return (False, e, False, prf, Left . Msg . show . fixColour (idris_colourRepl i) $ err)
410 Success Undo -> do (_, st) <- elabStep e loadState
411 return (True, st, False, init prf, Right $ iPrintResult "")
412 Success ProofState -> return (True, e, False, prf, Right $ iPrintResult "")
413 Success ProofTerm -> do tm <- lifte e get_term
414 iputStrLn $ "TT: " ++ show tm ++ "\n"
415 return (False, e, False, prf, Right $ iPrintResult "")
416 Success Qed -> do hs <- lifte e get_holes
417 when (not (null hs)) $ ifail "Incomplete proof"
418 iputStrLn "Proof completed!"
419 return (False, e, True, prf, Right $ iPrintResult "")
420 Success (TCheck (PRef _ _ n)) -> checkNameType e prf n
421 Success (TCheck t) -> checkType e prf t
422 Success (TEval t) -> evalTerm e prf t
423 Success (TDocStr x) -> docStr e prf x
424 Success (TSearch t) -> search e prf t
426 do (_, e) <- elabStep e saveState
427 (_, st) <- elabStep e (runTac autoSolve i (Just proverFC) fn tac)
428 return (True, st, False, prf ++ [step], Right $ iPrintResult ""))
429 (\err -> return (False, e, False, prf, Left err))
430 idemodePutSExp "write-proof-state" (prf', length prf')
432 Left err -> do ist <- getIState
433 iRenderError $ pprintErr ist err
434 ploop fn d prompt prf' st h'
436 if done then do (tm, _) <- elabStep st get_term
439 ploop fn d prompt prf' st h'
442envCtxt env ctxt = foldl (\c (n, b) -> addTyDecl n Bound (binderTy b) c) ctxt env
444checkNameType :: ElabState EState -> [String] -> Name -> Idris (Bool, ElabState EState, Bool, [String], Either Err (Idris ()))
445checkNameType e prf n = do
450 let OK env = envAtFocus (proof e)
451 ctxt' = envCtxt env ctxt
452 bnd = map (\x -> (fst x, False)) env
453 ist' = ist { tt_ctxt = ctxt' }
455 -- Unlike the REPL, metavars have no special treatment, to
456 -- make it easier to see how to prove with them.
457 let action = case lookupNames n ctxt' of
458 [] -> iPrintError $ "No such variable " ++ show n
459 ts -> iPrintFunTypes bnd n (map (\n -> (n, pprintDelabTy ist' n)) ts)
461 return (False, e, False, prf, Right action))
462 (\err -> do putIState ist ; ierror err)
464checkType :: ElabState EState -> [String] -> PTerm -> Idris (Bool, ElabState EState, Bool, [String], Either Err (Idris ()))
465checkType e prf t = do
469 let OK env = envAtFocus (proof e)
470 ctxt' = envCtxt env ctxt
471 putIState ist { tt_ctxt = ctxt' }
472 (tm, ty) <- elabVal recinfo ERHS t
473 let ppo = ppOptionIst ist
474 ty' = normaliseC ctxt [] ty
475 infixes = idris_infixes ist
478 iPrintTermWithType (prettyImp ppo (PType emptyFC)) type1Doc
479 _ -> let bnd = map (\x -> (fst x, False)) env in
480 iPrintTermWithType (pprintPTerm ppo bnd [] infixes (delab ist tm))
481 (pprintPTerm ppo bnd [] infixes (delab ist ty))
483 return (False, e, False, prf, Right action))
484 (\err -> do putIState ist { tt_ctxt = ctxt } ; ierror err)
486proverFC = FC "(prover shell)" (0, 0) (0, 0)
488evalTerm :: ElabState EState -> [String] -> PTerm -> Idris (Bool, ElabState EState, Bool, [String], Either Err (Idris ()))
489evalTerm e prf t = withErrorReflection $
490 do ctxt <- getContext
493 let OK env = envAtFocus (proof e)
494 ctxt' = envCtxt env ctxt
495 ist' = ist { tt_ctxt = ctxt' }
496 bnd = map (\x -> (fst x, False)) env
498 (tm, ty) <- elabVal recinfo ERHS t
499 let tm' = force (normaliseAll ctxt' env tm)
500 ty' = force (normaliseAll ctxt' env ty)
501 ppo = ppOption (idris_options ist')
502 infixes = idris_infixes ist
503 tmDoc = pprintPTerm ppo bnd [] infixes (delab ist' tm')
504 tyDoc = pprintPTerm ppo bnd [] infixes (delab ist' ty')
505 action = iPrintTermWithType tmDoc tyDoc
507 return (False, e, False, prf, Right action))
508 (\err -> do putIState ist ; ierror err)
510docStr :: ElabState EState -> [String] -> Either Name Const -> Idris (Bool, ElabState EState, Bool, [String], Either Err (Idris ()))
511docStr e prf (Left n) = do ist <- getIState
512 idrisCatch (case lookupCtxtName n (idris_docstrings ist) of
513 [] -> return (False, e, False, prf,
514 Left . Msg $ "No documentation for " ++ show n)
515 ns -> do toShow <- mapM (showDoc ist) ns
516 return (False, e, False, prf,
517 Right $ iRenderResult (vsep toShow)))
518 (\err -> do putIState ist ; ierror err)
519 where showDoc ist (n, d) = do doc <- getDocs n FullDocs
520 return $ pprintDocs ist doc
521docStr e prf (Right c) = do ist <- getIState
522 return (False, e, False, prf, Right . iRenderResult $ pprintConstDocs ist c (constDocs c))
523search e prf t = return (False, e, False, prf, Right $ searchByType [] t)
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