Small = {} function Small.isDigit(ch) local n = string.byte(ch) if not n then return false end return n >= 48 and n <= 57 end function Small.encodeString(raw, islua51) local out = '"' for i = 1,#raw do local ch = raw:sub(i,i) local code = string.byte(ch) if ch == '\\' then out = out .. "\\\\" elseif ch == '"' then out = out .. '\\"' elseif ch == "\n" then out = out .. "\\n" elseif ch == '\r' then out = out .. "\\r" elseif ch == "\a" then out = out .. "\\a" elseif ch == "\b" then out = out .. "\\b" elseif ch == '\f' then out = out .. "\\f" elseif ch == "\t" then out = out .. "\\t" elseif code >= 32 and code <= 126 then out = out .. ch else local nextchar = raw:sub(i+1,i+1) if Small.isDigit(nextchar) then out = out .. "\\" .. DecEncode(code, 3) else out = out .. "\\" .. DecEncode(code, 1) end end end out = out .. '"' return out end function Small.encodeNumber(n) local num = '' local isneg = false if n < 0 then n = n * -1 isneg = true; end local whole = math.floor(n) if whole == 0 then num = num .. '0' end while whole > 0 do local digit = whole % 10 num = string.char(string.byte'0' + digit) .. num whole = math.floor(whole / 10) end local dec = n % 1 if dec > 0 then num = num .. "." while dec > 0 do -- aw nah dec = dec * 10 local digit = math.floor(dec) num = num .. string.char(string.byte'0' + digit) dec = dec % 1 end end if isneg then num = '-' .. num end return num end function Small.convertBinaryOperator(lakeop, spacer) if lakeop == "+" then return "+" elseif lakeop == "-" then return "-" elseif lakeop == "*" then return "*" elseif lakeop == "/" then return "/" elseif lakeop == "%" then return "%" elseif lakeop == "^" then return "^" elseif lakeop == ".." then return ".." elseif lakeop == "<" then return "<" elseif lakeop == ">" then return ">" elseif lakeop == "<=" then return "<=" elseif lakeop == ">=" then return ">=" elseif lakeop == "!=" then return "~=" elseif lakeop == "==" then return "==" elseif lakeop == "&&" then return spacer and ' and ' or "and" elseif lakeop == "||" then return spacer and ' or ' or "or" end end function Small.convertUnaryOperator(op) if op == "#" then return "#" elseif op == "-" then return "-" elseif op == "!" then return "not" end end function Small.output(outp, ast) Small.outputStatement(outp, ast) end ---@param outp Output ---@param node Node function Small.outputStatement(outp, node, noblock) if node.kind == "Program" then outp:appendLine("do ", '') for i = 1,#node.content do local stmt = node.content[i] Small.outputStatement(outp, stmt) end outp:appendLine("end;", '') elseif node.kind == "VariableDeclaration" then outp:appendLine("local ", '') local varnames = node.content[1] for i = 1,#varnames do local vname = varnames[i] outp:appendLine(vname[1], '') if i ~= #varnames then outp:appendLine(",", '') end end local values = node.content[2] if #values > 0 then outp:appendLine("=", '') for i = 1,#values do local val = values[i] Small.outputExpression(outp, val, false) if i ~= #values then outp:appendLine(",", '') end end end outp:appendLine(";", '') elseif node.kind == "Block" then if not noblock then outp:appendLine("do ", '') end for i = 1,#node.content do local stmt = node.content[i] Small.outputStatement(outp, stmt, #node.content == 1) end if not noblock then outp:appendLine("end;", '') end elseif node.kind == "IfStatement" then outp:appendLine("if ", '') Small.outputExpression(outp, node.content[1][1][1]) outp:appendLine("then ", '') Small.outputStatement(outp, node.content[1][1][2], true) for i = 2, #node.content[1] do local item = node.content[1][i] -- print(item, item[1], item[2]) outp:appendLine("elseif ", '') Small.outputExpression(outp,item[1]); outp:appendLine("then ", '') Small.outputStatement(outp,item[2], true); end if node.content[2] then outp:appendLine("else ", '') Small.outputStatement(outp, node.content[2], true) end outp:appendLine("end;", '') elseif node.kind == "FunctionDeclaration" then outp:appendLine("function ", '') Small.outputFunctionName(outp, node.content[1]); outp:appendLine("(", '') for i = 1,#node.content[2] do local part = node.content[2][i] outp:appendLine(part, '') if i ~= #node.content[2] then outp:appendLine(",", '') end end outp:appendLine(")", '') Small.outputStatement(outp, node.content[3], true) outp:appendLine("end;", '') elseif node.kind == "LocalFunctionDeclaration" then outp:appendLine("local function ", '') outp:appendLine(node.content[1].content, ''); outp:appendLine("(", '') for i = 1,#node.content[2] do local part = node.content[2][i] outp:appendLine(part, '') if i ~= #node.content[2] then outp:appendLine(",", '') end end outp:appendLine(")", '') Small.outputStatement(outp, node.content[3], true) outp:appendLine("end;", '') elseif node.kind == "Return" then if #node.content > 0 then outp:appendLine("return ", '') else outp:appendLine('return', '') end for i = 1,#node.content do local expr = node.content[i] Small.outputExpression(outp, expr, false) if i ~= #node.content then outp:appendLine(",", '') end end outp:appendLine(";", '') elseif node.kind == "Assignment" then local vars = node.content[1] local vals = node.content[2] for i = 1,#vars do local var = vars[i] Small.outputVariable(outp, var) if i ~= #vars then outp:appendLine(",", '') end end outp:appendLine("=", '') for i = 1,#vals do local val = vals[i] Small.outputExpression(outp, val, false) if i ~= #vals then outp:appendLine(",", '') end end outp:appendLine(";", '') elseif node.kind == "Call" then outp:appendLine("(", '') Small.outputExpression(outp, node.content[1], false) outp:appendLine(")", '') outp:appendLine("(", '') local args = node.content[2] for i = 1,#args do local arg = args[i] Small.outputExpression(outp, arg, false) if i ~= #args then outp:appendLine(",", '') end end outp:appendLine(")", '') outp:appendLine(";", '') elseif node.kind == "MethodCall" then outp:appendLine("(", '') Small.outputExpression(outp, node.content[1], false) outp:appendLine("):", '') outp:appendLine(node.content[2], '') outp:appendLine("(", '') local args = node.content[3] for i = 1,#args do local arg = args[i] Small.outputExpression(outp, arg, false) if i ~= #args then outp:appendLine(",", '') end end outp:appendLine(")", '') outp:appendLine(";", '') elseif node.kind == "While" then 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" then 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" then local lake_op = node.content[2] local op = Small.convertBinaryOperator(lake_op, true) local lhs = node.content[1] local rhs = node.content[3] if lhs.kind == "Variable" then local 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" then 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" then 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;', '') end elseif node.kind == "Break" then outp:appendLine('break;', '') elseif node.kind == "Foreach" then outp:appendLine("for ", '') local varlist,exprlist,block = node.content[1],node.content[2],node.content[3] for i = 1,#varlist do outp:appendLine(varlist[i], '') if i ~= #varlist then outp:appendLine(',', '') end end outp:appendLine(" in ", '') for i = 1,#exprlist do Small.outputExpression(outp, exprlist[i], i == #exprlist) if i ~= #exprlist then outp:appendLine(",", '') end end outp:appendLine('do ', '') Small.outputStatement(outp, block, true); outp:appendLine('end;', '') end end ---@param outp Output ---@param node Node function Small.outputExpression(outp, node, needsp) if needsp == nil then needsp = true end if node.kind == "StringLiteral" then local lakestr = node.content local raw = Lower.decodeString(lakestr) local enc = Small.encodeString(raw, outp.target == "lua51") outp:appendLine(enc, '') elseif node.kind == "ParenthesizedExpression" then outp:appendLine("(", '') Small.outputExpression(outp, node.content, false) outp:appendLine(")", '') elseif node.kind == "NumberLiteral" then local lakenum = node.content local raw = Lower.decodeNumber(lakenum) -- print(raw, type(raw)) local enc = Small.encodeNumber(raw) outp:appendLine(enc, '') if needsp then outp:appendLine(' ', '') end elseif node.kind == "TableLiteral" then outp:appendLine('{', '') local list_part = node.content[1] local table_part = node.content[2] for i = 1,#list_part do local item = list_part[i] Small.outputExpression(outp, item, false); if (#table_part > 0) or (i < #list_part) then outp:appendLine(',', '') end end for i = 1,#table_part do local item = table_part[i] outp:appendLine('[', '') Small.outputExpression(outp, item[1], false) outp:appendLine(']', '') outp:appendLine('=', '') Small.outputExpression(outp, item[2], false) if (i < #table_part) then outp:appendLine(',', '') end end outp:appendLine('}', '') elseif node.kind == "LambdaFunctionLiteral" then outp:appendLine('function(', '') for i = 1,#node.content[1] do local part = node.content[1][i] outp:appendLine(part, '') if i ~= #node.content[1] then outp:appendLine(",", '') end end outp:appendLine(")", '') Small.outputStatement(outp, node.content[2], true) outp:appendLine("end", '') if needsp then outp:appendLine(' ', '') end elseif node.kind == "BinaryOperator" then local op = node.content[1] local lhs = node.content[2] local rhs = node.content[3] outp:appendLine("((", '') Small.outputExpression(outp,lhs, false) outp:appendLine(")", '') local luaop = Small.convertBinaryOperator(op) outp:appendLine(luaop, '') outp:appendLine("(", '') Small.outputExpression(outp,rhs,false) outp:appendLine("))", '') elseif node.kind == "UnaryOperator" then local op = node.content[1] local val = node.content[2] outp:appendLine("(", '') local luaop = Small.convertUnaryOperator(op) outp:appendLine(luaop, '') outp:appendLine("(", '') Small.outputExpression(outp,val,false) outp:appendLine("))", '') elseif node.kind == "Field" then local left = node.content[1] local right = node.content[2] outp:appendLine("(", '') Small.outputExpression(outp, left, false) outp:appendLine(").", '') outp:appendLine(right, '') if needsp then outp:appendLine(' ', '') end elseif node.kind == "Index" then local left = node.content[1] local 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" then outp:appendLine(node.content, '') if needsp then outp:appendLine(' ', '') end elseif node.kind == "BooleanLiteral" then outp:appendLine(node.content, '') if needsp then outp:appendLine(' ', '') end elseif node.kind == "NullLiteral" then outp:appendLine("nil", '') if needsp then outp:appendLine(' ', '') end elseif node.kind == "Call" then outp:appendLine("(", '') Small.outputExpression(outp, node.content[1], false) outp:appendLine(")", '') outp:appendLine("(", '') local args = node.content[2] for i = 1,#args do local arg = args[i] Small.outputExpression(outp, arg, false) if i ~= #args then outp:appendLine(",", '') end end outp:appendLine(")", '') elseif node.kind == "MethodCall" then outp:appendLine("(", '') Small.outputExpression(outp, node.content[1], false) outp:appendLine("):", '') outp:appendLine(node.content[2], '') outp:appendLine("(", '') local args = node.content[3] for i = 1,#args do local arg = args[i] Small.outputExpression(outp, arg, false) if i ~= #args then outp:appendLine(",", '') end end outp:appendLine(")", '') end end ---@param outp Output ---@param node Node function Small.outputFunctionName(outp, node) if node.kind == "Field" then local left = node.content[1] local right = node.content[2] Small.outputFunctionName(outp, left) outp:appendLine(".", '') outp:appendLine(right, '') elseif node.kind == "Index" then local left = node.content[1] local right = node.content[2] Small.outputFunctionName(outp, left) outp:appendLine("[", '') Small.outputExpression(outp, right, false) outp:appendLine("]", '') elseif node.kind == "Variable" then outp:appendLine(node.content, '') elseif node.kind == "Method" then local left = node.content[1] local right = node.content[2] Small.outputFunctionName(outp, left) outp:appendLine(":", '') outp:appendLine(right, '') end end ---@param outp Output ---@param node Node function Small.outputVariable(outp, node) if node.kind == "Field" then local left = node.content[1] local right = node.content[2] Small.outputExpression(outp, left, false) outp:appendLine(".", '') outp:appendLine(right, '') elseif node.kind == "Index" then local left = node.content[1] local right = node.content[2] Small.outputExpression(outp, left, false) outp:appendLine("[", '') Small.outputExpression(outp, right, false) outp:appendLine("]", '') elseif node.kind == "Variable" then outp:appendLine(node.content, '') end end RegisterOutMethod("small", Small)