import init.meta.lean.parser open tactic open interactive def for_m {m : Type → Type} {α β : Type} [monad_m : monad m] (action : list α) (f : α → m β) : m (list β) := monad.mapm f action private meta def until_first_hyp_aux (f : expr → tactic unit) : list expr → tactic unit | [] := tactic.fail "until_first_hyp: provided tactic did not succed on any hypotheses" | (h :: hs) := f h <|> until_first_hyp_aux hs meta def until_first_hyp (action : expr → tactic unit) : tactic unit := do ls ← local_context, until_first_hyp_aux action ls def subterm_err_msg := "subterms: only provides subterms of applications; i.e terms of the form (f x_1 ... x_n)" meta def subterms : expr → (expr → list expr → tactic unit) → tactic unit | (expr.app f g) action := let head := expr.get_app_fn (expr.app f g), args := expr.get_app_args (expr.app f g) in action head args <|> list.foldl (<|>) (tactic.fail subterm_err_msg) (args.map (fun e, subterms e action)) | _ _ := tactic.fail subterm_err_msg meta def is_match_wrapper (n : name): bool := match n.components.ilast with | name.mk_string s _ := s.to_list.take 7 = "_match_".to_list | _ := bool.ff end meta def find_scrutinee : list expr → tactic expr := fun xs, return (xs.ilast) meta def rw_one (hyp_name : option name) (eq_n : name) : tactic unit := match hyp_name with | none := do eq ← get_local eq_n, rewrite_target eq | some hn := do eq ← get_local eq_n, H ← get_local hn, rewrite_hyp eq H, return () end /- Removes any reflexive equalities of the form a = a. -/ meta def clear_refl_eqs : tactic unit := do ls ← local_context, for_m ls (λ l, do ty ← infer_type l, -- is there a better way to write this? (do (a, b) ← match_eq ty, if a = b then clear l else return ()) <|> return ()), return () meta def intros_and_subst (hyp_name : option name) : tactic unit := do tgt ← target, if tgt.is_pi then do n ← get_unused_name `a, H ← intro n, -- trace H, ty ← infer_type H, try (rw_one hyp_name n), intros_and_subst else return () meta def split_app (e : expr) : tactic (expr × list expr) := match e with | (expr.app _ _) := let head := expr.get_app_fn e, args := expr.get_app_args e in return (head, args) | _ := tactic.fail "expected an application found ..." end meta def find_match (head : expr) (args : list expr) : tactic (name × expr) := if is_match_wrapper head.const_name then do scrut ← find_scrutinee args, return $ (head.const_name, scrut) else tactic.fail "head is not match" -- todo improve me meta def destruct_subst_dsimp (hyp_name : option name) (match_name : name) (scrut : expr) := seq (destruct scrut) $ seq (intros_and_subst hyp_name) (all_goals $ match hyp_name with | none := dsimp_target none [match_name, `id_rhs] | some n := do H ← get_local n, dsimp_hyp H none [match_name, `id_rhs], return () end) meta def break_match_or_fail (hyp_name : option name) : expr → tactic unit | (expr.app f g) := subterms (expr.app f g) (fun head args, do -- trace head, (match_name, scrut) ← find_match head args, destruct_subst_dsimp hyp_name match_name scrut, return ()) | _ := tactic.fail "break_match: does not support this term" meta def break_match_hyp : tactic unit := -- The English name for `;`. seq -- First try to find a hypothesis where break_match succeds (until_first_hyp (λ loc, do ty ← infer_type loc, break_match_or_fail loc.local_pp_name ty)) -- If one succeds, clean up the context for all goals, by first clearing redundant equalities -- then try to prune goals using contradiction. (do clear_refl_eqs, try contradiction) meta def break_match_goal : tactic unit := do tgt ← target, match tgt with | (expr.app f g) := break_match_or_fail none (expr.app f g) | _ := return () end meta def break_if_or_fail (loc : option name) : expr → tactic unit | (expr.app f g) := subterms (expr.app f g) (fun head args, match head with -- add support for dite | (expr.const `ite _) := match args with | (pred :: _ ) := do n ← get_unused_name `p, tactic.trace pred, by_cases pred n, all_goals `[simp * at *] -- replace this -- | (expr.const `dite _) := -- do n ← get_unused_name `p, -- tactic.trace pred, -- by_cases pred n, -- all_goals `[simp * at *] -- replace this -- | (expr.const `dite _) := | [] := tactic.fail "should be unreachable" end | _ := tactic.fail "no if" end) | _ := tactic.fail "break_if: does not support this term" meta def is_recursor : name → tactic bool | (name.mk_string s n) := if s = "rec" then return true else return false | _ := return false #eval (is_recursor `prod.rec) meta def break_irrefutable_or_fail (hyp_name : option name) (h : expr) : tactic unit := subterms h (fun h args, do is_rec ← (fun b, h.is_constant && b) <$> is_recursor h.const_name, if is_rec then destruct_subst_dsimp hyp_name h.const_name args.ilast else tactic.fail "") meta def break_irrefutable_hyp : tactic unit := until_first_hyp (λ loc, do ty ← infer_type loc, break_irrefutable_or_fail loc.local_pp_name ty) meta def break_if_hyp : tactic unit := (until_first_hyp (λ loc, do ty ← infer_type loc, break_if_or_fail loc.local_pp_name ty)) meta def break_if_target : tactic unit := do tgt ← target, match tgt with | (expr.app f g) := break_if_or_fail none (expr.app f g) | _ := return () end meta def break_match := break_match_hyp <|> break_match_goal meta def break_if := break_if_hyp <|> break_if_target meta def case_split := break_if <|> break_match open interactive.types meta def match_head (rhs lhs : expr) : tactic unit := let head_rhs := rhs.get_app_fn, head_lhs := lhs.get_app_fn in if head_rhs = head_lhs then return () else tactic.fail "match_head, no match" -- Not sure about this name anymore meta def find_inversion : tactic unit := until_first_hyp $ λ h, do ty ← infer_type h, (rhs, lhs) ← match_eq ty, match_head rhs lhs, cases h /- Interactive Tactics -/ lemma option_bind_some {α β : Type} : forall (o1 : option α) (o2 : α → option β) v, o1 >>= o2 = some v → exists v', o1 = some v' ∧ o2 v' = some v := begin intros, destruct o1 ; intros, unfold bind at *, subst a_1, dsimp [option.bind] at *, cases a, subst a_2, unfold bind at *, dsimp [option.bind] at *, constructor, split, reflexivity, assumption, end meta def simp_option (n : parse lean.parser.ident) : tactic unit := do h ← get_local n, ty ← tactic.infer_type h, (a :: b :: o1 :: o2 :: v :: _) ← tactic.match_expr ``(fun (a b : Type) (o1 : option a) (o2 : a → option b) (v : b), @option.bind a b o1 o2 = some v) ty | tactic.failed, destruct o1 ; intros_and_subst n, tactic.trace o1, tactic.trace o2, tactic.trace v, h ← get_local n, -- dsimp_hyp h none [`bind, `option.bind], return () meta def induction_on (induction_var : parse lean.parser.ident) : tactic unit := do tgt ← target, match tgt with | (expr.pi binder_name _ _ _) := if binder_name = induction_var then do intro binder_name, ivar_local ← get_local binder_name, induction ivar_local, return () else do intro binder_name, induction_on | _ := tactic.fail $ "induction_on: unknown name `" ++ induction_var.to_string ++ "`" end run_cmd add_interactive [ `induction_on, `simp_option ]