diff --git a/src/frontends/lean/parser_expr.cpp b/src/frontends/lean/parser_expr.cpp index a389dd160..63fbd651f 100644 --- a/src/frontends/lean/parser_expr.cpp +++ b/src/frontends/lean/parser_expr.cpp @@ -1002,6 +1002,34 @@ expr parser_imp::parse_show_expr() { } } +/** \brief Parse 'have' Id ':' expr, 'from' expr, expr and + 'have' Id ':' expr, 'by' expr, expr +*/ +expr parser_imp::parse_have_expr() { + auto p = pos(); + next(); + mk_scope scope(*this); + name id = check_identifier_next("invalid 'have' expression, identifier expected"); + check_colon_next("invalid 'have' expression, ':' expected"); + expr t = parse_expr(); + check_comma_next("invalid 'have' expression, ',' expected"); + expr val; + if (curr() == scanner::token::By) { + next(); + tactic tac = parse_tactic_expr(); + expr r = mk_placeholder(some_expr(t)); + m_tactic_hints.insert(mk_pair(r, tac)); + val = save(r, p); + } else if (curr_is_identifier() && curr_name() == g_from) { + next(); + val = parse_expr(); + } + check_comma_next("invalid 'have' expression, ',' expected"); + register_binding(id); + expr body = parse_expr(); + return mk_let(id, t, val, body); +} + /** \brief Parse 'by' tactic */ expr parser_imp::parse_by_expr() { auto p = pos(); @@ -1030,10 +1058,11 @@ expr parser_imp::parse_nud() { case scanner::token::Placeholder: return parse_placeholder(); case scanner::token::Type: return parse_type(false); case scanner::token::Show: return parse_show_expr(); + case scanner::token::Have: return parse_have_expr(); + case scanner::token::By: return parse_by_expr(); case scanner::token::Tuple: return parse_tuple(); case scanner::token::Proj1: return parse_proj(true); case scanner::token::Proj2: return parse_proj(false); - case scanner::token::By: return parse_by_expr(); default: throw parser_error("invalid expression, unexpected token", pos()); } diff --git a/src/frontends/lean/parser_imp.h b/src/frontends/lean/parser_imp.h index 263465061..e7e6e6621 100644 --- a/src/frontends/lean/parser_imp.h +++ b/src/frontends/lean/parser_imp.h @@ -320,6 +320,7 @@ private: expr parse_proj(bool first); tactic parse_tactic_macro(name tac_id, pos_info const & p); expr parse_show_expr(); + expr parse_have_expr(); expr parse_calc(); expr parse_nud_id(); expr parse_nud(); diff --git a/tests/lean/j2.lean b/tests/lean/j2.lean new file mode 100644 index 000000000..a506dcb99 --- /dev/null +++ b/tests/lean/j2.lean @@ -0,0 +1,33 @@ +import macros +import tactic +using Nat + +definition dvd (a b : Nat) : Bool := ∃ c, a * c = b + +infix 50 | : dvd + +theorem dvd_trans {a b c} (H1 : a | b) (H2 : b | c) : a | c +:= obtain (w1 : Nat) (Hw1 : a * w1 = b), from H1, + obtain (w2 : Nat) (Hw2 : b * w2 = c), from H2, + exists_intro (w1 * w2) + (calc a * (w1 * w2) = a * w1 * w2 : symm (mul_assoc a w1 w2) + ... = b * w2 : { Hw1 } + ... = c : Hw2) + +definition prime p := p ≥ 2 ∧ forall m, m | p → m = 1 ∨ m = p + +theorem not_prime_eq (n : Nat) (H1 : n ≥ 2) (H2 : ¬ prime n) : ∃ m, m | n ∧ m ≠ 1 ∧ m ≠ n +:= have H3 : ¬ n ≥ 2 ∨ ¬ (∀ m : Nat, m | n → m = 1 ∨ m = n), + from (not_and _ _ ◂ H2), + have H4 : ¬ ¬ n ≥ 2, + from ((symm (not_not_eq _)) ◂ H1), + obtain (m : Nat) (H5 : ¬ (m | n → m = 1 ∨ m = n)), + from (not_forall_elim (resolve1 H3 H4)), + have H6 : m | n ∧ ¬ (m = 1 ∨ m = n), + from ((not_implies _ _) ◂ H5), + have H7 : ¬ (m = 1 ∨ m = n) ↔ (m ≠ 1 ∧ m ≠ n), + from (not_or (m = 1) (m = n)), + have H8 : m | n ∧ m ≠ 1 ∧ m ≠ n, + from subst H6 H7, + show ∃ m, m | n ∧ m ≠ 1 ∧ m ≠ n, + from exists_intro m H8 diff --git a/tests/lean/j2.lean.expected.out b/tests/lean/j2.lean.expected.out new file mode 100644 index 000000000..d3758b577 --- /dev/null +++ b/tests/lean/j2.lean.expected.out @@ -0,0 +1,9 @@ + Set: pp::colors + Set: pp::unicode + Imported 'macros' + Imported 'tactic' + Using: Nat + Defined: dvd + Proved: dvd_trans + Defined: prime + Proved: not_prime_eq