Files
Lake/src/methods/small.lua
2026-04-25 16:59:45 +02:00

720 lines
15 KiB
Lua

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)