Add Ctrl-C support for interrupting Lean shell.

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2013-08-24 16:11:35 -07:00
parent f0edf2b4a3
commit dc91a7adb8
13 changed files with 173 additions and 27 deletions

View file

@ -163,6 +163,11 @@ struct frontend::imp {
void add_mixfixr(unsigned sz, name const * opns, unsigned p, expr const & d) { add_op(mixfixr(sz, opns, p), d, true); } void add_mixfixr(unsigned sz, name const * opns, unsigned p, expr const & d) { add_op(mixfixr(sz, opns, p), d, true); }
void add_mixfixc(unsigned sz, name const * opns, unsigned p, expr const & d) { add_op(mixfixc(sz, opns, p), d, false); } void add_mixfixc(unsigned sz, name const * opns, unsigned p, expr const & d) { add_op(mixfixc(sz, opns, p), d, false); }
void set_interrupt(bool flag) {
m_env.set_interrupt(flag);
m_state.set_interrupt(flag);
}
imp(frontend & fe): imp(frontend & fe):
m_num_children(0) { m_num_children(0) {
} }
@ -235,4 +240,6 @@ state & frontend::get_state_core() { return m_imp->m_state; }
void frontend::set_options(options const & opts) { return m_imp->m_state.set_options(opts); } void frontend::set_options(options const & opts) { return m_imp->m_state.set_options(opts); }
void frontend::set_regular_channel(std::shared_ptr<output_channel> const & out) { return m_imp->m_state.set_regular_channel(out); } void frontend::set_regular_channel(std::shared_ptr<output_channel> const & out) { return m_imp->m_state.set_regular_channel(out); }
void frontend::set_diagnostic_channel(std::shared_ptr<output_channel> const & out) { return m_imp->m_state.set_diagnostic_channel(out); } void frontend::set_diagnostic_channel(std::shared_ptr<output_channel> const & out) { return m_imp->m_state.set_diagnostic_channel(out); }
void frontend::set_interrupt(bool flag) { m_imp->set_interrupt(flag); }
} }

View file

@ -127,5 +127,13 @@ public:
void set_regular_channel(std::shared_ptr<output_channel> const & out); void set_regular_channel(std::shared_ptr<output_channel> const & out);
void set_diagnostic_channel(std::shared_ptr<output_channel> const & out); void set_diagnostic_channel(std::shared_ptr<output_channel> const & out);
/*@}*/ /*@}*/
/**
@name Interrupts.
*/
void set_interrupt(bool flag);
void interrupt() { set_interrupt(true); }
void reset_interrupt() { set_interrupt(false); }
/*@}*/
}; };
} }

View file

