lean4-htt/library/init/meta/simp_tactic.lean

302 lines
12 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/-
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import init.meta.tactic init.meta.attribute init.meta.constructor_tactic
import init.meta.relation_tactics init.meta.occurrences
open tactic
meta constant simp_lemmas : Type
meta constant simp_lemmas.mk : simp_lemmas
meta constant simp_lemmas.join : simp_lemmas → simp_lemmas → simp_lemmas
meta constant simp_lemmas.erase : simp_lemmas → list name → simp_lemmas
meta constant simp_lemmas.mk_default_core : transparency → tactic simp_lemmas
meta constant simp_lemmas.add_core : transparency → simp_lemmas → expr → tactic simp_lemmas
meta constant simp_lemmas.add_simp_core : transparency → simp_lemmas → name → tactic simp_lemmas
meta constant simp_lemmas.add_congr_core : transparency → simp_lemmas → name → tactic simp_lemmas
meta def simp_lemmas.mk_default : tactic simp_lemmas :=
simp_lemmas.mk_default_core reducible
meta def simp_lemmas.add : simp_lemmas → expr → tactic simp_lemmas :=
simp_lemmas.add_core reducible
meta def simp_lemmas.add_simp : simp_lemmas → name → tactic simp_lemmas :=
simp_lemmas.add_simp_core reducible
meta def simp_lemmas.add_congr : simp_lemmas → name → tactic simp_lemmas :=
simp_lemmas.add_congr_core reducible
meta def simp_lemmas.append : simp_lemmas → list expr → tactic simp_lemmas
| sls [] := return sls
| sls (l::ls) := do
new_sls ← simp_lemmas.add sls l,
simp_lemmas.append new_sls ls
/- (simp_lemmas.rewrite_core m s prove R e) apply a simplification lemma from 's'
- 'prove' is used to discharge proof obligations.
- 'R' is the equivalence relation being used (e.g., 'eq', 'iff')
- 'e' is the expression to be "simplified"
Result (new_e, pr) is the new expression 'new_e' and a proof (pr : e R new_e) -/
meta constant simp_lemmas.rewrite_core : transparency → simp_lemmas → tactic unit → name → expr → tactic (expr × expr)
meta def simp_lemmas.rewrite : simp_lemmas → tactic unit → name → expr → tactic (expr × expr) :=
simp_lemmas.rewrite_core reducible
/- (simp_lemmas.drewrite s e) tries to rewrite 'e' using only refl lemmas in 's' -/
meta constant simp_lemmas.drewrite_core : transparency → simp_lemmas → expr → tactic expr
meta def simp_lemmas.drewrite : simp_lemmas → expr → tactic expr :=
simp_lemmas.drewrite_core reducible
/- (Definitional) Simplify the given expression using *only* reflexivity equality lemmas from the given set of lemmas.
The resulting expression is definitionally equal to the input. -/
meta constant simp_lemmas.dsimplify_core (max_steps : nat) (visit_instances : bool) : simp_lemmas → expr → tactic expr
def default_max_steps := 10000000
meta def simp_lemmas.dsimplify : simp_lemmas → expr → tactic expr :=
simp_lemmas.dsimplify_core default_max_steps ff
namespace tactic
meta constant dsimplify_core
/- The user state type. -/
{A : Type}
/- Initial user data -/
(a : A)
(max_steps : nat)
/- If visit_instances = ff, then instance implicit arguments are not visited, but
tactic will canonize them. -/
(visit_instances : bool)
/- (pre a e) is invoked before visiting the children of subterm 'e',
if it succeeds the result (new_a, new_e, flag) where
- 'new_a' is the new value for the user data
- 'new_e' is a new expression that must be definitionally equal to 'e',
- 'flag' if tt 'new_e' children should be visited, and 'post' invoked. -/
(pre : A → expr → tactic (A × expr × bool))
/- (post a e) is invoked after visiting the children of subterm 'e',
The output is similar to (pre a e), but the 'flag' indicates whether
the new expression should be revisited or not. -/
(post : A → expr → tactic (A × expr × bool))
: expr → tactic (A × expr)
meta def dsimplify
(pre : expr → tactic (expr × bool))
(post : expr → tactic (expr × bool))
: expr → tactic expr :=
λ e, do (a, new_e) ← dsimplify_core () default_max_steps ff
(λ u e, do r ← pre e, return (u, r))
(λ u e, do r ← post e, return (u, r)) e,
return new_e
meta constant dunfold_expr_core : transparency → expr → tactic expr
meta def dunfold_expr : expr → tactic expr :=
dunfold_expr_core reducible
meta constant unfold_projection_core : transparency → expr → tactic expr
meta def unfold_projection : expr → tactic expr :=
unfold_projection_core reducible
meta def dunfold_occs_core (m : transparency) (max_steps : nat) (occs : occurrences) (cs : list name) (e : expr) : tactic expr :=
let unfold (c : nat) (e : expr) : tactic (nat × expr × bool) := do
guard (cs^.any e^.is_app_of),
new_e ← dunfold_expr_core m e,
if occs^.contains c
then return (c+1, new_e, tt)
else return (c+1, e, tt)
in do (c, new_e) ← dsimplify_core 1 max_steps tt unfold (λ c e, failed) e,
return new_e
meta def dunfold_core (m : transparency) (max_steps : nat) (cs : list name) (e : expr) : tactic expr :=
let unfold (u : unit) (e : expr) : tactic (unit × expr × bool) := do
guard (cs^.any e^.is_app_of),
new_e ← dunfold_expr_core m e,
return (u, new_e, tt)
in do (c, new_e) ← dsimplify_core () max_steps tt (λ c e, failed) unfold e,
return new_e
meta def dunfold : list name → tactic unit :=
λ cs, target >>= dunfold_core reducible default_max_steps cs >>= change
meta def dunfold_occs_of (occs : list nat) (c : name) : tactic unit :=
target >>= dunfold_occs_core reducible default_max_steps (occurrences.pos occs) [c] >>= change
meta def dunfold_core_at (occs : occurrences) (cs : list name) (h : expr) : tactic unit :=
do num_reverted : ← revert h,
(expr.pi n bi d b : expr) ← target | failed,
new_d : expr ← dunfold_occs_core reducible default_max_steps occs cs d,
change $ expr.pi n bi new_d b,
intron num_reverted
meta def dunfold_at (cs : list name) (h : expr) : tactic unit :=
do num_reverted : ← revert h,
(expr.pi n bi d b : expr) ← target | failed,
new_d : expr ← dunfold_core reducible default_max_steps cs d,
change $ expr.pi n bi new_d b,
intron num_reverted
structure simplify_config :=
(max_steps : nat)
(contextual : bool)
(lift_eq : bool)
(canonize_instances : bool)
(canonize_proofs : bool)
(use_axioms : bool)
def default_simplify_config : simplify_config :=
{ max_steps := default_max_steps,
contextual := ff,
lift_eq := tt,
canonize_instances := tt,
canonize_proofs := ff,
use_axioms := tt }
meta constant simplify_core
(c : simplify_config)
(s : simp_lemmas)
(r : name) :
expr → tactic (expr × expr)
meta constant ext_simplify_core
/- The user state type. -/
{A : Type}
/- Initial user data -/
(a : A)
(c : simplify_config)
/- Congruence and simplification lemmas.
Remark: the simplification lemmas at not applied automatically like in the simplify_core tactic.
the caller must use them at pre/post. -/
(s : simp_lemmas)
/- Tactic for dischaging hypothesis in conditional rewriting rules.
The argument 'A' is the current user state. -/
(prove : A → tactic A)
/- (pre a r s p e) is invoked before visiting the children of subterm 'e',
'r' is the simplification relation being used, 's' is the updated set of lemmas if 'contextual' is tt,
'p' is the "parent" expression (if there is one).
if it succeeds the result is (new_a, new_e, new_pr, flag) where
- 'new_a' is the new value for the user data
- 'new_e' is a new expression s.t. 'e r new_e'
- 'new_pr' is a proof for 'e r new_e', If it is none, the proof is assumed to be by reflexivity
- 'flag' if tt 'new_e' children should be visited, and 'post' invoked. -/
(pre : A → simp_lemmas → name → option expr → expr → tactic (A × expr × option expr × bool))
/- (post a r s p e) is invoked after visiting the children of subterm 'e',
The output is similar to (pre a r s p e), but the 'flag' indicates whether
the new expression should be revisited or not. -/
(post : A → simp_lemmas → name → option expr → expr → tactic (A × expr × option expr × bool))
/- simplification relation -/
(r : name) :
expr → tactic (A × expr × expr)
meta def simplify (cfg : simplify_config) (extra_lemmas : list expr) (e : expr) : tactic (expr × expr) :=
do lemmas ← simp_lemmas.mk_default,
new_lemmas ← lemmas^.append extra_lemmas,
e_type ← infer_type e >>= whnf,
simplify_core cfg new_lemmas `eq e
meta def simplify_goal (cfg : simplify_config) (extra_lemmas : list expr) : tactic unit :=
do (new_target, Heq) ← target >>= simplify cfg extra_lemmas,
assert `Htarget new_target, swap,
Ht ← get_local `Htarget,
mk_app `eq.mpr [Heq, Ht] >>= exact
meta def simp : tactic unit :=
simplify_goal default_simplify_config [] >> try triv >> try reflexivity
meta def simp_using (Hs : list expr) : tactic unit :=
simplify_goal default_simplify_config Hs >> try triv
meta def ctx_simp : tactic unit :=
simplify_goal {default_simplify_config with contextual := tt} [] >> try triv >> try reflexivity
meta def dsimp : tactic unit :=
do S ← simp_lemmas.mk_default,
target >>= S^.dsimplify >>= change
meta def dsimp_at (H : expr) : tactic unit :=
do num_reverted : ← revert H,
(expr.pi n bi d b : expr) ← target | failed,
S ← simp_lemmas.mk_default,
H_simp ← S^.dsimplify d,
change $ expr.pi n bi H_simp b,
intron num_reverted
private meta def is_equation : expr → bool
| (expr.pi n bi d b) := is_equation b
| e := match (expr.is_eq e) with (some a) := tt | none := ff end
private meta def collect_eqs : list expr → tactic (list expr)
| [] := return []
| (H :: Hs) := do
Eqs ← collect_eqs Hs,
Htype ← infer_type H >>= whnf,
return $ if is_equation Htype then H :: Eqs else Eqs
/- Simplify target using all hypotheses in the local context. -/
meta def simp_using_hs : tactic unit :=
local_context >>= collect_eqs >>= simp_using
meta def simp_core_at (extra_lemmas : list expr) (H : expr) : tactic unit :=
do when (expr.is_local_constant H = ff) (fail "tactic simp_at failed, the given expression is not a hypothesis"),
Htype ← infer_type H,
(new_Htype, Heq) ← simplify default_simplify_config extra_lemmas Htype,
assert (expr.local_pp_name H) new_Htype,
mk_app `eq.mp [Heq, H] >>= exact,
try $ clear H
meta def simp_at : expr → tactic unit :=
simp_core_at []
meta def simp_at_using (Hs : list expr) : expr → tactic unit :=
simp_core_at Hs
meta def simp_at_using_hs (H : expr) : tactic unit :=
do Hs ← local_context >>= collect_eqs,
simp_core_at (list.filter (ne H) Hs) H
meta def mk_eq_simp_ext (simp_ext : expr → tactic (expr × expr)) : tactic unit :=
do (lhs, rhs) ← target >>= match_eq,
(new_rhs, Heq) ← simp_ext lhs,
unify rhs new_rhs,
exact Heq
/- Simp attribute support -/
meta def to_simp_lemmas : simp_lemmas → list name → tactic simp_lemmas
| S [] := return S
| S (n::ns) := do S' ← S^.add_simp n, to_simp_lemmas S' ns
meta def mk_simp_attr (attr_name : name) : command :=
do t ← to_expr `(caching_user_attribute simp_lemmas),
a ← attr_name^.to_expr,
v ← to_expr `(({ name := %%a,
descr := "simplifier attribute",
mk_cache := λ ns, do {tactic.to_simp_lemmas simp_lemmas.mk ns},
dependencies := [`reducibility] } : caching_user_attribute simp_lemmas)),
add_decl (declaration.defn attr_name [] t v reducibility_hints.abbrev ff),
attribute.register attr_name
meta def get_user_simp_lemmas (attr_name : name) : tactic simp_lemmas :=
if attr_name = `default then simp_lemmas.mk_default
else do
cnst ← return (expr.const attr_name []),
attr ← eval_expr (caching_user_attribute simp_lemmas) cnst,
caching_user_attribute.get_cache attr
meta def join_user_simp_lemmas_core : simp_lemmas → list name → tactic simp_lemmas
| S [] := return S
| S (attr_name::R) := do S' ← get_user_simp_lemmas attr_name, join_user_simp_lemmas_core (S^.join S') R
meta def join_user_simp_lemmas : list name → tactic simp_lemmas
| [] := simp_lemmas.mk_default
| attr_names := join_user_simp_lemmas_core simp_lemmas.mk attr_names
end tactic
export tactic (mk_simp_attr)