Compare commits

..

No commits in common. "2efe383cbfc2bbcd28ffea646703f67e670bd809" and "9c6ede00df3cb82aa5fe49d214aeb523d4987043" have entirely different histories.

36 changed files with 0 additions and 3765 deletions

7
.gitignore vendored
View File

@ -1,7 +0,0 @@
/*
!/addon/
!/build/
!/core/
!/mod/
!/.gitignore
!/Makefile

View File

@ -1,13 +0,0 @@
.PHONY: clean install-mods install
install: clean install-mods
luajit build/init.lua --linux
install-mods: clean
rm -rf ~/.local/lib/.tal_mod
mkdir -p ~/.local/lib/.tal_mod
cp -r mod/* ~/.local/lib/.tal_mod/
clean:
rm -rf ~/.local/bin/tal
rm -rf ~/.local/lib/.tal_mod

View File

@ -1,14 +0,0 @@
{
"$schema": "https://raw.githubusercontent.com/LuaLS/LLS-Addons/main/schemas/addon_config.schema.json",
"words": ["array%s*%{"],
"settings": {
"Lua.runtime.version" : "LuaJIT",
"Lua.diagnostics.globals" : [
"global1",
"global2"
],
"Lua.runtime.special" : {
"import" : "require"
}
}
}

View File

@ -1,123 +0,0 @@
--- @meta
--- @alias array<T> { [integer]: T } | arraylib
--- @class arraylib
arrays = {};
--- Converts the object to an array by putting "arrays" as its metatable
--- @generic T
--- @param obj { [integer]: T }
--- @return array<T>
--- @overload fun(val: string): array<string>
function array(obj) end
--- Converts the string to an array of characters
--- @param str string
--- @return array<string>
function array(str) end
--- Returns all the given arrays, concatenated to one
--- @generic T
--- @param ... T[]
--- @return array<T>
function arrays.concat(...) end
--- @generic T
--- @param self T[]
--- @param ... T[]
--- @return self
function arrays.append(self, ...) end
--- Adds all the given elements to the end of this array
--- @generic T
--- @param self T[]
--- @param ... T[]
--- @return self
function arrays.push(self, ...) end
--- Removes the last element of the array and returns it
--- @generic T
--- @param self array<T>
--- @return T val? The removed element, or nil if none
function arrays:pop() end
--- Returns the last element of the array
--- @generic T
--- @param self array<T>
--- @return T val? The last element of this array, or nil of none
function arrays:peek() end
--- Removes the first element of this array
--- @generic T
--- @param self array<T>
--- @return T val? The last element of this array, or nil of none
function arrays.shift(self) end
--- Adds all the given elements to the end of this array
--- @generic T
--- @param self T[]
--- @param ... T[]
--- @return self
function arrays.unshift(self, ...) end
--- Returns the result of mapping the values in table t through the function f
--- @generic In, Out
--- @param self In[]
--- @param f fun(val: In, i: integer, self: self): Out
--- @param mutate? boolean If true, will operate directly on the given array
--- @return Out[] arr
function arrays.map(self, f, mutate) end
--- Like arrays:map, but will expect the function to return arrays, and will :append them to the result array instead
--- @generic In, Out
--- @param self In[]
--- @param f fun(val: In, i: integer, self: self): Out[]
--- @return Out[] arr
function arrays.flat_map(self, f) end
--- Sorts the array in ascending order
--- @generic T
--- @param self T[]
--- @param f? fun(a: T, b: T): boolean A "less than" function, aka, a < b
--- @param copy? boolean If true will operate on a copy of the array
--- @return T[]
function arrays.sort(self, f, copy) end
--- Finds the index of the given element, or nil if it doesn't exist
--- @generic T
--- @param self T[]
--- @param f? fun(val: T, i: integer, self: self): T The predicate
--- @return integer?
function arrays.find_i(self, f) end
--- Sets each value from b to e to val in the given array
--- @generic T
--- @param self T[]
--- @param b? integer
--- @param e? integer
--- @return self
function arrays.fill(self, val, b, e) end
--- Every element from start to stop is removed from this array and are replaced with the given elements
--- @generic T
--- @param self T[]
--- @param b? integer
--- @param e? integer
--- @param ... T
--- @return self
function arrays.splice(self, b, e, ...) end
--- Returns the subarray from b to e
--- @generic T
--- @param self T[]
--- @param b? integer
--- @param e? integer
--- @return T[]
function arrays.slice(self, b, e) end
--- Equivalent of table.concat(self, sep, b, e)
--- @param sep string? Separator (defaults to empty)
--- @param b number? First element to take (defaults to beginning)
--- @param e number? Last element to take (defaults to end)
function arrays:join(sep, b, e) end

View File

@ -1,94 +0,0 @@
--- @meta
--- @class coro
coro = {};
--- Creates a symmetric coroutine from the function
--- @param func function
--- @return thread
function coro.create(func) end
--- @return thread th The currently running thread
--- @return boolean is_main Whether or not this is the main thread
function coro.running() end
--- Gets the status of the thread
--- @param th thread
--- @return
--- | "running" -- The thread is the currently running one
--- | "suspended" -- The thread is not running, but may be resumed
--- | "normal" -- Is active but not running.
--- | "dead" -- The coroutine is not running and can't be resumed
function coro.status(th) end
--- Transfers the execution to the given thread
--- @param th thread The thread to transfer execution to
--- @param ... any The arguments to pass to the thread
--- @return boolean ok Whether or not the thread yielded normally
--- @return ... Result if ok is true, the error if ok is false
function coro.ptransfer(th, ...) end
--- Transfers the execution to the given thread
--- @param th thread The thread to transfer execution to
--- @param ... any The arguments to pass to the thread
--- @return ... The yielded values form the function
function coro.transfer(th, ...) end
--- Transfers the execution to the given thread
--- @param f fun(yield: fun(...): nil, ...): ... Wrapped function
--- @return fun(...): ...
function coro.wrap(f) end
--- @param f fun(yield: (fun(...): ...), ...): ...
--- @return fun(...): fun(...): ...
function coro.gen(f) end
--- Prematurely kills the coroutine
--- @param th thread
--- @return boolean ok
--- @return string? err
function coro.close(th) end
--- @class coroutine
coroutine = {};
--- Creates a asymmetric coroutine from the function
--- @param func function
--- @return thread
function coroutine.create(func) end
--- @return thread th The currently running thread
--- @return boolean is_main Whether or not this is the main thread
function coroutine.running() end
--- Gets the status of the thread
--- @param th thread
--- @return
--- | "running" -- The thread is the currently running one
--- | "suspended" -- The thread is not running, but may be resumed
--- | "normal" -- Is active but not running.
--- | "dead" -- The coroutine is not running and can't be resumed
function coroutine.status(th) end
--- Transfers to the coroutine asymmetrically; If it uses an asymmetric yield it will transfer to this coroutine
--- @param th thread The thread to transfer execution to
--- @param ... any The arguments to pass to the thread
--- @return boolean ok Whether or not the thread yielded normally
--- @return ... Result if ok is true, the error if ok is false
function coroutine.resume(th, ...) end
--- Yields to the calling coroutine
--- @param ... any The arguments to pass to the thread
--- @return ... The yielded values form the function
function coroutine.yield(...) end
--- Transfers the execution to the given thread
--- @param f fun(...): ... Wrapped function
--- @return fun(...): ...
function coroutine.wrap(f) end
--- Prematurely kills the coroutine
--- @param th thread
--- @return boolean ok
--- @return string? err
function coroutine.close(th) end

View File

@ -1,8 +0,0 @@
--- @meta
--- @class env
--- @field jit boolean
--- @field os string
--- @field runtime string
--- @field arch string
env = {};

View File

@ -1,26 +0,0 @@
--- @meta
--- @class functionlib
functions = {};
--- Constructs a function, such that it calls the first function with the passed arguments,
--- the second function with the return of the first, and so on. The return value of the last function is returned
---
--- In short, does the following, if the passed functions are a, b and c: return c(b(a(...)))
---
--- Sometimes less cumbersome to write (a | b | c | d)(args...) than d(c(b(a(args...))))
--- @param self function
function functions:pipe(...) end
function functions:apply(args) end
--- Constructs a function, such that it calls the first function with the passed arguments,
--- the second function with the return of the first, and so on. The return value of the last function is returned
---
--- In short, does the following, if the passed functions are a, b and c: return c(b(a(...)))
---
--- Sometimes less cumbersome to write (a | b | c | d)(args...) than d(c(b(a(args...))))
--- @param self function
function functions:pcall(...) end
--- Calls pipe with a and b; Alternative syntax for older Lua installations
function functions.__sub(a, b) end
--- Calls pipe with a and b
function functions.__bor(a, b) end

View File

@ -1,6 +0,0 @@
--- @meta
module = { exports = {}, require = require, import = function (id) end, export = function (obj) end };
exports = module.exports;
import = require;
export = module.export;

View File

@ -1,4 +0,0 @@
--- @meta
unpack = table.unpack;
table.unpack = unpack;

View File

@ -1,10 +0,0 @@
--- @meta
--- Converts the value to a readable string
function to_readable(val, indent_str) end
--- Prints the given values to stderr
function print(...) end
--- Pretty-prints the given values to stderr
function pprint(...) end

View File

@ -1,24 +0,0 @@
--- @meta
--- Splits the given string with the given delimiter
--- @param self string
--- @param sep string The delimiter to split by. Defaults to an empty string
--- @return array<string>
function string.split(self, sep)end
--- Gives the character at the specified position
--- @param self string
--- @param i integer
--- @return string
function string.at(self, i)end
--- string.format("%q", self)
--- @param self string
--- @return string
function string.quote(self)end
--- Quotes the string, making it safe to pass to a shell script (unix only!!!)
--- @param self string
--- @return string
function string.quotesh(self)end

View File

@ -1,20 +0,0 @@
--- @meta
box = table.pack;
unbox = table.unpack;
exit = os.exit;
--- @param box table
--- @param s? number
--- @param e? number
--- @return table
function rebox(box, s, e) end
--- Concatenates the arguments to a string
--- @return string
function str(...) end
---@generic T
---@param obj { [integer]: T }
---@return fun(): T
function iterate(obj) end

View File

@ -1,118 +0,0 @@
local fs;
if not TAL then
local module = require "core.module";
local root = module.init {
tal_path = { "./mod/" },
lua_path = package.path,
target_glob = _G,
pwd = "./",
home = "",
join = require "mod.path".join,
cwd = require "mod.path".cwd,
};
require = root.require;
import = root.import;
export = root.export;
exports = root.exports;
end
fs = require "fs";
local args = require "..mod.args";
local modules = {
"core.entry",
"core.module",
"mod.path",
"mod.fs",
};
local function main(...)
local arg_readers = {};
local target;
local executable;
local paths = array {};
function arg_readers.dst(v)
target = v;
return true;
end
function arg_readers.path(v)
paths:push(v);
return true;
end
function arg_readers.executable(v)
executable = v;
return true;
end
function arg_readers.linux()
target = fs.home() .. "/.local/bin/tal";
if fs.exists "/bin/luajit" then
executable = "/bin/luajit";
elseif fs.exists "/bin/lua" then
executable = "/bin/lua"
else
print "No valid lua runtime found, WTF?";
end
paths = array {
"~/.local/lib/tal",
"~/.local/lib/.tal_mod",
"/usr/lib/tal",
"/usr/lib/.tal_mod",
"/usr/local/lib/.tal_mod",
};
return false;
end
arg_readers.o = arg_readers.dst;
arg_readers.x = arg_readers.executable;
arg_readers.p = arg_readers.path;
args(arg_readers, ...);
if not target then error "No target specified" end
local f = assert(io.open(target, "w"));
if executable then
f:write(("#!%s\n"):format(executable));
end
f:write "load(string.dump(function(...)\n";
f:write "function package.preload.__tal__PATH() return {\n";
f:write(paths:map(function (v) return "\t" .. v:quote() .. ",\n" end, true):join"");
f:write("} end\n");
for i = 1, #modules do
local name = modules[i];
local file = name:gsub("%.", "/") .. ".lua";
f:write(("package.preload[%q] = function(...)\n"):format(name));
for el in assert(io.open(file, "r")):lines(1024) do
f:write(el);
end
f:write("\nend\n");
end
f:write(("require %q(...)"):format("core.entry"));
f:write "\nend, true), '<internal>', 'b')(...)";
f:close();
if executable then
os.execute(("chmod +x %q"):format(target));
end
end
if type(module) ~= "table" then
main(...);
else
return main;
end

View File

@ -1,871 +0,0 @@
local TOK_ID = 1;
local TOK_OP = 2;
local TOK_STR = 3;
local TOK_NUM = 4;
local operators = {
AND = 1,
OR = 2,
NOT = 3,
CONCAT = 10,
ADD = 11,
SUB = 12,
MUL = 13,
DIV = 14,
IDIV = 15,
MOD = 16,
B_AND = 20,
B_OR = 21,
B_XOR = 22,
B_LSH = 24,
B_RSH = 25,
RSH = 26,
EQ = 30,
NEQ = 31,
LEQ = 32,
GEQ = 33,
LESS = 34,
GR = 35,
PAREN_OPEN = 40,
PAREN_CLOSE = 41,
BRACKET_OPEN = 42,
BRACKET_CLOSE = 43,
BRACE_OPEN = 44,
BRACE_CLOSE = 45,
SEMICOLON = 50,
COLON = 51,
COMMA = 52,
DOT = 53,
SPREAD = 54,
-- QUESTION = 55,
ASSIGN = 60,
ASSIGN_OR = 75,
};
local op_map = {
["+"] = { operators.ADD },
["-"] = { operators.SUB },
["*"] = {
operators.MUL,
["*"] = { operators.POW },
},
["/"] = {
operators.DIV,
["/"] = { operators.IDIV },
},
["%"] = { operators.MOD },
["&"] = { operators.B_AND },
["|"] = {operators.B_OR },
["^"] = { operators.POW },
["~"] = {
operators.B_XOR,
["="] = { operators.NEQ },
},
[">"] = {
operators.GR,
[">"] = {
operators.RSH,
[">"] = { operators.B_RSH },
},
},
["<"] = {
operators.LESS,
["<"] = { operators.B_LSH },
},
["="] = {
operators.ASSIGN,
["="] = { operators.EQ },
},
[","] = { operators.COMMA },
["."] = { operators.DOT },
[";"] = { operators.SEMICOLON },
[":"] = { operators.COLON },
["?"] = { operators.QUESTION },
["("] = { operators.PAREN_OPEN },
[")"] = { operators.PAREN_CLOSE },
["["] = { operators.BRACKET_OPEN },
["]"] = { operators.BRACKET_CLOSE },
["{"] = { operators.BRACE_OPEN },
["}"] = { operators.BRACE_CLOSE },
};
local to_byte = string.byte;
--- @class tok_base
--- @field loc string
--- @field end_loc string
--- @field comments string[]
--- @field raw string
--- @class tok_id: tok_base
--- @field type 1
--- @field val string
--- @class tok_op: tok_base
--- @field type 2
--- @field val integer
--- @class tok_str: tok_base
--- @field type 3
--- @field val string
--- @class tok_num: tok_base
--- @field type 4
--- @field val number
---@param base tok_base
---@param id string
---@return tok_id
local function tok_id(base, id)
base = base or { loc = "", comments = {} };
return {
loc = base.loc,
comments = base.comments,
type = TOK_ID,
val = id,
}
end
---@param base tok_base
---@param op integer
---@return tok_str
local function tok_op(base, op)
base = base or { loc = "", comments = {} };
return {
loc = base.loc,
comments = base.comments,
type = TOK_OP,
val = op,
}
end
---@param base tok_base?
---@param data string
---@return tok_str
local function tok_str(base, data)
base = base or { loc = "", comments = {} };
return {
loc = base.loc,
comments = base.comments,
type = TOK_STR,
val = data,
};
end
---@param base tok_base
---@param num number
---@return tok_num
local function tok_num(base, num)
base = base or { loc = "", comments = {} };
return {
loc = base.loc,
comments = base.comments,
type = TOK_NUM,
val = num,
};
end
--- @alias token
--- | tok_id
--- | tok_op
--- | tok_str
--- | tok_num
---@param loader string | fun(): string
---@return fun(): string?
local function char_supplier(loader)
if type(loader) == "string" then
local i = 0;
return function ()
i = i + 1;
local res = string.sub(loader, i, i);
if #res == 1 then return res end
end
else
local curr_str;
local i = 0;
return function ()
if curr_str == "" then return nil end
i = i + 1;
if curr_str == nil or i > #curr_str then
curr_str = loader();
if curr_str == false or curr_str == nil or curr_str == "" then
curr_str = "";
return nil;
end
i = 1;
end
return string.sub(curr_str, i, i);
end
end
end
---@param filename string
---@param chars fun(): string | nil
---@return fun(): token?
local function token_supplier(filename, chars, get_comments)
local line = 1;
local start = 1;
local _chars = chars;
chars = function ()
local c = _chars();
if c == "\n" then
line = line + 1;
start = 1;
else
start = start + 1;
end
return c;
end
local function unconsume(c)
if c == nil then return end
local old_chars = chars;
start = start - 1;
chars = function ()
chars = old_chars;
start = start + 1;
return c;
end
end
local consume_white;
if get_comments then
local function consume_comment()
-- local data = {};
-- local c = chars();
-- if c == "[" then
-- while true do
-- if c == nil then
-- return nil, "Unclosed comment";
-- elseif c == "]" and chars() == "#" then
-- break;
-- end
-- data[#data + 1] = c;
-- c = chars();
-- end
-- else
-- while true do
-- if c == "\n" or c == nil then break end
-- data[#data + 1] = c;
-- c = chars();
-- end
-- end
-- return table.concat(data);
local data = array {};
local function singleline(c)
while true do
if c == "\n" or c == nil then break end
data:push(c);
c = chars();
end
return data:join "";
end
local function multiline_end()
if chars() ~= "]" then return false end
if chars() ~= "-" then return false end
if chars() ~= "-" then return false end
return true
end
local function multiline()
while true do
local c = chars()
if c == "]" and multiline_end() then
break;
elseif c == nil then
return nil, "Missing ]]";
end
data:push(c);
end
return data:join "";
end
local c = chars()
if c == "[" then
c = chars()
if c == "[" then
return multiline();
else
return singleline(c);
end
else singleline(c) end
return data:join "";
end
---@return false | string[]?, string?
function consume_white()
local comments = {};
while true do
local c = chars();
if c == nil then
chars = function () return nil end
break;
elseif start == 2 and line == 1 and c == "#" then
local c2 = chars();
if c2 == "!" then
while c ~= "\n" do
c = chars();
end
else
unconsume(c2);
unconsume(c);
end
elseif c == "-" then
local c2 = chars()
if c2 == "-" then
local res, err = consume_comment();
if res == nil then return nil, err end
comments[#comments + 1] = res;
else
unconsume(c2)
unconsume(c)
break
end
elseif not string.find(c, "%s") then
unconsume(c);
break;
end
end
return comments;
end
else
---@return string?
local function consume_comment()
local c = chars();
if c == "[" then
while true do
c = chars();
if c == nil then
return "Unclosed comment";
elseif c == "]" and chars() == "#" then break end
end
else
while true do
if c == "\n" or c == nil then break end
c = chars();
end
end
end
---@return false | string[]?, string?
function consume_white()
while true do
local c = chars();
if c == nil then
chars = function () return nil end
break;
elseif c == "#" then
local err = consume_comment();
if err ~= nil then return nil, err end
elseif not string.find(c, "%s") then
unconsume(c);
break;
end
end
return false;
end
end
local function hex_one(c)
local b = to_byte(c)
if b >= 48 and b <= 57 then
return b - 48
elseif b >= 97 and b <= 102 then
return b - 97 + 10
elseif b >= 65 and b <= 70 then
return b - 65 + 10
else
return -1
end
end
local function hex(base)
local res = 0
local any = false
while true do
local c = chars()
if c == nil then break end
local digit = hex_one(c)
if digit == -1 then
unconsume(c)
break
else
res = res * 16 + digit
end
any = true
end
if not any then return end
return tok_num(base, res)
end
local function decimal(res, float, mult)
local any = true
local fract_mult = .1
if type(res) == "string" then
local b = to_byte(res)
if b >= 48 and b <= 57 then
res = b - 48
else
return nil
end
elseif res == nil then
res, any = 0, false
end
if mult == nil then mult = 1 end
local c
while true do
c = chars()
if c == nil then break end
local b = to_byte(c)
if b >= 48 and b <= 57 then
any = true
if float then
res = res + (b - 48) * fract_mult
fract_mult = fract_mult * .1
else
res = res * 10 + (b - 48)
end
else
break
end
end
if any then
return mult * res, c
else
return nil, c
end
end
local function number(base, res)
local fract, e
local whole, next = decimal(res, false)
if next == "." then
fract, next = decimal(nil, true)
end
if next == "e" then
local c = chars()
if c == "-" then
e, next = decimal(nil, false, -1)
else
e, next = decimal(c)
end
if e == nil then
return nil, "Expected number after 'e'"
end
end
if fract == nil then fract = 0 end
if e == nil then
e = 1
else
e = 10 ^ e
end
unconsume(next)
return tok_num(base, (whole + fract) * e)
end
local function zero(base)
local c = chars()
if c == nil then return tok_num(base, 0) end
local b = to_byte(c)
if c == "x" then
local res = hex(base)
if res == nil then return nil, "Expected a hex literal"
else return res end
else
unconsume(c)
return number(base, 0)
end
end
local function id(base, c)
local res = c
while true do
c = chars()
if c == nil then break end
local b = to_byte(c)
if
b >= 65 and b <= 90 or -- A-Z
b >= 97 and b <= 122 or -- a-z
b >= 48 and b <= 57 or -- 0-9
c == "_"
then
res = res .. c
else
unconsume(c)
break
end
end
base.raw = res
return tok_id(base, res)
end
local function dot(base)
local e, fract, next = nil, decimal(nil, true)
if fract == nil then
if next == "." then
local c = chars()
if c == "." then
return tok_op(base, operators.SPREAD)
else
unconsume(c)
return tok_op(base, operators.CONCAT)
end
else
unconsume(next)
return tok_op(base, operators.DOT)
end
return base
end
if next == "e" then
local c = chars()
if c == "-" then
e, next = decimal(nil, false, -1)
else
e, next = decimal(c)
end
if e == nil then
return nil, "Expected number after 'e'"
end
end
if fract == nil then fract = 0 end
if e == nil then
e = 1
else
e = 10 ^ e
end
unconsume(next)
return tok_num(base, fract * e)
end
local function char(c, allow_newline)
if c == nil then return nil
elseif c == "\\" then
c = chars()
if c == "a" then return "\a"
elseif c == "b" then return "\b"
elseif c == "f" then return "\f"
elseif c == "n" then return "\n"
elseif c == "r" then return "\r"
elseif c == "t" then return "\t"
elseif c == "v" then return "\v"
elseif c == "z" then
repeat
c = chars()
until c == " " or c == "\n" or c == "\r" or c == "\t" or c == "\v"
return char(c)
elseif c == "x" then
local ca, cb = chars(), chars()
if ca == nil or cb == nil then return nil, "Expected a hex number" end
local a, b = hex_one(ca), hex_one(cb)
if a == -1 or b == -1 then return nil, "Expected a hex number" end
return string.char(a * 16 + b)
else return c end
else return c end
end
local function quote_str(base, first)
local res = {};
while true do
local c, err;
c = chars()
if c == first then break end
if c == nil then return nil, "Unterminated string literal" end
c, err = char(c)
if c == nil then
return nil, err or "Unterminated string literal";
else
res[#res + 1] = c;
end
end
return tok_str(base, table.concat(res));
end
local function quote_char(base, first)
local res = 0;
while true do
local c, err;
c = chars();
if c == first then break end
if c == nil then return nil, "Unterminated string literal" end
c, err = char(c);
if c == nil then
return nil, err or "Unterminated string literal";
else
for _, v in ipairs { string.byte(c, 1, #c) } do
res = res * 256 + v;
end
end
end
return tok_num(base, res);
end
return function ()
-- local comments = consume_white()
local comments, err = consume_white();
if comments == nil then
error(table.concat({ filename, line, start }, ":") .. ": " .. err);
elseif comments == false then
comments = nil;
end
local loc = table.concat({ filename, line, start }, ":");
--- @type table | nil
local base = { loc = loc, comments = comments, raw = "" };
local c = chars()
if c == nil then return nil end
local b = to_byte(c)
if c == "." then base, err = dot(base)
elseif c == "0" then base, err = zero(base)
elseif b >= 49 and b <= 57 then base, err = number(base, b - 48) -- 1-9
elseif
b >= 65 and b <= 90 or -- A-Z
b >= 97 and b <= 122 or -- a-z
c == "_"
then
base, err = id(base, c)
elseif c == "\"" then
base, err = quote_str(base, c);
elseif c == "\'" then
base, err = quote_char(base, c);
else
local res = op_map;
while true do
local next = res[c];
if next == nil then
unconsume(c);
res = res[1];
break;
else
c = chars();
res = next;
end
end
if res == nil then
base, err = nil, string.format("Unexpected char '%s'", c)
else
base.type = TOK_OP;
base.val = res;
end
end
if base == nil then
return error(loc .. ": " .. err);
else
base.end_loc = table.concat({ filename, line, start }, ":");
return base;
end
end
end
---@param ... fun(): token?, string?
local function concat_tokens(...)
local arr = {...};
local i = 1;
return function ()
while true do
local el = arr[i];
if el == nil then
return nil;
elseif type(el) == "function" then
local buff = el();
if buff ~= "" then
return buff;
else
i = i + 1;
end
end
end
end
end
---@param first token
---@param second token
local function can_go_after(first, second)
if first.type == TOK_OP and second.type == TOK_OP then
if (
first.val == operators.ASSIGN or
first.val == operators.EQ or
first.val == operators.LESS or
first.val == operators.GR or
first.val == operators.B_XOR
) and (
second.val == operators.ASSIGN or
second.val == operators.EQ
) then return false end
if (
first.val == operators.LESS or
first.val == operators.GR
) and (
second.val == operators.LESS or
second.val == operators.GR
) then return false end
if (
first.val == operators.DOT or
first.val == operators.CONCAT or
first.val == operators.SPREAD
) and
(
second.val == operators.DOT or
second.val == operators.CONCAT or
second.val == operators.SPREAD
) then return false end
if (
first.val == operators.LABEL or
first.val == operators.COLON
) and (
second.val == operators.LABEL or
second.val == operators.COLON
) then return false end
if (
first.val == operators.DIV or
first.val == operators.IDIV
) and (
second.val == operators.DIV or
second.val == operators.IDIV
) then return false end
return true;
elseif first.type == TOK_NUM and second.type == TOK_ID then
return false
elseif first.type == TOK_ID and second.type == TOK_NUM then
return false
elseif first.type == TOK_ID and second.type == TOK_ID then
return false
elseif first.type == TOK_NUM and second.type == TOK_OP then
return (
second.val ~= operators.DOT and
second.val ~= operators.CONCAT and
second.val ~= operators.SPREAD
);
elseif first.type == TOK_OP and second.type == TOK_NUM then
return (
first.val ~= operators.DOT and
first.val ~= operators.CONCAT and
first.val ~= operators.SPREAD
);
elseif first.type == TOK_ID and second.type == TOK_ID then
return true;
elseif first.type == TOK_NUM and second.type == TOK_NUM then
return false;
elseif first.type == TOK_STR and second.type == TOK_STR then
return false;
else
return true;
end
end
---@param tokens fun(): token?, string?
---@return fun(): string?, string?
local function token_stringifier(tokens)
local last_tok
return function ()
if tokens == nil then return nil end
local tok, err = tokens()
if tok == nil then
--- @diagnostic disable-next-line: cast-local-type
tokens = nil
return nil, err
end
if last_tok ~= nil and not can_go_after(last_tok, tok) then
last_tok = tok
return " " .. tok.raw
else
last_tok = tok
return tok.raw
end
end
end
return {
TOK_ID = TOK_ID,
TOK_OP = TOK_OP,
TOK_STR = TOK_STR,
TOK_NUM = TOK_NUM,
operators = operators,
char_supplier = char_supplier,
token_supplier = token_supplier,
concat_tokens = concat_tokens,
token_stringifier = token_stringifier,
tok_id = tok_id,
tok_op = tok_op,
tok_num = tok_num,
tok_str = tok_str,
}

View File

@ -1,29 +0,0 @@
local lexer = require "lexer";
---@param tokens fun(): token?, string?
---@param mapper fun(name: string, base: tok_base): token?
---@return fun(): token?, string?
return function (tokens, mapper)
local last_req = false;
return function ()
local tok, err = tokens();
if tok == nil then
return tok, err;
elseif last_req then
if tok.type == lexer.TOK_STR then
last_req = false;
--- @diagnostic disable-next-line: param-type-mismatch
return mapper(tok.val, tok);
elseif tok.type ~= lexer.TOK_KW or tok.val ~= lexer.K_PAREN_OPEN then
last_req = false;
end
elseif tok.type == lexer.TOK_ID and tok.val == "require" then
last_req = true;
end
return tok;
end
end

View File

@ -1,17 +0,0 @@
local module = require "core.module";
local fs = require "mod.fs";
local path = require "mod.path";
TAL = "0.0.1";
return function (...)
local root, loop_run = module.init {
tal_path = require "__tal__PATH",
fs = fs,
path = path,
};
loop_run.run(function (...)
root.require "tal.cli".main(...);
end, ...);
end

View File

@ -1,324 +0,0 @@
local exports = {};
local function mk_module(name, init, parent)
local module;
local cache = {};
parent.modules = parent.modules or {};
module = setmetatable({
name = name,
exports = {},
init = init,
env = setmetatable({}, { __index = parent.env }),
}, { __index = parent });
module.returns = { n = 1, module.exports };
function module.resolve(id, list)
if cache[id] ~= nil then return cache[id] end
for i = 1, #module.resolvers do
local res, files = module.resolvers[i](module, id);
if res then
cache[id] = res;
return res;
elseif list then
for i = 1, #files do
list[#list + 1] = files[i];
end
end
end
return nil;
end
function module.import(id)
local paths = {};
local mod = module.resolve(id, paths);
if mod == nil then
error(table.concat {
"Unable to resolve module '",
id,
"' - tried the following paths:\n- ",
table.concat(paths, "\n- ")
}, 2);
elseif type(mod.init) == "function" then
local init = mod.init;
mod.init = nil;
init(mod);
end
return (unpack or table.unpack)(mod.returns, 1, mod.returns.n);
end
function module.require(id)
local original_id = id;
local match = id:match "[^/]+$";
if match == id then
local start = id:match "^%.+" or "";
local rest = id:sub(#start + 1):gsub("%.", "/");
if #start == 0 then
id = rest;
elseif #start == 1 then
id = "./" .. rest;
else
id = ("../"):rep(#start - 1) .. rest;
end
else
id = id:sub(1, -#match - 1) .. match:gsub("%.", "/");
end
local paths = {};
local mod = module.resolve(id .. ".lua", paths);
if mod == nil then
mod = module.resolve(id .. "/init.lua", paths);
end
if mod == nil then
error(table.concat {
"Unable to resolve module '",
original_id,
"' - tried the following paths:\n- ",
table.concat(paths, "\n- ")
}, 2);
elseif type(mod.init) == "function" then
local init = mod.init;
mod.init = nil;
init(mod);
end
return (unpack or table.unpack)(mod.returns, 1, mod.returns.n);
end
function module.export(exports)
for k, v in next, exports do
module.exports[k] = v;
end
end
function module.mk(name, init)
if module.modules[name] ~= nil then
return module.modules[name], true;
else
local mod = mk_module(name, init, module);
module.modules[name] = mod;
return mod, false;
end
end
module.env.module = module;
module.env.import = module.import;
module.env.require = module.require;
module.env.export = module.export;
module.env.exports = module.exports;
module.env.package = setmetatable({}, {
__index = function()
error "This is a TAL module, please use 'module' instead";
end,
__newindex = function()
error "This is a TAL module, please use 'module' instead";
end,
});
return module;
end
local function try_file(self, cwd, id, base, suffixes)
suffixes = suffixes or { "" };
local file, found;
local files = {};
for i = 1, #suffixes do
file = cwd(base, id .. suffixes[i]);
local f = io.open(file, "r");
if f ~= nil then
f:close();
found = true;
break;
end
files[#files + 1] = file;
end
if not found then
return nil, files;
end
return self.mk(file, function (self)
local func = assert(loadfile(file, "t", self.env));
local function fin(...)
if select("#", ...) > 0 then
self.exports = ...;
self.returns = { n = select("#", ...), ... };
end
end
fin(func());
end), file;
end
function exports.file_resolver(cwd, join)
return function(self, id)
if id:match "^%./" or id:match "^%.%./" then
return try_file(self, cwd, id, join(self.name, "..", self.extensions), self.extensions);
else
local files = {};
for i = 1, #self.paths do
local res, file = try_file(self, cwd, id, self.paths[i], self.extensions);
if res ~= nil then
return res, file;
end
for i = 1, #file do
files[#files + 1] = file[i];
end
end
return nil, files;
end
end
end
function exports.lua_resolver(opts)
local returns = {
modules = {},
};
function returns.resolver(self, id)
if id:match "^lua:" then
id = id:sub(5);
end
if returns.modules[id] ~= nil then
return returns.modules[id], "lua:" ..id;
else
return nil, { "lua:" .. id };
end
end
function returns.register(name, ...)
returns.modules[name] = {
name = "lua:" .. name,
exports = ...,
returns = { n = select("#", ...), ... },
};
end
if opts.glob then
returns.register("global.lua", opts.glob);
end
if opts.expose then
local expose = opts.expose;
if expose == true then
expose = {
"utf8", "bit32", "bit",
"table", "string", "os", "math", "jit", "io", "ffi", "debug", "coroutine",
"table.new", "table.clear",
"string.buffer",
"jit.profile",
};
end
for i = 1, #expose do
local ok, mod = pcall(require, expose[i]);
if ok then
returns.register(expose[i]:gsub("%.", "/") .. ".lua", mod);
end
end
end
if opts.root then
returns.register("root.lua", opts.root);
end
returns.register("lua-resolver.lua", returns);
return returns;
end
function exports.mk_root(opts)
local run_loop;
local root = mk_module(
opts.join(opts.pwd, "/<root>"),
nil,
{
paths = {},
resolvers = {},
modules = {},
env = opts.glob,
}
);
root.paths[#root.paths + 1] = opts.pwd .. "/.tal_mod";
if opts.tal_path then
for i = 1, #opts.tal_path do
local p = opts.tal_path[i];
if p:match "^~/" then
root.paths[#root.paths + 1] = opts.join(opts.home, p:sub(2));
elseif p:match "^/" then
root.paths[#root.paths + 1] = opts.join(p);
else
root.paths[#root.paths + 1] = opts.join(opts.pwd, p);
end
end
end
if opts.lua_path then
for v in opts.lua_path:gmatch "[^;]+" do
local match = v:match "^(.+)/%?%.lua$";
if match ~= nil and match ~= "." then
root.paths[#root.paths + 1] = match;
end
end
end
if opts.resolvers then
root.resolvers[#root.resolvers + 1] = exports.file_resolver(opts.cwd, opts.join);
root.resolvers[#root.resolvers + 1] = exports.lua_resolver {
glob = opts.glob,
expose = true,
root = root,
}.resolver;
end
return root, run_loop;
end
function exports.init(opts, msg)
if opts.glob == nil then
---@diagnostic disable-next-line: deprecated
opts.glob = _ENV or _G or (getfenv and getfenv());
if opts.glob == nil then
error "Environment is not accessible";
end
end
local root = exports.mk_root {
tal_path = opts.tal_path,
lua_path = opts.lua_path or package.path,
resolvers = true,
evn_loop = true,
pwd = opts.pwd or opts.fs.pwd(),
home = opts.home or opts.fs.home(),
join = opts.join or opts.path.join,
cwd = opts.cwd or opts.path.cwd,
glob = opts.glob,
};
local require = root.require;
require "core"(opts.target_glob or root.env);
local run_loop = require "core.evn-loop";
root.env.promise = require "promise";
return root, run_loop;
end
return exports;

View File

@ -1,106 +0,0 @@
---@param consumers table<string, (fun(arg: string): boolean?, boolean?) | string>
--- @param ... string
--- @returns nil
return function (consumers, ...)
local consumer_stack = array {};
local pass_args = false;
local function digest_arg(v)
local consumed = false;
while true do
local el = consumer_stack:pop();
if el == nil then break end
local res, fin = el(v);
if res then
consumed = true;
end
if fin then
pass_args = true;
break;
end
if fin or res then
break;
end
end
if not consumed then
if consumers[1](v) then
pass_args = true;
end
end
end
local function get_consumer(name)
local consumer = name;
local path = {};
local n = 0;
while true do
local curr = consumer;
if path[curr] then
local path_arr = array {};
for k, v in next, path do
path_arr[v] = k;
end
error("Alias to '" .. curr .. "' is recursive: " .. path_arr:join " -> ");
end
consumer = consumers[curr];
if consumer == nil then
local prefix;
if n == 0 then
prefix = "Unknown flag";
else
prefix = "Unknown alias";
end
if #curr == 1 then
error(prefix .. " '-" .. curr .. "'");
else
error(prefix .. " '--" .. curr .. "'");
end
elseif type(consumer) == "function" then
return consumer;
end
path[curr] = n;
n = n + 1;
end
end
local function next(...)
if ... == nil then
while #consumer_stack > 0 do
local el = consumer_stack:pop();
if el(nil) then
error("Unexpected end of arguments", 0);
break;
end
end
else
if pass_args then
consumers[1]((...));
elseif ... == "--" then
pass_args = true;
elseif (...):match "^%-%-" then
consumer_stack:push(get_consumer((...):sub(3)));
elseif (...):match "^%-" then
for c in (...):sub(2):gmatch "." do
consumer_stack:unshift(get_consumer(c));
end
else
digest_arg(...);
end
return next(select(2, ...));
end
end
return next(...);
end

View File

@ -1,183 +0,0 @@
return function (glob)
--- @diagnostic disable: duplicate-set-field
--- @class arraylib
local arrays = {};
arrays.__index = arrays;
local function array(obj)
if type(obj) == "string" then
return obj:split "";
else
return arrays.mk(obj);
end
end
--- LuaLS is a piece of shit
--- @generic T
--- @param type `T`
--- @return fun(obj: T[]): array<T>
local function arrof(type)
return array;
end
--- Creates an array
function arrays.mk(obj)
return setmetatable(obj, arrays);
end
function arrays.concat(...)
--- @diagnostic disable-next-line: missing-fields
return arrays.append(array {}, ...);
end
function arrays:append(...)
local res = self;
local n = #res + 1;
for i = 1, select("#", ...) do
local curr = select(i, ...);
for j = 1, #curr do
res[n] = curr[j];
n = n + 1;
end
end
return res;
end
function arrays:push(...)
return self:append { ... };
end
function arrays:pop()
local res = self[#self];
self[#self] = nil;
return res;
end
function arrays:peek()
return self[#self];
end
function arrays:shift()
return table.remove(self, 1);
end
function arrays:unshift(...)
local len = select("#", ...);
table.move(self, 1, #self, len + 1, self);
for i = 1, len do
self[i] = select(i, ...);
end
end
function arrays:map(f, mutate)
local out;
if mutate then
out = self;
else
out = array {};
end
for i = 1, #self do
out[i] = f(self[i], i, self);
end
return out;
end
function arrays:flat_map(f)
local out = array {};
for i = 1, #self do
out:append(f(self[i], i, self));
end
return out;
end
function arrays:each(f)
for i = 1, #self do
f(self[i], i, self);
end
end
function arrays:sort(f, copy)
local target = self;
if copy then
target = array {}:append(self);
end
table.sort(target, f);
return target;
end
function arrays:find_i(f)
for i = 1, #self do
if f(self[i], i, self) then
return i;
end
end
return nil;
end
function arrays:fill(val, b, e)
if b == nil then b = 1 end
if e == nil then e = self end
if b < 0 then b = #self + 1 - b end
if e < 0 then e = #self + 1 - e end
for i = b, e do
self[i] = val;
end
return self;
end
function arrays:splice(b, e, ...)
-- TODO: optimize
if select("#") > 0 then
local n = e - b + 1;
while n > 0 do
table.remove(self, b);
n = n - 1;
end
for i = 1, select("#") do
table.insert(self, b, (select(i, ...)));
end
return self;
else
local res = {};
for i = b, e do
table.insert(res, self[i]);
end
return res;
end
end
function arrays:slice(b, e)
b = b or 1;
e = e or #self;
local res = array {};
for i = b, e do
res:push(self[i]);
end
return res;
end
function arrays:join(sep, b, e)
return table.concat(self, sep, b, e);
end
function arrays:__concat(other)
return arrays.concat(self, other);
end
glob.arrays = arrays;
glob.array = array;
glob.arrof = arrof;
end

View File

@ -1,234 +0,0 @@
-- symmetric coroutines from the paper at
-- http://www.inf.puc-rio.br/~roberto/docs/corosblp.pdf
-- Written by Cosmin Apreutesei. Public Domain.
-- Reworked from the (in)famous coro lib
---@diagnostic disable: duplicate-set-field
local old_create = coroutine.create;
local old_close = coroutine.close;
local old_running = coroutine.running;
local old_status = coroutine.status;
local old_resume = coroutine.resume;
local old_yield = coroutine.yield;
--- @diagnostic disable-next-line: deprecated
local main, is_main = coroutine.running();
if main ~= nil and not is_main then
error "This library must be initialized in the main thread";
elseif main == nil then
main = old_create(function () end);
old_resume(main);
end
local threads = setmetatable({}, { __mode = "k" });
local resumers = setmetatable({}, { __mode = "k" });
local responsible = setmetatable({}, { __mode = "k" });
local current = main;
threads[main] = "main";
local function assert_thread(thread, level)
if type(thread) ~= "thread" then
local err = string.format("coroutine expected but %s given", type(thread));
error(err, level);
end
return thread;
end
local function unprotect(thread, ok, ...)
if not ok then
local s = debug.traceback(thread, (...));
s = string.gsub(s, "stack traceback:", tostring(thread) .. " stack traceback:");
error(s, 2);
end
return ...;
end
local function finish(thread, ...)
local caller = resumers[thread];
if not caller then
error("coroutine ended without transferring control", 4);
end
return caller, true, ...;
end
local function go(thread, arg_box)
while true do
current = thread
if thread == main then
-- transfer to the main thread: stop the scheduler.
return unbox(arg_box);
end
-- transfer to a coroutine: resume it and check the result.
arg_box = box(old_resume(thread, unbox(arg_box)));
if not arg_box[1] then
-- the coroutine finished with an error. pass the error back to the
-- caller thread, or to the main thread if there's no caller thread.
thread = resumers[thread] or main;
arg_box = box(arg_box[1], arg_box[2], debug.traceback());
else
-- loop over the next transfer request.
thread = arg_box[2];
arg_box = rebox(arg_box, 3);
end
end
end
local coro = {};
function coro.create(f)
local thread;
thread = old_create(function(ok, ...)
return finish(thread, f(...));
end);
responsible[thread] = current;
return thread;
end
function coro.running()
return current, current == main;
end
function coro.status(thread)
assert_thread(thread, 2);
return old_status(thread);
end
function coro.ptransfer(thread, ...)
assert(thread ~= current, "trying to transfer to the running thread");
if current ~= main then
-- we're inside a coroutine: signal the transfer request by yielding.
return old_yield(thread, true, ...);
else
-- we're in the main thread: start the scheduler.
local arg_box = box(true, ...);
while true do
current = thread;
if thread == main then
-- transfer to the main thread: stop the scheduler.
return unbox(arg_box);
end
-- transfer to a coroutine: resume it and check the result.
arg_box = box(old_resume(thread, unbox(arg_box)));
if not arg_box[1] then
-- the coroutine finished with an error. pass the error back to the
-- caller thread, or to the main thread if there's no caller thread.
thread = responsible[thread] or main;
arg_box = box(arg_box[1], arg_box[2], debug.traceback());
else
-- loop over the next transfer request.
thread = arg_box[2];
arg_box = rebox(arg_box, 3);
end
end
end
end
function coro.transfer(thread, ...)
-- print(current, ">", thread, ...);
return unprotect(thread, coro.ptransfer(thread, ...));
end
function coro.wrap(f)
local calling_thread, yielding_thread;
local function yield(...)
yielding_thread = current;
return coro.transfer(calling_thread, ...);
end
local function finish(...)
yielding_thread = nil;
return coro.transfer(calling_thread, ...);
end
local function wrapper(...)
return finish(f(yield, ...));
end
local thread = coro.create(wrapper);
yielding_thread = thread;
return function(...)
resumers[thread] = calling_thread;
calling_thread = current;
assert(yielding_thread, "cannot transfer to dead coroutine");
return coro.transfer(yielding_thread, ...);
end, thread;
end
--- @generic T, T1, T2, T3, T4, T5, Args
--- @param f fun(yield: (fun(p1?: T1, p2?: T2, p3?: T3, p4?: T4, p5?: T5): ...), ...: Args)
--- @return fun(...: Args): fun(...): T1, T2, T3, T4, T5
function coro.gen(f)
return function (...)
local prev = box(...);
return coro.wrap(function (yield)
return f(yield, unbox(prev));
end);
end
end
function coro.close(t)
if old_close == nil then
return false, "Closing coroutines is not supported";
else
return old_close(t);
end
end
local coroutine = {};
function coroutine.close(co)
coro.close(co);
end
function coroutine.create(f)
return coro.create(f);
end
function coroutine.isyieldable()
return true;
end
function coroutine.resume(co, ...)
resumers[co] = coro.running();
responsible[co] = coro.running();
return coro.ptransfer(co, ...);
end
function coroutine.yield(...)
local cb = resumers[coro.running()];
if cb == nil then
error("Must call 'yield' from a thread that has been invoked asymmetrically", 2);
end
return coro.transfer(cb, ...);
end
function coroutine.wrap(f)
local co = coroutine.create(f);
return function (...)
return assert(coroutine.resume(co, ...));
end
end
function coroutine.running()
return coro.running();
end
function coroutine.status(co)
return coro.status(co);
end
return function (glob)
glob.coroutine = coroutine;
glob.coro = coro;
end

View File

@ -1,44 +0,0 @@
local env = {};
local has_jit, jit = pcall(require, "jit");
if has_jit then
env.jit = true;
env.os = jit.os;
env.runtime = jit.version;
env.arch = jit.arch;
else
env.jit = false;
print "NOT RUNNING IN LUAJIT!!";
print "Some libraries might depend on ffi. Continue at your own risk!";
env.jit = false;
env.os = "unknown";
env.arch = "unknown";
local function extract_os()
if not io.popen then return end
local uname_f = io.popen("uname -ms");
if uname_f ~= nil then
local os, arch = uname_f:read "*a":sub(1, -2):match "([^%s]+) ([^%s]+)";
env.os = os;
env.arch = arch;
return;
end
local os_name = os.getenv "OS";
if os_name == "Windows_NT" then
env.os = "windows";
env.arch = os.getenv "PROCESSOR_ARCHITECTURE";
end
end
extract_os();
env.runtime = _VERSION;
end
return function (glob)
glob.env = env;
end

View File

@ -1,55 +0,0 @@
local resolver = require "lua-resolver";
local stack = array {};
local active;
local exports = {};
--- @param ... fun()
function exports.push(...)
stack:push(...);
end
function exports.interrupt()
if coro.running() == active then
-- We are trying to interrupt the currently active event loop thread
-- We will transfer away from it and "abandon" it (leave it for non-event loop use)
return coro.transfer(exports.main);
else
-- We are currently in an abandoned or a non-loop thread. We can calmly transfer to the active thread
return coro.transfer(active);
end
end
--- @param main fun(...)
--- @param ... any
function exports.run(main, ...)
if active then
error "Event loop is already running!";
end
exports.main = coro.running();
local args = box(...);
exports.push(function ()
main(unbox(args));
end)
while #stack > 0 do
active = coro.create(function ()
while #stack > 0 and coro.running() == active do
local msg = stack:shift();
msg();
end
coro.transfer(exports.main);
end);
coro.transfer(active);
print "Abandoned thread...";
end
end
-- Register the module as phony
resolver.register("evn-loop.lua", exports);
return exports;

View File

@ -1,57 +0,0 @@
local unpack = unpack or table.unpack;
--- @class functions
local functions = {};
functions.__index = functions;
--- Constructs a function, such that it calls the first function with the passed arguments,
--- the second function with the return of the first, and so on. The return value of the last function is returned
---
--- In short, does the following, if the passed functions are a, b and c: return c(b(a(...)))
---
--- Sometimes less cumbersome to write (a | b | c | d)(args...) than d(c(b(a(args...))))
--- @param self function
function functions:pipe(...)
if ... == nil then
return self;
else
local next = ...;
return functions.pipe(function (...)
return next(self(...));
end, select(2, ...));
end
end
function functions:apply(args)
return self(unpack(args));
end
--- Constructs a function, such that it calls the first function with the passed arguments,
--- the second function with the return of the first, and so on. The return value of the last function is returned
---
--- In short, does the following, if the passed functions are a, b and c: return c(b(a(...)))
---
--- Sometimes less cumbersome to write (a | b | c | d)(args...) than d(c(b(a(args...))))
--- @param self function
function functions:pcall(...)
return pcall(self, ...);
end
--- Calls pipe with a and b; Alternative syntax for older Lua installation
function functions.__sub(a, b)
return functions.pipe(a, b);
end
--- Calls pipe with a and b
function functions.__bor(a, b)
return functions.pipe(a, b);
end
return function (glob)
-- It's not vital to have this metatable, so we will just try our very best
if debug then
debug.setmetatable(debug.setmetatable, functions);
end
glob.functions = functions;
glob["function"] = functions;
end

View File

@ -1,10 +0,0 @@
return function (glob)
require ".polyfills"(glob);
require ".array"(glob);
require ".coro"(glob);
require ".env"(glob);
require ".function"(glob);
require ".printing"(glob);
require ".string"(glob);
require ".utils"(glob);
end

View File

@ -1,72 +0,0 @@
return function (glob)
if glob.getfenv then
glob._ENV = glob.getfenv();
glob._G = _ENV;
else
glob._G = _ENV;
if debug.getupvalue then
local getupvalue = debug.getupvalue;
function glob.getfenv(func)
return getupvalue(func, 1);
end
else
function glob.getfenv()
error("getfenv is not supported", 2);
end
end
end
if not glob.setfenv then
if glob.debug.setupvalue then
local setupvalue = glob.debug.setupvalue;
if glob.debug.getinfo then
local getinfo = glob.debug.getinfo;
function glob.setfenv(func, val)
setupvalue(func, 1, val);
if type(func) == "function" then
return func;
else
return getinfo(func, "f");
end
end
else
function glob.setfenv(func, val)
setupvalue(func, 1, val);
return func;
end
end
else
function glob.setfenv()
error("setfenv is not supported", 2);
end
end
end
-- Reimplement this for lua <5.1
if glob.table.move == nil then
--- @diagnostic disable: duplicate-set-field
function glob.table.move(src, src_b, src_e, dst_b, dst)
if dst == nil then dst = src end
local offset = dst_b - src_b;
if dst_b < src_b then
for i = src_e, src_b, -1 do
dst[i + offset] = src[i];
end
else
for i = src_b, src_e do
dst[i + offset] = src[i];
end
end
return dst;
end
end
-- Why did we *remove* this from the spec again? Classical PUC
glob.unpack = table.unpack or unpack;
glob.table.unpack = unpack;
end

View File

@ -1,141 +0,0 @@
local function escape_str(s)
local in_char = { "\\", "\"", "/", "\b", "\f", "\n", "\r", "\t" };
local out_char = { "\\", "\"", "/", "b", "f", "n", "r", "t" };
for i, c in ipairs(in_char) do
s = s:gsub(c, "\\" .. out_char[i]);
end
return s
end
local function stringify_impl(obj, indent_str, n, passed)
local parts = {};
local kind = type(obj);
if kind == "table" then
if passed[obj] then return "<circular>" end
passed[obj] = true;
local len = #obj;
for i = 1, len do
parts[i] = stringify_impl(obj[i], indent_str, n + 1, passed) .. ",";
end
local keys = {};
for k, v in pairs(obj) do
if type(k) ~= "number" or k > len then
keys[#keys + 1] = { k, v };
end
end
table.sort(keys, function (a, b)
if type(a[1]) == "number" and type(b[1]) == "number" then
return a[1] < b[1];
elseif type(a[1]) == "string" and type(b[1]) == "string" then
return a[1] < b[1];
else
return type(a[1]) < type(b[1]) or tostring(a[1]) < tostring(b[1]);
end
end);
for i = 1, #keys do
local k = keys[i][1];
local v = keys[i][2];
local val = stringify_impl(v, indent_str, n + 1, passed);
if val ~= nil then
if type(k) == "string" then
parts[#parts + 1] = table.concat { k, ": ", val, "," };
else
parts[#parts + 1] = table.concat { "[", stringify_impl(k, indent_str, n + 1, passed), "]: ", val, "," };
end
end
end
local meta = getmetatable(obj);
if meta ~= nil and meta ~= arrays then
parts[#parts + 1] = "<meta> = " .. stringify_impl(meta, indent_str, n + 1, passed) .. ",";
end
passed[obj] = false;
if #parts == 0 then
if meta == arrays then
return "[]";
else
return "{}";
end
end
local contents = table.concat(parts, " "):sub(1, -2);
if #contents > 80 then
local indent = "\n" .. string.rep(indent_str, n + 1);
contents = table.concat {
indent,
table.concat(parts, indent),
"\n", string.rep(indent_str, n)
};
else
contents = " " .. contents .. " ";
end
if meta == arrays then
return table.concat { "[", contents, "]" };
else
return table.concat { "{", contents, "}" };
end
elseif kind == "string" then
return "\"" .. escape_str(obj) .. "\"";
elseif kind == "function" then
local data = debug.getinfo(obj, "S");
return table.concat { tostring(obj), " @ ", data.short_src, ":", data.linedefined };
elseif kind == "nil" then
return "nil";
else
return tostring(obj);
end
end
--- Turns the given value to a human-readable string.
--- Should be used only for debugging and display purposes
local function to_readable(obj, indent_str)
return stringify_impl(obj, indent_str or " ", 0, {});
end
local function print(...)
for i = 1, select("#", ...) do
if i > 1 then
io.stderr:write("\t");
end
io.stderr:write(tostring((select(i, ...))));
end
io.stderr:write("\n");
end
--- Prints the given values in a human-readable manner to stderr
--- Should be used only for debugging
local function pprint(...)
if select("#", ...) == 0 then
io.stderr:write "<nothing>\n";
else
for i = 1, select("#", ...) do
if i > 1 then
io.stderr:write "\t";
end
io.stderr:write(to_readable((select(i, ...))));
end
io.stderr:write "\n";
end
end
return function (glob)
glob.to_readable = to_readable;
glob.print = print;
glob.pprint = pprint;
end

View File

@ -1,70 +0,0 @@
--- @diagnostic disable: duplicate-set-field
return function (glob)
--- @param self string
function glob.string:split(sep)
sep = sep or "";
local lines = array {};
local pos = 1;
if sep == "" then
for i = 1, #self do
lines:push(self:sub(1, 1));
end
else
while true do
local b, e = self:find(sep, pos);
if not b then
table.insert(lines, self:sub(pos));
break;
else
table.insert(lines, self:sub(pos, b - 1));
pos = e + 1;
end
end
end
return lines;
end
--- @param self string
function glob.string:at(i)
return self:sub(i, i);
end
--- @param self string
function glob.string:replace_first(old, new)
local b, e = self:find(old, 1, true);
if b == nil then
return self;
else
return self:sub(1, b - 1) .. new .. self:sub(e + 1);
end
end
--- @param self string
function glob.string:quote()
return ("%q"):format(self);
end
--- @param self string
function glob.string:quotesh()
return "'" .. self
:gsub("*", "\\*")
:gsub("?", "\\?")
:gsub("~", "\\~")
:gsub("$", "\\$")
:gsub("&", "\\&")
:gsub("|", "\\|")
:gsub(";", "\\;")
:gsub("<", "\\<")
:gsub(">", "\\>")
:gsub("(", "\\)")
:gsub("[", "\\]")
:gsub("{", "\\}")
:gsub("%\\", "\\\\")
:gsub("\'", "\\\'")
:gsub("\"", "\\\"")
:gsub("`", "\\`") .. "'";
end
end

View File

@ -1,72 +0,0 @@
return function (glob)
function glob.box(...)
return { n = select("#", ...), ... };
end
function glob.unbox(obj, s, e)
return unpack(obj, s or 1, e or obj.n);
end
function glob.rebox(obj, s, e)
return box(unpack(obj, s or 1, e or obj.n));
end
function glob.exit()
os.exit(0);
end
function glob.str(...)
return table.concat { ... };
end
function glob.iterate(obj)
local i = 0;
return function()
i = i + 1;
return obj[i];
end
end
--- Prepares the object to be a class - puts an __index member in it pointing to the object itself
--- @generic T
--- @param obj T
--- @return T | { __index: T }
function glob.class(obj)
--- @diagnostic disable-next-line: inject-field
obj.__index = obj;
return obj;
end
function glob.try(try_fn, catch_fn, finally_fn)
if not try_fn then
if finally_fn then
return finally_fn();
end
else
local function handle_catch(...)
if finally_fn then
finally_fn();
end
return ...;
end
local function handle_try(ok, ...)
if ok then
if finally_fn then
finally_fn();
end
return ...;
else
if catch_fn then
return handle_catch(catch_fn(...));
elseif finally_fn then
return finally_fn();
end
end
end
return handle_try(pcall(try_fn));
end
end
end

View File

@ -1,23 +0,0 @@
local exports = {};
function exports.exists(path)
local f = io.open(path, "r");
if f == nil then return false end
if f:read(1) == nil then return false end
return true;
end
function exports.home()
return os.getenv "HOME";
end
function exports.pwd()
return os.getenv "PWD" or "./";
end
function exports.ls(path)
return os.execute("ls ")
end
return exports;

View File

@ -1,245 +0,0 @@
local exports = {};
-- We can have arbitrary promises in the middle of the module
-- NO MORE ASYNC AWAIT MADNESS
promise.resolve():await();
local function kind_of(obj)
if type(obj) ~= "table" then return type(obj) end
local i = 1;
for _ in pairs(obj) do
if obj[i] ~= nil then
i = i + 1;
else
return "table";
end
end
if i == 1 then
return "table";
else
return "array";
end
end
local function escape_str(s)
local in_char = { "\\", "\"", "/", "\b", "\f", "\n", "\r", "\t" };
local out_char = { "\\", "\"", "/", "b", "f", "n", "r", "t" };
for i, c in ipairs(in_char) do
s = s:gsub(c, "\\" .. out_char[i]);
end
return s
end
-- Returns pos, did_find; there are two cases:
-- 1. Delimiter found: pos = pos after leading space + delim; did_find = true.
-- 2. Delimiter not found: pos = pos after leading space; did_find = false.
-- This throws an error if err_if_missing is true and the delim is not found.
local function skip_delim(str, pos, delim, err_if_missing)
pos = string.find(str, "%S", pos) or pos;
if string.sub(str, pos, pos) ~= delim then
if err_if_missing then
error(table.concat { "Expected ", delim, " near position ", pos });
end
return pos, false;
end
return pos + 1, true;
end
local esc_map = { b = "\b", f = "\f", n = "\n", r = "\r", t = "\t", v = "\v" };
-- Expects the given pos to be the first character after the opening quote.
-- Returns val, pos; the returned pos is after the closing quote character.
local function parse_str_val(str, pos, check)
if pos > #str then error("End of input found while parsing string") end
if check then
if string.sub(str, pos, pos) ~= "\"" then
return nil, pos;
else
pos = pos + 1;
end
else
pos = pos + 1;
end
local res = {};
while true do
local c = string.sub(str, pos, pos);
if c == "\"" then
return table.concat(res), pos + 1;
elseif c == "\\" then
c = string.sub(str, pos + 1, pos + 1);
res[#res + 1] = esc_map[c] or c;
pos = pos + 2;
else
res[#res + 1] = c;
pos = pos + 1;
end
end
end
-- Returns val, pos; the returned pos is after the number's final character.
local function parse_num_val(str, pos)
local num_str = string.match(str, "^-?%d+%.?%d*[eE]?[+-]?%d*", pos);
local val = tonumber(num_str);
if not val then error(table.concat { "Error parsing number at position ", pos, "." }) end
return val, pos + #num_str;
end
local json_end = { "eof" };
local function parse_impl(str, pos, end_delim)
pos = pos or 1;
if pos > #str then error("Reached unexpected end of input") end
pos = str:find("%S", pos) or pos;
local c = str:sub(pos, pos);
local delim_found;
if c == "{" then
pos = pos + 1;
local key;
local obj = {};
c = string.sub(str, pos, pos);
if c == "}" then
return obj, pos
else
while true do
key, pos = parse_str_val(str, pos, true);
if key == nil then error("Expected a string key") end
pos = skip_delim(str, pos, ":", true); -- true -> error if missing.
obj[key], pos = parse_impl(str, pos);
pos, delim_found = skip_delim(str, pos, "}");
if delim_found then return obj, pos end
pos, delim_found = skip_delim(str, pos, ",");
if not delim_found then error("Expected semicolon or comma") end
end
end
elseif c == "[" then
pos = pos + 1
local arr = array {};
local val;
local delim_found = true;
while true do
val, pos = parse_impl(str, pos, "]");
if val == json_end then return arr, pos end
if not delim_found then error("Comma missing between array items: " .. str:sub(pos, pos + 25)) end
arr:push(val);
pos, delim_found = skip_delim(str, pos, ",");
end
elseif c == "\"" then -- Parse a string.
return parse_str_val(str, pos, false);
elseif c == "-" or c:find("%d") then -- Parse a number.
return parse_num_val(str, pos);
elseif c == end_delim then -- End of an object or array.
return json_end, pos + 1, true;
elseif str:sub(pos, pos + 3) == "null" then
return nil, pos + 4;
elseif str:sub(pos, pos + 3) == "true" then
return true, pos + 4;
elseif str:sub(pos, pos + 4) == "false" then
return true, pos + 5;
else
error(table.concat { "Invalid json syntax starting at position ", pos, ": ", str:sub(pos, pos + 10) });
end
end
local function stringify_impl(obj, all, indent_str, n)
local s = {}; -- We'll build the string as an array of strings to be concatenated.
local kind = kind_of(obj); -- This is 'array' if it's an array or type(obj) otherwise.
if kind == "array" then
for i, val in ipairs(obj) do
s[i] = stringify_impl(val, all, indent_str, n + 1);
end
if not indent_str then
return "[" .. table.concat(s, ",") .. "]";
elseif #s == 0 then
return "[]";
else
local indent = "\n" .. string.rep(indent_str, n + 1);
return table.concat {
"[",
indent, table.concat(s, "," .. indent),
"\n", string.rep(indent_str, n), "]"
};
end
elseif kind == "table" then
for k, v in pairs(obj) do
local sep = indent_str and ": " or ":";
local val = stringify_impl(v, all, indent_str, n + 1);
if val ~= nil then
if type(k) == "string" then
s[#s + 1] = stringify_impl(k) .. sep .. val;
elseif type(k) == "number" then
s[#s + 1] = "\"" .. k .. "\"" .. sep .. val;
elseif type(k) == "boolean" then
s[#s + 1] = "\"" .. k .. "\"" .. sep .. val;
end
end
end
if not indent_str then
return "{" .. table.concat(s, ",") .. "}";
elseif #s == 0 then
return "{}";
else
local indent = "\n" .. string.rep(indent_str, n + 1);
return table.concat {
"{",
indent, table.concat(s, "," .. indent),
"\n", string.rep(indent_str, n), "}"
};
end
return "{" .. table.concat(s, ",") .. "}";
elseif kind == "string" then
return "\"" .. escape_str(obj) .. "\"";
elseif kind == "number" then
return tostring(obj);
elseif kind == 'boolean' then
return tostring(obj);
elseif kind == "nil" then
return "null";
elseif all then
return tostring(obj);
else
return nil;
end
end
function exports.stringify(obj, indent_str)
if indent_str == true then
indent_str = " ";
end
return stringify_impl(obj, false, indent_str, 0);
end
function exports.pretty(obj)
return stringify_impl(obj, true, " ", 0);
end
exports.null = {}; -- This is a one-off table to represent the null value.
---@param str string
---@return unknown
function exports.parse(str)
local obj = parse_impl(str, 1);
return obj;
end
return exports;

View File

@ -1,160 +0,0 @@
---@diagnostic disable: cast-local-type
local exports = {};
---@param ... string
---@return array<string> parts
---@return integer? i
---@return boolean dir
function exports.split(...)
--- @type integer | nil
local i = 0;
--- @type string[]
local res = {};
local dir = false;
local _, start = (... or ""):find("^/+");
if start == nil then
start = 1;
else
i = nil;
dir = true;
end
for i = 1, select("#", ...) do
local p = select(i, ...);
if p ~= nil then
for part, slashes in p:gmatch("([^/]+)(/*)", start) do
dir = #slashes > 0;
if part == ".." then
if #res == 0 then
if i ~= nil then
i = i + 1;
end
else
res[#res] = nil;
end
elseif part ~= "." and part ~= "" then
res[#res + 1] = part;
end
end
end
end
return res, i, dir;
end
---@param parts array<string>
---@param i integer?
---@param dir boolean | "all"
function exports.stringify(parts, i, dir)
local a = table.concat(parts, "/");
if #parts == 0 then
if i == nil then
return dir and "/" or "/.";
elseif i == 0 then
return dir and "./" or "";
else
local res = (".."):rep(i, "/");
return dir and res .. "/" or res;
end
end
local res = i and ("../"):rep(i) or "/";
if dir then
return res .. a .. "/";
else
return res .. a;
end
end
---@param ... string
---@return string path
---@return boolean dir
function exports.join(...)
local parts, i, dir = exports.split(...);
return exports.stringify(parts, i, dir), dir;
end
---@param ... string
---@return string
function exports.join_dir(...)
local p, i = exports.split(...);
return exports.stringify(p, i, true);
end
---@param ... string
---@return string
function exports.join_file(...)
local p, i = exports.split(...);
return exports.stringify(p, i, false);
end
---@param p string
---@return boolean
function exports.is_dir(p)
return p:match "/$" ~= nil;
end
---@param ... string
---@return string
---@return boolean dir
function exports.chroot(...)
local parts, i, dir = exports.split((...));
for i = 2, select("#", ...) do
local new_parts, _, new_dir = exports.split((select(i, ...)));
dir = new_dir;
for j = 1, #new_parts do
parts[#parts + 1] = new_parts[j];
end
end
return exports.stringify(parts, i, dir), dir;
end
---@param ... string
---@return string
function exports.cwd(...)
local parts, i, dir = exports.split((...));
for i = 2, select("#", ...) do
local new_parts, new_i, new_dir = exports.split((select(i, ...)));
dir = new_dir;
if new_i == nil then
parts = new_parts;
i = nil;
else
for _ = 1, new_i do
parts[#parts] = nil;
end
if new_parts ~= nil then
local offset = parts and #parts or 0;
for i = 1, #new_parts do
parts[i + offset] = new_parts[i];
end
end
end
end
return exports.stringify(parts, i, dir);
end
---@param ... string
---@return string
function exports.dirname(...)
local parts, i = exports.split(...);
parts[#parts] = nil;
return exports.stringify(parts, i, true);
end
---@param ... string
---@return string
function exports.filename(...)
return table.remove(exports.split(...)) or "";
end
return exports;

View File

@ -1,145 +0,0 @@
local io = io or require "io";
local exports = {};
--- The default buffer size used by read operations
exports.BUFF_SIZE = 4096;
--- @alias Reader fun(n: number, no_throw?: boolean): string?, string?
--- @alias Writer fun(data: string | nil, no_throw?: boolean): true?, string?
--- Creates a reader from the path
--- @param path string
--- @return Reader? result
function exports.reader(path, force_file)
if path == "-" and not force_file then return exports.stdin end
return exports.reader_from(io.open(path, "rb"))
end
--- @param f? file*
--- @return Reader? result
function exports.reader_from(f, ...)
f = assert(f, ...);
return function (n)
if n == nil then n = exports.BUFF_SIZE end
if n < 0 then
if f ~= nil then
f:close();
f = nil;
end
elseif f == nil then
error("File closed", 2);
else
local res = f:read(n);
if res == nil then
f:close();
f = nil;
return "";
else
return res;
end
end
end
end
--- Creates a writer from the path
--- @param path string
--- @return Writer? result
function exports.writer(path, force_file)
if path == "-" and not force_file then return exports.stdout end
return exports.writer_from(io.open(path, "wb"))
end
--- @param f? file*
--- @return Writer? result
function exports.writer_from(f, ...)
f = assert(f, ...);
return function (data)
if data == nil then
if f ~= nil then
f:close();
f = nil;
end
return true, nil;
elseif f == nil then
error("File closed", 2);
else
local res, w_err = f:write(data);
if res == nil then
error(w_err, 2);
else
return true;
end
end
end
end
--- Writes the full contents of src to dst. Since src is read until its end, it will be closed after this function
--- @param src Reader
--- @param dst Writer
--- @param close_dst boolean? Wether or not to close dst after copying
function exports.copy(src, dst, close_dst)
local res, buff, err, failed;
while true do
buff, err = src(4096, true);
if buff == nil and err ~= nil then
error(err, 2);
elseif buff == nil or buff == "" then
break;
else
res, err = dst(buff, true)
if res == nil then
error(err, 2);
end
end
end
if close_dst then
res, err = dst(nil, true);
if res == nil then
error(err, 2);
end
end
if failed then
error(err, 2);
end
end
function exports.loader(...)
local arr = {...};
local i = 1;
return function ()
while true do
local el = arr[i];
if el == "" then
i = i + 1;
elseif el == nil then
return nil;
elseif type(el) == "function" then
local buff = el();
if buff ~= "" then
return buff;
else
i = i + 1;
end
elseif type(el) == "string" then
i = i + 1;
return el;
else
error("Loader may consist only of strings and functions");
end
end
end
end
exports.stdin = exports.writer_from(io.stdin);
exports.stdout = exports.writer_from(io.stdout);
exports.stderr = exports.writer_from(io.stderr);

View File

@ -1,50 +0,0 @@
--- @meta promise
--- @alias promise<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> promiselib | { when: fun(ful: fun(p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7, p8: T8, p9: T9, p10: T10), rej: fun(err)) }
--- @class promiselib
promise = {};
promise.__index = promise;
--- @generic T1, T2, T3, T4, T5, T6, T7, T8, T9, T10
--- @param on_ful? fun(p1: T1, p2: T2, p3: T3, p4: T4, p5: T5, p6: T6, p7: T7, p8: T8, p9: T9, p10: T10)
--- @param on_rej? fun(err: any)
function promise:when(on_ful, on_rej) end
--- @generic T1, T2, T3, T4, T5, T6, T7, T8, T9, T10
--- @param self promise<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>
--- @returns T1, T2, T3, T4, T5, T6, T7, T8, T9, T10
function promise:await() end
--- @generic T1, T2, T3, T4, T5, T6, T7, T8, T9, T10
--- @param func fun(ful: fun(v: T1, v: T2, v: T3, v: T4, v: T5, v: T6, v: T7, v: T8, v: T9, v: T10), rej: fun(val: any))
--- @return promise<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>
function promise.mk(func) end
--- @generic T1, T2, T3, T4, T5, T6, T7, T8, T9, T10
--- @param p1? T1
--- @param p2? T2
--- @param p3? T3
--- @param p4? T4
--- @param p5? T5
--- @param p6? T6
--- @param p7? T7
--- @param p8? T8
--- @param p9? T9
--- @param p10? T10
--- @return promise<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>
function promise.resolve(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) end
--- @param err any
--- @return promise<any>
function promise.reject(err)
return promise.mk(function (_, rej)
if err and type(err.when) == "function" then
err:when(rej, rej);
else
rej(err);
end
end);
end
return promise;

View File

@ -1,134 +0,0 @@
local loop = require "evn-loop";
local promise = {};
promise.__index = promise;
function promise:when(on_ful, on_rej) end
function promise:await()
local curr = coro.running();
local res = nil;
self:when(function (...)
if coro.running() == curr then
res = box(true, ...);
else
coro.transfer(curr, true, ...);
end
end, function (...)
if coro.running() == curr then
res = box(false, ...);
else
coro.transfer(curr, false, ...);
end
end);
local function process(ok, ...)
if ok then
return ...;
else
error((...), 2);
end
end
if res then
return process(unbox(res));
else
return process(loop.interrupt());
end
end
function promise.mk(func)
local state = 0;
local val = nil;
--- @type array<fun(...)>
local ful_handles = array {};
--- @type array<fun(val: any)>
local rej_handles = array {};
local function invoke_handle(fun, arg_box)
loop.push(function ()
pcall(fun, unbox(arg_box));
end);
end
local function fulfill(...)
if state == 0 then
state = 1;
val = box(...);
for i = 1, #ful_handles do
invoke_handle(ful_handles[i], val);
end
end
end
local function reject(err)
if state == 0 then
state = 2;
val = box(err);
for i = 1, #ful_handles do
invoke_handle(rej_handles[i], val);
end
end
end
local ok, err = pcall(func, fulfill, reject);
if not ok then reject(err) end
return setmetatable({
when = function (_, on_ful, on_rej)
if state == 0 then
ful_handles:push(on_ful);
rej_handles:push(on_rej);
elseif state == 1 then
return invoke_handle(on_ful, val);
elseif state == 2 then
return invoke_handle(on_rej, val);
end
end
}, promise);
end
function promise.resolve(...)
local res = box(...);
return promise.mk(function (ful, rej)
local passed = 0;
for i = 1, res.n do
if type(res[i]) == "table" and type(res[i].when) == "function" then
res[i]:when(
function (...)
res[i] = ...;
passed = passed + 1;
if passed >= res.n then
ful(unbox(res));
end
end,
function (err) rej(err) end
);
else
passed = passed + 1;
end
end
if passed == res.n then
ful(unbox(res));
end
end);
end
function promise.reject(err)
return promise.mk(function (_, rej)
if err and type(err.when) == "function" then
err:when(rej, rej);
else
rej(err);
end
end);
end
return promise;

View File

@ -1,222 +0,0 @@
local arg_parse = require "args";
local path = require "path";
local traceback = require "traceback";
local root = require "root";
local exports = {};
function exports.stacktrace_call(func, ...)
return xpcall(func, function (err)
local trace = traceback(2, "\t");
io.stderr:write "Unhandled error: ";
if type(err) == "string" then
print(err);
else
pprint(err);
end
print(trace);
return err;
end, ...);
end
function exports.load_eval(src, name, env)
local f, err = load(iterate { "return ", src }, name, "t", env);
if f == nil then
f, err = load(src, name, "t", env);
end
return f, err;
end
function exports.repl()
local mod = root.mk(path.join(root.name, "../<repl>"));
function mod.export()
error "Can't export in the REPL";
end
mod.exports = nil;
mod.returns = {};
mod.env.export = mod.export;
mod.env.import = mod.import;
mod.env.require = mod.require;
mod.env.module = mod;
local local_err_shown = false;
while true do
local cont = true;
exports.stacktrace_call(function ()
local src = "";
local err;
repeat
local done = false;
local f;
if src == "" then
io.stderr:write "> ";
else
io.stderr:write "... ";
end
--- @type string
local line = io.stdin:read("l");
if line == nil then
cont = false;
return;
elseif line == "" then
break
end
src = src .. "\n" .. line;
f, err = exports.load_eval(src, "<repl>", mod.env);
if f ~= nil then
if not local_err_shown then
local i = 1;
local locals = array {};
while true do
local n = debug.getlocal(f, i);
if not n then break end
if not n:match("^%(") then
locals:push(n);
end
i = i + 1;
end
if #locals > 0 then
local_err_shown = true;
print "You have defined the following locals in your code:";
print(locals:join ", ");
print "Consider declaring them as globals instead (\"a = 10\" and \"function a() ... end\")";
end
end
pprint(f());
done = true;
end
until done;
if err ~= nil then
error(err, 0);
end
return true;
end);
if not cont then return end
promise.resolve():await();
end
end
function exports.exec(mod, requires, compile, ...)
exports.stacktrace_call(function (...)
for i = 1, #requires do
mod.require(requires[i])
end
local func = compile();
mod.main = true;
mod.returns = box(func(...));
if mod.returns.n > 0 then
mod.exports = mod.returns[1];
end
if type(mod.exports) == "function" then
return mod.exports(...);
elseif type(mod.exports) == "table" and type(mod.exports.main) == "function" then
return mod.exports.main(...);
end
end, ...);
end
function exports.main(...)
require "core";
local file;
local args = array {};
local requires = array {};
local eval;
arg_parse({
function (v)
if eval then
eval:push(v);
elseif file ~= nil then
args:push(v);
else
file = v;
end
return true;
end,
require = function (v)
requires:push(v);
return true;
end,
eval = function ()
eval = array {};
return false, true;
end,
version = function ()
print(str("TAL v", TAL));
print("Created /w love and dread by TopchetoEU");
exit();
end,
help = function ()
print(str("TAL v", TAL, " by TopchetoEU"));
print "A pseudo-runtime on top of my beloved Lua. Includes a better module system and";
print "an improved set of stdlibs to make your life easier.";
print "PLEASE DON'T USE IN PRODUCTION, MIGHT BREAK AT ANY SECOND";
print "Options:";
print "\t--require (-r) [name]: Requires the given package before execution, similar to \"lua -l\"";
print "\t--help (-h): Shows this message";
print "\t--version: Shows the version";
print "\t--eval (-e): Evaluates the rest of the arguments";
print "\t--: Passes the rest of the arguments as arguments";
exit();
end,
r = "require",
h = "help",
e = "eval",
}, ...);
if eval then
local mod = root.mk(path.join(root.name, "../<eval>"));
return exports.exec(mod, requires, function ()
return assert(exports.load_eval(eval:join " ", "<eval>", mod.env));
end, unpack(args));
elseif file then
return exports.stacktrace_call(function (...)
if not file:match "^/" then
file = "./" .. file;
end
local mod = root.import(file);
if type(mod) == "function" then
return mod(...);
elseif mod and type(mod.main) == "function" then
return mod.main(...);
end
end, ...);
else
return exports.repl();
end
end
return exports;

View File

@ -1,34 +0,0 @@
return function(i, prefix)
i = i + 1;
local lines = array {};
while true do
local info = debug.getinfo(i, "Snl");
if info == nil then break end
local name = info.name;
local location = info.short_src;
if location == "[C]" then
location = "at <internal>";
elseif string.find(location, "[string", 1, true) then
location = "at <string>";
else
location = "at " .. location;
end
if info.currentline > 0 then
location = location .. ":" .. info.currentline;
end
if name ~= nil then
lines:push(prefix .. location .. " in " .. name);
else
lines:push(prefix .. location);
end
i = i + 1
end
return lines:join "\n";
end