@ -1132,6 +1132,7 @@ class parser_fn {
/** \brief Parse a Lean command. */ /** \brief Parse a Lean command. */
void parse_command() { void parse_command() {
m_frontend.reset_interrupt();
m_expr_pos_info.clear(); m_expr_pos_info.clear();
m_last_cmd_pos = pos(); m_last_cmd_pos = pos();
name const & cmd_id = curr_name(); name const & cmd_id = curr_name();
@ -1234,6 +1235,13 @@ public:
} else { } else {
display_error(ex); display_error(ex);
} }
} catch (interrupted & ex) {
if (m_use_exceptions) {
throw;
} else {
regular(m_frontend) << "\n!!!Interrupted!!!" << endl;
sync();
}
} catch (exception & ex) { } catch (exception & ex) {
m_found_errors = true; m_found_errors = true;
if (m_use_exceptions) { if (m_use_exceptions) {

View file

@ -15,6 +15,8 @@ Author: Leonardo de Moura
#include "free_vars.h" #include "free_vars.h"
#include "context_to_lambda.h" #include "context_to_lambda.h"
#include "options.h" #include "options.h"
#include "interruptable_ptr.h"
#include "exception.h"
#include "lean_notation.h" #include "lean_notation.h"
#include "lean_pp.h" #include "lean_pp.h"
#include "lean_frontend.h" #include "lean_frontend.h"
@ -126,6 +128,8 @@ class pp_fn {
bool m_notation; //!< if true use notation bool m_notation; //!< if true use notation
bool m_extra_lets; //!< introduce extra let-expression to cope with sharing. bool m_extra_lets; //!< introduce extra let-expression to cope with sharing.
unsigned m_alias_min_weight; //!< minimal weight for creating an alias unsigned m_alias_min_weight; //!< minimal weight for creating an alias
volatile bool m_interrupted;
// Create a scope for local definitions // Create a scope for local definitions
struct mk_scope { struct mk_scope {
@ -700,6 +704,8 @@ class pp_fn {
} }
result pp(expr const & e, unsigned depth, bool main = false) { result pp(expr const & e, unsigned depth, bool main = false) {
if (m_interrupted)
throw interrupted();
if (!is_atomic(e) && (m_num_steps > m_max_steps || depth > m_max_depth)) { if (!is_atomic(e) && (m_num_steps > m_max_steps || depth > m_max_depth)) {
return pp_ellipsis(); return pp_ellipsis();
} else { } else {
@ -789,6 +795,7 @@ public:
pp_fn(frontend const & fe, options const & opts): pp_fn(frontend const & fe, options const & opts):
m_frontend(fe) { m_frontend(fe) {
set_options(opts); set_options(opts);
m_interrupted = false;
} }
format operator()(expr const & e) { format operator()(expr const & e) {
@ -801,13 +808,20 @@ public:
expr T(t); expr T(t);
return pp_abstraction_core(v, 0, T).first; return pp_abstraction_core(v, 0, T).first;
} }
void set_interrupt(bool flag) {
m_interrupted = flag;
}
}; };
class pp_formatter_cell : public formatter_cell { class pp_formatter_cell : public formatter_cell {
frontend const & m_frontend; frontend const & m_frontend;
interruptable_ptr<pp_fn> m_pp_fn;
format pp(expr const & e, options const & opts) { format pp(expr const & e, options const & opts) {
return pp_fn(m_frontend, opts)(e); pp_fn fn(m_frontend, opts);
scoped_set_interruptable_ptr<pp_fn> set(m_pp_fn, &fn);
return fn(e);
} }
format pp(context const & c, expr const & e, bool include_e, options const & opts) { format pp(context const & c, expr const & e, bool include_e, options const & opts) {
@ -859,9 +873,10 @@ class pp_formatter_cell : public formatter_cell {
if (!is_lambda(v) || is_pi(it1)) { if (!is_lambda(v) || is_pi(it1)) {
return pp_definition(kwd, n, t, v, opts); return pp_definition(kwd, n, t, v, opts);
} else { } else {
lean_assert(is_lambda(v)); lean_assert(is_lambda(v));
format def = pp_fn(m_frontend, opts).pp_definition(v, t); pp_fn fn(m_frontend, opts);
scoped_set_interruptable_ptr<pp_fn> set(m_pp_fn, &fn);
format def = fn.pp_definition(v, t);
return format{highlight_command(format(kwd)), space(), format(n), def}; return format{highlight_command(format(kwd)), space(), format(n), def};
} }
} }
@ -942,6 +957,10 @@ public:
}); });
return r; return r;
} }
virtual void set_interrupt(bool flag) {
m_pp_fn.set_interrupt(flag);
}
}; };
formatter mk_pp_formatter(frontend const & fe) { formatter mk_pp_formatter(frontend const & fe) {

View file

@ -30,6 +30,7 @@ struct environment::imp {
// Object management // Object management
std::vector<object> m_objects; std::vector<object> m_objects;
object_dictionary m_object_dictionary; object_dictionary m_object_dictionary;
type_checker m_type_checker;
/** /**
\brief Return true iff this environment has children. \brief Return true iff this environment has children.
@ -221,9 +222,9 @@ struct environment::imp {
infer_universe and infer_type expect an environment instead of environment::imp. infer_universe and infer_type expect an environment instead of environment::imp.
*/ */
void check_type(name const & n, expr const & t, expr const & v, environment const & env) { void check_type(name const & n, expr const & t, expr const & v, environment const & env) {
infer_universe(t, env); m_type_checker.infer_universe(t);
expr v_t = infer_type(v, env); expr v_t = m_type_checker.infer_type(v);
if (!is_convertible(t, v_t, env)) if (!m_type_checker.is_convertible(t, v_t))
throw def_type_mismatch_exception(env, n, t, v, v_t); throw def_type_mismatch_exception(env, n, t, v, v_t);
} }
@ -245,7 +246,7 @@ struct environment::imp {
*/ */
void add_definition(name const & n, expr const & v, bool opaque, environment const & env) { void add_definition(name const & n, expr const & v, bool opaque, environment const & env) {
check_name(n, env); check_name(n, env);
expr v_t = infer_type(v, env); expr v_t = m_type_checker.infer_type(v);
register_named_object(mk_definition(n, v_t, v, opaque)); register_named_object(mk_definition(n, v_t, v, opaque));
} }
@ -300,14 +301,20 @@ struct environment::imp {
} }
} }
imp(): void set_interrupt(bool flag) {
m_num_children(0) { m_type_checker.set_interrupt(flag);
}
imp(environment const & env):
m_num_children(0),
m_type_checker(env) {
init_uvars(); init_uvars();
} }
explicit imp(std::shared_ptr<imp> const & parent): imp(std::shared_ptr<imp> const & parent, environment const & env):
m_num_children(0), m_num_children(0),
m_parent(parent) { m_parent(parent),
m_type_checker(env) {
m_parent->inc_children(); m_parent->inc_children();
} }
@ -318,13 +325,15 @@ struct environment::imp {
}; };
environment::environment(): environment::environment():
m_imp(new imp()) { m_imp(new imp(*this)) {
} }
// used when creating a new child environment
environment::environment(imp * new_ptr): environment::environment(imp * new_ptr):
m_imp(new_ptr) { m_imp(new_ptr) {
} }
// used when creating a reference to the parent environment
environment::environment(std::shared_ptr<imp> const & ptr): environment::environment(std::shared_ptr<imp> const & ptr):
m_imp(ptr) { m_imp(ptr) {
} }
@ -333,7 +342,7 @@ environment::~environment() {
} }
environment environment::mk_child() const { environment environment::mk_child() const {
return environment(new imp(m_imp)); return environment(new imp(m_imp, *this));
} }
bool environment::has_children() const { bool environment::has_children() const {
@ -404,4 +413,8 @@ object const & environment::get_object(unsigned i, bool local) const {
void environment::display(std::ostream & out) const { void environment::display(std::ostream & out) const {
m_imp->display(out, *this); m_imp->display(out, *this);
} }
void environment::set_interrupt(bool flag) {
m_imp->set_interrupt(flag);
}
} }

View file

@ -174,5 +174,9 @@ public:
/** \brief Display universal variable constraints and objects stored in this environment and its parents. */ /** \brief Display universal variable constraints and objects stored in this environment and its parents. */
void display(std::ostream & out) const; void display(std::ostream & out) const;
void set_interrupt(bool flag);
void interrupt() { set_interrupt(true); }
void reset_interrupt() { set_interrupt(false); }
}; };
} }

