diff --git a/src/frontends/lean/parser.cpp b/src/frontends/lean/parser.cpp index 3c28682a03..665d6f4221 100644 --- a/src/frontends/lean/parser.cpp +++ b/src/frontends/lean/parser.cpp @@ -1076,6 +1076,54 @@ expr parser::parse_binder(unsigned rbp) { } } +/* Lean allow binders of the form ID_1 ... ID_n 'op' S + Where 'op' is an infix operator, and s an expression (i.e., "collection"). + This notation expands to: + (ID_1 ... ID_n : _) (H_1 : ID_1 'op' S) ... (H_n : ID_n 'op' S) + + This method return true if the next token is an infix operator, + and populates r with the locals above. +*/ +bool parser::parse_binder_collection(buffer> const & names, binder_info const & bi, buffer & r) { + if (!curr_is_keyword()) + return false; + name tk = get_token_info().value(); + list> trans_list = led().find(tk); + if (length(trans_list) != 1) + return false; + pair const & p = head(trans_list); + list const & acc_lst = p.second.is_accepting(); + if (length(acc_lst) != 1) + return false; // no overloading + notation::accepting const & acc = head(acc_lst); + lean_assert(!acc.get_postponed()); + expr pred = acc.get_expr(); + unsigned rbp = p.first.get_action().rbp(); + next(); // consume tk + expr S = parse_expr(rbp); + unsigned old_sz = r.size(); + /* Add (ID_1 ... ID_n : _) to r */ + for (auto p : names) { + expr arg_type = save_pos(mk_expr_placeholder(), p.first); + save_identifier_info(p.first, p.second); + expr local = save_pos(mk_local(p.second, arg_type, bi), p.first); + add_local(local); + r.push_back(local); + } + /* Add (H_1 : ID_1 'op' S) ... (H_n : ID_n 'op' S) */ + unsigned i = old_sz; + for (auto p : names) { + expr ID = r[i]; + expr args[2] = {ID, S}; + expr ID_op_S = instantiate_rev(pred, 2, args); + expr local = save_pos(mk_local("H", ID_op_S, bi), p.first); + add_local(local); + r.push_back(local); + i++; + } + return true; +} + /** \brief Parse ID ... ID ':' expr, where the expression represents the type of the identifiers. @@ -1092,6 +1140,8 @@ void parser::parse_binder_block(buffer & r, binder_info const & bi, unsign if (curr_is_token(get_colon_tk())) { next(); type = parse_expr(rbp); + } else if (parse_binder_collection(names, bi, r)) { + return; } for (auto p : names) { expr arg_type = type ? *type : save_pos(mk_expr_placeholder(), p.first); diff --git a/src/frontends/lean/parser.h b/src/frontends/lean/parser.h index 0df758d90b..03b7bfa49c 100644 --- a/src/frontends/lean/parser.h +++ b/src/frontends/lean/parser.h @@ -199,6 +199,7 @@ class parser : public pos_info_provider { expr parse_inst_implicit_decl(); void parse_inst_implicit_decl(buffer & r); expr parse_binder_core(binder_info const & bi, unsigned rbp); + bool parse_binder_collection(buffer> const & names, binder_info const & bi, buffer & r); void parse_binder_block(buffer & r, binder_info const & bi, unsigned rbp); void parse_binders_core(buffer & r, buffer * nentries, bool & last_block_delimited, unsigned rbp, bool simple_only); local_environment parse_binders(buffer & r, buffer * nentries, bool & last_block_delimited, diff --git a/tests/lean/run/cute_binders.lean b/tests/lean/run/cute_binders.lean new file mode 100644 index 0000000000..689ede441b --- /dev/null +++ b/tests/lean/run/cute_binders.lean @@ -0,0 +1,21 @@ +definition set (A : Type) := A → Prop +definition mem {A : Type} (a : A) (s : set A) : Prop := +s a +definition range (lower : nat) (upper : nat) : set nat := +λ a, lower ≤ a ∧ a ≤ upper +infix ` ∈ ` := mem + +local notation `[` L `, ` U `]` := range L U + +variables s : set nat +variables p : nat → nat → Prop + + +-- check a ∈ s +set_option pp.binder_types true +check ∀ b c a ∈ s, a + b + c > 0 +-- ∀ (b c a : ℕ), b ∈ s → c ∈ s → a ∈ s → a + b + c > 0 : Prop +check ∀ a < 5, p a (a+1) +-- ∀ (a : ℕ), a < 5 → p a (a + 1) : Prop +check ∀ a b ∈ [2, 3], p a b +-- ∀ (a b : ℕ), a ∈ [2, 3] → b ∈ [2, 3] → p a b