- [NEW] [debugger] Soport per a expressions d'assignació en vscode

- [NEW] [debugger] Soport per a Logpoints en vscode
- [NEW] Al tornar del debugger, torna a pillar el foco la finestra de mini
This commit is contained in:
2026-03-31 14:04:17 +02:00
parent 6f5bdd274a
commit 6e5f9fb1a8
3 changed files with 235 additions and 87 deletions

315
lua.cpp
View File

@@ -474,6 +474,112 @@ json expandTable(lua_State* L, int ref) {
};
}
json setVariable(lua_State* L, int frame, const std::string& scope,
const std::string& name, const std::string& valueStr)
{
lua_Debug ar;
if (!lua_getstack(L, frame, &ar)) {
return { {"error", "invalid frame"} };
}
lua_getinfo(L, "nSl", &ar);
// Convertir valueStr a valor Lua
auto pushValue = [&](const std::string& s) {
// número
char* end;
double num = strtod(s.c_str(), &end);
if (*end == '\0') {
lua_pushnumber(L, num);
return;
}
// boolean
if (s == "true") { lua_pushboolean(L, 1); return; }
if (s == "false") { lua_pushboolean(L, 0); return; }
// nil
if (s == "nil") { lua_pushnil(L); return; }
// string
lua_pushstring(L, s.c_str());
};
if (scope == "locals") {
int i = 1;
const char* localName;
while ((localName = lua_getlocal(L, &ar, i)) != NULL) {
lua_pop(L, 1); // pop old value
if (name == localName) {
pushValue(valueStr);
lua_setlocal(L, &ar, i);
return { {"value", valueStr} };
}
i++;
}
}
if (scope == "upvalues") {
lua_getinfo(L, "f", &ar); // push function
int i = 1;
const char* upName;
while ((upName = lua_getupvalue(L, -1, i)) != NULL) {
lua_pop(L, 1); // pop old value
if (name == upName) {
pushValue(valueStr);
lua_setupvalue(L, -1, i);
lua_pop(L, 1); // pop function
return { {"value", valueStr} };
}
i++;
}
lua_pop(L, 1); // pop function
}
if (scope == "globals") {
pushValue(valueStr);
lua_setglobal(L, name.c_str());
return { {"value", valueStr} };
}
return { {"error", "variable not found"} };
}
json setTableField(lua_State* L, int ref, const std::string& key, const std::string& valueStr)
{
// Recuperar la tabla desde tu sistema de referencias
if (!pushTableFromRef(L, ref)) {
return { {"error", "invalid table ref"} };
}
// Convertir el valor
auto pushValue = [&](const std::string& s) {
char* end;
double num = strtod(s.c_str(), &end);
if (*end == '\0') { lua_pushnumber(L, num); return; }
if (s == "true") { lua_pushboolean(L, 1); return; }
if (s == "false") { lua_pushboolean(L, 0); return; }
if (s == "nil") { lua_pushnil(L); return; }
lua_pushstring(L, s.c_str());
};
pushValue(valueStr);
// tabla[key] = valor
lua_setfield(L, -2, key.c_str());
lua_pop(L, 1); // pop tabla
return { {"value", valueStr} };
}
json evalExpression(lua_State* L, const std::string& expr) {
lua_Debug ar;
if (!lua_getstack(L, 0, &ar)) {
@@ -586,110 +692,95 @@ json evalExpression(lua_State* L, const std::string& expr) {
return result;
}
json setVariable(lua_State* L, int frame, const std::string& scope,
const std::string& name, const std::string& valueStr)
{
lua_Debug ar;
if (!lua_getstack(L, frame, &ar)) {
return { {"error", "invalid frame"} };
json evalAssign(lua_State* L, int frame, const std::string& expr) {
// 1. Separar LHS y RHS
size_t eq = expr.find('=');
if (eq == std::string::npos) {
return { {"error", "not an assignment"} };
}
lua_getinfo(L, "nSl", &ar);
std::string lhs = expr.substr(0, eq);
std::string rhs = expr.substr(eq + 1);
// Convertir valueStr a valor Lua
auto pushValue = [&](const std::string& s) {
// número
char* end;
double num = strtod(s.c_str(), &end);
if (*end == '\0') {
lua_pushnumber(L, num);
return;
}
// boolean
if (s == "true") { lua_pushboolean(L, 1); return; }
if (s == "false") { lua_pushboolean(L, 0); return; }
// nil
if (s == "nil") { lua_pushnil(L); return; }
// string
lua_pushstring(L, s.c_str());
// limpiar espacios
auto trim = [](std::string& s) {
size_t a = s.find_first_not_of(" \t");
size_t b = s.find_last_not_of(" \t");
if (a == std::string::npos) { s = ""; return; }
s = s.substr(a, b - a + 1);
};
if (scope == "locals") {
int i = 1;
const char* localName;
while ((localName = lua_getlocal(L, &ar, i)) != NULL) {
lua_pop(L, 1); // pop old value
trim(lhs);
trim(rhs);
if (name == localName) {
pushValue(valueStr);
lua_setlocal(L, &ar, i);
return { {"value", valueStr} };
}
// 2. Evaluar RHS usando tu evalExpression
json rhsValue = evalExpression(L, rhs);
if (rhsValue.contains("error")) {
return rhsValue;
}
i++;
std::string rhsStr = rhsValue["value"];
// 3. Determinar si LHS es:
// - variable simple: x
// - acceso tabla: player.health
// - acceso profundo: a.b.c
if (lhs.find('.') == std::string::npos) {
// variable simple → locals, upvalues o globals
// Intentar locals
json r = setVariable(L, frame, "locals", lhs, rhsStr);
if (!r.contains("error")) return r;
// Intentar upvalues
r = setVariable(L, frame, "upvalues", lhs, rhsStr);
if (!r.contains("error")) return r;
// Intentar globals
r = setVariable(L, frame, "globals", lhs, rhsStr);
return r;
}
// 4. Acceso tabla: a.b.c
// separar en partes
std::vector<std::string> parts;
{
std::stringstream ss(lhs);
std::string item;
while (std::getline(ss, item, '.')) {
trim(item);
parts.push_back(item);
}
}
if (scope == "upvalues") {
lua_getinfo(L, "f", &ar); // push function
int i = 1;
const char* upName;
while ((upName = lua_getupvalue(L, -1, i)) != NULL) {
lua_pop(L, 1); // pop old value
if (name == upName) {
pushValue(valueStr);
lua_setupvalue(L, -1, i);
lua_pop(L, 1); // pop function
return { {"value", valueStr} };
}
i++;
}
lua_pop(L, 1); // pop function
if (parts.size() < 2) {
return { {"error", "invalid table assignment"} };
}
if (scope == "globals") {
pushValue(valueStr);
lua_setglobal(L, name.c_str());
return { {"value", valueStr} };
// La clave final
std::string finalKey = parts.back();
parts.pop_back();
// 5. Evaluar la ruta de tabla (a.b.c → obtener tabla c)
// usando tu evalExpression
std::string tableExpr;
for (size_t i = 0; i < parts.size(); i++) {
if (i > 0) tableExpr += ".";
tableExpr += parts[i];
}
return { {"error", "variable not found"} };
}
json setTableField(lua_State* L, int ref, const std::string& key, const std::string& valueStr)
{
// Recuperar la tabla desde tu sistema de referencias
if (!pushTableFromRef(L, ref)) {
return { {"error", "invalid table ref"} };
json tableValue = evalExpression(L, tableExpr);
if (tableValue.contains("error")) {
return tableValue;
}
// Convertir el valor
auto pushValue = [&](const std::string& s) {
char* end;
double num = strtod(s.c_str(), &end);
if (*end == '\0') { lua_pushnumber(L, num); return; }
if (s == "true") { lua_pushboolean(L, 1); return; }
if (s == "false") { lua_pushboolean(L, 0); return; }
if (s == "nil") { lua_pushnil(L); return; }
lua_pushstring(L, s.c_str());
};
if (tableValue["type"] != "table") {
return { {"error", "LHS is not a table"} };
}
pushValue(valueStr);
int ref = tableValue["ref"];
// tabla[key] = valor
lua_setfield(L, -2, key.c_str());
lua_pop(L, 1); // pop tabla
return { {"value", valueStr} };
// 6. Asignar dentro de la tabla
return setTableField(L, ref, finalKey, rhsStr);
}
void sendDebugResponse(const std::string& type, const json& payload) {
@@ -743,6 +834,7 @@ void processDebugCommand(const std::string& line) {
printf("CONTINUA\n\n");
g_stepMode = STEP_NONE;
g_paused = false;
raisewindow();
}
else if (cmd == "pause") {
g_stepMode = STEP_INTO;
@@ -807,6 +899,19 @@ void processDebugCommand(const std::string& line) {
};
sendDebugResponse("eval", payload);
}
else if (cmd == "evalAssign") {
int frame = j.value("frame", 0);
std::string expr = j["expr"];
json result = evalAssign(L, frame, expr);
json payload = {
{ "kind", "eval" },
{ "result", result }
};
sendDebugResponse("eval", payload);
}
else if (cmd == "setVariable") {
int frame = j.value("frame", 0);
std::string scope = j["scope"];
@@ -840,6 +945,37 @@ bool checkBreakpointCondition(lua_State* L, const Breakpoint& bp) {
return false;
}
std::string expandLogMessage(lua_State* L, const std::string& msg) {
std::string out;
size_t i = 0;
while (i < msg.size()) {
if (msg[i] == '{') {
size_t j = msg.find('}', i + 1);
if (j == std::string::npos) break;
std::string expr = msg.substr(i + 1, j - i - 1);
json r = evalExpression(L, expr);
out += r.value("value", "nil");
i = j + 1;
} else {
out += msg[i++];
}
}
return out;
}
void sendLogOutput(const std::string& text) {
json payload = {
{ "kind", "log" },
{ "text", text }
};
sendDebugResponse("log", payload);
}
bool isBreakpoint(const std::string& file, int line) {
std::lock_guard<std::mutex> lock(g_breakMutex);
@@ -849,9 +985,14 @@ bool isBreakpoint(const std::string& file, int line) {
for (auto& bp : it->second) {
if (bp.line == line) {
if (checkBreakpointCondition(L, bp)) {
if (!bp.logMessage.empty()) {
std::string msg = expandLogMessage(L, bp.logMessage);
sendLogOutput(msg);
return false; // NO parar
} else if (checkBreakpointCondition(L, bp)) {
return true;
}
}
}

View File

@@ -146,6 +146,11 @@ int16_t beats, num_beats = 0;
void createNewProject();
void raisewindow() {
SDL_RaiseWindow(mini_win);
//SDL_SetWindowInputFocus(mini_win);
}
char* get_value_from_line(char* line) {
char* equal_character = strchr(line, '=');
if (equal_character == NULL) return NULL;

2
mini.h
View File

@@ -124,6 +124,8 @@ void loop();
int scrw();
int scrh();
void raisewindow();
uint8_t newsurf(int w, int h);
uint8_t loadsurf(const char* filename, const bool external = false);
void savesurf(uint8_t surface, const char* filename, uint8_t *pal, uint8_t colors=0);