1313#include < unordered_map>
1414#include < unordered_set>
1515
16+ // boost::regex (vendored by imguipack via USE_IMGUI_COLOR_TEXT_EDIT) — same engine the host will use
17+ // when the future shared ltg:regex(...) brick lands. linked through the boost_regex CMake target.
18+ #include < boost/regex.hpp>
19+
1620#include < lua.hpp>
1721#include < sol/sol.hpp>
1822
@@ -25,6 +29,33 @@ namespace {
2529// plugin DLL's unload.
2630const char * const kDebugModuleRegistryKey = " ltg_debug_module" ;
2731
32+ // Parse a Lua/sol2 error message and extract (file, line). Sol2 wraps an in-memory chunk as
33+ // `[string "NAME"]:LINE: MSG`; a file-based script reads `NAME:LINE: MSG`. On a match, the full
34+ // original message is kept as `aoErr.message` (it stays useful for the tooltip even if regex only
35+ // found a prefix). On no match, the message lands on `aFallbackChunk:0` so the host can still display
36+ // it (just unanchored). Returns true on a regex match.
37+ bool parseLuaError (const std::string& aMsg, const std::string& aFallbackChunk, Ltg::ScriptingError& aoErr) {
38+ // delimiter `re(...)re` mandatory: the chunkRe pattern contains a literal `)"` (the `+)"` after
39+ // the `[^"]+` group), which would otherwise close an empty-delimiter R"(...)" prematurely.
40+ static const boost::regex chunkRe (R"re( \[string\s+"([^"]+)"\]:(\d+):\s*(.*))re" );
41+ static const boost::regex plainRe (R"re( ([^:\[\]\s]+):(\d+):\s*(.*))re" );
42+ boost::smatch match;
43+ aoErr.message = aMsg;
44+ if (boost::regex_search (aMsg, match, chunkRe)) {
45+ aoErr.file = match[1 ].str ();
46+ aoErr.line = static_cast <size_t >(std::stoul (match[2 ].str ()));
47+ return true ;
48+ }
49+ if (boost::regex_search (aMsg, match, plainRe)) {
50+ aoErr.file = match[1 ].str ();
51+ aoErr.line = static_cast <size_t >(std::stoul (match[2 ].str ()));
52+ return true ;
53+ }
54+ aoErr.file = aFallbackChunk;
55+ aoErr.line = 0 ;
56+ return false ;
57+ }
58+
2859// Stringify a value on the Lua stack WITHOUT luaL_tolstring (absent from LuaJIT 5.1).
2960// The type is checked first so lua_tostring is only called on real strings (it would
3061// otherwise coerce a number in place and disturb the stack inside the hook).
@@ -198,7 +229,9 @@ bool Module::compileScript(const Ltg::ScriptFilePathName& vFilePathName, Ltg::Er
198229
199230bool Module::compileScriptCode (const std::string& aCode, Ltg::ErrorContainer& vOutErrors) {
200231 try {
201- m_luaPtr->script (aCode); // compile + run the chunk from memory (defines parse/startFile/endFile)
232+ // pass an explicit chunk name so sol2's error messages identify our project script
233+ // (otherwise sol2 uses the first line of code as the chunk name, which breaks routing).
234+ m_luaPtr->script (aCode, Ltg::sc_PROJECT_SCRIPT_CHUNK); // compile + run the chunk (defines parse/startFile/endFile)
202235 bool res = true ;
203236 sol::function parse = (*m_luaPtr)[" parse" ];
204237 if (!parse.valid ()) {
@@ -209,10 +242,20 @@ bool Module::compileScriptCode(const std::string& aCode, Ltg::ErrorContainer& vO
209242 res &= callScriptEnd (vOutErrors);
210243 return res;
211244 } catch (const sol::error& ex) {
245+ Ltg::ScriptingError err;
246+ parseLuaError (ex.what (), Ltg::sc_PROJECT_SCRIPT_CHUNK, err);
247+ vOutErrors.push_back (err);
212248 LogVarError (" Lua: Error in the Lua script : %s" , ex.what ());
213249 } catch (const std::exception& ex) {
250+ Ltg::ScriptingError err;
251+ parseLuaError (ex.what (), Ltg::sc_PROJECT_SCRIPT_CHUNK, err);
252+ vOutErrors.push_back (err);
214253 LogVarError (" Lua: Error in the Lua script : %s" , ex.what ());
215254 } catch (...) {
255+ Ltg::ScriptingError err;
256+ err.file = Ltg::sc_PROJECT_SCRIPT_CHUNK;
257+ err.message = " Unknown error in the Lua script" ;
258+ vOutErrors.push_back (err);
216259 LogVarError (" Lua: %s" , " Unknown error in the Lua script" );
217260 }
218261 return false ;
@@ -226,8 +269,11 @@ bool Module::callScriptStart(Ltg::ErrorContainer& vOutErrors) {
226269 }
227270 sol::protected_function_result result = startFile ();
228271 if (!result.valid ()) {
229- sol::error err = result;
230- LogVarLightError (" Lua: error in startFile func call : %s" , err.what ());
272+ sol::error solErr = result;
273+ Ltg::ScriptingError err;
274+ parseLuaError (solErr.what (), Ltg::sc_PROJECT_SCRIPT_CHUNK, err);
275+ vOutErrors.push_back (err);
276+ LogVarLightError (" Lua: error in startFile func call : %s" , solErr.what ());
231277 return false ;
232278 }
233279 return true ;
@@ -241,8 +287,11 @@ bool Module::callScriptExec(const Ltg::ScriptingDatas& vOutDatas, Ltg::ErrorCont
241287 }
242288 sol::protected_function_result result = parse (vOutDatas.buffer );
243289 if (!result.valid ()) {
244- sol::error err = result;
245- LogVarLightError (" Lua: error in parse func call : %s" , err.what ());
290+ sol::error solErr = result;
291+ Ltg::ScriptingError err;
292+ parseLuaError (solErr.what (), Ltg::sc_PROJECT_SCRIPT_CHUNK, err);
293+ vErrors.push_back (err);
294+ LogVarLightError (" Lua: error in parse func call : %s" , solErr.what ());
246295 return false ;
247296 }
248297 return true ;
@@ -256,8 +305,11 @@ bool Module::callScriptEnd(Ltg::ErrorContainer& vOutErrors) {
256305 }
257306 sol::protected_function_result result = endFile ();
258307 if (!result.valid ()) {
259- sol::error err = result;
260- LogVarLightError (" Lua: error in endFile func call : %s" , err.what ());
308+ sol::error solErr = result;
309+ Ltg::ScriptingError err;
310+ parseLuaError (solErr.what (), Ltg::sc_PROJECT_SCRIPT_CHUNK, err);
311+ vOutErrors.push_back (err);
312+ LogVarLightError (" Lua: error in endFile func call : %s" , solErr.what ());
261313 return false ;
262314 }
263315 return true ;
0 commit comments