on master help sign in
Merge pull request #1926 from david-christiansen/new-tactic-reflection

"claim" and "unfocus" tactics

commit2127664640090a7e43d56bb46a875b44df0ad94c authorDavid Christiansen <david@davidchristiansen.dk> dateTue 17 Feb 2015, 15:38 (12 years ago) parentda0a9a0f17606a
13 files changed+113−33
CHANGELOG+3 idris.cabal+3 libs/prelude/Language/Reflection.idr+7 src/Idris/AbsSyntaxTree.hs+2 src/Idris/Core/ProofState.hs+18−18 src/Idris/Core/ProofTerm.hs+37−14 src/Idris/DeepSeq.hs+2 src/Idris/ElabQuasiquote.hs+1 src/Idris/ElabTerm.hs+14 src/Idris/ParseExpr.hs+6−1 test/proof008/ClaimAndUnfocus.idr+17 test/proof008/expected test/proof008/run+3
CHANGELOG+3
@@ -2,6 +2,9 @@ New in 0.9.17:
22--------------
33* The --ideslave command line option has been replaced with a --ide-mode
44 command line option with the same semantics.
5+* A new tactic "claim N TY" that introduces a new hole named N with type TY
6+* A new tactic "unfocus" that moves the current hole to the bottom of the
7+ hole stack
58
69New in 0.9.16:
710--------------
idris.cabal+3
@@ -497,6 +497,9 @@ Extra-source-files:
497497 test/proof004/run
498498 test/proof004/*.idr
499499 test/proof004/expected
500+ test/proof008/run
501+ test/proof008/*.idr
502+ test/proof008/expected
500503
501504 test/quasiquote001/run
502505 test/quasiquote001/*.idr
libs/prelude/Language/Reflection.idr+7
@@ -213,6 +213,10 @@ data Tactic =
213213 Refine TTName |
214214 ||| Apply both tactics in sequence
215215 Seq Tactic Tactic |
216+ ||| Introduce a new hole with a particular type
217+ Claim TTName TT |
218+ ||| Move the current hole to the end
219+ Unfocus |
216220 ||| Intelligently construct the proof target from the context
217221 Trivial |
218222 ||| Build a proof by applying contructors up to a maximum depth
@@ -472,6 +476,8 @@ instance Quotable Tactic where
472476 quote (Try tac tac') = `(Try ~(quote tac) ~(quote tac'))
473477 quote (GoalType x tac) = `(GoalType ~(quote x) ~(quote tac))
474478 quote (Refine n) = `(Refine ~(quote n))
479+ quote (Claim n ty) = `(Claim ~(quote n) ~(quote ty))
480+ quote Unfocus = `(Unfocus)
475481 quote (Seq tac tac') = `(Seq ~(quote tac) ~(quote tac'))
476482 quote Trivial = `(Trivial)
477483 quote (Search x) = `(Search ~(quote x))
@@ -494,3 +500,4 @@ instance Quotable Tactic where
494500 quote Skip = `(Skip)
495501 quote (Fail xs) = `(Fail ~(quote xs))
496502 quote SourceFC = `(SourceFC)
503+
src/Idris/AbsSyntaxTree.hs+2
@@ -830,6 +830,8 @@ data PTactic' t = Intro [Name] | Intros | Focus Name
830830 | Induction t
831831 | CaseTac t
832832 | Equiv t
833+ | Claim Name t
834+ | Unfocus
833835 | MatchRefine Name
834836 | LetTac Name t | LetTacTy Name t t
835837 | Exact t | Compute | Trivial | TCInstance
src/Idris/Core/ProofState.hs+18−18
@@ -23,23 +23,23 @@ import Debug.Trace
2323import Util.Pretty hiding (fill)
2424
2525data ProofState = PS { thname :: Name,
26− holes :: [Name], -- holes still to be solved
27− usedns :: [Name], -- used names, don't use again
28− nextname :: Int, -- name supply
29− pterm :: ProofTerm, -- current proof term
30− ptype :: Type, -- original goal
31− dontunify :: [Name], -- explicitly given by programmer, leave it
26+ holes :: [Name], -- ^ holes still to be solved
27+ usedns :: [Name], -- ^ used names, don't use again
28+ nextname :: Int, -- ^ name supply
29+ pterm :: ProofTerm, -- ^ current proof term
30+ ptype :: Type, -- ^ original goal
31+ dontunify :: [Name], -- ^ explicitly given by programmer, leave it
3232 unified :: (Name, [(Name, Term)]),
3333 notunified :: [(Name, Term)],
34− dotted :: [(Name, [Name])], -- dot pattern holes + environment
35− -- either hole or something in env must turn up in the 'notunified' list during elaboration
34+ dotted :: [(Name, [Name])], -- ^ dot pattern holes + environment
35+ -- either hole or something in env must turn up in the 'notunified' list during elaboration
3636 solved :: Maybe (Name, Term),
3737 problems :: Fails,
3838 injective :: [Name],
39− deferred :: [Name], -- names we'll need to define
40− instances :: [Name], -- instance arguments (for type classes)
41− autos :: [(Name, [Name])], -- unsolved 'auto' implicits with their holes
42− previous :: Maybe ProofState, -- for undo
39+ deferred :: [Name], -- ^ names we'll need to define
40+ instances :: [Name], -- ^ instance arguments (for type classes)
41+ autos :: [(Name, [Name])], -- ^ unsolved 'auto' implicits with their holes
42+ previous :: Maybe ProofState, -- ^ for undo
4343 context :: Context,
4444 plog :: String,
4545 unifylog :: Bool,
@@ -290,12 +290,12 @@ addLog str = action (\ps -> ps { plog = plog ps ++ str ++ "\n" })
290290
291291newProof :: Name -> Context -> Type -> ProofState
292292newProof n ctxt ty = let h = holeName 0
293− ty' = vToP ty in
294− PS n [h] [] 1 (mkProofTerm (Bind h (Hole ty')
295− (P Bound h ty'))) ty [] (h, []) [] []
296− Nothing [] []
297− [] [] []
298− Nothing ctxt "" False False [] []
293+ ty' = vToP ty
294+ in PS n [h] [] 1 (mkProofTerm (Bind h (Hole ty')
295+ (P Bound h ty'))) ty [] (h, []) [] []
296+ Nothing [] []
297+ [] [] []
298+ Nothing ctxt "" False False [] []
299299
300300type TState = ProofState -- [TacticAction])
301301type RunTactic = RunTactic' TState
src/Idris/Core/ProofTerm.hs+37−14
@@ -1,8 +1,9 @@
11{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, PatternGuards #-}
22
3−{- Implements a proof state, some primitive tactics for manipulating
4− proofs, and some high level commands for introducing new theorems,
5− evaluation/checking inside the proof system, etc. --}
3+{- | Implements a proof state, some primitive tactics for manipulating
4+proofs, and some high level commands for introducing new theorems,
5+evaluation/checking inside the proof system, etc.
6+-}
67
78module Idris.Core.ProofTerm(ProofTerm, Goal(..), mkProofTerm, getProofTerm,
89 updateSolved, updateSolvedTerm, updateSolvedTerm',
@@ -18,6 +19,7 @@ import Control.Monad.State.Strict
1819import Data.List
1920import Debug.Trace
2021
22+-- | A zipper over terms, in order to efficiently update proof terms.
2123data TermPath = Top
2224 | AppL TermPath Term
2325 | AppR Term TermPath
@@ -25,6 +27,7 @@ data TermPath = Top
2527 | InScope Name (Binder Term) TermPath
2628 deriving Show
2729
30+-- | A zipper over binders, because terms and binders are mutually defined.
2831data BinderPath = Binder (Binder TermPath)
2932 | LetT TermPath Term
3033 | LetV Term TermPath
@@ -32,6 +35,8 @@ data BinderPath = Binder (Binder TermPath)
3235 | GuessV Term TermPath
3336 deriving Show
3437
38+-- | Replace the top of a term path with another term path. In other
39+-- words, "graft" one term path into another.
3540replaceTop :: TermPath -> TermPath -> TermPath
3641replaceTop p Top = p
3742replaceTop p (AppL l t) = AppL (replaceTop p l) t
@@ -45,6 +50,7 @@ replaceTop p (InBind n bp sc) = InBind n (replaceTopB p bp) sc
4550 replaceTopB p (GuessV t v) = GuessV t (replaceTop p v)
4651replaceTop p (InScope n b sc) = InScope n b (replaceTop p sc)
4752
53+-- | Build a term from a zipper, given something to put in the hole.
4854rebuildTerm :: Term -> TermPath -> Term
4955rebuildTerm tm Top = tm
5056rebuildTerm tm (AppL p a) = App (rebuildTerm tm p) a
@@ -52,6 +58,7 @@ rebuildTerm tm (AppR f p) = App f (rebuildTerm tm p)
5258rebuildTerm tm (InScope n b p) = Bind n b (rebuildTerm tm p)
5359rebuildTerm tm (InBind n bp sc) = Bind n (rebuildBinder tm bp) sc
5460
61+-- | Build a binder from a zipper, given something to put in the hole.
5562rebuildBinder :: Term -> BinderPath -> Binder Term
5663rebuildBinder tm (Binder p) = fmap (rebuildTerm tm) p
5764rebuildBinder tm (LetT p t) = Let (rebuildTerm tm p) t
@@ -59,6 +66,9 @@ rebuildBinder tm (LetV v p) = Let v (rebuildTerm tm p)
5966rebuildBinder tm (GuessT p t) = Guess (rebuildTerm tm p) t
6067rebuildBinder tm (GuessV v p) = Guess v (rebuildTerm tm p)
6168
69+-- | Find the binding of a hole in a term. If present, return the path
70+-- to the hole's binding, the environment extended by the binders that
71+-- are crossed, and the actual binding term.
6272findHole :: Name -> Env -> Term -> Maybe (TermPath, Env, Term)
6373findHole n env t = fh' env Top t where
6474 fh' env path tm@(Bind x h sc)
@@ -80,7 +90,7 @@ findHole n env t = fh' env Top t where
8090 fhB env path b
8191 | Just (p, env', tm) <- fh' env path (binderTy b)
8292 = Just (Binder (fmap (\_ -> p) b), env', tm)
83− fhB _ _ _ = Nothing
93+ fhB _ _ _ = Nothing
8494
8595data ProofTerm = PT { -- wholeterm :: Term,
8696 path :: TermPath,
@@ -92,13 +102,17 @@ data ProofTerm = PT { -- wholeterm :: Term,
92102type RunTactic' a = Context -> Env -> Term -> StateT a TC Term
93103type Hole = Maybe Name -- Nothing = default hole, first in list in proof state
94104
105+-- | Refocus the proof term zipper on a particular hole, if it
106+-- exists. If not, return the original proof term.
95107refocus :: Hole -> ProofTerm -> ProofTerm
96108refocus h t = let res = refocus' h t in res
97109-- trace ("OLD: " ++ show t ++ "\n" ++
98110-- "REFOCUSSED " ++ show h ++ ": " ++ show res) res
99111refocus' (Just n) pt@(PT path env tm ups)
112+ -- First look for the hole in the proof term as-is
100113 | Just (p', env', tm') <- findHole n env tm
101114 = PT (replaceTop p' path) env' tm' ups
115+ -- Next apply the updates, and look for the hole in the resulting term
102116 | Just (p', env', tm') <- findHole n [] (rebuildTerm tm (updateSolvedPath ups path))
103117 = PT p' env' tm' []
104118 | otherwise = pt
@@ -118,27 +132,32 @@ same :: Eq a => Maybe a -> a -> Bool
118132same Nothing n = True
119133same (Just x) n = x == n
120134
135+-- | Is a particular binder a hole or a guess?
121136hole :: Binder b -> Bool
122137hole (Hole _) = True
123138hole (Guess _ _) = True
124139hole _ = False
125140
126−updateSolvedTerm :: [(Name, Term)] -> Term -> Term
141+-- | Given a list of solved holes, fill out the solutions in a term
142+updateSolvedTerm :: [(Name, Term)] -> Term -> Term
127143updateSolvedTerm xs x = fst $ updateSolvedTerm' xs x
128144
145+-- | Given a list of solved holes, fill out the solutions in a
146+-- term. Return whether updates were performed, to facilitate sharing
147+-- when there are no updates.
148+updateSolvedTerm' :: [(Name, Term)] -> Term -> (Term, Bool)
129149updateSolvedTerm' [] x = (x, False)
130−updateSolvedTerm' xs x = -- updateSolved' xs x where
131−-- This version below saves allocations, because it doesn't need to reallocate
132−-- the term if there are no updates to do.
133−-- The Bool is ugly, and probably 'Maybe' would be less ugly, but >>= is
134−-- the wrong combinator. Feel free to tidy up as long as it's still as cheap :).
135− updateSolved' xs x where
150+updateSolvedTerm' xs x = updateSolved' xs x where
151+ -- This version below saves allocations, because it doesn't need to reallocate
152+ -- the term if there are no updates to do.
153+ -- The Bool is ugly, and probably 'Maybe' would be less ugly, but >>= is
154+ -- the wrong combinator. Feel free to tidy up as long as it's still as cheap :).
136155 updateSolved' [] x = (x, False)
137156 updateSolved' xs (Bind n (Hole ty) t)
138− | Just v <- lookup n xs
157+ | Just v <- lookup n xs
139158 = case xs of
140159 [_] -> (subst n v t, True) -- some may be Vs! Can't assume
141− -- explicit names
160+ -- explicit names
142161 _ -> let (t', _) = updateSolved' xs t in
143162 (subst n v t', True)
144163 updateSolved' xs tm@(Bind n b t)
@@ -146,7 +165,7 @@ updateSolvedTerm' xs x = -- updateSolved' xs x where
146165 (b', ub) = updateSolvedB' xs b in
147166 if ut || ub then (Bind n b' t', True)
148167 else (tm, False)
149− updateSolved' xs t@(App f a)
168+ updateSolved' xs t@(App f a)
150169 = let (f', uf) = updateSolved' xs f
151170 (a', ua) = updateSolved' xs a in
152171 if uf || ua then (App f' a', True)
@@ -176,10 +195,14 @@ updateSolvedTerm' xs x = -- updateSolved' xs x where
176195 noneOfB ns b = noneOf ns (binderTy b)
177196 noneOf ns _ = True
178197
198+-- | Apply solutions to an environment.
199+updateEnv :: [(Name, Term)] -> Env -> Env
179200updateEnv [] e = e
180201updateEnv ns [] = []
181202updateEnv ns ((n, b) : env) = (n, fmap (updateSolvedTerm ns) b) : updateEnv ns env
182203
204+-- | Fill out solved holes in a term zipper.
205+updateSolvedPath :: [(Name, Term)] -> TermPath -> TermPath
183206updateSolvedPath [] t = t
184207updateSolvedPath ns Top = Top
185208updateSolvedPath ns (AppL p r) = AppL (updateSolvedPath ns p) (updateSolvedTerm ns r)
src/Idris/DeepSeq.hs+2
@@ -250,6 +250,7 @@ instance (NFData t) => NFData (PTactic' t) where
250250 rnf (Induction x1) = rnf x1 `seq` ()
251251 rnf (CaseTac x1) = rnf x1 `seq` ()
252252 rnf (Equiv x1) = rnf x1 `seq` ()
253+ rnf (Claim x1 x2) = rnf x1 `seq` rnf x2 `seq` ()
253254 rnf (MatchRefine x1) = rnf x1 `seq` ()
254255 rnf (LetTac x1 x2) = rnf x1 `seq` rnf x2 `seq` ()
255256 rnf (LetTacTy x1 x2 x3) = rnf x1 `seq` rnf x2 `seq` rnf x3 `seq` ()
@@ -280,6 +281,7 @@ instance (NFData t) => NFData (PTactic' t) where
280281 rnf Skip = ()
281282 rnf (TFail x1) = rnf x1 `seq` ()
282283 rnf SourceFC = ()
284+ rnf Unfocus = ()
283285
284286instance NFData ErrorReportPart where
285287 rnf (TermPart x1) = rnf x1 `seq` ()
src/Idris/ElabQuasiquote.hs+1
@@ -36,6 +36,7 @@ extractTUnquotes n (GoalType s tac)
3636 return (GoalType s tac', ex)
3737extractTUnquotes n (TCheck t) = extract1 n TCheck t
3838extractTUnquotes n (TEval t) = extract1 n TEval t
39+extractTUnquotes n (Claim name t) = extract1 n (Claim name) t
3940extractTUnquotes n tac = return (tac, []) -- the rest don't contain PTerms
4041
4142extractPArgUnquotes :: Int -> PArg -> Elab' aux (PArg, [(Name, PTerm)])
src/Idris/ElabTerm.hs+14
@@ -1506,6 +1506,12 @@ runTac autoSolve ist perhapsFC fn tac
15061506 when autoSolve solveAll
15071507 runT DoUnify = do unify_all
15081508 when autoSolve solveAll
1509+ runT (Claim n tm) = do tmHole <- getNameFrom (sMN 0 "newGoal")
1510+ claim tmHole RType
1511+ claim n (Var tmHole)
1512+ focus tmHole
1513+ elab ist toplevel ERHS [] (sMN 0 "tac") tm
1514+ focus n
15091515 runT (Equiv tm) -- let bind tm, then
15101516 = do attack
15111517 tyn <- getNameFrom (sMN 0 "ety")
@@ -1566,6 +1572,10 @@ runTac autoSolve ist perhapsFC fn tac
15661572 = do proofSearch' ist rec False depth prover top fn hints
15671573 when autoSolve solveAll
15681574 runT (Focus n) = focus n
1575+ runT Unfocus = do hs <- get_holes
1576+ case hs of
1577+ [] -> return ()
1578+ (h : _) -> movelast h
15691579 runT Solve = solve
15701580 runT (Try l r) = do try' (runT l) (runT r) True
15711581 runT (TSeq l r) = do runT l; runT r
@@ -1710,6 +1720,7 @@ reify _ (P _ n _) | n == reflm "Solve" = return Solve
17101720reify _ (P _ n _) | n == reflm "Compute" = return Compute
17111721reify _ (P _ n _) | n == reflm "Skip" = return Skip
17121722reify _ (P _ n _) | n == reflm "SourceFC" = return SourceFC
1723+reify _ (P _ n _) | n == reflm "Unfocus" = return Unfocus
17131724reify ist t@(App _ _)
17141725 | (P _ f _, args) <- unApply t = reifyApp ist f args
17151726reify _ t = fail ("Unknown tactic " ++ show t)
@@ -1721,6 +1732,9 @@ reifyApp _ t [Constant (I i)]
17211732reifyApp _ t [x]
17221733 | t == reflm "Refine" = do n <- reifyTTName x
17231734 return $ Refine n []
1735+reifyApp ist t [n, ty] | t == reflm "Claim" = do n' <- reifyTTName n
1736+ goal <- reifyTT ty
1737+ return $ Claim n' (delab ist goal)
17241738reifyApp ist t [l, r] | t == reflm "Seq" = liftM2 TSeq (reify ist l) (reify ist r)
17251739reifyApp ist t [Constant (Str n), x]
17261740 | t == reflm "GoalType" = liftM (GoalType n) (reify ist x)
src/Idris/ParseExpr.hs+6−1
@@ -1251,10 +1251,15 @@ tactics =
12511251 [ (["intro"], Nothing, const $ -- FIXME syntax for intro (fresh name)
12521252 do ns <- sepBy (spaced name) (lchar ','); return $ Intro ns)
12531253 , noArgs ["intros"] Intros
1254+ , noArgs ["unfocus"] Unfocus
12541255 , (["refine"], Just ExprTArg, const $
12551256 do n <- spaced fnName
12561257 imps <- many imp
12571258 return $ Refine n imps)
1259+ , (["claim"], Nothing, \syn ->
1260+ do n <- indentPropHolds gtProp *> name
1261+ goal <- indentPropHolds gtProp *> expr syn
1262+ return $ Claim n goal)
12581263 , (["mrefine"], Just ExprTArg, const $
12591264 do n <- spaced fnName
12601265 return $ MatchRefine n)
@@ -1329,7 +1334,7 @@ tactics =
13291334
13301335
13311336tactic :: SyntaxInfo -> IdrisParser PTactic
1332−tactic syn = choice [ do choice (map reserved names); parser syn
1337+tactic syn = choice [ do choice (map reserved names); parser syn
13331338 | (names, _, parser) <- tactics ]
13341339 <|> do lchar '{'
13351340 t <- tactic syn;
test/proof008/ClaimAndUnfocus.idrnew file+17
@@ -0,0 +1,17 @@
1+module ClaimAndUnfocus
2+
3+foo : Nat -> Nat
4+foo = ?foo_rhs
5+
6+---------- Proofs ----------
7+
8+ClaimAndUnfocus.foo_rhs = proof
9+ claim bar Nat -> Nat -> Nat
10+ unfocus
11+ intro x
12+ exact bar x x
13+ intro x,y
14+ refine plus
15+ refine x
16+ refine y
17+
test/proof008/expectednew file

No changes to the content.

test/proof008/runnew file+3
@@ -0,0 +1,3 @@
1+#!/usr/bin/env bash
2+idris --check $@ ClaimAndUnfocus.idr
3+rm -f *.ibc