From 9c9252f3873a89f9642aa6c7ff9d5e914c4042cd Mon Sep 17 00:00:00 2001 From: V1K1NGbg Date: Thu, 22 Aug 2024 17:49:06 -0300 Subject: Teal: convert luarocks.core.util --- src/luarocks/core/util.lua | 322 ------------------------------------------- src/luarocks/core/util.tl | 335 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 335 insertions(+), 322 deletions(-) delete mode 100644 src/luarocks/core/util.lua create mode 100644 src/luarocks/core/util.tl (limited to 'src') diff --git a/src/luarocks/core/util.lua b/src/luarocks/core/util.lua deleted file mode 100644 index e9abdd34..00000000 --- a/src/luarocks/core/util.lua +++ /dev/null @@ -1,322 +0,0 @@ - -local util = {} - -local require = nil --------------------------------------------------------------------------------- - -local dir_sep = package.config:sub(1, 1) - ---- Run a process and read a its output. --- Equivalent to io.popen(cmd):read("*l"), except that it --- closes the fd right away. --- @param cmd string: The command to execute --- @param spec string: "*l" by default, to read a single line. --- May be used to read more, passing, for instance, "*a". --- @return string: the output of the program. -function util.popen_read(cmd, spec) - local tmpfile = (dir_sep == "\\") - and (os.getenv("TMP") .. "/luarocks-" .. tostring(math.floor(math.random() * 10000))) - or os.tmpname() - os.execute(cmd .. " > " .. tmpfile) - local fd = io.open(tmpfile, "rb") - if not fd then - os.remove(tmpfile) - return "" - end - local out = fd:read(spec or "*l") - fd:close() - os.remove(tmpfile) - return out or "" -end - ---- --- Formats tables with cycles recursively to any depth. --- References to other tables are shown as values. --- Self references are indicated. --- The string returned is "Lua code", which can be processed --- (in the case in which indent is composed by spaces or "--"). --- Userdata and function keys and values are shown as strings, --- which logically are exactly not equivalent to the original code. --- This routine can serve for pretty formating tables with --- proper indentations, apart from printing them: --- io.write(table.show(t, "t")) -- a typical use --- Written by Julio Manuel Fernandez-Diaz, --- Heavily based on "Saving tables with cycles", PIL2, p. 113. --- @param t table: is the table. --- @param tname string: is the name of the table (optional) --- @param top_indent string: is a first indentation (optional). --- @return string: the pretty-printed table -function util.show_table(t, tname, top_indent) - local cart -- a container - local autoref -- for self references - - local function is_empty_table(tbl) return next(tbl) == nil end - - local function basic_serialize(o) - local so = tostring(o) - if type(o) == "function" then - local info = debug and debug.getinfo(o, "S") - if not info then - return ("%q"):format(so) - end - -- info.name is nil because o is not a calling level - if info.what == "C" then - return ("%q"):format(so .. ", C function") - else - -- the information is defined through lines - return ("%q"):format(so .. ", defined in (" .. info.linedefined .. "-" .. info.lastlinedefined .. ")" .. info.source) - end - elseif type(o) == "number" then - return so - else - return ("%q"):format(so) - end - end - - local function add_to_cart(value, name, indent, saved, field) - indent = indent or "" - saved = saved or {} - field = field or name - - cart = cart .. indent .. field - - if type(value) ~= "table" then - cart = cart .. " = " .. basic_serialize(value) .. ";\n" - else - if saved[value] then - cart = cart .. " = {}; -- " .. saved[value] .. " (self reference)\n" - autoref = autoref .. name .. " = " .. saved[value] .. ";\n" - else - saved[value] = name - if is_empty_table(value) then - cart = cart .. " = {};\n" - else - cart = cart .. " = {\n" - for k, v in pairs(value) do - k = basic_serialize(k) - local fname = ("%s[%s]"):format(name, k) - field = ("[%s]"):format(k) - -- three spaces between levels - add_to_cart(v, fname, indent .. " ", saved, field) - end - cart = cart .. indent .. "};\n" - end - end - end - end - - tname = tname or "__unnamed__" - if type(t) ~= "table" then - return tname .. " = " .. basic_serialize(t) - end - cart, autoref = "", "" - add_to_cart(t, tname, top_indent) - return cart .. autoref -end - ---- Merges contents of src on top of dst's contents --- (i.e. if an key from src already exists in dst, replace it). --- @param dst Destination table, which will receive src's contents. --- @param src Table which provides new contents to dst. -function util.deep_merge(dst, src) - for k, v in pairs(src) do - if type(v) == "table" then - if dst[k] == nil then - dst[k] = {} - end - if type(dst[k]) == "table" then - util.deep_merge(dst[k], v) - else - dst[k] = v - end - else - dst[k] = v - end - end -end - ---- Merges contents of src below those of dst's contents --- (i.e. if an key from src already exists in dst, do not replace it). --- @param dst Destination table, which will receive src's contents. --- @param src Table which provides new contents to dst. -function util.deep_merge_under(dst, src) - for k, v in pairs(src) do - if type(v) == "table" then - if dst[k] == nil then - dst[k] = {} - end - if type(dst[k]) == "table" then - util.deep_merge_under(dst[k], v) - end - elseif dst[k] == nil then - dst[k] = v - end - end -end - ---- Clean up a path-style string ($PATH, $LUA_PATH/package.path, etc.), --- removing repeated entries and making sure only the relevant --- Lua version is used. --- Example: given ("a;b;c;a;b;d", ";"), returns "a;b;c;d". --- @param list string: A path string (from $PATH or package.path) --- @param sep string: The separator --- @param lua_version (optional) string: The Lua version to use. --- @param keep_first (optional) if true, keep first occurrence in case --- of duplicates; otherwise keep last occurrence. The default is false. -function util.cleanup_path(list, sep, lua_version, keep_first) - assert(type(list) == "string") - assert(type(sep) == "string") - - list = list:gsub(dir_sep, "/") - - local parts = util.split_string(list, sep) - local final, entries = {}, {} - local start, stop, step - - if keep_first then - start, stop, step = 1, #parts, 1 - else - start, stop, step = #parts, 1, -1 - end - - for i = start, stop, step do - local part = parts[i]:gsub("//", "/") - if lua_version then - part = part:gsub("/lua/([%d.]+)/", function(part_version) - if part_version:sub(1, #lua_version) ~= lua_version then - return "/lua/"..lua_version.."/" - end - end) - end - if not entries[part] then - local at = keep_first and #final+1 or 1 - table.insert(final, at, part) - entries[part] = true - end - end - - return (table.concat(final, sep):gsub("/", dir_sep)) -end - --- from http://lua-users.org/wiki/SplitJoin --- by Philippe Lhoste -function util.split_string(str, delim, maxNb) - -- Eliminate bad cases... - if string.find(str, delim) == nil then - return { str } - end - if maxNb == nil or maxNb < 1 then - maxNb = 0 -- No limit - end - local result = {} - local pat = "(.-)" .. delim .. "()" - local nb = 0 - local lastPos - for part, pos in string.gmatch(str, pat) do - nb = nb + 1 - result[nb] = part - lastPos = pos - if nb == maxNb then break end - end - -- Handle the last field - if nb ~= maxNb then - result[nb + 1] = string.sub(str, lastPos) - end - return result -end - ---- Return an array of keys of a table. --- @param tbl table: The input table. --- @return table: The array of keys. -function util.keys(tbl) - local ks = {} - for k,_ in pairs(tbl) do - table.insert(ks, k) - end - return ks -end - ---- Print a line to standard error -function util.printerr(...) - io.stderr:write(table.concat({...},"\t")) - io.stderr:write("\n") -end - ---- Display a warning message. --- @param msg string: the warning message -function util.warning(msg) - util.printerr("Warning: "..msg) -end - ---- Simple sort function used as a default for util.sortedpairs. -local function default_sort(a, b) - local ta = type(a) - local tb = type(b) - if ta == "number" and tb == "number" then - return a < b - elseif ta == "number" then - return true - elseif tb == "number" then - return false - else - return tostring(a) < tostring(b) - end -end - ---- A table iterator generator that returns elements sorted by key, --- to be used in "for" loops. --- @param tbl table: The table to be iterated. --- @param sort_function function or table or nil: An optional comparison function --- to be used by table.sort when sorting keys, or an array listing an explicit order --- for keys. If a value itself is an array, it is taken so that the first element --- is a string representing the field name, and the second element is a priority table --- for that key, which is returned by the iterator as the third value after the key --- and the value. --- @return function: the iterator function. -function util.sortedpairs(tbl, sort_function) - sort_function = sort_function or default_sort - local keys = util.keys(tbl) - local sub_orders = {} - - if type(sort_function) == "function" then - table.sort(keys, sort_function) - else - local order = sort_function - local ordered_keys = {} - local all_keys = keys - keys = {} - - for _, order_entry in ipairs(order) do - local key, sub_order - if type(order_entry) == "table" then - key = order_entry[1] - sub_order = order_entry[2] - else - key = order_entry - end - - if tbl[key] then - ordered_keys[key] = true - sub_orders[key] = sub_order - table.insert(keys, key) - end - end - - table.sort(all_keys, default_sort) - for _, key in ipairs(all_keys) do - if not ordered_keys[key] then - table.insert(keys, key) - end - end - end - - local i = 1 - return function() - local key = keys[i] - i = i + 1 - return key, tbl[key], sub_orders[key] - end -end - -return util - diff --git a/src/luarocks/core/util.tl b/src/luarocks/core/util.tl new file mode 100644 index 00000000..a9be2d43 --- /dev/null +++ b/src/luarocks/core/util.tl @@ -0,0 +1,335 @@ + +local record util +end + +-------------------------------------------------------------------------------- + +local type Ordering = require("luarocks.core.types.ordering").Ordering +local type SortBy = require("luarocks.core.types.ordering").SortBy + +local dir_sep = package.config:sub(1, 1) + +--- Run a process and read a its output. +-- Equivalent to io.popen(cmd):read("*l"), except that it +-- closes the fd right away. +-- @param cmd string: The command to execute +-- @param spec string: "*l" by default, to read a single line. +-- May be used to read more, passing, for instance, "*a". +-- @return string: the output of the program. +function util.popen_read(cmd: string, spec?: string): string + local tmpfile: string = (dir_sep == "\\") + and (os.getenv("TMP") .. "/luarocks-" .. tostring(math.floor(math.random() * 10000))) + or os.tmpname() + os.execute(cmd .. " > " .. tmpfile) + local fd = io.open(tmpfile, "rb") + if not fd then + os.remove(tmpfile) + return "" + end + local out = fd:read(spec or "*l") + fd:close() + os.remove(tmpfile) + return out or "" +end + +--- +-- Formats tables with cycles recursively to any depth. +-- References to other tables are shown as values. +-- Self references are indicated. +-- The string returned is "Lua code", which can be processed +-- (in the case in which indent is composed by spaces or "--"). +-- Userdata and function keys and values are shown as strings, +-- which logically are exactly not equivalent to the original code. +-- This routine can serve for pretty formating tables with +-- proper indentations, apart from printing them: +-- io.write(table.show(t, "t")) -- a typical use +-- Written by Julio Manuel Fernandez-Diaz, +-- Heavily based on "Saving tables with cycles", PIL2, p. 113. +-- @param t table: is the table. +-- @param tname string: is the name of the table (optional) +-- @param top_indent string: is a first indentation (optional). +-- @return string: the pretty-printed table +function util.show_table(t: {any:any}, tname: string, top_indent: string): string + local cart: string -- a container + local autoref: string -- for self references + + local function is_empty_table(tbl: {any:any}): boolean return next(tbl) == nil end + + local function basic_serialize(o: any): string + local so = tostring(o) + if o is function then + local info = debug and debug.getinfo(o, "S") + if not info then + return ("%q"):format(so) + end + -- info.name is nil because o is not a calling level + if info.what == "C" then + return ("%q"):format(so .. ", C function") + else + -- the information is defined through lines + return ("%q"):format(so .. ", defined in (" .. info.linedefined .. "-" .. info.lastlinedefined .. ")" .. info.source) + end + elseif o is number then + return so + else + return ("%q"):format(so) + end + end + + local function add_to_cart (value: any | {any:any}, name: string, indent: string, saved?: {any: string}, field?: string) + indent = indent or "" + saved = saved or {} + field = field or name + + cart = cart .. indent .. field + + if not value is {any: any} then + cart = cart .. " = " .. basic_serialize(value) .. ";\n" + else + if saved[value] then + cart = cart .. " = {}; -- " .. saved[value] .. " (self reference)\n" + autoref = autoref .. name .. " = " .. saved[value] .. ";\n" + else + saved[value] = name + if is_empty_table(value) then + cart = cart .. " = {};\n" + else + cart = cart .. " = {\n" + for k, v in pairs(value) do + k = basic_serialize(k) + local fname = ("%s[%s]"):format(name, k) + field = ("[%s]"):format(k) + -- three spaces between levels + add_to_cart(v, fname, indent .. " ", saved, field) + end + cart = cart .. indent .. "};\n" + end + end + end + end + + tname = tname or "__unnamed__" + if not t is {any:any} then + return tname .. " = " .. basic_serialize(t) + end + cart, autoref = "", "" + add_to_cart(t, tname, top_indent) + return cart .. autoref +end + +--- Produce a Lua pattern that matches precisely the given string +-- (this is suitable to be concatenating to other patterns, +-- so it does not include beginning- and end-of-string markers (^$) +-- @param s string: The input string +-- @return string: The equivalent pattern +function util.matchquote(s: string): string + return (s:gsub("[?%-+*%[%].%%()$^]","%%%1")) +end + +--- Merges contents of src on top of dst's contents +-- (i.e. if an key from src already exists in dst, replace it). +-- @param dst Destination table, which will receive src's contents. +-- @param src Table which provides new contents to dst. +function util.deep_merge(dst: {any: any}, src: {any: any}) + for k, v in pairs(src) do + if v is {any: any} then + local dstk: any | {any: any} = dst[k] + if dstk == nil then + dst[k] = {} + dstk = dst[k] + end + if dstk is {any: any} then + util.deep_merge(dstk, v) + else + dst[k] = v + end + else + dst[k] = v + end + end +end + +--- Merges contents of src below those of dst's contents +-- (i.e. if an key from src already exists in dst, do not replace it). +-- @param dst Destination table, which will receive src's contents. +-- @param src Table which provides new contents to dst. +function util.deep_merge_under(dst: {any: any}, src: {any: any}) + for k, v in pairs(src) do + if v is {any: any} then + local dstk: any | {any: any} = dst[k] + if dstk == nil then + dst[k] = {} + dstk = dst[k] + end + if dstk is {any: any} then + util.deep_merge_under(dstk, v) + end + elseif dst[k] == nil then + dst[k] = v + end + end +end + +-- from http://lua-users.org/wiki/SplitJoin +-- by Philippe Lhoste +function util.split_string(str: string, delim: string, maxNb?: number): {string} + -- Eliminate bad cases... + if string.find(str, delim) == nil then + return { str } + end + if maxNb == nil or maxNb < 1 then + maxNb = 0 -- No limit + end + local result = {} + local pat = "(.-)" .. delim .. "()" + local nb = 0 + local lastPos: integer + for part, pos in string.gmatch(str, pat) do + nb = nb + 1 + result[nb] = part + lastPos = tonumber(pos) as integer + if nb == maxNb then break end + end + -- Handle the last field + if nb ~= maxNb then + result[nb + 1] = string.sub(str, lastPos) + end + return result +end + + +--- Clean up a path-style string ($PATH, $LUA_PATH/package.path, etc.), +-- removing repeated entries and making sure only the relevant +-- Lua version is used. +-- Example: given ("a;b;c;a;b;d", ";"), returns "a;b;c;d". +-- @param list string: A path string (from $PATH or package.path) +-- @param sep string: The separator +-- @param lua_version (optional) string: The Lua version to use. +-- @param keep_first (optional) if true, keep first occurrence in case +-- of duplicates; otherwise keep last occurrence. The default is false. +function util.cleanup_path(list: string, sep: string, lua_version: string, keep_first: boolean): string + + list = list:gsub(dir_sep, "/") + + local parts = util.split_string(list, sep) + local final, entries = {}, {} + local start, stop, step: integer, integer, integer + + if keep_first then + start, stop, step = 1, #parts, 1 + else + start, stop, step = #parts, 1, -1 + end + + for i = start, stop, step do + local part = parts[i]:gsub("//", "/") + if lua_version then + part = part:gsub("/lua/([%d.]+)/", function(part_version: string): string + if part_version:sub(1, #lua_version) ~= lua_version then + return "/lua/"..lua_version.."/" + end + end) + end + if not entries[part] then + local at = keep_first and #final+1 or 1 + table.insert(final, at, part) + entries[part] = true + end + end + + return (table.concat(final, sep):gsub("/", dir_sep)) +end + +--- Return an array of keys of a table. +-- @param tbl table: The input table. +-- @return table: The array of keys. +function util.keys(tbl: {K: V}): {K} + local ks = {} + for k,_ in pairs(tbl) do + table.insert(ks, k) + end + return ks +end + +--- Print a line to standard error +function util.printerr(...: string | number) + io.stderr:write(table.concat({...},"\t")) + io.stderr:write("\n") +end + +--- Display a warning message. +-- @param msg string: the warning message +function util.warning(msg: string) + util.printerr("Warning: "..msg) +end + +--- Simple sort function used as a default for util.sortedpairs. +local function default_sort(a: any, b: any): boolean + local ta = type(a) + local tb = type(b) + if ta == "number" and tb == "number" then + return tonumber(a) < tonumber(b) + elseif ta == "number" then + return true + elseif tb == "number" then + return false + else + return tostring(a) < tostring(b) + end +end + +--- A table iterator generator that returns elements sorted by key, +-- to be used in "for" loops. +-- @param tbl table: The table to be iterated. +-- @param sort_function function or table or nil: An optional comparison function +-- to be used by table.sort when sorting keys, or an array listing an explicit order +-- for keys. If a value itself is an array, it is taken so that the first element +-- is a string representing the field name, and the second element is a priority table +-- for that key, which is returned by the iterator as the third value after the key +-- and the value. +-- @return function: the iterator function. +function util.sortedpairs(tbl: {K: V}, sort_by?: SortBy): function(): K, V, Ordering + local keys = util.keys(tbl) + local sub_orders: {K: Ordering} = nil + + if sort_by == nil then + table.sort(keys, default_sort) + elseif sort_by is table.SortFunction then + table.sort(keys, sort_by) + else + -- sort_by is Ordering + + sub_orders = sort_by.sub_orders + + local seen_ordered_key: {K: boolean} = {} + + local my_ordered_keys: {K} = {} + + for _, key in ipairs(sort_by) do + if tbl[key] then + seen_ordered_key[key] = true + table.insert(my_ordered_keys, key) + end + end + + table.sort(keys, default_sort) + + for _, key in ipairs(keys) do + if not seen_ordered_key[key] then + table.insert(my_ordered_keys, key) + end + end + + keys = my_ordered_keys + end + + local i = 1 + return function(): K, V, Ordering + local key = keys[i] + i = i + 1 + return key, tbl[key], sub_orders and sub_orders[key] + end +end + +return util + -- cgit v1.2.3-55-g6feb