feat(lua): add lua_exception for wrapping lua errors, and improve Lua error messages in the Lean frontend
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
parent
a9b2be0b9c
commit
57b9657bf0
10 changed files with 182 additions and 7 deletions
|
@ -5,12 +5,15 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
|||
Author: Leonardo de Moura
|
||||
*/
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include "util/debug.h"
|
||||
#include "util/exception.h"
|
||||
#include "bindings/lua/leanlua_state.h"
|
||||
|
||||
#ifdef LEAN_USE_LUA
|
||||
#include <lua.hpp>
|
||||
#include <mutex>
|
||||
#include "util/exception.h"
|
||||
#include "bindings/lua/name.h"
|
||||
#include "bindings/lua/numerics.h"
|
||||
#include "bindings/lua/options.h"
|
||||
|
@ -36,14 +39,14 @@ struct leanlua_state::imp {
|
|||
void exec() {
|
||||
int result = lua_pcall(m_state, 0, LUA_MULTRET, 0);
|
||||
if (result)
|
||||
throw exception(lua_tostring(m_state, -1));
|
||||
throw lua_exception(lua_tostring(m_state, -1));
|
||||
}
|
||||
|
||||
void dofile(char const * fname) {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
int result = luaL_loadfile(m_state, fname);
|
||||
if (result)
|
||||
throw exception(lua_tostring(m_state, -1));
|
||||
throw lua_exception(lua_tostring(m_state, -1));
|
||||
exec();
|
||||
}
|
||||
|
||||
|
@ -51,7 +54,7 @@ struct leanlua_state::imp {
|
|||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
int result = luaL_loadstring(m_state, str);
|
||||
if (result)
|
||||
throw exception(lua_tostring(m_state, -1));
|
||||
throw lua_exception(lua_tostring(m_state, -1));
|
||||
exec();
|
||||
}
|
||||
};
|
||||
|
@ -72,6 +75,7 @@ void leanlua_state::dostring(char const * str) {
|
|||
}
|
||||
}
|
||||
#else
|
||||
namespace lean {
|
||||
leanlua_state::leanlua_state() {
|
||||
}
|
||||
|
||||
|
@ -89,4 +93,74 @@ void leanlua_state::dofile(char const * fname) {
|
|||
void leanlua_state::dostring(char const * str) {
|
||||
throw_lua_not_supported();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace lean {
|
||||
lua_exception::lua_exception(char const * lua_error):exception("") {
|
||||
lean_assert(lua_error);
|
||||
std::string fname;
|
||||
std::string line;
|
||||
std::string msg;
|
||||
int state = 0;
|
||||
char const * it = lua_error;
|
||||
while (*it) {
|
||||
if (state == 0) {
|
||||
if (*it == ':') {
|
||||
state = 1;
|
||||
} else {
|
||||
fname += *it;
|
||||
}
|
||||
} else if (state == 1) {
|
||||
if (*it == ':') {
|
||||
state = 2;
|
||||
} else {
|
||||
line += *it;
|
||||
}
|
||||
} else {
|
||||
msg += *it;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
if (state != 2) {
|
||||
// failed to decode Lua error message
|
||||
m_source = source::Unknown;
|
||||
m_msg = lua_error;
|
||||
} else {
|
||||
if (fname == "[string \"...\"]") {
|
||||
m_source = source::String;
|
||||
} else {
|
||||
m_source = source::File;
|
||||
m_file = fname;
|
||||
}
|
||||
m_line = atoi(line.c_str());
|
||||
m_msg = msg;
|
||||
}
|
||||
}
|
||||
|
||||
char const * lua_exception::get_filename() const {
|
||||
lean_assert(get_source() == source::File);
|
||||
return m_file.c_str();
|
||||
}
|
||||
|
||||
unsigned lua_exception::get_line() const {
|
||||
lean_assert(get_source() != source::Unknown);
|
||||
return m_line;
|
||||
}
|
||||
|
||||
char const * lua_exception::msg() const noexcept {
|
||||
return exception::what();
|
||||
}
|
||||
|
||||
char const * lua_exception::what() const noexcept {
|
||||
static thread_local std::string buffer;
|
||||
std::ostringstream strm;
|
||||
switch (m_source) {
|
||||
case source::String: strm << "[string]:" << m_line << ":" << msg() << "\n"; break;
|
||||
case source::File: strm << m_file << ":" << m_file << ":" << msg() << "\n"; break;
|
||||
case source::Unknown: return msg();
|
||||
}
|
||||
buffer = strm.str();
|
||||
return buffer.c_str();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
|||
Author: Leonardo de Moura
|
||||
*/
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "util/exception.h"
|
||||
|
||||
namespace lean {
|
||||
/**
|
||||
|
@ -28,4 +30,24 @@ public:
|
|||
*/
|
||||
void dostring(char const * str);
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Exception for wrapping errors produced by the Lua engine.
|
||||
*/
|
||||
class lua_exception : public exception {
|
||||
public:
|
||||
enum class source { String, File, Unknown };
|
||||
private:
|
||||
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
|
||||
public:
|
||||
lua_exception(char const * lua_error);
|
||||
source get_source() const { return m_source; }
|
||||
char const * get_filename() const;
|
||||
unsigned get_line() const;
|
||||
/** \brief Return error message without position information */
|
||||
char const * msg() const noexcept;
|
||||
virtual char const * what() const noexcept;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -128,6 +128,7 @@ class parser::imp {
|
|||
unsigned m_num_local_decls;
|
||||
expr_pos_info m_expr_pos_info;
|
||||
pos_info m_last_cmd_pos;
|
||||
pos_info m_last_script_pos;
|
||||
// Reference to temporary parser used to process import command.
|
||||
// We need this reference to be able to interrupt it.
|
||||
interruptable_ptr<parser> m_import_parser;
|
||||
|
@ -1504,7 +1505,12 @@ class parser::imp {
|
|||
}
|
||||
/*@}*/
|
||||
|
||||
void display_error_pos(unsigned line, unsigned pos) { regular(m_frontend) << "Error (line: " << line << ", pos: " << pos << ")"; }
|
||||
void display_error_pos(unsigned line, unsigned pos) {
|
||||
regular(m_frontend) << "Error (line: " << line;
|
||||
if (pos != static_cast<unsigned>(-1))
|
||||
regular(m_frontend) << ", pos: " << pos;
|
||||
regular(m_frontend) << ")";
|
||||
}
|
||||
void display_error_pos(pos_info const & p) { display_error_pos(p.first, p.second); }
|
||||
void display_error_pos(expr const & e) {
|
||||
if (e) {
|
||||
|
@ -1553,6 +1559,24 @@ class parser::imp {
|
|||
regular(m_frontend) << mk_pair(ex.get_justification().pp(fmt, opts, &pos_provider, true), opts) << endl;
|
||||
}
|
||||
|
||||
void display_error(lua_exception const & ex) {
|
||||
switch (ex.get_source()) {
|
||||
case lua_exception::source::String:
|
||||
display_error_pos(ex.get_line() + m_last_script_pos.first - 1, static_cast<unsigned>(-1));
|
||||
regular(m_frontend) << " executing script," << ex.msg() << endl;
|
||||
break;
|
||||
case lua_exception::source::File:
|
||||
display_error_pos(m_last_script_pos);
|
||||
regular(m_frontend) << " executing external script (" << ex.get_filename() << ":" << ex.get_line() << ")," << ex.msg() << endl;
|
||||
break;
|
||||
case lua_exception::source::Unknown:
|
||||
display_error_pos(m_last_script_pos);
|
||||
regular(m_frontend) << " executing script, but could not decode position information, " << ex.what() << endl;
|
||||
break;
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
void updt_options() {
|
||||
m_verbose = get_parser_verbose(m_frontend.get_state().get_options());
|
||||
m_show_errors = get_parser_show_errors(m_frontend.get_state().get_options());
|
||||
|
@ -1566,6 +1590,7 @@ class parser::imp {
|
|||
}
|
||||
|
||||
void parse_script() {
|
||||
m_last_script_pos = mk_pair(m_scanner.get_script_block_line(), m_scanner.get_script_block_pos());
|
||||
if (!m_leanlua_state)
|
||||
throw exception("failed to execute Lua script, parser does not have a Lua interpreter");
|
||||
m_leanlua_state->dostring(m_scanner.get_str_val().c_str());
|
||||
|
@ -1629,6 +1654,13 @@ public:
|
|||
display_error(ex);
|
||||
if (m_use_exceptions)
|
||||
throw;
|
||||
} catch (lua_exception & ex) {
|
||||
m_found_errors = true;
|
||||
reset_interrupt();
|
||||
if (m_show_errors)
|
||||
display_error(ex);
|
||||
if (m_use_exceptions)
|
||||
throw;
|
||||
} catch (interrupted & ex) {
|
||||
if (m_verbose)
|
||||
regular(m_frontend) << "\n!!!Interrupted!!!" << endl;
|
||||
|
|
|
@ -93,7 +93,9 @@ scanner::scanner(std::istream& stream):
|
|||
m_curr(0),
|
||||
m_line(1),
|
||||
m_pos(0),
|
||||
m_stream(stream) {
|
||||
m_stream(stream),
|
||||
m_script_line(1),
|
||||
m_script_pos(0) {
|
||||
next();
|
||||
}
|
||||
|
||||
|
@ -337,6 +339,8 @@ scanner::token scanner::read_string() {
|
|||
scanner::token scanner::read_script_block() {
|
||||
lean_assert(curr() == '{');
|
||||
next();
|
||||
m_script_line = m_line;
|
||||
m_script_pos = m_pos;
|
||||
m_buffer.clear();
|
||||
int num_open_blocks = 1;
|
||||
while (true) {
|
||||
|
|
|
@ -31,6 +31,9 @@ protected:
|
|||
int m_pos; // start position of the token
|
||||
std::istream & m_stream;
|
||||
|
||||
int m_script_line; // hack for saving beginning of script block line and pos
|
||||
int m_script_pos;
|
||||
|
||||
mpq m_num_val;
|
||||
name m_name_val;
|
||||
std::string m_buffer;
|
||||
|
@ -68,6 +71,9 @@ public:
|
|||
name const & get_name_val() const { return m_name_val; }
|
||||
mpq const & get_num_val() const { return m_num_val; }
|
||||
std::string const & get_str_val() const { return m_buffer; }
|
||||
|
||||
int get_script_block_line() const { return m_script_line; }
|
||||
int get_script_block_pos() const { return m_script_pos; }
|
||||
};
|
||||
std::ostream & operator<<(std::ostream & out, scanner::token const & t);
|
||||
}
|
||||
|
|
15
tests/lean/lua2.lean
Normal file
15
tests/lean/lua2.lean
Normal file
|
@ -0,0 +1,15 @@
|
|||
Variable x : Bool
|
||||
|
||||
{{
|
||||
a = {}
|
||||
print("hello world")
|
||||
print ("ok")
|
||||
a = {
|
||||
x = 10,
|
||||
y = 20
|
||||
}
|
||||
rint ("ok")
|
||||
}}
|
||||
|
||||
Variable y : Bool
|
||||
|
7
tests/lean/lua2.lean.expected.out
Normal file
7
tests/lean/lua2.lean.expected.out
Normal file
|
@ -0,0 +1,7 @@
|
|||
Set: pp::colors
|
||||
Set: pp::unicode
|
||||
Assumed: x
|
||||
hello world
|
||||
ok
|
||||
Error (line: 11) executing script, attempt to call global 'rint' (a nil value)
|
||||
Assumed: y
|
6
tests/lean/lua3.lean
Normal file
6
tests/lean/lua3.lean
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
Variable x : Int
|
||||
|
||||
{{
|
||||
dofile("script.lua")
|
||||
}}
|
5
tests/lean/lua3.lean.expected.out
Normal file
5
tests/lean/lua3.lean.expected.out
Normal file
|
@ -0,0 +1,5 @@
|
|||
Set: pp::colors
|
||||
Set: pp::unicode
|
||||
Assumed: x
|
||||
hello
|
||||
Error (line: 4, pos: 0) executing external script (script.lua:4), attempt to call global 'prn' (a nil value)
|
4
tests/lean/script.lua
Normal file
4
tests/lean/script.lua
Normal file
|
@ -0,0 +1,4 @@
|
|||
|
||||
|
||||
print("hello")
|
||||
prn("world")
|
Loading…
Reference in a new issue