46a8300a2d
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
693 lines
28 KiB
C++
693 lines
28 KiB
C++
/*
|
|
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
|
Released under Apache 2.0 license as described in the file LICENSE.
|
|
|
|
Author: Leonardo de Moura
|
|
*/
|
|
#include <vector>
|
|
#include <utility>
|
|
#include <functional>
|
|
#include <string>
|
|
#include "util/thread.h"
|
|
#include "util/map.h"
|
|
#include "util/sstream.h"
|
|
#include "util/exception.h"
|
|
#include "util/name_map.h"
|
|
#include "util/name_set.h"
|
|
#include "kernel/environment.h"
|
|
#include "kernel/expr_maps.h"
|
|
#include "kernel/expr_sets.h"
|
|
#include "kernel/builtin.h"
|
|
#include "library/expr_pair.h"
|
|
#include "library/expr_pair_maps.h"
|
|
#include "library/io_state.h"
|
|
#include "library/all/all.h"
|
|
#include "frontends/lean/operator_info.h"
|
|
#include "frontends/lean/coercion.h"
|
|
#include "frontends/lean/frontend.h"
|
|
#include "frontends/lean/notation.h"
|
|
#include "frontends/lean/pp.h"
|
|
|
|
namespace lean {
|
|
class mark_implicit_command : public neutral_object_cell {
|
|
name m_obj_name;
|
|
std::vector<bool> m_implicit;
|
|
public:
|
|
mark_implicit_command(name const & n, unsigned sz, bool const * implicit):
|
|
m_obj_name(n), m_implicit(implicit, implicit+sz) {}
|
|
virtual ~mark_implicit_command() {}
|
|
virtual char const * keyword() const { return "MarkImplicit"; }
|
|
virtual void write(serializer & s) const {
|
|
unsigned sz = m_implicit.size();
|
|
s << "Imp" << m_obj_name << sz;
|
|
for (auto b : m_implicit)
|
|
s << b;
|
|
}
|
|
};
|
|
static void read_mark_implicit(environment const & env, io_state const &, deserializer & d) {
|
|
name n = read_name(d);
|
|
buffer<bool> implicit;
|
|
unsigned num = d.read_unsigned();
|
|
for (unsigned i = 0; i < num; i++)
|
|
implicit.push_back(d.read_bool());
|
|
mark_implicit_arguments(env, n, implicit.size(), implicit.data());
|
|
}
|
|
static object_cell::register_deserializer_fn mark_implicit_ds("Imp", read_mark_implicit);
|
|
|
|
static std::vector<bool> g_empty_vector;
|
|
/**
|
|
\brief Environment extension object for the Lean default frontend.
|
|
*/
|
|
struct lean_extension : public environment_extension {
|
|
typedef std::pair<std::vector<bool>, name> implicit_info;
|
|
// 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;
|
|
typedef expr_struct_set coercion_set;
|
|
typedef expr_struct_map<list<name>> inv_aliases;
|
|
|
|
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
|
|
coercion_set m_coercion_set; // Set of coercions
|
|
expr_to_coercions m_type_coercions; // mapping type -> list (to-type, function)
|
|
name_set m_explicit_names; // set of explicit version of constants with implicit parameters
|
|
name_map<expr> m_aliases;
|
|
inv_aliases m_inv_aliases; // inverse map for m_aliases
|
|
|
|
lean_extension() {
|
|
}
|
|
|
|
lean_extension const * get_parent() const {
|
|
return environment_extension::get_parent<lean_extension>();
|
|
}
|
|
|
|
/** \brief Return the nud operator for the given symbol. */
|
|
operator_info find_nud(name const & n) const {
|
|
auto it = m_nud.find(n);
|
|
if (it != m_nud.end())
|
|
return it->second;
|
|
lean_extension const * parent = get_parent();
|
|
if (parent)
|
|
return parent->find_nud(n);
|
|
else
|
|
return operator_info();
|
|
}
|
|
|
|
/** \brief Return the led operator for the given symbol. */
|
|
operator_info find_led(name const & n) const {
|
|
auto it = m_led.find(n);
|
|
if (it != m_led.end())
|
|
return it->second;
|
|
lean_extension const * parent = get_parent();
|
|
if (parent)
|
|
return parent->find_led(n);
|
|
else
|
|
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).
|
|
*/
|
|
bool defined_here(operator_info const & n, bool led) const {
|
|
if (led)
|
|
return m_led.find(n.get_op_name()) != m_led.end();
|
|
else
|
|
return m_nud.find(n.get_op_name()) != m_nud.end();
|
|
}
|
|
|
|
/** \brief Return the led/nud operator for the given symbol. */
|
|
operator_info find_op(name const & n, bool led) const {
|
|
return led ? find_led(n) : find_nud(n);
|
|
}
|
|
|
|
/** \brief Insert a new led/nud operator. */
|
|
void insert_op(operator_info const & op, bool led) {
|
|
if (led)
|
|
insert(m_led, op.get_op_name(), op);
|
|
else
|
|
insert(m_nud, op.get_op_name(), op);
|
|
}
|
|
|
|
/** \brief Find the operator that is used as notation for the given expression. */
|
|
operator_info find_op_for(expr const & e, bool unicode) const {
|
|
auto it = m_expr_to_operators.find(e);
|
|
if (it != m_expr_to_operators.end()) {
|
|
auto l = it->second;
|
|
for (auto op : l) {
|
|
if (unicode || op.is_safe_ascii())
|
|
return op;
|
|
}
|
|
}
|
|
lean_extension const * parent = get_parent();
|
|
if (parent)
|
|
return parent->find_op_for(e, unicode);
|
|
else
|
|
return operator_info();
|
|
}
|
|
|
|
/** \brief Remove all internal denotations that are associated with the given operator symbol (aka notation) */
|
|
void remove_bindings(operator_info const & op) {
|
|
lean_extension const * parent = get_parent();
|
|
for (expr const & d : op.get_denotations()) {
|
|
if (parent && parent->find_op_for(d, true)) {
|
|
// parent has an association for d... we must hide it.
|
|
insert(m_expr_to_operators, d, list<operator_info>(operator_info()));
|
|
} else {
|
|
m_expr_to_operators.erase(d);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** \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 const & ios) const {
|
|
auto old_prec = get_lbp(n);
|
|
if (old_prec && *old_prec != prec)
|
|
diagnostic(ios) << "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, io_state const & ios) {
|
|
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, ios);
|
|
for (name const & part : tail(parts)) {
|
|
check_precedence(part, prec, ios);
|
|
m_other_lbp[part] = prec;
|
|
}
|
|
}
|
|
|
|
/**
|
|
\brief Two operator (aka notation) denotations are compatible
|
|
iff after ignoring all implicit arguments in the prefix and
|
|
explicit arguments in the suffix, the remaining implicit arguments
|
|
occur in the same positions.
|
|
|
|
Let us denote implicit arguments with a '_' and explicit with a '*'.
|
|
Then a denotation can be associated with a pattern containing one or more
|
|
'_' and '*'.
|
|
Two denotations are compatible, if we have the same pattern after
|
|
removed the '_' from the prefix and '*' from the suffix.
|
|
|
|
Here is an example of compatible denotations
|
|
f : Int -> Int -> Int Pattern * *
|
|
g : Pi {A : Type}, A -> A -> A Pattern _ * *
|
|
h : Pi {A B : Type}, A -> B -> A Pattern _ _ * *
|
|
They are compatible, because after we remove the _ from the prefix, and * from the suffix,
|
|
all of them reduce to the empty sequence
|
|
|
|
Here is another example of compatible denotations:
|
|
f : Pi {A : Type} (a : A) {B : Type} (b : B), A Pattern _ * _ *
|
|
g : Pi (i : Int) {T : Type} (x : T), T Pattern * _ *
|
|
They are compatible, because after we remove the _ from the prefix, and * from the suffix,
|
|
we get the same sequence: * _
|
|
|
|
The following two are not compatible
|
|
f : Pi {A : Type} (a : A) {B : Type} (b : B), A Pattern _ * _ *
|
|
g : Pi {A B : Type} (a : A) (b : B), A Pattern _ _ * *
|
|
|
|
Remark: we remove the explicit suffix at mark_implicit_arguments.
|
|
*/
|
|
bool compatible_denotation(expr const & d1, expr const & d2) {
|
|
auto imp1 = get_implicit_arguments(d1);
|
|
auto imp2 = get_implicit_arguments(d2);
|
|
auto it1 = std::find(imp1.begin(), imp1.end(), false);
|
|
auto it2 = std::find(imp2.begin(), imp2.end(), false);
|
|
for (; it1 != imp1.end() && it2 != imp2.end() && *it1 == *it2; ++it1, ++it2) {}
|
|
return it1 == imp1.end() && it2 == imp2.end();
|
|
}
|
|
|
|
/**
|
|
\brief Return true iff the existing denotations (aka
|
|
overloads) for an operator op are compatible with the new
|
|
denotation d.
|
|
|
|
The compatibility is only an issue if implicit arguments are
|
|
used. If one of the denotations has implicit arguments, then
|
|
all of them should have implicit arguments, and the implicit
|
|
arguments should occur in the same positions.
|
|
*/
|
|
bool compatible_denotations(operator_info const & op, expr const & d) {
|
|
return std::all_of(op.get_denotations().begin(), op.get_denotations().end(), [&](expr const & prev_d) { return compatible_denotation(prev_d, d); });
|
|
}
|
|
|
|
/**
|
|
\brief Add a new operator and save information as object.
|
|
|
|
If the new operator does not conflict with existing operators,
|
|
then we just register it.
|
|
If it conflicts, there are two options:
|
|
1) It is an overload (we just add the internal name \c n as
|
|
new option.
|
|
2) It is a real conflict, and report the issue in the
|
|
diagnostic channel, and override the existing operator (aka notation).
|
|
*/
|
|
void add_op(operator_info new_op, expr const & d, bool led, environment const & env, io_state const & ios) {
|
|
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, ios);
|
|
} else if (old_op == new_op) {
|
|
if (compatible_denotations(old_op, d)) {
|
|
// overload
|
|
if (defined_here(old_op, led)) {
|
|
old_op.add_expr(d);
|
|
insert_expr_to_operator_entry(d, old_op);
|
|
} else {
|
|
// 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, ios);
|
|
}
|
|
} else {
|
|
diagnostic(ios) << "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, ios);
|
|
}
|
|
} else {
|
|
diagnostic(ios) << "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, ios);
|
|
}
|
|
env->add_neutral_object(new notation_declaration(new_op, d));
|
|
}
|
|
|
|
expr mk_explicit_definition_body(expr type, name const & n, unsigned i, unsigned sz) {
|
|
if (i == sz) {
|
|
buffer<expr> args;
|
|
args.push_back(mk_constant(n));
|
|
unsigned j = sz;
|
|
while (j > 0) { --j; args.push_back(mk_var(j)); }
|
|
return mk_app(args);
|
|
} else {
|
|
lean_assert(is_pi(type));
|
|
return mk_lambda(abst_name(type), abst_domain(type), mk_explicit_definition_body(abst_body(type), n, i+1, sz));
|
|
}
|
|
}
|
|
|
|
static name mk_explicit_name(name const & n) {
|
|
if (n.is_anonymous()) {
|
|
throw exception("anonymous names cannot be used in definitions");
|
|
} else if (n.is_numeral()) {
|
|
return name(n, "explicit");
|
|
} else {
|
|
std::string new_name = "@";
|
|
new_name += n.get_string();
|
|
if (n.is_atomic())
|
|
return name(new_name);
|
|
else
|
|
return name(n.get_prefix(), new_name.c_str());
|
|
}
|
|
}
|
|
|
|
void mark_implicit_arguments(name const & n, unsigned sz, bool const * implicit, environment const & env) {
|
|
if (env->has_children())
|
|
throw exception(sstream() << "failed to mark implicit arguments, frontend object is read-only");
|
|
object const & obj = env->get_object(n);
|
|
if (obj.kind() != object_kind::Definition && obj.kind() != object_kind::Postulate && obj.kind() != object_kind::Builtin)
|
|
throw exception(sstream() << "failed to mark implicit arguments, the object '" << n << "' is not a definition or postulate");
|
|
if (has_implicit_arguments(n))
|
|
throw exception(sstream() << "the object '" << n << "' already has implicit argument information associated with it");
|
|
name explicit_version = mk_explicit_name(n);
|
|
if (env->find_object(explicit_version))
|
|
throw exception(sstream() << "failed to mark implicit arguments for '" << n << "', the frontend already has an object named '" << explicit_version << "'");
|
|
expr const & type = obj.get_type();
|
|
unsigned num_args = 0;
|
|
expr it = type;
|
|
while (is_pi(it)) { num_args++; it = abst_body(it); }
|
|
if (sz > num_args)
|
|
throw exception(sstream() << "failed to mark implicit arguments for '" << n << "', object has only " << num_args << " arguments, but trying to mark " << sz << " arguments");
|
|
// remove explicit suffix
|
|
while (sz > 0 && !implicit[sz - 1]) sz--;
|
|
if (sz == 0)
|
|
throw exception(sstream() << "failed to mark implicit arguments for '" << n << "', all arguments are explicit");
|
|
std::vector<bool> v(implicit, implicit+sz);
|
|
m_implicit_table[n] = mk_pair(v, explicit_version);
|
|
expr body = mk_explicit_definition_body(type, n, 0, num_args);
|
|
m_explicit_names.insert(explicit_version);
|
|
env->add_neutral_object(new mark_implicit_command(n, sz, implicit));
|
|
env->auxiliary_section([&]() {
|
|
if (obj.is_axiom() || obj.is_theorem()) {
|
|
env->add_theorem(explicit_version, type, body);
|
|
} else {
|
|
env->add_definition(explicit_version, type, body);
|
|
}
|
|
});
|
|
}
|
|
|
|
bool has_implicit_arguments(name const & n) const {
|
|
if (m_implicit_table.find(n) != m_implicit_table.end())
|
|
return true;
|
|
lean_extension const * parent = get_parent();
|
|
if (parent)
|
|
return parent->has_implicit_arguments(n);
|
|
else
|
|
return false;
|
|
}
|
|
|
|
std::vector<bool> const & get_implicit_arguments(name const & n) const {
|
|
auto it = m_implicit_table.find(n);
|
|
if (it != m_implicit_table.end())
|
|
return it->second.first;
|
|
lean_extension const * parent = get_parent();
|
|
if (parent)
|
|
return parent->get_implicit_arguments(n);
|
|
else
|
|
return g_empty_vector;
|
|
}
|
|
|
|
std::vector<bool> const & 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 & get_explicit_version(name const & n) const {
|
|
auto it = m_implicit_table.find(n);
|
|
if (it != m_implicit_table.end())
|
|
return it->second.second;
|
|
lean_extension const * parent = get_parent();
|
|
if (parent)
|
|
return parent->get_explicit_version(n);
|
|
else
|
|
return name::anonymous();
|
|
}
|
|
|
|
bool is_explicit(name const & n) const {
|
|
if (m_explicit_names.find(n) != m_explicit_names.end())
|
|
return true;
|
|
lean_extension const * parent = get_parent();
|
|
if (parent)
|
|
return parent->is_explicit(n);
|
|
else
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
\brief It is too expensive to normalize \c t when checking if there is a coercion for it.
|
|
So, we just do a 'quick' normalization following a chain of definitions.
|
|
*/
|
|
expr coercion_type_normalization(expr t, ro_environment const & env) const {
|
|
while (true) {
|
|
if (is_constant(t)) {
|
|
auto obj = env->find_object(const_name(t));
|
|
if (obj && obj->is_definition()) {
|
|
t = obj->get_value();
|
|
} else {
|
|
return t;
|
|
}
|
|
} else {
|
|
return t;
|
|
}
|
|
}
|
|
}
|
|
|
|
void add_coercion(expr const & f, environment const & env) {
|
|
expr type = env->type_check(f);
|
|
expr norm_type = env->normalize(type);
|
|
if (!is_arrow(norm_type))
|
|
throw exception("invalid coercion declaration, a coercion must have an arrow type (i.e., a non-dependent functional type)");
|
|
expr from = coercion_type_normalization(abst_domain(norm_type), env);
|
|
expr to = coercion_type_normalization(abst_body(norm_type), env);
|
|
if (from == to)
|
|
throw exception("invalid coercion declaration, 'from' and 'to' types are the same");
|
|
if (get_coercion_core(from, to))
|
|
throw exception("invalid coercion declaration, frontend already has a coercion for the given types");
|
|
m_coercion_map[expr_pair(from, to)] = f;
|
|
m_coercion_set.insert(f);
|
|
list<expr_pair> l = get_coercions_core(from);
|
|
insert(m_type_coercions, from, cons(expr_pair(to, f), l));
|
|
env->add_neutral_object(new coercion_declaration(f));
|
|
}
|
|
|
|
optional<expr> get_coercion_core(expr const & from_type, expr const & to_type) const {
|
|
expr_pair p(from_type, to_type);
|
|
auto it = m_coercion_map.find(p);
|
|
if (it != m_coercion_map.end())
|
|
return some_expr(it->second);
|
|
lean_extension const * parent = get_parent();
|
|
if (parent)
|
|
return parent->get_coercion_core(from_type, to_type);
|
|
else
|
|
return none_expr();
|
|
}
|
|
|
|
optional<expr> get_coercion(expr const & from_type, expr const & to_type, ro_environment const & env) const {
|
|
return get_coercion_core(coercion_type_normalization(from_type, env),
|
|
coercion_type_normalization(to_type, env));
|
|
}
|
|
|
|
list<expr_pair> get_coercions_core(expr const & from_type) const {
|
|
auto r = m_type_coercions.find(from_type);
|
|
if (r != m_type_coercions.end())
|
|
return r->second;
|
|
lean_extension const * parent = get_parent();
|
|
if (parent)
|
|
return parent->get_coercions_core(from_type);
|
|
else
|
|
return list<expr_pair>();
|
|
}
|
|
|
|
list<expr_pair> get_coercions(expr const & from_type, ro_environment const & env) const {
|
|
return get_coercions_core(coercion_type_normalization(from_type, env));
|
|
}
|
|
|
|
bool is_coercion(expr const & f) const {
|
|
if (m_coercion_set.find(f) != m_coercion_set.end())
|
|
return true;
|
|
lean_extension const * parent = get_parent();
|
|
return parent && parent->is_coercion(f);
|
|
}
|
|
|
|
optional<expr> get_alias(name const & n) const {
|
|
auto it = m_aliases.find(n);
|
|
if (it != m_aliases.end())
|
|
return some_expr(it->second);
|
|
lean_extension const * parent = get_parent();
|
|
if (parent)
|
|
return parent->get_alias(n);
|
|
else
|
|
return none_expr();
|
|
}
|
|
|
|
optional<list<name>> get_aliased(expr const & e) const {
|
|
auto it = m_inv_aliases.find(e);
|
|
if (it != m_inv_aliases.end())
|
|
return optional<list<name>>(it->second);
|
|
lean_extension const * parent = get_parent();
|
|
if (parent)
|
|
return parent->get_aliased(e);
|
|
else
|
|
return optional<list<name>>();
|
|
}
|
|
|
|
void add_alias(name const & n, expr const & e, environment const & env) {
|
|
if (get_alias(n))
|
|
throw exception(sstream() << "alias '" << n << "' was already defined");
|
|
m_aliases[n] = e;
|
|
auto l = get_aliased(e);
|
|
if (l)
|
|
m_inv_aliases[e] = list<name>(n, *l);
|
|
else
|
|
m_inv_aliases[e] = list<name>(n);
|
|
env->add_neutral_object(new alias_declaration(n, e));
|
|
}
|
|
};
|
|
|
|
struct lean_extension_initializer {
|
|
unsigned m_extid;
|
|
lean_extension_initializer() {
|
|
m_extid = environment_cell::register_extension([](){ return std::unique_ptr<environment_extension>(new lean_extension()); });
|
|
}
|
|
};
|
|
|
|
static lean_extension_initializer g_lean_extension_initializer;
|
|
|
|
static lean_extension const & to_ext(ro_environment const & env) {
|
|
return env->get_extension<lean_extension>(g_lean_extension_initializer.m_extid);
|
|
}
|
|
|
|
static lean_extension & to_ext(environment const & env) {
|
|
return env->get_extension<lean_extension>(g_lean_extension_initializer.m_extid);
|
|
}
|
|
|
|
/**
|
|
\brief Import all definitions and notation.
|
|
*/
|
|
void init_frontend(environment const & env, io_state & ios, bool kernel_only) {
|
|
ios.set_formatter(mk_pp_formatter(env));
|
|
if (!kernel_only)
|
|
import_all(env);
|
|
}
|
|
void init_frontend(environment const & env) {
|
|
io_state ios;
|
|
init_frontend(env, ios);
|
|
}
|
|
|
|
void add_infix(environment const & env, io_state const & ios, name const & opn, unsigned p, expr const & d) {
|
|
to_ext(env).add_op(infix(opn, p), d, true, env, ios);
|
|
}
|
|
void add_infixl(environment const & env, io_state const & ios, name const & opn, unsigned p, expr const & d) {
|
|
to_ext(env).add_op(infixl(opn, p), d, true, env, ios);
|
|
}
|
|
void add_infixr(environment const & env, io_state const & ios, name const & opn, unsigned p, expr const & d) {
|
|
to_ext(env).add_op(infixr(opn, p), d, true, env, ios);
|
|
}
|
|
void add_prefix(environment const & env, io_state const & ios, name const & opn, unsigned p, expr const & d) {
|
|
to_ext(env).add_op(prefix(opn, p), d, false, env, ios);
|
|
}
|
|
void add_postfix(environment const & env, io_state const & ios, name const & opn, unsigned p, expr const & d) {
|
|
to_ext(env).add_op(postfix(opn, p), d, true, env, ios);
|
|
}
|
|
void add_mixfixl(environment const & env, io_state const & ios, unsigned sz, name const * opns, unsigned p, expr const & d) {
|
|
to_ext(env).add_op(mixfixl(sz, opns, p), d, false, env, ios);
|
|
}
|
|
void add_mixfixr(environment const & env, io_state const & ios, unsigned sz, name const * opns, unsigned p, expr const & d) {
|
|
to_ext(env).add_op(mixfixr(sz, opns, p), d, true, env, ios);
|
|
}
|
|
void add_mixfixc(environment const & env, io_state const & ios, unsigned sz, name const * opns, unsigned p, expr const & d) {
|
|
to_ext(env).add_op(mixfixc(sz, opns, p), d, false, env, ios);
|
|
}
|
|
void add_mixfixo(environment const & env, io_state const & ios, unsigned sz, name const * opns, unsigned p, expr const & d) {
|
|
to_ext(env).add_op(mixfixo(sz, opns, p), d, true, env, ios);
|
|
}
|
|
operator_info find_op_for(ro_environment const & env, expr const & n, bool unicode) {
|
|
operator_info r = to_ext(env).find_op_for(n, unicode);
|
|
if (r) {
|
|
return r;
|
|
} else if (is_constant(n)) {
|
|
optional<object> obj = env->find_object(const_name(n));
|
|
if (obj && obj->is_builtin() && obj->get_name() == const_name(n)) {
|
|
// n is a constant that is referencing a builtin object.
|
|
// If the notation is associated with the builtin object, we should try it.
|
|
// TODO(Leo): Remark: in the new approach using .Lean files, the table is populated with constants.
|
|
// So, this branch is not really needed anymore.
|
|
return to_ext(env).find_op_for(obj->get_value(), unicode);
|
|
} else {
|
|
return r;
|
|
}
|
|
} else if (is_value(n)) {
|
|
// Check whether the notation was declared for a constant referencing this builtin object.
|
|
return to_ext(env).find_op_for(mk_constant(to_value(n).get_name()), unicode);
|
|
} else {
|
|
return r;
|
|
}
|
|
}
|
|
operator_info find_nud(ro_environment const & env, name const & n) {
|
|
return to_ext(env).find_nud(n);
|
|
}
|
|
operator_info find_led(ro_environment const & env, name const & n) {
|
|
return to_ext(env).find_led(n);
|
|
}
|
|
optional<unsigned> get_lbp(ro_environment const & env, name const & n) {
|
|
return to_ext(env).get_lbp(n);
|
|
}
|
|
void mark_implicit_arguments(environment const & env, name const & n, unsigned sz, bool const * implicit) {
|
|
to_ext(env).mark_implicit_arguments(n, sz, implicit, env);
|
|
}
|
|
void mark_implicit_arguments(environment const & env, name const & n, unsigned prefix_sz) {
|
|
buffer<bool> implicit; implicit.resize(prefix_sz, true);
|
|
mark_implicit_arguments(env, n, implicit.size(), implicit.data());
|
|
}
|
|
void mark_implicit_arguments(environment const & env, expr const & n, unsigned prefix_sz) {
|
|
if (is_constant(n)) {
|
|
mark_implicit_arguments(env, const_name(n), prefix_sz);
|
|
} else {
|
|
lean_assert(is_value(n));
|
|
mark_implicit_arguments(env, to_value(n).get_name(), prefix_sz);
|
|
}
|
|
}
|
|
void mark_implicit_arguments(environment const & env, name const & n, std::initializer_list<bool> const & l) {
|
|
mark_implicit_arguments(env, n, l.size(), l.begin());
|
|
}
|
|
void mark_implicit_arguments(environment const & env, expr const & n, std::initializer_list<bool> const & l) {
|
|
if (is_constant(n)) {
|
|
mark_implicit_arguments(env, const_name(n), l);
|
|
} else {
|
|
lean_assert(is_value(n));
|
|
mark_implicit_arguments(env, to_value(n).get_name(), l);
|
|
}
|
|
}
|
|
bool has_implicit_arguments(ro_environment const & env, name const & n) {
|
|
return to_ext(env).has_implicit_arguments(n);
|
|
}
|
|
std::vector<bool> const & get_implicit_arguments(ro_environment const & env, name const & n) {
|
|
return to_ext(env).get_implicit_arguments(n);
|
|
}
|
|
std::vector<bool> const & get_implicit_arguments(ro_environment const & env, expr const & n) {
|
|
if (is_constant(n))
|
|
return get_implicit_arguments(env, const_name(n));
|
|
else
|
|
return g_empty_vector;
|
|
}
|
|
name const & get_explicit_version(ro_environment const & env, name const & n) {
|
|
return to_ext(env).get_explicit_version(n);
|
|
}
|
|
bool is_explicit(ro_environment const & env, name const & n) {
|
|
return to_ext(env).is_explicit(n);
|
|
}
|
|
void add_coercion(environment const & env, expr const & f) {
|
|
to_ext(env).add_coercion(f, env);
|
|
}
|
|
optional<expr> get_coercion(ro_environment const & env, expr const & from_type, expr const & to_type) {
|
|
return to_ext(env).get_coercion(from_type, to_type, env);
|
|
}
|
|
list<expr_pair> get_coercions(ro_environment const & env, expr const & from_type) {
|
|
return to_ext(env).get_coercions(from_type, env);
|
|
}
|
|
bool is_coercion(ro_environment const & env, expr const & f) {
|
|
return to_ext(env).is_coercion(f);
|
|
}
|
|
optional<expr> get_alias(ro_environment const & env, name const & n) {
|
|
return to_ext(env).get_alias(n);
|
|
}
|
|
optional<list<name>> get_aliased(ro_environment const & env, expr const & e) {
|
|
return to_ext(env).get_aliased(e);
|
|
}
|
|
void add_alias(environment const & env, name const & n, expr const & e) {
|
|
to_ext(env).add_alias(n, e, env);
|
|
}
|
|
}
|