2021-06-11 20:47:29 +02:00
|
|
|
-- Localize globals
|
2021-06-17 19:45:08 +02:00
|
|
|
local assert, error, io, ipairs, loadfile, math, minetest, modlib, pairs, setfenv, setmetatable, table, type = assert, error, io, ipairs, loadfile, math, minetest, modlib, pairs, setfenv, setmetatable, table, type
|
|
|
|
|
|
|
|
-- Set environment
|
|
|
|
local _ENV = {}
|
|
|
|
setfenv(1, _ENV)
|
2021-06-11 20:47:29 +02:00
|
|
|
|
2021-07-03 19:35:02 +02:00
|
|
|
lua_log_file = {
|
|
|
|
-- default value
|
|
|
|
reference_strings = true
|
|
|
|
}
|
2021-04-21 21:11:00 +02:00
|
|
|
local files = {}
|
|
|
|
local metatable = {__index = lua_log_file}
|
|
|
|
|
2021-07-03 19:35:02 +02:00
|
|
|
function lua_log_file.new(file_path, root, reference_strings)
|
|
|
|
local self = setmetatable({
|
|
|
|
file_path = assert(file_path),
|
|
|
|
root = root,
|
|
|
|
reference_strings = reference_strings
|
|
|
|
}, metatable)
|
2021-04-21 21:29:22 +02:00
|
|
|
if minetest then
|
|
|
|
files[self] = true
|
|
|
|
end
|
|
|
|
return self
|
2021-04-21 21:11:00 +02:00
|
|
|
end
|
|
|
|
|
2021-07-03 19:35:02 +02:00
|
|
|
local function set_references(self, table)
|
|
|
|
-- Weak table keys to allow the collection of dead reference tables
|
|
|
|
-- TODO garbage collect strings in the references table
|
|
|
|
self.references = setmetatable(table, {__mode = "k"})
|
|
|
|
end
|
|
|
|
|
2021-04-21 21:11:00 +02:00
|
|
|
function lua_log_file:load()
|
2021-04-21 21:29:22 +02:00
|
|
|
-- Bytecode is blocked by the engine
|
|
|
|
local read = assert(loadfile(self.file_path))
|
2021-04-22 11:54:25 +02:00
|
|
|
-- math.huge is serialized to inf
|
|
|
|
local env = {inf = math.huge}
|
2021-04-21 21:29:22 +02:00
|
|
|
setfenv(read, env)
|
|
|
|
read()
|
|
|
|
env.R = env.R or {{}}
|
|
|
|
self.reference_count = #env.R
|
|
|
|
self.root = env.R[1]
|
2021-07-03 19:35:02 +02:00
|
|
|
set_references(self, {})
|
2021-04-21 21:11:00 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
function lua_log_file:open()
|
2021-04-21 21:29:22 +02:00
|
|
|
self.file = io.open(self.file_path, "a+")
|
2021-04-21 21:11:00 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
function lua_log_file:init()
|
2021-04-21 21:29:22 +02:00
|
|
|
if modlib.file.exists(self.file_path) then
|
|
|
|
self:load()
|
|
|
|
self:_rewrite()
|
|
|
|
self:open()
|
|
|
|
return
|
|
|
|
end
|
|
|
|
self:open()
|
|
|
|
self.root = {}
|
|
|
|
self:_write()
|
2021-04-21 21:11:00 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
function lua_log_file:log(statement)
|
2021-04-21 21:29:22 +02:00
|
|
|
self.file:write(statement)
|
|
|
|
self.file:write"\n"
|
2021-04-21 21:11:00 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
function lua_log_file:flush()
|
2021-04-21 21:29:22 +02:00
|
|
|
self.file:flush()
|
2021-04-21 21:11:00 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
function lua_log_file:close()
|
2021-04-21 21:29:22 +02:00
|
|
|
self.file:close()
|
|
|
|
self.file = nil
|
|
|
|
files[self] = nil
|
2021-04-21 21:11:00 +02:00
|
|
|
end
|
|
|
|
|
2021-04-21 21:28:27 +02:00
|
|
|
if minetest then
|
2021-04-21 21:29:22 +02:00
|
|
|
minetest.register_on_shutdown(function()
|
|
|
|
for self in pairs(files) do
|
|
|
|
self.file:close()
|
|
|
|
end
|
|
|
|
end)
|
2021-04-21 21:28:27 +02:00
|
|
|
end
|
2021-04-21 21:11:00 +02:00
|
|
|
|
2021-04-22 13:32:38 +02:00
|
|
|
function lua_log_file:_dump(value, is_key)
|
2021-04-21 21:29:22 +02:00
|
|
|
if value == nil then
|
|
|
|
return "nil"
|
|
|
|
end
|
|
|
|
if value == true then
|
|
|
|
return "true"
|
|
|
|
end
|
|
|
|
if value == false then
|
|
|
|
return "false"
|
|
|
|
end
|
|
|
|
if value ~= value then
|
|
|
|
-- nan
|
|
|
|
return "0/0"
|
|
|
|
end
|
|
|
|
local _type = type(value)
|
|
|
|
if _type == "number" then
|
|
|
|
return ("%.17g"):format(value)
|
|
|
|
end
|
2021-05-12 20:53:44 +02:00
|
|
|
local reference = self.references[value]
|
|
|
|
if reference then
|
|
|
|
return "R[" .. reference .."]"
|
2021-04-21 21:29:22 +02:00
|
|
|
end
|
2021-05-12 20:53:44 +02:00
|
|
|
reference = self.reference_count + 1
|
2021-04-22 13:32:38 +02:00
|
|
|
local key = "R[" .. reference .."]"
|
2021-05-26 10:33:34 +02:00
|
|
|
local function create_reference()
|
|
|
|
self.reference_count = reference
|
|
|
|
self.references[value] = reference
|
|
|
|
end
|
2021-04-21 21:29:22 +02:00
|
|
|
if _type == "string" then
|
2021-07-03 19:35:02 +02:00
|
|
|
local reference_strings = self.reference_strings
|
2021-07-06 09:24:13 +02:00
|
|
|
if is_key and ((not reference_strings) or value:len() <= key:len()) and value:match"^[%a_][%a%d_]*$" then
|
2021-04-22 13:32:38 +02:00
|
|
|
-- Short key
|
|
|
|
return value, true
|
|
|
|
end
|
2021-07-05 18:00:54 +02:00
|
|
|
local formatted = ("%q"):format(value)
|
2021-07-03 19:35:02 +02:00
|
|
|
if (not reference_strings) or formatted:len() <= key:len() then
|
2021-04-22 13:32:38 +02:00
|
|
|
-- Short string
|
|
|
|
return formatted
|
|
|
|
end
|
2021-05-26 10:33:34 +02:00
|
|
|
-- Use reference
|
|
|
|
create_reference()
|
2021-07-05 18:00:54 +02:00
|
|
|
self:log(key .. "=" .. formatted)
|
2021-04-21 21:29:22 +02:00
|
|
|
elseif _type == "table" then
|
2021-05-26 10:33:34 +02:00
|
|
|
-- Tables always need a reference before they are traversed to prevent infinite recursion
|
|
|
|
create_reference()
|
2021-07-05 18:00:54 +02:00
|
|
|
-- TODO traverse tables to determine whether this is actually needed
|
|
|
|
self:log(key .. "={}")
|
2021-05-12 20:45:15 +02:00
|
|
|
local tablelen = #value
|
2021-07-05 18:00:54 +02:00
|
|
|
for k, v in pairs(value) do
|
|
|
|
if type(k) ~= "number" or k % 1 ~= 0 or k < 1 or k > tablelen then
|
|
|
|
local dumped, short = self:_dump(k, true)
|
|
|
|
self:log(key .. (short and ("." .. dumped) or ("[" .. dumped .. "]")) .. "=" .. self:_dump(v))
|
2021-04-21 21:29:22 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
else
|
|
|
|
error("unsupported type: " .. _type)
|
|
|
|
end
|
|
|
|
return key
|
2021-04-21 21:11:00 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
function lua_log_file:set(table, key, value)
|
2021-04-21 21:29:22 +02:00
|
|
|
if not self.references[table] then
|
|
|
|
error"orphan table"
|
|
|
|
end
|
2021-07-03 19:35:02 +02:00
|
|
|
if table[key] == value then
|
|
|
|
-- No change
|
|
|
|
return
|
|
|
|
end
|
|
|
|
table[key] = value
|
2021-04-22 13:32:38 +02:00
|
|
|
table = self:_dump(table)
|
|
|
|
local key, short_key = self:_dump(key, true)
|
|
|
|
self:log(table .. (short_key and ("." .. key) or ("[" .. key .. "]")) .. "=" .. self:_dump(value))
|
2021-04-21 21:11:00 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
function lua_log_file:set_root(key, value)
|
2021-04-21 21:29:22 +02:00
|
|
|
return self:set(self.root, key, value)
|
2021-04-21 21:11:00 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
function lua_log_file:_write()
|
2021-07-03 19:35:02 +02:00
|
|
|
set_references(self, {})
|
2021-04-21 21:29:22 +02:00
|
|
|
self.reference_count = 0
|
|
|
|
self:log"R={}"
|
2021-04-22 13:32:38 +02:00
|
|
|
self:_dump(self.root)
|
2021-04-21 21:11:00 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
function lua_log_file:_rewrite()
|
2021-04-21 21:29:22 +02:00
|
|
|
self.file = io.open(self.file_path, "w+")
|
|
|
|
self:_write()
|
|
|
|
self.file:close()
|
2021-04-21 21:11:00 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
function lua_log_file:rewrite()
|
2021-04-21 21:29:22 +02:00
|
|
|
if self.file then
|
|
|
|
self.file:close()
|
|
|
|
end
|
|
|
|
self:_rewrite()
|
2021-04-22 11:54:37 +02:00
|
|
|
self:open()
|
2021-06-17 19:45:08 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
-- Export environment
|
|
|
|
return _ENV
|