Fair enough for configuration files, which is the subject of the thread. I agree, simple INI files are probably the best choice.
When a line in a configuration file has an out of range value (e.g. when value can only be 1, 2 or 3 but the user enters a 4), doing it right (according to my standards) involves informing the user about the line number where that happens. That basically discards about every parsing library out there,
including Lua. A custom INI-like key-value parser is easy to write and it can handle values on the fly, which makes it easy to report the faulty line number.
Edit: After more thought, I think Lua is actually a viable alternative. The main problem is that it's difficult to secure. I don't know if I've secured it against most possible attacks, I think I've left out a possible memory overflow problem, but here's a possible approach:
(edit 2: edited to take care of errors during love.filesystem.load)
Edit 3: Edited to remove the __index field of the metatable of strings, to prevent patterns being used as DoS via things like ("a"):rep(40):match(("a*"):rep(40).."b")
Code: Select all
-- Limit number of debug.sethook events in the config file.
-- Ideally, this should grow proportionally to the number of elements in valid_config_keys.
local max_config_events = 2000
-- Allowed configuration keys
local valid_config_keys = {param=true, value=true}
local function check_set(t, idx, val)
if not valid_config_keys[idx] then
error('Unknown configuration parameter:' .. tostring(idx), 2)
end
if idx == 'value' then
if type(val) ~= 'number' then
error("Invalid type for value: " .. type(val), 2)
end
if val ~= val or val < 1 or val > 3 then
error("Value out of range: " .. tostring(val), 2)
end
end
end
local function timeout()
error("Timeout reading config file", 2)
end
local function read_config_file(name)
local config = setmetatable({}, {__newindex=check_set, __metatable=false})
local ok, err = pcall(love.filesystem.load, name)
if not ok then return false, "Error loading " .. name .. ": " .. err end
local config_fn = setfenv(err, config)
if jit then jit.off(config_fn) end
debug.sethook(timeout, "", max_config_events)
local str_idx = getmetatable("").__index
getmetatable("").__index = nil
ok, err = pcall(config_fn)
getmetatable("").__index = str_idx
debug.sethook()
if not ok then return false, err end
return config
end
local ok, err = read_config_file('config.lua')
print("So far so good...")
if not ok then print(err) end
-- this is an example, so we exit immediately:
love.event.quit()
Example config.lua:
Output:
Code: Select all
config.lua:2: Value out of range: 4
Another example config.lua:
Code: Select all
param = 5
repeat until false
value = 4
Output:
Code: Select all
config.lua:3: Timeout reading config file