Files
Lake/src/methods/debug.lake
2026-04-25 16:50:59 +02:00

668 lines
17 KiB
Plaintext

Debug = {};
fn Debug.encodeString(raw, islua51) {
var out = '"';
for (var i = 1; i <= #raw; i += 1;) {
var ch = raw:sub(i,i);
var code = string.byte(ch);
if (ch == '\') {
out ..= '\\';
} elseif (ch == '"') {
out ..= '\"';
} elseif (code >= 32 && code <= 126) {
out ..= ch;
} else {
if (islua51)
out ..= '\' .. DecEncode(code, 3);
else
out ..= '\x' .. HexEncode(code, 2);
}
}
out ..= '"';
return out;
}
fn Debug.encodeNumber(n) {
var num = '';
var isneg = false;
if (n < 0) {
n *= -1;
isneg = true;
}
var whole = math.floor(n);
if (whole == 0) {
num ..= '0';
}
while (whole > 0) {
var digit = whole % 10;
num = string.char(string.byte('0') + digit) .. num;
whole = math.floor(whole / 10);
}
var dec = n % 1;
if (dec > 0) {
num ..= '.';
while (dec > 0) {
dec *= 10;
var digit = math.floor(dec);
num ..= string.char(string.byte('0') + digit);
dec %= 1;
}
}
if (isneg) num = '-' .. num;
return num;
}
fn Debug.convertBinaryOperator(lakeop) {
if (lakeop == '+') {
return '+';
} elseif (lakeop == "-" ) {
return "-";
} elseif ( lakeop == "*" ) {
return "*";
} elseif ( lakeop == "/" ) {
return "/";
} elseif ( lakeop == "%" ) {
return "%";
} elseif ( lakeop == "^" ) {
return "^";
} elseif ( lakeop == ".." ) {
return "..";
} elseif ( lakeop == "<" ) {
return "<";
} elseif ( lakeop == ">" ) {
return ">";
} elseif ( lakeop == "<=" ) {
return "<=";
} elseif ( lakeop == ">=" ) {
return ">=";
} elseif ( lakeop == "!=" ) {
return "~=";
} elseif ( lakeop == "==" ) {
return "==";
} elseif ( lakeop == "&&" ) {
return "and";
} elseif ( lakeop == "||" ) {
return "or";
}
}
fn Debug.convertUnaryOperator(op) {
if (op == '#') return '#';
elseif (op == '-') return '-';
elseif (op == '!') return 'not';
}
fn Debug.output(outp, ast) {
Debug.outputStatement(outp, ast);
}
fn Debug.outputStatement(outp, node) {
if (node.kind == 'Program') {
outp:addLine('do', node.location:format());
for (var i = 1; i <= #node.content; i += 1;) {
var stmt = node.content[i];
Debug.outputStatement(outp, stmt);
}
outp:addLine('end;', node.location:format());
} elseif (node.kind == 'VariableDeclaration') {
outp:addLine('local', node.location:format());
var varnames = node.content[1];
for (var i = 1; i <= #varnames; i += 1;) {
var vname = varnames[i];
outp:addLine(vname[1], vname[2]:format());
if (i != #varnames) {
outp:addLine(',', node.location:format());
}
}
var values = node.content[2];
if (#values > 0) {
outp:addLine('=', node.location:format());
for (var i = 1; i <= #values; i += 1;) {
var val = values[i];
Debug.outputExpression(outp, val);
if (i != #values) {
outp:addLine(',', node.location:format());
}
}
}
outp:addLine(';', node.location:format());
} elseif (node.kind == "Block") {
outp:addLine('do', node.location:format());
for (var i = 1; i <= #node.content; i += 1;) {
var stmt = node.content[i];
Debug.outputStatement(outp, stmt);
}
outp:addLine('end;', node.location:format());
} elseif (node.kind == 'IfStatement') {
outp:addLine('if', node.location:format());
Debug.outputExpression(outp, node.content[1][1][1]);
outp:addLine('then', node.location:format());
Debug.outputStatement(outp, node.content[1][1][2]);
for (var i = 2; i <= #node.content[1]; i += 1;) {
var item = node.content[1][i];
outp:addLine('elseif', node.location:format());
Debug.outputExpression(outp, item[1]);
outp:addLine('then', node.location:format());
Debug.outputStatement(outp, item[2]);
}
if (node.content[2]) {
outp:addLine('else', node.location:format());
Debug.outputStatement(outp, node.content[2]);
}
outp:addLine('end;', node.location:format());
} elseif (node.kind == "FunctionDeclaration") {
outp:addLine('function', node.location:format());
Debug.outputFunctionName(outp, node.content[1]);
outp:addLine('(', node.location:format());
for (var i = 1; i <= #node.content[2]; i += 1;) {
var part = node.content[2][i];
outp:addLine(part, node.location:format());
if (i != #node.content[2]) {
outp:addLine(',', node.location:format());
}
}
outp:addLine(')', node.location:format());
Debug.outputStatement(outp, node.content[3]);
outp:addLine('end;', node.location:format());
} elseif (node.kind == 'LocalFunctionDeclaration') {
outp:addLine('local function', node.location:format());
outp:addLine(node.content[1].content, node.content[1].location:format());
outp:addLine('(', node.location:format());
for (var i = 1; i <= #node.content[2]; i += 1;) {
var part = node.content[2][i];
outp:addLine(part, node.location:format());
if (i != #node.content[2]) {
outp:addLine(',', node.location:format());
}
}
outp:addLine(')', node.location:format());
Debug.outputStatement(outp, node.content[3]);
outp:addLine('end;', node.location:format());
} elseif (node.kind == 'Return') {
outp:addLine('return', node.location:format()); // if true because it has to be at the end of a block in lua, but not here.
for (var i = 1; i <= #node.content; i += 1;) {
var expr = node.content[i];
Debug.outputExpression(outp, expr);
if (i != #node.content) {
outp:addLine(',', node.location:format());
}
}
outp:addLine(';', node.location:format());
} elseif (node.kind == 'Assignment') {
var vars = node.content[1];
var vals = node.content[2];
for (var i = 1; i <= #vars; i += 1;) {
var vari = vars[i];
Debug.outputVariable(outp, vari);
if (i != #vars) {
outp:addLine(',', node.location:format());
}
}
outp:addLine('=', node.location:format());
for (var i = 1; i <= #vals; i += 1;) {
var val = vals[i];
Debug.outputExpression(outp, val);
if (i != #vals) {
outp:addLine(',', node.location:format());
}
}
outp:addLine(';', node.location:format());
} elseif (node.kind == 'Call') {
outp:addLine('(', node.location:format());
Debug.outputExpression(outp, node.content[1]);
outp:addLine(')(', node.location:format());
var args = node.content[2];
for (var i = 1; i <= #args; i += 1;) {
var arg = args[i];
Debug.outputExpression(outp, arg);
if (i != #args) {
outp:addLine(',', node.location:format());
}
}
outp:addLine(');', node.location:format());
} elseif (node.kind == 'MethodCall') {
outp:addLine('(', node.location:format());
Debug.outputExpression(outp, node.content[1]);
outp:addLine('):', node.location:format());
outp:addLine(node.content[2] .. '(', node.location:format());
var args = node.content[3];
for (var i = 1; i <= #args; i += 1;) {
var arg = args[i];
Debug.outputExpression(outp, arg);
if (i != #args) {
outp:addLine(',', node.location:format());
}
}
outp:addLine(');', node.location:format());
} elseif (node.kind == 'While') {
outp:addLine('while', node.location:format());
Debug.outputExpression(outp, node.content[1]);
outp:addLine('do', node.location:format());
Debug.outputStatement(outp, node.content[2]);
outp:addLine('end;', node.location:format());
} elseif (node.kind == 'For') {
outp:addLine('do', node.location:format());
Debug.outputStatement(outp, node.content[1]);
outp:addLine('while', node.location:format());
Debug.outputExpression(outp, node.content[2]);
outp:addLine('do', node.location:format());
outp:addLine('do', node.location:format());
Debug.outputStatement(outp, node.content[4]);
outp:addLine('end;', node.location:format());
Debug.outputStatement(outp, node.content[3]);
outp:addLine('end;', node.location:format());
outp:addLine('end;', node.location:format());
} elseif (node.kind == 'OperatorAssignment') {
var lakeop = node.content[2];
var op = Debug.convertBinaryOperator(lakeop);
var lhs = node.content[1];
var rhs = node.content[3];
if (lhs.kind == 'Variable') {
var varname = lhs.content;
outp:addLine(varname, node.location:format());
outp:addLine('=', node.location:format());
outp:addLine('(', node.location:format());
outp:addLine(varname, node.location:format());
outp:addLine(op, node.location:format());
outp:addLine('(', node.location:format());
Debug.outputExpression(outp, rhs);
outp:addLine('));', node.location:format());
} elseif (lhs.kind == 'Field') {
outp:addLine('do', node.location:format());
outp:addLine('local _lake_operator_assignment_tmp =', node.location:format());
Debug.outputExpression(outp, lhs.content[1]);
outp:addLine(';', node.location:format());
outp:addLine('(_lake_operator_assignment_tmp).', node.location:format());
outp:addLine(lhs.content[2], node.location:format());
outp:addLine('=', node.location:format());
outp:addLine('(', node.location:format());
outp:addLine('(_lake_operator_assignment_tmp).', node.location:format());
outp:addLine(lhs.content[2], node.location:format());
outp:addLine(op, node.location:format());
outp:addLine('(', node.location:format());
Debug.outputExpression(outp, rhs);
outp:addLine('));', node.location:format());
outp:addLine('end;', node.location:format());
} elseif (lhs.kind == 'Index') {
outp:addLine('do', node.location:format());
outp:addLine('local _lake_operator_assignment_tmp =', node.location:format());
Debug.outputExpression(outp, lhs.content[1]);
outp:addLine(';', node.location:format());
outp:addLine('(_lake_operator_assignment_tmp)[', node.location:format());
Debug.outputExpression(outp, lhs.content[2]);
outp:addLine(']', node.location:format());
outp:addLine('=', node.location:format());
outp:addLine('(', node.location:format());
outp:addLine('(_lake_operator_assignment_tmp)[', node.location:format());
Debug.outputExpression(outp, lhs.content[2]);
outp:addLine(']', node.location:format());
outp:addLine(op, node.location:format());
outp:addLine('(', node.location:format());
Debug.outputExpression(outp, rhs);
outp:addLine('));', node.location:format());
outp:addLine('end;', node.location:format());
}
} elseif (node.kind == 'Break') {
outp:addLine('break;', node.location:format());
} elseif (node.kind == 'Foreach') {
outp:addLine("for", node.location:format());
var varlist,exprlist,block = node.content[1],node.content[2],node.content[3];
for (var i = 1; i <= #varlist; i += 1;) {
outp:addLine(varlist[i], node.location:format());
if (i != #varlist) {
outp:addLine(',', node.location:format());
}
}
outp:addLine("in", node.location:format());
for (var i = 1; i <= #exprlist; i += 1;) {
Debug.outputExpression(outp, exprlist[i]);
if (i != #exprlist) {
outp:addLine(",", node.location:format());
}
}
outp:addLine('do', node.location:format());
Debug.outputStatement(outp, block);
outp:addLine('end;', node.location:format());
}
}
fn Debug.outputExpression(outp, node) {
if (node.kind == 'StringLiteral') {
var lakestr = node.content;
var raw = Lower.decodeString(lakestr);
var enc = Debug.encodeString(raw, outp.target == 'lua51');
outp:addLine(enc, node.location:format());
} elseif (node.kind == 'CompiledStringLiteral') {
var raw = node.content;
var enc = Debug.encodeString(raw, outp.target == 'lua51');
outp:addLine(enc, node.location:format());
} elseif (node.kind == 'ParenthesizedExpression') {
outp:addLine("(", node.location:format());
Debug.outputExpression(outp, node.content);
outp:addLine(")", node.location:format());
} elseif (node.kind == 'NumberLiteral') {
var lakenum = node.content;
var raw = Lower.decodeNumber(lakenum);
var enc = Debug.encodeNumber(raw);
outp:addLine("(", node.location:format());
outp:addLine(enc, node.location:format());
outp:addLine(")", node.location:format());
} elseif (node.kind == 'CompiledNumberLiteral') { // usually generated by optimizer doing math
var raw = node.content;
var enc = Debug.encodeNumber(raw);
outp:addLine("(", node.location:format());
outp:addLine(enc, node.location:format());
outp:addLine(")", node.location:format());
} elseif (node.kind == 'TableLiteral') {
outp:addLine('{', node.location:format());
var listpart = node.content[1];
var tablepart = node.content[2];
for (var i = 1; i <= #listpart; i += 1;) {
var item = listpart[i];
Debug.outputExpression(outp, item);
outp:addLine(',', node.location:format());
}
for (var i = 1; i <= #tablepart; i += 1;) {
var item = tablepart[i];
outp:addLine('[', node.location:format());
Debug.outputExpression(outp, item[1]);
outp:addLine(']', node.location:format());
outp:addLine('=', node.location:format());
Debug.outputExpression(outp, item[2]);
outp:addLine(',', node.location:format());
}
outp:addLine('}', node.location:format());
} elseif (node.kind == 'LambdaFunctionLiteral') {
outp:addLine('function(', node.location:format());
for (var i = 1; i <= #node.content[1]; i += 1;) {
var part = node.content[1][i];
outp:addLine(part, node.location:format());
if (i != #node.content[1]) {
outp:addLine(",", node.location:format());
}
}
outp:addLine(")", node.location:format());
Debug.outputStatement(outp, node.content[2]);
outp:addLine("end", node.location:format());
} elseif (node.kind == 'BinaryOperator') {
var op = node.content[1];
var lhs = node.content[2];
var rhs = node.content[3];
outp:addLine("((", node.location:format());
Debug.outputExpression(outp,lhs);
outp:addLine(")", node.location:format());
var luaop = Debug.convertBinaryOperator(op);
outp:addLine(luaop, node.location:format());
outp:addLine("(", node.location:format());
Debug.outputExpression(outp,rhs);
outp:addLine("))", node.location:format());
} elseif (node.kind == 'UnaryOperator') {
var op = node.content[1];
var val = node.content[2];
outp:addLine("(", node.location:format());
var luaop = Debug.convertUnaryOperator(op);
outp:addLine(luaop, node.location:format());
outp:addLine("(", node.location:format());
Debug.outputExpression(outp,val);
outp:addLine("))", node.location:format());
} elseif (node.kind == 'Field') {
var left = node.content[1];
var right = node.content[2];
outp:addLine("((", node.location:format());
Debug.outputExpression(outp, left);
outp:addLine(").", node.location:format());
outp:addLine(right, node.location:format());
outp:addLine(")", node.location:format());
} elseif (node.kind == 'Index') {
var left = node.content[1];
var right = node.content[2];
outp:addLine("((", node.location:format());
Debug.outputExpression(outp, left);
outp:addLine(")", node.location:format());
outp:addLine("[", node.location:format());
Debug.outputExpression(outp, right);
outp:addLine("]", node.location:format());
outp:addLine(")", node.location:format());
} elseif (node.kind == 'Variable') {
outp:addLine("(", node.location:format());
outp:addLine(node.content, node.location:format());
outp:addLine(")", node.location:format());
} elseif (node.kind == 'BooleanLiteral') {
outp:addLine("(", node.location:format());
outp:addLine(node.content, node.location:format());
outp:addLine(")", node.location:format());
} elseif (node.kind == 'NullLiteral') {
outp:addLine("(nil)", node.location:format());
} elseif (node.kind == 'Call') {
outp:addLine("(", node.location:format());
Debug.outputExpression(outp, node.content[1]);
outp:addLine(")(", node.location:format());
var args = node.content[2];
for (var i = 1; i <= #args; i += 1;) {
var arg = args[i];
Debug.outputExpression(outp, arg);
if (i != #args) {
outp:addLine(",", node.location:format());
}
}
outp:addLine(")", node.location:format());
} elseif (node.kind == 'MethodCall') {
outp:addLine("(", node.location:format());
Debug.outputExpression(outp, node.content[1]);
outp:addLine("):", node.location:format());
outp:addLine(node.content[2] .. "(", node.location:format());
var args = node.content[3];
for (var i = 1; i <= #args; i += 1;) {
var arg = args[i];
Debug.outputExpression(outp, arg);
if (i != #args) {
outp:addLine(",", node.location:format());
}
}
outp:addLine(")", node.location:format());
}
}
fn Debug.outputFunctionName(outp, node) {
if (node.kind == 'Field') {
var left = node.content[1];
var right = node.content[2];
Debug.outputFunctionName(outp, left);
outp:addLine(".", node.location:format());
outp:addLine(right, node.location:format());
} elseif (node.kind == 'Index') {
var left = node.content[1];
var right = node.content[2];
Debug.outputFunctionName(outp, left);
outp:addLine("[", node.location:format());
Debug.outputExpression(outp, right);
outp:addLine("]", node.location:format());
} elseif (node.kind == 'Variable') {
outp:addLine(node.content, node.location:format());
} elseif (node.kind == 'Method') {
var left = node.content[1];
var right = node.content[2];
Debug.outputFunctionName(outp, left);
outp:addLine(":", node.location:format());
outp:addLine(right, node.location:format());
}
}
fn Debug.outputVariable(outp, node) {
if (node.kind == 'Field') {
var left = node.content[1];
var right = node.content[2];
Debug.outputExpression(outp, left);
outp:addLine(".", node.location:format());
outp:addLine(right, node.location:format());
} elseif (node.kind == 'Index') {
var left = node.content[1];
var right = node.content[2];
Debug.outputExpression(outp, left);
outp:addLine("[", node.location:format());
Debug.outputExpression(outp, right);
outp:addLine("]", node.location:format());
} elseif (node.kind == 'Variable') {
outp:addLine(node.content, node.location:format());
}
}
RegisterOutMethod("debug", Debug);