feat(library/tactic): add 'intros' tactic

This commit is contained in:
Leonardo de Moura 2014-10-20 15:26:16 -07:00
parent 5cba7244ce
commit 7d0100a340
10 changed files with 167 additions and 5 deletions

View file

@ -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)

View file

@ -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() {

View file

@ -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",

View file

@ -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})

View file

@ -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;

View file

@ -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 {

View 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);
}
}

View 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);
}

View file

@ -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):

View 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