feat(frontends/lean): add proof-qed expression
Remark: we still have to add support to it in the elaborator. Right now, it is just an embellished parenthesis.
This commit is contained in:
parent
c8e20ff3c0
commit
010ecebfea
7 changed files with 163 additions and 2 deletions
|
@ -80,6 +80,8 @@
|
||||||
(,(rx symbol-start (or "bool" "int" "nat" "real" "Prop" "Type" "ℕ" "ℤ") symbol-end) . 'font-lock-type-face)
|
(,(rx symbol-start (or "bool" "int" "nat" "real" "Prop" "Type" "ℕ" "ℤ") symbol-end) . 'font-lock-type-face)
|
||||||
;; sorry
|
;; sorry
|
||||||
(,(rx symbol-start "sorry" symbol-end) . 'font-lock-warning-face)
|
(,(rx symbol-start "sorry" symbol-end) . 'font-lock-warning-face)
|
||||||
|
;; extra-keywords
|
||||||
|
(,(rx (or "∎")) . 'font-lock-keyword-face)
|
||||||
;; lean-keywords
|
;; lean-keywords
|
||||||
(, (concat "\\(" (regexp-opt lean-keywords 'words) "\\)")
|
(, (concat "\\(" (regexp-opt lean-keywords 'words) "\\)")
|
||||||
(1 'font-lock-keyword-face)))))
|
(1 'font-lock-keyword-face)))))
|
||||||
|
|
|
@ -26,7 +26,7 @@ namespace lean {
|
||||||
namespace notation {
|
namespace notation {
|
||||||
static name g_llevel_curly(".{"), g_rcurly("}"), g_in("in"), g_colon(":"), g_assign(":=");
|
static name g_llevel_curly(".{"), g_rcurly("}"), g_in("in"), g_colon(":"), g_assign(":=");
|
||||||
static name g_comma(","), g_visible("[visible]"), g_from("from"), g_using("using");
|
static name g_comma(","), g_visible("[visible]"), g_from("from"), g_using("using");
|
||||||
static name g_then("then"), g_have("have"), g_by("by"), g_qed("qed"), g_end("end");
|
static name g_then("then"), g_have("have"), g_by("by"), g_proof("proof"), g_qed("qed"), g_end("end");
|
||||||
static name g_take("take"), g_assume("assume"), g_show("show"), g_fun("fun");
|
static name g_take("take"), g_assume("assume"), g_show("show"), g_fun("fun");
|
||||||
|
|
||||||
static expr parse_Type(parser & p, unsigned, expr const *, pos_info const & pos) {
|
static expr parse_Type(parser & p, unsigned, expr const *, pos_info const & pos) {
|
||||||
|
@ -153,11 +153,21 @@ static expr parse_begin_end(parser & p, unsigned, expr const *, pos_info const &
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static expr parse_proof_qed_core(parser & p, pos_info const & pos) {
|
||||||
|
expr r = p.save_pos(mk_proof_qed_annotation(p.parse_expr()), pos);
|
||||||
|
p.check_token_next(g_qed, "invalid proof-qed, 'qed' expected");
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
static expr parse_proof(parser & p, expr const & prop) {
|
static expr parse_proof(parser & p, expr const & prop) {
|
||||||
if (p.curr_is_token(g_from)) {
|
if (p.curr_is_token(g_from)) {
|
||||||
// parse: 'from' expr
|
// parse: 'from' expr
|
||||||
p.next();
|
p.next();
|
||||||
return p.parse_expr();
|
return p.parse_expr();
|
||||||
|
} else if (p.curr_is_token(g_proof)) {
|
||||||
|
auto pos = p.pos();
|
||||||
|
p.next();
|
||||||
|
return parse_proof_qed_core(p, pos);
|
||||||
} else if (p.curr_is_token(g_by)) {
|
} else if (p.curr_is_token(g_by)) {
|
||||||
// parse: 'by' tactic
|
// parse: 'by' tactic
|
||||||
auto pos = p.pos();
|
auto pos = p.pos();
|
||||||
|
@ -369,6 +379,10 @@ static expr parse_rparen(parser & p, unsigned, expr const * args, pos_info const
|
||||||
return args[0];
|
return args[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static expr parse_proof_qed(parser & p, unsigned, expr const *, pos_info const & pos) {
|
||||||
|
return parse_proof_qed_core(p, pos);
|
||||||
|
}
|
||||||
|
|
||||||
parse_table init_nud_table() {
|
parse_table init_nud_table() {
|
||||||
action Expr(mk_expr_action());
|
action Expr(mk_expr_action());
|
||||||
action Skip(mk_skip_action());
|
action Skip(mk_skip_action());
|
||||||
|
@ -390,6 +404,7 @@ parse_table init_nud_table() {
|
||||||
r = r.add({transition("#", mk_ext_action(parse_overwrite_notation))}, x0);
|
r = r.add({transition("#", mk_ext_action(parse_overwrite_notation))}, x0);
|
||||||
r = r.add({transition("@", mk_ext_action(parse_explicit_expr))}, x0);
|
r = r.add({transition("@", mk_ext_action(parse_explicit_expr))}, x0);
|
||||||
r = r.add({transition("begin", mk_ext_action(parse_begin_end))}, x0);
|
r = r.add({transition("begin", mk_ext_action(parse_begin_end))}, x0);
|
||||||
|
r = r.add({transition("proof", mk_ext_action(parse_proof_qed))}, x0);
|
||||||
r = r.add({transition("sorry", mk_ext_action(parse_sorry))}, x0);
|
r = r.add({transition("sorry", mk_ext_action(parse_sorry))}, x0);
|
||||||
r = r.add({transition("including", mk_ext_action(parse_including_expr))}, x0);
|
r = r.add({transition("including", mk_ext_action(parse_including_expr))}, x0);
|
||||||
return r;
|
return r;
|
||||||
|
|
|
@ -315,6 +315,8 @@ public:
|
||||||
return visit_choice(e, some_expr(t), cs);
|
return visit_choice(e, some_expr(t), cs);
|
||||||
} else if (is_by(e)) {
|
} else if (is_by(e)) {
|
||||||
return visit_by(e, some_expr(t), cs);
|
return visit_by(e, some_expr(t), cs);
|
||||||
|
} else if (is_proof_qed_annotation(e)) {
|
||||||
|
return visit_proof_qed(e, some_expr(t), cs);
|
||||||
} else {
|
} else {
|
||||||
return visit(e, cs);
|
return visit(e, cs);
|
||||||
}
|
}
|
||||||
|
@ -343,6 +345,12 @@ public:
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expr visit_proof_qed(expr const & e, optional<expr> const & /* t */, constraint_seq & cs) {
|
||||||
|
lean_assert(is_proof_qed_annotation(e));
|
||||||
|
// TODO(Leo)
|
||||||
|
return visit(get_annotation_arg(e), cs);
|
||||||
|
}
|
||||||
|
|
||||||
/** \brief Make sure \c f is really a function, if it is not, try to apply coercions.
|
/** \brief Make sure \c f is really a function, if it is not, try to apply coercions.
|
||||||
The result is a pair <tt>new_f, f_type</tt>, where new_f is the new value for \c f,
|
The result is a pair <tt>new_f, f_type</tt>, where new_f is the new value for \c f,
|
||||||
and \c f_type is its type (and a Pi-expression)
|
and \c f_type is its type (and a Pi-expression)
|
||||||
|
@ -693,6 +701,8 @@ public:
|
||||||
return visit_let_value(e, cs);
|
return visit_let_value(e, cs);
|
||||||
} else if (is_by(e)) {
|
} else if (is_by(e)) {
|
||||||
return visit_by(e, none_expr(), cs);
|
return visit_by(e, none_expr(), cs);
|
||||||
|
} else if (is_proof_qed_annotation(e)) {
|
||||||
|
return visit_proof_qed(e, none_expr(), cs);
|
||||||
} else if (is_no_info(e)) {
|
} else if (is_no_info(e)) {
|
||||||
flet<bool> let(m_no_info, true);
|
flet<bool> let(m_no_info, true);
|
||||||
return visit(get_annotation_arg(e), cs);
|
return visit(get_annotation_arg(e), cs);
|
||||||
|
|
|
@ -65,6 +65,7 @@ static char const * g_pi_unicode = "\u03A0";
|
||||||
static char const * g_forall_unicode = "\u2200";
|
static char const * g_forall_unicode = "\u2200";
|
||||||
static char const * g_arrow_unicode = "\u2192";
|
static char const * g_arrow_unicode = "\u2192";
|
||||||
static char const * g_cup = "\u2294";
|
static char const * g_cup = "\u2294";
|
||||||
|
static char const * g_qed_unicode = "∎";
|
||||||
|
|
||||||
token_table init_token_table() {
|
token_table init_token_table() {
|
||||||
token_table t;
|
token_table t;
|
||||||
|
@ -87,7 +88,7 @@ token_table init_token_table() {
|
||||||
|
|
||||||
pair<char const *, char const *> aliases[] =
|
pair<char const *, char const *> aliases[] =
|
||||||
{{g_lambda_unicode, "fun"}, {"forall", "Pi"}, {g_forall_unicode, "Pi"}, {g_pi_unicode, "Pi"},
|
{{g_lambda_unicode, "fun"}, {"forall", "Pi"}, {g_forall_unicode, "Pi"}, {g_pi_unicode, "Pi"},
|
||||||
{nullptr, nullptr}};
|
{g_qed_unicode, "qed"}, {nullptr, nullptr}};
|
||||||
|
|
||||||
pair<char const *, char const *> cmd_aliases[] =
|
pair<char const *, char const *> cmd_aliases[] =
|
||||||
{{"parameter", "variable"}, {"parameters", "variables"}, {"lemma", "theorem"},
|
{{"parameter", "variable"}, {"parameters", "variables"}, {"lemma", "theorem"},
|
||||||
|
|
|
@ -136,11 +136,20 @@ name const & get_show_name() {
|
||||||
return g_show;
|
return g_show;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
name const & get_proof_qed_name() {
|
||||||
|
static name g_proof_qed("proof-qed");
|
||||||
|
static register_annotation_fn g_proof_qed_annotation(g_proof_qed);
|
||||||
|
return g_proof_qed;
|
||||||
|
}
|
||||||
|
|
||||||
static name g_have_name = get_have_name(); // force 'have' annotation to be registered
|
static name g_have_name = get_have_name(); // force 'have' annotation to be registered
|
||||||
static name g_show_name = get_show_name(); // force 'show' annotation to be registered
|
static name g_show_name = get_show_name(); // force 'show' annotation to be registered
|
||||||
|
static name g_proof_qed_name = get_proof_qed_name(); // force 'proof-qed' annotation to be registered
|
||||||
|
|
||||||
expr mk_have_annotation(expr const & e) { return mk_annotation(get_have_name(), e); }
|
expr mk_have_annotation(expr const & e) { return mk_annotation(get_have_name(), e); }
|
||||||
expr mk_show_annotation(expr const & e) { return mk_annotation(get_show_name(), e); }
|
expr mk_show_annotation(expr const & e) { return mk_annotation(get_show_name(), e); }
|
||||||
|
expr mk_proof_qed_annotation(expr const & e) { return mk_annotation(get_proof_qed_name(), e); }
|
||||||
bool is_have_annotation(expr const & e) { return is_annotation(e, get_have_name()); }
|
bool is_have_annotation(expr const & e) { return is_annotation(e, get_have_name()); }
|
||||||
bool is_show_annotation(expr const & e) { return is_annotation(e, get_show_name()); }
|
bool is_show_annotation(expr const & e) { return is_annotation(e, get_show_name()); }
|
||||||
|
bool is_proof_qed_annotation(expr const & e) { return is_annotation(e, get_proof_qed_name()); }
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,10 +60,14 @@ expr copy_annotations(expr const & from, expr const & to);
|
||||||
expr mk_have_annotation(expr const & e);
|
expr mk_have_annotation(expr const & e);
|
||||||
/** \brief Tag \c e as a 'show'-expression. 'show' is a pre-registered annotation. */
|
/** \brief Tag \c e as a 'show'-expression. 'show' is a pre-registered annotation. */
|
||||||
expr mk_show_annotation(expr const & e);
|
expr mk_show_annotation(expr const & e);
|
||||||
|
/** \brief Tag \c e as a 'proof-qed'-expression. 'proof-qed' is a pre-registered annotation. */
|
||||||
|
expr mk_proof_qed_annotation(expr const & e);
|
||||||
/** \brief Return true iff \c e was created using #mk_have_annotation. */
|
/** \brief Return true iff \c e was created using #mk_have_annotation. */
|
||||||
bool is_have_annotation(expr const & e);
|
bool is_have_annotation(expr const & e);
|
||||||
/** \brief Return true iff \c e was created using #mk_show_annotation. */
|
/** \brief Return true iff \c e was created using #mk_show_annotation. */
|
||||||
bool is_show_annotation(expr const & e);
|
bool is_show_annotation(expr const & e);
|
||||||
|
/** \brief Return true iff \c e was created using #mk_proof_qed_annotation. */
|
||||||
|
bool is_proof_qed_annotation(expr const & e);
|
||||||
|
|
||||||
/** \brief Return the serialization 'opcode' for annotation macros. */
|
/** \brief Return the serialization 'opcode' for annotation macros. */
|
||||||
std::string const & get_annotation_opcode();
|
std::string const & get_annotation_opcode();
|
||||||
|
|
120
tests/lean/run/div2.lean
Normal file
120
tests/lean/run/div2.lean
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
import logic data.nat.sub struc.relation data.prod
|
||||||
|
import tools.fake_simplifier
|
||||||
|
|
||||||
|
open nat relation relation.iff_ops prod
|
||||||
|
open fake_simplifier decidable
|
||||||
|
open eq_ops
|
||||||
|
|
||||||
|
namespace nat
|
||||||
|
|
||||||
|
-- A general recursion principle
|
||||||
|
-- -----------------------------
|
||||||
|
--
|
||||||
|
-- Data:
|
||||||
|
--
|
||||||
|
-- dom, codom : Type
|
||||||
|
-- default : codom
|
||||||
|
-- measure : dom → ℕ
|
||||||
|
-- rec_val : dom → (dom → codom) → codom
|
||||||
|
--
|
||||||
|
-- and a proof
|
||||||
|
--
|
||||||
|
-- rec_decreasing : ∀m, m ≥ measure x, rec_val x f = rec_val x (restrict f m)
|
||||||
|
--
|
||||||
|
-- ... which says that the recursive call only depends on f at values with measure less than x,
|
||||||
|
-- in the sense that changing other values to the default value doesn't change the result.
|
||||||
|
--
|
||||||
|
-- The result is a function f = rec_measure, satisfying
|
||||||
|
--
|
||||||
|
-- f x = rec_val x f
|
||||||
|
|
||||||
|
definition restrict {dom codom : Type} (default : codom) (measure : dom → ℕ) (f : dom → codom)
|
||||||
|
(m : ℕ) (x : dom) :=
|
||||||
|
if measure x < m then f x else default
|
||||||
|
|
||||||
|
theorem restrict_lt_eq {dom codom : Type} (default : codom) (measure : dom → ℕ) (f : dom → codom)
|
||||||
|
(m : ℕ) (x : dom) (H : measure x < m) :
|
||||||
|
restrict default measure f m x = f x :=
|
||||||
|
if_pos H
|
||||||
|
|
||||||
|
theorem restrict_not_lt_eq {dom codom : Type} (default : codom) (measure : dom → ℕ)
|
||||||
|
(f : dom → codom) (m : ℕ) (x : dom) (H : ¬ measure x < m) :
|
||||||
|
restrict default measure f m x = default :=
|
||||||
|
if_neg H
|
||||||
|
|
||||||
|
definition rec_measure_aux {dom codom : Type} (default : codom) (measure : dom → ℕ)
|
||||||
|
(rec_val : dom → (dom → codom) → codom) : ℕ → dom → codom :=
|
||||||
|
rec (λx, default) (λm g x, if measure x < succ m then rec_val x g else default)
|
||||||
|
|
||||||
|
definition rec_measure {dom codom : Type} (default : codom) (measure : dom → ℕ)
|
||||||
|
(rec_val : dom → (dom → codom) → codom) (x : dom) : codom :=
|
||||||
|
rec_measure_aux default measure rec_val (succ (measure x)) x
|
||||||
|
|
||||||
|
theorem rec_measure_aux_spec {dom codom : Type} (default : codom) (measure : dom → ℕ)
|
||||||
|
(rec_val : dom → (dom → codom) → codom)
|
||||||
|
(rec_decreasing : ∀g1 g2 x, (∀z, measure z < measure x → g1 z = g2 z) →
|
||||||
|
rec_val x g1 = rec_val x g2)
|
||||||
|
(m : ℕ) :
|
||||||
|
let f' := rec_measure_aux default measure rec_val in
|
||||||
|
let f := rec_measure default measure rec_val in
|
||||||
|
∀x, f' m x = restrict default measure f m x :=
|
||||||
|
let f' := rec_measure_aux default measure rec_val in
|
||||||
|
let f := rec_measure default measure rec_val in
|
||||||
|
case_strong_induction_on m
|
||||||
|
(take x,
|
||||||
|
have H1 : f' 0 x = default, from rfl,
|
||||||
|
have H2 : ¬ measure x < 0, from not_lt_zero,
|
||||||
|
have H3 : restrict default measure f 0 x = default, from if_neg H2,
|
||||||
|
show f' 0 x = restrict default measure f 0 x, from H1 ⬝ H3⁻¹)
|
||||||
|
(take m,
|
||||||
|
assume IH: ∀n, n ≤ m → ∀x, f' n x = restrict default measure f n x,
|
||||||
|
take x : dom,
|
||||||
|
show f' (succ m) x = restrict default measure f (succ m) x, from
|
||||||
|
by_cases -- (measure x < succ m)
|
||||||
|
(assume H1 : measure x < succ m,
|
||||||
|
have H2a : ∀z, measure z < measure x → f' m z = f z,
|
||||||
|
proof
|
||||||
|
take z,
|
||||||
|
assume Hzx : measure z < measure x,
|
||||||
|
calc
|
||||||
|
f' m z = restrict default measure f m z : IH m le_refl z
|
||||||
|
... = f z : restrict_lt_eq _ _ _ _ _ (lt_le_trans Hzx (lt_succ_imp_le H1))
|
||||||
|
∎,
|
||||||
|
have H2 : f' (succ m) x = rec_val x f,
|
||||||
|
proof
|
||||||
|
calc
|
||||||
|
f' (succ m) x = if measure x < succ m then rec_val x (f' m) else default : rfl
|
||||||
|
... = rec_val x (f' m) : if_pos H1
|
||||||
|
... = rec_val x f : rec_decreasing (f' m) f x H2a
|
||||||
|
∎,
|
||||||
|
let m' := measure x in
|
||||||
|
have H3a : ∀z, measure z < m' → f' m' z = f z,
|
||||||
|
proof
|
||||||
|
take z,
|
||||||
|
assume Hzx : measure z < measure x,
|
||||||
|
calc
|
||||||
|
f' m' z = restrict default measure f m' z : IH _ (lt_succ_imp_le H1) _
|
||||||
|
... = f z : restrict_lt_eq _ _ _ _ _ Hzx
|
||||||
|
qed,
|
||||||
|
have H3 : restrict default measure f (succ m) x = rec_val x f,
|
||||||
|
proof
|
||||||
|
calc
|
||||||
|
restrict default measure f (succ m) x = f x : if_pos H1
|
||||||
|
... = f' (succ m') x : eq.refl _
|
||||||
|
... = if measure x < succ m' then rec_val x (f' m') else default : rfl
|
||||||
|
... = rec_val x (f' m') : if_pos self_lt_succ
|
||||||
|
... = rec_val x f : rec_decreasing _ _ _ H3a
|
||||||
|
qed,
|
||||||
|
show f' (succ m) x = restrict default measure f (succ m) x,
|
||||||
|
from H2 ⬝ H3⁻¹)
|
||||||
|
(assume H1 : ¬ measure x < succ m,
|
||||||
|
have H2 : f' (succ m) x = default, from
|
||||||
|
calc
|
||||||
|
f' (succ m) x = if measure x < succ m then rec_val x (f' m) else default : rfl
|
||||||
|
... = default : if_neg H1,
|
||||||
|
have H3 : restrict default measure f (succ m) x = default,
|
||||||
|
from if_neg H1,
|
||||||
|
show f' (succ m) x = restrict default measure f (succ m) x,
|
||||||
|
from H2 ⬝ H3⁻¹))
|
||||||
|
|
||||||
|
end nat
|
Loading…
Reference in a new issue