lean4-htt/src/library/tactic/ac_tactics.cpp
Leonardo de Moura bf9f7560f7 feat(frontends/lean): (Type u) can't be a proposition
(Type u)  is the old (Type (u+1))
(PType u) is the old (Type u)
Type*     is the old (Type (_+1))
PType*    is the old Type*

The stdlib can be compiled, but we still have > 70 broken tests

See discussion at #1341
2017-01-30 11:54:00 -08:00

715 lines
24 KiB
C++

/*
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include <string>
#include <algorithm>
#include "library/trace.h"
#include "library/util.h"
#include "library/reducible.h"
#include "library/app_builder.h"
#include "library/class.h"
#include "library/constants.h"
#include "library/kernel_serializer.h"
#include "library/vm/vm_expr.h"
#include "library/tactic/tactic_state.h"
#include "library/tactic/ac_tactics.h"
namespace lean {
struct ac_manager::cache {
environment m_env;
unsigned m_reducibility_fingerprint;
unsigned m_instance_fingerprint;
expr_struct_map<optional<expr>> m_assoc_cache[2];
expr_struct_map<optional<expr>> m_comm_cache[2];
cache(environment const & env):
m_env(env),
m_reducibility_fingerprint(get_reducibility_fingerprint(env)),
m_instance_fingerprint(get_instance_fingerprint(env)) {}
};
MK_THREAD_LOCAL_GET_DEF(ac_manager::cache_ptr, get_cache_ptr);
static ac_manager::cache_ptr get_cache(environment const & env) {
auto & cache_ptr = get_cache_ptr();
if (!cache_ptr ||
!env.is_descendant(cache_ptr->m_env) ||
get_reducibility_fingerprint(env) != cache_ptr->m_reducibility_fingerprint ||
get_instance_fingerprint(env) != cache_ptr->m_instance_fingerprint) {
cache_ptr.reset();
return std::make_shared<ac_manager::cache>(env);
}
ac_manager::cache_ptr r = cache_ptr;
cache_ptr.reset();
r->m_env = env;
if (!is_eqp_declarations(env, r->m_env)) {
/* erase cache for expressions containing locals, since it is probably not useful. */
r->m_assoc_cache[1].clear();
r->m_comm_cache[1].clear();
}
return r;
}
static void recycle_cache(ac_manager::cache_ptr const & cache) {
get_cache_ptr() = cache;
}
ac_manager::ac_manager(type_context & ctx):
m_ctx(ctx),
m_cache_ptr(get_cache(ctx.env())) {
}
ac_manager::~ac_manager() {
recycle_cache(m_cache_ptr);
}
optional<expr> ac_manager::is_assoc(expr const & e) {
auto op = get_binary_op(e);
if (!op) return none_expr();
bool idx = has_local(e);
auto it = m_cache_ptr->m_assoc_cache[idx].find(*op);
if (it != m_cache_ptr->m_assoc_cache[idx].end())
return it->second;
optional<expr> r;
try {
expr assoc_class = mk_app(m_ctx, get_is_associative_name(), *op);
if (auto assoc_inst = m_ctx.mk_class_instance(assoc_class))
r = some_expr(mk_app(m_ctx, get_is_associative_assoc_name(), 3, *op, *assoc_inst));
} catch (app_builder_exception & ex) {}
m_cache_ptr->m_assoc_cache[idx].insert(mk_pair(*op, r));
return r;
}
optional<expr> ac_manager::is_comm(expr const & e) {
auto op = get_binary_op(e);
if (!op) return none_expr();
bool idx = has_local(e);
auto it = m_cache_ptr->m_comm_cache[idx].find(*op);
if (it != m_cache_ptr->m_comm_cache[idx].end())
return it->second;
optional<expr> r;
try {
expr comm_class = mk_app(m_ctx, get_is_commutative_name(), *op);
if (auto comm_inst = m_ctx.mk_class_instance(comm_class))
r = some_expr(mk_app(m_ctx, get_is_commutative_comm_name(), 3, *op, *comm_inst));
} catch (app_builder_exception & ex) {}
m_cache_ptr->m_comm_cache[idx].insert(mk_pair(*op, r));
return r;
}
struct flat_assoc_fn {
abstract_type_context & m_ctx;
expr m_op;
expr m_assoc;
flat_assoc_fn(abstract_type_context & ctx, expr const & op, expr const & assoc):
m_ctx(ctx), m_op(op), m_assoc(assoc) {}
bool is_op_app(expr const & e, expr & lhs, expr & rhs) {
if (!is_app(e)) return false;
expr const & fn1 = app_fn(e);
if (!is_app(fn1)) return false;
if (app_fn(fn1) != m_op) return false;
lhs = app_arg(fn1);
rhs = app_arg(e);
return true;
}
bool is_op_app(expr const & e) {
if (!is_app(e)) return false;
expr const & fn1 = app_fn(e);
if (!is_app(fn1)) return false;
return app_fn(fn1) == m_op;
}
expr mk_op(expr const & a, expr const & b) {
return mk_app(m_op, a, b);
}
expr mk_assoc(expr const & a, expr const & b, expr const & c) {
return mk_app(m_assoc, a, b, c);
}
expr mk_eq_refl(expr const & a) {
return ::lean::mk_eq_refl(m_ctx, a);
}
expr mk_eq_trans(expr const & H1, expr const & H2) {
return ::lean::mk_eq_trans(m_ctx, H1, H2);
}
expr mk_eq_trans(expr const & H1, optional<expr> const & H2) {
if (!H2) return H1;
return mk_eq_trans(H1, *H2);
}
optional<expr> mk_eq_trans(optional<expr> const & H1, optional<expr> const & H2) {
if (!H1) return H2;
if (!H2) return H1;
return some_expr(mk_eq_trans(*H1, *H2));
}
expr mk_eq_symm(expr const & H) {
return ::lean::mk_eq_symm(m_ctx, H);
}
optional<expr> mk_eq_symm(optional<expr> const & H) {
if (!H) return none_expr();
return some_expr(mk_eq_symm(*H));
}
expr mk_congr_arg(expr const & fn, expr const & H) {
return ::lean::mk_congr_arg(m_ctx, fn, H);
}
pair<expr, optional<expr>> flat_with(expr const & e, expr const & rest) {
expr lhs, rhs;
if (is_op_app(e, lhs, rhs)) {
auto p1 = flat_with(rhs, rest);
if (p1.second) {
auto p2 = flat_with(lhs, p1.first);
// H3 is a proof for (lhs `op` rhs) `op` rest = lhs `op` (rhs `op` rest)
expr H3 = mk_assoc(lhs, rhs, rest);
// H4 is a proof for lhs `op` (rhs `op` rest) = lhs `op` p1.first
expr H4 = mk_congr_arg(mk_app(m_op, lhs), *p1.second);
expr H = mk_eq_trans(mk_eq_trans(H3, H4), p2.second);
return mk_pair(p2.first, some_expr(H));
} else {
if (is_op_app(lhs)) {
auto p2 = flat_with(lhs, p1.first);
// H3 is a proof for (lhs `op` rhs) `op` rest = lhs `op` (rhs `op` rest)
expr H3 = mk_assoc(lhs, rhs, rest);
expr H = mk_eq_trans(H3, p2.second);
return mk_pair(p2.first, some_expr(H));
} else {
return mk_pair(mk_op(lhs, p1.first), some_expr(mk_assoc(lhs, rhs, rest)));
}
}
} else {
return mk_pair(mk_op(e, rest), none_expr());
}
}
pair<expr, optional<expr>> flat_core(expr const & e) {
expr lhs, rhs;
if (is_op_app(e, lhs, rhs)) {
auto p1 = flat_core(rhs);
if (p1.second) {
if (is_op_app(lhs)) {
auto p2 = flat_with(lhs, p1.first);
expr H3 = mk_congr_arg(mk_app(m_op, lhs), *p1.second);
expr H = mk_eq_trans(H3, p2.second);
return mk_pair(p2.first, some_expr(H));
} else {
expr r = mk_op(lhs, p1.first);
expr H = mk_congr_arg(mk_app(m_op, lhs), *p1.second);
return mk_pair(r, some_expr(H));
}
} else {
if (is_op_app(lhs)) {
return flat_with(lhs, rhs);
} else {
return mk_pair(e, none_expr());
}
}
} else {
return mk_pair(e, none_expr());
}
}
pair<expr, expr> flat(expr const & e) {
auto p = flat_core(e);
if (p.second) {
return mk_pair(p.first, *p.second);
} else {
return mk_pair(e, mk_eq_refl(e));
}
}
};
#define lean_perm_ac_trace(code) lean_trace(name({"tactic", "perm_ac"}), scope_trace_env _scope1(m_ctx.env(), m_ctx); code)
struct perm_ac_fn : public flat_assoc_fn {
expr m_comm;
optional<expr> m_left_comm;
perm_ac_fn(abstract_type_context & ctx, expr const & op, expr const & assoc, expr const & comm):
flat_assoc_fn(ctx, op, assoc), m_comm(comm) {
}
[[ noreturn ]] void throw_failed() {
throw exception("perm_ac failed, arguments are not equal modulo AC");
}
expr mk_comm(expr const & a, expr const & b) {
return mk_app(m_comm, a, b);
}
level dec_level(level const & l) {
if (auto r = ::lean::dec_level(l))
return *r;
throw_failed();
}
expr mk_left_comm(expr const & a, expr const & b, expr const & c) {
if (!m_left_comm) {
expr A = m_ctx.infer(a);
level lvl = dec_level(get_level(m_ctx, A));
m_left_comm = mk_app(mk_constant(get_left_comm_name(), {lvl}), A, m_op, m_comm, m_assoc);
}
return mk_app(*m_left_comm, a, b, c);
}
/* Given a term \c e of the form (op t_1 (op t_2 ... (op t_{n-1} t_n))), if
for some i, t_i == t, then produce the term
(op t_i (op t_2 ... (op t_{n-1} t_n)))
and a proof they are equal AC.
Throw exception if t is not found. */
pair<expr, expr> pull_term(expr const & t, expr const & e) {
expr lhs1, rhs1;
if (!is_op_app(e, lhs1, rhs1)) {
lean_perm_ac_trace(tout() << "right-hand-side does not contain:\n" << t << "\n";);
throw_failed();
}
if (t == rhs1) {
return mk_pair(mk_op(rhs1, lhs1), mk_comm(lhs1, rhs1));
}
expr lhs2, rhs2;
if (!is_op_app(rhs1, lhs2, rhs2)) {
lean_perm_ac_trace(tout() << "right-hand-side does not contain:\n" << t << "\n";);
throw_failed();
}
if (t == lhs2) {
return mk_pair(mk_op(lhs2, mk_op(lhs1, rhs2)), mk_left_comm(lhs1, lhs2, rhs2));
}
/* We have e := lhs1 `op` lhs2 `op` rhs2 */
auto p = pull_term(t, rhs1);
expr lhs3, rhs3;
lean_verify(is_op_app(p.first, lhs3, rhs3));
lean_assert(t == lhs3);
/* p.second : rhs1 = t `op` rhs3 */
expr H1 = mk_congr_arg(mk_app(m_op, lhs1), p.second);
/* H1 : lhs1 `op` rhs1 = lhs1 `op` t `op` rhs3 */
expr H2 = mk_left_comm(lhs1, t, rhs3);
/* H2 : lhs1 `op` t `op` rhs3 = t `op` lhs1 `op` rhs3 */
return mk_pair(mk_op(t, mk_op(lhs1, rhs3)), mk_eq_trans(H1, H2));
}
/* Return a proof that e1 == e2 modulo AC. Return none if reflexivity.
Throw exception if failure */
optional<expr> perm_flat(expr const & e1, expr const & e2) {
expr lhs1, rhs1;
expr lhs2, rhs2;
bool b1 = is_op_app(e1, lhs1, rhs1);
bool b2 = is_op_app(e2, lhs2, rhs2);
if (b1 != b2) {
lean_perm_ac_trace(tout() << "left and right-hand-sides have different number of terms\n";);
throw_failed();
}
if (!b1 && !b2) {
if (e1 == e2) {
return none_expr(); // reflexivity
} else {
lean_perm_ac_trace(tout() << "the left and right hand sides contain the terms:\n" << e1 << "\n" << e2 << "\n";);
throw_failed();
}
}
lean_assert(b1 && b2);
if (lhs1 == lhs2) {
optional<expr> H = perm_flat(rhs1, rhs2);
if (!H) return none_expr();
return some_expr(mk_congr_arg(mk_app(m_op, lhs1), *H));
} else {
auto p = pull_term(lhs2, e1);
is_op_app(p.first, lhs1, rhs1);
lean_assert(lhs1 == lhs2);
optional<expr> H1 = perm_flat(rhs1, rhs2);
if (!H1) return some_expr(p.second);
expr H2 = mk_congr_arg(mk_app(m_op, lhs1), *H1);
return some_expr(mk_eq_trans(p.second, H2));
}
}
/* Return a proof that lhs == rhs modulo AC. Return none if reflexivity.
Throw exception if failure */
optional<expr> perm_core(expr const & lhs, expr const & rhs) {
auto p1 = flat_core(lhs);
auto p2 = flat_core(rhs);
auto H = perm_flat(p1.first, p2.first);
return mk_eq_trans(p1.second, mk_eq_trans(H, mk_eq_symm(p2.second)));
}
expr perm(expr const & lhs, expr const & rhs) {
if (auto H = perm_core(lhs, rhs))
return *H;
else
return mk_eq_refl(lhs);
}
};
pair<expr, optional<expr>> flat_assoc(abstract_type_context & ctx, expr const & op, expr const & assoc, expr const & e) {
return flat_assoc_fn(ctx, op, assoc).flat_core(e);
}
expr perm_ac(abstract_type_context & ctx, expr const & op, expr const & assoc, expr const & comm, expr const & e1, expr const & e2) {
return perm_ac_fn(ctx, op, assoc, comm).perm(e1, e2);
}
static name * g_ac_app_name = nullptr;
static macro_definition * g_ac_app_macro = nullptr;
static std::string * g_ac_app_opcode = nullptr;
static expr expand_ac_core(expr const & e) {
unsigned nargs = macro_num_args(e);
unsigned i = nargs - 1;
expr const & op = macro_arg(e, i);
--i;
expr r = macro_arg(e, i);
while (i > 0) {
--i;
r = mk_app(op, macro_arg(e, i), r);
}
return r;
}
class ac_app_macro_cell : public macro_definition_cell {
public:
virtual name get_name() const { return *g_ac_app_name; }
virtual unsigned trust_level() const { return LEAN_AC_MACROS_TRUST_LEVEL; }
virtual expr check_type(expr const & e, abstract_type_context & ctx, bool) const {
return ctx.infer(macro_arg(e, 0));
}
virtual optional<expr> expand(expr const & e, abstract_type_context &) const {
return some_expr(expand_ac_core(e));
}
virtual void write(serializer & s) const {
s.write_string(*g_ac_app_opcode);
}
virtual bool operator==(macro_definition_cell const & other) const {
ac_app_macro_cell const * other_ptr = dynamic_cast<ac_app_macro_cell const *>(&other);
return other_ptr;
}
virtual unsigned hash() const { return 37; }
};
static expr mk_ac_app_core(unsigned nargs, expr const * args_op) {
lean_assert(nargs >= 3);
return mk_macro(*g_ac_app_macro, nargs, args_op);
}
static expr mk_ac_app_core(expr const & op, buffer<expr> & args) {
lean_assert(args.size() >= 2);
args.push_back(op);
expr r = mk_ac_app_core(args.size(), args.data());
args.pop_back();
return r;
}
expr mk_ac_app(expr const & op, buffer<expr> & args) {
lean_assert(args.size() > 0);
if (args.size() == 1) {
return args[0];
} else {
std::sort(args.begin(), args.end(), is_hash_lt);
return mk_ac_app_core(op, args);
}
}
bool is_ac_app(expr const & e) {
return is_macro(e) && is_eqp(macro_def(e), *g_ac_app_macro);
}
expr const & get_ac_app_op(expr const & e) {
lean_assert(is_ac_app(e));
return macro_arg(e, macro_num_args(e) - 1);
}
unsigned get_ac_app_num_args(expr const & e) {
lean_assert(is_ac_app(e));
return macro_num_args(e) - 1;
}
expr const * get_ac_app_args(expr const & e) {
lean_assert(is_ac_app(e));
return macro_args(e);
}
/* Return true iff e1 occurs in e2.
Example ac_mem(b, a*a*b*c) returns true. */
static bool ac_mem(expr const & e1, expr const & e2) {
unsigned nargs2 = get_ac_app_num_args(e2);
expr const * args2 = get_ac_app_args(e2);
return std::find(args2, args2+nargs2, e1) != args2+nargs2;
}
/* Return true iff e1 is a "subset" of e2.
Example: The result is true for e1 := (a*a*a*b*d) and e2 := (a*a*a*a*b*b*c*d*d) */
bool is_ac_subset(expr const & e1, expr const & e2) {
if (is_ac_app(e1)) {
if (is_ac_app(e2)) {
if (get_ac_app_op(e1) == get_ac_app_op(e2)) {
unsigned nargs1 = get_ac_app_num_args(e1);
unsigned nargs2 = get_ac_app_num_args(e2);
if (nargs1 > nargs2) return false;
expr const * args1 = get_ac_app_args(e1);
expr const * args2 = get_ac_app_args(e2);
unsigned i1 = 0;
unsigned i2 = 0;
while (i1 < nargs1 && i2 < nargs2) {
if (args1[i1] == args2[i2]) {
i1++;
i2++;
} else if (is_hash_lt(args2[i2], args1[i1])) {
i2++;
} else {
lean_assert(is_hash_lt(args1[i1], args2[i2]));
return false;
}
}
return i1 == nargs1;
} else {
lean_assert(get_ac_app_op(e1) != get_ac_app_op(e2));
/* treat e1 as an atomic value that may occur in e2 */
return ac_mem(e1, e2);
}
} else {
return false;
}
} else {
if (is_ac_app(e2)) {
return ac_mem(e1, e2);
} else {
return e1 == e2;
}
}
}
/* Store in r e1\e2.
Example: given e1 := (a*a*a*a*b*b*c*d*d*d) and e2 := (a*a*a*b*b*d),
the result is (a, c, d, d)
\pre is_ac_subset(e2, e1) */
void ac_diff(expr const & e1, expr const & e2, buffer<expr> & r) {
lean_assert(is_ac_subset(e2, e1));
if (is_ac_app(e1)) {
if (is_ac_app(e2) && get_ac_app_op(e1) == get_ac_app_op(e2)) {
unsigned nargs1 = get_ac_app_num_args(e1);
unsigned nargs2 = get_ac_app_num_args(e2);
lean_assert(nargs1 >= nargs2);
expr const * args1 = get_ac_app_args(e1);
expr const * args2 = get_ac_app_args(e2);
unsigned i2 = 0;
for (unsigned i1 = 0; i1 < nargs1; i1++) {
if (i2 == nargs2) {
r.push_back(args1[i1]);
} else if (args1[i1] == args2[i2]) {
i2++;
} else {
lean_assert(is_hash_lt(args1[i1], args2[i2]));
r.push_back(args1[i1]);
}
}
} else {
bool found = false;
unsigned nargs1 = get_ac_app_num_args(e1);
expr const * args1 = get_ac_app_args(e1);
for (unsigned i = 0; i < nargs1; i++) {
if (!found && args1[i] == e2) {
found = true;
} else {
r.push_back(args1[i]);
}
}
lean_assert(found);
}
} else {
lean_assert(!is_ac_app(e1));
lean_assert(!is_ac_app(e2));
lean_assert(e1 == e2);
}
}
void ac_append(expr const & op, expr const & e, buffer<expr> & r) {
if (is_ac_app(e) && get_ac_app_op(e) == op) {
r.append(get_ac_app_num_args(e), get_ac_app_args(e));
} else {
r.push_back(e);
}
}
void ac_intersection(expr const & e1, expr const & e2, buffer<expr> & r) {
lean_assert(is_ac_app(e1));
lean_assert(is_ac_app(e2));
lean_assert(get_ac_app_op(e1) == get_ac_app_op(e2));
unsigned nargs1 = get_ac_app_num_args(e1);
unsigned nargs2 = get_ac_app_num_args(e2);
expr const * args1 = get_ac_app_args(e1);
expr const * args2 = get_ac_app_args(e2);
unsigned i1 = 0;
unsigned i2 = 0;
while (i1 < nargs1 && i2 < nargs2) {
if (args1[i1] == args2[i2]) {
r.push_back(args1[i1]);
i1++;
i2++;
} else if (is_hash_lt(args2[i2], args1[i1])) {
i2++;
} else {
lean_assert(is_hash_lt(args1[i1], args2[i2]));
i1++;
}
}
}
expr mk_ac_flat_app(expr const & op, expr const & e1, expr const & e2) {
buffer<expr> new_args;
ac_append(op, e1, new_args);
ac_append(op, e2, new_args);
return mk_ac_app(op, new_args);
}
/* lexdeg order */
bool ac_lt(expr const & e1, expr const & e2) {
if (is_ac_app(e1)) {
if (is_ac_app(e2) && get_ac_app_op(e1) == get_ac_app_op(e2)) {
unsigned nargs1 = get_ac_app_num_args(e1);
unsigned nargs2 = get_ac_app_num_args(e2);
if (nargs1 < nargs2) return true;
if (nargs1 > nargs2) return false;
expr const * args1 = get_ac_app_args(e1);
expr const * args2 = get_ac_app_args(e2);
for (unsigned i = 0; i < nargs1; i++) {
if (args1[i] != args2[i])
return is_hash_lt(args1[i], args2[i]);
}
return false;
} else {
return false;
}
} else {
if (is_ac_app(e2)) {
return true;
} else {
return is_hash_lt(e1, e2);
}
}
}
static expr expand_if_ac_app(expr const & e) {
if (is_ac_app(e))
return expand_ac_core(e);
else
return e;
}
static name * g_perm_ac_name = nullptr;
static macro_definition * g_perm_ac_macro = nullptr;
static std::string * g_perm_ac_opcode = nullptr;
class perm_ac_macro_cell : public macro_definition_cell {
public:
virtual name get_name() const { return *g_perm_ac_name; }
virtual expr check_type(expr const & e, abstract_type_context & ctx, bool) const {
return mk_eq(ctx, macro_arg(e, 2), macro_arg(e, 3));
}
virtual unsigned trust_level() const { return LEAN_AC_MACROS_TRUST_LEVEL; }
virtual optional<expr> expand(expr const & e, abstract_type_context & ctx) const {
expr const & assoc = macro_arg(e, 0);
expr const & comm = macro_arg(e, 1);
expr e1 = expand_if_ac_app(macro_arg(e, 2));
expr e2 = expand_if_ac_app(macro_arg(e, 3));
expr const & op = app_fn(app_fn(e1));
return some_expr(perm_ac(ctx, op, assoc, comm, e1, e2));
}
virtual void write(serializer & s) const {
s.write_string(*g_perm_ac_opcode);
}
virtual bool operator==(macro_definition_cell const & other) const {
perm_ac_macro_cell const * other_ptr = dynamic_cast<perm_ac_macro_cell const *>(&other);
return other_ptr;
}
virtual unsigned hash() const { return 31; }
};
expr mk_perm_ac_macro_core(expr const & assoc, expr const & comm, expr const & e1, expr const & e2) {
lean_assert((get_binary_op(e1) || is_ac_app(e1)) && (get_binary_op(e2) || is_ac_app(e2)));
expr args[4] = {assoc, comm, e1, e2};
return mk_macro(*g_perm_ac_macro, 4, args);
}
expr mk_perm_ac_macro(abstract_type_context & ctx, expr const & assoc, expr const & comm, expr const & e1, expr const & e2) {
if (e1 == e2) {
return mk_eq_refl(ctx, e1);
} else {
return mk_perm_ac_macro_core(assoc, comm, e1, e2);
}
}
#define TRY LEAN_TACTIC_TRY
#define CATCH LEAN_TACTIC_CATCH(to_tactic_state(s))
vm_obj tactic_flat_assoc(vm_obj const & op, vm_obj const & assoc, vm_obj const & e, vm_obj const & s) {
TRY;
type_context ctx = mk_type_context_for(s);
pair<expr, expr> p = flat_assoc_fn(ctx, to_expr(op), to_expr(assoc)).flat(to_expr(e));
return mk_tactic_success(mk_vm_pair(to_obj(p.first), to_obj(p.second)), to_tactic_state(s));
CATCH;
}
vm_obj tactic_perm_ac(vm_obj const & op, vm_obj const & assoc, vm_obj const & comm, vm_obj const & e1, vm_obj const & e2, vm_obj const & s) {
TRY;
type_context ctx = mk_type_context_for(s);
expr H = perm_ac_fn(ctx, to_expr(op), to_expr(assoc), to_expr(comm)).perm(to_expr(e1), to_expr(e2));
return mk_tactic_success(to_obj(H), to_tactic_state(s));
CATCH;
}
void initialize_ac_tactics() {
register_trace_class(name{"tactic", "perm_ac"});
DECLARE_VM_BUILTIN(name({"tactic", "flat_assoc"}), tactic_flat_assoc);
DECLARE_VM_BUILTIN(name({"tactic", "perm_ac"}), tactic_perm_ac);
g_ac_app_name = new name("ac_app");
g_ac_app_opcode = new std::string("ACApp");
g_ac_app_macro = new macro_definition(new ac_app_macro_cell());
register_macro_deserializer(*g_ac_app_opcode,
[](deserializer &, unsigned num, expr const * args) {
return mk_ac_app_core(num, args);
});
g_perm_ac_name = new name("perm_ac");
g_perm_ac_opcode = new std::string("PermAC");
g_perm_ac_macro = new macro_definition(new perm_ac_macro_cell());
register_macro_deserializer(*g_perm_ac_opcode,
[](deserializer &, unsigned num, expr const * args) {
if (num != 4) corrupted_stream_exception();
return mk_perm_ac_macro_core(args[0], args[1], args[2], args[3]);
});
}
void finalize_ac_tactics() {
delete g_ac_app_name;
delete g_ac_app_opcode;
delete g_ac_app_macro;
delete g_perm_ac_name;
delete g_perm_ac_opcode;
delete g_perm_ac_macro;
}
}