feat(frontends/lean): add 'universe variable' command

We can declare variables anywhere. So, we must also be able do declare
"universe" variables anywhere. Here is a minimal example that requires
this feature

```
-- We want A and B to be in the same universe
universe variable l
variable A : Type.{l}
variable B : Type.{l}
definition tst := A = B
```

The following doesn't work because A and B are in different universes
```
variable A : Type
variable B : Type
definition tst := A = B
```

The following works, but tst is not universe polymorphic, since l is
one *fixed* global universe
```
universe l
variable A : Type.{l}
variable B : Type.{l}
definition tst := A = B
```
This commit is contained in:
Leonardo de Moura 2014-10-11 10:58:15 -07:00
parent 6a40f80612
commit ca632cca13
9 changed files with 70 additions and 16 deletions

View file

@ -24,8 +24,8 @@ Author: Leonardo de Moura
#include "frontends/lean/tokens.h"
namespace lean {
static environment declare_universe(parser & p, environment env, name const & n) {
if (in_section_or_context(env)) {
static environment declare_universe(parser & p, environment env, name const & n, bool local) {
if (in_section_or_context(env) || local) {
p.add_local_level(n, mk_param_univ(n));
} else {
name const & ns = get_namespace(env);
@ -37,23 +37,37 @@ static environment declare_universe(parser & p, environment env, name const & n)
return env;
}
environment universe_cmd(parser & p) {
name n = p.check_id_next("invalid 'universe' command, identifier expected");
return declare_universe(p, p.env(), n);
}
environment universes_cmd(parser & p) {
static environment universes_cmd_core(parser & p, bool local) {
if (!p.curr_is_identifier())
throw parser_error("invalid 'universes' command, identifier expected", p.pos());
environment env = p.env();
while (p.curr_is_identifier()) {
name n = p.get_name_val();
p.next();
env = declare_universe(p, env, n);
env = declare_universe(p, env, n, local);
}
return env;
}
static environment universe_cmd(parser & p) {
if (p.curr_is_token(get_variables_tk())) {
p.next();
return universes_cmd_core(p, true);
} else {
bool local = false;
if (p.curr_is_token(get_variable_tk())) {
p.next();
local = true;
}
name n = p.check_id_next("invalid 'universe' command, identifier expected");
return declare_universe(p, p.env(), n, local);
}
}
static environment universes_cmd(parser & p) {
return universes_cmd_core(p, false);
}
bool parse_univ_params(parser & p, buffer<name> & ps) {
if (p.curr_is_token(get_llevel_curly_tk())) {
p.next();
@ -383,7 +397,7 @@ environment definition_cmd_core(parser & p, bool is_theorem, bool is_opaque, boo
value = Fun_as_is(section_value_ps, value, p);
update_univ_parameters(ls_buffer, collect_univ_params(value, collect_univ_params(type)), p);
ls = to_list(ls_buffer.begin(), ls_buffer.end());
levels section_ls = collect_section_nonvar_levels(p, ls);
levels section_ls = collect_local_nonvar_levels(p, ls);
remove_section_variables(p, section_ps);
if (!section_ps.empty()) {
expr ref = mk_section_local_ref(real_n, section_ls, section_ps);

View file

@ -608,7 +608,7 @@ struct inductive_cmd_fn {
buffer<expr> section_params_only(section_params);
remove_section_variables(m_p, section_params_only);
// Create aliases/local refs
levels section_levels = collect_section_nonvar_levels(m_p, ls);
levels section_levels = collect_local_nonvar_levels(m_p, ls);
for (auto & d : decls) {
name d_name = inductive_decl_name(d);
name d_short_name(d_name.get_string());

View file

@ -75,7 +75,7 @@ void init_token_table(token_table & t) {
{"using", 0}, {"|", 0}, {"!", g_max_prec}, {"with", 0}, {"...", 0}, {",", 0},
{".", 0}, {":", 0}, {"::", 0}, {"calc", 0}, {":=", 0}, {"--", 0}, {"#", 0},
{"(*", 0}, {"/-", 0}, {"begin", g_max_prec}, {"proof", g_max_prec}, {"qed", 0}, {"@", g_max_prec},
{"sorry", g_max_prec}, {"+", g_plus_prec}, {g_cup, g_cup_prec}, {"->", g_arrow_prec},
{"sorry", g_max_prec}, {"+", g_plus_prec}, {g_cup, g_cup_prec}, {"->", g_arrow_prec}, {"local", 0},
{nullptr, 0}};
char const * commands[] =

View file

@ -65,6 +65,7 @@ static name * g_definition = nullptr;
static name * g_theorem = nullptr;
static name * g_axiom = nullptr;
static name * g_variable = nullptr;
static name * g_variables = nullptr;
static name * g_opaque = nullptr;
static name * g_instance = nullptr;
static name * g_priority = nullptr;
@ -148,6 +149,7 @@ void initialize_tokens() {
g_opaque = new name("opaque");
g_axiom = new name("axiom");
g_variable = new name("variable");
g_variables = new name("variables");
g_instance = new name("[instance]");
g_priority = new name("[priority");
g_coercion = new name("[coercion]");
@ -193,6 +195,7 @@ void finalize_tokens() {
delete g_theorem;
delete g_opaque;
delete g_axiom;
delete g_variables;
delete g_variable;
delete g_instance;
delete g_priority;
@ -312,6 +315,7 @@ name const & get_definition_tk() { return *g_definition; }
name const & get_theorem_tk() { return *g_theorem; }
name const & get_axiom_tk() { return *g_axiom; }
name const & get_variable_tk() { return *g_variable; }
name const & get_variables_tk() { return *g_variables; }
name const & get_opaque_tk() { return *g_opaque; }
name const & get_instance_tk() { return *g_instance; }
name const & get_priority_tk() { return *g_priority; }

View file

@ -67,6 +67,7 @@ name const & get_definition_tk();
name const & get_theorem_tk();
name const & get_axiom_tk();
name const & get_variable_tk();
name const & get_variables_tk();
name const & get_opaque_tk();
name const & get_instance_tk();
name const & get_priority_tk();

View file

@ -60,8 +60,8 @@ void sort_section_params(expr_struct_set const & locals, parser const & p, buffe
});
}
// Return the levels in \c ls that are defined in the section
levels collect_section_nonvar_levels(parser & p, level_param_names const & ls) {
// Return the local levels in \c ls that are not associated with variables
levels collect_local_nonvar_levels(parser & p, level_param_names const & ls) {
buffer<level> section_ls_buffer;
for (name const & l : ls) {
if (p.get_local_level_index(l) && !p.is_local_level_variable(l))

View file

@ -22,8 +22,10 @@ void check_atomic(name const & n);
void check_in_section_or_context(parser const & p);
bool is_root_namespace(name const & n);
name remove_root_prefix(name const & n);
/** \brief Return the levels in \c ls that are defined in the section, but are not tagged as variables. */
levels collect_section_nonvar_levels(parser & p, level_param_names const & ls);
/** \brief Return the local levels in \c ls that are not tagged as variables.
A local level is tagged as variable if it associated with a variable.
*/
levels collect_local_nonvar_levels(parser & p, level_param_names const & ls);
/** \brief Collect local (section) constants occurring in type and value, sort them, and store in section_ps */
void collect_section_locals(expr const & type, expr const & value, parser const & p, buffer<expr> & section_ps);
/** \brief Copy the local parameters to \c section_ps, then sort \c section_ps (using the order in which they were declared). */

28
tests/lean/univ_vars.lean Normal file
View file

@ -0,0 +1,28 @@
import logic
set_option pp.universes true
universe u
variable A : Type.{u}
definition id1 (a : A) : A := a
check @id1
variable B : Type
definition id2 (a : B) : B := a
check @id2
universe variable k
variable C : Type.{k}
definition id3 (a : C) := a
check @id3
universe variables l m
variable A₁ : Type.{l}
variable A₂ : Type.{l}
definition foo (a₁ : A₁) (a₂ : A₂) := a₁ == a₂
check @foo
check Type.{m}

View file

@ -0,0 +1,5 @@
id1 : Π (A : Type.{u}), A → A
id2.{l_2} : Π (B : Type.{l_2}), B → B
id3.{k} : Π (C : Type.{k}), C → C
foo.{l} : Π (A₁ A₂ : Type.{l}), A₁ → A₂ → Prop
Type.{m} : Type.{succ m}