feat(library/tactic): add focus tactical

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2013-11-30 11:28:10 -08:00
parent 7ff791eb9f
commit 1a221d8bbe
8 changed files with 193 additions and 18 deletions

View file

@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura Author: Leonardo de Moura
*/ */
#include "util/sstream.h"
#include "util/script_state.h" #include "util/script_state.h"
#include "util/luaref.h" #include "util/luaref.h"
#include "library/kernel_bindings.h" #include "library/kernel_bindings.h"
@ -14,6 +15,15 @@ namespace lean {
cex_builder & cex_builder::operator=(cex_builder const & s) { LEAN_COPY_REF(cex_builder, s); } cex_builder & cex_builder::operator=(cex_builder const & s) { LEAN_COPY_REF(cex_builder, s); }
cex_builder & cex_builder::operator=(cex_builder && s) { LEAN_MOVE_REF(cex_builder, s); } cex_builder & cex_builder::operator=(cex_builder && s) { LEAN_MOVE_REF(cex_builder, s); }
cex_builder mk_cex_builder_for(name const & gname) {
return mk_cex_builder(
[=](name const & n, optional<counterexample> const & cex, assignment const &) -> counterexample {
if (n != gname || !cex)
throw exception(sstream() << "failed to build counterexample for '" << gname << "' goal");
return *cex;
});
}
DECL_UDATA(cex_builder) DECL_UDATA(cex_builder)
static int mk_cex_builder(lua_State * L) { static int mk_cex_builder(lua_State * L) {

View file

@ -68,6 +68,11 @@ cex_builder mk_cex_builder(F && f) {
return cex_builder(new cex_builder_tpl<F>(std::forward<F>(f))); return cex_builder(new cex_builder_tpl<F>(std::forward<F>(f)));
} }
/**
\brief Return a counterexample builder that expects a counterexample for the given goal.
*/
cex_builder mk_cex_builder_for(name const & gname);
UDATA_DEFS(cex_builder) UDATA_DEFS(cex_builder)
void open_cex_builder(lua_State * L); void open_cex_builder(lua_State * L);
} }

View file

@ -51,6 +51,12 @@ bool proof_state::is_cex_final_state() const {
} }
} }
void proof_state::get_goal_names(name_set & r) const {
for (auto const & p : get_goals()) {
r.insert(p.first);
}
}
static name g_main("main"); static name g_main("main");
proof_state to_proof_state(environment const & env, context const & ctx, expr const & t) { proof_state to_proof_state(environment const & env, context const & ctx, expr const & t) {
@ -61,12 +67,7 @@ proof_state to_proof_state(environment const & env, context const & ctx, expr co
[=](proof_map const & m, assignment const &) -> expr { [=](proof_map const & m, assignment const &) -> expr {
return fn(find(m, g_main)); return fn(find(m, g_main));
}); });
cex_builder cex_builder = mk_cex_builder( cex_builder cex_builder = mk_cex_builder_for(g_main);
[](name const & n, optional<counterexample> const & cex, assignment const &) -> counterexample {
if (n != g_main || !cex)
throw exception(sstream() << "failed to build counterexample for '" << g_main << "' goal");
return *cex;
});
return proof_state(goals(mk_pair(g_main, g)), metavar_env(), pr_builder, cex_builder); return proof_state(goals(mk_pair(g_main, g)), metavar_env(), pr_builder, cex_builder);
} }

View file

