feat(frontends/lean): nested begin-end blocks
This commit is contained in:
parent
21e972e34a
commit
1ff6446a63
4 changed files with 103 additions and 42 deletions
|
@ -143,7 +143,7 @@ static expr parse_by(parser & p, unsigned, expr const *, pos_info const & pos) {
|
||||||
return p.mk_by(t, pos);
|
return p.mk_by(t, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
static expr parse_begin_end_core(parser & p, pos_info const & pos) {
|
static expr parse_begin_end_core(parser & p, pos_info const & pos, name const & end_token, bool nested = false) {
|
||||||
if (!p.has_tactic_decls())
|
if (!p.has_tactic_decls())
|
||||||
throw parser_error("invalid 'begin-end' expression, tactic module has not been imported", pos);
|
throw parser_error("invalid 'begin-end' expression, tactic module has not been imported", pos);
|
||||||
environment env = open_tactic_namespace(p);
|
environment env = open_tactic_namespace(p);
|
||||||
|
@ -153,7 +153,7 @@ static expr parse_begin_end_core(parser & p, pos_info const & pos) {
|
||||||
optional<expr> pre_tac = get_begin_end_pre_tactic(env);
|
optional<expr> pre_tac = get_begin_end_pre_tactic(env);
|
||||||
buffer<expr> tacs;
|
buffer<expr> tacs;
|
||||||
bool first = true;
|
bool first = true;
|
||||||
while (!p.curr_is_token(get_end_tk())) {
|
while (!p.curr_is_token(end_token)) {
|
||||||
if (first) {
|
if (first) {
|
||||||
first = false;
|
first = false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -164,19 +164,27 @@ static expr parse_begin_end_core(parser & p, pos_info const & pos) {
|
||||||
tacs.push_back(mk_begin_end_element_annotation(info_tac));
|
tacs.push_back(mk_begin_end_element_annotation(info_tac));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (p.curr_is_token(get_end_tk()))
|
if (p.curr_is_token(get_begin_tk())) {
|
||||||
|
auto pos = p.pos();
|
||||||
|
tacs.push_back(parse_begin_end_core(p, pos, get_end_tk(), true));
|
||||||
|
} else if (p.curr_is_token(get_lcurly_tk())) {
|
||||||
|
auto pos = p.pos();
|
||||||
|
tacs.push_back(parse_begin_end_core(p, pos, get_rcurly_tk(), true));
|
||||||
|
} else if (p.curr_is_token(end_token)) {
|
||||||
break;
|
break;
|
||||||
bool use_exact = (p.curr_is_token(get_have_tk()) || p.curr_is_token(get_show_tk()) ||
|
} else {
|
||||||
p.curr_is_token(get_assume_tk()) || p.curr_is_token(get_take_tk()) ||
|
bool use_exact = (p.curr_is_token(get_have_tk()) || p.curr_is_token(get_show_tk()) ||
|
||||||
p.curr_is_token(get_fun_tk()));
|
p.curr_is_token(get_assume_tk()) || p.curr_is_token(get_take_tk()) ||
|
||||||
auto pos = p.pos();
|
p.curr_is_token(get_fun_tk()));
|
||||||
expr tac = p.parse_expr();
|
auto pos = p.pos();
|
||||||
if (use_exact)
|
expr tac = p.parse_expr();
|
||||||
tac = p.mk_app(get_exact_tac_fn(), tac, pos);
|
if (use_exact)
|
||||||
if (pre_tac)
|
tac = p.mk_app(get_exact_tac_fn(), tac, pos);
|
||||||
tac = p.mk_app({get_and_then_tac_fn(), *pre_tac, tac}, pos);
|
if (pre_tac)
|
||||||
tac = mk_begin_end_element_annotation(tac);
|
tac = p.mk_app({get_and_then_tac_fn(), *pre_tac, tac}, pos);
|
||||||
tacs.push_back(tac);
|
tac = mk_begin_end_element_annotation(tac);
|
||||||
|
tacs.push_back(tac);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
auto end_pos = p.pos();
|
auto end_pos = p.pos();
|
||||||
p.next();
|
p.next();
|
||||||
|
@ -196,11 +204,15 @@ static expr parse_begin_end_core(parser & p, pos_info const & pos) {
|
||||||
for (unsigned i = 1; i < tacs.size(); i++) {
|
for (unsigned i = 1; i < tacs.size(); i++) {
|
||||||
r = p.mk_app({get_and_then_tac_fn(), r, tacs[i]}, end_pos);
|
r = p.mk_app({get_and_then_tac_fn(), r, tacs[i]}, end_pos);
|
||||||
}
|
}
|
||||||
return p.mk_by(mk_begin_end_annotation(r), end_pos);
|
r = p.save_pos(mk_begin_end_annotation(r), end_pos);
|
||||||
|
if (nested)
|
||||||
|
return r;
|
||||||
|
else
|
||||||
|
return p.mk_by(r, end_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
static expr parse_begin_end(parser & p, unsigned, expr const *, pos_info const & pos) {
|
static expr parse_begin_end(parser & p, unsigned, expr const *, pos_info const & pos) {
|
||||||
return parse_begin_end_core(p, pos);
|
return parse_begin_end_core(p, pos, get_end_tk());
|
||||||
}
|
}
|
||||||
|
|
||||||
static expr parse_proof_qed_core(parser & p, pos_info const & pos) {
|
static expr parse_proof_qed_core(parser & p, pos_info const & pos) {
|
||||||
|
@ -220,7 +232,7 @@ static expr parse_proof(parser & p, expr const & prop) {
|
||||||
return parse_proof_qed_core(p, pos);
|
return parse_proof_qed_core(p, pos);
|
||||||
} else if (p.curr_is_token(get_begin_tk())) {
|
} else if (p.curr_is_token(get_begin_tk())) {
|
||||||
auto pos = p.pos();
|
auto pos = p.pos();
|
||||||
return parse_begin_end_core(p, pos);
|
return parse_begin_end_core(p, pos, get_end_tk());
|
||||||
} else if (p.curr_is_token(get_by_tk())) {
|
} else if (p.curr_is_token(get_by_tk())) {
|
||||||
// parse: 'by' tactic
|
// parse: 'by' tactic
|
||||||
auto pos = p.pos();
|
auto pos = p.pos();
|
||||||
|
|
|
@ -1536,6 +1536,9 @@ bool elaborator::try_using(substitution & subst, expr const & mvar, proof_state
|
||||||
static void extract_begin_end_tactics(expr pre_tac, buffer<expr> & pre_tac_seq) {
|
static void extract_begin_end_tactics(expr pre_tac, buffer<expr> & pre_tac_seq) {
|
||||||
if (is_begin_end_element_annotation(pre_tac)) {
|
if (is_begin_end_element_annotation(pre_tac)) {
|
||||||
pre_tac_seq.push_back(get_annotation_arg(pre_tac));
|
pre_tac_seq.push_back(get_annotation_arg(pre_tac));
|
||||||
|
} else if (is_begin_end_annotation(pre_tac)) {
|
||||||
|
// nested begin-end block
|
||||||
|
pre_tac_seq.push_back(pre_tac);
|
||||||
} else {
|
} else {
|
||||||
buffer<expr> args;
|
buffer<expr> args;
|
||||||
if (get_app_args(pre_tac, args) == get_and_then_tac_fn()) {
|
if (get_app_args(pre_tac, args) == get_and_then_tac_fn()) {
|
||||||
|
@ -1548,47 +1551,64 @@ static void extract_begin_end_tactics(expr pre_tac, buffer<expr> & pre_tac_seq)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void elaborator::try_using_begin_end(substitution & subst, expr const & mvar, proof_state ps, expr const & pre_tac) {
|
bool elaborator::try_using_begin_end(substitution & subst, expr const & mvar, proof_state ps, expr const & pre_tac) {
|
||||||
lean_assert(is_begin_end_annotation(pre_tac));
|
lean_assert(is_begin_end_annotation(pre_tac));
|
||||||
buffer<expr> pre_tac_seq;
|
buffer<expr> pre_tac_seq;
|
||||||
extract_begin_end_tactics(get_annotation_arg(pre_tac), pre_tac_seq);
|
extract_begin_end_tactics(get_annotation_arg(pre_tac), pre_tac_seq);
|
||||||
for (expr ptac : pre_tac_seq) {
|
for (expr ptac : pre_tac_seq) {
|
||||||
expr new_ptac = subst.instantiate_all(ptac);
|
if (is_begin_end_annotation(ptac)) {
|
||||||
if (auto tac = pre_tactic_to_tactic(new_ptac)) {
|
goals gs = ps.get_goals();
|
||||||
try {
|
if (!gs)
|
||||||
proof_state_seq seq = (*tac)(env(), ios(), ps);
|
throw_elaborator_exception("invalid nested begin-end block, there are no goals to be solved", ptac);
|
||||||
auto r = seq.pull();
|
goal g = head(gs);
|
||||||
if (!r) {
|
expr mvar = g.get_mvar();
|
||||||
// tactic failed to produce any result
|
proof_state focus_ps(ps, goals(g));
|
||||||
display_unsolved_proof_state(mvar, ps, "tactic failed", ptac);
|
if (!try_using_begin_end(subst, mvar, focus_ps, ptac))
|
||||||
return;
|
return false;
|
||||||
}
|
substitution ps_new_subst = ps.get_subst();
|
||||||
if (m_ctx.m_flycheck_goals) {
|
name const & mvar_name = mlocal_name(mvar);
|
||||||
if (auto p = pip()->get_pos_info(ptac)) {
|
ps_new_subst.assign(mvar_name, *subst.get_expr(mvar_name));
|
||||||
auto out = regular(env(), ios());
|
ps = proof_state(ps, tail(gs), ps_new_subst);
|
||||||
flycheck_information info(out);
|
} else {
|
||||||
if (info.enabled()) {
|
expr new_ptac = subst.instantiate_all(ptac);
|
||||||
display_information_pos(out, pip()->get_file_name(), p->first, p->second);
|
if (auto tac = pre_tactic_to_tactic(new_ptac)) {
|
||||||
out << " proof state:\n" << ps.pp(env(), ios()) << "\n";
|
try {
|
||||||
|
proof_state_seq seq = (*tac)(env(), ios(), ps);
|
||||||
|
auto r = seq.pull();
|
||||||
|
if (!r) {
|
||||||
|
// tactic failed to produce any result
|
||||||
|
display_unsolved_proof_state(mvar, ps, "tactic failed", ptac);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (m_ctx.m_flycheck_goals) {
|
||||||
|
if (auto p = pip()->get_pos_info(ptac)) {
|
||||||
|
auto out = regular(env(), ios());
|
||||||
|
flycheck_information info(out);
|
||||||
|
if (info.enabled()) {
|
||||||
|
display_information_pos(out, pip()->get_file_name(), p->first, p->second);
|
||||||
|
out << " proof state:\n" << ps.pp(env(), ios()) << "\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ps = r->first;
|
||||||
|
} catch (tactic_exception & ex) {
|
||||||
|
display_tactic_exception(ex, ps, ptac);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
ps = r->first;
|
} else {
|
||||||
} catch (tactic_exception & ex) {
|
return false;
|
||||||
display_tactic_exception(ex, ps, ptac);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty(ps.get_goals())) {
|
if (!empty(ps.get_goals())) {
|
||||||
display_unsolved_proof_state(mvar, ps, "unsolved subgoals", pre_tac);
|
display_unsolved_proof_state(mvar, ps, "unsolved subgoals", pre_tac);
|
||||||
|
return false;
|
||||||
} else {
|
} else {
|
||||||
subst = ps.get_subst();
|
subst = ps.get_subst();
|
||||||
expr v = subst.instantiate(mvar);
|
expr v = subst.instantiate(mvar);
|
||||||
subst.assign(mlocal_name(mvar), v);
|
subst.assign(mlocal_name(mvar), v);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -150,7 +150,7 @@ class elaborator : public coercion_info_manager {
|
||||||
optional<tactic> pre_tactic_to_tactic(expr const & pre_tac);
|
optional<tactic> pre_tactic_to_tactic(expr const & pre_tac);
|
||||||
bool try_using(substitution & subst, expr const & mvar, proof_state const & ps,
|
bool try_using(substitution & subst, expr const & mvar, proof_state const & ps,
|
||||||
expr const & pre_tac, tactic const & tac, bool show_failure);
|
expr const & pre_tac, tactic const & tac, bool show_failure);
|
||||||
void try_using_begin_end(substitution & subst, expr const & mvar, proof_state ps, expr const & pre_tac);
|
bool try_using_begin_end(substitution & subst, expr const & mvar, proof_state ps, expr const & pre_tac);
|
||||||
void solve_unassigned_mvar(substitution & subst, expr mvar, name_set & visited);
|
void solve_unassigned_mvar(substitution & subst, expr mvar, name_set & visited);
|
||||||
expr solve_unassigned_mvars(substitution & subst, expr e, name_set & visited);
|
expr solve_unassigned_mvars(substitution & subst, expr e, name_set & visited);
|
||||||
expr solve_unassigned_mvars(substitution & subst, expr const & e);
|
expr solve_unassigned_mvars(substitution & subst, expr const & e);
|
||||||
|
|
29
tests/lean/run/nested_begin_end.lean
Normal file
29
tests/lean/run/nested_begin_end.lean
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
example (p q : Prop) : p ∧ q ↔ q ∧ p :=
|
||||||
|
begin
|
||||||
|
apply iff.intro,
|
||||||
|
begin
|
||||||
|
intro H,
|
||||||
|
apply and.intro,
|
||||||
|
apply (and.elim_right H),
|
||||||
|
apply (and.elim_left H)
|
||||||
|
end,
|
||||||
|
begin
|
||||||
|
intro H,
|
||||||
|
apply and.intro,
|
||||||
|
apply (and.elim_right H),
|
||||||
|
apply (and.elim_left H)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
example (p q : Prop) : p ∧ q ↔ q ∧ p :=
|
||||||
|
begin
|
||||||
|
apply iff.intro,
|
||||||
|
{intro H,
|
||||||
|
apply and.intro,
|
||||||
|
apply (and.elim_right H),
|
||||||
|
apply (and.elim_left H)},
|
||||||
|
{intro H,
|
||||||
|
apply and.intro,
|
||||||
|
apply (and.elim_right H),
|
||||||
|
apply (and.elim_left H)}
|
||||||
|
end
|
Loading…
Reference in a new issue