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