@ -11,6 +11,7 @@ Author: Leonardo de Moura
#include "util/rc.h" #include "util/rc.h"
#include "util/interrupt.h" #include "util/interrupt.h"
#include "util/optional.h" #include "util/optional.h"
#include "util/name_set.h"
#include "library/io_state.h" #include "library/io_state.h"
#include "library/tactic/goal.h" #include "library/tactic/goal.h"
#include "library/tactic/proof_builder.h" #include "library/tactic/proof_builder.h"
@ -52,8 +53,14 @@ public:
proof_state(proof_state && s):m_ptr(s.m_ptr) { s.m_ptr = nullptr; } proof_state(proof_state && s):m_ptr(s.m_ptr) { s.m_ptr = nullptr; }
proof_state(goals const & gs, metavar_env const & menv, proof_builder const & p, cex_builder const & c): proof_state(goals const & gs, metavar_env const & menv, proof_builder const & p, cex_builder const & c):
m_ptr(new cell(gs, menv, p, c)) {} m_ptr(new cell(gs, menv, p, c)) {}
proof_state(precision prec, goals const & gs, metavar_env const & menv, proof_builder const & p, cex_builder const & c):
m_ptr(new cell(prec, gs, menv, p, c)) {}
proof_state(proof_state const & s, goals const & gs, proof_builder const & p): proof_state(proof_state const & s, goals const & gs, proof_builder const & p):
m_ptr(new cell(s.get_precision(), gs, s.get_menv(), p, s.get_cex_builder())) {} m_ptr(new cell(s.get_precision(), gs, s.get_menv(), p, s.get_cex_builder())) {}
proof_state(proof_state const & s, goals const & gs):
m_ptr(new cell(s.get_precision(), gs, s.get_menv(), s.get_proof_builder(), s.get_cex_builder())) {}
proof_state(proof_state const & s, goals const & gs, proof_builder const & p, cex_builder const & c):
m_ptr(new cell(s.get_precision(), gs, s.get_menv(), p, c)) {}
~proof_state() { if (m_ptr) m_ptr->dec_ref(); } ~proof_state() { if (m_ptr) m_ptr->dec_ref(); }
friend void swap(proof_state & a, proof_state & b) { std::swap(a.m_ptr, b.m_ptr); } friend void swap(proof_state & a, proof_state & b) { std::swap(a.m_ptr, b.m_ptr); }
proof_state & operator=(proof_state const & s) { LEAN_COPY_REF(proof_state, s); } proof_state & operator=(proof_state const & s) { LEAN_COPY_REF(proof_state, s); }
@ -73,6 +80,10 @@ public:
and the precision is \c Precise or \c Under and the precision is \c Precise or \c Under
*/ */
bool is_cex_final_state() const; bool is_cex_final_state() const;
/**
\brief Store in \c r the goal names
*/
void get_goal_names(name_set & r) const;
format pp(formatter const & fmt, options const & opts) const; format pp(formatter const & fmt, options const & opts) const;
}; };

View file

