Small = {}; fn Small.isDigit(ch) { var n = string.byte(ch); if (!n) return false; return n >= 48 && n <= 57; } fn Small.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 (ch == "\n") out ..= '\n'; elseif (ch == "\r") out ..= '\r'; elseif (ch == "\x07") out ..= '\a'; elseif (ch == "\x08") out ..= '\b'; elseif (ch == "\x0c") out ..= '\f'; elseif (ch == "\t") out ..= '\t'; elseif (code >= 32 && code <= 126) out ..= ch; else { var nextchar = raw:sub(i+1,i+1); if (Small.isDigit(nextchar)) out ..= '\' .. DecEncode(code, 3); else out ..= '\' .. DecEncode(code, 1); } } out ..= '"'; return out; } fn Small.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 Small.convertBinaryOperator(lakeop, spacer) { 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 == "&&" ) { if (spacer) return " and "; else return "and"; } elseif ( lakeop == "||" ) { if (spacer) return " or "; else return 'or'; } } fn Small.convertUnaryOperator(op) { if (op == '#') return '#'; elseif (op == '-') return '-'; elseif (op == '!') return 'not'; } fn Small.output(outp, ast) { return Small.outputStatement(outp, ast); } fn Small.outputStatement(outp, node, noblock) { if (node.kind == 'Program') { outp:appendLine('do ', ''); for (var i = 1; i <= #node.content; i += 1;) { var stmt = node.content[i]; Small.outputStatement(outp, stmt, #node.content == 1); } outp:appendLine('end;', ''); } elseif (node.kind == 'VariableDeclaration') { outp:appendLine('local ', ''); var varnames = node.content[1]; for (var i = 1; i <= #varnames; i += 1;) { var vname = varnames[i]; outp:appendLine(vname[1], ''); if (i != #varnames) { outp:appendLine(',', ''); } } var values = node.content[2]; if (#values > 0) { outp:appendLine('=', ''); for (var i = 1; i <= #values; i += 1;) { var val = values[i]; Small.outputExpression(outp, val, false); if (i != #values) { outp:appendLine(',', ''); } } } outp:appendLine(';', ''); } elseif (node.kind == 'Block') { if (!noblock) outp:appendLine('do ', ''); for (var i = 1; i <= #node.content; i += 1;) { var stmt = node.content[i]; Small.outputStatement(outp, stmt, i == #node.content); } if (!noblock) outp:appendLine('end;', ''); } elseif (node.kind == 'IfStatement') { outp:appendLine("if ", ''); Small.outputExpression(outp, node.content[1][1][1]); outp:appendLine("then ", ''); Small.outputStatement(outp, node.content[1][1][2], true); for (var i = 2; i <= #node.content[1]; i += 1;) { var item = node.content[1][i]; outp:appendLine("elseif ", ''); Small.outputExpression(outp,item[1]); outp:appendLine("then ", ''); Small.outputStatement(outp,item[2], true); } if (node.content[2]) { outp:appendLine("else ", ''); Small.outputStatement(outp, node.content[2], true); } outp:appendLine("end;", ''); } elseif (node.kind == 'FunctionDeclaration') { outp:appendLine("function ", ''); Small.outputFunctionName(outp, node.content[1]); outp:appendLine("(", ''); for (var i = 1; i <= #node.content[2]; i += 1;) { var part = node.content[2][i]; outp:appendLine(part, ''); if (i != #node.content[2]) { outp:appendLine(",", ''); } } outp:appendLine(")", ''); Small.outputStatement(outp, node.content[3], true); outp:appendLine("end;", ''); } elseif (node.kind == 'LocalFunctionDeclaration') { outp:appendLine("local function ", ''); outp:appendLine(node.content[1].content, ''); outp:appendLine("(", ''); for (var i = 1; i <= #node.content[2]; i += 1;) { var part = node.content[2][i]; outp:appendLine(part, ''); if (i != #node.content[2]) { outp:appendLine(",", ''); } } outp:appendLine(")", ''); Small.outputStatement(outp, node.content[3], true); outp:appendLine("end;", ''); } elseif (node.kind == 'Return') { if (!noblock) { outp:appendLine("if''then "); } if (#node.content > 0) { outp:appendLine("return ", ''); } else { outp:appendLine('return', ''); } for (var i = 1; i <= #node.content; i += 1;) { var expr = node.content[i]; Small.outputExpression(outp, expr, false); if (i != #node.content) { outp:appendLine(",", ''); } } outp:appendLine(";", ''); if (!noblock) { outp:appendLine("end;"); } } 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]; Small.outputVariable(outp, vari); if (i != #vars) { outp:appendLine(",", ''); } } outp:appendLine("=", ''); for (var i = 1; i <= #vals; i += 1;) { var val = vals[i]; Small.outputExpression(outp, val, false); if (i != #vals) { outp:appendLine(",", ''); } } outp:appendLine(";", ''); } elseif (node.kind == 'Call') { outp:appendLine("(", ''); Small.outputExpression(outp, node.content[1], false); outp:appendLine(")", ''); outp:appendLine("(", ''); var args = node.content[2]; for (var i = 1; i <= #args; i += 1;) { var arg = args[i]; Small.outputExpression(outp, arg, false); if (i != #args) { outp:appendLine(",", ''); } } outp:appendLine(")", ''); outp:appendLine(";", ''); } elseif (node.kind == 'MethodCall') { outp:appendLine("(", ''); Small.outputExpression(outp, node.content[1], false); outp:appendLine("):", ''); outp:appendLine(node.content[2], ''); outp:appendLine("(", ''); var args = node.content[3]; for (var i = 1; i <= #args; i += 1;) { var arg = args[i]; Small.outputExpression(outp, arg, false); if (i != #args) { outp:appendLine(",", ''); } } outp:appendLine(")", ''); outp:appendLine(";", ''); } elseif (node.kind == 'While') { outp:appendLine("while ", ''); Small.outputExpression(outp, node.content[1]); outp:appendLine("do ", ''); Small.outputStatement(outp, node.content[2], true); outp:appendLine("end;", ''); } elseif (node.kind == 'For') { outp:appendLine("do ", ''); Small.outputStatement(outp,node.content[1]); outp:appendLine("while ", ''); Small.outputExpression(outp,node.content[2]); outp:appendLine("do ", ''); outp:appendLine("do ", ''); Small.outputStatement(outp,node.content[4], true); outp:appendLine("end;", ''); Small.outputStatement(outp,node.content[3], true); outp:appendLine("end;", ''); outp:appendLine("end;", ''); } elseif (node.kind == 'OperatorAssignment') { var lakeop = node.content[2]; var op = Small.convertBinaryOperator(lakeop, true); var lhs = node.content[1]; var rhs = node.content[3]; if (lhs.kind == "Variable") { var varname = lhs.content; outp:appendLine(varname, ''); outp:appendLine('=', ''); outp:appendLine(varname, ''); outp:appendLine(op, ''); outp:appendLine('(', ''); Small.outputExpression(outp, rhs, false); outp:appendLine(');', ''); } elseif (lhs.kind == "Field") { outp:appendLine('do ', ''); outp:appendLine('local _lake_oat=', ''); Small.outputExpression(outp, lhs.content[1], false); outp:appendLine(';', ''); outp:appendLine('_lake_oat.', ''); outp:appendLine(lhs.content[2], ''); outp:appendLine('=', ''); outp:appendLine('_lake_oat.', ''); outp:appendLine(lhs.content[2], ''); outp:appendLine(op, ''); outp:appendLine('(', ''); Small.outputExpression(outp, rhs, false); outp:appendLine(');', ''); outp:appendLine('end;', ''); } elseif (lhs.kind == "Index") { outp:appendLine('do ', ''); outp:appendLine('local _lake_oat=', ''); Small.outputExpression(outp, lhs.content[1], false); outp:appendLine(';', ''); outp:appendLine('_lake_oat[', ''); Small.outputExpression(outp, lhs.content[2], false); outp:appendLine(']', ''); outp:appendLine('=', ''); outp:appendLine('_lake_oat[', ''); Small.outputExpression(outp, lhs.content[2], false); outp:appendLine(']', ''); outp:appendLine(op, ''); outp:appendLine('(', ''); Small.outputExpression(outp, rhs, false); outp:appendLine(');', ''); outp:appendLine('end;', ''); } } elseif (node.kind == 'Break') { outp:appendLine('break;', ''); } elseif (node.kind == 'Foreach') { outp:appendLine("for ", ''); var varlist,exprlist,block = node.content[1],node.content[2],node.content[3]; for (var i = 1; i <= #varlist; i += 1;) { outp:appendLine(varlist[i], ''); if (i != #varlist) { outp:appendLine(',', ''); } } outp:appendLine(" in ", ''); for (var i = 1; i <= #exprlist; i += 1;) { Small.outputExpression(outp, exprlist[i], i == #exprlist); if (i != #exprlist) { outp:appendLine(",", ''); } } outp:appendLine('do ', ''); Small.outputStatement(outp, block, true); outp:appendLine('end;', ''); } } fn Small.outputExpression(outp, node, needsp) { if (needsp == null) needsp = true; if (node.kind == 'StringLiteral') { var lakestr = node.content; var raw = Lower.decodeString(lakestr); var enc = Small.encodeString(raw, outp.target == "lua51"); outp:appendLine(enc, ''); } elseif (node.kind == 'CompiledStringLiteral') { var raw = node.content; var enc = Small.encodeString(raw, outp.target == "lua51"); outp:appendLine(enc, ''); } elseif (node.kind == 'ParenthesizedExpression') { outp:appendLine("(", ''); Small.outputExpression(outp, node.content, false); outp:appendLine(")", ''); } elseif (node.kind == 'NumberLiteral') { var lakenum = node.content; var raw = Lower.decodeNumber(lakenum); var enc = Small.encodeNumber(raw); outp:appendLine(enc, ''); if (needsp) outp:appendLine(' ', ''); } elseif (node.kind == 'CompiledNumberLiteral') { var raw = node.content; var enc = Small.encodeNumber(raw); outp:appendLine(enc, ''); if (needsp) outp:appendLine(' ', ''); } elseif (node.kind == 'TableLiteral') { outp:appendLine('{', ''); var listpart = node.content[1]; var tablepart = node.content[2]; for (var i = 1; i <= #listpart; i += 1;) { var item = listpart[i]; Small.outputExpression(outp, item, false); if ((#tablepart > 0) || (i < #listpart)) { outp:appendLine(',', ''); } } for (var i = 1; i <= #tablepart; i += 1;) { var item = tablepart[i]; outp:appendLine('[', ''); Small.outputExpression(outp, item[1], false); outp:appendLine(']', ''); outp:appendLine('=', ''); Small.outputExpression(outp, item[2], false); if (i < #tablepart) { outp:appendLine(',', ''); } } outp:appendLine('}', ''); } elseif (node.kind == 'LambdaFunctionLiteral') { outp:appendLine('function(', ''); for (var i = 1; i <= #node.content[1]; i += 1;) { var part = node.content[1][i]; outp:appendLine(part, ''); if (i != #node.content[1]) { outp:appendLine(",", ''); } } outp:appendLine(")", ''); Small.outputStatement(outp, node.content[2], true); outp:appendLine("end", ''); if (needsp) outp:appendLine(' ', ''); } elseif (node.kind == 'BinaryOperator') { var op = node.content[1]; var lhs = node.content[2]; var rhs = node.content[3]; outp:appendLine("((", ''); Small.outputExpression(outp,lhs, false); outp:appendLine(")", ''); var luaop = Small.convertBinaryOperator(op); outp:appendLine(luaop, ''); outp:appendLine("(", ''); Small.outputExpression(outp,rhs,false); outp:appendLine("))", ''); } elseif (node.kind == 'UnaryOperator') { var op = node.content[1]; var val = node.content[2]; outp:appendLine("(", ''); var luaop = Small.convertUnaryOperator(op); outp:appendLine(luaop, ''); outp:appendLine("(", ''); Small.outputExpression(outp,val,false); outp:appendLine("))", ''); } elseif (node.kind == 'Field') { var left = node.content[1]; var right = node.content[2]; outp:appendLine("(", ''); Small.outputExpression(outp, left, false); outp:appendLine(").", ''); outp:appendLine(right, ''); if (needsp) outp:appendLine(' ', ''); } elseif (node.kind == 'Index') { var left = node.content[1]; var right = node.content[2]; outp:appendLine("(", ''); Small.outputExpression(outp, left, false); outp:appendLine(")", ''); outp:appendLine("[", ''); Small.outputExpression(outp, right, false); outp:appendLine("]", ''); } elseif (node.kind == 'Variable') { outp:appendLine(node.content, ''); if (needsp) outp:appendLine(' ', ''); } elseif (node.kind == 'BooleanLiteral') { outp:appendLine(node.content, ''); if (needsp) outp:appendLine(' ', ''); } elseif (node.kind == 'NullLiteral') { outp:appendLine("nil", ''); if (needsp) outp:appendLine(' ', ''); } elseif (node.kind == 'Call') { outp:appendLine("(", ''); Small.outputExpression(outp, node.content[1], false); outp:appendLine(")", ''); outp:appendLine("(", ''); var args = node.content[2]; for (var i = 1; i <= #args; i += 1;) { var arg = args[i]; Small.outputExpression(outp, arg, false); if (i != #args) { outp:appendLine(",", ''); } } outp:appendLine(")", ''); } elseif (node.kind == 'MethodCall') { outp:appendLine("(", ''); Small.outputExpression(outp, node.content[1], false); outp:appendLine("):", ''); outp:appendLine(node.content[2], ''); outp:appendLine("(", ''); var args = node.content[3]; for (var i = 1; i <= #args; i += 1;) { var arg = args[i]; Small.outputExpression(outp, arg, false); if (i != #args) { outp:appendLine(",", ''); } } outp:appendLine(")", ''); } } fn Small.outputFunctionName(outp, node) { if (node.kind == "Field") { var left = node.content[1]; var right = node.content[2]; Small.outputFunctionName(outp, left); outp:appendLine(".", ''); outp:appendLine(right, ''); } elseif (node.kind == "Index") { var left = node.content[1]; var right = node.content[2]; Small.outputFunctionName(outp, left); outp:appendLine("[", ''); Small.outputExpression(outp, right, false); outp:appendLine("]", ''); } elseif (node.kind == "Variable") { outp:appendLine(node.content, ''); } elseif (node.kind == "Method") { var left = node.content[1]; var right = node.content[2]; Small.outputFunctionName(outp, left); outp:appendLine(":", ''); outp:appendLine(right, ''); } } fn Small.outputVariable(outp, node) { if (node.kind == "Field") { var left = node.content[1]; var right = node.content[2]; Small.outputExpression(outp, left, false); outp:appendLine(".", ''); outp:appendLine(right, ''); } elseif (node.kind == "Index") { var left = node.content[1]; var right = node.content[2]; Small.outputExpression(outp, left, false); outp:appendLine("[", ''); Small.outputExpression(outp, right, false); outp:appendLine("]", ''); } elseif (node.kind == "Variable") { outp:appendLine(node.content, ''); } } RegisterOutMethod('small', Small);