refactor(*): error messages

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2014-01-13 16:54:21 -08:00
parent 55aa4cbfa3
commit ccb9faf065
39 changed files with 572 additions and 274 deletions

View file

@ -304,7 +304,7 @@ print(o2)
-- An error is raised if the option is not known.
local ok, ex = pcall(function() options({"pp", "foo"}, true) end)
assert(not ok)
assert(ex:what() == "unknown option 'pp::foo'")
assert(ex:what():find("unknown option 'pp::foo'"))
```
Options objects are non-mutable values. The method `update` returns a new

View file

@ -223,6 +223,8 @@ add_subdirectory(library/elaborator)
set(LEAN_LIBS ${LEAN_LIBS} elaborator)
add_subdirectory(library/tactic)
set(LEAN_LIBS ${LEAN_LIBS} tactic)
add_subdirectory(library/error_handling)
set(LEAN_LIBS ${LEAN_LIBS} error_handling)
add_subdirectory(frontends/lean)
set(LEAN_LIBS ${LEAN_LIBS} lean_frontend)
add_subdirectory(frontends/lua)

View file

@ -14,6 +14,7 @@ namespace lean {
parser::parser(environment const & env, io_state const & ios, std::istream & in, char const * strm_name, script_state * S, bool use_exceptions, bool interactive) {
parser_imp::show_prompt(interactive, ios);
m_ptr.reset(new parser_imp(env, ios, in, strm_name, S, use_exceptions, interactive));
m_ptr->m_this = m_ptr;
}
parser::~parser() {

View file

@ -16,7 +16,7 @@ class parser_imp;
/** \brief Functional object for parsing commands and expressions */
class parser {
private:
std::unique_ptr<parser_imp> m_ptr;
std::shared_ptr<parser_imp> m_ptr;
public:
parser(environment const & env, io_state const & st, std::istream & in, char const * strm_name, script_state * S, bool use_exceptions = true, bool interactive = false);
~parser();

View file

@ -83,7 +83,7 @@ void parser_imp::register_implicit_arguments(name const & n, parameter_buffer &
/** \brief Throw an exception if \c e contains a metavariable */
void parser_imp::check_no_metavar(expr const & e, metavar_env const & menv, char const * msg) {
void parser_imp::check_no_metavar(expr const & e, metavar_env const &, char const * msg) {
if (has_metavar(e))
throw unsolved_metavar_exception(msg, e);
}

View file

@ -5,10 +5,10 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include <utility>
#include "kernel/kernel_exception.h"
#include "kernel/for_each_fn.h"
#include "library/io_state_stream.h"
#include "library/elaborator/elaborator_justification.h"
#include "library/parser_nested_exception.h"
#include "library/error_handling/error_handling.h"
#include "frontends/lean/parser_imp.h"
namespace lean {
@ -21,94 +21,21 @@ void parser_imp::display_error_pos(unsigned line, unsigned pos) {
void parser_imp::display_error_pos(pos_info const & p) { display_error_pos(p.first, p.second); }
void parser_imp::display_error_pos(optional<expr> const & e) {
if (e) {
auto it = m_expr_pos_info.find(*e);
if (it == m_expr_pos_info.end())
return display_error_pos(m_last_cmd_pos);
else
return display_error_pos(it->second);
} else {
return display_error_pos(m_last_cmd_pos);
}
}
void parser_imp::display_error(char const * msg, unsigned line, unsigned pos) {
display_error_pos(line, pos);
regular(m_io_state) << " " << msg << endl;
}
void parser_imp::display_error(char const * msg) {
display_error(msg, m_scanner.get_line(), m_scanner.get_pos());
}
void parser_imp::display_error(kernel_exception const & ex) {
optional<expr> main_expr = ex.get_main_expr();
if (main_expr)
display_error_pos(some_expr(m_elaborator.get_original(*main_expr)));
else
display_error_pos(main_expr);
regular(m_io_state) << " " << ex << endl;
}
void parser_imp::display_error(unsolved_metavar_exception const & ex) {
display_error_pos(some_expr(m_elaborator.get_original(ex.get_expr())));
formatter fmt = m_io_state.get_formatter();
options opts = m_io_state.get_options();
unsigned indent = get_pp_indent(opts);
format r = nest(indent, compose(line(), fmt(ex.get_expr(), opts)));
regular(m_io_state) << " " << ex.what() << mk_pair(r, opts) << endl;
name_set already_displayed;
for_each(ex.get_expr(), [&](expr const & e, unsigned) -> bool {
if (is_metavar(e)) {
name const & m = metavar_name(e);
if (already_displayed.find(m) == already_displayed.end()) {
already_displayed.insert(m);
for (unsigned i = 0; i < indent; i++) regular(m_io_state) << " ";
display_error_pos(some_expr(m_elaborator.get_original(e)));
regular(m_io_state) << " unsolved metavar " << m << endl;
}
}
return true;
});
}
std::pair<unsigned, unsigned> parser_imp::lean_pos_info_provider::get_pos_info(expr const & e) const {
expr const & o = m_ref.m_elaborator.get_original(e);
auto it = m_ref.m_expr_pos_info.find(o);
if (it == m_ref.m_expr_pos_info.end())
throw exception("position is not available"); // information is not available
expr const & o = m_parser->m_elaborator.get_original(e);
auto it = m_parser->m_expr_pos_info.find(o);
if (it == m_parser->m_expr_pos_info.end())
return m_pos;
return it->second;
}
char const * parser_imp::lean_pos_info_provider::get_file_name(expr const & ) const {
return m_ref.m_strm_name.c_str();
}
void parser_imp::display_error(elaborator_exception const & ex) {
formatter fmt = m_io_state.get_formatter();
options opts = m_io_state.get_options();
lean_pos_info_provider pos_provider(*this);
auto j = ex.get_justification();
j = remove_detail(j);
regular(m_io_state) << mk_pair(j.pp(fmt, opts, &pos_provider, true), opts) << endl;
}
void parser_imp::display_error(script_exception const & ex) {
switch (ex.get_source()) {
case script_exception::source::String:
display_error_pos(ex.get_line() + m_last_script_pos.first - 1, static_cast<unsigned>(-1));
regular(m_io_state) << " executing script," << ex.get_msg() << endl;
break;
case script_exception::source::File:
display_error_pos(m_last_script_pos);
regular(m_io_state) << " executing external script (" << ex.get_filename() << ":" << ex.get_line() << ")," << ex.get_msg() << endl;
break;
case script_exception::source::Unknown:
display_error_pos(m_last_script_pos);
regular(m_io_state) << " executing script, exact error position is not available, " << ex.what() << endl;
break;
}
char const * parser_imp::lean_pos_info_provider::get_file_name() const {
return m_parser->m_strm_name.c_str();
}
void parser_imp::display_error(tactic_cmd_error const & ex) {
@ -116,13 +43,21 @@ void parser_imp::display_error(tactic_cmd_error const & ex) {
display_proof_state(ex.m_state);
}
#define CATCH_CORE(ShowError, ThrowError) \
m_found_errors = true; \
if (m_show_errors) { ShowError ; } \
sync(); \
if (m_use_exceptions) { ThrowError ; }
void parser_imp::display_error(exception const & ex) {
lean_pos_info_provider pos_provider(m_this.lock(), m_last_cmd_pos);
::lean::display_error(m_io_state, &pos_provider, ex);
}
#define CATCH(ShowError) CATCH_CORE(ShowError, throw;)
void parser_imp::display_error(script_exception const & ex) {
lean_pos_info_provider pos_provider(m_this.lock(), m_last_script_pos);
::lean::display_error(m_io_state, &pos_provider, ex);
}
#define CATCH(ShowError, ThrowError) \
m_found_errors = true; \
if (!m_use_exceptions && m_show_errors) { ShowError ; } \
sync(); \
if (m_use_exceptions) { ThrowError ; }
/**
\brief Execute the given function \c f, and handle exceptions occurring
@ -133,21 +68,14 @@ void parser_imp::protected_call(std::function<void()> && f, std::function<void()
try {
f();
} catch (tactic_cmd_error & ex) {
CATCH(display_error(ex));
CATCH(display_error(ex),
throw parser_exception(ex.what(), m_strm_name.c_str(), ex.m_pos.first, ex.m_pos.second));
} catch (parser_exception & ex) {
CATCH(regular(m_io_state) << ex.what() << endl;);
CATCH(regular(m_io_state) << ex.what() << endl,
throw);
} catch (parser_error & ex) {
CATCH_CORE(display_error(ex.what(), ex.m_pos.first, ex.m_pos.second),
throw parser_exception(ex.what(), m_strm_name.c_str(), ex.m_pos.first, ex.m_pos.second));
} catch (kernel_exception & ex) {
CATCH(display_error(ex));
} catch (elaborator_exception & ex) {
CATCH(display_error(ex));
} catch (unsolved_metavar_exception & ex) {
CATCH(display_error(ex));
} catch (script_exception & ex) {
reset_interrupt();
CATCH(display_error(ex));
CATCH(display_error(ex.what(), ex.m_pos.first, ex.m_pos.second),
throw parser_exception(ex.what(), m_strm_name.c_str(), ex.m_pos.first, ex.m_pos.second));
} catch (interrupted & ex) {
reset_interrupt();
if (m_verbose)
@ -155,8 +83,16 @@ void parser_imp::protected_call(std::function<void()> && f, std::function<void()
sync();
if (m_use_exceptions)
throw;
} catch (script_exception & ex) {
reset_interrupt();
CATCH(display_error(ex),
throw parser_nested_exception(std::shared_ptr<exception>(ex.clone()),
std::shared_ptr<pos_info_provider>(new lean_pos_info_provider(m_this.lock(), m_last_script_pos))));
} catch (exception & ex) {
CATCH(display_error(ex.what()););
reset_interrupt();
CATCH(display_error(ex),
throw parser_nested_exception(std::shared_ptr<exception>(ex.clone()),
std::shared_ptr<pos_info_provider>(new lean_pos_info_provider(m_this.lock(), m_last_cmd_pos))));
}
}
}

View file

@ -8,6 +8,7 @@ Author: Leonardo de Moura
#include <string>
#include <vector>
#include "library/io_state_stream.h"
#include "library/parser_nested_exception.h"
#include "frontends/lean/parser_imp.h"
#include "frontends/lean/parser_macros.h"
#include "frontends/lean/parser_calc.h"
@ -217,6 +218,9 @@ expr parser_imp::parse_expr_main() {
return p.first;
} catch (parser_error & ex) {
throw parser_exception(ex.what(), m_strm_name.c_str(), ex.m_pos.first, ex.m_pos.second);
} catch (exception & ex) {
throw parser_nested_exception(std::shared_ptr<exception>(ex.clone()),
std::shared_ptr<pos_info_provider>(new lean_pos_info_provider(m_this.lock(), m_last_cmd_pos)));
}
}
};

View file

@ -34,7 +34,7 @@ bool get_parser_show_errors(options const & opts);
/** \brief Auxiliary object that stores a reference to the parser object inside the Lua State */
struct set_parser {
script_state * m_state;
script_state::weak_ref m_state;
parser_imp * m_prev;
set_parser(script_state * S, parser_imp * ptr);
~set_parser();
@ -57,29 +57,30 @@ class parser_imp {
typedef expr_map<pos_info> expr_pos_info;
typedef expr_map<tactic> tactic_hints; // a mapping from placeholder to tactic
typedef scoped_map<name, expr, name_hash, name_eq> using_decls;
environment m_env;
io_state m_io_state;
scanner m_scanner;
std::string m_strm_name; // input stream name
frontend_elaborator m_elaborator;
macros const * m_expr_macros;
macros const * m_cmd_macros;
macros const * m_tactic_macros;
scanner::token m_curr;
bool m_use_exceptions;
bool m_interactive;
bool m_found_errors;
local_decls m_local_decls;
unsigned m_num_local_decls;
expr_pos_info m_expr_pos_info;
pos_info m_last_cmd_pos;
pos_info m_last_script_pos;
tactic_hints m_tactic_hints;
using_decls m_using_decls;
std::vector<name> m_namespace_prefixes;
enum class scope_kind { Scope, Namespace };
std::vector<scope_kind> m_scope_kinds;
std::weak_ptr<parser_imp> m_this;
environment m_env;
io_state m_io_state;
scanner m_scanner;
std::string m_strm_name; // input stream name
frontend_elaborator m_elaborator;
macros const * m_expr_macros;
macros const * m_cmd_macros;
macros const * m_tactic_macros;
scanner::token m_curr;
bool m_use_exceptions;
bool m_interactive;
bool m_found_errors;
local_decls m_local_decls;
unsigned m_num_local_decls;
expr_pos_info m_expr_pos_info;
pos_info m_last_cmd_pos;
pos_info m_last_script_pos;
tactic_hints m_tactic_hints;
using_decls m_using_decls;
std::vector<name> m_namespace_prefixes;
std::vector<scope_kind> m_scope_kinds;
std::unique_ptr<calc_proof_parser> m_calc_proof_parser;
@ -202,22 +203,20 @@ public:
private:
void display_error_pos(unsigned line, unsigned pos);
void display_error_pos(pos_info const & p);
void display_error_pos(optional<expr> const & e);
void display_error(char const * msg, unsigned line, unsigned pos);
void display_error(char const * msg);
void display_error(kernel_exception const & ex);
void display_error(unsolved_metavar_exception const & ex);
struct lean_pos_info_provider : public pos_info_provider {
parser_imp const & m_ref;
lean_pos_info_provider(parser_imp const & r):m_ref(r) {}
std::shared_ptr<parser_imp> m_parser;
pos_info m_pos;
lean_pos_info_provider(std::shared_ptr<parser_imp> const & p, pos_info const & pos):m_parser(p), m_pos(pos) {}
virtual std::pair<unsigned, unsigned> get_pos_info(expr const & e) const;
virtual char const * get_file_name(expr const & e) const;
virtual std::pair<unsigned, unsigned> get_some_pos() const { return m_pos; }
virtual char const * get_file_name() const;
};
void display_error(elaborator_exception const & ex);
void display_error(script_exception const & ex);
void display_error(tactic_cmd_error const & ex);
void display_error(script_exception const & ex);
void display_error(exception const & ex);
public:
void protected_call(std::function<void()> && f, std::function<void()> && sync);
/*@}*/

View file

@ -24,9 +24,9 @@ parser_imp * get_parser(lua_State * L) {
}
set_parser::set_parser(script_state * S, parser_imp * ptr) {
m_state = S;
if (m_state) {
m_state->apply([&](lua_State * L) {
if (S) {
m_state = S->to_weak_ref();
S->apply([&](lua_State * L) {
m_prev = get_parser(L);
lua_pushlightuserdata(L, static_cast<void *>(&g_set_parser_key));
lua_pushlightuserdata(L, ptr);
@ -35,8 +35,9 @@ set_parser::set_parser(script_state * S, parser_imp * ptr) {
}
}
set_parser::~set_parser() {
if (m_state) {
m_state->apply([&](lua_State * L) {
if (!m_state.expired()) {
script_state S(m_state);
S.apply([&](lua_State * L) {
lua_pushlightuserdata(L, static_cast<void *>(&g_set_parser_key));
lua_pushlightuserdata(L, m_prev);
lua_settable(L, LUA_REGISTRYINDEX);

View file

@ -101,9 +101,9 @@ bool shell::operator()() {
#ifdef LEAN_USE_READLINE
readline_streambuf buf;
std::istream is(&buf);
parser p(m_env, m_io_state, is, "stdin", m_script_state, false, true);
parser p(m_env, m_io_state, is, "[stdin]", m_script_state, false, true);
#else
parser p(m_env, m_io_state, std::cin, "stdin", m_script_state, false, true);
parser p(m_env, m_io_state, std::cin, "[stdin]", m_script_state, false, true);
#endif
return p();
}

View file

@ -7,13 +7,13 @@ Author: Leonardo de Moura
#include "kernel/pos_info_provider.h"
namespace lean {
char const * pos_info_provider::get_file_name(expr const & ) const {
char const * pos_info_provider::get_file_name() const {
return "unknown";
}
format pos_info_provider::pp(expr const & e) const {
try {
auto p = get_pos_info(e);
return format{format(get_file_name(e)), colon(), format(p.first), colon(), format(p.second), colon()};
return format{format(get_file_name()), colon(), format(p.first), colon(), format(p.second), colon()};
} catch (exception &) {
return format();
}

View file

@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#pragma once
#include <utility>
#include "util/sexpr/format.h"
#include "kernel/expr.h"
@ -20,7 +21,9 @@ public:
Throws an exception if the given expression does not have this kind of information associated with it.
*/
virtual std::pair<unsigned, unsigned> get_pos_info(expr const & e) const = 0;
virtual char const * get_file_name(expr const & e) const;
virtual char const * get_file_name() const;
virtual std::pair<unsigned, unsigned> get_some_pos() const = 0;
unsigned get_line(expr const & e) const { return get_pos_info(e).first; }
unsigned get_pos(expr const & e) const { return get_pos_info(e).second; }
/**

View file

@ -0,0 +1,2 @@
add_library(error_handling error_handling.cpp)
target_link_libraries(error_handling ${LEAN_LIBS})

View file

@ -0,0 +1,183 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include <utility>
#include <string>
#include "util/script_exception.h"
#include "util/name_set.h"
#include "kernel/kernel_exception.h"
#include "kernel/for_each_fn.h"
#include "library/io_state_stream.h"
#include "library/elaborator/elaborator_justification.h"
#include "library/elaborator/elaborator_exception.h"
#include "library/parser_nested_exception.h"
#include "library/unsolved_metavar_exception.h"
namespace lean {
void display_error_pos(io_state const & ios, char const * strm_name, unsigned line, unsigned pos) {
regular(ios) << strm_name << ":" << line << ":";
if (pos != static_cast<unsigned>(-1))
regular(ios) << pos << ":";
regular(ios) << " error:";
}
void display_error_pos(io_state const & ios, pos_info_provider const * p, expr const & e) {
if (p) {
auto pos = p->get_pos_info(e);
display_error_pos(ios, p->get_file_name(), pos.first, pos.second);
} else {
regular(ios) << "error:";
}
}
void display_error_pos(io_state const & ios, pos_info_provider const * p, optional<expr> const & e) {
if (e) {
display_error_pos(ios, p, *e);
} else if (p) {
auto pos = p->get_some_pos();
display_error_pos(ios, p->get_file_name(), pos.first, pos.second);
} else {
regular(ios) << "error:";
}
}
void display_error(io_state const & ios, pos_info_provider const * p, exception const & ex);
static void display_error(io_state const & ios, pos_info_provider const * p, kernel_exception const & ex) {
display_error_pos(ios, p, ex.get_main_expr());
regular(ios) << " " << ex << endl;
}
static void display_error(io_state const & ios, pos_info_provider const * p, elaborator_exception const & ex) {
formatter fmt = ios.get_formatter();
options opts = ios.get_options();
auto j = ex.get_justification();
j = remove_detail(j);
regular(ios) << mk_pair(j.pp(fmt, opts, p, true), opts) << endl;
}
struct delta_pos_info_provider : public pos_info_provider {
unsigned m_delta;
std::string m_file_name;
pos_info_provider const & m_provider;
delta_pos_info_provider(unsigned d, char const * fname, pos_info_provider const & p):m_delta(d), m_file_name(fname), m_provider(p) {}
virtual std::pair<unsigned, unsigned> get_pos_info(expr const & e) const {
auto r = m_provider.get_pos_info(e);
return mk_pair(r.first + m_delta, r.second);
}
virtual char const * get_file_name() const { return m_file_name.c_str(); }
virtual std::pair<unsigned, unsigned> get_some_pos() const {
auto r = m_provider.get_some_pos();
return mk_pair(r.first + m_delta, r.second);
}
};
static void display_error(io_state const & ios, pos_info_provider const * p, script_exception const & ex) {
if (p) {
char const * msg = ex.get_msg();
char const * space = msg && *msg == ' ' ? "" : " ";
switch (ex.get_source()) {
case script_exception::source::String:
display_error_pos(ios, p->get_file_name(), ex.get_line() + p->get_some_pos().first - 1, static_cast<unsigned>(-1));
regular(ios) << " executing script," << space << msg << endl;
break;
case script_exception::source::File:
display_error_pos(ios, p->get_file_name(), p->get_some_pos().first, p->get_some_pos().second);
regular(ios) << " executing external script (" << ex.get_file_name() << ":" << ex.get_line() << ")," << space << msg << endl;
break;
case script_exception::source::Unknown:
display_error_pos(ios, p->get_file_name(), p->get_some_pos().first, p->get_some_pos().second);
regular(ios) << " executing script, exact error position is not available, " << ex.what() << endl;
break;
}
} else {
regular(ios) << ex.what() << endl;
}
}
static void display_error(io_state const & ios, pos_info_provider const * p, script_nested_exception const & ex) {
switch (ex.get_source()) {
case script_exception::source::String:
if (p) {
display_error_pos(ios, p->get_file_name(), ex.get_line() + p->get_some_pos().first - 1, static_cast<unsigned>(-1));
regular(ios) << " executing script" << endl;
}
display_error(ios, nullptr, ex.get_exception());
break;
case script_exception::source::File:
if (p) {
display_error_pos(ios, p->get_file_name(), p->get_some_pos().first, p->get_some_pos().second);
regular(ios) << " executing external script (" << ex.get_file_name() << ":" << ex.get_line() << ")" << endl;
} else {
display_error_pos(ios, ex.get_file_name(), ex.get_line(), -1);
regular(ios) << " executing script" << endl;
}
display_error(ios, nullptr, ex.get_exception());
break;
case script_exception::source::Unknown:
display_error(ios, nullptr, ex.get_exception());
break;
}
}
static void display_error(io_state const & ios, pos_info_provider const *, parser_nested_exception const & ex) {
display_error(ios, &(ex.get_provider()), ex.get_exception());
}
static void display_error(io_state const & ios, pos_info_provider const *, parser_exception const & ex) {
regular(ios) << ex.what() << endl;
}
static void display_error(io_state const & ios, pos_info_provider const * p, unsolved_metavar_exception const & ex) {
display_error_pos(ios, p, ex.get_expr());
formatter fmt = ios.get_formatter();
options opts = ios.get_options();
unsigned indent = get_pp_indent(opts);
format r = nest(indent, compose(line(), fmt(ex.get_expr(), opts)));
regular(ios) << " " << ex.what() << mk_pair(r, opts) << endl;
if (p) {
name_set already_displayed;
for_each(ex.get_expr(), [&](expr const & e, unsigned) -> bool {
if (is_metavar(e)) {
name const & m = metavar_name(e);
if (already_displayed.find(m) == already_displayed.end()) {
already_displayed.insert(m);
for (unsigned i = 0; i < indent; i++) regular(ios) << " ";
display_error_pos(ios, p, e);
regular(ios) << " unsolved metavar " << m << endl;
}
}
return true;
});
}
}
void display_error(io_state const & ios, pos_info_provider const * p, exception const & ex) {
if (auto k_ex = dynamic_cast<kernel_exception const *>(&ex)) {
display_error(ios, p, *k_ex);
} else if (auto e_ex = dynamic_cast<elaborator_exception const *>(&ex)) {
display_error(ios, p, *e_ex);
} else if (auto m_ex = dynamic_cast<unsolved_metavar_exception const *>(&ex)) {
display_error(ios, p, *m_ex);
} else if (auto ls_ex = dynamic_cast<script_nested_exception const *>(&ex)) {
display_error(ios, p, *ls_ex);
} else if (auto s_ex = dynamic_cast<script_exception const *>(&ex)) {
display_error(ios, p, *s_ex);
} else if (auto n_ex = dynamic_cast<parser_nested_exception const *>(&ex)) {
display_error(ios, p, *n_ex);
} else if (auto n_ex = dynamic_cast<parser_exception const *>(&ex)) {
display_error(ios, p, *n_ex);
} else if (p) {
display_error_pos(ios, p->get_file_name(), p->get_some_pos().first, p->get_some_pos().second);
regular(ios) << " " << ex.what() << endl;
} else {
regular(ios) << "error: " << ex.what() << endl;
}
}
}

View file

@ -0,0 +1,18 @@
/*
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 "util/exception.h"
#include "kernel/pos_info_provider.h"
#include "kernel/io_state.h"
namespace lean {
/**
\brief Display exception in the regular stream of \c ios, using the configuration options and formatter from \c ios.
Exceptions that contain expressions use the given \c pos_info_provider (if available) to retrieve line number information.
*/
void display_error(io_state const & ios, pos_info_provider const * p, exception const & ex);
}

View file

@ -1205,11 +1205,15 @@ static const struct luaL_Reg environment_m[] = {
static char g_set_environment_key;
void set_global_environment(lua_State * L, environment const & env) {
lua_pushlightuserdata(L, static_cast<void *>(&g_set_environment_key));
push_environment(L, env);
lua_settable(L, LUA_REGISTRYINDEX);
}
set_environment::set_environment(lua_State * L, environment const & env) {
m_state = L;
lua_pushlightuserdata(m_state, static_cast<void *>(&g_set_environment_key));
push_environment(m_state, env);
lua_settable(m_state, LUA_REGISTRYINDEX);
set_global_environment(L, env);
}
set_environment::~set_environment() {
@ -1832,6 +1836,13 @@ void open_io_state(lua_State * L) {
static char g_set_state_key;
void set_global_io_state(lua_State * L, io_state & ios) {
lua_pushlightuserdata(L, static_cast<void *>(&g_set_state_key));
lua_pushlightuserdata(L, &ios);
lua_settable(L, LUA_REGISTRYINDEX);
set_global_options(L, ios.get_options());
}
set_io_state::set_io_state(lua_State * L, io_state & st) {
m_state = L;
m_prev = get_io_state(L);

View file

@ -44,8 +44,11 @@ formatter get_global_formatter(lua_State * L);
Otherwise, we update the registry of \c L.
*/
void set_global_formatter(lua_State * L, formatter const & fmt);
/** \brief Set the Lua registry of a Lua state with an environment object. */
void set_global_environment(lua_State * L, environment const & env);
/**
\brief Auxiliary class for setting the Lua registry of a Lua state
\brief Auxiliary class for temporarily setting the Lua registry of a Lua state
with an environment object.
*/
class set_environment {
@ -74,6 +77,9 @@ public:
rw_shared_environment(lua_State * L, int idx);
rw_shared_environment(lua_State * L);
};
/** \brief Set the Lua registry of a Lua state with an io_state object. */
void set_global_io_state(lua_State * L, io_state & ios);
/**
\brief Auxiliary class for temporarily setting the Lua registry of a Lua state
with a Lean io_state object.

View file

@ -0,0 +1,25 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#include <memory>
#include "util/exception.h"
#include "kernel/pos_info_provider.h"
namespace lean {
/** \brief Lean exception occurred when parsing file. */
class parser_nested_exception : public exception {
std::shared_ptr<exception> m_exception;
std::shared_ptr<pos_info_provider> m_provider;
public:
parser_nested_exception(std::shared_ptr<exception> const & ex, std::shared_ptr<pos_info_provider> const & pr):exception("parser exception"), m_exception(ex), m_provider(pr) {}
virtual ~parser_nested_exception() {}
virtual exception * clone() const { return new parser_nested_exception(m_exception, m_provider); }
virtual void rethrow() const { throw *this; }
virtual char const * what() const noexcept { return m_exception->what(); }
exception const & get_exception() const { return *(m_exception.get()); }
pos_info_provider const & get_provider() const { return *(m_provider.get()); }
};
}

View file

@ -23,6 +23,7 @@ Author: Leonardo de Moura
#include "library/printer.h"
#include "library/kernel_bindings.h"
#include "library/io_state_stream.h"
#include "library/error_handling/error_handling.h"
#include "frontends/lean/parser.h"
#include "frontends/lean/shell.h"
#include "frontends/lean/frontend.h"
@ -108,67 +109,77 @@ static struct option g_long_options[] = {
};
int main(int argc, char ** argv) {
try {
lean::save_stack_info();
lean::register_modules();
bool no_kernel = false;
bool export_objects = false;
bool trust_imported = false;
bool quiet = false;
std::string output;
input_kind default_k = input_kind::Lean; // default
while (true) {
int c = getopt_long(argc, argv, "qtnlupgvhc:012s:012o:", g_long_options, NULL);
if (c == -1)
break; // end of command line
switch (c) {
case 'v':
display_header(std::cout);
return 0;
case 'g':
std::cout << g_githash << "\n";
return 0;
case 'h':
display_help(std::cout);
return 0;
case 'l':
default_k = input_kind::Lean;
break;
case 'u':
default_k = input_kind::Lua;
break;
case 'b':
default_k = input_kind::OLean;
break;
case 'c':
script_state::set_check_interrupt_freq(atoi(optarg));
break;
case 'p':
std::cout << lean::get_lean_path() << "\n";
return 0;
case 's':
lean::set_thread_stack_size(atoi(optarg)*1024);
break;
case 'n':
no_kernel = true;
break;
case 'o':
output = optarg;
export_objects = true;
break;
case 't':
trust_imported = true;
lean::set_default_trust_imported_for_lua(true);
break;
case 'q':
quiet = true;
break;
default:
std::cerr << "Unknown command line option\n";
display_help(std::cerr);
return 1;
}
lean::save_stack_info();
lean::register_modules();
bool no_kernel = false;
bool export_objects = false;
bool trust_imported = false;
bool quiet = false;
std::string output;
input_kind default_k = input_kind::Lean; // default
while (true) {
int c = getopt_long(argc, argv, "qtnlupgvhc:012s:012o:", g_long_options, NULL);
if (c == -1)
break; // end of command line
switch (c) {
case 'v':
display_header(std::cout);
return 0;
case 'g':
std::cout << g_githash << "\n";
return 0;
case 'h':
display_help(std::cout);
return 0;
case 'l':
default_k = input_kind::Lean;
break;
case 'u':
default_k = input_kind::Lua;
break;
case 'b':
default_k = input_kind::OLean;
break;
case 'c':
script_state::set_check_interrupt_freq(atoi(optarg));
break;
case 'p':
std::cout << lean::get_lean_path() << "\n";
return 0;
case 's':
lean::set_thread_stack_size(atoi(optarg)*1024);
break;
case 'n':
no_kernel = true;
break;
case 'o':
output = optarg;
export_objects = true;
break;
case 't':
trust_imported = true;
lean::set_default_trust_imported_for_lua(true);
break;
case 'q':
quiet = true;
break;
default:
std::cerr << "Unknown command line option\n";
display_help(std::cerr);
return 1;
}
}
environment env;
env->set_trusted_imported(trust_imported);
io_state ios = init_frontend(env, no_kernel);
if (quiet)
ios.set_option("verbose", false);
script_state S;
S.apply([&](lua_State * L) {
set_global_environment(L, env);
set_global_io_state(L, ios);
});
try {
if (optind >= argc) {
display_header(std::cout);
signal(SIGINT, on_ctrl_c);
@ -178,12 +189,6 @@ int main(int argc, char ** argv) {
#else
std::cout << "Type Ctrl-D or 'Exit.' to exit or 'Help.' for help."<< std::endl;
#endif
environment env;
env->set_trusted_imported(trust_imported);
io_state ios = init_frontend(env, no_kernel);
if (quiet)
ios.set_option("verbose", false);
script_state S;
shell sh(env, &S);
int status = sh() ? 0 : 1;
if (export_objects)
@ -191,17 +196,10 @@ int main(int argc, char ** argv) {
return status;
} else {
lean_assert(default_k == input_kind::Lua);
script_state S;
S.import("repl");
return 0;
}
} else {
environment env;
env->set_trusted_imported(trust_imported);
io_state ios = init_frontend(env, no_kernel);
if (quiet)
ios.set_option("verbose", false);
script_state S;
bool ok = true;
for (int i = optind; i < argc; i++) {
char const * ext = get_file_extension(argv[i]);
@ -227,15 +225,9 @@ int main(int argc, char ** argv) {
}
} else if (k == input_kind::Lua) {
try {
S.apply([&](lua_State * L) {
lean::set_io_state set1(L, ios);
lean::set_environment set2(L, env);
S.exec_unprotected([&]() {
S.dofile(argv[i]);
});
});
S.dofile(argv[i]);
} catch (lean::exception & ex) {
std::cerr << ex.what() << std::endl;
::lean::display_error(ios, nullptr, ex);
ok = false;
}
} else {
@ -246,12 +238,8 @@ int main(int argc, char ** argv) {
env->export_objects(output);
return ok ? 0 : 1;
}
} catch (lean::kernel_exception & ex) {
std::cerr << "Error: " << ex.pp(lean::mk_simple_formatter(), lean::options()) << "\n";
} catch (lean::parser_exception & ex) {
std::cerr << ex.what() << "\n";
} catch (lean::exception & ex) {
std::cerr << "Error: " << ex.what() << "\n";
::lean::display_error(ios, nullptr, ex);
}
return 1;
}

View file

@ -8,6 +8,7 @@ Author: Leonardo de Moura
#include "util/lua.h"
#include <exception>
#include <string>
#include <memory>
namespace lean {
class sstream;
@ -39,9 +40,12 @@ public:
virtual char const * what() const noexcept;
unsigned get_line() const { return m_line; }
unsigned get_pos() const { return m_pos; }
std::string const & get_file_name() const { return m_fname; }
virtual exception * clone() const { return new parser_exception(m_msg, m_fname.c_str(), m_line, m_pos); }
virtual void rethrow() const { throw *this; }
parser_exception update_line(unsigned line_delta) const { return parser_exception(m_msg, m_fname.c_str(), m_line + line_delta, m_pos); }
};
/** \brief Exception used to sign that a computation was interrupted */
class interrupted : public exception {
public:

View file

@ -91,10 +91,11 @@ static void exec(lua_State * L) {
void check_result(lua_State * L, int result) {
if (result) {
if (is_exception(L, -1))
if (is_exception(L, -1)) {
to_exception(L, -1).rethrow();
else
} else {
throw script_exception(lua_tostring(L, -1));
}
}
}
@ -137,10 +138,16 @@ bool resume(lua_State * L, int nargs) {
int safe_function_wrapper(lua_State * L, lua_CFunction f) {
try {
return f(L);
} catch (script_exception & e) {
lua_pushstring(L, e.what());
} catch (exception & e) {
push_exception(L, e);
lua_Debug ar;
lua_getstack(L, 1, &ar);
lua_getinfo(L, "Sl", &ar);
if (ar.source && *(ar.source) == '@')
push_exception(L, script_nested_exception(true, ar.source+1, ar.currentline, std::shared_ptr<exception>(e.clone())));
else if (ar.source)
push_exception(L, script_nested_exception(false, ar.source, ar.currentline, std::shared_ptr<exception>(e.clone())));
else
push_exception(L, e);
} catch (std::bad_alloc &) {
lua_pushstring(L, "out of memory");
} catch (std::exception & e) {

View file

@ -55,7 +55,7 @@ script_exception::script_exception(char const * lua_error) {
script_exception::~script_exception() {
}
char const * script_exception::get_filename() const {
char const * script_exception::get_file_name() const {
lean_assert(get_source() == source::File);
return m_file.c_str();
}
@ -72,12 +72,30 @@ char const * script_exception::get_msg() const noexcept {
char const * script_exception::what() const noexcept {
static thread_local std::string buffer;
std::ostringstream strm;
char const * msg = get_msg();
char const * space = msg && *msg == ' ' ? "" : " ";
switch (get_source()) {
case source::String: strm << "[string]:" << get_line() << ":" << get_msg() << "\n"; break;
case source::File: strm << get_filename() << ":" << get_line() << ":" << get_msg() << "\n"; break;
case source::String: strm << "[string]:" << get_line() << ":" << space << get_msg(); break;
case source::File: strm << get_file_name() << ":" << get_line() << ":" << space << get_msg(); break;
case source::Unknown: return get_msg();
}
buffer = strm.str();
return buffer.c_str();
}
script_nested_exception::script_nested_exception(source s, std::string f, unsigned l, std::shared_ptr<exception> const & ex):
script_exception(s, f, l, "Lean exception"),
m_exception(ex) {
lean_assert(ex);
}
script_nested_exception::~script_nested_exception() {}
char const * script_nested_exception::what() const noexcept {
static thread_local std::string buffer;
std::ostringstream strm;
strm << script_exception::what() << "\n" << get_exception().what();
buffer = strm.str();
return buffer.c_str();
}
}

View file

@ -6,6 +6,7 @@ Author: Leonardo de Moura
*/
#pragma once
#include <string>
#include <memory>
#include "util/exception.h"
namespace lean {
@ -15,21 +16,36 @@ namespace lean {
class script_exception : public exception {
public:
enum class source { String, File, Unknown };
private:
protected:
source m_source;
std::string m_file; // if source == File, then this field contains the filename that triggered the error.
unsigned m_line; // line number
script_exception(source s, std::string f, unsigned l):m_source(s), m_file(f), m_line(l) {}
script_exception(source s, std::string f, unsigned l, std::string const & msg):exception(msg), m_source(s), m_file(f), m_line(l) {}
public:
script_exception(char const * lua_error);
virtual ~script_exception();
virtual char const * what() const noexcept;
virtual source get_source() const { return m_source; }
virtual char const * get_filename() const;
virtual char const * get_file_name() const;
virtual unsigned get_line() const;
/** \brief Return error message without position information */
virtual char const * get_msg() const noexcept;
virtual exception * clone() const { return new script_exception(m_source, m_file, m_line); }
virtual exception * clone() const { return new script_exception(m_source, m_file, m_line, m_msg); }
virtual void rethrow() const { throw *this; }
};
/**
\brief Lean exception occurred inside of a script
*/
class script_nested_exception : public script_exception {
std::shared_ptr<exception> m_exception;
script_nested_exception(source s, std::string f, unsigned l, std::shared_ptr<exception> const & ex);
public:
script_nested_exception(bool file, std::string f, unsigned l, std::shared_ptr<exception> const & ex):script_nested_exception(file ? source::File : source::String, f, l, ex) {}
virtual ~script_nested_exception();
virtual char const * what() const noexcept;
virtual exception * clone() const { return new script_nested_exception(m_source, m_file, m_line, m_exception); }
virtual void rethrow() const { throw *this; }
exception const & get_exception() const { return *(m_exception.get()); }
};
}

View file

@ -1,3 +1,3 @@
Set: pp::colors
Set: pp::unicode
alias3.lean:2:13: error: alias 'A' was already defined
alias3.lean:2:0: error: alias 'A' was already defined

View file

@ -7,12 +7,12 @@
variable f : T → R
coercion f
Assumed: g
coercion1.lean:8:0: error: invalid coercion declaration, frontend already has a coercion for the given types
coercion1.lean:7:0: error: invalid coercion declaration, frontend already has a coercion for the given types
Assumed: h
coercion1.lean:10:0: error: invalid coercion declaration, a coercion must have an arrow type (i.e., a non-dependent functional type)
coercion1.lean:9:0: error: invalid coercion declaration, a coercion must have an arrow type (i.e., a non-dependent functional type)
Defined: T2
Defined: R2
Assumed: f2
coercion1.lean:14:0: error: invalid coercion declaration, frontend already has a coercion for the given types
coercion1.lean:13:0: error: invalid coercion declaration, frontend already has a coercion for the given types
Assumed: id
coercion1.lean:16:0: error: invalid coercion declaration, 'from' and 'to' types are the same
coercion1.lean:15:0: error: invalid coercion declaration, 'from' and 'to' types are the same

View file

@ -3,10 +3,14 @@
Assumed: x
env_errors.lean:3:0: error: set_opaque failed, 'x' is not a definition
before import
env_errors.lean:11:0: error: file 'tstblafoo.lean' not found in the LEAN_PATH
env_errors.lean:8: error: executing script
error: file 'tstblafoo.lean' not found in the LEAN_PATH
before load1
env_errors.lean:17:0: error: failed to open file 'tstblafoo.lean'
env_errors.lean:14: error: executing script
error: failed to open file 'tstblafoo.lean'
before load2
env_errors.lean:23:0: error: corrupted binary file
env_errors.lean:20: error: executing script
error: corrupted binary file
before load3
env_errors.lean:28:0: error: file 'fake2.olean' does not seem to be a valid object Lean file
env_errors.lean:26: error: executing script
error: file 'fake2.olean' does not seem to be a valid object Lean file

View file

@ -9,4 +9,4 @@ module::@g : ∀ (A : Type), A → A → A
h::1::explicit : ∀ (A B : Type), A → B → A
Assumed: @h
Assumed: h
explicit.lean:9:37: error: failed to mark implicit arguments for 'h', the frontend already has an object named '@h'
explicit.lean:9:0: error: failed to mark implicit arguments for 'h', the frontend already has an object named '@h'

View file

@ -2,18 +2,18 @@
Set: pp::unicode
Proof state:
a : Bool, b : Bool, H : a, H::1 : b ⊢ a ∧ b
## stdin:5:0: error: invalid 'done' command, proof cannot be produced from this state
## [stdin]:5:0: error: invalid 'done' command, proof cannot be produced from this state
Proof state:
a : Bool, b : Bool, H : a, H::1 : b ⊢ a ∧ b
## stdin:6:0: error: invalid 'done' command, proof cannot be produced from this state
## [stdin]:6:0: error: invalid 'done' command, proof cannot be produced from this state
Proof state:
a : Bool, b : Bool, H : a, H::1 : b ⊢ a ∧ b
## stdin:7:0: error: unknown tactic 'imp_tac2'
## stdin:8:0: error: unknown tactic 'foo'
## [stdin]:7:0: error: unknown tactic 'imp_tac2'
## [stdin]:8:0: error: unknown tactic 'foo'
## Proof state:
a : Bool, b : Bool, H : a, H::1 : b ⊢ a
a : Bool, b : Bool, H : a, H::1 : b ⊢ b
## stdin:10:0: error: failed to backtrack
## [stdin]:10:0: error: failed to backtrack
Proof state:
a : Bool, b : Bool, H : a, H::1 : b ⊢ a
a : Bool, b : Bool, H : a, H::1 : b ⊢ b

View file

@ -6,15 +6,15 @@ A : Bool, B : Bool, H : A ∧ B ⊢ A
no goals
## Proof state:
A : Bool, B : Bool, H : A ∧ B, H1 : A ⊢ B
## stdin:15:3: error: unknown tactic 'simple2_tac'
## stdin:15:16: error: invalid 'done' command, proof cannot be produced from this state
## [stdin]:15:3: error: unknown tactic 'simple2_tac'
## [stdin]:15:16: error: invalid 'done' command, proof cannot be produced from this state
Proof state:
A : Bool, B : Bool, H : A ∧ B, H1 : A ⊢ B
## Proof state:
no goals
## Proof state:
A : Bool, B : Bool, H : A ∧ B, H1 : A, H2 : B ⊢ B ∧ A
## stdin:18:0: error: failed to prove theorem, proof has been aborted
## [stdin]:18:0: error: failed to prove theorem, proof has been aborted
Proof state:
A : Bool, B : Bool, H : A ∧ B, H1 : A, H2 : B ⊢ B ∧ A
# echo command after failure

View file

@ -2,8 +2,8 @@
Set: pp::unicode
Proof state:
a : Bool, b : Bool, H : a, H::1 : b ⊢ a ∧ b
## stdin:5:0: error: unknown tactic 'foo'
## stdin:6:5: error: failed to prove theorem, proof has been aborted
## [stdin]:5:0: error: unknown tactic 'foo'
## [stdin]:6:5: error: failed to prove theorem, proof has been aborted
Proof state:
a : Bool, b : Bool, H : a, H::1 : b ⊢ a ∧ b
# Assumed: a

View file

@ -6,7 +6,7 @@ a : Bool, b : Bool, H : b ⊢ a b
a : Bool, b : Bool, H : b ⊢ a
## Proof state:
a : Bool, b : Bool, H : b ⊢ b
## stdin:9:0: error: failed to backtrack
## [stdin]:9:0: error: failed to backtrack
Proof state:
a : Bool, b : Bool, H : b ⊢ b
## Proof state:

View file

@ -1,5 +1,5 @@
# Set: pp::colors
Set: pp::unicode
stdin:4:8: error: invalid definition, identifier expected
[stdin]:4:8: error: invalid definition, identifier expected
# done
#

View file

@ -4,21 +4,23 @@
Assumed: i
Assumed: j
Assumed: p
[string]:5: Lean exception
elaborator exception
lua15.lean:9: error: executing script
Failed to solve
⊢ (?M::0 ≈ Nat::add) ⊕ (?M::0 ≈ Int::add)
Overloading at
[string]:1:3: Overloading at
(Int::add | Nat::add) i p
Failed to solve
Type of argument 1 must be convertible to the expected type in the application of
[string]:1:3: Type of argument 1 must be convertible to the expected type in the application of
Nat::add
with arguments:
i
p
Failed to solve
⊢ Bool ≺
Type of argument 2 must be convertible to the expected type in the application of
[string]:1:3: Type of argument 2 must be convertible to the expected type in the application of
Int::add
with arguments:
i

13
tests/lean/lua15b.lean Normal file
View file

@ -0,0 +1,13 @@
import Int.
variables i j : Int
variable p : Bool
(*
local env = get_environment()
parse_lean_cmds([[
print "hello"
eval i + j
check i + p
]])
*)

View file

@ -0,0 +1,27 @@
Set: pp::colors
Set: pp::unicode
Imported 'Int'
Assumed: i
Assumed: j
Assumed: p
hello
i + j
lua15b.lean:8: error: executing script
Failed to solve
⊢ (?M::0 ≈ Nat::add) ⊕ (?M::0 ≈ Int::add)
[string]:3:12: Overloading at
(Int::add | Nat::add) i p
Failed to solve
[string]:3:12: Type of argument 1 must be convertible to the expected type in the application of
Nat::add
with arguments:
i
p
Failed to solve
⊢ Bool ≺
[string]:3:12: Type of argument 2 must be convertible to the expected type in the application of
Int::add
with arguments:
i
p

20
tests/lean/parser1.lean Normal file
View file

@ -0,0 +1,20 @@
(*
foo(10)
*)
print "checkpoint"
(*
parse_lean_cmds("(" .. "*" .. [[
foo(20)
]] .. "*" .. ")")
*)

View file

@ -0,0 +1,6 @@
Set: pp::colors
Set: pp::unicode
parser1.lean:6: error: executing script, attempt to call global 'foo' (a nil value)
checkpoint
parser1.lean:14: error: executing script
[string]:2: error: executing script, attempt to call global 'foo' (a nil value)

View file

@ -8,4 +8,4 @@ T:interrupt()
local ok, msg = pcall(function() T:wait() end)
assert(not ok)
assert(is_exception(msg))
assert(msg:what() == "interrupted")
print(msg:what():find("interrupted"))

View file

@ -31,6 +31,8 @@ assert(not pcall(function() S:eval([[ x = ]]) end))
local T2 = thread(S, [[ local x = ...; error("failed") ]], 10)
local ok, msg = pcall(function() T2:wait() end)
assert(not ok)
assert(string.sub(msg, -7) == "failed\n")
assert(is_exception(msg))
print(msg:what())
assert(msg:what():find("failed"))
local T3 = thread(S, [[ local x = ...; return x + 10, x - 10 ]], 10)
T3 = nil