batteries/stringx.lua

107 lines
2.6 KiB
Lua

--[[
extra string routines
]]
local stringx = setmetatable({}, {
__index = string
})
--split a string on a delimiter into an ordered table
function stringx:split(delim)
--we try to create as little garbage as possible!
--only one table to contain the result, plus the split strings.
--so we do two passes, and work with the bytes underlying the string
--partly because string.find is not compiled on older luajit :)
local res = {}
local length = self:len()
--
local delim_length = delim:len()
--empty delim? split to individual characters
if delim_length == 0 then
for i = 1, length do
table.insert(res, self:sub(i, i))
end
return res
end
local delim_start = delim:byte(1)
--pass 1
--collect split sites
local i = 1
while i <= length do
--scan for delimiter
if self:byte(i) == delim_start then
local has_whole_delim = true
for j = 2, delim_length do
if self:byte(i + j - 1) ~= delim:byte(j) then
has_whole_delim = false
break
end
end
if has_whole_delim then
table.insert(res, i)
end
--iterate forward
i = i + delim_length
else
--iterate forward
i = i + 1
end
end
--pass 2
--collect substrings
i = 1
for si, j in ipairs(res) do
res[si] = self:sub(i, j-1)
i = j + delim_length
end
--add the final section
table.insert(res, self:sub(i, -1))
--return the collection
return res
end
--turn input into a vaguely easy to read string
--(which is also able to be parsed by lua in many cases)
--todo: multi-line for big tables
--todo: support self-referential tables at least without crashing :)
function stringx.pretty(input)
--if the input is not a table, or it has a tostring metamethod
--then we can just use tostring
local mt = getmetatable(input)
if type(input) ~= "table" or mt and mt.__tostring then
local s = tostring(input)
--quote strings
if type(input) == "string" then
s = '"' .. s .. '"'
end
return s
end
--otherwise, we've got to build up a table representation
--collate into member chunks
local chunks = {}
--(tracking for already-seen elements from ipairs)
local seen = {}
--sequential part first
--(in practice, pairs already does this, but the order isn't guaranteed)
for i, v in ipairs(input) do
seen[i] = true
table.insert(chunks, stringx.pretty(v))
end
--non sequential follows
for k, v in pairs(input) do
if not seen[k] then
--encapsulate anything that's not a string
--todo: also keywords
if type(k) ~= "string" then
k = "[" .. tostring(k) .. "]"
end
table.insert(chunks, k .. " = " .. stringx.pretty(v))
end
end
return "{" .. table.concat(chunks, ", ") .. "}"
end
return stringx