initial commit
This commit is contained in:
11
main.lake
Normal file
11
main.lake
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
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');
|
||||||
168
src/cli.lake
Normal file
168
src/cli.lake
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
fn ParseArgData(args, argConfig) {
|
||||||
|
var i = 1;
|
||||||
|
|
||||||
|
var knorps = {};
|
||||||
|
var flarples = {};
|
||||||
|
|
||||||
|
foreach (k,v = pairs(argConfig)) {
|
||||||
|
if (v[1] == 'flag') {
|
||||||
|
flarples[v[2]] = false;
|
||||||
|
} elseif (v[1] == 'list') {
|
||||||
|
flarples[v[2]] = {};
|
||||||
|
} elseif (v[1] == 'input') {
|
||||||
|
// can't initialize to anything.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (i <= #args) {
|
||||||
|
var argument = args[i];
|
||||||
|
|
||||||
|
if (argConfig[argument]) {
|
||||||
|
var argconf = argConfig[argument];
|
||||||
|
|
||||||
|
var argtype = argconf[1];
|
||||||
|
var storageplace = argconf[2];
|
||||||
|
|
||||||
|
if (argtype == 'flag') {
|
||||||
|
flarples[storageplace] = true;
|
||||||
|
} elseif (argtype == 'input') {
|
||||||
|
var next = args[i+1];
|
||||||
|
i += 1;
|
||||||
|
|
||||||
|
if (!next) {
|
||||||
|
error('no value given to an input flag!');
|
||||||
|
}
|
||||||
|
|
||||||
|
flarples[storageplace] = next;
|
||||||
|
} elseif (argtype == 'list') {
|
||||||
|
var next = args[i+1];
|
||||||
|
i += 1;
|
||||||
|
|
||||||
|
if (!next) {
|
||||||
|
error('no value given to a list flag!');
|
||||||
|
}
|
||||||
|
|
||||||
|
table.insert(flarples[storageplace], next);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (argument:sub(1,1) == '-') {
|
||||||
|
error('knorp starts with a dash!');
|
||||||
|
}
|
||||||
|
|
||||||
|
knorps[#knorps+1] = argument;
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return knorps, flarples;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (#arg == 0) {
|
||||||
|
while (true) {
|
||||||
|
io.write("\x1b[36minput>\x1b[0m ");
|
||||||
|
io.flush();
|
||||||
|
|
||||||
|
var line = io.read('L');
|
||||||
|
if (!line) break;
|
||||||
|
|
||||||
|
var lexer = Lexer:new(line, "stdin");
|
||||||
|
var parser = Parser:new(lexer);
|
||||||
|
|
||||||
|
var ast,err = parser:parse();
|
||||||
|
if (err) {
|
||||||
|
err:print();
|
||||||
|
} else {
|
||||||
|
if (!ast) {
|
||||||
|
print("\x1b[31m!!! an unknown error occured !!!\x1b[0m");
|
||||||
|
os.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var output = Output:new('lua54', 'small');
|
||||||
|
output:output(ast);
|
||||||
|
|
||||||
|
var outdata = table.concat(output.lines, "\n");
|
||||||
|
print(outdata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg[1] == 'compile') {
|
||||||
|
table.remove(arg, 1);
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
'-v' = {'flag', 'verbose'},
|
||||||
|
'-o' = {'input', 'output'},
|
||||||
|
'-s' = {'input', 'sourcemap'},
|
||||||
|
'-m' = {'input', 'method'},
|
||||||
|
'-t' = {'input', 'target'}
|
||||||
|
};
|
||||||
|
|
||||||
|
var knorps, flarples = ParseArgData(arg, options);
|
||||||
|
flarples.target ||= 'lua54';
|
||||||
|
flarples.method ||= 'debug';
|
||||||
|
|
||||||
|
if (!flarples.output) {
|
||||||
|
print("\x1b[31mno output file given\x1b[0m");
|
||||||
|
os.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var outfile = io.open(flarples.output, 'w');
|
||||||
|
if (!outfile) {
|
||||||
|
print("\x1b[31moutput file not writable\x1b[0m");
|
||||||
|
os.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var SMfile;
|
||||||
|
if (flarples.sourcemap) {
|
||||||
|
SMfile = io.open(flarples.sourcemap, 'w');
|
||||||
|
if (!SMfile) {
|
||||||
|
print("\x1b[31msourcemap file not writable\x1b[0m");
|
||||||
|
os.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 1; i <= #knorps; i += 1;) {
|
||||||
|
var file = io.open(knorps[i], 'r');
|
||||||
|
if (!file) {
|
||||||
|
print("\x1b[31mcould not read file " .. tostring(knorps[i]) .. "\x1b[0m");
|
||||||
|
os.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var code = file:read('*all');
|
||||||
|
file:close();
|
||||||
|
|
||||||
|
var lexer = Lexer:new(code, knorps[i]);
|
||||||
|
|
||||||
|
var parser = Parser:new(lexer);
|
||||||
|
|
||||||
|
var ast,err = parser:parse();
|
||||||
|
if (!ast) {
|
||||||
|
if (err)
|
||||||
|
err:print();
|
||||||
|
else
|
||||||
|
print("\x1b[31m!!! an unknown error occured !!!\x1b[0m");
|
||||||
|
os.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var optimizer = Optimizer:new();
|
||||||
|
|
||||||
|
optimizer:run(ast);
|
||||||
|
|
||||||
|
var output = Output:new(flarples.target, flarples.method);
|
||||||
|
|
||||||
|
output:output(ast);
|
||||||
|
|
||||||
|
var outdata = table.concat(output.lines, "\n");
|
||||||
|
var smdata = table.concat(output.debug, "\n");
|
||||||
|
|
||||||
|
outfile:write(outdata);
|
||||||
|
if (SMfile)
|
||||||
|
SMfile:write(smdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
outfile:close();
|
||||||
|
if (SMfile) SMfile:close();
|
||||||
|
}
|
||||||
326
src/lexer.lake
Normal file
326
src/lexer.lake
Normal file
@@ -0,0 +1,326 @@
|
|||||||
|
Lexer = {};
|
||||||
|
|
||||||
|
Lexer.keywords = {
|
||||||
|
'var',
|
||||||
|
'return',
|
||||||
|
'fn',
|
||||||
|
'true',
|
||||||
|
'false',
|
||||||
|
'null',
|
||||||
|
'if',
|
||||||
|
'var',
|
||||||
|
'else',
|
||||||
|
'elseif',
|
||||||
|
'while',
|
||||||
|
'for',
|
||||||
|
'do',
|
||||||
|
'break',
|
||||||
|
'foreach'
|
||||||
|
};
|
||||||
|
|
||||||
|
Lexer.symbols = {
|
||||||
|
"!=",
|
||||||
|
"==",
|
||||||
|
"<=",
|
||||||
|
">=",
|
||||||
|
"&&=",
|
||||||
|
"||=",
|
||||||
|
"&&",
|
||||||
|
"||",
|
||||||
|
"+=",
|
||||||
|
"-=",
|
||||||
|
"*=",
|
||||||
|
"/=",
|
||||||
|
"%=",
|
||||||
|
"^=",
|
||||||
|
"..=",
|
||||||
|
"..",
|
||||||
|
">",
|
||||||
|
"<",
|
||||||
|
"+",
|
||||||
|
"-",
|
||||||
|
"*",
|
||||||
|
"/",
|
||||||
|
"^",
|
||||||
|
"=",
|
||||||
|
";",
|
||||||
|
"#",
|
||||||
|
"!",
|
||||||
|
"[",
|
||||||
|
"]",
|
||||||
|
".",
|
||||||
|
"{",
|
||||||
|
"}",
|
||||||
|
"(",
|
||||||
|
")",
|
||||||
|
",",
|
||||||
|
"%",
|
||||||
|
':'
|
||||||
|
};
|
||||||
|
|
||||||
|
fn Lexer:new(code, fname) {
|
||||||
|
return setmetatable({
|
||||||
|
'cache' = null,
|
||||||
|
'cursor' = 1,
|
||||||
|
'location' = Location:new(fname),
|
||||||
|
'code' = code
|
||||||
|
}, {'__index' = self});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Lexer:next(n) {
|
||||||
|
n ||= 1;
|
||||||
|
|
||||||
|
for (var i = 1; i <= n; i += 1;) {
|
||||||
|
self.location:skip(self.code:sub(self.cursor, self.cursor));
|
||||||
|
self.cursor += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Lexer:get(n) {
|
||||||
|
n ||= 1;
|
||||||
|
return self.code:sub(self.cursor, self.cursor+n-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Lexer:isWhitespace(ch) {
|
||||||
|
return ch == "\r" || ch == "\n" || ch == "\t" || ch == ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Lexer:isDigit(ch) {
|
||||||
|
var n = string.byte(ch);
|
||||||
|
if (!n) return false;
|
||||||
|
return n >= 48 && n <= 57;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Lexer:isHex(ch) {
|
||||||
|
return (ch:match('%x') != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Lexer:isIdentifierValid(ch) {
|
||||||
|
return (ch:match('%a') != null) || (Lexer:isDigit(ch)) || ch == '_';
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Lexer:run() {
|
||||||
|
while (true) {
|
||||||
|
if (self:isWhitespace(self:get())) {
|
||||||
|
self:next();
|
||||||
|
} elseif (self:get(2) == '//') {
|
||||||
|
while ((self:get() != "\n") && (self:get() != '')) {
|
||||||
|
self:next();
|
||||||
|
}
|
||||||
|
} elseif (self:get() == '') {
|
||||||
|
return Token:new('eof', 'eof', self.location:clone());
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self:isDigit(self:get())) {
|
||||||
|
// number
|
||||||
|
|
||||||
|
var stpos = self.location:clone();
|
||||||
|
var st = '';
|
||||||
|
|
||||||
|
while (self:isDigit(self:get())) {
|
||||||
|
st ..= self:get();
|
||||||
|
self:next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self:get() == '.') {
|
||||||
|
// decimal point real
|
||||||
|
|
||||||
|
self:next();
|
||||||
|
st ..= '.';
|
||||||
|
|
||||||
|
while (self:isDigit(self:get())) {
|
||||||
|
st ..= self:get();
|
||||||
|
self:next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Token:new('number', st, stpos);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self:get() == "'") {
|
||||||
|
var d = "'";
|
||||||
|
var stpos = self.location:clone();
|
||||||
|
self:next();
|
||||||
|
|
||||||
|
while (self:get() != "'") {
|
||||||
|
var ch = self:get();
|
||||||
|
if (ch == '') break;
|
||||||
|
d ..= ch;
|
||||||
|
self:next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self:get() != "'") {
|
||||||
|
return null, Error:new('unfinished string', 'lexer', self.location:clone());
|
||||||
|
}
|
||||||
|
d ..= "'";
|
||||||
|
self:next();
|
||||||
|
|
||||||
|
return Token:new('string', d, stpos);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self:get() == '"') {
|
||||||
|
var d = '"';
|
||||||
|
var stpos = self.location:clone();
|
||||||
|
self:next();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
var ch = self:get();
|
||||||
|
|
||||||
|
if (ch == '\') {
|
||||||
|
d ..= ch;
|
||||||
|
self:next();
|
||||||
|
|
||||||
|
var nch = self:get();
|
||||||
|
if (nch == '\') {
|
||||||
|
d ..= '\';
|
||||||
|
self:next();
|
||||||
|
} elseif (nch == 'n') {
|
||||||
|
d ..= 'n';
|
||||||
|
self:next();
|
||||||
|
} elseif (nch == 'r') {
|
||||||
|
d ..= 'r';
|
||||||
|
self:next();
|
||||||
|
} elseif (nch == 'v') {
|
||||||
|
d ..= 'v';
|
||||||
|
self:next();
|
||||||
|
} elseif (nch == '"') {
|
||||||
|
d ..= '"';
|
||||||
|
self:next();
|
||||||
|
} elseif (nch == 'f') {
|
||||||
|
d ..= 'f';
|
||||||
|
self:next();
|
||||||
|
} elseif (nch == 't') {
|
||||||
|
d ..= 't';
|
||||||
|
self:next();
|
||||||
|
} elseif (nch == 'a') {
|
||||||
|
d ..= 'a';
|
||||||
|
self:next();
|
||||||
|
} elseif (nch == 'b') {
|
||||||
|
d ..= 'b';
|
||||||
|
self:next();
|
||||||
|
} elseif (nch == 'x') {
|
||||||
|
d ..= 'x';
|
||||||
|
self:next();
|
||||||
|
|
||||||
|
var hexdata = self:get(2);
|
||||||
|
|
||||||
|
if ((!self:isHex(hexdata:sub(1,1))) || (!self:isHex(hexdata:sub(2,2)))) {
|
||||||
|
return null, Error:new('invalid hex digits after x escape code', 'lexer', self.location:clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
d ..= hexdata;
|
||||||
|
self:next(2);
|
||||||
|
} elseif (nch == 'u') {
|
||||||
|
d ..= 'u';
|
||||||
|
self:next();
|
||||||
|
|
||||||
|
if (self:get() != '{') {
|
||||||
|
return null, Error:new('expected { after u escape', 'lexer', self.location:clone());
|
||||||
|
}
|
||||||
|
d ..= '{';
|
||||||
|
self:next();
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
while (self:isHex(self:get()) && (self:get() != '')) {
|
||||||
|
d ..= self:get();
|
||||||
|
self:next();
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i > 0) {
|
||||||
|
var hexpart = d:sub(-i);
|
||||||
|
var value = HexDecode(hexpart);
|
||||||
|
|
||||||
|
if (value >= 2^31) {
|
||||||
|
return null, Error:new('invalid utf8 codepoint in u escape', 'lexer', self.location:clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self:get() != '}') {
|
||||||
|
return null, Error:new('expected } to close u escape', 'lexer', self.location:clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
d ..= '}';
|
||||||
|
self:next();
|
||||||
|
} else {
|
||||||
|
return null, Error:new('unknown escape in string', 'lexer', self.location:clone());
|
||||||
|
}
|
||||||
|
} elseif (ch == '') {
|
||||||
|
break;
|
||||||
|
} elseif (ch == '"') {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
d ..= ch;
|
||||||
|
self:next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self:get() != '"') {
|
||||||
|
return null, Error:new('unfinished double-quoted string', 'lexer', self.location:clone());
|
||||||
|
}
|
||||||
|
d ..= '"';
|
||||||
|
self:next();
|
||||||
|
|
||||||
|
return Token:new('string', d, stpos);
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
var stloc = self.location:clone();
|
||||||
|
for (var i = 1; i <= #self.symbols; i += 1;) {
|
||||||
|
var symbol = self.symbols[i];
|
||||||
|
|
||||||
|
if (self:get(#symbol) == symbol) {
|
||||||
|
// ladies and gentlemen, we got him.
|
||||||
|
|
||||||
|
self:next(#symbol);
|
||||||
|
|
||||||
|
return Token:new('symbol', symbol, stloc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self:isIdentifierValid(self:get())) {
|
||||||
|
var ident = '';
|
||||||
|
var stpos = self.location:clone();
|
||||||
|
|
||||||
|
while (self:isIdentifierValid(self:get())) {
|
||||||
|
ident ..= self:get();
|
||||||
|
self:next();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 1; i <= #self.keywords; i += 1;) {
|
||||||
|
var kw = self.keywords[i];
|
||||||
|
|
||||||
|
if (ident == kw) {
|
||||||
|
return Token:new('keyword', kw, stpos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Token:new('identifier', ident, stpos);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null, Error:new('tried to lex invalid code', 'lexer', self.location:clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Lexer:peek() {
|
||||||
|
if (self.cache != null) {
|
||||||
|
return self.cache, null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var token, err = self:run();
|
||||||
|
self.cache = token;
|
||||||
|
return token, err;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Lexer:consume() {
|
||||||
|
if (self.cache == null) {
|
||||||
|
return self:run();
|
||||||
|
}
|
||||||
|
|
||||||
|
var token = self.cache;
|
||||||
|
self.cache = null;
|
||||||
|
return token, null;
|
||||||
|
}
|
||||||
155
src/lower.lake
Normal file
155
src/lower.lake
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
Lower = {};
|
||||||
|
|
||||||
|
fn Lower.makeUTF8Char(decimal) {
|
||||||
|
if (decimal < 128) {
|
||||||
|
return string.char(decimal);
|
||||||
|
} elseif (decimal < 2048) {
|
||||||
|
var byte2 = (128 + (decimal % 64));
|
||||||
|
var byte1 = (192 + math.floor(decimal / 64));
|
||||||
|
|
||||||
|
return string.char(byte1, byte2);
|
||||||
|
} elseif (decimal < 65536) {
|
||||||
|
var byte3 = (128 + (decimal % 64));
|
||||||
|
decimal = math.floor(decimal / 64);
|
||||||
|
var byte2 = (128 + (decimal % 64));
|
||||||
|
var byte1 = (224 + math.floor(decimal / 64));
|
||||||
|
|
||||||
|
return string.char(byte1, byte2, byte3);
|
||||||
|
} elseif (decimal < 2097152) {
|
||||||
|
var byte4 = (128 + (decimal % 64));
|
||||||
|
decimal = math.floor(decimal / 64);
|
||||||
|
var byte3 = (128 + (decimal % 64));
|
||||||
|
decimal = math.floor(decimal / 64);
|
||||||
|
var byte2 = (128 + (decimal % 64));
|
||||||
|
var byte1 = (240 + math.floor(decimal / 64));
|
||||||
|
|
||||||
|
return string.char(byte1,byte2,byte3,byte4);
|
||||||
|
} elseif (decimal < 67108864) {
|
||||||
|
// different code style cause new lol
|
||||||
|
var b1 = 248 + (math.floor(decimal / 16777216)); // my fav power of two!!
|
||||||
|
var b2 = 128 + (math.floor(decimal / 262144) % 64);
|
||||||
|
var b3 = 128 + (math.floor(decimal / 4096) % 64);
|
||||||
|
var b4 = 128 + (math.floor(decimal / 64) % 64);
|
||||||
|
var b5 = 128 + (decimal % 64);
|
||||||
|
|
||||||
|
return string.char(b1,b2,b3,b4,b5);
|
||||||
|
} elseif (decimal < 2147483648) {
|
||||||
|
var b1 = 252 + math.floor(decimal / 1073741824);
|
||||||
|
var b2 = 128 + (math.floor(decimal / 16777216) % 64);
|
||||||
|
var b3 = 128 + (math.floor(decimal / 262144) % 64);
|
||||||
|
var b4 = 128 + (math.floor(decimal / 4096) % 64);
|
||||||
|
var b5 = 128 + (math.floor(decimal / 64) % 64);
|
||||||
|
var b6 = 128 + (decimal % 64);
|
||||||
|
|
||||||
|
return string.char(b1, b2, b3, b4, b5, b6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Lower.decodeString(lakeString) {
|
||||||
|
if (lakeString:sub(1,1) == "'") {
|
||||||
|
return lakeString:sub(2,-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lakeString:sub(1,1) == '"') {
|
||||||
|
var i = 2;
|
||||||
|
var out = '';
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
var ch = lakeString:sub(i,i);
|
||||||
|
|
||||||
|
if (ch == '\') {
|
||||||
|
i += 1;
|
||||||
|
var nextch = lakeString:sub(i,i);
|
||||||
|
|
||||||
|
if (nextch == '\') {
|
||||||
|
out ..= '\';
|
||||||
|
i += 1;
|
||||||
|
} elseif (nextch == 'n') {
|
||||||
|
out ..= "\n";
|
||||||
|
i += 1;
|
||||||
|
} elseif (nextch == 'r') {
|
||||||
|
out ..= "\r";
|
||||||
|
i += 1;
|
||||||
|
} elseif (nextch == 'v') {
|
||||||
|
out ..= "\v";
|
||||||
|
i += 1;
|
||||||
|
} elseif (nextch == '"') {
|
||||||
|
out ..= '"';
|
||||||
|
i += 1;
|
||||||
|
} elseif (nextch == 'f') {
|
||||||
|
out ..= "\f";
|
||||||
|
i += 1;
|
||||||
|
} elseif (nextch == 't') {
|
||||||
|
out ..= "\t";
|
||||||
|
i += 1;
|
||||||
|
} elseif (nextch == 'a') {
|
||||||
|
out ..= "\x07";
|
||||||
|
i += 1;
|
||||||
|
} elseif (nextch == 'b') {
|
||||||
|
out ..= "\x08";
|
||||||
|
i += 1;
|
||||||
|
} elseif (nextch == 'x') {
|
||||||
|
i += 1;
|
||||||
|
|
||||||
|
var hex = lakeString:sub(i, i+1);
|
||||||
|
i += 2;
|
||||||
|
|
||||||
|
var decoded = HexDecode(hex);
|
||||||
|
|
||||||
|
out ..= string.char(decoded);
|
||||||
|
} elseif (nextch == 'u') {
|
||||||
|
i += 2;
|
||||||
|
|
||||||
|
var hexpart = '';
|
||||||
|
|
||||||
|
while (lakeString:sub(i,i) != '}') {
|
||||||
|
hexpart ..= lakeString:sub(i,i);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
|
||||||
|
var decoded = HexDecode(hexpart);
|
||||||
|
|
||||||
|
out ..= Lower.makeUTF8Char(decoded);
|
||||||
|
}
|
||||||
|
} elseif (ch == '"') {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
i += 1;
|
||||||
|
out ..= ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Lower.decodeNumber(num) {
|
||||||
|
var numeric = 0;
|
||||||
|
var decimal = false;
|
||||||
|
|
||||||
|
for (var i = 1; i <= #num; i += 1;) {
|
||||||
|
var ch = num:sub(i,i);
|
||||||
|
|
||||||
|
if (ch == '.') {
|
||||||
|
decimal = i + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
numeric *= 10;
|
||||||
|
numeric += string.byte(ch) - string.byte('0');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decimal) {
|
||||||
|
var n = 0.1;
|
||||||
|
for (var j = decimal; j <= #num; j += 1;) {
|
||||||
|
var ch = num:sub(j,j);
|
||||||
|
|
||||||
|
numeric += (string.byte(ch) - string.byte('0')) * n;
|
||||||
|
n *= 0.1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return numeric;
|
||||||
|
}
|
||||||
667
src/methods/debug.lake
Normal file
667
src/methods/debug.lake
Normal file
@@ -0,0 +1,667 @@
|
|||||||
|
Debug = {};
|
||||||
|
|
||||||
|
fn Debug.encodeString(raw, islua51) {
|
||||||
|
var out = '"';
|
||||||
|
|
||||||
|
for (var i = 1; i <= #raw; i += 1;) {
|
||||||
|
var ch = raw:sub(i,i);
|
||||||
|
|
||||||
|
var code = string.byte(ch);
|
||||||
|
if (ch == '\') {
|
||||||
|
out ..= '\\';
|
||||||
|
} elseif (ch == '"') {
|
||||||
|
out ..= '\"';
|
||||||
|
} elseif (code >= 32 && code <= 126) {
|
||||||
|
out ..= ch;
|
||||||
|
} else {
|
||||||
|
if (islua51)
|
||||||
|
out ..= '\' .. DecEncode(code, 3);
|
||||||
|
else
|
||||||
|
out ..= '\x' .. HexEncode(code, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out ..= '"';
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Debug.encodeNumber(n) {
|
||||||
|
var num = '';
|
||||||
|
var isneg = false;
|
||||||
|
|
||||||
|
if (n < 0) {
|
||||||
|
n *= -1;
|
||||||
|
isneg = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var whole = math.floor(n);
|
||||||
|
|
||||||
|
if (whole == 0) {
|
||||||
|
num ..= '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
while (whole > 0) {
|
||||||
|
var digit = whole % 10;
|
||||||
|
num = string.char(string.byte('0') + digit) .. num;
|
||||||
|
whole = math.floor(whole / 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
var dec = n % 1;
|
||||||
|
|
||||||
|
if (dec > 0) {
|
||||||
|
num ..= '.';
|
||||||
|
|
||||||
|
while (dec > 0) {
|
||||||
|
dec *= 10;
|
||||||
|
|
||||||
|
var digit = math.floor(dec);
|
||||||
|
num ..= string.char(string.byte('0') + digit);
|
||||||
|
|
||||||
|
dec %= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isneg) num = '-' .. num;
|
||||||
|
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Debug.convertBinaryOperator(lakeop) {
|
||||||
|
if (lakeop == '+') {
|
||||||
|
return '+';
|
||||||
|
} elseif (lakeop == "-" ) {
|
||||||
|
return "-";
|
||||||
|
} elseif ( lakeop == "*" ) {
|
||||||
|
return "*";
|
||||||
|
} elseif ( lakeop == "/" ) {
|
||||||
|
return "/";
|
||||||
|
} elseif ( lakeop == "%" ) {
|
||||||
|
return "%";
|
||||||
|
} elseif ( lakeop == "^" ) {
|
||||||
|
return "^";
|
||||||
|
} elseif ( lakeop == ".." ) {
|
||||||
|
return "..";
|
||||||
|
} elseif ( lakeop == "<" ) {
|
||||||
|
return "<";
|
||||||
|
} elseif ( lakeop == ">" ) {
|
||||||
|
return ">";
|
||||||
|
} elseif ( lakeop == "<=" ) {
|
||||||
|
return "<=";
|
||||||
|
} elseif ( lakeop == ">=" ) {
|
||||||
|
return ">=";
|
||||||
|
} elseif ( lakeop == "!=" ) {
|
||||||
|
return "~=";
|
||||||
|
} elseif ( lakeop == "==" ) {
|
||||||
|
return "==";
|
||||||
|
} elseif ( lakeop == "&&" ) {
|
||||||
|
return "and";
|
||||||
|
} elseif ( lakeop == "||" ) {
|
||||||
|
return "or";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Debug.convertUnaryOperator(op) {
|
||||||
|
if (op == '#') return '#';
|
||||||
|
elseif (op == '-') return '-';
|
||||||
|
elseif (op == '!') return 'not';
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Debug.output(outp, ast) {
|
||||||
|
Debug.outputStatement(outp, ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Debug.outputStatement(outp, node) {
|
||||||
|
if (node.kind == 'Program') {
|
||||||
|
outp:addLine('do', node.location:format());
|
||||||
|
|
||||||
|
for (var i = 1; i <= #node.content; i += 1;) {
|
||||||
|
var stmt = node.content[i];
|
||||||
|
|
||||||
|
Debug.outputStatement(outp, stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:addLine('end;', node.location:format());
|
||||||
|
} elseif (node.kind == 'VariableDeclaration') {
|
||||||
|
outp:addLine('local', node.location:format());
|
||||||
|
var varnames = node.content[1];
|
||||||
|
|
||||||
|
for (var i = 1; i <= #varnames; i += 1;) {
|
||||||
|
var vname = varnames[i];
|
||||||
|
|
||||||
|
outp:addLine(vname[1], vname[2]:format());
|
||||||
|
|
||||||
|
if (i != #varnames) {
|
||||||
|
outp:addLine(',', node.location:format());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var values = node.content[2];
|
||||||
|
if (#values > 0) {
|
||||||
|
outp:addLine('=', node.location:format());
|
||||||
|
|
||||||
|
for (var i = 1; i <= #values; i += 1;) {
|
||||||
|
var val = values[i];
|
||||||
|
|
||||||
|
Debug.outputExpression(outp, val);
|
||||||
|
|
||||||
|
if (i != #values) {
|
||||||
|
outp:addLine(',', node.location:format());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:addLine(';', node.location:format());
|
||||||
|
} elseif (node.kind == "Block") {
|
||||||
|
outp:addLine('do', node.location:format());
|
||||||
|
|
||||||
|
for (var i = 1; i <= #node.content; i += 1;) {
|
||||||
|
var stmt = node.content[i];
|
||||||
|
|
||||||
|
Debug.outputStatement(outp, stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:addLine('end;', node.location:format());
|
||||||
|
} elseif (node.kind == 'IfStatement') {
|
||||||
|
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 (var i = 2; i <= #node.content[1]; i += 1;) {
|
||||||
|
var item = node.content[1][i];
|
||||||
|
|
||||||
|
outp:addLine('elseif', node.location:format());
|
||||||
|
|
||||||
|
Debug.outputExpression(outp, item[1]);
|
||||||
|
|
||||||
|
outp:addLine('then', node.location:format());
|
||||||
|
|
||||||
|
Debug.outputStatement(outp, item[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.content[2]) {
|
||||||
|
outp:addLine('else', node.location:format());
|
||||||
|
|
||||||
|
Debug.outputStatement(outp, node.content[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:addLine('end;', node.location:format());
|
||||||
|
} elseif (node.kind == "FunctionDeclaration") {
|
||||||
|
outp:addLine('function', node.location:format());
|
||||||
|
|
||||||
|
Debug.outputFunctionName(outp, node.content[1]);
|
||||||
|
|
||||||
|
outp:addLine('(', node.location:format());
|
||||||
|
|
||||||
|
for (var i = 1; i <= #node.content[2]; i += 1;) {
|
||||||
|
var part = node.content[2][i];
|
||||||
|
|
||||||
|
outp:addLine(part, node.location:format());
|
||||||
|
|
||||||
|
if (i != #node.content[2]) {
|
||||||
|
outp:addLine(',', node.location:format());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:addLine(')', node.location:format());
|
||||||
|
|
||||||
|
Debug.outputStatement(outp, node.content[3]);
|
||||||
|
|
||||||
|
outp:addLine('end;', node.location:format());
|
||||||
|
} elseif (node.kind == 'LocalFunctionDeclaration') {
|
||||||
|
outp:addLine('local function', node.location:format());
|
||||||
|
|
||||||
|
outp:addLine(node.content[1].content, node.content[1].location:format());
|
||||||
|
|
||||||
|
outp:addLine('(', node.location:format());
|
||||||
|
|
||||||
|
for (var i = 1; i <= #node.content[2]; i += 1;) {
|
||||||
|
var part = node.content[2][i];
|
||||||
|
|
||||||
|
outp:addLine(part, node.location:format());
|
||||||
|
|
||||||
|
if (i != #node.content[2]) {
|
||||||
|
outp:addLine(',', node.location:format());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:addLine(')', node.location:format());
|
||||||
|
|
||||||
|
Debug.outputStatement(outp, node.content[3]);
|
||||||
|
|
||||||
|
outp:addLine('end;', node.location:format());
|
||||||
|
} elseif (node.kind == 'Return') {
|
||||||
|
outp:addLine('return', node.location:format()); // if true because it has to be at the end of a block in lua, but not here.
|
||||||
|
|
||||||
|
for (var i = 1; i <= #node.content; i += 1;) {
|
||||||
|
var expr = node.content[i];
|
||||||
|
|
||||||
|
Debug.outputExpression(outp, expr);
|
||||||
|
|
||||||
|
if (i != #node.content) {
|
||||||
|
outp:addLine(',', node.location:format());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:addLine(';', node.location:format());
|
||||||
|
} elseif (node.kind == 'Assignment') {
|
||||||
|
var vars = node.content[1];
|
||||||
|
var vals = node.content[2];
|
||||||
|
|
||||||
|
for (var i = 1; i <= #vars; i += 1;) {
|
||||||
|
var vari = vars[i];
|
||||||
|
|
||||||
|
Debug.outputVariable(outp, vari);
|
||||||
|
|
||||||
|
if (i != #vars) {
|
||||||
|
outp:addLine(',', node.location:format());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:addLine('=', node.location:format());
|
||||||
|
|
||||||
|
for (var i = 1; i <= #vals; i += 1;) {
|
||||||
|
var val = vals[i];
|
||||||
|
|
||||||
|
Debug.outputExpression(outp, val);
|
||||||
|
|
||||||
|
if (i != #vals) {
|
||||||
|
outp:addLine(',', node.location:format());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:addLine(';', node.location:format());
|
||||||
|
} elseif (node.kind == 'Call') {
|
||||||
|
outp:addLine('(', node.location:format());
|
||||||
|
Debug.outputExpression(outp, node.content[1]);
|
||||||
|
outp:addLine(')(', node.location:format());
|
||||||
|
|
||||||
|
var args = node.content[2];
|
||||||
|
for (var i = 1; i <= #args; i += 1;) {
|
||||||
|
var arg = args[i];
|
||||||
|
|
||||||
|
Debug.outputExpression(outp, arg);
|
||||||
|
|
||||||
|
if (i != #args) {
|
||||||
|
outp:addLine(',', node.location:format());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:addLine(');', node.location:format());
|
||||||
|
} elseif (node.kind == 'MethodCall') {
|
||||||
|
outp:addLine('(', node.location:format());
|
||||||
|
Debug.outputExpression(outp, node.content[1]);
|
||||||
|
outp:addLine('):', node.location:format());
|
||||||
|
outp:addLine(node.content[2] .. '(', node.location:format());
|
||||||
|
|
||||||
|
var args = node.content[3];
|
||||||
|
for (var i = 1; i <= #args; i += 1;) {
|
||||||
|
var arg = args[i];
|
||||||
|
|
||||||
|
Debug.outputExpression(outp, arg);
|
||||||
|
|
||||||
|
if (i != #args) {
|
||||||
|
outp:addLine(',', node.location:format());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:addLine(');', node.location:format());
|
||||||
|
} elseif (node.kind == 'While') {
|
||||||
|
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') {
|
||||||
|
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') {
|
||||||
|
var lakeop = node.content[2];
|
||||||
|
var op = Debug.convertBinaryOperator(lakeop);
|
||||||
|
|
||||||
|
var lhs = node.content[1];
|
||||||
|
var rhs = node.content[3];
|
||||||
|
|
||||||
|
if (lhs.kind == 'Variable') {
|
||||||
|
var 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') {
|
||||||
|
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') {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
} elseif (node.kind == 'Break') {
|
||||||
|
outp:addLine('break;', node.location:format());
|
||||||
|
} elseif (node.kind == 'Foreach') {
|
||||||
|
outp:addLine("for", node.location:format());
|
||||||
|
|
||||||
|
var varlist,exprlist,block = node.content[1],node.content[2],node.content[3];
|
||||||
|
|
||||||
|
for (var i = 1; i <= #varlist; i += 1;) {
|
||||||
|
outp:addLine(varlist[i], node.location:format());
|
||||||
|
if (i != #varlist) {
|
||||||
|
outp:addLine(',', node.location:format());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:addLine("in", node.location:format());
|
||||||
|
|
||||||
|
for (var i = 1; i <= #exprlist; i += 1;) {
|
||||||
|
Debug.outputExpression(outp, exprlist[i]);
|
||||||
|
|
||||||
|
if (i != #exprlist) {
|
||||||
|
outp:addLine(",", node.location:format());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:addLine('do', node.location:format());
|
||||||
|
|
||||||
|
Debug.outputStatement(outp, block);
|
||||||
|
|
||||||
|
outp:addLine('end;', node.location:format());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fn Debug.outputExpression(outp, node) {
|
||||||
|
if (node.kind == 'StringLiteral') {
|
||||||
|
var lakestr = node.content;
|
||||||
|
var raw = Lower.decodeString(lakestr);
|
||||||
|
var enc = Debug.encodeString(raw, outp.target == 'lua51');
|
||||||
|
|
||||||
|
outp:addLine(enc, node.location:format());
|
||||||
|
} elseif (node.kind == 'CompiledStringLiteral') {
|
||||||
|
var raw = node.content;
|
||||||
|
var enc = Debug.encodeString(raw, outp.target == 'lua51');
|
||||||
|
|
||||||
|
outp:addLine(enc, node.location:format());
|
||||||
|
} elseif (node.kind == 'ParenthesizedExpression') {
|
||||||
|
outp:addLine("(", node.location:format());
|
||||||
|
Debug.outputExpression(outp, node.content);
|
||||||
|
outp:addLine(")", node.location:format());
|
||||||
|
} elseif (node.kind == 'NumberLiteral') {
|
||||||
|
var lakenum = node.content;
|
||||||
|
|
||||||
|
var raw = Lower.decodeNumber(lakenum);
|
||||||
|
|
||||||
|
var enc = Debug.encodeNumber(raw);
|
||||||
|
|
||||||
|
outp:addLine("(", node.location:format());
|
||||||
|
outp:addLine(enc, node.location:format());
|
||||||
|
outp:addLine(")", node.location:format());
|
||||||
|
} elseif (node.kind == 'CompiledNumberLiteral') { // usually generated by optimizer doing math
|
||||||
|
var raw = node.content;
|
||||||
|
|
||||||
|
var enc = Debug.encodeNumber(raw);
|
||||||
|
|
||||||
|
outp:addLine("(", node.location:format());
|
||||||
|
outp:addLine(enc, node.location:format());
|
||||||
|
outp:addLine(")", node.location:format());
|
||||||
|
} elseif (node.kind == 'TableLiteral') {
|
||||||
|
outp:addLine('{', node.location:format());
|
||||||
|
var listpart = node.content[1];
|
||||||
|
var tablepart = node.content[2];
|
||||||
|
|
||||||
|
for (var i = 1; i <= #listpart; i += 1;) {
|
||||||
|
var item = listpart[i];
|
||||||
|
|
||||||
|
Debug.outputExpression(outp, item);
|
||||||
|
outp:addLine(',', node.location:format());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 1; i <= #tablepart; i += 1;) {
|
||||||
|
var item = tablepart[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());
|
||||||
|
}
|
||||||
|
outp:addLine('}', node.location:format());
|
||||||
|
} elseif (node.kind == 'LambdaFunctionLiteral') {
|
||||||
|
outp:addLine('function(', node.location:format());
|
||||||
|
|
||||||
|
for (var i = 1; i <= #node.content[1]; i += 1;) {
|
||||||
|
var part = node.content[1][i];
|
||||||
|
|
||||||
|
outp:addLine(part, node.location:format());
|
||||||
|
|
||||||
|
if (i != #node.content[1]) {
|
||||||
|
outp:addLine(",", node.location:format());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:addLine(")", node.location:format());
|
||||||
|
|
||||||
|
Debug.outputStatement(outp, node.content[2]);
|
||||||
|
|
||||||
|
outp:addLine("end", node.location:format());
|
||||||
|
} elseif (node.kind == 'BinaryOperator') {
|
||||||
|
var op = node.content[1];
|
||||||
|
var lhs = node.content[2];
|
||||||
|
var rhs = node.content[3];
|
||||||
|
|
||||||
|
outp:addLine("((", node.location:format());
|
||||||
|
|
||||||
|
Debug.outputExpression(outp,lhs);
|
||||||
|
|
||||||
|
outp:addLine(")", node.location:format());
|
||||||
|
|
||||||
|
var 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') {
|
||||||
|
var op = node.content[1];
|
||||||
|
var val = node.content[2];
|
||||||
|
|
||||||
|
outp:addLine("(", node.location:format());
|
||||||
|
|
||||||
|
var 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') {
|
||||||
|
var left = node.content[1];
|
||||||
|
var 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') {
|
||||||
|
var left = node.content[1];
|
||||||
|
var 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') {
|
||||||
|
outp:addLine("(", node.location:format());
|
||||||
|
outp:addLine(node.content, node.location:format());
|
||||||
|
outp:addLine(")", node.location:format());
|
||||||
|
} elseif (node.kind == 'BooleanLiteral') {
|
||||||
|
outp:addLine("(", node.location:format());
|
||||||
|
outp:addLine(node.content, node.location:format());
|
||||||
|
outp:addLine(")", node.location:format());
|
||||||
|
} elseif (node.kind == 'NullLiteral') {
|
||||||
|
outp:addLine("(nil)", node.location:format());
|
||||||
|
} elseif (node.kind == 'Call') {
|
||||||
|
outp:addLine("(", node.location:format());
|
||||||
|
Debug.outputExpression(outp, node.content[1]);
|
||||||
|
outp:addLine(")(", node.location:format());
|
||||||
|
|
||||||
|
var args = node.content[2];
|
||||||
|
for (var i = 1; i <= #args; i += 1;) {
|
||||||
|
var arg = args[i];
|
||||||
|
|
||||||
|
Debug.outputExpression(outp, arg);
|
||||||
|
|
||||||
|
if (i != #args) {
|
||||||
|
outp:addLine(",", node.location:format());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:addLine(")", node.location:format());
|
||||||
|
} elseif (node.kind == 'MethodCall') {
|
||||||
|
outp:addLine("(", node.location:format());
|
||||||
|
Debug.outputExpression(outp, node.content[1]);
|
||||||
|
outp:addLine("):", node.location:format());
|
||||||
|
outp:addLine(node.content[2] .. "(", node.location:format());
|
||||||
|
|
||||||
|
var args = node.content[3];
|
||||||
|
for (var i = 1; i <= #args; i += 1;) {
|
||||||
|
var arg = args[i];
|
||||||
|
|
||||||
|
Debug.outputExpression(outp, arg);
|
||||||
|
|
||||||
|
if (i != #args) {
|
||||||
|
outp:addLine(",", node.location:format());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:addLine(")", node.location:format());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fn Debug.outputFunctionName(outp, node) {
|
||||||
|
if (node.kind == 'Field') {
|
||||||
|
var left = node.content[1];
|
||||||
|
var right = node.content[2];
|
||||||
|
|
||||||
|
Debug.outputFunctionName(outp, left);
|
||||||
|
outp:addLine(".", node.location:format());
|
||||||
|
outp:addLine(right, node.location:format());
|
||||||
|
} elseif (node.kind == 'Index') {
|
||||||
|
var left = node.content[1];
|
||||||
|
var 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') {
|
||||||
|
outp:addLine(node.content, node.location:format());
|
||||||
|
} elseif (node.kind == 'Method') {
|
||||||
|
var left = node.content[1];
|
||||||
|
var right = node.content[2];
|
||||||
|
|
||||||
|
Debug.outputFunctionName(outp, left);
|
||||||
|
outp:addLine(":", node.location:format());
|
||||||
|
outp:addLine(right, node.location:format());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fn Debug.outputVariable(outp, node) {
|
||||||
|
if (node.kind == 'Field') {
|
||||||
|
var left = node.content[1];
|
||||||
|
var right = node.content[2];
|
||||||
|
|
||||||
|
Debug.outputExpression(outp, left);
|
||||||
|
outp:addLine(".", node.location:format());
|
||||||
|
outp:addLine(right, node.location:format());
|
||||||
|
} elseif (node.kind == 'Index') {
|
||||||
|
var left = node.content[1];
|
||||||
|
var 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') {
|
||||||
|
outp:addLine(node.content, node.location:format());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RegisterOutMethod("debug", Debug);
|
||||||
689
src/methods/small.lake
Normal file
689
src/methods/small.lake
Normal file
@@ -0,0 +1,689 @@
|
|||||||
|
Small = {};
|
||||||
|
|
||||||
|
fn Small.isDigit(ch) {
|
||||||
|
var n = string.byte(ch);
|
||||||
|
if (!n) return false;
|
||||||
|
return n >= 48 && n <= 57;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Small.encodeString(raw, islua51) {
|
||||||
|
var out = '"';
|
||||||
|
|
||||||
|
for (var i = 1; i <= #raw; i += 1;) {
|
||||||
|
var ch = raw:sub(i,i);
|
||||||
|
var code = string.byte(ch);
|
||||||
|
|
||||||
|
if (ch == '\') out ..= '\\';
|
||||||
|
elseif (ch == '"') out ..= '\"';
|
||||||
|
elseif (ch == "\n") out ..= '\n';
|
||||||
|
elseif (ch == "\r") out ..= '\r';
|
||||||
|
elseif (ch == "\x07") out ..= '\a';
|
||||||
|
elseif (ch == "\x08") out ..= '\b';
|
||||||
|
elseif (ch == "\x0c") out ..= '\f';
|
||||||
|
elseif (ch == "\t") out ..= '\t';
|
||||||
|
elseif (code >= 32 && code <= 126) out ..= ch;
|
||||||
|
else {
|
||||||
|
var nextchar = raw:sub(i+1,i+1);
|
||||||
|
if (Small.isDigit(nextchar))
|
||||||
|
out ..= '\' .. DecEncode(code, 3);
|
||||||
|
else
|
||||||
|
out ..= '\' .. DecEncode(code, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out ..= '"';
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Small.encodeNumber(n) {
|
||||||
|
var num = '';
|
||||||
|
var isneg = false;
|
||||||
|
|
||||||
|
if (n < 0) {
|
||||||
|
n *= -1;
|
||||||
|
isneg = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var whole = math.floor(n);
|
||||||
|
|
||||||
|
if (whole == 0) {
|
||||||
|
num ..= '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
while (whole > 0) {
|
||||||
|
var digit = whole % 10;
|
||||||
|
num = string.char(string.byte('0') + digit) .. num;
|
||||||
|
whole = math.floor(whole / 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
var dec = n % 1;
|
||||||
|
|
||||||
|
if (dec > 0) {
|
||||||
|
num ..= '.';
|
||||||
|
|
||||||
|
while (dec > 0) {
|
||||||
|
dec *= 10;
|
||||||
|
|
||||||
|
var digit = math.floor(dec);
|
||||||
|
num ..= string.char(string.byte('0') + digit);
|
||||||
|
|
||||||
|
dec %= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isneg) num = '-' .. num;
|
||||||
|
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Small.convertBinaryOperator(lakeop, spacer) {
|
||||||
|
if (lakeop == '+') return '+';
|
||||||
|
elseif ( lakeop == "-" ) return "-";
|
||||||
|
elseif ( lakeop == "*" ) return "*";
|
||||||
|
elseif ( lakeop == "/" ) return "/";
|
||||||
|
elseif ( lakeop == "%" ) return "%";
|
||||||
|
elseif ( lakeop == "^" ) return "^";
|
||||||
|
elseif ( lakeop == ".." ) return "..";
|
||||||
|
elseif ( lakeop == "<" ) return "<";
|
||||||
|
elseif ( lakeop == ">" ) return ">";
|
||||||
|
elseif ( lakeop == "<=" ) return "<=";
|
||||||
|
elseif ( lakeop == ">=" ) return ">=";
|
||||||
|
elseif ( lakeop == "!=" ) return "~=";
|
||||||
|
elseif ( lakeop == "==" ) return "==";
|
||||||
|
elseif ( lakeop == "&&" ) {
|
||||||
|
if (spacer) return " and ";
|
||||||
|
else return "and";
|
||||||
|
} elseif ( lakeop == "||" ) {
|
||||||
|
if (spacer) return " or ";
|
||||||
|
else return 'or';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Small.convertUnaryOperator(op) {
|
||||||
|
if (op == '#') return '#';
|
||||||
|
elseif (op == '-') return '-';
|
||||||
|
elseif (op == '!') return 'not';
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Small.output(outp, ast) {
|
||||||
|
return Small.outputStatement(outp, ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fn Small.outputStatement(outp, node, noblock) {
|
||||||
|
if (node.kind == 'Program') {
|
||||||
|
outp:appendLine('do ', '');
|
||||||
|
|
||||||
|
for (var i = 1; i <= #node.content; i += 1;) {
|
||||||
|
var stmt = node.content[i];
|
||||||
|
|
||||||
|
Small.outputStatement(outp, stmt, #node.content == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:appendLine('end;', '');
|
||||||
|
} elseif (node.kind == 'VariableDeclaration') {
|
||||||
|
outp:appendLine('local ', '');
|
||||||
|
var varnames = node.content[1];
|
||||||
|
|
||||||
|
for (var i = 1; i <= #varnames; i += 1;) {
|
||||||
|
var vname = varnames[i];
|
||||||
|
|
||||||
|
outp:appendLine(vname[1], '');
|
||||||
|
|
||||||
|
if (i != #varnames) {
|
||||||
|
outp:appendLine(',', '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var values = node.content[2];
|
||||||
|
if (#values > 0) {
|
||||||
|
outp:appendLine('=', '');
|
||||||
|
|
||||||
|
for (var i = 1; i <= #values; i += 1;) {
|
||||||
|
var val = values[i];
|
||||||
|
|
||||||
|
Small.outputExpression(outp, val, false);
|
||||||
|
|
||||||
|
if (i != #values) {
|
||||||
|
outp:appendLine(',', '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:appendLine(';', '');
|
||||||
|
} elseif (node.kind == 'Block') {
|
||||||
|
if (!noblock) outp:appendLine('do ', '');
|
||||||
|
|
||||||
|
for (var i = 1; i <= #node.content; i += 1;) {
|
||||||
|
var stmt = node.content[i];
|
||||||
|
|
||||||
|
Small.outputStatement(outp, stmt, i == #node.content);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!noblock) outp:appendLine('end;', '');
|
||||||
|
} elseif (node.kind == 'IfStatement') {
|
||||||
|
outp:appendLine("if ", '');
|
||||||
|
|
||||||
|
Small.outputExpression(outp, node.content[1][1][1]);
|
||||||
|
|
||||||
|
outp:appendLine("then ", '');
|
||||||
|
|
||||||
|
Small.outputStatement(outp, node.content[1][1][2], true);
|
||||||
|
|
||||||
|
for (var i = 2; i <= #node.content[1]; i += 1;) {
|
||||||
|
var item = node.content[1][i];
|
||||||
|
|
||||||
|
outp:appendLine("elseif ", '');
|
||||||
|
|
||||||
|
Small.outputExpression(outp,item[1]);
|
||||||
|
|
||||||
|
outp:appendLine("then ", '');
|
||||||
|
|
||||||
|
Small.outputStatement(outp,item[2], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.content[2]) {
|
||||||
|
outp:appendLine("else ", '');
|
||||||
|
|
||||||
|
Small.outputStatement(outp, node.content[2], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:appendLine("end;", '');
|
||||||
|
} elseif (node.kind == 'FunctionDeclaration') {
|
||||||
|
outp:appendLine("function ", '');
|
||||||
|
|
||||||
|
Small.outputFunctionName(outp, node.content[1]);
|
||||||
|
|
||||||
|
outp:appendLine("(", '');
|
||||||
|
|
||||||
|
for (var i = 1; i <= #node.content[2]; i += 1;) {
|
||||||
|
var part = node.content[2][i];
|
||||||
|
|
||||||
|
outp:appendLine(part, '');
|
||||||
|
|
||||||
|
if (i != #node.content[2]) {
|
||||||
|
outp:appendLine(",", '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:appendLine(")", '');
|
||||||
|
|
||||||
|
Small.outputStatement(outp, node.content[3], true);
|
||||||
|
|
||||||
|
outp:appendLine("end;", '');
|
||||||
|
} elseif (node.kind == 'LocalFunctionDeclaration') {
|
||||||
|
outp:appendLine("local function ", '');
|
||||||
|
|
||||||
|
outp:appendLine(node.content[1].content, '');
|
||||||
|
|
||||||
|
outp:appendLine("(", '');
|
||||||
|
|
||||||
|
for (var i = 1; i <= #node.content[2]; i += 1;) {
|
||||||
|
var part = node.content[2][i];
|
||||||
|
|
||||||
|
outp:appendLine(part, '');
|
||||||
|
|
||||||
|
if (i != #node.content[2]) {
|
||||||
|
outp:appendLine(",", '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:appendLine(")", '');
|
||||||
|
|
||||||
|
Small.outputStatement(outp, node.content[3], true);
|
||||||
|
|
||||||
|
outp:appendLine("end;", '');
|
||||||
|
} elseif (node.kind == 'Return') {
|
||||||
|
if (!noblock) {
|
||||||
|
outp:appendLine("if''then ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (#node.content > 0) {
|
||||||
|
outp:appendLine("return ", '');
|
||||||
|
} else {
|
||||||
|
outp:appendLine('return', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 1; i <= #node.content; i += 1;) {
|
||||||
|
var expr = node.content[i];
|
||||||
|
|
||||||
|
Small.outputExpression(outp, expr, false);
|
||||||
|
|
||||||
|
if (i != #node.content) {
|
||||||
|
outp:appendLine(",", '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:appendLine(";", '');
|
||||||
|
if (!noblock) {
|
||||||
|
outp:appendLine("end;");
|
||||||
|
}
|
||||||
|
} elseif (node.kind == 'Assignment') {
|
||||||
|
var vars = node.content[1];
|
||||||
|
var vals = node.content[2];
|
||||||
|
|
||||||
|
for (var i = 1; i <= #vars; i += 1;) {
|
||||||
|
var vari = vars[i];
|
||||||
|
|
||||||
|
Small.outputVariable(outp, vari);
|
||||||
|
|
||||||
|
if (i != #vars) {
|
||||||
|
outp:appendLine(",", '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:appendLine("=", '');
|
||||||
|
|
||||||
|
for (var i = 1; i <= #vals; i += 1;) {
|
||||||
|
var val = vals[i];
|
||||||
|
|
||||||
|
Small.outputExpression(outp, val, false);
|
||||||
|
|
||||||
|
if (i != #vals) {
|
||||||
|
outp:appendLine(",", '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:appendLine(";", '');
|
||||||
|
} elseif (node.kind == 'Call') {
|
||||||
|
outp:appendLine("(", '');
|
||||||
|
Small.outputExpression(outp, node.content[1], false);
|
||||||
|
outp:appendLine(")", '');
|
||||||
|
|
||||||
|
outp:appendLine("(", '');
|
||||||
|
|
||||||
|
var args = node.content[2];
|
||||||
|
for (var i = 1; i <= #args; i += 1;) {
|
||||||
|
var arg = args[i];
|
||||||
|
|
||||||
|
Small.outputExpression(outp, arg, false);
|
||||||
|
|
||||||
|
if (i != #args) {
|
||||||
|
outp:appendLine(",", '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:appendLine(")", '');
|
||||||
|
outp:appendLine(";", '');
|
||||||
|
} elseif (node.kind == 'MethodCall') {
|
||||||
|
outp:appendLine("(", '');
|
||||||
|
Small.outputExpression(outp, node.content[1], false);
|
||||||
|
outp:appendLine("):", '');
|
||||||
|
outp:appendLine(node.content[2], '');
|
||||||
|
outp:appendLine("(", '');
|
||||||
|
|
||||||
|
var args = node.content[3];
|
||||||
|
for (var i = 1; i <= #args; i += 1;) {
|
||||||
|
var arg = args[i];
|
||||||
|
|
||||||
|
Small.outputExpression(outp, arg, false);
|
||||||
|
|
||||||
|
if (i != #args) {
|
||||||
|
outp:appendLine(",", '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:appendLine(")", '');
|
||||||
|
outp:appendLine(";", '');
|
||||||
|
} elseif (node.kind == 'While') {
|
||||||
|
outp:appendLine("while ", '');
|
||||||
|
|
||||||
|
Small.outputExpression(outp, node.content[1]);
|
||||||
|
|
||||||
|
outp:appendLine("do ", '');
|
||||||
|
|
||||||
|
Small.outputStatement(outp, node.content[2], true);
|
||||||
|
|
||||||
|
outp:appendLine("end;", '');
|
||||||
|
} elseif (node.kind == 'For') {
|
||||||
|
outp:appendLine("do ", '');
|
||||||
|
Small.outputStatement(outp,node.content[1]);
|
||||||
|
|
||||||
|
outp:appendLine("while ", '');
|
||||||
|
Small.outputExpression(outp,node.content[2]);
|
||||||
|
outp:appendLine("do ", '');
|
||||||
|
|
||||||
|
outp:appendLine("do ", '');
|
||||||
|
Small.outputStatement(outp,node.content[4], true);
|
||||||
|
outp:appendLine("end;", '');
|
||||||
|
|
||||||
|
Small.outputStatement(outp,node.content[3], true);
|
||||||
|
|
||||||
|
outp:appendLine("end;", '');
|
||||||
|
|
||||||
|
outp:appendLine("end;", '');
|
||||||
|
} elseif (node.kind == 'OperatorAssignment') {
|
||||||
|
var lakeop = node.content[2];
|
||||||
|
var op = Small.convertBinaryOperator(lakeop, true);
|
||||||
|
|
||||||
|
var lhs = node.content[1];
|
||||||
|
var rhs = node.content[3];
|
||||||
|
|
||||||
|
if (lhs.kind == "Variable") {
|
||||||
|
var varname = lhs.content;
|
||||||
|
|
||||||
|
outp:appendLine(varname, '');
|
||||||
|
outp:appendLine('=', '');
|
||||||
|
outp:appendLine(varname, '');
|
||||||
|
outp:appendLine(op, '');
|
||||||
|
outp:appendLine('(', '');
|
||||||
|
Small.outputExpression(outp, rhs, false);
|
||||||
|
outp:appendLine(');', '');
|
||||||
|
} elseif (lhs.kind == "Field") {
|
||||||
|
outp:appendLine('do ', '');
|
||||||
|
|
||||||
|
outp:appendLine('local _lake_oat=', '');
|
||||||
|
Small.outputExpression(outp, lhs.content[1], false);
|
||||||
|
outp:appendLine(';', '');
|
||||||
|
|
||||||
|
outp:appendLine('_lake_oat.', '');
|
||||||
|
outp:appendLine(lhs.content[2], '');
|
||||||
|
|
||||||
|
outp:appendLine('=', '');
|
||||||
|
|
||||||
|
outp:appendLine('_lake_oat.', '');
|
||||||
|
outp:appendLine(lhs.content[2], '');
|
||||||
|
outp:appendLine(op, '');
|
||||||
|
outp:appendLine('(', '');
|
||||||
|
Small.outputExpression(outp, rhs, false);
|
||||||
|
outp:appendLine(');', '');
|
||||||
|
|
||||||
|
outp:appendLine('end;', '');
|
||||||
|
} elseif (lhs.kind == "Index") {
|
||||||
|
outp:appendLine('do ', '');
|
||||||
|
|
||||||
|
outp:appendLine('local _lake_oat=', '');
|
||||||
|
Small.outputExpression(outp, lhs.content[1], false);
|
||||||
|
outp:appendLine(';', '');
|
||||||
|
|
||||||
|
outp:appendLine('_lake_oat[', '');
|
||||||
|
Small.outputExpression(outp, lhs.content[2], false);
|
||||||
|
outp:appendLine(']', '');
|
||||||
|
|
||||||
|
outp:appendLine('=', '');
|
||||||
|
|
||||||
|
outp:appendLine('_lake_oat[', '');
|
||||||
|
Small.outputExpression(outp, lhs.content[2], false);
|
||||||
|
outp:appendLine(']', '');
|
||||||
|
outp:appendLine(op, '');
|
||||||
|
outp:appendLine('(', '');
|
||||||
|
Small.outputExpression(outp, rhs, false);
|
||||||
|
outp:appendLine(');', '');
|
||||||
|
|
||||||
|
outp:appendLine('end;', '');
|
||||||
|
}
|
||||||
|
} elseif (node.kind == 'Break') {
|
||||||
|
outp:appendLine('break;', '');
|
||||||
|
} elseif (node.kind == 'Foreach') {
|
||||||
|
outp:appendLine("for ", '');
|
||||||
|
|
||||||
|
var varlist,exprlist,block = node.content[1],node.content[2],node.content[3];
|
||||||
|
|
||||||
|
for (var i = 1; i <= #varlist; i += 1;) {
|
||||||
|
outp:appendLine(varlist[i], '');
|
||||||
|
if (i != #varlist) {
|
||||||
|
outp:appendLine(',', '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:appendLine(" in ", '');
|
||||||
|
|
||||||
|
for (var i = 1; i <= #exprlist; i += 1;) {
|
||||||
|
Small.outputExpression(outp, exprlist[i], i == #exprlist);
|
||||||
|
|
||||||
|
if (i != #exprlist) {
|
||||||
|
outp:appendLine(",", '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:appendLine('do ', '');
|
||||||
|
|
||||||
|
Small.outputStatement(outp, block, true);
|
||||||
|
|
||||||
|
outp:appendLine('end;', '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fn Small.outputExpression(outp, node, needsp) {
|
||||||
|
if (needsp == null) needsp = true;
|
||||||
|
if (node.kind == 'StringLiteral') {
|
||||||
|
var lakestr = node.content;
|
||||||
|
|
||||||
|
var raw = Lower.decodeString(lakestr);
|
||||||
|
|
||||||
|
var enc = Small.encodeString(raw, outp.target == "lua51");
|
||||||
|
|
||||||
|
outp:appendLine(enc, '');
|
||||||
|
} elseif (node.kind == 'CompiledStringLiteral') {
|
||||||
|
var raw = node.content;
|
||||||
|
|
||||||
|
var enc = Small.encodeString(raw, outp.target == "lua51");
|
||||||
|
|
||||||
|
outp:appendLine(enc, '');
|
||||||
|
} elseif (node.kind == 'ParenthesizedExpression') {
|
||||||
|
outp:appendLine("(", '');
|
||||||
|
Small.outputExpression(outp, node.content, false);
|
||||||
|
outp:appendLine(")", '');
|
||||||
|
} elseif (node.kind == 'NumberLiteral') {
|
||||||
|
var lakenum = node.content;
|
||||||
|
var raw = Lower.decodeNumber(lakenum);
|
||||||
|
var enc = Small.encodeNumber(raw);
|
||||||
|
|
||||||
|
outp:appendLine(enc, '');
|
||||||
|
|
||||||
|
if (needsp) outp:appendLine(' ', '');
|
||||||
|
} elseif (node.kind == 'CompiledNumberLiteral') {
|
||||||
|
var raw = node.content;
|
||||||
|
var enc = Small.encodeNumber(raw);
|
||||||
|
|
||||||
|
outp:appendLine(enc, '');
|
||||||
|
|
||||||
|
if (needsp) outp:appendLine(' ', '');
|
||||||
|
} elseif (node.kind == 'TableLiteral') {
|
||||||
|
outp:appendLine('{', '');
|
||||||
|
var listpart = node.content[1];
|
||||||
|
var tablepart = node.content[2];
|
||||||
|
|
||||||
|
for (var i = 1; i <= #listpart; i += 1;) {
|
||||||
|
var item = listpart[i];
|
||||||
|
|
||||||
|
Small.outputExpression(outp, item, false);
|
||||||
|
|
||||||
|
if ((#tablepart > 0) || (i < #listpart)) {
|
||||||
|
outp:appendLine(',', '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 1; i <= #tablepart; i += 1;) {
|
||||||
|
var item = tablepart[i];
|
||||||
|
|
||||||
|
outp:appendLine('[', '');
|
||||||
|
Small.outputExpression(outp, item[1], false);
|
||||||
|
outp:appendLine(']', '');
|
||||||
|
outp:appendLine('=', '');
|
||||||
|
Small.outputExpression(outp, item[2], false);
|
||||||
|
if (i < #tablepart) {
|
||||||
|
outp:appendLine(',', '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outp:appendLine('}', '');
|
||||||
|
} elseif (node.kind == 'LambdaFunctionLiteral') {
|
||||||
|
outp:appendLine('function(', '');
|
||||||
|
|
||||||
|
for (var i = 1; i <= #node.content[1]; i += 1;) {
|
||||||
|
var part = node.content[1][i];
|
||||||
|
|
||||||
|
outp:appendLine(part, '');
|
||||||
|
|
||||||
|
if (i != #node.content[1]) {
|
||||||
|
outp:appendLine(",", '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:appendLine(")", '');
|
||||||
|
|
||||||
|
Small.outputStatement(outp, node.content[2], true);
|
||||||
|
|
||||||
|
outp:appendLine("end", '');
|
||||||
|
if (needsp) outp:appendLine(' ', '');
|
||||||
|
} elseif (node.kind == 'BinaryOperator') {
|
||||||
|
var op = node.content[1];
|
||||||
|
var lhs = node.content[2];
|
||||||
|
var rhs = node.content[3];
|
||||||
|
|
||||||
|
outp:appendLine("((", '');
|
||||||
|
|
||||||
|
Small.outputExpression(outp,lhs, false);
|
||||||
|
|
||||||
|
outp:appendLine(")", '');
|
||||||
|
|
||||||
|
var luaop = Small.convertBinaryOperator(op);
|
||||||
|
|
||||||
|
outp:appendLine(luaop, '');
|
||||||
|
|
||||||
|
outp:appendLine("(", '');
|
||||||
|
|
||||||
|
Small.outputExpression(outp,rhs,false);
|
||||||
|
|
||||||
|
outp:appendLine("))", '');
|
||||||
|
} elseif (node.kind == 'UnaryOperator') {
|
||||||
|
var op = node.content[1];
|
||||||
|
var val = node.content[2];
|
||||||
|
|
||||||
|
outp:appendLine("(", '');
|
||||||
|
|
||||||
|
var luaop = Small.convertUnaryOperator(op);
|
||||||
|
outp:appendLine(luaop, '');
|
||||||
|
|
||||||
|
outp:appendLine("(", '');
|
||||||
|
|
||||||
|
Small.outputExpression(outp,val,false);
|
||||||
|
|
||||||
|
outp:appendLine("))", '');
|
||||||
|
} elseif (node.kind == 'Field') {
|
||||||
|
var left = node.content[1];
|
||||||
|
var right = node.content[2];
|
||||||
|
|
||||||
|
outp:appendLine("(", '');
|
||||||
|
Small.outputExpression(outp, left, false);
|
||||||
|
outp:appendLine(").", '');
|
||||||
|
outp:appendLine(right, '');
|
||||||
|
if (needsp) outp:appendLine(' ', '');
|
||||||
|
} elseif (node.kind == 'Index') {
|
||||||
|
var left = node.content[1];
|
||||||
|
var right = node.content[2];
|
||||||
|
|
||||||
|
outp:appendLine("(", '');
|
||||||
|
Small.outputExpression(outp, left, false);
|
||||||
|
outp:appendLine(")", '');
|
||||||
|
outp:appendLine("[", '');
|
||||||
|
Small.outputExpression(outp, right, false);
|
||||||
|
outp:appendLine("]", '');
|
||||||
|
} elseif (node.kind == 'Variable') {
|
||||||
|
outp:appendLine(node.content, '');
|
||||||
|
if (needsp) outp:appendLine(' ', '');
|
||||||
|
} elseif (node.kind == 'BooleanLiteral') {
|
||||||
|
outp:appendLine(node.content, '');
|
||||||
|
if (needsp) outp:appendLine(' ', '');
|
||||||
|
} elseif (node.kind == 'NullLiteral') {
|
||||||
|
outp:appendLine("nil", '');
|
||||||
|
if (needsp) outp:appendLine(' ', '');
|
||||||
|
} elseif (node.kind == 'Call') {
|
||||||
|
outp:appendLine("(", '');
|
||||||
|
Small.outputExpression(outp, node.content[1], false);
|
||||||
|
outp:appendLine(")", '');
|
||||||
|
|
||||||
|
outp:appendLine("(", '');
|
||||||
|
|
||||||
|
var args = node.content[2];
|
||||||
|
for (var i = 1; i <= #args; i += 1;) {
|
||||||
|
var arg = args[i];
|
||||||
|
|
||||||
|
Small.outputExpression(outp, arg, false);
|
||||||
|
|
||||||
|
if (i != #args) {
|
||||||
|
outp:appendLine(",", '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:appendLine(")", '');
|
||||||
|
} elseif (node.kind == 'MethodCall') {
|
||||||
|
outp:appendLine("(", '');
|
||||||
|
Small.outputExpression(outp, node.content[1], false);
|
||||||
|
outp:appendLine("):", '');
|
||||||
|
outp:appendLine(node.content[2], '');
|
||||||
|
outp:appendLine("(", '');
|
||||||
|
|
||||||
|
var args = node.content[3];
|
||||||
|
for (var i = 1; i <= #args; i += 1;) {
|
||||||
|
var arg = args[i];
|
||||||
|
|
||||||
|
Small.outputExpression(outp, arg, false);
|
||||||
|
|
||||||
|
if (i != #args) {
|
||||||
|
outp:appendLine(",", '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outp:appendLine(")", '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fn Small.outputFunctionName(outp, node) {
|
||||||
|
if (node.kind == "Field") {
|
||||||
|
var left = node.content[1];
|
||||||
|
var right = node.content[2];
|
||||||
|
|
||||||
|
Small.outputFunctionName(outp, left);
|
||||||
|
outp:appendLine(".", '');
|
||||||
|
outp:appendLine(right, '');
|
||||||
|
} elseif (node.kind == "Index") {
|
||||||
|
var left = node.content[1];
|
||||||
|
var right = node.content[2];
|
||||||
|
|
||||||
|
Small.outputFunctionName(outp, left);
|
||||||
|
outp:appendLine("[", '');
|
||||||
|
Small.outputExpression(outp, right, false);
|
||||||
|
outp:appendLine("]", '');
|
||||||
|
} elseif (node.kind == "Variable") {
|
||||||
|
outp:appendLine(node.content, '');
|
||||||
|
} elseif (node.kind == "Method") {
|
||||||
|
var left = node.content[1];
|
||||||
|
var right = node.content[2];
|
||||||
|
|
||||||
|
Small.outputFunctionName(outp, left);
|
||||||
|
outp:appendLine(":", '');
|
||||||
|
outp:appendLine(right, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fn Small.outputVariable(outp, node) {
|
||||||
|
if (node.kind == "Field") {
|
||||||
|
var left = node.content[1];
|
||||||
|
var right = node.content[2];
|
||||||
|
|
||||||
|
Small.outputExpression(outp, left, false);
|
||||||
|
outp:appendLine(".", '');
|
||||||
|
outp:appendLine(right, '');
|
||||||
|
} elseif (node.kind == "Index") {
|
||||||
|
var left = node.content[1];
|
||||||
|
var right = node.content[2];
|
||||||
|
|
||||||
|
Small.outputExpression(outp, left, false);
|
||||||
|
outp:appendLine("[", '');
|
||||||
|
Small.outputExpression(outp, right, false);
|
||||||
|
outp:appendLine("]", '');
|
||||||
|
} elseif (node.kind == "Variable") {
|
||||||
|
outp:appendLine(node.content, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RegisterOutMethod('small', Small);
|
||||||
259
src/optimizer.lake
Normal file
259
src/optimizer.lake
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
Optimizer = {};
|
||||||
|
|
||||||
|
fn Optimizer:new() {
|
||||||
|
return setmetatable({}, {'__index' = self});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Optimizer:run(ast) { // in-place optimization!!
|
||||||
|
self:optimizeStatement(ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Optimizer:optimizeStatement(node, justBlock) {
|
||||||
|
if (node.kind == 'Program') {
|
||||||
|
for (var i = 1; i <= #node.content; i += 1;) {
|
||||||
|
var substmt = node.content[i];
|
||||||
|
|
||||||
|
self:optimizeStatement(substmt);
|
||||||
|
}
|
||||||
|
} elseif (node.kind == 'VariableDeclaration') {
|
||||||
|
// var varnames = node.content[1];
|
||||||
|
|
||||||
|
// for (var i = 1; i <= #varnames; i += 1;) {
|
||||||
|
// var vname = varnames[i];
|
||||||
|
// }
|
||||||
|
|
||||||
|
var values = node.content[2];
|
||||||
|
for (var i = 1; i <= #values; i += 1;) {
|
||||||
|
var val = values[i];
|
||||||
|
|
||||||
|
self:optimizeExpression(val);
|
||||||
|
}
|
||||||
|
} elseif (node.kind == 'Block') {
|
||||||
|
for (var i = 1; i <= #node.content; i += 1;) {
|
||||||
|
var stmt = node.content[i];
|
||||||
|
|
||||||
|
self:optimizeStatement(stmt, i == #node.content); // TODO: make sure i can do this
|
||||||
|
}
|
||||||
|
|
||||||
|
if (#node.content == 1 && justBlock) { // eat the block
|
||||||
|
var subn = node.content[1];
|
||||||
|
|
||||||
|
node.kind = subn.kind; // should work fine to do this
|
||||||
|
node.content = subn.content;
|
||||||
|
node.location = subn.location;
|
||||||
|
}
|
||||||
|
} elseif (node.kind == 'IfStatement') {
|
||||||
|
self:optimizeExpression(node.content[1][1][1]); // condition
|
||||||
|
|
||||||
|
self:optimizeStatement(node.content[1][1][2], true); // base body
|
||||||
|
|
||||||
|
// elseif chain:
|
||||||
|
for (var i = 2; i <= #node.content[1]; i += 1;) {
|
||||||
|
var item = node.content[1][i];
|
||||||
|
|
||||||
|
self:optimizeExpression(item[1]); // condition
|
||||||
|
|
||||||
|
self:optimizeStatement(item[2], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// else
|
||||||
|
if (node.content[2]) {
|
||||||
|
self:optimizeStatement(node.content[2], true);
|
||||||
|
}
|
||||||
|
} elseif (node.kind == 'FunctionDeclaration') {
|
||||||
|
// TODO: function name?
|
||||||
|
|
||||||
|
self:optimizeStatement(node.content[3], true);
|
||||||
|
} elseif (node.kind == 'LocalFunctionDeclaration') {
|
||||||
|
self:optimizeStatement(node.content[3], true);
|
||||||
|
} elseif (node.kind == 'Return') {
|
||||||
|
for (var i = 1; i <= #node.content; i += 1;) {
|
||||||
|
var expr = node.content[i];
|
||||||
|
|
||||||
|
self:optimizeExpression(expr);
|
||||||
|
}
|
||||||
|
} elseif (node.kind == 'Assignment') {
|
||||||
|
// var vars = node.content[1];
|
||||||
|
var vals = node.content[2];
|
||||||
|
|
||||||
|
for (var i = 1; i <= #vals; i += 1;) {
|
||||||
|
var val = vals[i];
|
||||||
|
|
||||||
|
self:optimizeExpression(val);
|
||||||
|
}
|
||||||
|
} elseif (node.kind == 'Call') {
|
||||||
|
self:optimizeExpression(node.content[1]); // function name
|
||||||
|
|
||||||
|
var args = node.content[2];
|
||||||
|
for (var i = 1; i <= #args; i += 1;) {
|
||||||
|
var arg = args[i];
|
||||||
|
|
||||||
|
self:optimizeExpression(arg);
|
||||||
|
}
|
||||||
|
} elseif (node.kind == 'MethodCall') {
|
||||||
|
self:optimizeExpression(node.content[1]);
|
||||||
|
|
||||||
|
var args = node.content[3];
|
||||||
|
for (var i = 1; i <= #args; i += 1;) {
|
||||||
|
var arg = args[i];
|
||||||
|
|
||||||
|
self:optimizeExpression(arg);
|
||||||
|
}
|
||||||
|
} elseif (node.kind == 'While') {
|
||||||
|
self:optimizeExpression(node.content[1]);
|
||||||
|
|
||||||
|
self:optimizeStatement(node.content[2], true);
|
||||||
|
} elseif (node.kind == 'For') {
|
||||||
|
self:optimizeStatement(node.content[1]);
|
||||||
|
|
||||||
|
self:optimizeExpression(node.content[2]);
|
||||||
|
|
||||||
|
self:optimizeStatement(node.content[4], true);
|
||||||
|
|
||||||
|
self:optimizeStatement(node.content[3], true);
|
||||||
|
} elseif (node.kind == 'OperatorAssignment') {
|
||||||
|
self:optimizeExpression(node.content[3]); // rhs
|
||||||
|
} elseif (node.kind == 'Break') {
|
||||||
|
// nothing
|
||||||
|
} elseif (node.kind == 'Foreach') {
|
||||||
|
// exprs!!
|
||||||
|
var exprlist = node.content[2];
|
||||||
|
|
||||||
|
for (var i = 1; i <= #exprlist; i += 1;) {
|
||||||
|
self:optimizeExpression(exprlist[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var block = node.content[3];
|
||||||
|
|
||||||
|
self:optimizeStatement(block, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Optimizer:optimizeExpression(node) {
|
||||||
|
if (node.kind == 'StringLiteral') {
|
||||||
|
// nothing
|
||||||
|
} elseif (node.kind == 'ParenthesizedExpression') {
|
||||||
|
// todo: check if parentheses required
|
||||||
|
var subn = node.content;
|
||||||
|
|
||||||
|
self:optimizeExpression(subn);
|
||||||
|
|
||||||
|
if (subn.kind == 'ParenthesizedExpression' || subn.kind == 'Variable' || subn.kind == 'NumberLiteral' || subn.kind == 'CompiledNumberLiteral' || subn.kind == 'StringLiteral' || subn.kind == 'CompiledStringLiteral' || subn.kind == 'BooleanLiteral' || subn.kind == 'NullLiteral' || subn.kind == 'TableLiteral') {
|
||||||
|
node.kind = subn.kind;
|
||||||
|
node.content = subn.content;
|
||||||
|
node.location = subn.location;
|
||||||
|
}
|
||||||
|
} elseif (node.kind == 'NumberLiteral') {
|
||||||
|
// nothing
|
||||||
|
} elseif (node.kind == 'TableLiteral') {
|
||||||
|
// todo: maybe remove duplicate entries or something?
|
||||||
|
var listpart = node.content[1];
|
||||||
|
var tablepart = node.content[2];
|
||||||
|
|
||||||
|
for (var i = 1; i <= #listpart; i += 1;) {
|
||||||
|
var item = listpart[i];
|
||||||
|
|
||||||
|
self:optimizeExpression(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 1; i <= #tablepart; i += 1;) {
|
||||||
|
var item = tablepart[i];
|
||||||
|
|
||||||
|
self:optimizeExpression(item[1]);
|
||||||
|
self:optimizeExpression(item[2]);
|
||||||
|
}
|
||||||
|
} elseif (node.kind == 'LambdaFunctionLiteral') {
|
||||||
|
self:optimizeStatement(node.content[2], true);
|
||||||
|
} elseif (node.kind == 'BinaryOperator') {
|
||||||
|
// todo: do calculations with constants ahead of time
|
||||||
|
|
||||||
|
var lhs = node.content[2];
|
||||||
|
var rhs = node.content[3];
|
||||||
|
|
||||||
|
self:optimizeExpression(lhs);
|
||||||
|
self:optimizeExpression(rhs);
|
||||||
|
|
||||||
|
var op = node.content[1];
|
||||||
|
if ((lhs.kind == 'NumberLiteral' || lhs.kind == 'CompiledNumberLiteral') && (rhs.kind == 'NumberLiteral' || rhs.kind == 'CompiledNumberLiteral')) { // we can optimize, presumably
|
||||||
|
var a,b = lhs.content, rhs.content;
|
||||||
|
if (lhs.kind == 'NumberLiteral') a = Lower.decodeNumber(a);
|
||||||
|
if (rhs.kind == 'NumberLiteral') b = Lower.decodeNumber(b);
|
||||||
|
|
||||||
|
if (op == '+') {
|
||||||
|
node.location = lhs.location;
|
||||||
|
node.kind = 'CompiledNumberLiteral';
|
||||||
|
node.content = a+b;
|
||||||
|
} elseif (op == '-') {
|
||||||
|
node.location = lhs.location;
|
||||||
|
node.kind = 'CompiledNumberLiteral';
|
||||||
|
node.content = a-b;
|
||||||
|
} elseif (op == '*') {
|
||||||
|
node.location = lhs.location;
|
||||||
|
node.kind = 'CompiledNumberLiteral';
|
||||||
|
node.content = a*b;
|
||||||
|
} elseif (op == '/') {
|
||||||
|
node.location = lhs.location;
|
||||||
|
node.kind = 'CompiledNumberLiteral';
|
||||||
|
node.content = a/b;
|
||||||
|
} elseif (op == '%') {
|
||||||
|
node.location = lhs.location;
|
||||||
|
node.kind = 'CompiledNumberLiteral';
|
||||||
|
node.content = a%b;
|
||||||
|
} elseif (op == '^') {
|
||||||
|
node.location = lhs.location;
|
||||||
|
node.kind = 'CompiledNumberLiteral';
|
||||||
|
node.content = a^b;
|
||||||
|
}
|
||||||
|
// print(lhs.location:format(), rhs.location:format());
|
||||||
|
} elseif ((lhs.kind == 'StringLiteral' || lhs.kind == 'CompiledStringLiteral') && (rhs.kind == 'StringLiteral' || rhs.kind == 'CompiledStringLiteral')) {
|
||||||
|
var a,b = lhs.content, rhs.content;
|
||||||
|
if (lhs.kind == 'StringLiteral') a = Lower.decodeString(a);
|
||||||
|
if (rhs.kind == 'StringLiteral') b = Lower.decodeString(b);
|
||||||
|
|
||||||
|
if (op == '..') {
|
||||||
|
node.location = lhs.location;
|
||||||
|
node.kind = 'CompiledStringLiteral';
|
||||||
|
node.content = a..b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif (node.kind == 'UnaryOperator') {
|
||||||
|
// todo: again, constants, blah blah blah
|
||||||
|
var val = node.content[2];
|
||||||
|
|
||||||
|
self:optimizeExpression(val);
|
||||||
|
} elseif (node.kind == 'Field') {
|
||||||
|
var left = node.content[1];
|
||||||
|
|
||||||
|
self:optimizeExpression(left);
|
||||||
|
} elseif (node.kind == 'Index') {
|
||||||
|
var left = node.content[1];
|
||||||
|
var right = node.content[2];
|
||||||
|
|
||||||
|
self:optimizeExpression(left);
|
||||||
|
self:optimizeExpression(right);
|
||||||
|
} elseif (node.kind == 'Variable') {
|
||||||
|
// nothing
|
||||||
|
} elseif (node.kind == 'BooleanLiteral') {
|
||||||
|
// nothing
|
||||||
|
} elseif (node.kind == 'NullLiteral') {
|
||||||
|
// nun
|
||||||
|
} elseif (node.kind == 'Call') {
|
||||||
|
self:optimizeExpression(node.content[1]); // function name
|
||||||
|
|
||||||
|
var args = node.content[2];
|
||||||
|
for (var i = 1; i <= #args; i += 1;) {
|
||||||
|
var arg = args[i];
|
||||||
|
|
||||||
|
self:optimizeExpression(arg);
|
||||||
|
}
|
||||||
|
} elseif (node.kind == 'MethodCall') {
|
||||||
|
self:optimizeExpression(node.content[1]);
|
||||||
|
|
||||||
|
var args = node.content[3];
|
||||||
|
for (var i = 1; i <= #args; i += 1;) {
|
||||||
|
var arg = args[i];
|
||||||
|
|
||||||
|
self:optimizeExpression(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
40
src/output.lake
Normal file
40
src/output.lake
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
OutputMethods = {};
|
||||||
|
|
||||||
|
fn RegisterOutMethod(name, data) {
|
||||||
|
OutputMethods[name] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
Output = {};
|
||||||
|
|
||||||
|
fn Output:new(target, method) {
|
||||||
|
return setmetatable({
|
||||||
|
'target' = target,
|
||||||
|
'method' = method,
|
||||||
|
'lines' = {},
|
||||||
|
'debug' = {}
|
||||||
|
}, {'__index' = self});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Output:output(ast) {
|
||||||
|
var method = OutputMethods[self.method];
|
||||||
|
assert(method, 'invalid output method!');
|
||||||
|
|
||||||
|
method.output(self, ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Output:addLine(line, debug) {
|
||||||
|
self.lines[#self.lines+1] = line;
|
||||||
|
self.debug[#self.debug+1] = debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Output:appendLine(data, debuginfo) {
|
||||||
|
if (!self.lines[#self.lines]) {
|
||||||
|
self.lines[#self.lines+1] = '';
|
||||||
|
}
|
||||||
|
self.lines[#self.lines] = self.lines[#self.lines] .. data;
|
||||||
|
|
||||||
|
if (!self.debug[#self.debug]) {
|
||||||
|
self.debug[#self.debug+1] = '';
|
||||||
|
}
|
||||||
|
self.debug[#self.debug] = self.debug[#self.debug] .. debuginfo;
|
||||||
|
}
|
||||||
1035
src/parser.lake
Normal file
1035
src/parser.lake
Normal file
File diff suppressed because it is too large
Load Diff
76
src/types.lake
Normal file
76
src/types.lake
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
Location = {};
|
||||||
|
|
||||||
|
fn Location:new(file) {
|
||||||
|
return setmetatable({'file' = file, 'column' = 1, 'line' = 1}, {'__index' = self});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Location:clone() {
|
||||||
|
return setmetatable({
|
||||||
|
'file' = self.file,
|
||||||
|
'column' = self.column,
|
||||||
|
'line' = self.line
|
||||||
|
}, getmetatable(self));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Location:skip(chr) {
|
||||||
|
for (var i = 1; i <= #chr; i += 1;) {
|
||||||
|
self.column += 1;
|
||||||
|
var c = chr:sub(i,i);
|
||||||
|
|
||||||
|
if (c == "\n") {
|
||||||
|
self.column = 1;
|
||||||
|
self.line += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Location:format() {
|
||||||
|
return self.file .. ':' .. self.line .. ':' .. self.column;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Token = {};
|
||||||
|
|
||||||
|
fn Token:new(kind, content, location) {
|
||||||
|
return setmetatable({
|
||||||
|
'kind' = kind,
|
||||||
|
'content' = content,
|
||||||
|
'location' = location
|
||||||
|
}, {'__index' = self});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Error = {};
|
||||||
|
|
||||||
|
fn Error:new(message, origin, location) {
|
||||||
|
return setmetatable({
|
||||||
|
'message' = message,
|
||||||
|
'origin' = origin,
|
||||||
|
'location' = location
|
||||||
|
}, {'__index' = self});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Error:format() {
|
||||||
|
var capitalizedOrigin = self.origin:sub(1,1):upper() .. self.origin:sub(2):lower();
|
||||||
|
var locationString = self.location:format();
|
||||||
|
|
||||||
|
return capitalizedOrigin .. ' error: ' .. locationString .. ': ' .. self.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Error:print() {
|
||||||
|
print("\x1b[31m" .. self:format() .. "\x1b[0m");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Node = {};
|
||||||
|
|
||||||
|
fn Node:new(kind, content, location) {
|
||||||
|
return setmetatable({
|
||||||
|
'kind' = kind,
|
||||||
|
'content' = content,
|
||||||
|
'location' = location,
|
||||||
|
}, {'__index' = self});
|
||||||
|
}
|
||||||
58
src/util.lake
Normal file
58
src/util.lake
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
var deckey = '0123456789';
|
||||||
|
fn DecEncode(num, minchars) {
|
||||||
|
var outp = '';
|
||||||
|
|
||||||
|
while (num > 0) {
|
||||||
|
var dig = num % (#deckey);
|
||||||
|
var ch = deckey:sub(dig + 1, dig + 1);
|
||||||
|
|
||||||
|
outp = ch .. outp;
|
||||||
|
|
||||||
|
num = math.floor(num / (#deckey));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minchars) {
|
||||||
|
while (#outp < minchars) {
|
||||||
|
outp = deckey:sub(1,1) .. outp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return outp;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hexkey = '0123456789abcdef';
|
||||||
|
fn HexEncode(num, minchars) {
|
||||||
|
var outp = '';
|
||||||
|
|
||||||
|
while (num > 0) {
|
||||||
|
var dig = num % (#hexkey);
|
||||||
|
var ch = hexkey:sub(dig + 1, dig + 1);
|
||||||
|
|
||||||
|
outp = ch .. outp;
|
||||||
|
|
||||||
|
num = math.floor(num / (#hexkey));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minchars) {
|
||||||
|
while (#outp < minchars) {
|
||||||
|
outp = hexkey:sub(1,1) .. outp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return outp;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn HexDecode(hexstr) {
|
||||||
|
var out = 0;
|
||||||
|
|
||||||
|
for (var i = 1; i <= #hexstr; i += 1;) {
|
||||||
|
var char = hexstr:sub(i,i):lower();
|
||||||
|
|
||||||
|
var pos = hexkey:find(char, null, true) - 1;
|
||||||
|
|
||||||
|
out *= 16;
|
||||||
|
out += pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user