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);