@ -64,6 +64,36 @@ tactic & tactic::operator=(tactic && s) {
LEAN_MOVE_REF(tactic, s); LEAN_MOVE_REF(tactic, s);
} }
/**
\brief Try to extract a proof from the given proof state
*/
optional<expr> to_proof(proof_state const & s) {
if (s.is_proof_final_state()) {
try {
assignment a(s.get_menv());
proof_map m;
return some(s.get_proof_builder()(m, a));
} catch (...) {
}
}
return optional<expr>();
}
/**
\brief Try to extract a counterexample from the given proof state.
*/
optional<counterexample> to_counterexample(proof_state const & s) {
if (s.is_cex_final_state()) {
assignment a(s.get_menv());
name goal_name(head(s.get_goals()).first);
try {
return some(s.get_cex_builder()(goal_name, optional<counterexample>(), a));
} catch (...) {
}
}
return optional<counterexample>();
}
solve_result tactic::solve(environment const & env, io_state const & io, proof_state const & s1) { solve_result tactic::solve(environment const & env, io_state const & io, proof_state const & s1) {
proof_state_seq r = operator()(env, io, s1); proof_state_seq r = operator()(env, io, s1);
list<proof_state> failures; list<proof_state> failures;
@ -75,17 +105,12 @@ solve_result tactic::solve(environment const & env, io_state const & io, proof_s
} else { } else {
proof_state s2 = p->first; proof_state s2 = p->first;
r = p->second; r = p->second;
try { optional<expr> pr = to_proof(s2);
if (s2.is_proof_final_state()) { if (pr)
assignment a(s2.get_menv()); return solve_result(*pr);
proof_map m; optional<counterexample> cex = to_counterexample(s2);
return solve_result(s2.get_proof_builder()(m, a)); if (cex)
} else if (s2.is_cex_final_state()) { return solve_result(*cex);
assignment a(s2.get_menv());
name goal_name(head(s2.get_goals()).first);
return solve_result(s2.get_cex_builder()(goal_name, optional<counterexample>(), a));
}
} catch (exception & ex) {}
failures = cons(s2, failures); failures = cons(s2, failures);
} }
} }
@ -137,7 +162,7 @@ tactic trace_state_tactic() {
return mk_tactic1([=](environment const &, io_state const & io, proof_state const & s) -> proof_state { return mk_tactic1([=](environment const &, io_state const & io, proof_state const & s) -> proof_state {
options opts = io.get_options(); options opts = io.get_options();
format fmt = s.pp(io.get_formatter(), opts); format fmt = s.pp(io.get_formatter(), opts);
io.get_diagnostic_channel() << mk_pair(fmt, opts) << "\n"; io.get_diagnostic_channel() << "Proof state:\n" << mk_pair(fmt, opts) << "\n";
io.get_diagnostic_channel().get_stream().flush(); io.get_diagnostic_channel().get_stream().flush();
return s; return s;
}); });
@ -258,6 +283,81 @@ tactic take(tactic const & t, unsigned k) {
}); });
} }
proof_state_seq focus_core(tactic const & t, name const & gname, environment const & env, io_state const & io, proof_state const & s) {
for (auto const & p : s.get_goals()) {
if (p.first == gname) {
proof_builder pb = mk_proof_builder(
[=](proof_map const & m, assignment const &) -> expr {
return find(m, gname);
});
cex_builder cb = mk_cex_builder_for(gname);
proof_state new_s(s, goals(p), pb, cb); // new state with singleton goal
return map(t(env, io, new_s), [=](proof_state const & s2) {
// we have to put back the goals that were not selected
list<std::pair<name, name>> renamed_goals;
goals new_gs = map_append(s.get_goals(), [&](std::pair<name, goal> const & p) {
if (p.first == gname) {
name_set used_names;
s.get_goal_names(used_names);
used_names.erase(gname);
return map(s2.get_goals(), [&](std::pair<name, goal> const & p2) -> std::pair<name, goal> {
name uname = mk_unique(used_names, p2.first);
used_names.insert(uname);
renamed_goals.emplace_front(p2.first, uname);
return mk_pair(uname, p2.second);
});
} else {
return goals(p);
}
});
proof_builder pb1 = s.get_proof_builder();
proof_builder pb2 = s2.get_proof_builder();
proof_builder new_pb = mk_proof_builder(
[=](proof_map const & m, assignment const & a) -> expr {
proof_map m1(m); // map for pb1
proof_map m2; // map for pb2
for (auto p : renamed_goals) {
m2.insert(p.first, find(m, p.second));
m1.erase(p.first);
}
m1.insert(gname, pb2(m2, a));
return pb1(m1, a);
});
cex_builder cb1 = s.get_cex_builder();
cex_builder cb2 = s2.get_cex_builder();
cex_builder new_cb = mk_cex_builder(
[=](name const & n, optional<counterexample> const & cex, assignment const & a) -> counterexample {
for (auto p : renamed_goals) {
if (p.second == n)
return cb2(p.first, cex, a);
}
return cb1(n, cex, a);
});
return proof_state(s2, new_gs, new_pb, new_cb);
});
}
}
return proof_state_seq(); // tactic is not applicable
}
tactic focus(tactic const & t, name const & gname) {
return mk_tactic([=](environment const & env, io_state const & io, proof_state const & s) -> proof_state_seq {
return focus_core(t, gname, env, io, s);
});
}
tactic focus(tactic const & t, int i) {
return mk_tactic([=](environment const & env, io_state const & io, proof_state const & s) -> proof_state_seq {
int j = 1;
for (auto const & p : s.get_goals()) {
if (i == j)
return focus_core(t, p.first, env, io, s);
j++;
}
return proof_state_seq();
});
}
DECL_UDATA(proof_state_seq) DECL_UDATA(proof_state_seq)
static const struct luaL_Reg proof_state_seq_m[] = { static const struct luaL_Reg proof_state_seq_m[] = {
@ -382,6 +482,16 @@ static int tactic_try_for(lua_State * L) { return push_tactic(L, try_for
static int tactic_using_params(lua_State * L) { return push_tactic(L, using_params(to_tactic_ext(L, 1), to_options(L, 2))); } static int tactic_using_params(lua_State * L) { return push_tactic(L, using_params(to_tactic_ext(L, 1), to_options(L, 2))); }
static int tactic_try(lua_State * L) { return push_tactic(L, orelse(to_tactic_ext(L, 1), id_tactic())); } static int tactic_try(lua_State * L) { return push_tactic(L, orelse(to_tactic_ext(L, 1), id_tactic())); }
static int tactic_focus(lua_State * L) {
int nargs = lua_gettop(L);
if (nargs == 1)
return push_tactic(L, focus(to_tactic_ext(L, 1)));
else if (lua_isnumber(L, 2))
return push_tactic(L, focus(to_tactic_ext(L, 1), lua_tointeger(L, 2)));
else
return push_tactic(L, focus(to_tactic_ext(L, 1), to_name_ext(L, 2)));
}
static int push_solve_result(lua_State * L, solve_result const & r) { static int push_solve_result(lua_State * L, solve_result const & r) {
switch (r.kind()) { switch (r.kind()) {
case solve_result_kind::None: lua_pushnil(L); break; case solve_result_kind::None: lua_pushnil(L); break;
@ -544,6 +654,7 @@ static const struct luaL_Reg tactic_m[] = {
{"try_for", safe_function<tactic_try_for>}, {"try_for", safe_function<tactic_try_for>},
{"using_params", safe_function<tactic_using_params>}, {"using_params", safe_function<tactic_using_params>},
{"using", safe_function<tactic_using_params>}, {"using", safe_function<tactic_using_params>},
{"focus", safe_function<tactic_focus>},
{0, 0} {0, 0}
}; };
@ -591,5 +702,6 @@ void open_tactic(lua_State * L) {
SET_GLOBAL_FUN(tactic_using_params, "USING"); SET_GLOBAL_FUN(tactic_using_params, "USING");
SET_GLOBAL_FUN(tactic_using_params, "USING_PARAMS"); SET_GLOBAL_FUN(tactic_using_params, "USING_PARAMS");
SET_GLOBAL_FUN(tactic_determ, "DETERM"); SET_GLOBAL_FUN(tactic_determ, "DETERM");
SET_GLOBAL_FUN(tactic_focus, "FOCUS");
} }
} }

View file

@ -276,6 +276,17 @@ tactic cond(P && p, tactic const & t1, tactic const & t2) {
*/ */
template<typename P> template<typename P>
tactic when(P && p, tactic const & t) { return cond(std::forward<P>(p), t, id_tactic()); } tactic when(P && p, tactic const & t) { return cond(std::forward<P>(p), t, id_tactic()); }
/**
\brief Return a tactic that applies \c t only to the goal named \c gname.
The tactic fails if the input state does not have a goal named \c gname.
*/
tactic focus(tactic const & t, name const & gname);
/**
\brief Return a tactic that applies \c t only to the i-th goal.
The tactic fails if the input state does have at least i goals.
*/
tactic focus(tactic const & t, int i);
inline tactic focus(tactic const & t) { return focus(t, 1); }
UDATA_DEFS_CORE(proof_state_seq) UDATA_DEFS_CORE(proof_state_seq)
UDATA_DEFS(tactic); UDATA_DEFS(tactic);

21
tests/lean/tactic6.lean Normal file
View file

@ -0,0 +1,21 @@
Theorem T (a b c : Bool): a => b /\ c => c /\ a /\ b := _.
apply imp_tactic
apply imp_tactic
apply conj_hyp_tactic
apply conj_tactic
apply (** FOCUS(THEN(show_tactic, conj_tactic, show_tactic, assumption_tactic), 2) **)
apply assumption_tactic
done
Theorem T2 (a b c : Bool): a => b /\ c => c /\ a /\ b := _.
apply imp_tactic
apply imp_tactic
apply conj_hyp_tactic
apply conj_tactic
apply show_tactic
apply (** FOCUS(THEN(show_tactic, conj_tactic, FOCUS(assumption_tactic, 1)), 2) **)
apply show_tactic
apply (** FOCUS(assumption_tactic, 1) **)
apply show_tactic
apply (** FOCUS(assumption_tactic, 1) **)
done

View file

@ -0,0 +1,4 @@
Set: pp::colors
Set: pp::unicode
Proved: T
Proved: T2