refactor(frontends/lean/info_manager): store environment+options in the info_manager, fixes #96
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
parent
9bea23111f
commit
dd99e60a00
9 changed files with 195 additions and 58 deletions
|
@ -569,7 +569,8 @@ public:
|
|||
if (!infom())
|
||||
return;
|
||||
m_pre_info_data.instantiate(s);
|
||||
infom()->merge(m_pre_info_data);
|
||||
bool overwrite = true;
|
||||
infom()->merge(m_pre_info_data, overwrite);
|
||||
m_pre_info_data.clear();
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,9 @@ Author: Leonardo de Moura
|
|||
*/
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include "util/thread.h"
|
||||
#include "kernel/environment.h"
|
||||
#include "library/choice.h"
|
||||
#include "frontends/lean/info_manager.h"
|
||||
#include "frontends/lean/pp_options.h"
|
||||
|
@ -195,14 +197,36 @@ struct info_data_cmp {
|
|||
|
||||
struct info_manager::imp {
|
||||
typedef rb_tree<info_data, info_data_cmp> info_data_set;
|
||||
/** \brief Saved environment + options for a given line and iteration
|
||||
Whenever "lean server" starts processing a file again, we bump the iteration.
|
||||
*/
|
||||
struct env_info {
|
||||
unsigned m_line;
|
||||
unsigned m_iteration;
|
||||
environment m_env;
|
||||
options m_options;
|
||||
env_info():m_line(0), m_iteration(0) {}
|
||||
env_info(unsigned l, unsigned i, environment const & env, options const & o):
|
||||
m_line(l), m_iteration(i), m_env(env), m_options(o) {}
|
||||
friend bool operator<(env_info const & i1, env_info const & i2) { return i1.m_line < i2.m_line; }
|
||||
};
|
||||
mutex m_mutex;
|
||||
bool m_block_updates;
|
||||
std::vector<info_data_set> m_line_data;
|
||||
std::vector<bool> m_line_valid;
|
||||
unsigned m_available_upto;
|
||||
std::set<env_info> m_env_info;
|
||||
unsigned m_iteration; // current interation
|
||||
unsigned m_processed_upto;
|
||||
|
||||
imp():m_available_upto(0) {}
|
||||
imp():m_block_updates(false), m_iteration(0), m_processed_upto(0) {}
|
||||
|
||||
void block_updates(bool f) {
|
||||
lock_guard<mutex> lc(m_mutex);
|
||||
m_block_updates = f;
|
||||
}
|
||||
|
||||
void synch_line(unsigned l) {
|
||||
lean_assert(!m_block_updates);
|
||||
lean_assert(l > 0);
|
||||
if (l >= m_line_data.size()) {
|
||||
m_line_data.resize(l+1);
|
||||
|
@ -210,38 +234,68 @@ struct info_manager::imp {
|
|||
}
|
||||
}
|
||||
|
||||
void save_environment_options(unsigned l, environment const & env, options const & o) {
|
||||
lock_guard<mutex> lc(m_mutex);
|
||||
if (m_block_updates)
|
||||
return;
|
||||
// erase all entries in m_env_info such that e.m_line <= l and e.m_iteration < m_iteration
|
||||
auto it = m_env_info.begin();
|
||||
auto end = m_env_info.end();
|
||||
while (it != end) {
|
||||
if (it->m_line > l)
|
||||
break;
|
||||
else if (it->m_line <= l && it->m_iteration < m_iteration)
|
||||
it = m_env_info.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
m_env_info.insert(env_info(l, m_iteration, env, o));
|
||||
}
|
||||
|
||||
void add_type_info(unsigned l, unsigned c, expr const & e) {
|
||||
lock_guard<mutex> lc(m_mutex);
|
||||
if (m_block_updates)
|
||||
return;
|
||||
synch_line(l);
|
||||
m_line_data[l].insert(mk_type_info(c, e));
|
||||
}
|
||||
|
||||
void add_synth_info(unsigned l, unsigned c, expr const & e) {
|
||||
lock_guard<mutex> lc(m_mutex);
|
||||
if (m_block_updates)
|
||||
return;
|
||||
synch_line(l);
|
||||
m_line_data[l].insert(mk_synth_info(c, e));
|
||||
}
|
||||
|
||||
void add_overload_info(unsigned l, unsigned c, expr const & e) {
|
||||
lock_guard<mutex> lc(m_mutex);
|
||||
if (m_block_updates)
|
||||
return;
|
||||
synch_line(l);
|
||||
m_line_data[l].insert(mk_overload_info(c, e));
|
||||
}
|
||||
|
||||
void add_coercion_info(unsigned l, unsigned c, expr const & e) {
|
||||
lock_guard<mutex> lc(m_mutex);
|
||||
if (m_block_updates)
|
||||
return;
|
||||
synch_line(l);
|
||||
m_line_data[l].insert(mk_coercion_info(c, e));
|
||||
}
|
||||
|
||||
void add_symbol_info(unsigned l, unsigned c, name const & s) {
|
||||
lock_guard<mutex> lc(m_mutex);
|
||||
if (m_block_updates)
|
||||
return;
|
||||
synch_line(l);
|
||||
m_line_data[l].insert(mk_symbol_info(c, s));
|
||||
}
|
||||
|
||||
void add_identifier_info(unsigned l, unsigned c, name const & full_id) {
|
||||
lock_guard<mutex> lc(m_mutex);
|
||||
if (m_block_updates)
|
||||
return;
|
||||
synch_line(l);
|
||||
m_line_data[l].insert(mk_identifier_info(c, full_id));
|
||||
}
|
||||
|
@ -256,6 +310,8 @@ struct info_manager::imp {
|
|||
|
||||
void instantiate(substitution const & s) {
|
||||
lock_guard<mutex> lc(m_mutex);
|
||||
if (m_block_updates)
|
||||
return;
|
||||
substitution tmp_s = s;
|
||||
unsigned sz = m_line_data.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
|
@ -263,25 +319,30 @@ struct info_manager::imp {
|
|||
}
|
||||
}
|
||||
|
||||
void merge(info_manager::imp const & m) {
|
||||
void merge(info_manager::imp const & m, bool overwrite) {
|
||||
if (m.m_line_data.empty())
|
||||
return;
|
||||
lock_guard<mutex> lc(m_mutex);
|
||||
if (m_block_updates)
|
||||
return;
|
||||
unsigned sz = m.m_line_data.size();
|
||||
for (unsigned i = 1; i < sz; i++) {
|
||||
info_data_set const & s = m.m_line_data[i];
|
||||
s.for_each([&](info_data const & d) {
|
||||
synch_line(i);
|
||||
m_line_data[i].insert(d);
|
||||
if (overwrite || !m_line_data[i].contains(d))
|
||||
m_line_data[i].insert(d);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void insert_line(unsigned l) {
|
||||
lock_guard<mutex> lc(m_mutex);
|
||||
if (m_block_updates)
|
||||
return;
|
||||
synch_line(l);
|
||||
if (m_available_upto > l - 1)
|
||||
m_available_upto = l - 1;
|
||||
if (m_processed_upto > l - 1)
|
||||
m_processed_upto = l - 1;
|
||||
m_line_data.push_back(info_data_set());
|
||||
unsigned i = m_line_data.size();
|
||||
while (i > l) {
|
||||
|
@ -294,6 +355,8 @@ struct info_manager::imp {
|
|||
|
||||
void remove_line(unsigned l) {
|
||||
lock_guard<mutex> lc(m_mutex);
|
||||
if (m_block_updates)
|
||||
return;
|
||||
lean_assert(l > 0);
|
||||
if (l >= m_line_data.size())
|
||||
return;
|
||||
|
@ -303,29 +366,28 @@ struct info_manager::imp {
|
|||
m_line_valid[i] = m_line_valid[i+1];
|
||||
}
|
||||
m_line_data.pop_back();
|
||||
if (m_available_upto > l - 1)
|
||||
m_available_upto = l - 1;
|
||||
if (m_processed_upto > l - 1)
|
||||
m_processed_upto = l - 1;
|
||||
}
|
||||
|
||||
void invalidate_line(unsigned l) {
|
||||
lock_guard<mutex> lc(m_mutex);
|
||||
if (m_block_updates)
|
||||
return;
|
||||
synch_line(l);
|
||||
if (m_available_upto > l - 1)
|
||||
m_available_upto = l - 1;
|
||||
if (m_processed_upto > l - 1)
|
||||
m_processed_upto = l - 1;
|
||||
m_line_data[l] = info_data_set();
|
||||
m_line_valid[l] = false;
|
||||
}
|
||||
|
||||
void commit_upto(unsigned l, bool valid) {
|
||||
lock_guard<mutex> lc(m_mutex);
|
||||
for (unsigned i = m_available_upto; i < l && i < m_line_valid.size(); i++)
|
||||
if (m_block_updates)
|
||||
return;
|
||||
for (unsigned i = m_processed_upto; i < l && i < m_line_valid.size(); i++)
|
||||
m_line_valid[i] = valid;
|
||||
m_available_upto = l;
|
||||
}
|
||||
|
||||
bool is_available(unsigned l) {
|
||||
lock_guard<mutex> lc(m_mutex);
|
||||
return l < m_available_upto;
|
||||
m_processed_upto = l;
|
||||
}
|
||||
|
||||
bool is_invalidated(unsigned l) {
|
||||
|
@ -335,18 +397,60 @@ struct info_manager::imp {
|
|||
return !m_line_valid[l];
|
||||
}
|
||||
|
||||
void display(io_state_stream const & ios, unsigned l) {
|
||||
lock_guard<mutex> lc(m_mutex);
|
||||
if (l >= m_line_data.size())
|
||||
return;
|
||||
m_line_data[l].for_each([&](info_data const & d) {
|
||||
d.display(ios, l);
|
||||
void display_core(environment const & env, options const & o, io_state const & ios, unsigned line) {
|
||||
io_state_stream out = regular(env, ios).update_options(o);
|
||||
m_line_data[line].for_each([&](info_data const & d) {
|
||||
d.display(out, line);
|
||||
});
|
||||
}
|
||||
|
||||
void display(environment const & env, io_state const & ios, unsigned line) {
|
||||
lock_guard<mutex> lc(m_mutex);
|
||||
if (line >= m_line_data.size() || m_line_data[line].empty()) {
|
||||
regular(env, ios) << "-- NAY\n";
|
||||
return;
|
||||
}
|
||||
if (m_env_info.empty()) {
|
||||
display_core(env, ios.get_options(), ios, line);
|
||||
return;
|
||||
}
|
||||
auto it = m_env_info.begin();
|
||||
auto end = m_env_info.end();
|
||||
lean_assert(it != end);
|
||||
if (it->m_line > line) {
|
||||
display_core(env, ios.get_options(), ios, line);
|
||||
return;
|
||||
}
|
||||
while (true) {
|
||||
lean_assert(it->m_line <= line);
|
||||
lean_assert(it != end);
|
||||
auto next = it;
|
||||
++next;
|
||||
if (next == end || next->m_line > line) {
|
||||
display_core(it->m_env, it->m_options, ios, line);
|
||||
return;
|
||||
}
|
||||
it = next;
|
||||
}
|
||||
}
|
||||
|
||||
optional<pair<environment, options>> get_final_env_opts() {
|
||||
lock_guard<mutex> lc(m_mutex);
|
||||
if (m_env_info.empty()) {
|
||||
return optional<pair<environment, options>>();
|
||||
} else {
|
||||
auto it = m_env_info.end();
|
||||
--it;
|
||||
return optional<pair<environment, options>>(mk_pair(it->m_env, it->m_options));
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
lock_guard<mutex> lc(m_mutex);
|
||||
if (m_block_updates)
|
||||
return;
|
||||
m_line_data.clear();
|
||||
m_line_valid.clear();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -361,13 +465,20 @@ void info_manager::add_identifier_info(unsigned l, unsigned c, name const & full
|
|||
m_ptr->add_identifier_info(l, c, full_id);
|
||||
}
|
||||
void info_manager::instantiate(substitution const & s) { m_ptr->instantiate(s); }
|
||||
void info_manager::merge(info_manager const & m) { m_ptr->merge(*m.m_ptr); }
|
||||
void info_manager::merge(info_manager const & m, bool overwrite) { m_ptr->merge(*m.m_ptr, overwrite); }
|
||||
void info_manager::insert_line(unsigned l) { m_ptr->insert_line(l); }
|
||||
void info_manager::remove_line(unsigned l) { m_ptr->remove_line(l); }
|
||||
void info_manager::invalidate_line(unsigned l) { m_ptr->invalidate_line(l); }
|
||||
void info_manager::commit_upto(unsigned l, bool valid) { m_ptr->commit_upto(l, valid); }
|
||||
bool info_manager::is_available(unsigned l) const { return m_ptr->is_available(l); }
|
||||
bool info_manager::is_invalidated(unsigned l) const { return m_ptr->is_invalidated(l); }
|
||||
void info_manager::clear() { m_ptr->clear(); }
|
||||
void info_manager::display(io_state_stream const & ios, unsigned line) { m_ptr->display(ios, line); }
|
||||
void info_manager::save_environment_options(unsigned l, environment const & env, options const & o) {
|
||||
m_ptr->save_environment_options(l, env, o);
|
||||
}
|
||||
void info_manager::clear() { m_ptr->clear(); }
|
||||
optional<pair<environment, options>> info_manager::get_final_env_opts() const {
|
||||
return m_ptr->get_final_env_opts();
|
||||
}
|
||||
void info_manager::display(environment const & env, io_state const & ios, unsigned line) {
|
||||
m_ptr->display(env, ios, line);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,14 +25,15 @@ public:
|
|||
void add_symbol_info(unsigned l, unsigned c, name const & n);
|
||||
void add_identifier_info(unsigned l, unsigned c, name const & full_id);
|
||||
void instantiate(substitution const & s);
|
||||
void merge(info_manager const & m);
|
||||
void merge(info_manager const & m, bool overwrite);
|
||||
void insert_line(unsigned l);
|
||||
void remove_line(unsigned l);
|
||||
void invalidate_line(unsigned l);
|
||||
void commit_upto(unsigned l, bool valid);
|
||||
bool is_available(unsigned l) const;
|
||||
bool is_invalidated(unsigned l) const;
|
||||
void save_environment_options(unsigned l, environment const & env, options const & o);
|
||||
optional<pair<environment, options>> get_final_env_opts() const;
|
||||
void clear();
|
||||
void display(io_state_stream const & ios, unsigned line);
|
||||
void display(environment const & env, io_state const & ios, unsigned line);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1209,6 +1209,14 @@ void parser::parse_imports() {
|
|||
}
|
||||
}
|
||||
|
||||
void parser::commit_info(unsigned line) {
|
||||
save_snapshot();
|
||||
if (m_info_manager) {
|
||||
m_info_manager->save_environment_options(line, m_env, m_ios.get_options());
|
||||
m_info_manager->commit_upto(line, true);
|
||||
}
|
||||
}
|
||||
|
||||
bool parser::parse_commands() {
|
||||
// We disable hash-consing while parsing to make sure the pos-info are correct.
|
||||
scoped_expr_caching disable(false);
|
||||
|
@ -1230,9 +1238,7 @@ bool parser::parse_commands() {
|
|||
switch (curr()) {
|
||||
case scanner::token_kind::CommandKeyword:
|
||||
parse_command();
|
||||
save_snapshot();
|
||||
if (m_info_manager)
|
||||
m_info_manager->commit_upto(m_scanner.get_line(), true);
|
||||
commit_info(m_scanner.get_line());
|
||||
break;
|
||||
case scanner::token_kind::ScriptBlock:
|
||||
parse_script();
|
||||
|
@ -1260,9 +1266,7 @@ bool parser::parse_commands() {
|
|||
throw_parser_exception("invalid end of module, expecting 'end'", pos());
|
||||
}
|
||||
} catch (interrupt_parser) {}
|
||||
save_snapshot();
|
||||
if (m_info_manager)
|
||||
m_info_manager->commit_upto(m_scanner.get_line()+1, true);
|
||||
commit_info(m_scanner.get_line()+1);
|
||||
for (certified_declaration const & thm : m_theorem_queue.join()) {
|
||||
m_env.replace(thm);
|
||||
}
|
||||
|
@ -1299,7 +1303,8 @@ void parser::save_snapshot() {
|
|||
void parser::save_pre_info_data() {
|
||||
// if elaborator failed, then m_pre_info_data contains type information before elaboration.
|
||||
if (m_info_manager) {
|
||||
m_info_manager->merge(m_pre_info_manager);
|
||||
bool overwrite = false;
|
||||
m_info_manager->merge(m_pre_info_manager, overwrite);
|
||||
m_pre_info_manager.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -170,6 +170,7 @@ class parser {
|
|||
void save_type_info(expr const & e);
|
||||
void save_pre_info_data();
|
||||
void save_identifier_info(pos_info const & p, name const & full_id);
|
||||
void commit_info(unsigned line);
|
||||
|
||||
elaborator_context mk_elaborator_context(pos_info_provider const & pp, bool check_unassigned = true);
|
||||
elaborator_context mk_elaborator_context(environment const & env, pos_info_provider const & pp);
|
||||
|
|
|
@ -256,31 +256,17 @@ void server::set_option(std::string const & line) {
|
|||
|
||||
void server::show_info(unsigned linenum) {
|
||||
check_file();
|
||||
if (!m_file->m_info.is_available(linenum)) {
|
||||
m_out << "-- BEGININFO\n-- NAY\n-- ENDINFO" << std::endl;
|
||||
return;
|
||||
}
|
||||
unsigned i = m_file->find(linenum);
|
||||
environment const & env = i == 0 ? m_env : m_file->m_snapshots[i-1].m_env;
|
||||
options const & o = i == 0 ? m_ios.get_options() : m_file->m_snapshots[i-1].m_options;
|
||||
scoped_updt_options updt(m_ios, o);
|
||||
io_state_stream out(env, m_ios);
|
||||
out << "-- BEGININFO" << endl;
|
||||
m_file->m_info.display(out, linenum);
|
||||
out << "-- ENDINFO" << endl;
|
||||
m_out << "-- BEGININFO" << std::endl;
|
||||
m_file->m_info.display(m_env, m_ios, linenum);
|
||||
m_out << "-- ENDINFO" << std::endl;
|
||||
}
|
||||
|
||||
void server::eval(std::string const & line) {
|
||||
if (!m_file->m_info.is_available(m_file->m_lines.size())) {
|
||||
m_out << "-- BEGINEVAL\n-- NAY\n-- ENDEVAL" << std::endl;
|
||||
return;
|
||||
}
|
||||
snapshot & s = !m_file || m_file->m_snapshots.empty() ? m_empty_snapshot : m_file->m_snapshots.back();
|
||||
void server::eval_core(environment const & env, options const & o, std::string const & line) {
|
||||
std::istringstream strm(line);
|
||||
scoped_updt_options updt(m_ios, s.m_options);
|
||||
scoped_updt_options updt(m_ios, o);
|
||||
m_out << "-- BEGINEVAL" << std::endl;
|
||||
try {
|
||||
parser p(s.m_env, m_ios, strm, "EVAL_command", true, 1, s.m_lds, s.m_eds, 1);
|
||||
parser p(env, m_ios, strm, "EVAL_command", true);
|
||||
p();
|
||||
} catch (exception & ex) {
|
||||
m_out << ex.what() << std::endl;
|
||||
|
@ -288,6 +274,16 @@ void server::eval(std::string const & line) {
|
|||
m_out << "-- ENDEVAL" << std::endl;
|
||||
}
|
||||
|
||||
void server::eval(std::string const & line) {
|
||||
if (!m_file) {
|
||||
eval_core(m_env, m_ios.get_options(), line);
|
||||
} else if (auto p = m_file->m_info.get_final_env_opts()) {
|
||||
eval_core(p->first, p->second, line);
|
||||
} else {
|
||||
eval_core(m_env, m_ios.get_options(), line);
|
||||
}
|
||||
}
|
||||
|
||||
bool server::operator()(std::istream & in) {
|
||||
for (std::string line; std::getline(in, line);) {
|
||||
try {
|
||||
|
|
|
@ -53,6 +53,7 @@ class server {
|
|||
void show_info(unsigned linenum);
|
||||
void process_from(unsigned linenum);
|
||||
void set_option(std::string const & line);
|
||||
void eval_core(environment const & env, options const & o, std::string const & line);
|
||||
void eval(std::string const & line);
|
||||
unsigned find(unsigned linenum);
|
||||
void read_line(std::istream & in, std::string & line);
|
||||
|
|
10
tests/lean/interactive/in2.input
Normal file
10
tests/lean/interactive/in2.input
Normal file
|
@ -0,0 +1,10 @@
|
|||
EVAL
|
||||
check Type.{1}
|
||||
SET
|
||||
pp.universes true
|
||||
EVAL
|
||||
check Type.{1}
|
||||
VISIT simple.lean
|
||||
WAIT
|
||||
EVAL
|
||||
check tst.foo
|
11
tests/lean/interactive/in2.input.expected.out
Normal file
11
tests/lean/interactive/in2.input.expected.out
Normal file
|
@ -0,0 +1,11 @@
|
|||
-- BEGINEVAL
|
||||
Type : Type
|
||||
-- ENDEVAL
|
||||
-- BEGINSET
|
||||
-- ENDSET
|
||||
-- BEGINEVAL
|
||||
Type.{1} : Type.{2}
|
||||
-- ENDEVAL
|
||||
-- BEGINEVAL
|
||||
tst.foo.{l_1 l_2} : ?M_1 → ?M_2 → ?M_1
|
||||
-- ENDEVAL
|
Loading…
Reference in a new issue