View file

@ -66,12 +66,12 @@ value_stack extend(value_stack const & s, svalue const & v) { return cons(v, s);
class normalizer::imp { class normalizer::imp {
typedef scoped_map<expr, svalue, expr_hash, expr_eqp> cache; typedef scoped_map<expr, svalue, expr_hash, expr_eqp> cache;
environment m_env; environment const & m_env;
context m_ctx; context m_ctx;
cache m_cache; cache m_cache;
unsigned m_max_depth; unsigned m_max_depth;
unsigned m_depth; unsigned m_depth;
volatile bool m_interrupted; volatile bool m_interrupted;
/** /**
\brief Auxiliary object for saving the current context. \brief Auxiliary object for saving the current context.

View file

@ -18,10 +18,10 @@ namespace lean {
class type_checker::imp { class type_checker::imp {
typedef scoped_map<expr, expr, expr_hash, expr_eqp> cache; typedef scoped_map<expr, expr, expr_hash, expr_eqp> cache;
environment m_env; environment const & m_env;
cache m_cache; cache m_cache;
normalizer m_normalizer; normalizer m_normalizer;
volatile bool m_interrupted; volatile bool m_interrupted;
expr lookup(context const & c, unsigned i) { expr lookup(context const & c, unsigned i) {
auto p = lookup_ext(c, i); auto p = lookup_ext(c, i);
@ -151,8 +151,12 @@ public:
return r; return r;
} }
bool is_convertible(expr const & t1, expr const & t2, context const & ctx) {
return m_normalizer.is_convertible(t1, t2, ctx);
}
void set_interrupt(bool flag) { void set_interrupt(bool flag) {
m_interrupted = true; m_interrupted = flag;
m_normalizer.set_interrupt(flag); m_normalizer.set_interrupt(flag);
} }
@ -168,6 +172,9 @@ expr type_checker::infer_type(expr const & e, context const & ctx) { return m_pt
level type_checker::infer_universe(expr const & e, context const & ctx) { return m_ptr->infer_universe(e, ctx); } level type_checker::infer_universe(expr const & e, context const & ctx) { return m_ptr->infer_universe(e, ctx); }
void type_checker::clear() { m_ptr->clear(); } void type_checker::clear() { m_ptr->clear(); }
void type_checker::set_interrupt(bool flag) { m_ptr->set_interrupt(flag); } void type_checker::set_interrupt(bool flag) { m_ptr->set_interrupt(flag); }
bool type_checker::is_convertible(expr const & t1, expr const & t2, context const & ctx) {
return m_ptr->is_convertible(t1, t2, ctx);
}
expr infer_type(expr const & e, environment const & env, context const & ctx) { expr infer_type(expr const & e, environment const & env, context const & ctx) {
return type_checker(env).infer_type(e, ctx); return type_checker(env).infer_type(e, ctx);

View file

@ -21,6 +21,7 @@ public:
expr infer_type(expr const & e, context const & ctx = context()); expr infer_type(expr const & e, context const & ctx = context());
level infer_universe(expr const & e, context const & ctx = context()); level infer_universe(expr const & e, context const & ctx = context());
void check(expr const & e, context const & ctx = context()) { infer_type(e, ctx); } void check(expr const & e, context const & ctx = context()) { infer_type(e, ctx); }
bool is_convertible(expr const & t1, expr const & t2, context const & ctx = context());
void clear(); void clear();

View file

@ -34,6 +34,8 @@ public:
virtual format operator()(object const & obj, options const & opts) = 0; virtual format operator()(object const & obj, options const & opts) = 0;
/** \brief Format the given environment */ /** \brief Format the given environment */
virtual format operator()(environment const & env, options const & opts) = 0; virtual format operator()(environment const & env, options const & opts) = 0;
/** \brief Request interruption */
virtual void set_interrupt(bool flag) {}
}; };
class kernel_exception; class kernel_exception;
class formatter { class formatter {
@ -48,6 +50,7 @@ public:
format operator()(environment const & env, options const & opts = options()) { return (*m_cell)(env, opts); } format operator()(environment const & env, options const & opts = options()) { return (*m_cell)(env, opts); }
/** \brief Pretty print a kernel exception using the this formatter */ /** \brief Pretty print a kernel exception using the this formatter */
format operator()(kernel_exception const & ex, options const & opts = options()); format operator()(kernel_exception const & ex, options const & opts = options());
void set_interrupt(bool flag) { m_cell->set_interrupt(flag); }
}; };
formatter mk_simple_formatter(); formatter mk_simple_formatter();

View file

@ -39,6 +39,7 @@ public:
template<typename T> void set_option(name const & n, T const & v) { template<typename T> void set_option(name const & n, T const & v) {
set_options(get_options().update(n, v)); set_options(get_options().update(n, v));
} }
void set_interrupt(bool flag) { m_formatter.set_interrupt(flag); }
}; };
struct regular { struct regular {

View file

@ -1,18 +1,33 @@
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include <signal.h>
#include "version.h" #include "version.h"
#include "printer.h" #include "printer.h"
#include "interruptable_ptr.h"
#include "lean_parser.h" #include "lean_parser.h"
using namespace lean; using namespace lean;
int main(int argc, char ** argv) { static interruptable_ptr<frontend> g_lean_frontend;
static void on_ctrl_c(int) {
g_lean_frontend.set_interrupt(true);
}
bool lean_shell() {
std::cout << "Lean (version " << LEAN_VERSION_MAJOR << "." << LEAN_VERSION_MINOR << ")\n"; std::cout << "Lean (version " << LEAN_VERSION_MAJOR << "." << LEAN_VERSION_MINOR << ")\n";
std::cout << "Type Ctrl-D to exit or 'Help.' for help."<< std::endl; std::cout << "Type Ctrl-D to exit or 'Help.' for help."<< std::endl;
frontend f; frontend f;
scoped_set_interruptable_ptr<frontend> set(g_lean_frontend, &f);
signal(SIGINT, on_ctrl_c);
return parse_commands_from_stdin(f);
}
int main(int argc, char ** argv) {
if (argc == 1) { if (argc == 1) {
return parse_commands_from_stdin(f) ? 0 : 1; return lean_shell() ? 0 : 1;
} else { } else {
bool ok = true; bool ok = true;
frontend f;
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
std::ifstream in(argv[i]); std::ifstream in(argv[i]);
if (!parse_commands(f, in)) if (!parse_commands(f, in))

View file

@ -0,0 +1,60 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#pragma once
#include <mutex>
namespace lean {
/**
\brief Auxiliary class that wraps a pointer to a type that has a
set_interrupt method. The set_interrupt can be invoked from a different
execution thread. To prevent race conditions, we use mutex whenever we
reset the pointer, or invoke the set_interrupt method.
The common usage scenario is:
1) Thread A creates creates a new object Obj and invokes set(Obj)
2) Thread B invokes set_interrupt, which invokes Obj->set_interrupt
3) Thread A invokes set(nullptr) before deleting Obj.
We use the mutex to avoid a race condition in 2&3.
*/
template<typename T>
class interruptable_ptr {
T * m_ptr;
std::mutex m_mutex;
public:
interruptable_ptr():m_ptr(nullptr) {}
T * set(T * ptr) {
std::lock_guard<std::mutex> lock(m_mutex);
T * old = m_ptr;
m_ptr = ptr;
return old;
}
void set_interrupt(bool flag) {
std::lock_guard<std::mutex> lock(m_mutex);
if (m_ptr)
m_ptr->set_interrupt(flag);
}
};
/**
\brief Auxiliary class for setting/resetting a interruptable_ptr.
*/
template<typename T>
struct scoped_set_interruptable_ptr {
interruptable_ptr<T> & m_ptr;
T * m_old_p;
public:
scoped_set_interruptable_ptr(interruptable_ptr<T> & ptr, T * p):m_ptr(ptr) {
m_old_p = m_ptr.set(p);
}
~scoped_set_interruptable_ptr() {
m_ptr.set(m_old_p);
}
};
}