feat(frontends/lean): improve error messages when users forget to import 'tactic'
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
parent
6b8b5f3dd8
commit
e3ab0a1d10
5 changed files with 81 additions and 19 deletions
|
@ -129,10 +129,12 @@ static expr parse_placeholder(parser & p, unsigned, expr const *, pos_info const
|
||||||
|
|
||||||
static expr parse_by(parser & p, unsigned, expr const *, pos_info const & pos) {
|
static expr parse_by(parser & p, unsigned, expr const *, pos_info const & pos) {
|
||||||
expr t = p.parse_expr();
|
expr t = p.parse_expr();
|
||||||
return p.save_pos(mk_by(t), pos);
|
return p.mk_by(t, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
static expr parse_proof_qed(parser & p, unsigned, expr const *, pos_info const &) {
|
static expr parse_proof_qed(parser & p, unsigned, expr const *, pos_info const & pos) {
|
||||||
|
if (!p.has_tactic_decls())
|
||||||
|
throw parser_error("invalid 'proof' expression, tactic module has not been imported", pos);
|
||||||
optional<expr> pre_tac = get_proof_qed_pre_tactic(p.env());
|
optional<expr> pre_tac = get_proof_qed_pre_tactic(p.env());
|
||||||
optional<expr> r;
|
optional<expr> r;
|
||||||
while (true) {
|
while (true) {
|
||||||
|
@ -148,7 +150,7 @@ static expr parse_proof_qed(parser & p, unsigned, expr const *, pos_info const &
|
||||||
if (p.curr_is_token(g_qed)) {
|
if (p.curr_is_token(g_qed)) {
|
||||||
auto pos = p.pos();
|
auto pos = p.pos();
|
||||||
p.next();
|
p.next();
|
||||||
return p.save_pos(mk_by(*r), pos);
|
return p.mk_by(*r, pos);
|
||||||
} else if (p.curr_is_token(g_comma)) {
|
} else if (p.curr_is_token(g_comma)) {
|
||||||
p.next();
|
p.next();
|
||||||
} else {
|
} else {
|
||||||
|
@ -167,7 +169,7 @@ static expr parse_proof(parser & p, expr const & prop) {
|
||||||
auto pos = p.pos();
|
auto pos = p.pos();
|
||||||
p.next();
|
p.next();
|
||||||
expr t = p.parse_expr();
|
expr t = p.parse_expr();
|
||||||
return p.save_pos(mk_by(t), pos);
|
return p.mk_by(t, pos);
|
||||||
} else if (p.curr_is_token(g_using)) {
|
} else if (p.curr_is_token(g_using)) {
|
||||||
// parse: 'using' locals* ',' proof
|
// parse: 'using' locals* ',' proof
|
||||||
auto using_pos = p.pos();
|
auto using_pos = p.pos();
|
||||||
|
|
|
@ -28,6 +28,7 @@ Author: Leonardo de Moura
|
||||||
#include "library/num.h"
|
#include "library/num.h"
|
||||||
#include "library/string.h"
|
#include "library/string.h"
|
||||||
#include "library/error_handling/error_handling.h"
|
#include "library/error_handling/error_handling.h"
|
||||||
|
#include "library/tactic/expr_to_tactic.h"
|
||||||
#include "frontends/lean/parser.h"
|
#include "frontends/lean/parser.h"
|
||||||
#include "frontends/lean/parser_bindings.h"
|
#include "frontends/lean/parser_bindings.h"
|
||||||
#include "frontends/lean/notation_cmd.h"
|
#include "frontends/lean/notation_cmd.h"
|
||||||
|
@ -100,6 +101,18 @@ parser::parser(environment const & env, io_state const & ios,
|
||||||
[&]() { sync_command(); });
|
[&]() { sync_command(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool parser::has_tactic_decls() {
|
||||||
|
if (!m_has_tactic_decls)
|
||||||
|
m_has_tactic_decls = ::lean::has_tactic_decls(m_env);
|
||||||
|
return *m_has_tactic_decls;
|
||||||
|
}
|
||||||
|
|
||||||
|
expr parser::mk_by(expr const & t, pos_info const & pos) {
|
||||||
|
if (!has_tactic_decls())
|
||||||
|
throw parser_error("invalid 'by' expression, tactic module has not been imported", pos);
|
||||||
|
return save_pos(::lean::mk_by(t), pos);
|
||||||
|
}
|
||||||
|
|
||||||
void parser::updt_options() {
|
void parser::updt_options() {
|
||||||
m_verbose = get_verbose(m_ios.get_options());
|
m_verbose = get_verbose(m_ios.get_options());
|
||||||
m_show_errors = get_parser_show_errors(m_ios.get_options());
|
m_show_errors = get_parser_show_errors(m_ios.get_options());
|
||||||
|
|
|
@ -66,6 +66,7 @@ class parser {
|
||||||
bool m_no_undef_id_error;
|
bool m_no_undef_id_error;
|
||||||
optional<bool> m_has_num;
|
optional<bool> m_has_num;
|
||||||
optional<bool> m_has_string;
|
optional<bool> m_has_string;
|
||||||
|
optional<bool> m_has_tactic_decls;
|
||||||
|
|
||||||
void display_error_pos(unsigned line, unsigned pos);
|
void display_error_pos(unsigned line, unsigned pos);
|
||||||
void display_error_pos(pos_info p);
|
void display_error_pos(pos_info p);
|
||||||
|
@ -139,6 +140,9 @@ public:
|
||||||
local_level_decls const & get_local_level_decls() const { return m_local_level_decls; }
|
local_level_decls const & get_local_level_decls() const { return m_local_level_decls; }
|
||||||
local_expr_decls const & get_local_expr_decls() const { return m_local_decls; }
|
local_expr_decls const & get_local_expr_decls() const { return m_local_decls; }
|
||||||
|
|
||||||
|
bool has_tactic_decls();
|
||||||
|
expr mk_by(expr const & t, pos_info const & pos);
|
||||||
|
|
||||||
void updt_options();
|
void updt_options();
|
||||||
template<typename T> void set_option(name const & n, T const & v) { m_ios.set_option(n, v); }
|
template<typename T> void set_option(name const & n, T const & v) { m_ios.set_option(n, v); }
|
||||||
|
|
||||||
|
|
|
@ -25,18 +25,31 @@ void register_expr_to_tactic(name const & n, expr_to_tactic_fn const & fn) {
|
||||||
get_expr_to_tactic_map().insert(mk_pair(n, fn));
|
get_expr_to_tactic_map().insert(mk_pair(n, fn));
|
||||||
}
|
}
|
||||||
|
|
||||||
static name g_tac("tactic"), g_builtin_tac(g_tac, "builtin_tactic");
|
static name g_tac("tactic"), g_tac_name(g_tac, "tactic"), g_builtin_tac_name(g_tac, "builtin_tactic");
|
||||||
static name g_tac_name(g_tac, "tactic"), g_exact_tac_name(g_tac, "exact"), g_and_then_tac_name(g_tac, "and_then");
|
static name g_exact_tac_name(g_tac, "exact"), g_and_then_tac_name(g_tac, "and_then");
|
||||||
static name g_or_else_tac_name(g_tac, "or_else"), g_repeat_tac_name(g_tac, "repeat");
|
static name g_or_else_tac_name(g_tac, "or_else"), g_repeat_tac_name(g_tac, "repeat");
|
||||||
static expr g_exact_tac_fn(Const(g_exact_tac_name)), g_and_then_tac_fn(Const(g_and_then_tac_name));
|
static expr g_exact_tac_fn(Const(g_exact_tac_name)), g_and_then_tac_fn(Const(g_and_then_tac_name));
|
||||||
static expr g_or_else_tac_fn(Const(g_or_else_tac_name)), g_repeat_tac_fn(Const(g_repeat_tac_name));
|
static expr g_or_else_tac_fn(Const(g_or_else_tac_name)), g_repeat_tac_fn(Const(g_repeat_tac_name));
|
||||||
static expr g_tac_type(Const(g_tac_name));
|
static expr g_tac_type(Const(g_tac_name)), g_builtin_tac(Const(g_builtin_tac_name));
|
||||||
expr const & get_exact_tac_fn() { return g_exact_tac_fn; }
|
expr const & get_exact_tac_fn() { return g_exact_tac_fn; }
|
||||||
expr const & get_and_then_tac_fn() { return g_and_then_tac_fn; }
|
expr const & get_and_then_tac_fn() { return g_and_then_tac_fn; }
|
||||||
expr const & get_or_else_tac_fn() { return g_or_else_tac_fn; }
|
expr const & get_or_else_tac_fn() { return g_or_else_tac_fn; }
|
||||||
expr const & get_repeat_tac_fn() { return g_repeat_tac_fn; }
|
expr const & get_repeat_tac_fn() { return g_repeat_tac_fn; }
|
||||||
expr const & get_tactic_type() { return g_tac_type; }
|
expr const & get_tactic_type() { return g_tac_type; }
|
||||||
|
|
||||||
|
bool has_tactic_decls(environment const & env) {
|
||||||
|
try {
|
||||||
|
type_checker tc(env);
|
||||||
|
return
|
||||||
|
tc.infer(g_builtin_tac) == g_tac_type &&
|
||||||
|
tc.infer(g_and_then_tac_fn) == g_tac_type >> (g_tac_type >> g_tac_type) &&
|
||||||
|
tc.infer(g_or_else_tac_fn) == g_tac_type >> (g_tac_type >> g_tac_type) &&
|
||||||
|
tc.infer(g_repeat_tac_fn) == g_tac_type >> g_tac_type;
|
||||||
|
} catch (...) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void throw_failed(expr const & e) {
|
static void throw_failed(expr const & e) {
|
||||||
throw expr_to_tactic_exception(e, "failed to convert expression into tactic");
|
throw expr_to_tactic_exception(e, "failed to convert expression into tactic");
|
||||||
}
|
}
|
||||||
|
@ -45,7 +58,7 @@ static void throw_failed(expr const & e) {
|
||||||
static bool is_builtin_tactic(expr const & v) {
|
static bool is_builtin_tactic(expr const & v) {
|
||||||
if (is_lambda(v))
|
if (is_lambda(v))
|
||||||
return is_builtin_tactic(binding_body(v));
|
return is_builtin_tactic(binding_body(v));
|
||||||
else if (is_constant(v) && const_name(v) == g_builtin_tac)
|
else if (v == g_builtin_tac)
|
||||||
return true;
|
return true;
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -9,6 +9,44 @@ Author: Leonardo de Moura
|
||||||
#include "library/tactic/tactic.h"
|
#include "library/tactic/tactic.h"
|
||||||
|
|
||||||
namespace lean {
|
namespace lean {
|
||||||
|
/**
|
||||||
|
\brief Return true iff the environment \c env contains the following declarations
|
||||||
|
in the namespace 'tactic'
|
||||||
|
builtin_tactic : tactic
|
||||||
|
and_then : tactic -> tactic -> tactic
|
||||||
|
or_else : tactic -> tactic -> tactic
|
||||||
|
repeat : tactic -> tactic
|
||||||
|
*/
|
||||||
|
bool has_tactic_decls(environment const & env);
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Creates a tactic by 'executing' \c e. Definitions are unfolded, whnf procedure is invoked,
|
||||||
|
and definitions marked as 'tactic.builtin_tactic' are handled by the code registered using
|
||||||
|
\c register_expr_to_tactic.
|
||||||
|
*/
|
||||||
|
tactic expr_to_tactic(environment const & env, expr const & e, pos_info_provider const *p);
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Create an expression `by t`, where \c t is an expression of type `tactic`.
|
||||||
|
This kind of expression only affects the elaborator, the kernel will reject
|
||||||
|
any declaration that contains it.
|
||||||
|
|
||||||
|
\post get_by_arg(mk_by(t)) == t
|
||||||
|
\post is_by(mk_by(t))
|
||||||
|
*/
|
||||||
|
expr mk_by(expr const & t);
|
||||||
|
/** \brief Return true iff \c t is an expression created using \c mk_by */
|
||||||
|
bool is_by(expr const & t);
|
||||||
|
/** \see mk_by */
|
||||||
|
expr const & get_by_arg(expr const & t);
|
||||||
|
|
||||||
|
expr const & get_tactic_type();
|
||||||
|
expr const & get_exact_tac_fn();
|
||||||
|
expr const & get_and_then_tac_fn();
|
||||||
|
expr const & get_or_else_tac_fn();
|
||||||
|
expr const & get_repeat_tac_fn();
|
||||||
|
|
||||||
|
/** \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 {
|
||||||
public:
|
public:
|
||||||
expr_to_tactic_exception(expr const & e, char const * msg):tactic_exception(e, msg) {}
|
expr_to_tactic_exception(expr const & e, char const * msg):tactic_exception(e, msg) {}
|
||||||
|
@ -16,7 +54,10 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::function<tactic(type_checker & tc, expr const & e, pos_info_provider const *)> expr_to_tactic_fn;
|
typedef std::function<tactic(type_checker & tc, expr const & e, pos_info_provider const *)> expr_to_tactic_fn;
|
||||||
|
|
||||||
|
/** \brief Register a new "procedural attachment" for expr_to_tactic. */
|
||||||
void register_expr_to_tactic(name const & n, expr_to_tactic_fn const & fn);
|
void register_expr_to_tactic(name const & n, expr_to_tactic_fn const & fn);
|
||||||
|
|
||||||
struct register_tac {
|
struct register_tac {
|
||||||
register_tac(name const & n, expr_to_tactic_fn fn) { register_expr_to_tactic(n, fn); }
|
register_tac(name const & n, expr_to_tactic_fn fn) { register_expr_to_tactic(n, fn); }
|
||||||
};
|
};
|
||||||
|
@ -32,15 +73,4 @@ struct register_unary_tac {
|
||||||
struct register_unary_num_tac {
|
struct register_unary_num_tac {
|
||||||
register_unary_num_tac(name const & n, std::function<tactic(tactic const &, unsigned)> f);
|
register_unary_num_tac(name const & n, std::function<tactic(tactic const &, unsigned)> f);
|
||||||
};
|
};
|
||||||
|
|
||||||
expr const & get_tactic_type();
|
|
||||||
expr const & get_exact_tac_fn();
|
|
||||||
expr const & get_and_then_tac_fn();
|
|
||||||
expr const & get_or_else_tac_fn();
|
|
||||||
expr const & get_repeat_tac_fn();
|
|
||||||
|
|
||||||
tactic expr_to_tactic(environment const & env, expr const & e, pos_info_provider const *p);
|
|
||||||
expr mk_by(expr const & e);
|
|
||||||
bool is_by(expr const & e);
|
|
||||||
expr const & get_by_arg(expr const & e);
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue