blob: 2179000e92e0c18b450691e22c93d1f6371574e4 [file] [log] [blame] [raw]
local unicode = require("unicode")
local text = {}
function text.detab(value, tabWidth)
checkArg(1, value, "string")
checkArg(2, tabWidth, "number", "nil")
tabWidth = tabWidth or 4
local function rep(match)
local spaces = tabWidth - match:len() % tabWidth
return match .. string.rep(" ", spaces)
end
return value:gsub("([^\n]-)\t", rep)
end
function text.padRight(value, length)
checkArg(1, value, "string", "nil")
checkArg(2, length, "number")
local unicode = require("unicode")
if not value or unicode.len(value) == 0 then
return string.rep(" ", length)
else
return value .. string.rep(" ", length - unicode.len(value))
end
end
function text.padLeft(value, length)
checkArg(1, value, "string", "nil")
checkArg(2, length, "number")
local unicode = require("unicode")
if not value or unicode.len(value) == 0 then
return string.rep(" ", length)
else
return string.rep(" ", length - unicode.len(value)) .. value
end
end
function text.trim(value) -- from http://lua-users.org/wiki/StringTrim
local from = string.match(value, "^%s*()")
return from > #value and "" or string.match(value, ".*%S", from)
end
-------------------------------------------------------------------------------
-- Important: pretty formatting will allow presenting non-serializable values
-- but may generate output that cannot be unserialized back.
function text.serialize(value, pretty)
local kw = {["and"]=true, ["break"]=true, ["do"]=true, ["else"]=true,
["elseif"]=true, ["end"]=true, ["false"]=true, ["for"]=true,
["function"]=true, ["goto"]=true, ["if"]=true, ["in"]=true,
["local"]=true, ["nil"]=true, ["not"]=true, ["or"]=true,
["repeat"]=true, ["return"]=true, ["then"]=true, ["true"]=true,
["until"]=true, ["while"]=true}
local id = "^[%a_][%w_]*$"
local ts = {}
local function s(v)
local t = type(v)
if t == "nil" then
return "nil"
elseif t == "boolean" then
return v and "true" or "false"
elseif t == "number" then
if v ~= v then
return "0/0"
elseif v == math.huge then
return "math.huge"
elseif v == -math.huge then
return "-math.huge"
else
return tostring(v)
end
elseif t == "string" then
return string.format("%q", v)
elseif t == "table" then
if ts[v] then
if pretty then
return "recursion"
else
error("tables with cycles are not supported")
end
end
ts[v] = true
local i, r = 1, nil
for k, v in pairs(v) do
if r then
r = r .. ","
else
r = "{"
end
local tk = type(k)
if tk == "number" and k == i then
i = i + 1
r = r .. s(v)
else
if tk == "string" and not kw[k] and string.match(k, id) then
r = r .. k
else
r = r .. "[" .. s(k) .. "]"
end
r = r .. "=" .. s(v)
end
end
ts[v] = nil -- allow writing same table more than once
return (r or "{") .. "}"
else
if pretty then
return tostring(t)
else
error("unsupported type: " .. t)
end
end
end
local result = s(value)
local limit = type(pretty) == "number" and pretty or 1000
if pretty and unicode.len(result) > limit then
return result:sub(1, limit) .. "..."
end
return result
end
function text.unserialize(data)
checkArg(1, data, "string")
local result, reason = load("return " .. data, "=data", _, {math={huge=math.huge}})
if not result then
return nil, reason
end
local ok, output = pcall(result)
if not ok then
return nil, output
end
return output
end
-------------------------------------------------------------------------------
return text