feat(frontends/lean): implement relaxed operator compatibility in the parser

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2013-12-10 15:32:12 -08:00
parent c0b9c7ffc4
commit bbaa83e16a
9 changed files with 166 additions and 31 deletions

View file

@ -504,6 +504,12 @@ bool frontend::has_implicit_arguments(name const & n) const {
std::vector<bool> const & frontend::get_implicit_arguments(name const & n) const { std::vector<bool> const & frontend::get_implicit_arguments(name const & n) const {
return to_ext(m_env).get_implicit_arguments(n); return to_ext(m_env).get_implicit_arguments(n);
} }
std::vector<bool> const & frontend::get_implicit_arguments(expr const & n) const {
if (is_constant(n))
return get_implicit_arguments(const_name(n));
else
return g_empty_vector;
}
name const & frontend::get_explicit_version(name const & n) const { name const & frontend::get_explicit_version(name const & n) const {
return to_ext(m_env).get_explicit_version(n); return to_ext(m_env).get_explicit_version(n);
} }

View file

@ -129,6 +129,11 @@ public:
bool has_implicit_arguments(name const & n) const; bool has_implicit_arguments(name const & n) const;
/** \brief Return the position of the arguments that are implicit. */ /** \brief Return the position of the arguments that are implicit. */
std::vector<bool> const & get_implicit_arguments(name const & n) const; std::vector<bool> const & get_implicit_arguments(name const & n) const;
/**
\brief Return the position of the arguments that are implicit.
\remark If \c n is not a constant, then return the empty vector.
*/
std::vector<bool> const & get_implicit_arguments(expr const & n) const;
/** /**
\brief This frontend associates an definition with each \brief This frontend associates an definition with each
definition (or postulate) that has implicit arguments. The definition (or postulate) that has implicit arguments. The

View file

@ -15,6 +15,7 @@ Author: Leonardo de Moura
#include "kernel/replace_visitor.h" #include "kernel/replace_visitor.h"
#include "kernel/unification_constraint.h" #include "kernel/unification_constraint.h"
#include "kernel/instantiate.h" #include "kernel/instantiate.h"
#include "kernel/builtin.h"
#include "library/type_inferer.h" #include "library/type_inferer.h"
#include "library/placeholder.h" #include "library/placeholder.h"
#include "library/elaborator/elaborator.h" #include "library/elaborator/elaborator.h"
@ -23,29 +24,52 @@ Author: Leonardo de Moura
namespace lean { namespace lean {
static name g_x_name("x"); static name g_x_name("x");
static name g_choice_name = name::mk_internal_unique_name();
static expr g_choice = mk_constant(g_choice_name);
static format g_assignment_fmt = format(":="); static format g_assignment_fmt = format(":=");
static format g_unification_u_fmt = format("\u2248"); static format g_unification_u_fmt = format("\u2248");
static format g_unification_fmt = format("=?="); static format g_unification_fmt = format("=?=");
/**
\brief Internal value used to store choices for the elaborator.
This is a transient value that is only used to setup a problem
for the elaborator.
*/
struct choice_value : public value {
std::vector<expr> m_choices;
choice_value(unsigned num_fs, expr const * fs):m_choices(fs, fs + num_fs) {}
virtual ~choice_value() {}
virtual expr get_type() const { lean_unreachable(); } // LCOV_EXCL_LINE
virtual name get_name() const { return name("Choice"); }
virtual void display(std::ostream & out) const {
out << "(Choice";
for (auto c : m_choices) {
out << " (" << c << ")";
}
out << ")";
}
// Remark: we don't implement the pp methods because the lean::pp_fn formatter
// object has support for formatting choice internal values.
};
expr mk_choice(unsigned num_fs, expr const * fs) { expr mk_choice(unsigned num_fs, expr const * fs) {
lean_assert(num_fs >= 2); lean_assert(num_fs >= 2);
return mk_eq(g_choice, mk_app(num_fs, fs)); return mk_value(*(new choice_value(num_fs, fs)));
} }
bool is_choice(expr const & e) { bool is_choice(expr const & e) {
return is_eq(e) && eq_lhs(e) == g_choice; return is_value(e) && dynamic_cast<choice_value const *>(&to_value(e)) != nullptr;
}
choice_value const & to_choice_value(expr const & e) {
lean_assert(is_choice(e));
return static_cast<choice_value const &>(to_value(e));
} }
unsigned get_num_choices(expr const & e) { unsigned get_num_choices(expr const & e) {
lean_assert(is_choice(e)); return to_choice_value(e).m_choices.size();
return num_args(eq_rhs(e));
} }
expr const & get_choice(expr const & e, unsigned i) { expr const & get_choice(expr const & e, unsigned i) {
lean_assert(is_choice(e)); return to_choice_value(e).m_choices[i];
return arg(eq_rhs(e), i);
} }
class coercion_justification_cell : public justification_cell { class coercion_justification_cell : public justification_cell {
@ -251,6 +275,7 @@ class frontend_elaborator::imp {
} else { } else {
buffer<expr> to_keep; buffer<expr> to_keep;
buffer<optional<expr>> to_keep_types; buffer<optional<expr>> to_keep_types;
std::sort(matched.begin(), matched.end()); // we must preserve the original order
for (unsigned i : matched) { for (unsigned i : matched) {
to_keep.push_back(f_choices[i]); to_keep.push_back(f_choices[i]);
to_keep_types.push_back(f_choice_types[i]); to_keep_types.push_back(f_choice_types[i]);

View file

@ -17,6 +17,7 @@ Author: Leonardo de Moura
#include <string> #include <string>
#include <tuple> #include <tuple>
#include <vector> #include <vector>
#include <limits>
#include "util/scoped_map.h" #include "util/scoped_map.h"
#include "util/exception.h" #include "util/exception.h"
#include "util/sstream.h" #include "util/sstream.h"
@ -579,6 +580,41 @@ class parser::imp {
*/ */
/*@{*/ /*@{*/
/**
\brief Return the size of the implicit vector associated with the given denotation.
*/
unsigned get_implicit_vector_size(expr const & d) {
return m_frontend.get_implicit_arguments(d).size();
}
/**
\brief Return a vector \c v that is the implicit vector for some \c d in \c ds,
and <tt>v.size() == min{get_implicit_vector_size(d) | d in ds}</tt>
*/
std::vector<bool> const & get_smallest_implicit_vector(list<expr> const & ds) {
std::vector<bool> const * r = nullptr;
unsigned m = std::numeric_limits<unsigned>::max();
for (expr const & d : ds) {
std::vector<bool> const & v = m_frontend.get_implicit_arguments(d);
unsigned s = v.size();
if (s == 0) {
return v;
} else if (s < m) {
r = &v;
m = s;
}
}
lean_assert(r);
return *r;
}
/**
\brief Return <tt>min{get_implicit_vector_size(d) | d in ds}</tt>
*/
unsigned get_smallest_implicit_vector_size(list<expr> const & ds) {
return get_smallest_implicit_vector(ds).size();
}
/** /**
\brief Return the function associated with the given operator. \brief Return the function associated with the given operator.
If the operator has been overloaded, it returns a choice expression If the operator has been overloaded, it returns a choice expression
@ -586,7 +622,7 @@ class parser::imp {
After we finish parsing, the elaborator After we finish parsing, the elaborator
resolve/decide which f_i should be used. resolve/decide which f_i should be used.
*/ */
expr mk_fun(operator_info const & op) { expr mk_fun(operator_info const & op, pos_info const & pos) {
list<expr> const & fs = op.get_denotations(); list<expr> const & fs = op.get_denotations();
lean_assert(!is_nil(fs)); lean_assert(!is_nil(fs));
auto it = fs.begin(); auto it = fs.begin();
@ -595,10 +631,24 @@ class parser::imp {
if (it == fs.end()) { if (it == fs.end()) {
return r; return r;
} else { } else {
unsigned min_sz = get_smallest_implicit_vector_size(fs);
buffer<expr> alternatives; buffer<expr> alternatives;
alternatives.push_back(r); auto add_alternative = [&](expr const & d) {
unsigned sz = get_implicit_vector_size(d);
if (sz > min_sz) {
// must fill the difference with placeholders
buffer<expr> aux;
aux.push_back(d);
for (unsigned i = min_sz; i < sz; i++)
aux.push_back(save(mk_placeholder(), pos));
alternatives.push_back(mk_app(aux));
} else {
alternatives.push_back(d);
}
};
add_alternative(r);
for (; it != fs.end(); ++it) for (; it != fs.end(); ++it)
alternatives.push_back(*it); add_alternative(*it);
return mk_choice(alternatives.size(), alternatives.data()); return mk_choice(alternatives.size(), alternatives.data());
} }
} }
@ -609,29 +659,23 @@ class parser::imp {
*/ */
expr mk_application(operator_info const & op, pos_info const & pos, unsigned num_args, expr const * args) { expr mk_application(operator_info const & op, pos_info const & pos, unsigned num_args, expr const * args) {
buffer<expr> new_args; buffer<expr> new_args;
expr f = save(mk_fun(op), pos); expr f = save(mk_fun(op, pos), pos);
new_args.push_back(f); new_args.push_back(f);
// I'm using the fact that all denotations are compatible.
// See lean_frontend.cpp for the definition of compatible denotations. // See lean_frontend.cpp for the definition of compatible denotations.
expr const & d = head(op.get_denotations()); auto imp_args = get_smallest_implicit_vector(op.get_denotations());
if (is_constant(d) && m_frontend.has_implicit_arguments(const_name(d))) { unsigned i = 0;
std::vector<bool> const & imp_args = m_frontend.get_implicit_arguments(const_name(d)); for (unsigned j = 0; j < imp_args.size(); j++) {
unsigned i = 0; if (imp_args[j]) {
for (unsigned j = 0; j < imp_args.size(); j++) { new_args.push_back(save(mk_placeholder(), pos));
if (imp_args[j]) { } else {
new_args.push_back(save(mk_placeholder(), pos)); if (i >= num_args)
} else { throw parser_error(sstream() << "unexpected number of arguments for denotation with implicit arguments, it expects " << num_args << " explicit argument(s)", pos);
if (i >= num_args)
throw parser_error(sstream() << "unexpected number of arguments for denotation with implicit arguments, it expects " << num_args << " explicit argument(s)", pos);
new_args.push_back(args[i]);
i++;
}
}
for (; i < num_args; i++)
new_args.push_back(args[i]); new_args.push_back(args[i]);
} else { i++;
new_args.append(num_args, args); }
} }
for (; i < num_args; i++)
new_args.push_back(args[i]);
return save(mk_app(new_args), pos); return save(mk_app(new_args), pos);
} }
expr mk_application(operator_info const & op, pos_info const & pos, std::initializer_list<expr> const & l) { expr mk_application(operator_info const & op, pos_info const & pos, std::initializer_list<expr> const & l) {

View file

@ -200,8 +200,10 @@ class pp_fn {
*/ */
bool is_atomic(expr const & e) { bool is_atomic(expr const & e) {
switch (e.kind()) { switch (e.kind()) {
case expr_kind::Var: case expr_kind::Constant: case expr_kind::Value: case expr_kind::Var: case expr_kind::Constant:
return true; return true;
case expr_kind::Value:
return !is_choice(e);
case expr_kind::Type: case expr_kind::Type:
return e == Type(); return e == Type();
case expr_kind::MetaVar: case expr_kind::MetaVar:
@ -700,6 +702,8 @@ class pp_fn {
result p; result p;
if (is_constant(f)) if (is_constant(f))
p = mk_result(format(const_name(f)), 1); p = mk_result(format(const_name(f)), 1);
else if (is_choice(f))
p = pp_child(f, depth);
else if (is_value(f)) else if (is_value(f))
p = mk_result(to_value(f).pp(m_unicode, m_coercion), 1); p = mk_result(to_value(f).pp(m_unicode, m_coercion), 1);
else else

10
tests/lean/implicit6.lean Normal file
View file

@ -0,0 +1,10 @@
Variable f {A : Type} : A -> A -> A
Infixl 65 + : f
Show true + false
Show 10 + 20
Show 10 + (- 20)
Set pp::notation false
Set pp::coercion true
Show true + false
Show 10 + 20
Show 10 + (- 20)

View file

@ -0,0 +1,11 @@
Set: pp::colors
Set: pp::unicode
Assumed: f
+ ⊥
10 + 20
10 + - 20
Set: lean::pp::notation
Set: lean::pp::coercion
f
Nat::add 10 20
Int::add (nat_to_int 10) (Nat::neg 20)

18
tests/lean/implicit7.lean Normal file
View file

@ -0,0 +1,18 @@
(*
TODO(Leo):
Improve elaborator's performance on this example.
The main problem is that we don't have any indexing technique
for the elaborator.
*)
Variable f {A : Type} (a : A) {B : Type} : A -> B -> A
Variable g {A B : Type} (a : A) {C : Type} : B -> C -> C
Notation 100 _ ; _ ; _ : f
Notation 100 _ ; _ ; _ : g
Check 10 ; true ; false
Check 10 ; 10 ; true
Set pp::notation false
Check 10 ; true ; false
Check 10 ; 10 ; true
Set pp::implicit true
Check 10 ; true ; false
Check 10 ; 10 ; true

View file

@ -0,0 +1,12 @@
Set: pp::colors
Set: pp::unicode
Assumed: f
Assumed: g
10 ; ; ⊥ : Bool
10 ; 10 ; :
Set: lean::pp::notation
g 10 ⊥ : Bool
f 10 10 :
Set: lean::pp::implicit
g::explicit Bool 10 Bool ⊥ : Bool
f::explicit 10 Bool 10 :