feat(frontends/lean/parser): change function application precedence

Now, we can write

  Pi (x y : A), R x y -> R y x

instead of

  Pi (x y : A), (R x y) -> (R y x)

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2013-12-18 12:44:11 -08:00
parent 47c7bb1bde
commit 2aaa9a5273
4 changed files with 60 additions and 13 deletions

View file

@ -35,6 +35,7 @@ struct lean_extension : public environment_extension {
// Remark: only named objects are stored in the dictionary.
typedef name_map<operator_info> operator_table;
typedef name_map<implicit_info> implicit_table;
typedef name_map<unsigned> precedence_table;
typedef expr_struct_map<list<operator_info>> expr_to_operators;
typedef expr_pair_struct_map<expr> coercion_map;
typedef expr_struct_map<list<expr_pair>> expr_to_coercions;
@ -42,6 +43,9 @@ struct lean_extension : public environment_extension {
operator_table m_nud; // nud table for Pratt's parser
operator_table m_led; // led table for Pratt's parser
// The following table stores the precedence of other operator parts.
// The m_nud and m_led only map the first part of an operator to its definition.
precedence_table m_other_lbp;
expr_to_operators m_expr_to_operators; // map denotations to operators (this is used for pretty printing)
implicit_table m_implicit_table; // track the number of implicit arguments for a symbol.
coercion_map m_coercion_map; // mapping from (given_type, expected_type) -> coercion
@ -76,6 +80,26 @@ struct lean_extension : public environment_extension {
return operator_info();
}
optional<unsigned> get_other_lbp(name const & n) const {
auto it = m_other_lbp.find(n);
if (it != m_other_lbp.end())
return some(it->second);
lean_extension const * parent = get_parent();
if (parent)
return parent->get_other_lbp(n);
else
return optional<unsigned>();
}
/** \brief Return the precedence (aka binding power) of the given name. */
optional<unsigned> get_lbp(name const & n) const {
operator_info info = find_led(n);
if (info)
return some(info.get_precedence());
else
return get_other_lbp(n);
}
/**
\brief Return true if the given operator is defined in this
frontend object (parent frontends are ignored).
@ -130,18 +154,31 @@ struct lean_extension : public environment_extension {
}
}
/** \brief Add a new entry d -> op in the mapping m_expr_to_operators */
void insert_expr_to_operator_entry(expr const & d, operator_info const & op) {
list<operator_info> & l = m_expr_to_operators[d];
l = cons(op, l);
}
void check_precedence(name const & n, unsigned prec, io_state & st) const {
auto old_prec = get_lbp(n);
if (old_prec && *old_prec != prec)
diagnostic(st) << "The precedence of '" << n << "' changed from " << *old_prec << " to " << prec << ".\n";
}
/** \brief Register the new operator in the tables for parsing and pretty printing. */
void register_new_op(operator_info new_op, expr const & d, bool led) {
void register_new_op(operator_info new_op, expr const & d, bool led, io_state & st) {
new_op.add_expr(d);
insert_op(new_op, led);
insert_expr_to_operator_entry(d, new_op);
auto parts = new_op.get_op_name_parts();
auto prec = new_op.get_precedence();
if (led)
check_precedence(head(parts), prec, st);
for (name const & part : tail(parts)) {
check_precedence(part, prec, st);
m_other_lbp[part] = prec;
}
}
/**
@ -213,7 +250,7 @@ struct lean_extension : public environment_extension {
name const & opn = new_op.get_op_name();
operator_info old_op = find_op(opn, led);
if (!old_op) {
register_new_op(new_op, d, led);
register_new_op(new_op, d, led, st);
} else if (old_op == new_op) {
if (compatible_denotations(old_op, d)) {
// overload
@ -224,20 +261,20 @@ struct lean_extension : public environment_extension {
// we must copy the operator because it was defined in
// a parent frontend.
new_op = old_op.copy();
register_new_op(new_op, d, led);
register_new_op(new_op, d, led, st);
}
} else {
diagnostic(st) << "The denotation(s) for the existing notation:\n " << old_op
<< "\nhave been replaced with the new denotation:\n " << d
<< "\nbecause they conflict on how implicit arguments are used.\n";
remove_bindings(old_op);
register_new_op(new_op, d, led);
register_new_op(new_op, d, led, st);
}
} else {
diagnostic(st) << "Notation has been redefined, the existing notation:\n " << old_op
<< "\nhas been replaced with:\n " << new_op << "\nbecause they conflict with each other.\n";
remove_bindings(old_op);
register_new_op(new_op, d, led);
register_new_op(new_op, d, led, st);
}
env->add_neutral_object(new notation_declaration(new_op, d));
}
@ -474,6 +511,9 @@ operator_info frontend::find_nud(name const & n) const {
operator_info frontend::find_led(name const & n) const {
return to_ext(m_env).find_led(n);
}
optional<unsigned> frontend::get_lbp(name const & n) const {
return to_ext(m_env).get_lbp(n);
}
void frontend::mark_implicit_arguments(name const & n, unsigned sz, bool const * implicit) {
to_ext(m_env).mark_implicit_arguments(n, sz, implicit, m_env);
}

View file

@ -107,6 +107,9 @@ public:
\remark This is used for parsing.
*/
operator_info find_led(name const & n) const;
/** \brief Return the precedence (aka binding power) of the given name. */
optional<unsigned> get_lbp(name const & n) const;
/*@}*/
/**

View file

@ -117,6 +117,9 @@ static unsigned g_level_plus_prec = 10;
static unsigned g_level_cup_prec = 5;
// ==========================================
// precedence (aka binding power) for function application
constexpr unsigned app_lbp = std::numeric_limits<unsigned>::max();
// A name that can't be created by the user.
// It is used as placeholder for parsing A -> B expressions which
// are syntax sugar for (Pi (_ : A), B)
@ -814,7 +817,7 @@ class parser::imp {
if (imp_args[i]) {
args.push_back(save(mk_placeholder(), pos()));
} else {
args.push_back(parse_expr(1));
args.push_back(parse_expr(app_lbp));
}
}
return mk_app(args);
@ -1301,20 +1304,20 @@ class parser::imp {
name const & id = curr_name();
auto it = m_local_decls.find(id);
if (it != m_local_decls.end()) {
return 1;
return app_lbp;
} else {
operator_info op = m_frontend.find_led(id);
if (op)
return op.get_precedence();
optional<unsigned> prec = m_frontend.get_lbp(id);
if (prec)
return *prec;
else
return 1;
return app_lbp;
}
}
case scanner::token::Eq : return g_eq_precedence;
case scanner::token::Arrow : return g_arrow_precedence;
case scanner::token::LeftParen: case scanner::token::NatVal: case scanner::token::DecimalVal:
case scanner::token::StringVal: case scanner::token::Type: case scanner::token::Placeholder:
return 1;
return app_lbp;
default:
return 0;
}

View file

@ -9,6 +9,7 @@ f c d e
c |- d ; e
(c !) !
fact (fact c)
The precedence of ';' changed from 100 to 30.
[ c ; d ]
[ c ; ([ d ; e ]) ]
g c (g d e)