Compare commits
No commits in common. "2efe383cbfc2bbcd28ffea646703f67e670bd809" and "9c6ede00df3cb82aa5fe49d214aeb523d4987043" have entirely different histories.
2efe383cbf
...
9c6ede00df
7
.gitignore
vendored
7
.gitignore
vendored
@ -1,7 +0,0 @@
|
|||||||
/*
|
|
||||||
!/addon/
|
|
||||||
!/build/
|
|
||||||
!/core/
|
|
||||||
!/mod/
|
|
||||||
!/.gitignore
|
|
||||||
!/Makefile
|
|
13
Makefile
13
Makefile
@ -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
|
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -1,8 +0,0 @@
|
|||||||
--- @meta
|
|
||||||
|
|
||||||
--- @class env
|
|
||||||
--- @field jit boolean
|
|
||||||
--- @field os string
|
|
||||||
--- @field runtime string
|
|
||||||
--- @field arch string
|
|
||||||
env = {};
|
|
@ -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
|
|
@ -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;
|
|
@ -1,4 +0,0 @@
|
|||||||
--- @meta
|
|
||||||
|
|
||||||
unpack = table.unpack;
|
|
||||||
table.unpack = unpack;
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
118
build/init.lua
118
build/init.lua
@ -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
|
|
871
build/lexer.lua
871
build/lexer.lua
@ -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,
|
|
||||||
}
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
324
core/module.lua
324
core/module.lua
@ -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;
|
|
106
mod/args.lua
106
mod/args.lua
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
23
mod/fs.lua
23
mod/fs.lua
@ -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;
|
|
245
mod/json.lua
245
mod/json.lua
@ -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;
|
|
160
mod/path.lua
160
mod/path.lua
@ -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;
|
|
145
mod/pipes.lua
145
mod/pipes.lua
@ -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);
|
|
@ -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;
|
|
134
mod/promise.lua
134
mod/promise.lua
@ -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;
|
|
222
mod/tal/cli.lua
222
mod/tal/cli.lua
@ -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;
|
|
@ -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
|
|
Loading…
Reference in New Issue
Block a user