bootstrap compiler
This commit is contained in:
48
main.lua
Normal file
48
main.lua
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
require('src.util')
|
||||||
|
|
||||||
|
require("src.types")
|
||||||
|
require("src.lexer")
|
||||||
|
require("src.parser")
|
||||||
|
require("src.lower")
|
||||||
|
require("src.output")
|
||||||
|
require("src.methods.debug")
|
||||||
|
require("src.methods.small")
|
||||||
|
|
||||||
|
require("src.cli")
|
||||||
|
|
||||||
|
-- local file = assert(io.open("code.lake", "r"))
|
||||||
|
-- local code = file:read("*all")
|
||||||
|
|
||||||
|
-- local lexer = Lexer:new(code, "test.lake")
|
||||||
|
|
||||||
|
-- local lexer2 = Lexer:new(code, "test.lake")
|
||||||
|
-- while true do
|
||||||
|
-- local tok, err = lexer2:consume()
|
||||||
|
-- if not tok then
|
||||||
|
-- error("oh no " .. tostring(err.message))
|
||||||
|
-- end
|
||||||
|
|
||||||
|
-- print(tok.kind, tok.content)
|
||||||
|
|
||||||
|
-- if tok.kind == "eof" then break end
|
||||||
|
-- end
|
||||||
|
|
||||||
|
-- print'\n\n'
|
||||||
|
|
||||||
|
-- local parser = Parser:new(lexer)
|
||||||
|
|
||||||
|
-- local program, err = parser:parse()
|
||||||
|
|
||||||
|
-- if not program then
|
||||||
|
-- error(err.message)
|
||||||
|
-- end
|
||||||
|
|
||||||
|
-- ERGON = require("ERGON")
|
||||||
|
|
||||||
|
-- print(ERGON.encodeIndented(program, 1, " "))
|
||||||
|
|
||||||
|
-- local output = Output:new("lua54", "debug")
|
||||||
|
|
||||||
|
-- output:output(program)
|
||||||
|
-- print'\n\n'
|
||||||
|
-- print(table.concat(output.lines, "\n"))
|
||||||
171
src/cli.lua
Normal file
171
src/cli.lua
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
|
||||||
|
---@param args string[]
|
||||||
|
---@param argConfig table<string, string[]>
|
||||||
|
function ParseArgData(args, argConfig)
|
||||||
|
local i = 1
|
||||||
|
|
||||||
|
local knorps = {}
|
||||||
|
local flarples = {}
|
||||||
|
|
||||||
|
for k,v in pairs(argConfig) do
|
||||||
|
if v[1] == "flag" then
|
||||||
|
flarples[v[2]] = false
|
||||||
|
elseif v[1] == "list" then
|
||||||
|
flarples[v[2]] = {}
|
||||||
|
elseif v[1] == "input" then
|
||||||
|
-- no
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
while i <= #args do
|
||||||
|
local argument = args[i]
|
||||||
|
|
||||||
|
if argConfig[argument] then
|
||||||
|
local argconf = argConfig[argument]
|
||||||
|
|
||||||
|
local argtype = argconf[1]
|
||||||
|
local storageplace = argconf[2]
|
||||||
|
|
||||||
|
if argtype == "flag" then
|
||||||
|
flarples[storageplace] = true
|
||||||
|
elseif argtype == "input" then
|
||||||
|
local next = args[i+1]
|
||||||
|
i = i + 1 -- don't get it checked as a flag because that'd be rerarded
|
||||||
|
|
||||||
|
if not next then
|
||||||
|
error("gdsiohjubhoupidsgohupigdshoupigsdhpoid")
|
||||||
|
end
|
||||||
|
|
||||||
|
flarples[storageplace] = next
|
||||||
|
elseif argtype == "list" then
|
||||||
|
local next = args[i+1]
|
||||||
|
i = i + 1 -- don't get it checked as a flag because that'd be rerarded
|
||||||
|
|
||||||
|
if not next then
|
||||||
|
error("gdsiohjubhoupidsgohupigdshoupigsdhpoid")
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(flarples[storageplace], next)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if argument:sub(1,1) == "-" then
|
||||||
|
error("fuck you")
|
||||||
|
end
|
||||||
|
|
||||||
|
knorps[#knorps+1] = argument
|
||||||
|
end
|
||||||
|
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
return knorps, flarples
|
||||||
|
end
|
||||||
|
|
||||||
|
if #arg == 0 then
|
||||||
|
while true do
|
||||||
|
io.write("\x1b[36minput>\x1b[0m ")
|
||||||
|
io.flush()
|
||||||
|
local line = io.read("L")
|
||||||
|
if not line then break end
|
||||||
|
|
||||||
|
local lexer = Lexer:new(line, "stdin")
|
||||||
|
local parser = Parser:new(lexer)
|
||||||
|
|
||||||
|
local ast, err = parser:parse()
|
||||||
|
|
||||||
|
if err then
|
||||||
|
local msg = string.format("%s:%d %s", err.location.file, err.location.line, err.message)
|
||||||
|
print("\x1b[31m" .. msg .. "\x1b[0m")
|
||||||
|
else
|
||||||
|
assert(ast, "incredibly bad no good state very bad crash now")
|
||||||
|
|
||||||
|
-- this is the best code ever made
|
||||||
|
local output = Output:new("lua54", "small")
|
||||||
|
output:output(ast)
|
||||||
|
local outdata = table.concat(output.lines, '\n')
|
||||||
|
print(outdata)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if arg[1] == "compile" then
|
||||||
|
table.remove(arg, 1)
|
||||||
|
|
||||||
|
local options = {
|
||||||
|
['-v'] = {'flag', 'verbose'},
|
||||||
|
['-o'] = {'input', 'output'},
|
||||||
|
['-s'] = {'input', 'sourcemap'},
|
||||||
|
-- ['-d'] = {'list', 'dump'},
|
||||||
|
-- ['-D'] = {'input', 'dependency'},
|
||||||
|
['-m'] = {'input', 'method'},
|
||||||
|
['-t'] = {'input', 'target'}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
local knorps, flarples = ParseArgData(arg, options)
|
||||||
|
flarples.target = flarples.target or "lua54"
|
||||||
|
flarples.method = flarples.method or "debug"
|
||||||
|
|
||||||
|
if not flarples.output then
|
||||||
|
print("\x1b[31mno output file given\x1b[0m")
|
||||||
|
os.exit(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local outfile = io.open(flarples.output, "w")
|
||||||
|
if not outfile then
|
||||||
|
print("\x1b[31moutput file not writable\x1b[0m")
|
||||||
|
os.exit(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local SMfile
|
||||||
|
if flarples.sourcemap then
|
||||||
|
SMfile = io.open(flarples.sourcemap, 'w')
|
||||||
|
if not SMfile then
|
||||||
|
print("\x1b[31msourcemap file not writable\x1b[0m")
|
||||||
|
os.exit(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 1,#knorps do
|
||||||
|
local file = io.open(knorps[i],"r")
|
||||||
|
if not file then
|
||||||
|
print("\x1b[31mcould not read file " .. tostring(knorps[i]) .."\x1b[0m")
|
||||||
|
os.exit(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local code = file:read("*all")
|
||||||
|
file:close()
|
||||||
|
|
||||||
|
local lexer = Lexer:new(code, knorps[i])
|
||||||
|
|
||||||
|
local parser = Parser:new(lexer)
|
||||||
|
|
||||||
|
local ast,err = parser:parse()
|
||||||
|
if not ast then
|
||||||
|
if err then
|
||||||
|
err:print()
|
||||||
|
else
|
||||||
|
print("\x1b[31m!!! an unknown error occured !!!\x1b[0m")
|
||||||
|
end
|
||||||
|
os.exit(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local output = Output:new(flarples.target, flarples.method)
|
||||||
|
|
||||||
|
output:output(ast)
|
||||||
|
|
||||||
|
local outdata = table.concat(output.lines, '\n')
|
||||||
|
local smdata = table.concat(output.debug, '\n')
|
||||||
|
|
||||||
|
outfile:write(outdata);
|
||||||
|
if SMfile then
|
||||||
|
SMfile:write(smdata)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
outfile:close()
|
||||||
|
if SMfile then
|
||||||
|
SMfile:close()
|
||||||
|
end
|
||||||
|
end
|
||||||
321
src/lexer.lua
Normal file
321
src/lexer.lua
Normal file
@@ -0,0 +1,321 @@
|
|||||||
|
---@class Lexer
|
||||||
|
---@field cache Token?
|
||||||
|
---@field cursor integer
|
||||||
|
---@field location Location
|
||||||
|
---@field code string
|
||||||
|
Lexer = {}
|
||||||
|
|
||||||
|
Lexer.keywords = {
|
||||||
|
"var",
|
||||||
|
"return",
|
||||||
|
"fn",
|
||||||
|
"true",
|
||||||
|
"false",
|
||||||
|
"null",
|
||||||
|
"if",
|
||||||
|
"var",
|
||||||
|
"else",
|
||||||
|
"elseif",
|
||||||
|
"while",
|
||||||
|
"for",
|
||||||
|
"do",
|
||||||
|
"break",
|
||||||
|
"foreach"
|
||||||
|
}
|
||||||
|
|
||||||
|
Lexer.symbols = {
|
||||||
|
"!=",
|
||||||
|
"==",
|
||||||
|
"<=",
|
||||||
|
">=",
|
||||||
|
"&&=",
|
||||||
|
"||=",
|
||||||
|
"&&",
|
||||||
|
"||",
|
||||||
|
"+=",
|
||||||
|
"-=",
|
||||||
|
"*=",
|
||||||
|
"/=",
|
||||||
|
"%=",
|
||||||
|
"^=",
|
||||||
|
"..=",
|
||||||
|
"..",
|
||||||
|
">",
|
||||||
|
"<",
|
||||||
|
"+",
|
||||||
|
"-",
|
||||||
|
"*",
|
||||||
|
"/",
|
||||||
|
"^",
|
||||||
|
"=",
|
||||||
|
";",
|
||||||
|
"#",
|
||||||
|
"!",
|
||||||
|
"[",
|
||||||
|
"]",
|
||||||
|
".",
|
||||||
|
"{",
|
||||||
|
"}",
|
||||||
|
"(",
|
||||||
|
")",
|
||||||
|
",",
|
||||||
|
"%",
|
||||||
|
':'
|
||||||
|
}
|
||||||
|
|
||||||
|
function Lexer:new(code, fname)
|
||||||
|
return setmetatable({
|
||||||
|
cache = nil,
|
||||||
|
cursor = 1,
|
||||||
|
location = Location:new(fname),
|
||||||
|
code = code,
|
||||||
|
}, {__index = self})
|
||||||
|
end
|
||||||
|
|
||||||
|
function Lexer:next(n)
|
||||||
|
n = n or 1
|
||||||
|
for i = 1,n do
|
||||||
|
self.location:skip(self.code:sub(self.cursor,self.cursor))
|
||||||
|
self.cursor = self.cursor + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Lexer:get(n)
|
||||||
|
n = n or 1
|
||||||
|
return self.code:sub(self.cursor, self.cursor+n-1)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Lexer:isWhitespace(ch)
|
||||||
|
return ch == "\r" or ch == "\n" or ch == "\t" or ch == " "
|
||||||
|
end
|
||||||
|
|
||||||
|
function Lexer:isDigit(ch)
|
||||||
|
local n = string.byte(ch)
|
||||||
|
if not n then return false end
|
||||||
|
return n >= 48 and n <= 57
|
||||||
|
end
|
||||||
|
|
||||||
|
function Lexer:isHex(ch)
|
||||||
|
return (ch:match("%x") ~= nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Lexer:isIdentifierValid(ch)
|
||||||
|
return (ch:match("%a") ~= nil) or (Lexer:isDigit(ch)) or (ch == '_')
|
||||||
|
end
|
||||||
|
|
||||||
|
function Lexer:run()
|
||||||
|
while true do -- skip skippables
|
||||||
|
if self:isWhitespace(self:get()) then
|
||||||
|
self:next()
|
||||||
|
elseif self:get(2) == "//" then -- comment yay wooo i love it i'm so happy yayyyy
|
||||||
|
while (self:get() ~= "\n") and (self:get() ~= "") do
|
||||||
|
self:next()
|
||||||
|
end
|
||||||
|
elseif self:get() == '' then
|
||||||
|
return Token:new("eof", "eof", self.location:clone())
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if self:isDigit(self:get()) then
|
||||||
|
-- number!!!
|
||||||
|
|
||||||
|
local stpos = self.location:clone()
|
||||||
|
local st = ""
|
||||||
|
while self:isDigit(self:get()) do
|
||||||
|
st = st .. self:get()
|
||||||
|
self:next()
|
||||||
|
end
|
||||||
|
|
||||||
|
if self:get() == '.' then
|
||||||
|
-- omg number
|
||||||
|
|
||||||
|
self:next()
|
||||||
|
st = st .. "."
|
||||||
|
|
||||||
|
while self:isDigit(self:get()) do
|
||||||
|
st = st .. self:get()
|
||||||
|
self:next()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return Token:new("number", st, stpos)
|
||||||
|
end
|
||||||
|
|
||||||
|
if self:get() == "'" then -- rawer string
|
||||||
|
local d = "'"
|
||||||
|
local stpos = self.location:clone()
|
||||||
|
self:next()
|
||||||
|
|
||||||
|
while self:get() ~= "'" do
|
||||||
|
local ch = self:get()
|
||||||
|
if ch == '' then break end
|
||||||
|
d = d .. ch
|
||||||
|
self:next()
|
||||||
|
end
|
||||||
|
|
||||||
|
if self:get() ~= "'" then
|
||||||
|
return nil, Error:new("unfinished string", "lexer", self.location:clone())
|
||||||
|
end
|
||||||
|
d = d .. "'"
|
||||||
|
self:next()
|
||||||
|
|
||||||
|
return Token:new("string", d, stpos)
|
||||||
|
end
|
||||||
|
|
||||||
|
if self:get() == '"' then -- not raw string
|
||||||
|
local d = '"'
|
||||||
|
local stpos = self.location:clone()
|
||||||
|
self:next()
|
||||||
|
|
||||||
|
while true do
|
||||||
|
local ch = self:get()
|
||||||
|
if ch == '\\' then
|
||||||
|
d = d .. ch
|
||||||
|
self:next()
|
||||||
|
|
||||||
|
local nch = self:get()
|
||||||
|
if nch == '\\' then
|
||||||
|
d = d .. '\\'
|
||||||
|
self:next()
|
||||||
|
elseif nch == 'n' then
|
||||||
|
d = d .. 'n'
|
||||||
|
self:next()
|
||||||
|
elseif nch == 'r' then
|
||||||
|
d = d .. 'r'
|
||||||
|
self:next()
|
||||||
|
elseif nch == 'v' then
|
||||||
|
d = d .. 'v'
|
||||||
|
self:next()
|
||||||
|
elseif nch == '"' then
|
||||||
|
d = d .. '"'
|
||||||
|
self:next()
|
||||||
|
elseif nch == 'f' then
|
||||||
|
d = d .. 'f'
|
||||||
|
self:next()
|
||||||
|
elseif nch == 't' then
|
||||||
|
d = d .. "t"
|
||||||
|
self:next()
|
||||||
|
elseif nch == 'x' then
|
||||||
|
d = d .. 'x'
|
||||||
|
self:next()
|
||||||
|
|
||||||
|
local hexdata = self:get(2)
|
||||||
|
|
||||||
|
if (not self:isHex(hexdata:sub(1,1))) or (not self:isHex(hexdata:sub(2,2))) then
|
||||||
|
return nil, Error:new("invalid hex digits after x escape code", 'lexer', self.location:clone());
|
||||||
|
end
|
||||||
|
|
||||||
|
d = d .. hexdata
|
||||||
|
self:next(2)
|
||||||
|
elseif nch == "u" then
|
||||||
|
d = d .. "u"
|
||||||
|
self:next()
|
||||||
|
|
||||||
|
if self:get() ~= '{' then
|
||||||
|
return nil, Error:new('expected { after u escape', 'lexer', self.location:clone());
|
||||||
|
end
|
||||||
|
d = d .. "{"
|
||||||
|
self:next()
|
||||||
|
|
||||||
|
local i = 0
|
||||||
|
while self:isHex(self:get()) and (self:get() ~= '') do -- TODO: check validity
|
||||||
|
d = d .. self:get()
|
||||||
|
self:next()
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if i > 0 then
|
||||||
|
local hexpart = d:sub(-i)
|
||||||
|
|
||||||
|
local value = HexDecode(hexpart)
|
||||||
|
|
||||||
|
if value >= 2^31 then
|
||||||
|
return nil, Error:new("invalid utf8 codepoint in u escape", 'lexer', self.location:clone())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if self:get() ~= '}' then
|
||||||
|
return nil, Error:new('expected } to close { in u escape', 'lexer', self.location:clone())
|
||||||
|
end
|
||||||
|
d = d .. "}"
|
||||||
|
self:next()
|
||||||
|
end
|
||||||
|
elseif ch == '' then
|
||||||
|
break;
|
||||||
|
elseif ch == '"' then
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
d = d .. ch; -- assume it's just something you can have in there
|
||||||
|
self:next()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if self:get() ~= '"' then
|
||||||
|
return nil, Error:new('unfinished double-quote string', 'lexer', self.location:clone())
|
||||||
|
end
|
||||||
|
d = d .. '"'
|
||||||
|
self:next()
|
||||||
|
|
||||||
|
return Token:new("string", d, stpos)
|
||||||
|
end
|
||||||
|
|
||||||
|
do
|
||||||
|
local stloc = self.location:clone()
|
||||||
|
for i = 1,#self.symbols do
|
||||||
|
local symbol = self.symbols[i]
|
||||||
|
|
||||||
|
if self:get(#symbol) == symbol then
|
||||||
|
-- ladies and gentlemen, we got him.
|
||||||
|
|
||||||
|
self:next(#symbol)
|
||||||
|
|
||||||
|
return Token:new("symbol", symbol, stloc)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ident!
|
||||||
|
if self:isIdentifierValid(self:get()) then
|
||||||
|
local ident = ""
|
||||||
|
local stpos = self.location:clone()
|
||||||
|
|
||||||
|
while self:isIdentifierValid(self:get()) do
|
||||||
|
ident = ident .. self:get()
|
||||||
|
self:next()
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 1,#self.keywords do
|
||||||
|
local kw = self.keywords[i]
|
||||||
|
|
||||||
|
if ident == kw then
|
||||||
|
return Token:new("keyword", kw, stpos)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return Token:new("identifier", ident, stpos)
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil, Error:new("tried to lex invalid code", "lexer", self.location:clone())
|
||||||
|
end
|
||||||
|
|
||||||
|
function Lexer:peek()
|
||||||
|
if self.cache ~= nil then
|
||||||
|
return self.cache, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local token, err = self:run()
|
||||||
|
self.cache = token
|
||||||
|
return token, err
|
||||||
|
end
|
||||||
|
|
||||||
|
function Lexer:consume()
|
||||||
|
if self.cache == nil then
|
||||||
|
return self:run()
|
||||||
|
end
|
||||||
|
|
||||||
|
local token = self.cache
|
||||||
|
self.cache = nil
|
||||||
|
return token, nil
|
||||||
|
end
|
||||||
129
src/lower.lua
Normal file
129
src/lower.lua
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
Lower = {}
|
||||||
|
|
||||||
|
function Lower.makeUTF8Char(decimal)
|
||||||
|
if decimal < 128 then
|
||||||
|
return string.char(decimal)
|
||||||
|
elseif decimal < 2048 then
|
||||||
|
local byte2 = (128 + (decimal % 64))
|
||||||
|
local byte1 = (192 + math.floor(decimal / 64))
|
||||||
|
return string.char(byte1, byte2)
|
||||||
|
elseif decimal < 65536 then
|
||||||
|
local byte3 = (128 + (decimal % 64))
|
||||||
|
decimal = math.floor(decimal / 64)
|
||||||
|
local byte2 = (128 + (decimal % 64))
|
||||||
|
local byte1 = (224 + math.floor(decimal / 64))
|
||||||
|
return string.char(byte1, byte2, byte3)
|
||||||
|
elseif decimal < 1114112 then
|
||||||
|
local byte4 = (128 + (decimal % 64))
|
||||||
|
decimal = math.floor(decimal / 64)
|
||||||
|
local byte3 = (128 + (decimal % 64))
|
||||||
|
decimal = math.floor(decimal / 64)
|
||||||
|
local byte2 = (128 + (decimal % 64))
|
||||||
|
local byte1 = (240 + math.floor(decimal / 64))
|
||||||
|
return string.char(byte1, byte2, byte3, byte4)
|
||||||
|
else
|
||||||
|
return nil -- Invalid Unicode code point
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Lower.decodeString(lakeString)
|
||||||
|
if lakeString:sub(1,1) == "'" then
|
||||||
|
-- easy
|
||||||
|
return lakeString:sub(2,-2) -- bad way to do it but it works lol lmfao rofl
|
||||||
|
end
|
||||||
|
if lakeString:sub(1,1) == '"' then
|
||||||
|
local i = 2
|
||||||
|
local out = ""
|
||||||
|
while true do
|
||||||
|
local ch = lakeString:sub(i,i)
|
||||||
|
|
||||||
|
if ch == '\\' then
|
||||||
|
i = i + 1
|
||||||
|
local nextch = lakeString:sub(i,i)
|
||||||
|
|
||||||
|
if nextch == '\\' then
|
||||||
|
out = out .. '\\'
|
||||||
|
i = i + 1;
|
||||||
|
elseif nextch == 'n' then
|
||||||
|
out = out .. '\n'
|
||||||
|
i = i + 1;
|
||||||
|
elseif nextch == 'r' then
|
||||||
|
out = out .. '\r'
|
||||||
|
i = i + 1;
|
||||||
|
elseif nextch == 'v' then
|
||||||
|
out = out .. '\v'
|
||||||
|
i = i + 1;
|
||||||
|
elseif nextch == '"' then
|
||||||
|
out = out .. '"'
|
||||||
|
i = i + 1;
|
||||||
|
elseif nextch == 'f' then
|
||||||
|
out = out .. '\f'
|
||||||
|
i = i + 1;
|
||||||
|
elseif nextch == "t" then
|
||||||
|
out = out .. "\t"
|
||||||
|
i = i + 1
|
||||||
|
elseif nextch == 'x' then
|
||||||
|
i = i + 1;
|
||||||
|
local hex = lakeString:sub(i, i + 1)
|
||||||
|
i = i + 2;
|
||||||
|
|
||||||
|
local decoded = HexDecode(hex);
|
||||||
|
|
||||||
|
out = out .. string.char(decoded)
|
||||||
|
elseif nextch == "u" then
|
||||||
|
i = i + 2; -- skip u and {, if the { wasn't there that's the lexer's fault and their bug.
|
||||||
|
|
||||||
|
local hexpart = ""
|
||||||
|
|
||||||
|
while lakeString:sub(i,i) ~= '}' do
|
||||||
|
hexpart = hexpart .. lakeString:sub(i,i)
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
-- on the }, skip it:
|
||||||
|
i = i + 1 -- if we were not on it, welp
|
||||||
|
|
||||||
|
local decoded = HexDecode(hexpart)
|
||||||
|
|
||||||
|
out = out .. Lower.makeUTF8Char(decoded);
|
||||||
|
end
|
||||||
|
elseif ch == '"' then
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
i = i + 1
|
||||||
|
out = out .. ch
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return out;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Lower.decodeNumber(num)
|
||||||
|
local numeric = 0
|
||||||
|
local decimal = false
|
||||||
|
for i = 1,#num do
|
||||||
|
local ch = num:sub(i,i)
|
||||||
|
|
||||||
|
if ch == "." then
|
||||||
|
decimal = i + 1
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
numeric = numeric * 10
|
||||||
|
|
||||||
|
numeric = numeric + (string.byte(ch) - string.byte'0')
|
||||||
|
end
|
||||||
|
|
||||||
|
if decimal then
|
||||||
|
local n = 0.1
|
||||||
|
for j = decimal, #num do
|
||||||
|
local ch = num:sub(j,j)
|
||||||
|
|
||||||
|
numeric = numeric + (string.byte(ch) - string.byte'0')*n
|
||||||
|
|
||||||
|
n = n * 0.1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return numeric
|
||||||
|
end
|
||||||
676
src/methods/debug.lua
Normal file
676
src/methods/debug.lua
Normal file
@@ -0,0 +1,676 @@
|
|||||||
|
Debug = {}
|
||||||
|
|
||||||
|
-- very basic, but should always be correct and doesn't try any optimization shenanigans
|
||||||
|
function Debug.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 code >= 32 and code <= 126 then
|
||||||
|
out = out .. ch
|
||||||
|
else
|
||||||
|
if islua51 then
|
||||||
|
out = out .. "\\" .. DecEncode(code, 3)
|
||||||
|
else
|
||||||
|
out = out .. "\\x" .. HexEncode(code, 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
out = out .. '"'
|
||||||
|
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
|
function Debug.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 Debug.convertBinaryOperator(lakeop)
|
||||||
|
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 "and"
|
||||||
|
elseif lakeop == "||" then
|
||||||
|
return "or"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Debug.convertUnaryOperator(op)
|
||||||
|
if op == "#" then
|
||||||
|
return "#"
|
||||||
|
elseif op == "-" then
|
||||||
|
return "-"
|
||||||
|
elseif op == "!" then
|
||||||
|
return "not"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Debug.output(outp, ast)
|
||||||
|
Debug.outputStatement(outp, ast)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param outp Output
|
||||||
|
---@param node Node
|
||||||
|
function Debug.outputStatement(outp, node)
|
||||||
|
if node.kind == "Program" then
|
||||||
|
outp:addLine("do", node.location:format())
|
||||||
|
|
||||||
|
for i = 1,#node.content do
|
||||||
|
local stmt = node.content[i]
|
||||||
|
|
||||||
|
Debug.outputStatement(outp, stmt)
|
||||||
|
end
|
||||||
|
|
||||||
|
outp:addLine("end;", node.location:format())
|
||||||
|
elseif node.kind == "VariableDeclaration" then
|
||||||
|
outp:addLine("local", node.location:format())
|
||||||
|
local varnames = node.content[1]
|
||||||
|
|
||||||
|
for i = 1,#varnames do
|
||||||
|
local vname = varnames[i]
|
||||||
|
|
||||||
|
outp:addLine(vname[1], vname[2]:format())
|
||||||
|
|
||||||
|
if i ~= #varnames then
|
||||||
|
outp:addLine(",", node.location:format())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local values = node.content[2]
|
||||||
|
if #values > 0 then
|
||||||
|
outp:addLine("=", node.location:format())
|
||||||
|
|
||||||
|
for i = 1,#values do
|
||||||
|
local val = values[i]
|
||||||
|
|
||||||
|
Debug.outputExpression(outp, val)
|
||||||
|
|
||||||
|
if i ~= #values then
|
||||||
|
outp:addLine(",", node.location:format())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
outp:addLine(";", node.location:format())
|
||||||
|
elseif node.kind == "Block" then
|
||||||
|
outp:addLine("do", node.location:format())
|
||||||
|
|
||||||
|
for i = 1,#node.content do
|
||||||
|
local stmt = node.content[i]
|
||||||
|
|
||||||
|
Debug.outputStatement(outp, stmt)
|
||||||
|
end
|
||||||
|
|
||||||
|
outp:addLine("end;", node.location:format())
|
||||||
|
elseif node.kind == "IfStatement" then
|
||||||
|
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 i = 2, #node.content[1] do
|
||||||
|
local item = node.content[1][i]
|
||||||
|
-- print(item, item[1], item[2])
|
||||||
|
outp:addLine("elseif", node.location:format())
|
||||||
|
|
||||||
|
Debug.outputExpression(outp,item[1]);
|
||||||
|
|
||||||
|
outp:addLine("then", node.location:format())
|
||||||
|
|
||||||
|
Debug.outputStatement(outp,item[2]);
|
||||||
|
end
|
||||||
|
|
||||||
|
if node.content[2] then
|
||||||
|
outp:addLine("else", node.location:format())
|
||||||
|
|
||||||
|
Debug.outputStatement(outp, node.content[2])
|
||||||
|
end
|
||||||
|
|
||||||
|
outp:addLine("end;", node.location:format())
|
||||||
|
elseif node.kind == "FunctionDeclaration" then
|
||||||
|
outp:addLine("function", node.location:format())
|
||||||
|
|
||||||
|
Debug.outputFunctionName(outp, node.content[1]);
|
||||||
|
|
||||||
|
outp:addLine("(", node.location:format())
|
||||||
|
|
||||||
|
for i = 1,#node.content[2] do
|
||||||
|
local part = node.content[2][i]
|
||||||
|
|
||||||
|
outp:addLine(part, node.location:format())
|
||||||
|
|
||||||
|
if i ~= #node.content[2] then
|
||||||
|
outp:addLine(",", node.location:format())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
outp:addLine(")", node.location:format())
|
||||||
|
|
||||||
|
Debug.outputStatement(outp, node.content[3])
|
||||||
|
|
||||||
|
outp:addLine("end;", node.location:format())
|
||||||
|
elseif node.kind == "LocalFunctionDeclaration" then
|
||||||
|
outp:addLine("local function", node.location:format())
|
||||||
|
|
||||||
|
outp:addLine(node.content[1].content, node.content[1].location:format());
|
||||||
|
|
||||||
|
outp:addLine("(", node.location:format())
|
||||||
|
|
||||||
|
for i = 1,#node.content[2] do
|
||||||
|
local part = node.content[2][i]
|
||||||
|
|
||||||
|
outp:addLine(part, node.location:format())
|
||||||
|
|
||||||
|
if i ~= #node.content[2] then
|
||||||
|
outp:addLine(",", node.location:format())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
outp:addLine(")", node.location:format())
|
||||||
|
|
||||||
|
Debug.outputStatement(outp, node.content[3])
|
||||||
|
|
||||||
|
outp:addLine("end;", node.location:format())
|
||||||
|
elseif node.kind == "Return" then
|
||||||
|
outp:addLine("return", node.location:format())
|
||||||
|
|
||||||
|
for i = 1,#node.content do
|
||||||
|
local expr = node.content[i]
|
||||||
|
|
||||||
|
Debug.outputExpression(outp, expr)
|
||||||
|
|
||||||
|
if i ~= #node.content then
|
||||||
|
outp:addLine(",", node.location:format())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
outp:addLine(";", node.location:format())
|
||||||
|
elseif node.kind == "Assignment" then
|
||||||
|
local vars = node.content[1]
|
||||||
|
local vals = node.content[2]
|
||||||
|
|
||||||
|
for i = 1,#vars do
|
||||||
|
local var = vars[i]
|
||||||
|
|
||||||
|
Debug.outputVariable(outp, var)
|
||||||
|
|
||||||
|
if i ~= #vars then
|
||||||
|
outp:addLine(",", node.location:format())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
outp:addLine("=", node.location:format())
|
||||||
|
|
||||||
|
for i = 1,#vals do
|
||||||
|
local val = vals[i]
|
||||||
|
|
||||||
|
Debug.outputExpression(outp, val)
|
||||||
|
|
||||||
|
if i ~= #vals then
|
||||||
|
outp:addLine(",", node.location:format())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
outp:addLine(";", node.location:format())
|
||||||
|
elseif node.kind == "Call" then
|
||||||
|
outp:addLine("(", node.location:format())
|
||||||
|
Debug.outputExpression(outp, node.content[1])
|
||||||
|
outp:addLine(")(", node.location:format())
|
||||||
|
|
||||||
|
local args = node.content[2]
|
||||||
|
for i = 1,#args do
|
||||||
|
local arg = args[i]
|
||||||
|
|
||||||
|
Debug.outputExpression(outp, arg)
|
||||||
|
|
||||||
|
if i ~= #args then
|
||||||
|
outp:addLine(",", node.location:format())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
outp:addLine(")", node.location:format())
|
||||||
|
outp:addLine(";", node.location:format())
|
||||||
|
elseif node.kind == "MethodCall" then
|
||||||
|
outp:addLine("(", node.location:format())
|
||||||
|
Debug.outputExpression(outp, node.content[1])
|
||||||
|
outp:addLine("):", node.location:format())
|
||||||
|
outp:addLine(node.content[2] .. '(', node.location:format())
|
||||||
|
|
||||||
|
local args = node.content[3]
|
||||||
|
for i = 1,#args do
|
||||||
|
local arg = args[i]
|
||||||
|
|
||||||
|
Debug.outputExpression(outp, arg)
|
||||||
|
|
||||||
|
if i ~= #args then
|
||||||
|
outp:addLine(",", node.location:format())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
outp:addLine(")", node.location:format())
|
||||||
|
outp:addLine(";", node.location:format())
|
||||||
|
elseif node.kind == "While" then
|
||||||
|
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" then
|
||||||
|
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" then
|
||||||
|
local lake_op = node.content[2]
|
||||||
|
local op = Debug.convertBinaryOperator(lake_op)
|
||||||
|
|
||||||
|
local lhs = node.content[1]
|
||||||
|
local rhs = node.content[3]
|
||||||
|
|
||||||
|
if lhs.kind == "Variable" then
|
||||||
|
local 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" then
|
||||||
|
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" then
|
||||||
|
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())
|
||||||
|
end
|
||||||
|
elseif node.kind == "Break" then
|
||||||
|
outp:addLine('break;', node.location:format())
|
||||||
|
elseif node.kind == "Foreach" then
|
||||||
|
outp:addLine("for", node.location:format())
|
||||||
|
|
||||||
|
local varlist,exprlist,block = node.content[1],node.content[2],node.content[3]
|
||||||
|
|
||||||
|
for i = 1,#varlist do
|
||||||
|
outp:addLine(varlist[i], node.location:format())
|
||||||
|
if i ~= #varlist then
|
||||||
|
outp:addLine(',', node.location:format())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
outp:addLine("in", node.location:format())
|
||||||
|
|
||||||
|
for i = 1,#exprlist do
|
||||||
|
Debug.outputExpression(outp, exprlist[i])
|
||||||
|
|
||||||
|
if i ~= #exprlist then
|
||||||
|
outp:addLine(",", node.location:format())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
outp:addLine('do', node.location:format())
|
||||||
|
|
||||||
|
Debug.outputStatement(outp, block);
|
||||||
|
|
||||||
|
outp:addLine('end;', node.location:format())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param outp Output
|
||||||
|
---@param node Node
|
||||||
|
function Debug.outputExpression(outp, node)
|
||||||
|
if node.kind == "StringLiteral" then
|
||||||
|
local lakestr = node.content
|
||||||
|
|
||||||
|
local raw = Lower.decodeString(lakestr)
|
||||||
|
|
||||||
|
local enc = Debug.encodeString(raw, outp.target == "lua51")
|
||||||
|
|
||||||
|
outp:addLine(enc, node.location:format())
|
||||||
|
elseif node.kind == "ParenthesizedExpression" then
|
||||||
|
outp:addLine("(", node.location:format())
|
||||||
|
Debug.outputExpression(outp, node.content)
|
||||||
|
outp:addLine(")", node.location:format())
|
||||||
|
elseif node.kind == "NumberLiteral" then
|
||||||
|
local lakenum = node.content
|
||||||
|
|
||||||
|
local raw = Lower.decodeNumber(lakenum)
|
||||||
|
|
||||||
|
-- print(raw, type(raw))
|
||||||
|
|
||||||
|
local enc = Debug.encodeNumber(raw)
|
||||||
|
|
||||||
|
outp:addLine("(", node.location:format())
|
||||||
|
outp:addLine(enc, node.location:format())
|
||||||
|
outp:addLine(")", node.location:format())
|
||||||
|
elseif node.kind == "TableLiteral" then
|
||||||
|
outp:addLine('{', node.location:format())
|
||||||
|
local list_part = node.content[1]
|
||||||
|
local table_part = node.content[2]
|
||||||
|
|
||||||
|
for i = 1,#list_part do
|
||||||
|
local item = list_part[i]
|
||||||
|
|
||||||
|
Debug.outputExpression(outp, item);
|
||||||
|
outp:addLine(',', node.location:format())
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 1,#table_part do
|
||||||
|
local item = table_part[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())
|
||||||
|
end
|
||||||
|
outp:addLine('}', node.location:format())
|
||||||
|
elseif node.kind == "LambdaFunctionLiteral" then
|
||||||
|
outp:addLine('function(', node.location:format())
|
||||||
|
|
||||||
|
for i = 1,#node.content[1] do
|
||||||
|
local part = node.content[1][i]
|
||||||
|
|
||||||
|
outp:addLine(part, node.location:format())
|
||||||
|
|
||||||
|
if i ~= #node.content[1] then
|
||||||
|
outp:addLine(",", node.location:format())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
outp:addLine(")", node.location:format())
|
||||||
|
|
||||||
|
Debug.outputStatement(outp, node.content[2])
|
||||||
|
|
||||||
|
outp:addLine("end ", node.location:format())
|
||||||
|
elseif node.kind == "BinaryOperator" then
|
||||||
|
local op = node.content[1]
|
||||||
|
local lhs = node.content[2]
|
||||||
|
local rhs = node.content[3]
|
||||||
|
|
||||||
|
outp:addLine("((", node.location:format())
|
||||||
|
|
||||||
|
Debug.outputExpression(outp,lhs)
|
||||||
|
|
||||||
|
outp:addLine(")", node.location:format())
|
||||||
|
|
||||||
|
local 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" then
|
||||||
|
local op = node.content[1]
|
||||||
|
local val = node.content[2]
|
||||||
|
|
||||||
|
outp:addLine("(", node.location:format())
|
||||||
|
|
||||||
|
local 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" then
|
||||||
|
local left = node.content[1]
|
||||||
|
local 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" then
|
||||||
|
local left = node.content[1]
|
||||||
|
local 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" then
|
||||||
|
outp:addLine("(", node.location:format())
|
||||||
|
outp:addLine(node.content, node.location:format())
|
||||||
|
outp:addLine(")", node.location:format())
|
||||||
|
elseif node.kind == "BooleanLiteral" then
|
||||||
|
outp:addLine("(", node.location:format())
|
||||||
|
outp:addLine(node.content, node.location:format())
|
||||||
|
outp:addLine(")", node.location:format())
|
||||||
|
elseif node.kind == "NullLiteral" then
|
||||||
|
outp:addLine("(nil)", node.location:format())
|
||||||
|
elseif node.kind == "Call" then
|
||||||
|
outp:addLine("(", node.location:format())
|
||||||
|
Debug.outputExpression(outp, node.content[1])
|
||||||
|
outp:addLine(")(", node.location:format())
|
||||||
|
|
||||||
|
local args = node.content[2]
|
||||||
|
for i = 1,#args do
|
||||||
|
local arg = args[i]
|
||||||
|
|
||||||
|
Debug.outputExpression(outp, arg)
|
||||||
|
|
||||||
|
if i ~= #args then
|
||||||
|
outp:addLine(",", node.location:format())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
outp:addLine(")", node.location:format())
|
||||||
|
elseif node.kind == "MethodCall" then
|
||||||
|
outp:addLine("(", node.location:format())
|
||||||
|
Debug.outputExpression(outp, node.content[1])
|
||||||
|
outp:addLine("):", node.location:format())
|
||||||
|
outp:addLine(node.content[2] .. "(", node.location:format())
|
||||||
|
|
||||||
|
local args = node.content[3]
|
||||||
|
for i = 1,#args do
|
||||||
|
local arg = args[i]
|
||||||
|
|
||||||
|
Debug.outputExpression(outp, arg)
|
||||||
|
|
||||||
|
if i ~= #args then
|
||||||
|
outp:addLine(",", node.location:format())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
outp:addLine(")", node.location:format())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param outp Output
|
||||||
|
---@param node Node
|
||||||
|
function Debug.outputFunctionName(outp, node)
|
||||||
|
if node.kind == "Field" then
|
||||||
|
local left = node.content[1]
|
||||||
|
local right = node.content[2]
|
||||||
|
|
||||||
|
Debug.outputFunctionName(outp, left)
|
||||||
|
outp:addLine(".", node.location:format())
|
||||||
|
outp:addLine(right, node.location:format())
|
||||||
|
elseif node.kind == "Index" then
|
||||||
|
local left = node.content[1]
|
||||||
|
local 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" then
|
||||||
|
outp:addLine(node.content, node.location:format())
|
||||||
|
elseif node.kind == "Method" then
|
||||||
|
local left = node.content[1]
|
||||||
|
local right = node.content[2]
|
||||||
|
|
||||||
|
Debug.outputFunctionName(outp, left)
|
||||||
|
outp:addLine(":", node.location:format())
|
||||||
|
outp:addLine(right, node.location:format())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param outp Output
|
||||||
|
---@param node Node
|
||||||
|
function Debug.outputVariable(outp, node)
|
||||||
|
if node.kind == "Field" then
|
||||||
|
local left = node.content[1]
|
||||||
|
local right = node.content[2]
|
||||||
|
|
||||||
|
Debug.outputExpression(outp, left)
|
||||||
|
outp:addLine(".", node.location:format())
|
||||||
|
outp:addLine(right, node.location:format())
|
||||||
|
elseif node.kind == "Index" then
|
||||||
|
local left = node.content[1]
|
||||||
|
local 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" then
|
||||||
|
outp:addLine(node.content, node.location:format())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
RegisterOutMethod("debug", Debug)
|
||||||
719
src/methods/small.lua
Normal file
719
src/methods/small.lua
Normal file
@@ -0,0 +1,719 @@
|
|||||||
|
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)
|
||||||
46
src/output.lua
Normal file
46
src/output.lua
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
OutputMethods = {}
|
||||||
|
|
||||||
|
function RegisterOutMethod(name, data)
|
||||||
|
OutputMethods[name] = data
|
||||||
|
end
|
||||||
|
|
||||||
|
---@class Output
|
||||||
|
---@field target string
|
||||||
|
---@field method string
|
||||||
|
---@field lines string[]
|
||||||
|
---@field debug string[]
|
||||||
|
Output = {}
|
||||||
|
|
||||||
|
function Output:new(target, method)
|
||||||
|
return setmetatable({
|
||||||
|
target = target,
|
||||||
|
method = method,
|
||||||
|
lines = {},
|
||||||
|
debug = {}
|
||||||
|
}, {__index = self})
|
||||||
|
end
|
||||||
|
|
||||||
|
function Output:output(ast)
|
||||||
|
local method = OutputMethods[self.method]
|
||||||
|
assert(method)
|
||||||
|
|
||||||
|
method.output(self, ast)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Output:addLine(line, debuginfo)
|
||||||
|
self.lines[#self.lines+1] = line
|
||||||
|
self.debug[#self.debug+1] = debuginfo
|
||||||
|
end
|
||||||
|
|
||||||
|
function Output:appendLine(data, debuginfo)
|
||||||
|
if not self.lines[#self.lines] then
|
||||||
|
self.lines[#self.lines+1] = ""
|
||||||
|
end
|
||||||
|
self.lines[#self.lines] = self.lines[#self.lines] .. data
|
||||||
|
|
||||||
|
if not debuginfo then print(data) end
|
||||||
|
if not self.debug[#self.debug] then
|
||||||
|
self.debug[#self.debug+1] = ""
|
||||||
|
end
|
||||||
|
self.debug[#self.debug] = self.debug[#self.debug] .. debuginfo
|
||||||
|
end
|
||||||
1373
src/parser.lua
Normal file
1373
src/parser.lua
Normal file
File diff suppressed because it is too large
Load Diff
85
src/types.lua
Normal file
85
src/types.lua
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
---@class Location
|
||||||
|
---@field file string
|
||||||
|
---@field line integer
|
||||||
|
---@field column integer
|
||||||
|
Location = {}
|
||||||
|
|
||||||
|
function Location:new(file)
|
||||||
|
return setmetatable({file = file, column = 1, line = 1},{__index = self})
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return Location
|
||||||
|
function Location:clone()
|
||||||
|
return setmetatable({file = self.file, column = self.column, line = self.line}, getmetatable(self))
|
||||||
|
end
|
||||||
|
|
||||||
|
function Location:skip(chr)
|
||||||
|
for i=1,#chr do
|
||||||
|
self.column = self.column + 1
|
||||||
|
local c = chr:sub(i, i)
|
||||||
|
if c == '\n' then
|
||||||
|
self.column = 1
|
||||||
|
self.line = self.line + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Location:format()
|
||||||
|
return self.file .. ":" .. self.line .. ":" .. self.column
|
||||||
|
end
|
||||||
|
|
||||||
|
---@class Token
|
||||||
|
---@field kind string
|
||||||
|
---@field content any
|
||||||
|
---@field location Location
|
||||||
|
Token = {}
|
||||||
|
|
||||||
|
function Token:new(kind, content, location)
|
||||||
|
return setmetatable({
|
||||||
|
kind = kind,
|
||||||
|
content = content,
|
||||||
|
location = location
|
||||||
|
}, {__index = self})
|
||||||
|
end
|
||||||
|
|
||||||
|
---@class Error
|
||||||
|
---@field location Location
|
||||||
|
---@field message string
|
||||||
|
---@field origin string
|
||||||
|
Error = {}
|
||||||
|
|
||||||
|
function Error:new(message,origin, location)
|
||||||
|
return setmetatable({
|
||||||
|
message = message,
|
||||||
|
origin = origin,
|
||||||
|
location = location
|
||||||
|
}, {__index = self})
|
||||||
|
end
|
||||||
|
|
||||||
|
function Error:format()
|
||||||
|
local capitalizedOrigin = self.origin:sub(1,1):upper() .. self.origin:sub(2):lower()
|
||||||
|
local locationString = self.location:format()
|
||||||
|
|
||||||
|
-- local msg = string.format("%s:%d %s", err.location.file, err.location.line, err.message)
|
||||||
|
|
||||||
|
return capitalizedOrigin .. " error: " .. locationString .. ": " .. self.message;
|
||||||
|
-- print("\x1b[31m" .. msg .. "\x1b[0m")
|
||||||
|
end
|
||||||
|
|
||||||
|
function Error:print()
|
||||||
|
print("\x1b[31m" .. self:format() .. "\x1b[0m")
|
||||||
|
end
|
||||||
|
|
||||||
|
---@class Node
|
||||||
|
---@field kind string
|
||||||
|
---@field content any
|
||||||
|
---@field location Location
|
||||||
|
Node = {}
|
||||||
|
|
||||||
|
function Node:new(kind,content,location)
|
||||||
|
return setmetatable({
|
||||||
|
kind = kind,
|
||||||
|
content = content,
|
||||||
|
location = location,
|
||||||
|
}, {__index = self})
|
||||||
|
end
|
||||||
64
src/util.lua
Normal file
64
src/util.lua
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
local deckey = "0123456789"
|
||||||
|
function DecEncode(num, minchars)
|
||||||
|
local outp = ""
|
||||||
|
|
||||||
|
while num > 0 do
|
||||||
|
local dig = num % (#deckey)
|
||||||
|
|
||||||
|
local ch = deckey:sub(dig + 1, dig + 1)
|
||||||
|
|
||||||
|
outp = ch .. outp
|
||||||
|
|
||||||
|
num = math.floor(num / #deckey)
|
||||||
|
end
|
||||||
|
|
||||||
|
if minchars then
|
||||||
|
|
||||||
|
while #outp < minchars do
|
||||||
|
outp = deckey:sub(1,1) .. outp
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return outp
|
||||||
|
end
|
||||||
|
|
||||||
|
local hexkey = "0123456789abcdef"
|
||||||
|
function HexEncode(num, minchars)
|
||||||
|
local outp = ""
|
||||||
|
|
||||||
|
while num > 0 do
|
||||||
|
local dig = num % (#hexkey)
|
||||||
|
|
||||||
|
local ch = hexkey:sub(dig + 1, dig + 1)
|
||||||
|
|
||||||
|
outp = ch .. outp
|
||||||
|
|
||||||
|
num = math.floor(num / #hexkey)
|
||||||
|
end
|
||||||
|
|
||||||
|
if minchars then
|
||||||
|
|
||||||
|
while #outp < minchars do
|
||||||
|
outp = hexkey:sub(1,1) .. outp
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return outp
|
||||||
|
end
|
||||||
|
|
||||||
|
function HexDecode(hexstr)
|
||||||
|
local out = 0
|
||||||
|
|
||||||
|
for i = 1,#hexstr do
|
||||||
|
local char = hexstr:sub(i,i):lower()
|
||||||
|
|
||||||
|
local pos = hexkey:find(char, nil, true) - 1
|
||||||
|
|
||||||
|
out = out * 16;
|
||||||
|
out = out + pos;
|
||||||
|
end
|
||||||
|
|
||||||
|
return out;
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user