feat(library/tactic): add 'intros' tactic
This commit is contained in:
parent
5cba7244ce
commit
7d0100a340
10 changed files with 167 additions and 5 deletions
|
@ -118,7 +118,7 @@
|
|||
(,(rx "\[priority" (zero-or-more (not (any "\]"))) "\]") . font-lock-doc-face)
|
||||
;; tactics
|
||||
(,(rx word-start
|
||||
(or "\\b.*_tac" "Cond" "or_else" "then" "try" "when" "assumption" "apply" "rename"
|
||||
(or "\\b.*_tac" "Cond" "or_else" "then" "try" "when" "assumption" "apply" "rename" "intros"
|
||||
"back" "beta" "done" "exact" "repeat")
|
||||
word-end)
|
||||
. 'font-lock-constant-face)
|
||||
|
|
|
@ -26,10 +26,20 @@ static expr parse_rename(parser & p, unsigned, expr const *, pos_info const & po
|
|||
return p.save_pos(mk_rename_tactic_macro(from, to), pos);
|
||||
}
|
||||
|
||||
static expr parse_intros(parser & p, unsigned, expr const *, pos_info const & pos) {
|
||||
buffer<name> ns;
|
||||
while (p.curr_is_identifier()) {
|
||||
ns.push_back(p.get_name_val());
|
||||
p.next();
|
||||
}
|
||||
return p.save_pos(mk_intros_tactic_macro(ns), pos);
|
||||
}
|
||||
|
||||
void init_nud_tactic_table(parse_table & r) {
|
||||
expr x0 = mk_var(0);
|
||||
r = r.add({transition("apply", mk_ext_action(parse_apply))}, x0);
|
||||
r = r.add({transition("rename", mk_ext_action(parse_rename))}, x0);
|
||||
r = r.add({transition("intros", mk_ext_action(parse_intros))}, x0);
|
||||
}
|
||||
|
||||
void initialize_builtin_tactics() {
|
||||
|
|
|
@ -76,7 +76,7 @@ void init_token_table(token_table & t) {
|
|||
{".", 0}, {":", 0}, {"::", 0}, {"calc", 0}, {":=", 0}, {"--", 0}, {"#", 0},
|
||||
{"(*", 0}, {"/-", 0}, {"begin", g_max_prec}, {"proof", g_max_prec}, {"qed", 0}, {"@", g_max_prec},
|
||||
{"sorry", g_max_prec}, {"+", g_plus_prec}, {g_cup, g_cup_prec}, {"->", g_arrow_prec}, {"local", 0},
|
||||
{"apply", 0}, {"rename", 0}, {nullptr, 0}};
|
||||
{"apply", 0}, {"rename", 0}, {"intros", 0}, {nullptr, 0}};
|
||||
|
||||
char const * commands[] =
|
||||
{"theorem", "axiom", "variable", "protected", "private", "opaque", "definition", "coercion",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
add_library(tactic goal.cpp proof_state.cpp tactic.cpp apply_tactic.cpp
|
||||
expr_to_tactic.cpp init_module.cpp)
|
||||
intros_tactic.cpp expr_to_tactic.cpp init_module.cpp)
|
||||
|
||||
target_link_libraries(tactic ${LEAN_LIBS})
|
||||
|
|
|
@ -16,6 +16,7 @@ Author: Leonardo de Moura
|
|||
#include "library/kernel_serializer.h"
|
||||
#include "library/tactic/expr_to_tactic.h"
|
||||
#include "library/tactic/apply_tactic.h"
|
||||
#include "library/tactic/intros_tactic.h"
|
||||
|
||||
namespace lean {
|
||||
static expr * g_exact_tac_fn = nullptr;
|
||||
|
@ -80,6 +81,13 @@ public:
|
|||
m_name(n), m_fn(fn) {}
|
||||
name const & get_tatic_kind() const { return m_name; }
|
||||
expr_to_tactic_fn const & get_fn() const { return m_fn; }
|
||||
virtual bool operator==(macro_definition_cell const & other) const {
|
||||
if (tactic_macro_definition_cell const * other_ptr = dynamic_cast<tactic_macro_definition_cell const *>(&other)) {
|
||||
return m_name == other_ptr->m_name;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
virtual name get_name() const { return get_tactic_name(); }
|
||||
virtual format pp(formatter const &) const { return format(m_name); }
|
||||
virtual void display(std::ostream & out) const { out << m_name; }
|
||||
|
@ -138,6 +146,7 @@ bool is_tactic_macro(expr const & e) {
|
|||
|
||||
static name * g_apply_tactic_name = nullptr;
|
||||
static name * g_rename_tactic_name = nullptr;
|
||||
static name * g_intros_tactic_name = nullptr;
|
||||
|
||||
expr mk_apply_tactic_macro(expr const & e) {
|
||||
return mk_tactic_macro(*g_apply_tactic_name, e);
|
||||
|
@ -148,6 +157,14 @@ expr mk_rename_tactic_macro(name const & from, name const & to) {
|
|||
return mk_tactic_macro(*g_rename_tactic_name, 2, args);
|
||||
}
|
||||
|
||||
expr mk_intros_tactic_macro(buffer<name> const & ns) {
|
||||
buffer<expr> args;
|
||||
for (name const & n : ns) {
|
||||
args.push_back(Const(n));
|
||||
}
|
||||
return mk_tactic_macro(*g_intros_tactic_name, args.size(), args.data());
|
||||
}
|
||||
|
||||
expr_to_tactic_fn const & get_tactic_macro_fn(expr const & e) {
|
||||
lean_assert(is_tactic_macro(e));
|
||||
return static_cast<tactic_macro_definition_cell const*>(macro_def(e).raw())->get_fn();
|
||||
|
@ -220,7 +237,6 @@ static name_generator next_name_generator() {
|
|||
}
|
||||
|
||||
tactic expr_to_tactic(environment const & env, elaborate_fn const & fn, expr const & e, pos_info_provider const * p) {
|
||||
// std::cout << "expr_to_tactic: " << e << "\n";
|
||||
flet<bool> let(g_unfold_tactic_macros, false);
|
||||
type_checker tc(env, next_name_generator());
|
||||
return expr_to_tactic(tc, fn, e, p);
|
||||
|
@ -315,6 +331,7 @@ void initialize_expr_to_tactic() {
|
|||
|
||||
g_apply_tactic_name = new name(*g_tactic_name, "apply");
|
||||
g_rename_tactic_name = new name(*g_tactic_name, "rename");
|
||||
g_intros_tactic_name = new name(*g_tactic_name, "intros");
|
||||
|
||||
name builtin_tac_name(*g_tactic_name, "builtin");
|
||||
name exact_tac_name(*g_tactic_name, "exact");
|
||||
|
@ -380,7 +397,6 @@ void initialize_expr_to_tactic() {
|
|||
});
|
||||
register_tacm(*g_apply_tactic_name,
|
||||
[](type_checker &, elaborate_fn const & fn, expr const & e, pos_info_provider const *) {
|
||||
// std::cout << "gen apply: " << e << "\n";
|
||||
check_macro_args(e, 1, "invalid 'apply' tactic, it must have one argument");
|
||||
return apply_tactic(fn, macro_arg(e, 0));
|
||||
});
|
||||
|
@ -391,6 +407,17 @@ void initialize_expr_to_tactic() {
|
|||
throw expr_to_tactic_exception(e, "invalid 'rename' tactic, arguments must be identifiers");
|
||||
return rename_tactic(const_name(macro_arg(e, 0)), const_name(macro_arg(e, 1)));
|
||||
});
|
||||
register_tacm(*g_intros_tactic_name,
|
||||
[](type_checker &, elaborate_fn const &, expr const & e, pos_info_provider const *) {
|
||||
buffer<name> ns;
|
||||
for (unsigned i = 0; i < macro_num_args(e); i++) {
|
||||
expr const & arg = macro_arg(e, i);
|
||||
if (!is_constant(arg))
|
||||
throw expr_to_tactic_exception(e, "invalid 'intros' tactic, arguments must be identifiers");
|
||||
ns.push_back(const_name(arg));
|
||||
}
|
||||
return intros_tactic(to_list(ns.begin(), ns.end()));
|
||||
});
|
||||
register_tac(exact_tac_name,
|
||||
[](type_checker &, elaborate_fn const &, expr const & e, pos_info_provider const *) {
|
||||
// TODO(Leo): use elaborate_fn
|
||||
|
@ -433,6 +460,7 @@ void finalize_expr_to_tactic() {
|
|||
delete g_exact_tac_fn;
|
||||
delete g_apply_tactic_name;
|
||||
delete g_rename_tactic_name;
|
||||
delete g_intros_tactic_name;
|
||||
delete g_tactic_macros;
|
||||
delete g_map;
|
||||
delete g_tactic_name;
|
||||
|
|
|
@ -51,8 +51,10 @@ expr const & get_determ_tac_fn();
|
|||
expr mk_tactic_macro(name const & kind, unsigned num_args, expr const * args);
|
||||
expr mk_tactic_macro(name const & kind, expr const & e);
|
||||
bool is_tactic_macro(expr const & e);
|
||||
|
||||
expr mk_apply_tactic_macro(expr const & e);
|
||||
expr mk_rename_tactic_macro(name const & from, name const & to);
|
||||
expr mk_intros_tactic_macro(buffer<name> const & ns);
|
||||
|
||||
/** \brief Exception used to report a problem when an expression is being converted into a tactic. */
|
||||
class expr_to_tactic_exception : public tactic_exception {
|
||||
|
|
76
src/library/tactic/intros_tactic.cpp
Normal file
76
src/library/tactic/intros_tactic.cpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
Copyright (c) 2014 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Leonardo de Moura
|
||||
*/
|
||||
#include "kernel/instantiate.h"
|
||||
#include "library/reducible.h"
|
||||
#include "library/tactic/tactic.h"
|
||||
|
||||
namespace lean {
|
||||
/** \brief Return a "user" name that is not used by any local constant in the given goal */
|
||||
static name get_unused_name(goal const & g, name const & prefix, unsigned & idx) {
|
||||
buffer<expr> locals;
|
||||
get_app_rev_args(g.get_meta(), locals);
|
||||
while (true) {
|
||||
bool used = false;
|
||||
name curr = prefix.append_after(idx);
|
||||
idx++;
|
||||
for (expr const & local : locals) {
|
||||
if (is_local(local) && local_pp_name(local) == curr) {
|
||||
used = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!used)
|
||||
return curr;
|
||||
}
|
||||
}
|
||||
|
||||
tactic intros_tactic(list<name> _ns, bool relax_main_opaque) {
|
||||
auto fn = [=](environment const & env, io_state const &, proof_state const & s) {
|
||||
list<name> ns = _ns;
|
||||
goals const & gs = s.get_goals();
|
||||
if (empty(gs))
|
||||
return optional<proof_state>();
|
||||
goal const & g = head(gs);
|
||||
name_generator ngen = s.get_ngen();
|
||||
auto tc = mk_type_checker(env, ngen.mk_child(), relax_main_opaque);
|
||||
expr t = g.get_type();
|
||||
expr m = g.get_meta();
|
||||
bool gen_names = empty(ns);
|
||||
unsigned nidx = 1;
|
||||
try {
|
||||
while (true) {
|
||||
if (!gen_names && is_nil(ns))
|
||||
break;
|
||||
if (!is_pi(t)) {
|
||||
if (!is_nil(ns)) {
|
||||
t = tc->ensure_pi(t).first;
|
||||
} else {
|
||||
expr new_t = tc->whnf(t).first;
|
||||
if (!is_pi(new_t))
|
||||
break;
|
||||
}
|
||||
}
|
||||
name new_name;
|
||||
if (!is_nil(ns)) {
|
||||
new_name = head(ns);
|
||||
ns = tail(ns);
|
||||
} else {
|
||||
new_name = get_unused_name(g, name("H"), nidx);
|
||||
}
|
||||
expr new_local = mk_local(ngen.next(), new_name, binding_domain(t), binding_info(t));
|
||||
t = instantiate(binding_body(t), new_local);
|
||||
m = mk_app(m, new_local);
|
||||
}
|
||||
goal new_g(m, t);
|
||||
return some(proof_state(s, goals(new_g, tail(gs)), ngen));
|
||||
} catch (exception &) {
|
||||
return optional<proof_state>();
|
||||
}
|
||||
};
|
||||
return tactic01(fn);
|
||||
}
|
||||
}
|
11
src/library/tactic/intros_tactic.h
Normal file
11
src/library/tactic/intros_tactic.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
Copyright (c) 2014 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Leonardo de Moura
|
||||
*/
|
||||
#pragma once
|
||||
#include "library/tactic/tactic.h"
|
||||
namespace lean {
|
||||
tactic intros_tactic(list<name> ns, bool relax_main_opaque = true);
|
||||
}
|
|
@ -24,6 +24,8 @@ public:
|
|||
proof_state(goals const & gs, substitution const & s, name_generator const & ngen, constraints const & postponed);
|
||||
proof_state(proof_state const & s, goals const & gs, substitution const & subst):
|
||||
proof_state(gs, subst, s.m_ngen, s.m_postponed) {}
|
||||
proof_state(proof_state const & s, goals const & gs, name_generator const & ngen):
|
||||
proof_state(gs, s.m_subst, ngen, s.m_postponed) {}
|
||||
proof_state(proof_state const & s, goals const & gs):
|
||||
proof_state(s, gs, s.m_subst) {}
|
||||
proof_state(proof_state const & s, name_generator const & ngen):
|
||||
|
|
33
tests/lean/run/intros.lean
Normal file
33
tests/lean/run/intros.lean
Normal file
|
@ -0,0 +1,33 @@
|
|||
import logic tools.tactic
|
||||
open tactic
|
||||
|
||||
theorem tst1 (a b : Prop) : a → b → b :=
|
||||
by intros Ha; intros Hb; apply Hb
|
||||
|
||||
theorem tst2 (a b : Prop) : a → b → a ∧ b :=
|
||||
by intros Ha; intros Hb; apply and.intro; apply Hb; apply Ha
|
||||
|
||||
theorem tst3 (a b : Prop) : a → b → a ∧ b :=
|
||||
begin
|
||||
intros Ha,
|
||||
intros Hb,
|
||||
apply and.intro,
|
||||
apply Hb,
|
||||
apply Ha
|
||||
end
|
||||
|
||||
theorem tst4 (a b : Prop) : a → b → a ∧ b :=
|
||||
begin
|
||||
intros Ha Hb,
|
||||
apply and.intro,
|
||||
apply Hb,
|
||||
apply Ha
|
||||
end
|
||||
|
||||
theorem tst5 (a b : Prop) : a → b → a ∧ b :=
|
||||
begin
|
||||
intros,
|
||||
apply and.intro,
|
||||
eassumption,
|
||||
eassumption
|
||||
end
|
Loading…
Reference in a new issue