feat(frontends/lean): allow user to overload notation containing foldr/foldl and/or scoped expressions

see new tests for a summary of new features

see issue #800
This commit is contained in:
Leonardo de Moura 2015-08-16 18:21:29 -07:00
parent ffde40a500
commit ea04414058
12 changed files with 305 additions and 110 deletions

View file

@ -181,7 +181,7 @@ static bool print_parse_table(parser const & p, parse_table const & t, bool nud,
os = os.update(get_pp_preterm_name(), true); os = os.update(get_pp_preterm_name(), true);
ios.set_options(os); ios.set_options(os);
optional<token_table> tt(get_token_table(p.env())); optional<token_table> tt(get_token_table(p.env()));
t.for_each([&](unsigned num, notation::transition const * ts, list<pair<unsigned, expr>> const & overloads) { t.for_each([&](unsigned num, notation::transition const * ts, list<notation::accepting> const & overloads) {
if (uses_some_token(num, ts, tokens)) { if (uses_some_token(num, ts, tokens)) {
io_state_stream out = regular(p.env(), ios); io_state_stream out = regular(p.env(), ios);
if (tactic_table) if (tactic_table)

View file

@ -194,6 +194,24 @@ bool action::is_equal(action const & a) const {
} }
lean_unreachable(); // LCOV_EXCL_LINE lean_unreachable(); // LCOV_EXCL_LINE
} }
// Similar to is_equal, but ignores information that is "irrelevant" for parsing.
// For example, for Exprs actions, get_rec and get_initial are not relevant when parsing the input stream.
// We only need them when we reach an accepting state.
bool action::is_equivalent(action const & a) const {
if (kind() != a.kind())
return false;
switch (kind()) {
case action_kind::Exprs:
return
rbp() == a.rbp() &&
get_sep() == a.get_sep() &&
get_terminator() == a.get_terminator();
case action_kind::ScopedExpr:
return rbp() == a.rbp();
default:
return is_equal(a);
}
}
void action::display(io_state_stream & out) const { void action::display(io_state_stream & out) const {
switch (kind()) { switch (kind()) {
case action_kind::Skip: out << "skip"; break; case action_kind::Skip: out << "skip"; break;
@ -216,15 +234,13 @@ void action::display(io_state_stream & out) const {
out << "(fold" << (is_fold_right() ? "r" : "l"); out << "(fold" << (is_fold_right() ? "r" : "l");
if (get_terminator()) if (get_terminator())
out << "*"; out << "*";
out << " " << rbp() << " " << get_rec(); out << " " << rbp() << " `" << get_sep() << "`";
if (get_initial())
out << " " << *get_initial();
if (get_terminator()) if (get_terminator())
out << *get_terminator(); out << " `" << *get_terminator() << "`";
out << ")"; out << ")";
break; break;
case action_kind::ScopedExpr: case action_kind::ScopedExpr:
out << "(scoped " << rbp() << " " << get_rec() << ")"; out << "(scoped " << rbp() << ")";
break; break;
} }
} }
@ -303,7 +319,7 @@ transition replace(transition const & t, std::function<expr(expr const &)> const
struct parse_table::cell { struct parse_table::cell {
bool m_nud; bool m_nud;
list<pair<unsigned, expr>> m_accept; list<accepting> m_accept;
name_map<list<pair<action, parse_table>>> m_children; name_map<list<pair<action, parse_table>>> m_children;
MK_LEAN_RC(); // Declare m_rc counter MK_LEAN_RC(); // Declare m_rc counter
void dealloc() { delete this; } void dealloc() { delete this; }
@ -326,7 +342,7 @@ list<pair<action, parse_table>> parse_table::find(name const & tk) const {
return list<pair<action, parse_table>>(); return list<pair<action, parse_table>>();
} }
list<pair<unsigned, expr>> const & parse_table::is_accepting() const { list<accepting> const & parse_table::is_accepting() const {
return m_ptr->m_accept; return m_ptr->m_accept;
} }
@ -358,40 +374,47 @@ static void validate_transitions(bool nud, unsigned num, transition const * ts,
throw exception("invalid notation declaration, expression template has more free variables than arguments"); throw exception("invalid notation declaration, expression template has more free variables than arguments");
} }
static list<pair<unsigned, expr>> insert(list<pair<unsigned, expr>> const & l, unsigned priority, expr const & a) { static list<accepting> insert(list<accepting> const & l, unsigned priority, list<action> const & p, expr const & a) {
if (!l) { if (!l) {
return to_list(mk_pair(priority, a)); return to_list(accepting(priority, p, a));
} else if (priority <= head(l).first) { } else if (priority <= head(l).get_prio()) {
return cons(mk_pair(priority, a), l); return cons(accepting(priority, p, a), l);
} else { } else {
return cons(head(l), insert(tail(l), priority, a)); return cons(head(l), insert(tail(l), priority, p, a));
} }
} }
parse_table parse_table::add_core(unsigned num, transition const * ts, expr const & a, parse_table parse_table::add_core(unsigned num, transition const * ts, expr const & a,
unsigned priority, bool overload) const { unsigned priority, bool overload, buffer<action> & post_buffer) const {
parse_table r(new cell(*m_ptr)); parse_table r(new cell(*m_ptr));
if (num == 0) { if (num == 0) {
list<action> postponed = to_list(post_buffer);
if (!overload) { if (!overload) {
r.m_ptr->m_accept = to_list(mk_pair(priority, a)); r.m_ptr->m_accept = to_list(accepting(priority, postponed, a));
} else { } else {
auto new_accept = filter(r.m_ptr->m_accept, [&](pair<unsigned, expr> const & p) { return p.second != a; }); auto new_accept = filter(r.m_ptr->m_accept, [&](accepting const & p) {
r.m_ptr->m_accept = insert(new_accept, priority, a); return p.get_expr() != a || p.get_postponed() != postponed;
});
r.m_ptr->m_accept = insert(new_accept, priority, postponed, a);
} }
} else { } else {
list<pair<action, parse_table>> const * it = r.m_ptr->m_children.find(ts->get_token()); list<pair<action, parse_table>> const * it = r.m_ptr->m_children.find(ts->get_token());
action const & ts_act = ts->get_action();
action_kind k = ts_act.kind();
if (k == action_kind::Exprs || k == action_kind::ScopedExpr)
post_buffer.push_back(ts_act);
parse_table new_child; parse_table new_child;
if (it) { if (it) {
// TODO(Leo): support multiple actions // TODO(Leo): support multiple actions
action const & act = head(*it).first; action const & act = head(*it).first;
parse_table const & child = head(*it).second; parse_table const & child = head(*it).second;
if (act.is_equal(ts->get_action())) { if (act.is_equivalent(ts_act)) {
new_child = child.add_core(num-1, ts+1, a, priority, overload); new_child = child.add_core(num-1, ts+1, a, priority, overload, post_buffer);
} else { } else {
new_child = parse_table().add_core(num-1, ts+1, a, priority, overload); new_child = parse_table().add_core(num-1, ts+1, a, priority, overload, post_buffer);
} }
} else { } else {
new_child = parse_table().add_core(num-1, ts+1, a, priority, overload); new_child = parse_table().add_core(num-1, ts+1, a, priority, overload, post_buffer);
} }
r.m_ptr->m_children.insert(ts->get_token(), to_list(mk_pair(ts->get_action(), new_child))); r.m_ptr->m_children.insert(ts->get_token(), to_list(mk_pair(ts->get_action(), new_child)));
} }
@ -461,14 +484,15 @@ optional<head_index> get_head_index(unsigned num, transition const * ts, expr co
} }
parse_table parse_table::add(unsigned num, transition const * ts, expr const & a, unsigned priority, bool overload) const { parse_table parse_table::add(unsigned num, transition const * ts, expr const & a, unsigned priority, bool overload) const {
buffer<action> postponed;
expr new_a = annotate_macro_subterms(a); expr new_a = annotate_macro_subterms(a);
validate_transitions(is_nud(), num, ts, new_a); validate_transitions(is_nud(), num, ts, new_a);
return add_core(num, ts, new_a, priority, overload); return add_core(num, ts, new_a, priority, overload, postponed);
} }
void parse_table::for_each(buffer<transition> & ts, void parse_table::for_each(buffer<transition> & ts,
std::function<void(unsigned, transition const *, std::function<void(unsigned, transition const *,
list<pair<unsigned, expr>> const &)> const & fn) const { list<accepting> const &)> const & fn) const {
if (!is_nil(m_ptr->m_accept)) if (!is_nil(m_ptr->m_accept))
fn(ts.size(), ts.data(), m_ptr->m_accept); fn(ts.size(), ts.data(), m_ptr->m_accept);
m_ptr->m_children.for_each([&](name const & k, list<pair<action, parse_table>> const & lst) { m_ptr->m_children.for_each([&](name const & k, list<pair<action, parse_table>> const & lst) {
@ -481,7 +505,7 @@ void parse_table::for_each(buffer<transition> & ts,
} }
void parse_table::for_each(std::function<void(unsigned, transition const *, void parse_table::for_each(std::function<void(unsigned, transition const *,
list<pair<unsigned, expr>> const &)> const & fn) const { list<accepting> const &)> const & fn) const {
buffer<transition> tmp; buffer<transition> tmp;
for_each(tmp, fn); for_each(tmp, fn);
} }
@ -490,9 +514,9 @@ parse_table parse_table::merge(parse_table const & s, bool overload) const {
if (is_nud() != s.is_nud()) if (is_nud() != s.is_nud())
throw exception("invalid parse table merge, tables have different kinds"); throw exception("invalid parse table merge, tables have different kinds");
parse_table r(*this); parse_table r(*this);
s.for_each([&](unsigned num, transition const * ts, list<pair<unsigned, expr>> const & accept) { s.for_each([&](unsigned num, transition const * ts, list<accepting> const & accept) {
for (pair<unsigned, expr> const & p : accept) { for (accepting const & acc : accept) {
r = r.add(num, ts, p.second, p.first, overload); r = r.add(num, ts, acc.get_expr(), acc.get_prio(), overload);
} }
}); });
return r; return r;
@ -500,7 +524,7 @@ parse_table parse_table::merge(parse_table const & s, bool overload) const {
bool parse_table::is_nud() const { return m_ptr->m_nud; } bool parse_table::is_nud() const { return m_ptr->m_nud; }
void display(io_state_stream & out, unsigned num, transition const * ts, list<pair<unsigned, expr>> const & es, bool nud, void display(io_state_stream & out, unsigned num, transition const * ts, list<accepting> const & es, bool nud,
optional<token_table> const & tt) { optional<token_table> const & tt) {
if (!nud) if (!nud)
out << "_ "; out << "_ ";
@ -525,24 +549,24 @@ void display(io_state_stream & out, unsigned num, transition const * ts, list<pa
} }
out << " :="; out << " :=";
if (length(es) == 1) { if (length(es) == 1) {
out << " " << head(es).second << "\n"; out << " " << head(es).get_expr() << "\n";
} else { } else {
buffer<pair<unsigned, expr>> tmp; buffer<accepting> tmp;
to_buffer(es, tmp); to_buffer(es, tmp);
out << "\n"; out << "\n";
unsigned i = tmp.size(); unsigned i = tmp.size();
while (i > 0) { while (i > 0) {
--i; --i;
out << " | "; out << " | ";
if (tmp[i].first != LEAN_DEFAULT_NOTATION_PRIORITY) if (tmp[i].get_prio() != LEAN_DEFAULT_NOTATION_PRIORITY)
out << "[priority " << tmp[i].first << "] "; out << "[priority " << tmp[i].get_prio() << "] ";
out << tmp[i].second << "\n"; out << tmp[i].get_expr() << "\n";
} }
} }
} }
void parse_table::display(io_state_stream & out, optional<token_table> const & tt) const { void parse_table::display(io_state_stream & out, optional<token_table> const & tt) const {
for_each([&](unsigned num, transition const * ts, list<pair<unsigned, expr>> const & es) { for_each([&](unsigned num, transition const * ts, list<accepting> const & es) {
::lean::notation::display(out, num, ts, es, is_nud(), tt); ::lean::notation::display(out, num, ts, es, is_nud(), tt);
}); });
} }

View file

@ -86,6 +86,8 @@ public:
std::string const & get_lua_fn() const; std::string const & get_lua_fn() const;
bool is_equal(action const & a) const; bool is_equal(action const & a) const;
bool is_equivalent(action const & a) const;
void display(io_state_stream & out) const; void display(io_state_stream & out) const;
/** \brief Return true iff the action is not Ext or LuaExt */ /** \brief Return true iff the action is not Ext or LuaExt */
@ -129,7 +131,19 @@ inline bool operator!=(transition const & t1, transition const & t2) {
/** \brief Apply \c f to expressions embedded in the given transition */ /** \brief Apply \c f to expressions embedded in the given transition */
transition replace(transition const & t, std::function<expr(expr const &)> const & f); transition replace(transition const & t, std::function<expr(expr const &)> const & f);
void display(io_state_stream & out, unsigned num, transition const * ts, list<pair<unsigned, expr>> const & es, bool nud, class accepting {
unsigned m_prio;
list<action> m_postponed; // exprs and scoped_expr actions
expr m_expr; // resulting expression
public:
accepting(unsigned prio, list<action> const & post, expr const & e):
m_prio(prio), m_postponed(post), m_expr(e) {}
unsigned get_prio() const { return m_prio; }
list<action> const & get_postponed() const { return m_postponed; }
expr const & get_expr() const { return m_expr; }
};
void display(io_state_stream & out, unsigned num, transition const * ts, list<accepting> const & es, bool nud,
optional<token_table> const & tt); optional<token_table> const & tt);
/** /**
@ -140,9 +154,9 @@ class parse_table {
struct cell; struct cell;
cell * m_ptr; cell * m_ptr;
explicit parse_table(cell * c); explicit parse_table(cell * c);
parse_table add_core(unsigned num, transition const * ts, expr const & a, unsigned priority, bool overload) const; parse_table add_core(unsigned num, transition const * ts, expr const & a, unsigned priority, bool overload, buffer<action> & postponed) const;
void for_each(buffer<transition> & ts, std::function<void(unsigned, transition const *, void for_each(buffer<transition> & ts, std::function<void(unsigned, transition const *,
list<pair<unsigned, expr>> const &)> const & fn) const; list<accepting> const &)> const & fn) const;
public: public:
parse_table(bool nud = true); parse_table(bool nud = true);
parse_table(parse_table const & s); parse_table(parse_table const & s);
@ -156,10 +170,10 @@ public:
parse_table add(std::initializer_list<transition> const & ts, expr const & a) const { parse_table add(std::initializer_list<transition> const & ts, expr const & a) const {
return add(ts.size(), ts.begin(), a, LEAN_DEFAULT_NOTATION_PRIORITY, true); return add(ts.size(), ts.begin(), a, LEAN_DEFAULT_NOTATION_PRIORITY, true);
} }
parse_table merge(parse_table const & s, bool overload) const; parse_table merge(parse_table const & s, bool overload) const;\
list<pair<action, parse_table>> find(name const & tk) const; list<pair<action, parse_table>> find(name const & tk) const;
list<pair<unsigned, expr>> const & is_accepting() const; list<accepting> const & is_accepting() const;
void for_each(std::function<void(unsigned, transition const *, list<pair<unsigned, expr>> const &)> const & fn) const; void for_each(std::function<void(unsigned, transition const *, list<accepting> const &)> const & fn) const;
void display(io_state_stream & out, optional<token_table> const & tk) const; void display(io_state_stream & out, optional<token_table> const & tk) const;
}; };

View file

@ -1069,13 +1069,126 @@ bool parser::parse_local_notation_decl(buffer<notation_entry> * nentries) {
} }
} }
void parser::process_postponed(buffer<expr> const & args, bool is_left,
buffer<notation::action_kind> const & kinds,
buffer<list<expr>> const & nargs,
buffer<expr> const & ps,
buffer<pair<unsigned, pos_info>> const & scoped_info,
list<notation::action> const & postponed,
pos_info const & p,
buffer<expr> & new_args) {
unsigned args_idx = 0;
if (is_left) {
new_args.push_back(args[0]);
args_idx = 1;
}
unsigned kinds_idx = 0;
unsigned nargs_idx = 0;
unsigned scoped_idx = 0;
list<notation::action> it = postponed;
for (; kinds_idx < kinds.size(); kinds_idx++, args_idx++) {
auto k = kinds[kinds_idx];
switch (k) {
case notation::action_kind::Exprs: {
if (!it || head(it).kind() != k || nargs_idx >= nargs.size())
throw exception("ill-formed parsing tables");
notation::action const & a = head(it);
buffer<expr> r_args;
to_buffer(nargs[nargs_idx], r_args);
nargs_idx++;
expr rec = copy_with_new_pos(a.get_rec(), p);
expr r;
if (a.is_fold_right()) {
if (a.get_initial()) {
r = instantiate_rev(copy_with_new_pos(*a.get_initial(), p), new_args.size(), new_args.data());
} else {
r = r_args.back();
r_args.pop_back();
}
unsigned i = r_args.size();
while (i > 0) {
--i;
new_args.push_back(r_args[i]);
new_args.push_back(r);
r = instantiate_rev(rec, new_args.size(), new_args.data());
new_args.pop_back(); new_args.pop_back();
}
} else {
unsigned fidx = 0;
if (a.get_initial()) {
r = instantiate_rev(copy_with_new_pos(*a.get_initial(), p), new_args.size(), new_args.data());
} else {
r = r_args[0];
fidx++;
}
for (unsigned i = fidx; i < r_args.size(); i++) {
new_args.push_back(r_args[i]);
new_args.push_back(r);
r = instantiate_rev(rec, new_args.size(), new_args.data());
new_args.pop_back(); new_args.pop_back();
}
}
new_args.push_back(r);
it = tail(it);
break;
}
case notation::action_kind::ScopedExpr: {
if (!it || head(it).kind() != k || scoped_idx >= scoped_info.size())
throw exception("ill-formed parsing tables");
expr r = args[args_idx];
notation::action const & a = head(it);
bool no_cache = false;
unsigned ps_sz = scoped_info[scoped_idx].first;
pos_info binder_pos = scoped_info[scoped_idx].second;
scoped_idx++;
if (is_var(a.get_rec(), 0)) {
if (a.use_lambda_abstraction())
r = Fun(ps_sz, ps.data(), r, no_cache);
else
r = Pi(ps_sz, ps.data(), r, no_cache);
r = rec_save_pos(r, binder_pos);
} else {
expr rec = copy_with_new_pos(a.get_rec(), p);
unsigned i = ps_sz;
while (i > 0) {
--i;
expr const & l = ps[i];
if (a.use_lambda_abstraction())
r = Fun(l, r, no_cache);
else
r = Pi(l, r, no_cache);
r = save_pos(r, binder_pos);
new_args.push_back(r);
r = instantiate_rev(rec, new_args.size(), new_args.data());
new_args.pop_back();
}
}
new_args.push_back(r);
it = tail(it);
break;
}
default:
new_args.push_back(args[args_idx]);
break;
}
}
}
expr parser::parse_notation_core(parse_table t, expr * left, bool as_tactic) { expr parser::parse_notation_core(parse_table t, expr * left, bool as_tactic) {
lean_assert(curr() == scanner::token_kind::Keyword); lean_assert(curr() == scanner::token_kind::Keyword);
auto p = pos(); auto p = pos();
if (m_info_manager) if (m_info_manager)
m_info_manager->add_symbol_info(p.first, p.second, get_token_info().token()); m_info_manager->add_symbol_info(p.first, p.second, get_token_info().token());
buffer<expr> args; buffer<expr> args;
buffer<expr> ps; buffer<notation::action_kind> kinds;
buffer<list<expr>> nargs; // nary args
buffer<expr> ps;
buffer<pair<unsigned, pos_info>> scoped_info; // size of ps and binder_pos for scoped_exprs
// Invariants:
// args.size() == kinds.size() if not left
// args.size() == kinds.size()+1 if left
// nargs.size() == number of Exprs in kinds
// scoped_info.size() == number of Scoped_Exprs in kinds
local_environment lenv(m_env); local_environment lenv(m_env);
pos_info binder_pos; pos_info binder_pos;
if (left) if (left)
@ -1095,6 +1208,7 @@ expr parser::parse_notation_core(parse_table t, expr * left, bool as_tactic) {
case notation::action_kind::Expr: case notation::action_kind::Expr:
next(); next();
args.push_back(parse_expr_or_tactic(a.rbp(), as_tactic)); args.push_back(parse_expr_or_tactic(a.rbp(), as_tactic));
kinds.push_back(a.kind());
break; break;
case notation::action_kind::Exprs: { case notation::action_kind::Exprs: {
next(); next();
@ -1114,39 +1228,9 @@ expr parser::parse_notation_core(parse_table t, expr * left, bool as_tactic) {
throw parser_error(sstream() << "invalid composite expression, '" << *terminator << "' expected", pos()); throw parser_error(sstream() << "invalid composite expression, '" << *terminator << "' expected", pos());
} }
} }
expr rec = copy_with_new_pos(a.get_rec(), p); args.push_back(expr()); // placeholder
expr r; kinds.push_back(a.kind());
if (a.is_fold_right()) { nargs.push_back(to_list(r_args));
if (a.get_initial()) {
r = instantiate_rev(copy_with_new_pos(*a.get_initial(), p), args.size(), args.data());
} else {
r = r_args.back();
r_args.pop_back();
}
unsigned i = r_args.size();
while (i > 0) {
--i;
args.push_back(r_args[i]);
args.push_back(r);
r = instantiate_rev(rec, args.size(), args.data());
args.pop_back(); args.pop_back();
}
} else {
unsigned fidx = 0;
if (a.get_initial()) {
r = instantiate_rev(copy_with_new_pos(*a.get_initial(), p), args.size(), args.data());
} else {
r = r_args[0];
fidx++;
}
for (unsigned i = fidx; i < r_args.size(); i++) {
args.push_back(r_args[i]);
args.push_back(r);
r = instantiate_rev(rec, args.size(), args.data());
args.pop_back(); args.pop_back();
}
}
args.push_back(r);
break; break;
} }
case notation::action_kind::Binder: case notation::action_kind::Binder:
@ -1162,30 +1246,9 @@ expr parser::parse_notation_core(parse_table t, expr * left, bool as_tactic) {
case notation::action_kind::ScopedExpr: { case notation::action_kind::ScopedExpr: {
next(); next();
expr r = parse_scoped_expr(ps, lenv, a.rbp()); expr r = parse_scoped_expr(ps, lenv, a.rbp());
bool no_cache = false;
if (is_var(a.get_rec(), 0)) {
if (a.use_lambda_abstraction())
r = Fun(ps, r, no_cache);
else
r = Pi(ps, r, no_cache);
r = rec_save_pos(r, binder_pos);
} else {
expr rec = copy_with_new_pos(a.get_rec(), p);
unsigned i = ps.size();
while (i > 0) {
--i;
expr const & l = ps[i];
if (a.use_lambda_abstraction())
r = Fun(l, r, no_cache);
else
r = Pi(l, r, no_cache);
r = save_pos(r, binder_pos);
args.push_back(r);
r = instantiate_rev(rec, args.size(), args.data());
args.pop_back();
}
}
args.push_back(r); args.push_back(r);
kinds.push_back(a.kind());
scoped_info.push_back(mk_pair(ps.size(), binder_pos));
break; break;
} }
case notation::action_kind::LuaExt: case notation::action_kind::LuaExt:
@ -1207,17 +1270,18 @@ expr parser::parse_notation_core(parse_table t, expr * left, bool as_tactic) {
throw parser_error(sstream() << "failed to use notation implemented in Lua, value returned by function '" throw parser_error(sstream() << "failed to use notation implemented in Lua, value returned by function '"
<< a.get_lua_fn() << "' is not an expression", p); << a.get_lua_fn() << "' is not an expression", p);
args.push_back(rec_save_pos(to_expr(L, -1), p)); args.push_back(rec_save_pos(to_expr(L, -1), p));
kinds.push_back(a.kind());
lua_pop(L, 1); lua_pop(L, 1);
}); });
break; break;
case notation::action_kind::Ext: case notation::action_kind::Ext:
args.push_back(a.get_parse_fn()(*this, args.size(), args.data(), p)); args.push_back(a.get_parse_fn()(*this, args.size(), args.data(), p));
kinds.push_back(a.kind());
break; break;
} }
t = head(r).second; // TODO(Leo): t = head(r).second; // TODO(Leo):
} }
list<pair<unsigned, expr>> const & as = t.is_accepting(); list<notation::accepting> const & as = t.is_accepting();
save_overload_notation(as, p); save_overload_notation(as, p);
if (is_nil(as)) { if (is_nil(as)) {
if (p == pos()) if (p == pos())
@ -1225,16 +1289,28 @@ expr parser::parse_notation_core(parse_table t, expr * left, bool as_tactic) {
else else
throw parser_error(sstream() << "invalid expression starting at " << p.first << ":" << p.second, pos()); throw parser_error(sstream() << "invalid expression starting at " << p.first << ":" << p.second, pos());
} }
lean_assert(left || args.size() == kinds.size());
lean_assert(!left || args.size() == kinds.size() + 1);
buffer<expr> cs; buffer<expr> cs;
for (pair<unsigned, expr> const & a : as) { for (notation::accepting const & a : as) {
expr r = copy_with_new_pos(a.second, p); expr r = copy_with_new_pos(a.get_expr(), p);
if (args.empty()) { list<notation::action> const & postponed = a.get_postponed();
// Notation does not have arguments. Thus, the info-manager should treat is as a single thing. if (postponed) {
r = mk_notation_info(r, r.get_tag()); buffer<expr> new_args;
process_postponed(args, left, kinds, nargs, ps, scoped_info, postponed, p, new_args);
lean_assert(!args.empty());
r = instantiate_rev(r, new_args.size(), new_args.data());
cs.push_back(r);
} else { } else {
r = instantiate_rev(r, args.size(), args.data()); lean_assert(nargs.empty() && ps_sz.empty());
if (args.empty()) {
// Notation does not have arguments. Thus, the info-manager should treat is as a single thing.
r = mk_notation_info(r, r.get_tag());
} else {
r = instantiate_rev(r, args.size(), args.data());
}
cs.push_back(r);
} }
cs.push_back(r);
} }
expr r = save_pos(mk_choice(cs.size(), cs.data()), p); expr r = save_pos(mk_choice(cs.size(), cs.data()), p);
save_type_info(r); save_type_info(r);
@ -2110,10 +2186,12 @@ void parser::save_overload_notation(list<expr> const & as, pos_info const & p) {
return; return;
m_info_manager->add_overload_notation_info(p.first, p.second, as); m_info_manager->add_overload_notation_info(p.first, p.second, as);
} }
void parser::save_overload_notation(list<pair<unsigned, expr>> const & as, pos_info const & p) { void parser::save_overload_notation(list<notation::accepting> const & as, pos_info const & p) {
if (!m_info_manager || length(as) <= 1) if (!m_info_manager || length(as) <= 1)
return; return;
list<expr> new_as = map2<expr, pair<unsigned, expr>, std::function<expr(pair<unsigned, expr> const &)>>(as, [](pair<unsigned, expr> const & p) { return p.second; }); list<expr> new_as =
map2<expr, notation::accepting, std::function<expr(notation::accepting const &)>>
(as, [](notation::accepting const & p) { return p.get_expr(); });
save_overload_notation(new_as, p); save_overload_notation(new_as, p);
} }
void parser::save_identifier_info(pos_info const & p, name const & full_id) { void parser::save_identifier_info(pos_info const & p, name const & full_id) {

View file

@ -202,6 +202,9 @@ class parser {
void parse_script(bool as_expr = false); void parse_script(bool as_expr = false);
bool parse_commands(); bool parse_commands();
unsigned curr_lbp_core(bool as_tactic) const; unsigned curr_lbp_core(bool as_tactic) const;
void process_postponed(buffer<expr> const & args, bool is_left, buffer<notation::action_kind> const & kinds,
buffer<list<expr>> const & nargs, buffer<expr> const & ps, buffer<pair<unsigned, pos_info>> const & scoped_info,
list<notation::action> const & postponed, pos_info const & p, buffer<expr> & new_args);
expr parse_notation_core(parse_table t, expr * left, bool as_tactic); expr parse_notation_core(parse_table t, expr * left, bool as_tactic);
expr parse_expr_or_tactic(unsigned rbp, bool as_tactic) { expr parse_expr_or_tactic(unsigned rbp, bool as_tactic) {
return as_tactic ? parse_tactic(rbp) : parse_expr(rbp); return as_tactic ? parse_tactic(rbp) : parse_expr(rbp);
@ -234,7 +237,7 @@ class parser {
void save_snapshot(); void save_snapshot();
void save_overload(expr const & e); void save_overload(expr const & e);
void save_overload_notation(list<expr> const & as, pos_info const & p); void save_overload_notation(list<expr> const & as, pos_info const & p);
void save_overload_notation(list<pair<unsigned, expr>> const & as, pos_info const & p); void save_overload_notation(list<notation::accepting> const & as, pos_info const & p);
void save_type_info(expr const & e); void save_type_info(expr const & e);
void save_pre_info_data(); void save_pre_info_data();
void save_identifier_info(pos_info const & p, name const & full_id); void save_identifier_info(pos_info const & p, name const & full_id);

View file

@ -0,0 +1,11 @@
import data.set data.finset
open nat set finset
constant S : set nat
constant s : finset nat
check {x ∈ S | x > 0}
check {x ∈ s | x > 0}
set_option pp.all true
check {x ∈ S | x > 0}
check {x ∈ s | x > 0}

View file

@ -0,0 +1,7 @@
{x : ∈ S| x > 0} : set
{x : ∈ s| x > 0} : finset
@set.sep.{1} nat (λ (x : nat), nat.gt x (nat.of_num 0)) S : set.{1} nat
@finset.sep.{1} nat (λ (a b : nat), nat.has_decidable_eq a b) (λ (x : nat), nat.gt x (nat.of_num 0))
(λ (a : nat), nat.decidable_ge a (nat.succ (nat.of_num 0)))
s :
finset.{1} nat

View file

@ -0,0 +1,21 @@
prelude
constant vec.{l} : Type.{l} → Type.{l}
constant lst.{l} : Type.{l} → Type.{l}
constant vec.nil {A : Type} : vec A
constant lst.nil {A : Type} : lst A
constant vec.cons {A : Type} : A → vec A → vec A
constant lst.cons {A : Type} : A → lst A → lst A
notation `[` l:(foldr `,` (h t, vec.cons h t) vec.nil `]`) := l
notation `[` l:(foldr `,` (h t, lst.cons h t) lst.nil `]`) := l
constant A : Type.{1}
variables a b c : A
check [a, b, c]
check ([a, b, c] : vec A)
check ([a, b, c] : lst A)
set_option pp.all true
check ([a, b, c] : vec A)
check ([a, b, c] : lst A)

View file

@ -0,0 +1,5 @@
[a, b, c] : vec A
[a, b, c] : vec A
[a, b, c] : lst A
@vec.cons.{1} A a (@vec.cons.{1} A b (@vec.cons.{1} A c (@vec.nil.{1} A))) : vec.{1} A
@lst.cons.{1} A a (@lst.cons.{1} A b (@lst.cons.{1} A c (@lst.nil.{1} A))) : lst.{1} A

View file

@ -0,0 +1,17 @@
import data.list data.examples.vector
open nat list vector
check [1, 2, 3]
check ([1, 2, 3] : vector nat _)
check ([1, 2, 3] : list nat)
check (#list [1, 2, 3])
check (#vector [1, 2, 3])
example : (#vector [1, 2, 3]) = [1, 2, 3] :=
rfl
example : (#vector [1, 2, 3]) = ([1, 2, 3] : vector nat _) :=
rfl
example : (#list [1, 2, 3]) = ([1, 2, 3] : list nat) :=
rfl

View file

@ -0,0 +1,5 @@
[1, 2, 3] : list num
[1, 2, 3] : vector 3
[1, 2, 3] : list
[1, 2, 3] : list num
[1, 2, 3] : vector num 3

View file

@ -0,0 +1,10 @@
import data.list data.examples.vector
open list vector nat
variables (A : Type) (a b c : A)
check [a, b, c]
check (#list [a, b, c])
check (#vector [a, b, c])
check ([a, b, c] : vector A _)
check ([a, b, c] : list A)