feat(frontends/lean/parser): add namespace/section/end commands, add support for explicit universe levels, fix Type notation'

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2014-06-13 11:24:26 -07:00
parent 378b691ea7
commit ce259e6265
7 changed files with 257 additions and 45 deletions

View file

@ -4,18 +4,35 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include "util/sstream.h"
#include "library/io_state_stream.h"
#include "library/scoped_ext.h"
#include "library/aliases.h"
#include "frontends/lean/parser.h"
namespace lean {
static name g_raw("raw");
static void check_atomic(name const & n) {
if (!n.is_atomic())
throw exception(sstream() << "invalid declaration name '" << n << "', identifier must be atomic");
}
environment universe_cmd(parser & p) {
if (p.curr_is_identifier()) {
// TODO(Leo): take namespace and scopes into account
name n = p.get_name_val();
check_atomic(n);
p.next();
return p.env().add_universe(n);
environment env = p.env();
if (in_section(env)) {
p.add_local_level(n, mk_param_univ(n));
} else {
name const & ns = get_namespace(env);
name full_n = ns + n;
env = env.add_universe(full_n);
if (!ns.is_anonymous())
env = add_alias(env, n, mk_global_univ(full_n));
}
return env;
} else {
throw parser_error("invalid universe declaration, identifier expected", p.cmd_pos());
}
@ -35,10 +52,35 @@ environment print_cmd(parser & p) {
return p.env();
}
environment section_cmd(parser & p) {
p.push_local_scope();
return push_scope(p.env(), p.ios());
}
environment namespace_cmd(parser & p) {
if (p.curr_is_identifier()) {
name n = p.get_name_val();
check_atomic(n);
p.next();
return push_scope(p.env(), p.ios(), n);
} else {
throw parser_error("invalid namespace declaration, identifier expected", p.cmd_pos());
}
}
environment end_scoped_cmd(parser & p) {
if (in_section(p.env()))
p.pop_local_scope();
return pop_scope(p.env());
}
cmd_table init_cmd_table() {
cmd_table r;
add_cmd(r, cmd_info("print", "print a string", print_cmd));
add_cmd(r, cmd_info("universe", "declare a global universe level", universe_cmd));
add_cmd(r, cmd_info("print", "print a string", print_cmd));
add_cmd(r, cmd_info("universe", "declare a global universe level", universe_cmd));
add_cmd(r, cmd_info("section", "open a new section", section_cmd));
add_cmd(r, cmd_info("namespace", "open a new namespace", namespace_cmd));
add_cmd(r, cmd_info("end", "close the current namespace/section", end_scoped_cmd));
return r;
}

View file

@ -19,7 +19,7 @@ static expr parse_Type(parser & p, unsigned, expr const *) {
p.check_token_next(g_rcurly, "invalid Type expression, '}' expected");
return mk_sort(l);
} else {
return mk_sort(p.mk_new_level_param());
return p.mk_Type();
}
}

View file

@ -10,6 +10,7 @@ Author: Leonardo de Moura
#include "util/interrupt.h"
#include "util/script_exception.h"
#include "util/sstream.h"
#include "util/flet.h"
#include "kernel/for_each_fn.h"
#include "kernel/abstract.h"
#include "kernel/instantiate.h"
@ -42,6 +43,7 @@ parser::parser(environment const & env, io_state const & ios,
m_env(env), m_ios(ios), m_ss(ss),
m_verbose(true), m_use_exceptions(use_exceptions),
m_scanner(strm, strm_name), m_pos_table(std::make_shared<pos_info_table>()) {
m_type_use_placeholder = true;
m_found_errors = false;
updt_options();
m_next_tag_idx = 0;
@ -193,13 +195,31 @@ expr parser::mk_app(expr fn, expr arg, pos_info const & p) {
return save_pos(::lean::mk_app(fn, arg), p);
}
void parser::add_local_entry(name const & n, expr const & e) {
void parser::push_local_scope() {
m_local_level_decls.push();
m_local_decls.push();
}
void parser::pop_local_scope() {
m_local_level_decls.pop();
m_local_decls.pop();
}
void parser::add_local_level(name const & n, level const & l) {
if (m_env.is_universe(n))
throw exception(sstream() << "invalid universe declaration, '" << n << "' shadows a global universe");
if (m_local_level_decls.contains(n))
throw exception(sstream() << "invalid universe declaration, '" << n << "' shadows a local universe");
m_local_level_decls.insert(n, local_level_entry(l, m_local_level_decls.size()));
}
void parser::add_local_expr(name const & n, expr const & e) {
m_local_decls.insert(n, local_entry(e, m_local_decls.size()));
}
void parser::add_local_decl(expr const & e) {
void parser::add_local(expr const & e) {
lean_assert(is_local(e));
add_local_entry(local_pp_name(e), e);
add_local_expr(local_pp_name(e), e);
}
/** \brief Parse a sequence of identifiers <tt>ID*</tt>. Store the result in \c result. */
@ -214,6 +234,7 @@ static name g_period(".");
static name g_colon(":");
static name g_lparen("(");
static name g_rparen(")");
static name g_llevel_curly(".{");
static name g_lcurly("{");
static name g_rcurly("}");
static name g_lbracket("[");
@ -282,7 +303,9 @@ level parser::parse_level_id() {
return it->second.first;
if (m_env.is_universe(id))
return mk_global_univ(id);
throw parser_error(sstream() << "unknown level '" << id << "'", p);
if (auto it = get_alias_level(m_env, id))
return *it;
throw parser_error(sstream() << "unknown universe '" << id << "'", p);
}
level parser::parse_level_nud() {
@ -332,9 +355,21 @@ level parser::parse_level(unsigned rbp) {
return left;
}
level parser::mk_new_level_param() {
// TODO(Leo)
return level();
expr parser::mk_Type() {
if (m_type_use_placeholder) {
return mk_sort(mk_level_placeholder());
} else {
unsigned i = 1;
name l("l");
name r = l.append_after(i);
while (m_local_level_decls.contains(r) || m_env.is_universe(r)) {
i++;
r = l.append_after(i);
}
level lvl = mk_param_univ(r);
add_local_level(r, lvl);
return mk_sort(lvl);
}
}
/** \brief Parse <tt>ID ':' expr</tt>, where the expression represents the type of the identifier. */
@ -394,7 +429,7 @@ void parser::parse_binder_block(buffer<parameter> & r, binder_info const & bi) {
for (auto p : names) {
expr arg_type = type ? *type : save_pos(mk_expr_placeholder(), p.first);
expr local = mk_local(p.second, p.second, arg_type);
add_local_decl(local);
add_local(local);
r.push_back(parameter(p.first, local, bi));
}
}
@ -542,21 +577,41 @@ expr parser::parse_id() {
// locals
if (it1 != m_local_decls.end())
return copy_with_new_pos(it1->second.first, p);
buffer<level> lvl_buffer;
auto p_lvl = pos();
levels ls;
if (curr_is_token(g_llevel_curly)) {
next();
while (!curr_is_token(g_rcurly)) {
lvl_buffer.push_back(parse_level());
}
next();
ls = to_list(lvl_buffer.begin(), lvl_buffer.end());
}
optional<expr> r;
// globals
if (m_env.find(id))
return save_pos(mk_constant(id), p);
r = save_pos(mk_constant(id, ls), p);
// private globals
if (auto prv_id = user_to_hidden_name(m_env, id))
return save_pos(mk_constant(*prv_id), p);
else if (auto prv_id = user_to_hidden_name(m_env, id))
r = save_pos(mk_constant(*prv_id, ls), p);
// aliases
auto as = get_alias_exprs(m_env, id);
if (!is_nil(as)) {
if (!is_nil(ls))
throw parser_error(sstream() << "explicit universe levels are not supported for overloaded/aliased identifier '" << id << "' "
<< "solutions: use a fully qualified name; or use non-overloaded alias", p_lvl);
buffer<expr> new_as;
for (auto const & e : as)
if (r)
new_as.push_back(*r);
for (auto const & e : as) {
new_as.push_back(copy_with_new_pos(e, p));
return mk_choice(new_as.size(), new_as.data());
}
r = mk_choice(new_as.size(), new_as.data());
}
throw parser_error(sstream() << "unknown identifier '" << id << "'", p);
if (!r)
throw parser_error(sstream() << "unknown identifier '" << id << "'", p);
return *r;
}
expr parser::parse_numeral_expr() {
@ -621,14 +676,14 @@ expr parser::parse_expr(unsigned rbp) {
expr parser::parse_scoped_expr(unsigned num_locals, expr const * locals, unsigned rbp) {
local_decls::mk_scope scope(m_local_decls);
for (unsigned i = 0; i < num_locals; i++)
add_local_decl(locals[i]);
add_local(locals[i]);
return parse_expr(rbp);
}
expr parser::parse_scoped_expr(unsigned num_params, parameter const * ps, unsigned rbp) {
local_decls::mk_scope scope(m_local_decls);
for (unsigned i = 0; i < num_params; i++)
add_local_decl(ps[i].m_local);
add_local(ps[i].m_local);
return parse_expr(rbp);
}
@ -658,6 +713,8 @@ tactic parser::parse_tactic(unsigned /* rbp */) {
void parser::parse_command() {
lean_assert(curr() == scanner::token_kind::CommandKeyword);
flet<bool> save1(m_type_use_placeholder, m_type_use_placeholder);
m_last_cmd_pos = pos();
name const & cmd_name = get_token_info().value();
if (auto it = cmds().find(cmd_name)) {
next();

View file

@ -64,10 +64,10 @@ class parser {
unsigned m_next_tag_idx;
bool m_found_errors;
pos_info_table_ptr m_pos_table;
enum class scope_kind { Scope, Namespace, Structure };
std::vector<name> m_namespace_prefixes;
std::vector<scope_kind> m_scope_kinds;
// If m_type_use_placeholder is true, then the token Type is parsed as Type.{_}.
// if it is false, then it is parsed as Type.{l} where l is a fresh parameter,
// and is automatically inserted into m_local_level_decls.
bool m_type_use_placeholder;
void display_error_pos(unsigned line, unsigned pos);
void display_error_pos(pos_info p);
@ -100,9 +100,6 @@ class parser {
/** \brief Return true if the current token is a keyword named \c tk or an identifier named \c tk */
bool curr_is_token_or_id(name const & tk) const;
void add_local_entry(name const & n, expr const & e);
void add_local_decl(expr const & t);
unsigned curr_level_lbp() const;
level parse_max_imax(bool is_max);
level parse_level_id();
@ -140,7 +137,6 @@ public:
unsigned parse_small_nat();
level parse_level(unsigned rbp = 0);
level mk_new_level_param();
parameter parse_binder();
void parse_binders(buffer<parameter> & r);
@ -154,6 +150,25 @@ public:
tactic parse_tactic(unsigned rbp = 0);
void push_local_scope();
void pop_local_scope();
void add_local_level(name const & n, level const & l);
void add_local_expr(name const & n, expr const & e);
void add_local(expr const & t);
/**
\brief Specify how the method mk_Type behaves. When <tt>set_type_use_placeholder(true)</tt>, then
it returns <tt>'Type.{_}'</tt>, where '_' is placeholder that instructs the Lean elaborator to
automalically infer a universe level expression for '_'. When <tt>set_type_use_placeholder(false)</tt>,
then it returns <tt>'Type.{l}'</tt>, where \c l is a fresh universe level parameter.
The new parameter is automatically added to \c m_local_level_decls.
\remark When the parse is created the flag is set to false.
\remark Before parsing a command, the parser automatically "caches" the current value, and
restores it after the command is parsed (or aborted).
*/
void set_type_use_placeholder(bool f) { m_type_use_placeholder = f; }
expr mk_Type();
/** \brief Return the current position information */
pos_info pos() const { return pos_info(m_scanner.get_line(), m_scanner.get_pos()); }
expr save_pos(expr e, pos_info p);

View file

@ -15,34 +15,78 @@ Author: Leonardo de Moura
#include "library/kernel_bindings.h"
#include "library/aliases.h"
#include "library/placeholder.h"
#include "library/scoped_ext.h"
namespace lean {
struct aliases_ext;
static aliases_ext const & get_extension(environment const & env);
static environment update(environment const & env, aliases_ext const & ext);
struct aliases_ext : public environment_extension {
rb_map<name, list<expr>, name_quick_cmp> m_aliases;
rb_map<expr, name, expr_quick_cmp> m_inv_aliases;
rb_map<name, level, name_quick_cmp> m_level_aliases;
rb_map<level, name, level_quick_cmp> m_inv_level_aliases;
struct state {
rb_map<name, list<expr>, name_quick_cmp> m_aliases;
rb_map<expr, name, expr_quick_cmp> m_inv_aliases;
rb_map<name, level, name_quick_cmp> m_level_aliases;
rb_map<level, name, level_quick_cmp> m_inv_level_aliases;
};
state m_state;
list<state> m_scopes;
void add_alias(name const & a, expr const & e) {
auto it = m_aliases.find(a);
auto it = m_state.m_aliases.find(a);
if (it)
m_aliases.insert(a, list<expr>(e, *it));
m_state.m_aliases.insert(a, list<expr>(e, *it));
else
m_aliases.insert(a, list<expr>(e));
m_inv_aliases.insert(e, a);
m_state.m_aliases.insert(a, list<expr>(e));
m_state.m_inv_aliases.insert(e, a);
}
void add_alias(name const & a, level const & l) {
auto it = m_level_aliases.find(a);
auto it = m_state.m_level_aliases.find(a);
if (it)
throw exception(sstream() << "universe level alias '" << a << "' shadows existing alias");
m_level_aliases.insert(a, l);
m_inv_level_aliases.insert(l, a);
m_state.m_level_aliases.insert(a, l);
m_state.m_inv_level_aliases.insert(l, a);
}
void push() {
m_scopes = list<state>(m_state, m_scopes);
}
void pop() {
m_state = head(m_scopes);
m_scopes = tail(m_scopes);
}
static environment using_namespace(environment const & env, io_state const &, name const &) {
// do nothing, aliases are treated in a special way in the frontend.
return env;
}
static environment push_scope(environment const & env) {
aliases_ext ext = get_extension(env);
ext.push();
environment new_env = update(env, ext);
if (!in_section(new_env))
new_env = add_aliases(new_env, get_namespace(new_env), name());
return new_env;
}
static environment pop_scope(environment const & env) {
aliases_ext ext = get_extension(env);
ext.pop();
return update(env, ext);
}
};
static name g_aliases("aliases");
struct aliases_ext_reg {
unsigned m_ext_id;
aliases_ext_reg() { m_ext_id = environment::register_extension(std::make_shared<aliases_ext>()); }
aliases_ext_reg() {
register_scoped_ext(g_aliases, aliases_ext::using_namespace, aliases_ext::push_scope, aliases_ext::pop_scope);
m_ext_id = environment::register_extension(std::make_shared<aliases_ext>());
}
};
static aliases_ext_reg g_ext;
static aliases_ext const & get_extension(environment const & env) {
@ -59,12 +103,12 @@ environment add_alias(environment const & env, name const & a, expr const & e) {
}
optional<name> is_aliased(environment const & env, expr const & t) {
auto it = get_extension(env).m_inv_aliases.find(t);
auto it = get_extension(env).m_state.m_inv_aliases.find(t);
return it ? optional<name>(*it) : optional<name>();
}
list<expr> get_alias_exprs(environment const & env, name const & n) {
auto it = get_extension(env).m_aliases.find(n);
auto it = get_extension(env).m_state.m_aliases.find(n);
return it ? *it : list<expr>();
}
@ -77,12 +121,12 @@ environment add_alias(environment const & env, name const & a, level const & l)
}
optional<name> is_aliased(environment const & env, level const & l) {
auto it = get_extension(env).m_inv_level_aliases.find(l);
auto it = get_extension(env).m_state.m_inv_level_aliases.find(l);
return it ? optional<name>(*it) : optional<name>();
}
optional<level> get_alias_level(environment const & env, name const & n) {
auto it = get_extension(env).m_level_aliases.find(n);
auto it = get_extension(env).m_state.m_level_aliases.find(n);
return it ? some_level(*it) : optional<level>();
}

37
tests/lean/t3.lean Normal file
View file

@ -0,0 +1,37 @@
universe u
print raw Type.{u}
namespace tst
universe v
print raw Type.{v}
print raw Type.{tst.v}
end
print raw Type.{tst.v}
print raw Type.{v} -- Error: alias 'v' is not available anymore
section
universe z -- Remark: this is a local universe
print raw Type.{z}
end
print raw Type.{z} -- Error: local universe 'z' is gone
section
namespace foo -- Error: we cannot create a namespace inside a section
end
namespace tst
print raw Type.{v} -- Remark: alias 'v' is available again
print raw Type.{u}
namespace foo
universe U
end
end
print raw Type.{tst.foo.U}
namespace tst.foo -- Error: we cannot use qualified names in declarations
universe full.name.U -- Error: we cannot use qualified names in declarations
namespace tst
namespace foo
print raw Type.{v} -- Remark: alias 'v' for 'tst.v' is available again
print raw Type.{U} -- Remark: alias 'U' for 'tst.foo.U' is available again
end
end
namespace bla
universe u -- Error: we cannot shadow universe levels
end
print raw Type.{bla.u} -- Error: we failed to declare bla.u

View file

@ -0,0 +1,17 @@
Type.{u}
Type.{tst.v}
Type.{tst.v}
Type.{tst.v}
t3.lean:9:16: error: unknown universe 'v'
Type.{z}
t3.lean:14:16: error: unknown universe 'z'
t3.lean:16:2: error: invalid namespace declaration, a namespace cannot be declared inside a section
Type.{tst.v}
Type.{u}
Type.{tst.foo.U}
t3.lean:26:0: error: invalid declaration name 'tst.foo', identifier must be atomic
t3.lean:27:1: error: invalid declaration name 'full.name.U', identifier must be atomic
Type.{tst.v}
Type.{tst.foo.U}
t3.lean:35:2: error: universe level alias 'u' shadows existing global universe level
t3.lean:37:16: error: unknown universe 'bla.u'