3#include <simplesquirrel/simplesquirrel.hpp>
7#include <unordered_map>
13thread_local std::unordered_map<HSQUIRRELVM, ScriptErrorContext> g_last_errors;
15const char* typeName(SQObjectType
type) {
17 case OT_NULL:
return "null";
18 case OT_INTEGER:
return "integer";
19 case OT_FLOAT:
return "float";
20 case OT_BOOL:
return "bool";
21 case OT_STRING:
return "string";
22 case OT_TABLE:
return "table";
23 case OT_ARRAY:
return "array";
24 case OT_USERDATA:
return "userdata";
25 case OT_CLOSURE:
return "closure";
26 case OT_NATIVECLOSURE:
return "native";
27 case OT_GENERATOR:
return "generator";
28 case OT_USERPOINTER:
return "userpointer";
29 case OT_THREAD:
return "thread";
30 case OT_CLASS:
return "class";
31 case OT_INSTANCE:
return "instance";
32 case OT_WEAKREF:
return "weakref";
33 default:
return "other";
42 if (!
vm)
return "unknown error";
43 switch (sq_gettype(
vm, 2)) {
47 SQBool
value = SQFalse;
48 if (SQ_SUCCEEDED(sq_getbool(
vm, 2, &
value)))
49 return value ?
"true" :
"false";
54 if (SQ_SUCCEEDED(sq_getinteger(
vm, 2, &
value)))
55 return std::to_string(
static_cast<long long>(
value));
60 if (SQ_SUCCEEDED(sq_getfloat(
vm, 2, &
value))) {
61 std::ostringstream out;
68 const SQChar*
value =
nullptr;
75 return std::string(
"error value of type ") + typeName(sq_gettype(
vm, 2));
88 if (SQ_FAILED(sq_stackinfos(
vm,
level, &si)))
break;
90 frame.
source = si.source ? si.source :
"";
91 frame.
function = si.funcname ? si.funcname :
"";
92 frame.
line =
static_cast<int>(si.line);
93 ctx.
stack.push_back(std::move(frame));
100 ctx.
source = frame.source;
102 ctx.
line = frame.line;
106 if (ctx.
line <= 0 && !ctx.
stack.empty()) {
120 auto* machine =
reinterpret_cast<ssq::VM*
>(sq_getforeignptr(
vm));
121 if (!machine)
return ctx;
123 ctx.
message = machine->getLastCompileException().what();
133 constexpr const char* kPrefix =
"Compile error at ";
134 if (
text.rfind(kPrefix, 0) != 0)
return false;
135 const std::string rest =
text.substr(
sizeof(kPrefix) - 1);
140 for (
size_t i = 0; i < rest.size(); ++i) {
141 if (rest[i] !=
':')
continue;
142 size_t lineEnd = i + 1;
143 while (lineEnd < rest.size() &&
144 std::isdigit(
static_cast<unsigned char>(rest[lineEnd])))
146 if (lineEnd == i + 1 || lineEnd >= rest.size() || rest[lineEnd] !=
':')
continue;
147 size_t columnEnd = lineEnd + 1;
148 while (columnEnd < rest.size() &&
149 std::isdigit(
static_cast<unsigned char>(rest[columnEnd])))
151 if (columnEnd == lineEnd + 1 || columnEnd >= rest.size() ||
152 rest[columnEnd] !=
' ')
158 *
line = std::stoi(rest.substr(i + 1, lineEnd - i - 1));
160 *
column = std::stoi(rest.substr(lineEnd + 1, columnEnd - lineEnd - 1));
171 if (
line <= 0 || sourceText.empty())
return {};
174 const size_t newline = sourceText.find(
'\n',
start);
175 if (newline == std::string::npos)
return {};
178 size_t end = sourceText.find(
'\n',
start);
180 sourceText.substr(
start,
end == std::string::npos ? std::string::npos
182 if (!
text.empty() &&
text.back() ==
'\r')
text.pop_back();
187 std::ostringstream out;
189 out << (frame.source.empty() ?
"<unknown>" : frame.source) <<
':';
194 if (!frame.function.empty()) out <<
" in " << frame.function;
201 std::ostringstream out;
202 if (!ctx.
source.empty()) {
204 if (ctx.
line > 0) out <<
':' << ctx.
line;
210 if (!ctx.
hint.empty()) out <<
"\n " << ctx.
hint;
212 std::string result = out.str();
213 if (!result.empty() && result.back() ==
'\n') result.pop_back();
219 g_last_errors[
vm] = std::move(ctx);
224 auto found = g_last_errors.find(
vm);
225 if (
found == g_last_errors.end())
return {};
227 g_last_errors.erase(
found);
232 if (!
vm)
return nullptr;
233 auto found = g_last_errors.find(
vm);
234 return found == g_last_errors.end() ? nullptr : &
found->second;
239 g_last_errors.erase(
vm);
244 if (!ctx || ctx->
empty())
return {};
struct SQVM * HSQUIRRELVM
const UnitySourceAsset & source
std::string formatScriptError(const ScriptErrorContext &ctx)
Formats a context into a human-readable multi-line report.
void clearLastScriptError(HSQUIRRELVM vm)
Drops any recorded error for a VM.
std::string sourceLineText(const std::string &sourceText, int line)
Extracts one 1-based line from script source text.
ScriptErrorContext takeLastScriptError(HSQUIRRELVM vm)
Consumes and clears the last recorded error for a VM.
std::string formatLastScriptError(HSQUIRRELVM vm)
Formats the last captured error for a VM without consuming it.
ScriptErrorContext captureCompileError(HSQUIRRELVM vm)
Captures the last compilation error recorded by the VM.
const ScriptErrorContext * peekLastScriptError(HSQUIRRELVM vm)
Peeks at the last recorded error for a VM without clearing it.
bool parseCompileError(const std::string &text, std::string *source, int *line, int *column, std::string *message)
Parses the ssq compile message "Compile error at source:line:column msg".
void setLastScriptError(HSQUIRRELVM vm, ScriptErrorContext ctx)
Records the last error for a VM (thread-local, synchronous consumers).
std::string formatStackTrace(const std::vector< ScriptFrame > &frames)
Formats just the call-stack portion of a context.
ScriptErrorContext captureScriptError(HSQUIRRELVM vm)
Captures the pending runtime error from inside the Squirrel error handler.
Structured snapshot of a script error: message, throw site and stack.
int line
1-based throw-site line; -1 when unknown.
std::string function
Function of the throwing frame.
int column
1-based column (compile errors only).
bool empty() const noexcept
True when no error payload was captured.
std::string hint
Optional source snippet shown under the message.
std::string message
Raw error value ("kaboom", "42", ...).
std::vector< ScriptFrame > stack
Call stack, innermost frame first.
std::string source
Source of the throwing frame.
One call-stack frame captured while the script error is still live.
int line
1-based source line; -1 when unknown.
std::string function
Function name, when Squirrel debug info knows it.
std::string source
Source name of the frame ("main.nut", "buffer", ...).