lean4-htt/src/library/scoped_ext.h
2019-05-27 21:28:22 -07:00

206 lines
7.6 KiB
C++

/*
Copyright (c) 2014 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#pragma once
#include <vector>
#include <string>
#include "util/list.h"
#include "util/rb_map.h"
#include "util/name.h"
#include "kernel/environment.h"
#include "library/io_state.h"
#include "library/module.h"
namespace lean {
enum class scope_kind { Namespace, Section };
enum class persistence { scope, file, global };
typedef environment (*push_scope_fn)(environment const &, io_state const &, scope_kind);
typedef environment (*pop_scope_fn)(environment const &, io_state const &, scope_kind);
void register_scoped_ext(push_scope_fn push, pop_scope_fn pop);
/** \brief Create a new scope, all scoped extensions are notified. */
environment push_scope(environment const & env, io_state const & ios, scope_kind k, name const & n = name());
/** \brief Delete the most recent scope, all scoped extensions are notified.
\remark method throws an exception if there are no open scopes, or \c n does not match the name of the open scope
*/
environment pop_scope(environment const & env, io_state const & ios, name const & n = name());
/** \brief Similar to \c pop_scope, but it always succeed.
It always pops the current open scope, and does nothing if there are no open scopes.
*/
environment pop_scope_core(environment const & env, io_state const & ios);
/** \brief Return true iff there are open scopes */
bool has_open_scopes(environment const & env);
/** \brief Add a new namespace (if it does not exist) */
environment add_namespace(environment const & env, name const & ns);
name const & get_namespace(environment const & env);
environment set_namespace(environment const & env, name const & ns);
name const & get_scope_header(environment const & env);
/** \brief Return the current stack of namespaces.
Example: at
namespace foo
namespace bla
namespace boo
- It returns [foo.bla.boo, foo.bla, foo]
\remark This is *not* the set of opened namespaces. */
names const & get_namespaces(environment const & env);
bool in_section(environment const & env);
/** \brief Check if \c n may be a reference to a namespace, if it is return it.
The procedure checks if \c n is a registered namespace, if it is not, it tries
to prefix \c n with each prefix in the current scope. Example: suppose the scope is:
namespace foo
namespace bla
namespace boo
...
Then, the procedure tries n, 'foo.bla.boo'+n, 'foo.bla'+n, 'foo'+n. */
optional<name> to_valid_namespace_name(environment const & env, name const & n);
/** \brief Auxilary template used to simplify the creation of environment extensions that support
the scope */
template<typename Config>
class scoped_ext : public environment_extension {
typedef typename Config::state state;
typedef typename Config::entry entry;
static void add_entry(environment const & env, io_state const & ios, state & s, entry const & e) {
Config::add_entry(env, ios, s, e);
}
static void write_entry(serializer & s, entry const & e) { Config::write_entry(s, e); }
static entry read_entry(deserializer & d) { return Config::read_entry(d); }
static const char * get_serialization_key() { return Config::get_serialization_key(); }
/* Stack of states, it is updated using push/pop operations */
list<state> m_scopes;
state m_state; // explicit top-most (current) scope
/* Add \c e to all states in \c l. */
static list<state> add_all(environment const & env, io_state const & ios, list<state> const & l, entry const & e) {
if (is_nil(l)) {
return l;
} else {
state new_s = head(l);
add_entry(env, ios, new_s, e);
return cons(new_s, add_all(env, ios, tail(l), e));
}
}
/* Add persistent entry, updating all states with this entry. This method is invoked when importing files. */
scoped_ext _register_entry(environment const & env, io_state const & ios, entry const & e) const {
scoped_ext r(*this);
add_entry(env, ios, r.m_state, e);
r.m_scopes = add_all(env, ios, r.m_scopes, e);
return r;
}
/* Add entry to current state */
scoped_ext _add_tmp_entry(environment const & env, io_state const & ios, entry const & e) const {
scoped_ext r(*this);
add_entry(env, ios, r.m_state, e);
return r;
}
public:
/** \brief Open a namespace/section. It returns the new updated state. */
scoped_ext push() const {
scoped_ext r(*this);
r.m_scopes = cons(m_state, r.m_scopes);
return r;
}
/** \brief Close namespace/section. It returns the new updated state.
\pre There are open namespaces */
scoped_ext pop() const {
lean_assert(!is_nil(m_scopes));
scoped_ext r(*this);
r.m_state = head(m_scopes);
r.m_scopes = tail(m_scopes);
return r;
}
struct modification : public lean::modification {
LEAN_MODIFICATION(get_serialization_key())
entry m_entry;
modification() {}
modification(entry const & e) : m_entry(e) {}
void perform(environment & env) const override {
env = register_entry(env, get_global_ios(), m_entry);
}
void serialize(serializer & s) const override {
write_entry(s, m_entry);
}
static modification* deserialize(deserializer & d) {
return new modification(read_entry(d));
}
};
struct reg {
unsigned m_ext_id;
reg() {
register_scoped_ext(push_fn, pop_fn);
modification::init();
m_ext_id = environment::register_extension(new scoped_ext());
}
~reg() {
modification::finalize();
}
};
static reg * g_ext;
static void initialize() { g_ext = new reg(); }
static void finalize() { delete g_ext; }
static scoped_ext const & get(environment const & env) {
return static_cast<scoped_ext const &>(env.get_extension(g_ext->m_ext_id));
}
static environment update(environment const & env, scoped_ext const & ext) {
return env.update(g_ext->m_ext_id, new scoped_ext(ext));
}
static environment push_fn(environment const & env, io_state const &, scope_kind) {
return update(env, get(env).push());
}
static environment pop_fn(environment const & env, io_state const &, scope_kind) {
return update(env, get(env).pop());
}
static environment register_entry(environment const & env, io_state const & ios, entry const & e) {
return update(env, get(env)._register_entry(env, ios, e));
}
static environment add_entry(environment env, io_state const & ios, entry const & e, persistence persist) {
if (persist == persistence::scope) {
return update(env, get(env)._add_tmp_entry(env, ios, e));
} else {
if (persist == persistence::global) {
env = module::add(env, new modification(e));
}
return update(env, get(env)._register_entry(env, ios, e));
}
}
static environment add_entry(environment const & env, io_state const & ios, entry const & e, bool persistent) {
return add_entry(env, ios, e, persistent ? persistence::global : persistence::scope);
}
static state const & get_state(environment const & env) {
return get(env).m_state;
}
};
template<typename Config>
typename scoped_ext<Config>::reg * scoped_ext<Config>::g_ext = nullptr;
void initialize_scoped_ext();
void finalize_scoped_ext();
}