feat(frontends/lean/parser): add basic tactic support in the frontend
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
parent
73bbf67702
commit
dae86c2ffa
8 changed files with 140 additions and 17 deletions
|
@ -420,7 +420,7 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<expr, expr> elaborate(name const & n, expr const & t, expr const & e) {
|
std::tuple<expr, expr, metavar_env> elaborate(name const & n, expr const & t, expr const & e) {
|
||||||
// std::cout << "Elaborate " << t << " : " << e << "\n";
|
// std::cout << "Elaborate " << t << " : " << e << "\n";
|
||||||
clear();
|
clear();
|
||||||
expr new_t = preprocessor(*this)(t);
|
expr new_t = preprocessor(*this)(t);
|
||||||
|
@ -436,9 +436,11 @@ public:
|
||||||
// std::cout << c.pp(fmt, options(), nullptr, false) << "\n";
|
// std::cout << c.pp(fmt, options(), nullptr, false) << "\n";
|
||||||
// }
|
// }
|
||||||
metavar_env new_menv = elaborate_core();
|
metavar_env new_menv = elaborate_core();
|
||||||
return mk_pair(instantiate_metavars(new_t, new_menv), instantiate_metavars(new_e, new_menv));
|
return std::make_tuple(instantiate_metavars(new_t, new_menv),
|
||||||
|
instantiate_metavars(new_e, new_menv),
|
||||||
|
new_menv);
|
||||||
} else {
|
} else {
|
||||||
return mk_pair(new_t, new_e);
|
return std::make_tuple(new_t, new_e, metavar_env());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -471,7 +473,9 @@ public:
|
||||||
frontend_elaborator::frontend_elaborator(frontend const & fe):m_ptr(new imp(fe)) {}
|
frontend_elaborator::frontend_elaborator(frontend const & fe):m_ptr(new imp(fe)) {}
|
||||||
frontend_elaborator::~frontend_elaborator() {}
|
frontend_elaborator::~frontend_elaborator() {}
|
||||||
expr frontend_elaborator::operator()(expr const & e) { return m_ptr->elaborate(e); }
|
expr frontend_elaborator::operator()(expr const & e) { return m_ptr->elaborate(e); }
|
||||||
std::pair<expr, expr> frontend_elaborator::operator()(name const & n, expr const & t, expr const & e) { return m_ptr->elaborate(n, t, e); }
|
std::tuple<expr, expr, metavar_env> frontend_elaborator::operator()(name const & n, expr const & t, expr const & e) {
|
||||||
|
return m_ptr->elaborate(n, t, e);
|
||||||
|
}
|
||||||
expr const & frontend_elaborator::get_original(expr const & e) const { return m_ptr->get_original(e); }
|
expr const & frontend_elaborator::get_original(expr const & e) const { return m_ptr->get_original(e); }
|
||||||
void frontend_elaborator::clear() { m_ptr->clear(); }
|
void frontend_elaborator::clear() { m_ptr->clear(); }
|
||||||
environment const & frontend_elaborator::get_environment() const { return m_ptr->get_environment(); }
|
environment const & frontend_elaborator::get_environment() const { return m_ptr->get_environment(); }
|
||||||
|
|
|
@ -35,7 +35,7 @@ public:
|
||||||
This information is used by the elaborator. The result is a new
|
This information is used by the elaborator. The result is a new
|
||||||
elaborated type and expression.
|
elaborated type and expression.
|
||||||
*/
|
*/
|
||||||
std::pair<expr, expr> operator()(name const & n, expr const & t, expr const & e);
|
std::tuple<expr, expr, metavar_env> operator()(name const & n, expr const & t, expr const & e);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief If \c e is an expression instantiated by the elaborator, then it
|
\brief If \c e is an expression instantiated by the elaborator, then it
|
||||||
|
|
|
@ -30,11 +30,17 @@ Author: Leonardo de Moura
|
||||||
#include "kernel/kernel_exception.h"
|
#include "kernel/kernel_exception.h"
|
||||||
#include "kernel/expr_maps.h"
|
#include "kernel/expr_maps.h"
|
||||||
#include "kernel/printer.h"
|
#include "kernel/printer.h"
|
||||||
|
#include "kernel/metavar.h"
|
||||||
|
#include "kernel/for_each_fn.h"
|
||||||
|
#include "library/expr_lt.h"
|
||||||
|
#include "library/type_inferer.h"
|
||||||
#include "library/arith/arith.h"
|
#include "library/arith/arith.h"
|
||||||
#include "library/io_state.h"
|
#include "library/io_state.h"
|
||||||
#include "library/placeholder.h"
|
#include "library/placeholder.h"
|
||||||
#include "library/kernel_bindings.h"
|
#include "library/kernel_bindings.h"
|
||||||
#include "library/elaborator/elaborator_exception.h"
|
#include "library/elaborator/elaborator_exception.h"
|
||||||
|
#include "library/tactic/proof_state.h"
|
||||||
|
#include "library/tactic/tactic.h"
|
||||||
#include "frontends/lean/frontend.h"
|
#include "frontends/lean/frontend.h"
|
||||||
#include "frontends/lean/frontend_elaborator.h"
|
#include "frontends/lean/frontend_elaborator.h"
|
||||||
#include "frontends/lean/parser.h"
|
#include "frontends/lean/parser.h"
|
||||||
|
@ -85,6 +91,8 @@ static name g_env_kwd("Environment");
|
||||||
static name g_import_kwd("Import");
|
static name g_import_kwd("Import");
|
||||||
static name g_help_kwd("Help");
|
static name g_help_kwd("Help");
|
||||||
static name g_coercion_kwd("Coercion");
|
static name g_coercion_kwd("Coercion");
|
||||||
|
static name g_apply("apply");
|
||||||
|
static name g_done("done");
|
||||||
/** \brief Table/List with all builtin command keywords */
|
/** \brief Table/List with all builtin command keywords */
|
||||||
static list<name> g_command_keywords = {g_definition_kwd, g_variable_kwd, g_variables_kwd, g_theorem_kwd, g_axiom_kwd, g_universe_kwd, g_eval_kwd,
|
static list<name> g_command_keywords = {g_definition_kwd, g_variable_kwd, g_variables_kwd, g_theorem_kwd, g_axiom_kwd, g_universe_kwd, g_eval_kwd,
|
||||||
g_show_kwd, g_check_kwd, g_infix_kwd, g_infixl_kwd, g_infixr_kwd, g_notation_kwd, g_echo_kwd,
|
g_show_kwd, g_check_kwd, g_infix_kwd, g_infixl_kwd, g_infixr_kwd, g_notation_kwd, g_echo_kwd,
|
||||||
|
@ -123,6 +131,7 @@ class parser::imp {
|
||||||
frontend & m_frontend;
|
frontend & m_frontend;
|
||||||
scanner m_scanner;
|
scanner m_scanner;
|
||||||
frontend_elaborator m_elaborator;
|
frontend_elaborator m_elaborator;
|
||||||
|
type_inferer m_type_inferer;
|
||||||
scanner::token m_curr;
|
scanner::token m_curr;
|
||||||
bool m_use_exceptions;
|
bool m_use_exceptions;
|
||||||
bool m_interactive;
|
bool m_interactive;
|
||||||
|
@ -241,6 +250,8 @@ class parser::imp {
|
||||||
void check_rcurly_next(char const * msg) { check_next(scanner::token::RightCurlyBracket, msg); }
|
void check_rcurly_next(char const * msg) { check_next(scanner::token::RightCurlyBracket, msg); }
|
||||||
/** \brief Throws a parser error if the current token is not ':='. If it is, move to the next token. */
|
/** \brief Throws a parser error if the current token is not ':='. If it is, move to the next token. */
|
||||||
void check_assign_next(char const * msg) { check_next(scanner::token::Assign, msg); }
|
void check_assign_next(char const * msg) { check_next(scanner::token::Assign, msg); }
|
||||||
|
/** \brief Throws a parser error if the current token is not '.'. If it is, move to the next token. */
|
||||||
|
void check_period_next(char const * msg) { check_next(scanner::token::Period, msg); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief Throws a parser error if the current token is not a
|
\brief Throws a parser error if the current token is not a
|
||||||
|
@ -1036,6 +1047,90 @@ class parser::imp {
|
||||||
m_frontend.mark_implicit_arguments(n, imp_args.size(), imp_args.data());
|
m_frontend.mark_implicit_arguments(n, imp_args.size(), imp_args.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expr parse_tactic(proof_state s) {
|
||||||
|
proof_state ini = s;
|
||||||
|
std::vector<lazy_list<proof_state>> stack;
|
||||||
|
while (true) {
|
||||||
|
auto p = pos();
|
||||||
|
name id = check_identifier_next("invalid tactic, identifier expected");
|
||||||
|
if (id == g_apply) {
|
||||||
|
auto tac_name_pos = pos();
|
||||||
|
name tac_name = check_identifier_next("invalid apply command, identifier expected");
|
||||||
|
optional<tactic> t;
|
||||||
|
m_script_state->apply([&](lua_State * L) {
|
||||||
|
lua_getglobal(L, tac_name.to_string().c_str());
|
||||||
|
if (lua_type(L, -1) != LUA_TFUNCTION && !is_tactic(L, -1))
|
||||||
|
throw parser_error(sstream() << "unknown tactic '" << tac_name << "'", tac_name_pos);
|
||||||
|
if (lua_type(L, -1) == LUA_TFUNCTION) {
|
||||||
|
pcall(L, 0, 1, 0);
|
||||||
|
if (!is_tactic(L, -1))
|
||||||
|
throw parser_error(sstream() << "invalid function '" << tac_name << "', it does not return a tactic", tac_name_pos);
|
||||||
|
}
|
||||||
|
t = to_tactic(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
});
|
||||||
|
lazy_list<proof_state> seq = (*t)(m_frontend, m_frontend.get_state(), s);
|
||||||
|
auto r = seq.pull();
|
||||||
|
if (r) {
|
||||||
|
s = r->first;
|
||||||
|
stack.push_back(r->second);
|
||||||
|
} else {
|
||||||
|
diagnostic(m_frontend) << "current state:\n" << s << endl;
|
||||||
|
throw parser_error(sstream() << "tactic '" << tac_name << "' failed", tac_name_pos);
|
||||||
|
}
|
||||||
|
} else if (id == g_done) {
|
||||||
|
if (s.is_proof_final_state()) {
|
||||||
|
assignment a(s.get_menv());
|
||||||
|
proof_map m;
|
||||||
|
expr pr = s.get_proof_builder()(m, a);
|
||||||
|
return pr;
|
||||||
|
} else {
|
||||||
|
diagnostic(m_frontend) << "final state:\n" << s << endl;
|
||||||
|
throw exception("failed to synthesize proof object using given tactic");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw parser_error(sstream() << "invalid tactical proof, unknown command '" << id << "'", p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expr parse_tactic(expr const & type, expr const & val, metavar_env & menv) {
|
||||||
|
check_period_next("invalid theorem, '.' expected before tactical proof");
|
||||||
|
if (is_metavar(val)) {
|
||||||
|
// simple case
|
||||||
|
proof_state s = to_proof_state(m_frontend, context(), type);
|
||||||
|
return parse_tactic(s);
|
||||||
|
} else {
|
||||||
|
buffer<expr> mvars;
|
||||||
|
for_each(val, [&](expr const & e, unsigned) {
|
||||||
|
if (is_metavar(e)) {
|
||||||
|
mvars.push_back(e);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
std::sort(mvars.begin(), mvars.end(), [](expr const & e1, expr const & e2) { return is_lt(e1, e2, false); });
|
||||||
|
for (auto mvar : mvars) {
|
||||||
|
expr mvar_type = instantiate_metavars(menv.get_type(mvar), menv);
|
||||||
|
if (!m_type_inferer.is_proposition(mvar_type))
|
||||||
|
throw exception("failed to synthesize metavar, its type is not a proposition");
|
||||||
|
if (has_metavar(mvar_type))
|
||||||
|
throw exception("failed to synthesize metavar, its type contains metavariables");
|
||||||
|
buffer<context_entry> new_entries;
|
||||||
|
for (auto e : menv.get_context(mvar)) {
|
||||||
|
new_entries.emplace_back(e.get_name(),
|
||||||
|
instantiate_metavars(e.get_domain(), menv),
|
||||||
|
instantiate_metavars(e.get_body(), menv));
|
||||||
|
}
|
||||||
|
context mvar_ctx(to_list(new_entries.begin(), new_entries.end()));
|
||||||
|
proof_state s = to_proof_state(m_frontend, mvar_ctx, mvar_type);
|
||||||
|
expr mvar_val = parse_tactic(s);
|
||||||
|
if (mvar_val)
|
||||||
|
menv.assign(mvar, mvar_val);
|
||||||
|
}
|
||||||
|
return instantiate_metavars(val, menv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** \brief Auxiliary method used for parsing definitions and theorems. */
|
/** \brief Auxiliary method used for parsing definitions and theorems. */
|
||||||
void parse_def_core(bool is_definition) {
|
void parse_def_core(bool is_definition) {
|
||||||
next();
|
next();
|
||||||
|
@ -1062,9 +1157,17 @@ class parser::imp {
|
||||||
pre_type = mk_abstraction(false, bindings, type_body);
|
pre_type = mk_abstraction(false, bindings, type_body);
|
||||||
pre_val = mk_abstraction(true, bindings, val_body);
|
pre_val = mk_abstraction(true, bindings, val_body);
|
||||||
}
|
}
|
||||||
auto type_val_pair = m_elaborator(id, pre_type, pre_val);
|
auto r = m_elaborator(id, pre_type, pre_val);
|
||||||
expr type = type_val_pair.first;
|
expr type = std::get<0>(r);
|
||||||
expr val = type_val_pair.second;
|
expr val = std::get<1>(r);
|
||||||
|
metavar_env menv = std::get<2>(r);
|
||||||
|
if (has_metavar(type))
|
||||||
|
throw exception("invalid definition, type still contains metavariables after elaboration");
|
||||||
|
if (!is_definition && has_metavar(val)) {
|
||||||
|
val = parse_tactic(type, val, menv);
|
||||||
|
}
|
||||||
|
if (has_metavar(val))
|
||||||
|
throw exception("invalid definition, value still contains metavariables after elaboration");
|
||||||
if (is_definition) {
|
if (is_definition) {
|
||||||
m_frontend.add_definition(id, type, val);
|
m_frontend.add_definition(id, type, val);
|
||||||
if (m_verbose)
|
if (m_verbose)
|
||||||
|
@ -1609,6 +1712,7 @@ public:
|
||||||
m_frontend(fe),
|
m_frontend(fe),
|
||||||
m_scanner(in),
|
m_scanner(in),
|
||||||
m_elaborator(fe),
|
m_elaborator(fe),
|
||||||
|
m_type_inferer(fe),
|
||||||
m_use_exceptions(use_exceptions),
|
m_use_exceptions(use_exceptions),
|
||||||
m_interactive(interactive) {
|
m_interactive(interactive) {
|
||||||
m_script_state = S;
|
m_script_state = S;
|
||||||
|
|
|
@ -31,11 +31,11 @@ public:
|
||||||
*/
|
*/
|
||||||
class context {
|
class context {
|
||||||
list<context_entry> m_list;
|
list<context_entry> m_list;
|
||||||
explicit context(list<context_entry> const & l):m_list(l) {}
|
|
||||||
public:
|
public:
|
||||||
context() {}
|
context() {}
|
||||||
context(context const & c, name const & n, expr const & d):m_list(context_entry(n, d), c.m_list) {}
|
context(context const & c, name const & n, expr const & d):m_list(context_entry(n, d), c.m_list) {}
|
||||||
context(context const & c, name const & n, expr const & d, expr const & b):m_list(context_entry(n, d, b), c.m_list) {}
|
context(context const & c, name const & n, expr const & d, expr const & b):m_list(context_entry(n, d, b), c.m_list) {}
|
||||||
|
explicit context(list<context_entry> const & l):m_list(l) {}
|
||||||
context_entry const & lookup(unsigned vidx) const;
|
context_entry const & lookup(unsigned vidx) const;
|
||||||
std::pair<context_entry const &, context> lookup_ext(unsigned vidx) const;
|
std::pair<context_entry const &, context> lookup_ext(unsigned vidx) const;
|
||||||
bool empty() const { return is_nil(m_list); }
|
bool empty() const { return is_nil(m_list); }
|
||||||
|
|
|
@ -275,7 +275,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
expr instantiate_metavars(expr const & e, metavar_env const & menv, buffer<justification> & jsts) {
|
expr instantiate_metavars(expr const & e, metavar_env const & menv, buffer<justification> & jsts) {
|
||||||
if (!has_metavar(e)) {
|
if (!e || !has_metavar(e)) {
|
||||||
return e;
|
return e;
|
||||||
} else {
|
} else {
|
||||||
return instantiate_metavars_proc(menv, jsts)(e);
|
return instantiate_metavars_proc(menv, jsts)(e);
|
||||||
|
|
|
@ -107,7 +107,8 @@ std::pair<goal, goal_proof_fn> to_goal(environment const & env, context const &
|
||||||
buffer<context_entry> entries;
|
buffer<context_entry> entries;
|
||||||
for (auto const & e : ctx)
|
for (auto const & e : ctx)
|
||||||
entries.push_back(e);
|
entries.push_back(e);
|
||||||
buffer<hypothesis> hypotheses; // normalized names and types of the entries processed so far
|
std::reverse(entries.begin(), entries.end());
|
||||||
|
buffer<hypothesis> hypotheses; // normalized names and types of the entries processed so far
|
||||||
buffer<expr> bodies; // normalized bodies of the entries processed so far
|
buffer<expr> bodies; // normalized bodies of the entries processed so far
|
||||||
std::vector<expr> consts; // cached consts[i] == mk_constant(names[i], hypotheses[i])
|
std::vector<expr> consts; // cached consts[i] == mk_constant(names[i], hypotheses[i])
|
||||||
auto replace_vars = [&](expr const & e, unsigned offset) -> expr {
|
auto replace_vars = [&](expr const & e, unsigned offset) -> expr {
|
||||||
|
@ -128,10 +129,9 @@ std::pair<goal, goal_proof_fn> to_goal(environment const & env, context const &
|
||||||
return e;
|
return e;
|
||||||
};
|
};
|
||||||
replace_fn<decltype(replace_vars)> replacer(replace_vars);
|
replace_fn<decltype(replace_vars)> replacer(replace_vars);
|
||||||
auto it = entries.end();
|
auto it = entries.begin();
|
||||||
auto begin = entries.begin();
|
auto end = entries.end();
|
||||||
while (it != begin) {
|
for (; it != end; ++it) {
|
||||||
--it;
|
|
||||||
auto const & e = *it;
|
auto const & e = *it;
|
||||||
name n = mk_unique_name(used_names, e.get_name());
|
name n = mk_unique_name(used_names, e.get_name());
|
||||||
expr d = replacer(e.get_domain());
|
expr d = replacer(e.get_domain());
|
||||||
|
@ -148,8 +148,7 @@ std::pair<goal, goal_proof_fn> to_goal(environment const & env, context const &
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
expr conclusion = replacer(t);
|
expr conclusion = replacer(t);
|
||||||
std::reverse(consts.begin(), consts.end());
|
return mk_pair(goal(to_list(hypotheses.begin(), hypotheses.end()), conclusion),
|
||||||
return mk_pair(goal(reverse_to_list(hypotheses.begin(), hypotheses.end()), conclusion),
|
|
||||||
goal_proof_fn(std::move(consts)));
|
goal_proof_fn(std::move(consts)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,18 @@ proof_state to_proof_state(environment const & env, context const & ctx, expr co
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
regular const & operator<<(regular const & out, proof_state & s) {
|
||||||
|
options const & opts = out.m_io_state.get_options();
|
||||||
|
out.m_io_state.get_regular_channel().get_stream() << mk_pair(s.pp(out.m_io_state.get_formatter(), opts), opts);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
diagnostic const & operator<<(diagnostic const & out, proof_state & s) {
|
||||||
|
options const & opts = out.m_io_state.get_options();
|
||||||
|
out.m_io_state.get_diagnostic_channel().get_stream() << mk_pair(s.pp(out.m_io_state.get_formatter(), opts), opts);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
DECL_UDATA(goals)
|
DECL_UDATA(goals)
|
||||||
|
|
||||||
static int mk_goals(lua_State * L) {
|
static int mk_goals(lua_State * L) {
|
||||||
|
|
|
@ -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 "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"
|
||||||
#include "library/tactic/cex_builder.h"
|
#include "library/tactic/cex_builder.h"
|
||||||
|
@ -97,6 +98,9 @@ goals map_goals(proof_state const & s, F && f) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
regular const & operator<<(regular const & out, proof_state & s);
|
||||||
|
diagnostic const & operator<<(diagnostic const & out, proof_state & s);
|
||||||
|
|
||||||
UDATA_DEFS_CORE(goals)
|
UDATA_DEFS_CORE(goals)
|
||||||
UDATA_DEFS(proof_state)
|
UDATA_DEFS(proof_state)
|
||||||
void open_proof_state(lua_State * L);
|
void open_proof_state(lua_State * L);
|
||||||
|
|
Loading…
Reference in a new issue