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)
|
(,(rx "\[priority" (zero-or-more (not (any "\]"))) "\]") . font-lock-doc-face)
|
||||||
;; tactics
|
;; tactics
|
||||||
(,(rx word-start
|
(,(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")
|
"back" "beta" "done" "exact" "repeat")
|
||||||
word-end)
|
word-end)
|
||||||
. 'font-lock-constant-face)
|
. '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);
|
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) {
|
void init_nud_tactic_table(parse_table & r) {
|
||||||
expr x0 = mk_var(0);
|
expr x0 = mk_var(0);
|
||||||
r = r.add({transition("apply", mk_ext_action(parse_apply))}, x0);
|
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("rename", mk_ext_action(parse_rename))}, x0);
|
||||||
|
r = r.add({transition("intros", mk_ext_action(parse_intros))}, x0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void initialize_builtin_tactics() {
|
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}, {"::", 0}, {"calc", 0}, {":=", 0}, {"--", 0}, {"#", 0},
|
||||||
{"(*", 0}, {"/-", 0}, {"begin", g_max_prec}, {"proof", g_max_prec}, {"qed", 0}, {"@", g_max_prec},
|
{"(*", 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},
|
{"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[] =
|
char const * commands[] =
|
||||||
{"theorem", "axiom", "variable", "protected", "private", "opaque", "definition", "coercion",
|
{"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
|
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})
|
target_link_libraries(tactic ${LEAN_LIBS})
|
||||||
|
|
|
@ -16,6 +16,7 @@ Author: Leonardo de Moura
|
||||||
#include "library/kernel_serializer.h"
|
#include "library/kernel_serializer.h"
|
||||||
#include "library/tactic/expr_to_tactic.h"
|
#include "library/tactic/expr_to_tactic.h"
|
||||||
#include "library/tactic/apply_tactic.h"
|
#include "library/tactic/apply_tactic.h"
|
||||||
|
#include "library/tactic/intros_tactic.h"
|
||||||
|
|
||||||
namespace lean {
|
namespace lean {
|
||||||
static expr * g_exact_tac_fn = nullptr;
|
static expr * g_exact_tac_fn = nullptr;
|
||||||
|
@ -80,6 +81,13 @@ public:
|
||||||
m_name(n), m_fn(fn) {}
|
m_name(n), m_fn(fn) {}
|
||||||
name const & get_tatic_kind() const { return m_name; }
|
name const & get_tatic_kind() const { return m_name; }
|
||||||
expr_to_tactic_fn const & get_fn() const { return m_fn; }
|
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 name get_name() const { return get_tactic_name(); }
|
||||||
virtual format pp(formatter const &) const { return format(m_name); }
|
virtual format pp(formatter const &) const { return format(m_name); }
|
||||||
virtual void display(std::ostream & out) const { out << 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_apply_tactic_name = nullptr;
|
||||||
static name * g_rename_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) {
|
expr mk_apply_tactic_macro(expr const & e) {
|
||||||
return mk_tactic_macro(*g_apply_tactic_name, 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);
|
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) {
|
expr_to_tactic_fn const & get_tactic_macro_fn(expr const & e) {
|
||||||
lean_assert(is_tactic_macro(e));
|
lean_assert(is_tactic_macro(e));
|
||||||
return static_cast<tactic_macro_definition_cell const*>(macro_def(e).raw())->get_fn();
|
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) {
|
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);
|
flet<bool> let(g_unfold_tactic_macros, false);
|
||||||
type_checker tc(env, next_name_generator());
|
type_checker tc(env, next_name_generator());
|
||||||
return expr_to_tactic(tc, fn, e, p);
|
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_apply_tactic_name = new name(*g_tactic_name, "apply");
|
||||||
g_rename_tactic_name = new name(*g_tactic_name, "rename");
|
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 builtin_tac_name(*g_tactic_name, "builtin");
|
||||||
name exact_tac_name(*g_tactic_name, "exact");
|
name exact_tac_name(*g_tactic_name, "exact");
|
||||||
|
@ -380,7 +397,6 @@ void initialize_expr_to_tactic() {
|
||||||
});
|
});
|
||||||
register_tacm(*g_apply_tactic_name,
|
register_tacm(*g_apply_tactic_name,
|
||||||
[](type_checker &, elaborate_fn const & fn, expr const & e, pos_info_provider const *) {
|
[](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");
|
check_macro_args(e, 1, "invalid 'apply' tactic, it must have one argument");
|
||||||
return apply_tactic(fn, macro_arg(e, 0));
|
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");
|
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)));
|
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,
|
register_tac(exact_tac_name,
|
||||||
[](type_checker &, elaborate_fn const &, expr const & e, pos_info_provider const *) {
|
[](type_checker &, elaborate_fn const &, expr const & e, pos_info_provider const *) {
|
||||||
// TODO(Leo): use elaborate_fn
|
// TODO(Leo): use elaborate_fn
|
||||||
|
@ -433,6 +460,7 @@ void finalize_expr_to_tactic() {
|
||||||
delete g_exact_tac_fn;
|
delete g_exact_tac_fn;
|
||||||
delete g_apply_tactic_name;
|
delete g_apply_tactic_name;
|
||||||
delete g_rename_tactic_name;
|
delete g_rename_tactic_name;
|
||||||
|
delete g_intros_tactic_name;
|
||||||
delete g_tactic_macros;
|
delete g_tactic_macros;
|
||||||
delete g_map;
|
delete g_map;
|
||||||
delete g_tactic_name;
|
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, unsigned num_args, expr const * args);
|
||||||
expr mk_tactic_macro(name const & kind, expr const & e);
|
expr mk_tactic_macro(name const & kind, expr const & e);
|
||||||
bool is_tactic_macro(expr const & e);
|
bool is_tactic_macro(expr const & e);
|
||||||
|
|
||||||
expr mk_apply_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_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. */
|
/** \brief Exception used to report a problem when an expression is being converted into a tactic. */
|
||||||
class expr_to_tactic_exception : public tactic_exception {
|
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(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(proof_state const & s, goals const & gs, substitution const & subst):
|
||||||
proof_state(gs, subst, s.m_ngen, s.m_postponed) {}
|
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(proof_state const & s, goals const & gs):
|
||||||
proof_state(s, gs, s.m_subst) {}
|
proof_state(s, gs, s.m_subst) {}
|
||||||
proof_state(proof_state const & s, name_generator const & ngen):
|
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