> {-# OPTIONS_GHC -fglasgow-exts #-} > -- | > -- Module : Ivor.Construction > -- Copyright : Edwin Brady > -- Licence : BSD-style (see LICENSE in the distribution) > -- > -- Maintainer : eb@dcs.st-and.ac.uk > -- Stability : experimental > -- Portability : non-portable > -- > -- Some generic tactics for solving goals by applying constructors > module Ivor.Construction(auto,split,left,right,useCon,exists, > isItJust) where > import Ivor.TT > import Debug.Trace > -- | Tries to solve a simple goal automatically by trying each of these > -- in turn: > -- * Looking for an assumption ('trivial') > -- * 'intros' everything then solve by 'auto' > -- * Splitting the goal, then solving each subgoal by 'auto' > -- * If the goal is of a type with more than one constructor, try 'auto' > -- on each constructor in turn. > -- FIXME: not that this actually works yet. > auto :: Int -- ^ Search depth > -> Tactic > auto 0 = \g ctxt -> fail "auto got bored, try a bigger search depth" > auto n = \g ctxt -> if (allSolved ctxt) then return ctxt else > trace ("Auto "++ show n) $ > ((trivial >+> (auto n)) >|> > (intros1 >+> (auto n)) >|> > (split >+> (auto n)) >|> > (left >+> (auto (n-1))) >|> > (right >+> (auto (n-1)))) g ctxt > -- | Split a goal into subgoals. Type of goal must be a one constructor > -- family, with constructor @c@, then proceeds by 'refine' @c@. > split :: Tactic > split = useCon 1 0 > -- | Split a goal into subgoals. Type of goal must be a two constructor > -- family, with constructors @l@ and @r@, then proceeds by 'refine' @l@. > left :: Tactic > left = useCon 2 0 > -- | Split a goal into subgoals. Type of goal must be a two constructor > -- family, with constructors @l@ and @r@, then proceeds by 'refine' @r@. > right :: Tactic > right = useCon 2 1 Get the goal, look at the type. Refine by the constructor of that type - check that there is the right number (num). > -- | Solve the goal by applying a numbered constructor > useCon :: Int -- ^ Ensure at least this number of constructors (0 for no constraint) > -> Int -- ^ Use this constructor (0 based, order of definition) > -> Tactic > useCon num use g ctxt = do > goal <- goalData ctxt False g > let ty = getApp (view (goalType goal)) > case ty of > (Name _ n) -> do cons <- getConstructors ctxt n > splitnCon cons g ctxt > _ -> fail "Not a type constructor" > where splitnCon cs | length cs >= num || num == 0 > = refine (Name DataCon (cs!!use)) > splitnCon _ = \g ctxt -> fail $ "Not a " ++ show num ++ " constructor family" > -- | Solve an existential by providing a witness. > -- More generally; apply the first constructor of the > -- goal's type and provide the witness as its first non-inferrable argument. > exists :: IsTerm a => a -- ^Witness > -> Tactic > exists t = useCon 1 0 >+> fill t > -- | Try to solve a goal @A@ by evaluating a term of type @Maybe A@. If the > -- answer is @Just a@, fill in the goal with the proof term @a@. > isItJust :: IsTerm a => a -> Tactic > isItJust tm g ctxt = do > gd <- goalData ctxt False g > let gty = view $ goalType gd > vtm <- evalCtxt ctxt g tm > let (prf, ty) = (view vtm, viewType vtm) > -- make sure type is 'Maybe' > case ty of > (App (Name _ m) a) | m == (name "Maybe") > -> do case prf of > (App (Name _ n) _) | n == (name "Nothing") > -> fail "No solution found" > (App (App (Name _ j) _) p) | j == (name "Just") > -> fill p g ctxt > tm -> fail $ "Evaluated to " ++ show tm > _ -> fail $ "Type of decision procedure must be ++ " ++ > show (App (Name Unknown (name "Maybe")) gty)