2013-11-21 01:02:41 +00:00
|
|
|
/*
|
|
|
|
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
|
|
|
Released under Apache 2.0 license as described in the file LICENSE.
|
|
|
|
|
|
|
|
Author: Leonardo de Moura
|
|
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <algorithm>
|
2013-11-22 02:39:33 +00:00
|
|
|
#include <utility>
|
2013-11-21 18:44:53 +00:00
|
|
|
#include <memory>
|
2013-11-23 23:33:25 +00:00
|
|
|
#include <string>
|
2013-12-09 22:56:48 +00:00
|
|
|
#include "util/thread.h"
|
2013-11-22 02:39:33 +00:00
|
|
|
#include "util/lazy_list.h"
|
2013-12-30 19:20:23 +00:00
|
|
|
#include "kernel/io_state.h"
|
2013-11-21 01:02:41 +00:00
|
|
|
#include "library/tactic/proof_state.h"
|
|
|
|
|
|
|
|
namespace lean {
|
2013-11-22 02:39:33 +00:00
|
|
|
typedef lazy_list<proof_state> proof_state_seq;
|
2013-11-21 18:44:53 +00:00
|
|
|
|
2013-11-21 01:02:41 +00:00
|
|
|
class tactic_cell {
|
|
|
|
void dealloc() { delete this; }
|
|
|
|
MK_LEAN_RC();
|
|
|
|
public:
|
2013-11-22 02:39:33 +00:00
|
|
|
tactic_cell():m_rc(0) {}
|
|
|
|
virtual ~tactic_cell() {}
|
2013-12-13 00:33:31 +00:00
|
|
|
virtual proof_state_seq operator()(ro_environment const & env, io_state const & io, proof_state const & s) const = 0;
|
2013-11-21 01:02:41 +00:00
|
|
|
};
|
|
|
|
|
2013-11-25 18:39:40 +00:00
|
|
|
template<typename F>
|
|
|
|
class tactic_cell_tpl : public tactic_cell {
|
|
|
|
F m_f;
|
|
|
|
public:
|
|
|
|
tactic_cell_tpl(F && f):m_f(f) {}
|
2013-12-13 00:33:31 +00:00
|
|
|
virtual proof_state_seq operator()(ro_environment const & env, io_state const & io, proof_state const & s) const {
|
2013-11-25 18:39:40 +00:00
|
|
|
return m_f(env, io, s);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-11-28 01:47:29 +00:00
|
|
|
enum class solve_result_kind { None, Proof, Counterexample, Failure };
|
2013-11-25 21:04:09 +00:00
|
|
|
/**
|
|
|
|
\brief Result for the solve method in the tactic class.
|
|
|
|
The result may be a proof, a counterexample, or a list of unsolved proof_states.
|
|
|
|
*/
|
|
|
|
class solve_result {
|
|
|
|
solve_result_kind m_kind;
|
|
|
|
union {
|
|
|
|
expr m_proof;
|
|
|
|
counterexample m_cex;
|
|
|
|
list<proof_state> m_failures;
|
|
|
|
};
|
2013-11-28 01:47:29 +00:00
|
|
|
void init(solve_result const & r);
|
|
|
|
void destroy();
|
2013-11-25 21:04:09 +00:00
|
|
|
public:
|
2013-11-28 01:47:29 +00:00
|
|
|
solve_result():m_kind(solve_result_kind::None) {}
|
2013-11-25 21:04:09 +00:00
|
|
|
solve_result(expr const & pr);
|
|
|
|
solve_result(counterexample const & cex);
|
|
|
|
solve_result(list<proof_state> const & fs);
|
|
|
|
solve_result(solve_result const & r);
|
|
|
|
~solve_result();
|
2013-11-28 01:47:29 +00:00
|
|
|
solve_result & operator=(solve_result & other);
|
|
|
|
solve_result & operator=(solve_result && other);
|
2013-11-25 21:04:09 +00:00
|
|
|
solve_result_kind kind() const { return m_kind; }
|
|
|
|
expr get_proof() const { lean_assert(kind() == solve_result_kind::Proof); return m_proof; }
|
|
|
|
counterexample get_cex() const { lean_assert(kind() == solve_result_kind::Counterexample); return m_cex; }
|
|
|
|
list<proof_state> get_failures() const { lean_assert(kind() == solve_result_kind::Failure); return m_failures; }
|
|
|
|
};
|
|
|
|
|
2013-11-21 01:02:41 +00:00
|
|
|
class tactic {
|
|
|
|
protected:
|
|
|
|
tactic_cell * m_ptr;
|
|
|
|
public:
|
|
|
|
explicit tactic(tactic_cell * ptr):m_ptr(ptr) { if (m_ptr) m_ptr->inc_ref(); }
|
|
|
|
tactic(tactic const & s):m_ptr(s.m_ptr) { if (m_ptr) m_ptr->inc_ref(); }
|
|
|
|
tactic(tactic && s):m_ptr(s.m_ptr) { s.m_ptr = nullptr; }
|
|
|
|
~tactic() { if (m_ptr) m_ptr->dec_ref(); }
|
|
|
|
friend void swap(tactic & a, tactic & b) { std::swap(a.m_ptr, b.m_ptr); }
|
2013-11-21 18:44:53 +00:00
|
|
|
tactic & operator=(tactic const & s);
|
|
|
|
tactic & operator=(tactic && s);
|
2013-11-21 01:02:41 +00:00
|
|
|
|
2013-12-13 00:33:31 +00:00
|
|
|
proof_state_seq operator()(ro_environment const & env, io_state const & io, proof_state const & s) const {
|
2013-11-29 18:35:14 +00:00
|
|
|
lean_assert(m_ptr);
|
|
|
|
return m_ptr->operator()(env, io, s);
|
|
|
|
}
|
2013-11-22 00:44:31 +00:00
|
|
|
|
2013-12-13 00:33:31 +00:00
|
|
|
solve_result solve(ro_environment const & env, io_state const & io, proof_state const & s);
|
|
|
|
solve_result solve(ro_environment const & env, io_state const & io, context const & ctx, expr const & t);
|
2013-11-21 01:02:41 +00:00
|
|
|
};
|
|
|
|
|
2013-12-08 23:00:16 +00:00
|
|
|
inline optional<tactic> none_tactic() { return optional<tactic>(); }
|
|
|
|
inline optional<tactic> some_tactic(tactic const & o) { return optional<tactic>(o); }
|
|
|
|
inline optional<tactic> some_tactic(tactic && o) { return optional<tactic>(std::forward<tactic>(o)); }
|
|
|
|
|
2013-11-23 00:15:03 +00:00
|
|
|
/**
|
|
|
|
\brief Create a tactic using the given functor.
|
|
|
|
The functor must contain the operator:
|
|
|
|
|
|
|
|
<code>
|
2013-12-13 00:33:31 +00:00
|
|
|
proof_state_seq operator()(ro_environment const & env, io_state const & io, proof_state const & s)
|
2013-11-23 00:15:03 +00:00
|
|
|
</code>
|
|
|
|
*/
|
2013-11-21 23:31:55 +00:00
|
|
|
template<typename F>
|
2013-11-25 18:39:40 +00:00
|
|
|
tactic mk_tactic(F && f) { return tactic(new tactic_cell_tpl<F>(std::forward<F>(f))); }
|
2013-11-21 23:31:55 +00:00
|
|
|
|
2013-11-24 01:45:01 +00:00
|
|
|
template<typename F>
|
|
|
|
inline proof_state_seq mk_proof_state_seq(F && f) {
|
|
|
|
return mk_lazy_list<proof_state>(std::forward<F>(f));
|
|
|
|
}
|
|
|
|
|
2013-11-23 23:53:45 +00:00
|
|
|
/**
|
2013-11-25 00:29:04 +00:00
|
|
|
\brief Create a tactic that produces exactly one output state.
|
2013-11-23 23:53:45 +00:00
|
|
|
|
2013-11-25 00:29:04 +00:00
|
|
|
The functor f must contain the method:
|
2013-11-23 23:53:45 +00:00
|
|
|
<code>
|
2013-12-13 00:33:31 +00:00
|
|
|
proof_state operator()(ro_environment const & env, io_state const & io, proof_state const & s)
|
2013-11-23 23:53:45 +00:00
|
|
|
</code>
|
|
|
|
|
|
|
|
\remark The functor is invoked on demand.
|
|
|
|
*/
|
|
|
|
template<typename F>
|
2013-11-25 00:29:04 +00:00
|
|
|
tactic mk_tactic1(F && f) {
|
2013-11-23 23:53:45 +00:00
|
|
|
return
|
2013-12-13 00:33:31 +00:00
|
|
|
mk_tactic([=](ro_environment const & env, io_state const & io, proof_state const & s) {
|
2013-11-24 01:45:01 +00:00
|
|
|
return mk_proof_state_seq([=]() { return some(mk_pair(f(env, io, s), proof_state_seq())); });
|
2013-11-23 23:53:45 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-11-25 00:29:04 +00:00
|
|
|
/**
|
|
|
|
\brief Create a tactic that produces at most one output state.
|
|
|
|
|
|
|
|
The functor f must contain the method:
|
|
|
|
<code>
|
2013-12-13 00:33:31 +00:00
|
|
|
optional<proof_state> operator()(ro_environment const & env, io_state const & io, proof_state const & s)
|
2013-11-25 00:29:04 +00:00
|
|
|
</code>
|
|
|
|
|
|
|
|
\remark The functor is invoked on demand.
|
|
|
|
*/
|
|
|
|
template<typename F>
|
|
|
|
tactic mk_tactic01(F && f) {
|
|
|
|
return
|
2013-12-13 00:33:31 +00:00
|
|
|
mk_tactic([=](ro_environment const & env, io_state const & io, proof_state const & s) {
|
2013-11-25 00:29:04 +00:00
|
|
|
return mk_proof_state_seq([=]() {
|
|
|
|
auto r = f(env, io, s);
|
|
|
|
if (r)
|
|
|
|
return some(mk_pair(*r, proof_state_seq()));
|
|
|
|
else
|
|
|
|
return proof_state_seq::maybe_pair();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-11-23 00:15:03 +00:00
|
|
|
/**
|
|
|
|
\brief Return a "do nothing" tactic (aka skip).
|
|
|
|
*/
|
2013-11-21 23:31:55 +00:00
|
|
|
tactic id_tactic();
|
2013-11-23 00:15:03 +00:00
|
|
|
/**
|
|
|
|
\brief Return a tactic the always fails.
|
|
|
|
*/
|
2013-11-21 23:31:55 +00:00
|
|
|
tactic fail_tactic();
|
2013-11-23 00:15:03 +00:00
|
|
|
/**
|
|
|
|
\brief Return a tactic that fails if there are unsolved goals.
|
|
|
|
*/
|
2013-11-21 23:31:55 +00:00
|
|
|
tactic now_tactic();
|
2013-11-23 00:15:03 +00:00
|
|
|
/**
|
|
|
|
\brief Return a tactic that solves any goal of the form <tt>..., H : A, ... |- A</tt>.
|
|
|
|
*/
|
2013-11-21 23:31:55 +00:00
|
|
|
tactic assumption_tactic();
|
2013-11-23 23:33:25 +00:00
|
|
|
/**
|
|
|
|
\brief Return a tactic that just returns the input state, and display the given message in the diagnostic channel.
|
|
|
|
*/
|
|
|
|
tactic trace_tactic(char const * msg);
|
|
|
|
class sstream;
|
|
|
|
tactic trace_tactic(sstream const & msg);
|
|
|
|
tactic trace_tactic(std::string const & msg);
|
2013-11-25 00:44:02 +00:00
|
|
|
/**
|
|
|
|
\brief Return a tactic that just displays the input state in the diagnostic channel.
|
|
|
|
*/
|
|
|
|
tactic trace_state_tactic();
|
2013-11-25 00:29:04 +00:00
|
|
|
/**
|
|
|
|
\brief Create a tactic that applies \c t, but suppressing diagnostic messages.
|
|
|
|
*/
|
|
|
|
tactic suppress_trace(tactic const & t);
|
2013-11-23 23:33:25 +00:00
|
|
|
|
2013-11-23 00:15:03 +00:00
|
|
|
/**
|
|
|
|
\brief Return a tactic that performs \c t1 followed by \c t2.
|
|
|
|
*/
|
2013-11-24 19:22:40 +00:00
|
|
|
tactic then(tactic const & t1, tactic const & t2);
|
|
|
|
inline tactic operator<<(tactic const & t1, tactic const & t2) { return then(t1, t2); }
|
2013-11-23 00:15:03 +00:00
|
|
|
/**
|
|
|
|
\brief Return a tactic that applies \c t1, and if \c t1 returns the empty sequence of states,
|
|
|
|
then applies \c t2.
|
|
|
|
*/
|
2013-11-24 19:22:40 +00:00
|
|
|
tactic orelse(tactic const & t1, tactic const & t2);
|
|
|
|
inline tactic operator||(tactic const & t1, tactic const & t2) { return orelse(t1, t2); }
|
2013-11-25 00:29:04 +00:00
|
|
|
/**
|
|
|
|
\brief Return a tactic that appies \c t, but using the additional set of options
|
|
|
|
\c opts.
|
|
|
|
*/
|
|
|
|
tactic using_params(tactic const & t, options const & opts);
|
2013-11-23 00:15:03 +00:00
|
|
|
/**
|
|
|
|
\brief Return a tactic that tries the tactic \c t for at most \c ms milliseconds.
|
|
|
|
If the tactic does not terminate in \c ms milliseconds, then the empty
|
|
|
|
sequence is returned.
|
|
|
|
|
|
|
|
\remark the tactic \c t is executed in a separate execution thread.
|
|
|
|
|
|
|
|
\remark \c check_ms is how often the main thread checks whether the child
|
|
|
|
thread finished.
|
|
|
|
*/
|
2013-11-24 19:22:40 +00:00
|
|
|
tactic try_for(tactic const & t, unsigned ms, unsigned check_ms = 1);
|
2013-11-23 00:15:03 +00:00
|
|
|
/**
|
|
|
|
\brief Execute both tactics and and combines their results.
|
|
|
|
The results produced by tactic \c t1 are listed before the ones
|
|
|
|
from tactic \c t2.
|
|
|
|
*/
|
2013-11-24 19:22:40 +00:00
|
|
|
tactic append(tactic const & t1, tactic const & t2);
|
|
|
|
inline tactic operator+(tactic const & t1, tactic const & t2) { return append(t1, t2); }
|
2013-11-23 00:15:03 +00:00
|
|
|
/**
|
|
|
|
\brief Execute both tactics and and combines their results.
|
|
|
|
The results produced by tactics \c t1 and \c t2 are interleaved
|
|
|
|
to guarantee fairness.
|
|
|
|
*/
|
2013-11-24 19:22:40 +00:00
|
|
|
tactic interleave(tactic const & t1, tactic const & t2);
|
2013-11-23 00:15:03 +00:00
|
|
|
/**
|
2013-11-24 19:22:40 +00:00
|
|
|
\brief Return a tactic that executes \c t1 and \c t2 in parallel.
|
|
|
|
This is similar to \c append and \c interleave. The order of
|
|
|
|
the elements in the output sequence is not deterministic.
|
|
|
|
It depends on how fast \c t1 and \c t2 produce their output.
|
2013-11-23 00:15:03 +00:00
|
|
|
|
|
|
|
\remark \c check_ms is how often the main thread checks whether the children
|
|
|
|
threads finished.
|
|
|
|
*/
|
2013-11-29 06:11:07 +00:00
|
|
|
tactic par(tactic const & t1, tactic const & t2, unsigned check_ms);
|
|
|
|
inline tactic par(tactic const & t1, tactic const & t2) { return par(t1, t2, 1); }
|
2013-11-28 01:47:29 +00:00
|
|
|
|
2013-11-23 00:39:25 +00:00
|
|
|
/**
|
|
|
|
\brief Return a tactic that keeps applying \c t until it fails.
|
|
|
|
*/
|
2013-11-24 19:22:40 +00:00
|
|
|
tactic repeat(tactic const & t);
|
2013-11-24 19:38:45 +00:00
|
|
|
/**
|
|
|
|
\brief Similar to \c repeat, but applies \c t at least once.
|
|
|
|
*/
|
|
|
|
inline tactic repeat1(tactic const & t) { return then(t, repeat(t)); }
|
2013-11-23 00:39:25 +00:00
|
|
|
/**
|
|
|
|
\brief Similar to \c repeat, but execute \c t at most \c k times.
|
2013-11-23 00:15:03 +00:00
|
|
|
|
2013-11-23 00:39:25 +00:00
|
|
|
\remark The value \c k is the depth of the recursion.
|
|
|
|
For example, if tactic \c t always produce a sequence of size 2,
|
|
|
|
then tactic \c t will be applied 2^k times.
|
|
|
|
*/
|
2013-11-24 19:22:40 +00:00
|
|
|
tactic repeat_at_most(tactic const & t, unsigned k);
|
2013-11-23 01:05:18 +00:00
|
|
|
/**
|
|
|
|
\brief Return a tactic that applies \c t, but takes at most \c
|
|
|
|
k elements from the sequence produced by \c t.
|
|
|
|
*/
|
2013-11-24 19:22:40 +00:00
|
|
|
tactic take(tactic const & t, unsigned k);
|
2013-11-24 19:38:45 +00:00
|
|
|
/**
|
|
|
|
\brief Syntax sugar for take(t, 1)
|
|
|
|
*/
|
|
|
|
inline tactic determ(tactic const & t) { return take(t, 1); }
|
2013-11-24 20:04:32 +00:00
|
|
|
/**
|
|
|
|
\brief Return a tactic that applies the predicate \c p to the input state.
|
|
|
|
If \c p returns true, then applies \c t1. Otherwise, applies \c t2.
|
|
|
|
*/
|
|
|
|
template<typename P>
|
|
|
|
tactic cond(P && p, tactic const & t1, tactic const & t2) {
|
2013-12-13 00:33:31 +00:00
|
|
|
return mk_tactic([=](ro_environment const & env, io_state const & io, proof_state const & s) -> proof_state_seq {
|
2013-11-24 20:04:32 +00:00
|
|
|
return mk_proof_state_seq([=]() {
|
|
|
|
if (p(env, io, s)) {
|
|
|
|
return t1(env, io, s).pull();
|
|
|
|
} else {
|
|
|
|
return t2(env, io, s).pull();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
\brief Syntax-sugar for cond(p, t, id_tactic())
|
|
|
|
*/
|
|
|
|
template<typename P>
|
|
|
|
tactic when(P && p, tactic const & t) { return cond(std::forward<P>(p), t, id_tactic()); }
|
2013-11-30 19:28:10 +00:00
|
|
|
/**
|
|
|
|
\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); }
|
2013-12-22 22:10:42 +00:00
|
|
|
/**
|
|
|
|
\brief Return a tactic that solves any goal which the hypothesis evaluates to true.
|
|
|
|
*/
|
|
|
|
tactic trivial_tactic();
|
2013-12-01 16:51:56 +00:00
|
|
|
/**
|
|
|
|
\brief Return a tactic that unfolds the definition named \c n.
|
|
|
|
*/
|
|
|
|
tactic unfold_tactic(name const & n);
|
2013-12-01 18:41:05 +00:00
|
|
|
/**
|
refactor(kernel): add unfold_opaque flag to normalizer, modify how type checker uses the opaque flag, remove hidden_defs, and mark most builtin definitions as opaque
After this commit, in the type checker, when checking convertability, we first compute a normal form without expanding opaque terms.
If the terms are convertible, then we are done, and saved a lot of time by not expanding unnecessary definitions.
If they are not, instead of throwing an error, we try again expanding the opaque terms.
This seems to be the best of both worlds.
The opaque flag is a hint for the type checker, but it would never prevent us from type checking a valid term.
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2013-12-20 20:47:36 +00:00
|
|
|
\brief Return a tactic that unfolds all (non-opaque) definitions.
|
2013-12-01 18:41:05 +00:00
|
|
|
*/
|
|
|
|
tactic unfold_tactic();
|
2013-12-02 16:10:51 +00:00
|
|
|
/**
|
|
|
|
\brief Return a tactic that applies beta-reduction.
|
|
|
|
*/
|
|
|
|
tactic beta_tactic();
|
2013-12-22 22:10:42 +00:00
|
|
|
/**
|
|
|
|
\brief Return a tactic that normalize the goal conclusion.
|
|
|
|
*/
|
|
|
|
tactic normalize_tactic(bool unfold_opaque = false, bool all = true);
|
2013-11-28 01:47:29 +00:00
|
|
|
|
|
|
|
UDATA_DEFS_CORE(proof_state_seq)
|
|
|
|
UDATA_DEFS(tactic);
|
|
|
|
void open_tactic(lua_State * L);
|
2013-11-21 20:34:37 +00:00
|
|
|
}
|